class TestCertificate(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_certificate_ut_")

        self.compressed_certificate = '\n'.join([
            'eJyVlDmvq0gQhXN+xeTWlTFgbAcTNHSDm53LYkPWYNYGA+ay+deP3wtGI71oKqjgVEmfjo6qvr4+',
            'JSEVW3/J6NvHCpaBj36JX4yJsXysZRmkgiy78kgWxfaqXTe/gSUVdChppV4WVgJuoAAIDqZvLqYb',
            'wdB1Idr6kSGqVRmt1aStMEVcUbhcuCVNbBlB7GPUBFjRmkQNy5QLCv+mTNHt0aRPc7mWqWX6eGE+',
            'jTX9gLWgu91+ix+Cj9//avWf1P9Cmf9DLQpUmYBVZW9QPZzw0EUS8/EGgIAluIBfCzro8Mcv1DG5',
            'uXXiJVvch6UVmncOxjsx4OeXezj2km91Lkv2YcoUwDQe2dPO63GXs7XArfSeT2ZrZwUeW0OwrmvG',
            'Cq1A+zDv+kgO2h88H0YjvOT+t/0omYYGr1NYNbjn0+aGnrNRBJG2hGzpPuMyL+M+u+3oco6+UzGb',
            'XDF5y/OmhLsmx4kjT4SJyDRSEiihfs/ZJldeb5PuzcvEvqqATGtipEPGnxpOQa8mLwih97dGLFGU',
            'dE+rj+6ZUQV5HDa1mO7aYb2AWSHTZrSLnIshD/1OPJXyXCx5aXeZjYxj1DpUIXUa45Mz4mSGjCEJ',
            'J4dvB/Tuif/TddkpihbT7oVQuHlt1qw2T4f27noacEBhSgCgBbqRpncxLufU+qSAFMkF8JPAZ0hh',
            'BEt6PmAhFbrxGImb+mBn9nVRLn4pWJofPvQDUkA7vjxqcFH0ZHRnuMv4hy6sthzDV3+lQ+yRya+v',
            'KtKFbLlVMKmMvX47bHsL9aXonh1pvTzaM/LsTZKYkWtIALOd1xVrmL/pdztCObiofbB+n8ZZJZJu',
            'U5LzY7ubDUrKmMh+dTKhtM116awDgzK0r9flshqOJGK450+CrBaOgPLJJnl/BZFs7nX9B8WHlK20',
            'Kz3UaU15XjPJ3LaKz+RWdoqV6bArinRP+vfDXVulyiLzrKRJbIFr5ltSfufKAa0XuCufaRFkhnHO',
            'Qqo/NjQysr/VLo2Hl7M3mjmcF9torwQAEu9hQW8iNwZ/M78vH1nwz2/wD2fJXIA=',
            ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(zlib.decompress(compressed_cert))
        self.fingerprint = "81:8D:BE:49:A7:3E:F1:C4:DA:01:50:F0:80:D4:CB:27:96:80:1D"

        self.message_digest = "e380c3276b33440dd3e80af116fd6ee307b8ca6a"
        self.signature = 'q?\xd8p\r\x98w}3A\xc9/\x05\xd5\xf7\x19\xe1B-4\xcec\xe1\xa7\xb8\xb2&\xdbG\xd9\xc9\x81T\xfa\xdeX\xda\xd3\x11s\\H\xce\x82\xab\xcc\nP+*\x1eO\x02Q\x8f\xb0L\x11\xcd\x8fm\x10\xe7\xcc\xce\xc6\xd4hU\xf0d\x84\x8f\x82\xc3.G<\x1dt\x11\xd51\xb4\x13L t\x92\x02FO\x9e\x8e\xd7\x07#\xb9\xcd\x03)b\xffM\x13\x05v\xa43\xe9t\xbf\xfda\xa9\xb2K\x7f\x8b\xee\xd2\xa3\xb27\xb4\xa6\xc4\x88\xf5\xf9~}0\xdc\x8e\xfa\xf8q\xe6\x90\xf73w=\xd0\xff\xcbX\xdd\x10\x18e\x15\xec\xbb\xbf\xd6\x03\xc4\xf7\x86\x1fr\x11+\xbffmY\xff\x84q\xa1*{ \xca\xf2\nN\x8b\xfc\x85x3`\xab\xd5&\xb3\xb1\x03\xc6G5\xee\xebh\x91\xe3\xd5Y2\x96\x87\xe7\xd2\xaeu\xf7\xea\x80\x9c\xfc\x05HjZ\x81\xb7Qz\xd2p\x8e\xf6\xad\x0c\x9a\xf2\xdd"\x02\xf3\xab\xff2E\x03\xea\xd7W\x9b\xe2^`\xdc\xec5\x92z\xed\x02\xe7\xaa&'


    def test_load(self):
        with open(self.certificate_file) as cert_file:
            cvmfs.Certificate(cert_file)


    def test_get_openssl_x509(self):
        with open(self.certificate_file) as cert_file:
            cert = cvmfs.Certificate(cert_file)
            x509 = cert.get_openssl_certificate()
            self.assertTrue(isinstance(x509, X509))


    def test_verify_message(self):
        with open(self.certificate_file) as cert_file:
            cert = cvmfs.Certificate(cert_file)
            self.assertTrue(cert.verify(self.signature, self.message_digest))


    def test_verify_tampered_message(self):
        with open(self.certificate_file) as cert_file:
            cert = cvmfs.Certificate(cert_file)
            self.assertFalse(cert.verify(self.signature, "I am Malory!"))


    def test_certificate_fingerprint(self):
        with open(self.certificate_file) as cert_file:
            cert = cvmfs.Certificate(cert_file)
            self.assertEqual(self.fingerprint, cert.get_fingerprint())
Exemple #2
0
class TestManifest(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_manifest_ut_")

        self.compressed_certificate = '\n'.join([
            'eJxllLmOq1gQQHO+YnLUMtDG4OAFdwODWc1iIDOYxew0q/n6cU8wyauodHSkUqmWr69PQCIrxj+I',
            '3FxFUhBwyS/8onRFQccSIZB0CNl4CkPLYrtHIGJgwLwaiuoln1cGAtuTAAa77pJVW0Ps2zYm76Kg',
            'nvJWJG++jAJ11hqjU4hRJw1/dx1W9t7Qie5bHQZGrRC2iBup/Xi7Do8Bdsmm42SjTJfwOvY2Q+p+',
            '4fsXmvh/tur7R0SiDFiPgE3vwuA2hoE+h5z/jl98ST0QxL/V/YqZbrWee40/RndpjxxohHe+iD5i',
            'sn9adJQV26F67SKlWBID2ARCG2Aqz4kF8EewO/TJIdBjNEnfUxspTHSCpaem45Yb8syRc9TTIx2+',
            'uFzoWgk0D604y9RQbvIkDius8Njmg9nLHWqP/TTlImm2Q+uxOo5VMDyuomey65ZMoYNy9ZAflRqx',
            'zEi59Ok6CBUOaU8RnRMyewW5AuQjjWuKPGba5GQBXcNbX17opJaP9Perjq2n1p84LJ0NqkSZSdTG',
            'YGm59Gttq4dMzLlHF4usbmzZ2sYX3ZTENYZ25TyEBnZSx3Z+xrnv+RT/KNT67UQ8mI5qxhUnw4sv',
            'c7vlXI+nLUWcxJQ3lWltV2+wOMWhnZBxaxdVBSGj5cTOpplKpS01Jk5hD1M43OJntNy6JQiRkJAS',
            'stkhUiSzcggCKwHA/Xu5KADMzwQIWJkytdPoGgp75OFEep4SeQo6v6rol4rsq+CZmRtfZWScBfm+',
            'BMf4ehh4kFIOdLRzvtepQEf38bK0l+v48NkIG8EG0ltj2NNxSplDLXqXlNi+V7N2pvhLKfpHf9YY',
            'qk7qyIxzQW6Syqqf0ZhIqW+9cLjT6CbNZyxwlpMqluqqfXDPfe5aOeXhwDn+4g5yaFEwYuF5Bm27',
            '6I3/cPKfMRgyvp/gyVwaa17S6soH9n5d70X582Mo+IQux83JaVQcGJM9U/39eOG9bm93etYjiTH4',
            'gj0NUHy+jF7bnPGSvZ+zZ4ZBy77nn37a4XAZXqNys2BYZqJDOeL9wVSZFJmCtyfaUAvWub3Ygee7',
            'HgJ//lD/XT0x8N+f4F81GFZb', ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(
            compressed_cert)

        self.sane_manifest = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20', 'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D900', 'S8722',
            'Natlas.cern.ch', 'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', 'T1433937750', '--',
            'e7d58bfaaf75e9b725aa23c3a666daffd6351b8f',
            '"P4\x99a>LR\x8eE\x91\xdb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
        ]))
        self.file_manifest = self.sandbox.write_to_temporary(
            self.sane_manifest.getvalue())
        self.assertNotEqual(None, self.file_manifest)

        self.insane_manifest_tampered = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20', 'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D900', 'S8722',
            'Natlas-malicious.cern.ch',
            'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', 'T1433937750', '--',
            '892f6997d6046669640b8520842bc7c4f17cf269',
            '"P4\x99a>LR\x8eE\x91\xdb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
        ]))

        self.insane_manifest_broken_signature = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20', 'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D900', 'S8722',
            'Natlas.cern.ch', 'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', 'T1433937750', '--',
            'e7d58bfaaf75e9b725aa23c3a666daffd6351b8f',
            '"P4\x99a>LR\x8eE\x91\xeb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
            #                      ^-- this byte used to be a 'd'
        ]))

        self.full_manifest = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20', 'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D900',
            'L0000000000000000000000000000000000000000', 'S8722',
            'Natlas.cern.ch', 'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', 'T1433937750', 'Gno',
            '--', 'd79461faeaedc6875ee1592967811deee3fe2b99',
            'invalid signature'
        ]))

        self.unknown_field_manifest = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59', 'D3600',
            'L0000000000000000000000000000000000000000', 'Natlas.cern.ch',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'S4264', 'Qi_am_unexpected!',
            ''
        ]))

        self.minimal_manifest_entries = [
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D3600', 'S4264',
            'Natlas.cern.ch'
        ]

        self.minimal_manifest = StringIO.StringIO(
            '\n'.join(self.minimal_manifest_entries) + '\n')

        self.missing_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D3600', 'S4264',
            'Natlas.cern.ch', '--', ''
        ]))

        self.broken_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D3600', 'S4264',
            'Natlas.cern.ch', '--', 'foobar', ''
        ]))

        self.incomplete_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e', 'D3600', 'S4264',
            'Natlas.cern.ch', '--', 'b748926022513a4398743d31c49578bc3a5fc3ef',
            ''
        ]))

    def test_manifest_creation(self):
        manifest = cvmfs.Manifest(self.sane_manifest)
        last_modified = datetime.datetime(2015,
                                          6,
                                          10,
                                          12,
                                          2,
                                          30,
                                          tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20',
                         manifest.root_catalog)
        self.assertEqual(900, manifest.ttl)
        self.assertEqual('atlas.cern.ch', manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e',
                         manifest.root_hash)
        self.assertEqual(8722, manifest.revision)
        self.assertEqual(last_modified, manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab',
                         manifest.certificate)
        self.assertEqual(75834368, manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
                         manifest.history_database)

    def test_mainfest_from_file(self):
        manifest = cvmfs.Manifest.open(self.file_manifest)
        last_modified = datetime.datetime(2015,
                                          6,
                                          10,
                                          12,
                                          2,
                                          30,
                                          tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20',
                         manifest.root_catalog)
        self.assertEqual(900, manifest.ttl)
        self.assertEqual('atlas.cern.ch', manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e',
                         manifest.root_hash)
        self.assertEqual(8722, manifest.revision)
        self.assertEqual(last_modified, manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab',
                         manifest.certificate)
        self.assertEqual(75834368, manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
                         manifest.history_database)

    def test_full_manifest(self):
        manifest = cvmfs.Manifest(self.full_manifest)
        last_modified = datetime.datetime(2015,
                                          6,
                                          10,
                                          12,
                                          2,
                                          30,
                                          tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'micro_catalog'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertTrue(hasattr(manifest, 'garbage_collectable'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20',
                         manifest.root_catalog)
        self.assertEqual(900, manifest.ttl)
        self.assertEqual('0000000000000000000000000000000000000000',
                         manifest.micro_catalog)
        self.assertEqual('atlas.cern.ch', manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e',
                         manifest.root_hash)
        self.assertEqual(8722, manifest.revision)
        self.assertEqual(last_modified, manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab',
                         manifest.certificate)
        self.assertEqual(75834368, manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
                         manifest.history_database)
        self.assertFalse(manifest.garbage_collectable)

    def test_minimal_manifest(self):
        manifest = cvmfs.Manifest(self.minimal_manifest)
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertEqual('600230b0ba7620426f2e898f1e1f43c5466efe59',
                         manifest.root_catalog)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e',
                         manifest.root_hash)
        self.assertEqual(3600, manifest.ttl)
        self.assertEqual(4264, manifest.revision)
        self.assertEqual('atlas.cern.ch', manifest.repository_name)

    def test_unknown_manifest_field(self):
        self.assertRaises(cvmfs.UnknownManifestField, cvmfs.Manifest,
                          self.unknown_field_manifest)

    def test_invalid_manifest(self):
        for i in range(len(self.minimal_manifest_entries)):
            incomplete_manifest_entries = list(self.minimal_manifest_entries)
            del incomplete_manifest_entries[i]
            incomplete_manifest = StringIO.StringIO(
                '\n'.join(incomplete_manifest_entries))
            self.assertRaises(cvmfs.ManifestValidityError, cvmfs.Manifest,
                              incomplete_manifest)

    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Manifest,
                          self.missing_signature)

    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Manifest,
                          self.broken_signature)

    def test_incomplete_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Manifest,
                          self.incomplete_signature)

    def test_verify_signature(self):
        manifest = cvmfs.Manifest(self.sane_manifest)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertTrue(is_valid)

    def test_verify_invalid_signature(self):
        manifest = cvmfs.Manifest(self.insane_manifest_tampered)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertFalse(is_valid)

    def test_verify_inconsistent_signature(self):
        manifest = cvmfs.Manifest(self.insane_manifest_broken_signature)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertFalse(is_valid)
class MockRepository:
    """ Generates a mock CVMFS repository for unit testing purposes """
    repo_extract_dir = "repo"

    def __init__(self):
        self.running = False
        self.sandbox = FileSandbox("py_cvmfs_mock_repo_")
        self.repo_name = MockRepository.repo_name
        self._extract_dir = os.path.join(self.sandbox.temporary_dir,
                                         MockRepository.repo_extract_dir)
        self.dir = os.path.join(self._extract_dir, "cvmfs", self.repo_name)
        self._setup_repository()

    def __del__(self):
        if self.running:
            self._shut_down_http_server()

    def serve_via_http(self, port=8000):
        self._spawn_http_server(self._extract_dir, port)
        self.url = "http://localhost:" + str(port) + "/cvmfs/" + self.repo_name

    def make_valid_whitelist(self):
        tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1)
        self._resign_whitelist(tomorrow)

    def make_expired_whitelist(self):
        yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=2)
        self._resign_whitelist(yesterday)

    def _resign_whitelist(self, expiry_date):
        old_whitelist = os.path.join(self.dir, ".cvmfswhitelist")
        new_whitelist = os.path.join(self.dir, ".cvmfswhitelist.new")
        wl_hash = hashlib.sha1()
        new_wl = open(new_whitelist, 'w+')
        old_wl = open(old_whitelist)
        pos = old_wl.tell()
        while True:
            line = old_wl.readline()
            if len(line) >= 3 and line[0:3] == 'E20':  #fails in 85 years
                line = 'E' + expiry_date.strftime("%Y%m%d%H%M%S") + '\n'
            if line[0:2] == "--":
                break
            if pos == old_wl.tell():
                raise Exception("Signature not found in whitelist")
            wl_hash.update(line)
            new_wl.write(line)
            pos = old_wl.tell()
        old_wl.close()
        new_wl.write("--\n")
        new_wl.write(wl_hash.hexdigest())
        new_wl.write("\n")
        key = RSA.load_key(self.master_key)
        sig = key.private_encrypt(wl_hash.hexdigest(), RSA.pkcs1_padding)
        new_wl.write(sig)
        new_wl.close()
        os.rename(new_whitelist, old_whitelist)

    def _setup_repository(self):
        self.sandbox.create_directory(self._extract_dir)
        repo = StringIO.StringIO(base64.b64decode(MockRepository.repo_data))
        repo_tar = tarfile.open(None, "r:gz", repo)
        repo_tar.extractall(self._extract_dir)
        pubkey = self.sandbox.write_to_temporary(MockRepository.repo_pubkey)
        self.public_key = pubkey
        privkey = self.sandbox.write_to_temporary(MockRepository.repo_privkey)
        self.private_key = privkey
        mkey = self.sandbox.write_to_temporary(MockRepository.repo_masterkey)
        self.master_key = mkey

    def _spawn_http_server(self, document_root, port):
        handler = CvmfsRequestHandler
        address = ("localhost", port)
        self.httpd = CvmfsTestServer(document_root, address, handler)
        self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
        self.httpd_thread.setDaemon(True)
        self.httpd_thread.start()
        self.running = True

    def _shut_down_http_server(self):
        self.httpd.shutdown()
        self.url = None

################################################################################

    repo_name = "test.cern.ch"

    repo_pubkey = '\n'.join([
        '-----BEGIN PUBLIC KEY-----',
        'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueS/yBwR4UlRsgkv7hcM',
        'ljvt/KyrhkI5y7n7ksLBFumjPhieaWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCH',
        'erxxjuei4Vu9yG6BFqow0ZdmjqJzU4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+C',
        'hQ42pB+PD0CcEZp3VEsFV4wI9IY7EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEu',
        'RM76cnfxh/0qui8Vbs93St2VhsahJWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW',
        '2xAkfJ5d6NZJdrmRBD3agh5Zj8DuK0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuq',
        'lQIDAQAB', '-----END PUBLIC KEY-----', ''
    ])

    repo_privkey = '\n'.join([
        '-----BEGIN RSA PRIVATE KEY-----',
        'MIIEowIBAAKCAQEA5TQNCMPftxI8Z27ReqQKgi1MNNmfW/l0Ns1Ax2KxspBYnbE3',
        'FBhC1/B/uzIhW8mrYcXlFQ0400WfJHC+/pquDR/IXCrWB+1dyz9dp/7l4HcxcDNH',
        'fc/g2hSvtdcw1ZCHNGONcCOxOYE3Rx10eZjviQZiGkzVkHwpnjgWNxbT7bkIV6iu',
        'R8YWNUfGABwhTB5diq3UPtw5LEwYYj/CKFR9BkTK0VmZJbXV5bAvGQyINfT5296r',
        'XJWD4WItJJ95R/zkBQgBjvLTNpFPRiOln8FTFpEDVWdteezDDXGWNWma3jYwvJg8',
        'PBZEEcUmZBDQT9k0nfRizhsTyO7xzDmcAPfVHQIDAQABAoIBAB1JV1kFXjKQO/Oj',
        'b1TSXR1hGFmwbPJdn4HZHCvd6oK8evY7TKRerTvWWRvcPfLyg9mMZccY12f3f2wy',
        'k9UIgrDenMVaG9sLc26i/B6ZLVpPIJwLkVj8FOkIt6LuiijfvMbu6YWoqd6FKkEF',
        '/HoFFqZVkHd31doOY2r6E6yaWB4JxqXcNJLAQRnm0mCytZUkzHodr+Tsy1GkSJC2',
        'I3saPSIG5u4TL3XVsIBs5mE47FLK/YKRHJyp3tTLgTkhDvpLPEJC8ij1oWJ4PSzo',
        'jeaK8xpv+LAjqu8brL9WZQI812Fmx+nPncSmKZrmjAYr4HaZ4aAgsWKOnqnyzrZv',
        'QSao9EECgYEA8p1g/4iZVXkUjNVL6a+Cmj+g6+ji9wPXDOXFiwlOgXCDRFxunaMM',
        'ZbZtxe26XHQdVm4e2hC4Zv2nn7Sn9gGg+7NCso5Y5qDH2HMJ/w+ya6XUIcE/YDMb',
        'PqK6llLqsyLg+nrtV0ZkA5UN8Rlp116fWE0ensszH9qF43pB+7m8ANkCgYEA8dlA',
        'sv9ZJW4/MtK8dbxzmrpyCRbLFsuYYN7k1ybrUVvK6WpGljqKbduTJ3/XC6qhJuWR',
        'YAH9aTwwvRvEJor0EnYc+QpEnv5KHfNkKFU2x3tnnYZT24t2PLpY9AECjgeSUDk+',
        'kHQzUGmig5eSSYMpGdLvRtD0BYXQLoD8eAy3y+UCgYEAtQ0jFK7Qlotr/YkzRGm4',
        'kfmH0mUR8vqHolVZ7N7+GfRn0T0VQ0go+UKBeuJkX5g7SIOXPG6b3ifOzozXhutC',
        'QnNNA8jcqQc0+98lh5UkNdcjjikTbWvWGhEAIywvf404zVOtCKM8AbxbEiA/7vvq',
        '989dWW0UcuH1ZoOW+A5sMUkCgYB/E+zPISUyac+DYP/tzWvhLX6mD/f+rlQO8o/E',
        'DYswYM8p/tHANlpuhyW3Z5ETbEDpM09D50fEeAAUHfbfWbwNx0pKAX81G+DOBAno',
        't33lK46yUtbVUV57Yl9DNxSklI3o4Wtic+xSoG7oPkh7oBOEojVgPIM8M6fEB7qh',
        'Se15kQKBgBQ8/Z4A95yoGlv1RYOBuZpOpEtbgi/NiJdRXnzrmQ1m31heRbkfu3w0',
        '5WzlYjaQQ3g3rsh+0Ond9bLFcZor6azcPSsu+cjC3Gsxm/0KZKPAroi73Gd4O0QH',
        'ih/vJDlTHRS2ArfdYc9cUYTFvs8YuLy7y9Uho35ey6PLX6CEsJel',
        '-----END RSA PRIVATE KEY-----', ''
    ])

    repo_masterkey = '\n'.join([
        '-----BEGIN RSA PRIVATE KEY-----',
        'MIIEowIBAAKCAQEAueS/yBwR4UlRsgkv7hcMljvt/KyrhkI5y7n7ksLBFumjPhie',
        'aWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCHerxxjuei4Vu9yG6BFqow0ZdmjqJz',
        'U4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+ChQ42pB+PD0CcEZp3VEsFV4wI9IY7',
        'EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEuRM76cnfxh/0qui8Vbs93St2Vhsah',
        'JWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW2xAkfJ5d6NZJdrmRBD3agh5Zj8Du',
        'K0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuqlQIDAQABAoIBAASWKUk1sBc/6N0c',
        'rusP9IaMaf3PANhqL+Tf7N4dIgh/sUBp+Rae0qaAuojCJShFCsKmp++itOcrCIjy',
        'Vr9FZYJYvfCJtJIc4lzcpSC7CENTmfsw9Ol9yK4ozW5YdNWnfNxLILZBkbK1qqcC',
        'sLfYgB7qT9zSzoPQ00j357PTugkD56eiJcNZu80nRy0Ud3D/3dDFJADF1hQkebwu',
        '82NLqNQnTO2/KF1fJLgsIU3ymMdOV68k9rjtGfLRoK4qfX0lb8BNrAY2urPzU0yV',
        'Y2unrWWbmWT2lDOIqRCfLbGSQuVfLbY7JOq+PwA+H7C2Py6GQLuFi8t5DTVuGke/',
        'NtZpHkECgYEA9u+OPbZLISvmGNFZg4hn6k8PfH0GLrEcx0tPv4ReONqwXVobeYKX',
        '/x0b6o2BC0bmICMjnGsDrZSZj2wiXAYknSQCxMAGAj0PoBI9KQNU1Simwb/IA0EE',
        'd+c6BdR0YdVIQ7esSNaCaAb0zX1/y98U7HOQ2/ornhAM4wKKRtwykMUCgYEAwLeV',
        'IvRHnwXls8kux+KOEAHGoWf4KqaOYUbqktSdVB5DziqN2Ktj46/wQxcmaS5gbMNR',
        'B+lvveP7t3qKzMMMeBtKEKou1lGC+K7yWo//v1st25p8j9Ue0xlaw5ZiVRyYzZYV',
        'uwnaBNFiNk8YH+to8UdwYGDPuNNZjE7JuFcdr5ECgYEAtsTKWBzj8LJoRXg2M+ez',
        'WjaYNMDo4YhPz6aLaSpU/unGXeICseYaEEYAUpPXrnwUejbn9a8zcrepDQGxUMFv',
        'OivcLLof+GovdX/qar+/e2HyQzdqmBX4c7LePFBqr7rIGO8KgoLa1JpJeQrpmwEL',
        'oJNM5bR9sikZELDhnd7/Qi0CgYAV8VEzx6iX/K3oyJFhBPSz8d/R5OqmwIwZm19+',
        'FGNNfpytzr6T2v/mntO2b95Zv4QPHjYNtpCYiGrSu0sugU7cJg9K0nW+xU0qT5Ec',
        'qqSt/w27oV1paxS1aH+jIW5Uzoq/bcVPpJGEVurd0CepCr7KKh4rexprqvTZOudQ',
        '6+pfYQKBgBmC5quiKh2ILLh5IJ1g5UXQPzFgE6J9XcVY3BumXBr41g2CYPC5oklM',
        'v5PZ3wY76x8O+2S+sYBKzDHOv3Q8AJPC2PEIJORzTK6XfIetpnN3TR0LZvHiUpES',
        'hmCojC2QE3Y7i+XTL2d9rbXLSIbMEWDHdBHKzTWczDIDo+tFPEFo',
        '-----END RSA PRIVATE KEY-----', ''
    ])

    repo_data = '\n'.join([
        'H4sIAG+NeVUAA+zdeTxU//4HcEIxbSpKoWxZImY/Y7LvVCRK2sasjOymRPatIksbylYGhVIUokJZ',
        'ShLKvhelGGuKkLnqex/38ft+H199772/4nHv/Tz/mTnHzJwzXufzOZ/zeXDe5GP2NFcljl8KPgvC',
        'YL4/zvrj4/fnCCQGhUXBsWgMggOOgGMhiEMM82t36zdHXRlEFzExDhdHR8aPXvdXP/8PRf6eP4Pq',
        'ylAkU10cFMk2P/9g+FfyxyBnX4dAIUD+8+NP8lf8vo5gT3RlUF0ILlQnOzqZ+P/ZxreAsWj0P9n+',
        'UbP5QygMlkMM/rO+5I+A/P88fzcbOoNqR3f9CV/6r/LHzrb53/f/s2vQIP/5gIQjMHAsAoFAIZUR',
        'KJju92XoH8vG//fIgKEhvA4arwPhcRi8HhaPgON1cXi4Mh6ni9dC4bE4vDaER+vicQg8SgePUsYj',
        '9PAYPby2Ll4ZB9u6FYaYzRWHIyLhEJqEgShUIlKZisAiKGQyjYag4iA0Fk2mUtA42P51SiPuKpdQ',
        'D+Ia39EDvlzjHqnSPc6bk7eC55Sn4mEzZ0SQdWHLKYu9ozjnprSnEvXB+l/DlbaH8eTAVrneSy3w',
        'QbW0caYP8vsfR9aUesQwt667q3NTgysPjTofZXKw76Dwy2KXp1bKfsyDH3KnbelH3jXHG22dDnzB',
        'wU26PdiAeDZONHwXxqMfbiwVZLA4VFImP7rOvkDhWXNq8JYaP+l24+DqmU0wQW0YeoWLvp6tgsCz',
        'K6s90gztIg8RfFISfXf1LXc03bMK3jzinLib2HgR76DQksRf1aGplrt6x97Fmm0HoPyZyYSgwuGo',
        'Bjn3GkOjq9h44nDXyjdbXXXLtMORdIVj85j/n7R/CpFB/KmDgH99/IeeHQGC8/98mCt/ZdTPOwT+',
        'jfxRaATIfz7MlT8asbD5o5Ag//kwV/4oJMj/d/7H8kf8xCHAv5E/Gg6u/+fFXPkTSQubPwQH+c+H',
        'ufJHQguUP/bv/T9o//PiB/lj4XA0iUwlU2cv2bEYJBYJx5ExaCJEQ+NIZCQOBRHJKIj0T2zjW8Bz',
        'z/+gIexsm/9d/kgEBo4A8z/z4Xgc6xEn5+wT38iZW4O8sIXeHwAAAAAAAAAAAAAAfjXf5gCub5MB',
        'HGLsRr1dewAAAAAAAAAAAAAA+G9HXLmC+/ukwDRBWHOhdwYAAAAAAAAAAAAAgF8uSBXH//0vBEZ9',
        '7LkXemcAAAAAAAAAAAAAAPjlLn7u8v02FaDxlN24nG+h/lUBAAAAAAAAAAAAAP67DAusLubg/EUf',
        'Ptf9H+ELfP9XcP/f+THn/X8pC5P/b/Wf0GgIDfKfDz/IfzYECIElYtBwIoIMJyvDiTQUEQOhyBAE',
        'p9AglDKFjEGhDf56Gz++/yscgUX/IX8kAgMhwf1f58PxuIHo6ohWHYEn1tPZxv77IvdRmQkx9ekk',
        'pl3ujOh9MR3ZvcKyKZSAukyBoCzVjn2xuLRucwWRXTiJERMn3424x4QaLpF7fpE4MfEV05wiOGer',
        'OIbwkrutVfTQYf1Rm3fNQ1Xjzd3Q55n7UEDMKh+h5TY7zC9oCzEbeTV57UclI9ZyqLukIfRMaGvq',
        'vatvlRAMSlfyP+DrhA2VrPUWM3/xOvbsm+4cT/cTwiYDMXivtYjxpoE7mWsC0zKP36RCLVg6dLiI',
        'fr6OL/sVs3sjvdWt5X7p0KsSBL3DtrVxoEFRvKdlW97n1puL8yJrTd2OvR3Y+3FAr1/RvbJw2/hN',
        'c4apApSHgGlFeMELG3szUrfLjDhhB1Ol+5+laXpPbumpTFk9k7xtzcaP+ecl86QwWZHxkSuaJMPk',
        '8lfd33QmgBizV2QJV3nJMr2lzFYm8enk0oAn5Q4CZ96UHt1IF/F8alx9IpUoN6RjkjK621ih6CB+',
        '61BphrzMQIs+jtDR8vhVuUKtKVokS3ZF0/HaNVHnDjdVvFi1zVtqVG3AxHjwcXZx3aftyHzHlCQY',
        '6ctkA33LusxD7rWJD6v7DbkSJSa4vP2GjstISnYvOq4bKB9vunsw+LzzeDKLfi752eXkLenvNmTf',
        'QGqnCxcSY47Wp00VMoXDlZTTCzl3FbA6Qx/HWJSrRuu+Tmht5b8yOd00kMXpJbLLfW1JSli+acf5',
        'BM2KKTb/o+ph/pnATsVJRQqFckOHL7PLp355FsfmuDquJt8tM/rL+UOc0nB5N5fq2yz3ieiSP27c',
        'JFD4rv0MWxaCBbks6YVFB+EuB+qHGB5tZEbtEG1npFnvL5Telm57OZva93yP6pOLewgB4+HqR+6f',
        'lyURdzg0hUE77iqtZUm9LHB6qrWWx2HnWcpD6fUYS4fKvj1lBemWS+R3Wkk4maI2V78yr3olYmLK',
        'qBrtzyxflmibsDk2WCItSoZelxJSzT14U6Hi/nkl83MafSq7BigjtwRqyWrxY2Qk30NxNufpFZbP',
        'PwbLvLtpsES+iixtktosp/aiFYMpq+u7nBwrfV6lc7un3Ke4AlZ/AXFk/eaIoc9t7m5f0u7CLSxU',
        'zOJvbLqxulOnYqZosRl30WMIHpTY9XHp4dL169kyvE0TNyTkCzkEHxzsHbWeHF7Eoytb7QVT8j+k',
        'krFfis1e5OnXtaRj9nepEmTO816geqrZtGz6zUdvHqmpIg6CxMRZaP369SaS92se9c4MXM8cnmEv',
        '3/rkK9evbv8/6P8xOBKajKOREHAqhQKnYXBwNByNRSNpWBpZWRmDpVIxNDRc+6+38Rf9PxKF/EP9',
        'PyQCQoP6b/Nitv/fX+UorCuQ2JzXHDGB2mSVJGvD3PJY/prAtRANg1P4CixsOd/RMzbGGfYPwi2c',
        'WQy3dqyYuL8Hx+lbksObim+KrFJ1ClyqVZOU5PLal5vHDyeWbFDMinZrpL+UkJKAPp4fiB9vhT52',
        'PJ9098m1fstKIvbvvkl1sM1lrM7iJTOMzvFrSbinmvKGqzSfCqttF0/qVh/2Ckex8MdWHyMKEIpd',
        'jw9u9BjnhalEn/DglmRa1isdWLeBornnpn3ewQOHMvJihaP2j4X7dzzpDWEel3mdgEkv1byTo3fk',
        'UPvLHQ/pU2uhA3uNBHX377ts9sLwRHvy280V1skP9NPKDCscd1bcSlikBuFDbj2wOtIrXDDUt2mf',
        'z7ARnWbKd7L5hrEj/dihreeuC1IPy5kYcWIEd2veVt1Rei3zUn2wZqpwyo0C9J17Kig5y/JQDymd',
        'jA05dmQUqWFDiwclMlXPeO8FV0Hk08+pax7ltehhxvZHbHjJGn1plHJQIvBuzqobSWMH3V8373bf',
        'g2fe0JPeeOm2wibr/IN0RmdlHyE8qfP9Cb6P+SPOD8xc1nlW2tl/SLV/v7eJRKrcOLrbW7vzs4yL',
        '6ZG7AXLlqIiJJ+dkvKhfpErevnsyXolhpa8twbr3htiFPPjW37hNhIvIysp6rDP0zTqTRkArMOs9',
        '7U75aZXvz+iPvbwhskwq+Kx4RJwHo2CT8icf0UdHJ7gK1VljcrKyM6hz8OAAj5Pisl2Z1Rx6gatq',
        'u+Om1IOFT6nMft7g0h7n4G5uTslgYpm5EX9TRE0r1Ck0Nfn2krqPt4XnW33pUKMilaKvqaw2NqJN',
        'r0dTU9OZTYp5i8uaCijOYB/q+aqiP7tKpzjbXyy1rYF4TWt2SchpkH13Rk42XHp2Q6GO4p6hltQO',
        'f6VLgu/IEzDBwR55J8/qrxV8+hMRTh5ZHu9Joi8zedLZs69XvD5z7QquwOARppSZcaoCGd0l2uZv',
        'UrtnuodofZTj2D1vk3qP1bXZmNbbduQq2IG0mF3L3OWKGmwh+/Yzcq3h70K2ZxrkxfSNnhRbBDlH',
        'Fi+/n1F2O77DfV+Xa9Cijqw2VqyUz8MN15EZlayIKmZ/8p3+xswl2dmZggG0HanlvXWqOnakF07Z',
        '0z72Jt6R3a6SkhOwIT4vN8e748+eUAsr2zOeOdbbRjmcPOaJSLItPGB1gjiYfiuAv6MGub53zMXS',
        'MoaA1zi3nfr27u69UbSLgqeLCVFePVNFLduNn/l5GfW4WC+dOvq+4I1tVqgBblK9sk1jWL4tsLpr',
        'zQRHR0Bj1+obj1DDOJVFXM8Jz02WtYvq1GoLPdfSvN3Sa2/WuIe9bdxK+vGlSY/7Vy9x0SO3xVls',
        'KJHD1V611QsYk7mYYPw4a8UBzHM/nWSmRVi+d0y4WfKLuMIAktRtHwnJ4kCrD2H7Iw8Fr+RVz0H3',
        '6jB4lW75qckkkd3OGd2GSxcIVMgu9yuKfgHvfs4kT+VTnPjLrUW/JlILNYeFPZcmlmAm+DtONr5G',
        'mr9epsLpLkSZoTmsWQIL6fC7/wFzmN0s27vOksZPEbnFHuNyWjSUxjZfHP7a4KsMU/Sh/3YV7lcT',
        'xp86Ny1nJfLfgfsxPykxeNUxHWFCDeq9emfqfRLvVEPXfXK7h1K4BlUK+hMrigOFqGzO0jDzuoXu',
        'T//TzHX+xygvcP0/FLj+nw9z1n9c4Pk/JKj/NC/mrP+IXeD6f2D+d17MlT8JDub/f+d/LH/Kwtb/',
        'RSPA/P+8mHP8t9D1f8H4b17Mef5foPqPGCTqt/E/GP/Nix/kT0OicXAiDktB07A4HBKOgpDKylgy',
        'hoqGcDgSpAynolBYzE+Y/4WwyN/nj0QgEaD+47z4Nv9rb3Ngj1BhbyE0Lr3zcjI9r+li31bqXcvc',
        'Yx+W2NVnUY9gK3NLUy56dZ1yq9rpaq9W0Bbb0rdJfs203nSv1ZS4NnGCrG3OiC27qj52UoL8MeD0',
        'FbGALSHq0vcQquOSFQ/zBtQ0FFJ6/aR6vQaVh6YSXKfiplKVS6Y+G4Q8tQ5JeXBl+KGAtriOfbd7',
        'SFLbxNsUW6uRUETzqQJUW10wAXeyZb3D9dwlDhpL8SGLa7sOnTkszetq53QElZSstT/uXMS7Yf/U',
        'wOzddaw6m9yDQtFmq76+ksPfs0rmqbqXk7F7gxu/gLiFyPadvJuzrgitvS+5PjS8dOLqhBWLQ71I',
        'OPHUx8Ulbn3mW7IToP2qV63zONfZ0TWffJZ/fDbOhoFREyr5YsCYSthXtVOtXV1cttmcW1/09EDI',
        '5saPgunX9Pb4V5F3eGVrycWfH2ZUXYh7mzdkaEeornxpliSng7DPFZBzRKKUic97ksW6QiWzuCSb',
        'KhiHw6z2UeUO9WNo2AnR0xPDBsgQPm/Sg+yQacmKqw9rezS93VhX6G9S+U5qCQthhewtx3I35EmS',
        '0CImkopNDcG0pBZ/kwsha0iGZ3XyG5K2Vh7u/vLC2bkl5mlypLxFqk1VW13nOZWNwrBPL+2cLFpJ',
        '+Q3BqRI52JnKo4hPE/FBAjw+j1cOC3ryytzW1NAYJnSJry1YaXKh1DBtX5RZ3zUFwaXCQ6uPiNTU',
        'Rq2hbA6CDOUkT38wIDh2lidUPYydihWycVQUDIXtzYs5m1oQwZsyuHGlRsat1dIvDbSbZCJoZQ92',
        'hYmOiNk41FUkZM7gjerZfLCOsmdFyxIfHZ2gp60L8/XlIAsymO6xS1o5RJ9my+E8a8J3HODs9ttr',
        '/mFFGL9erqrvMc0723vretU1UDyJ/mfUeXl5zUU4NoW+7nNdFIDvi38Rfe90zVjKgUdR4x6Q6pDy',
        'zMhwwM1X3c+ErxlXP1SOKXIffJZDyFNhezfaZfOrX7hX6NFenLbuybXrl8wijIuyoz9siH9uTemO',
        '1fLHqkiy8ledGMR15gxWJmpXL2ubrD1BqzowNZBQpDr2KGcGOd526XXJaVdHgtoXj0qulaiCfjVp',
        'M4UVYlXiSTaXNdVYNYNdPc7sTteRCplygrdnf0NwG+FuYshMqnRDA1ONzUmTiykzYeenHGYsWuR/',
        'onjL4v6OQpRfeYubYAHfHcWphl31bJEGNmu3yUlf366NV+TaM+n7Ius/OSrx1E12jhK8Zt8lyPXp',
        'i8mYicZt5FU19nrGRS2LDwFjGcMY1R0+3NHXh7W84hXyWN49gYLT2rh8y8r3tK21aX9j5zyDmt7W',
        'NU4RKeqmCSgiQqQTIIUkVFGagqC0IAEChBKq9Ij0DopSlN4VUKQjvXcFQaoUSeiCiHQkUgN337kz',
        'Z86+M+wzd+4dmDmX34f/1/XhmbXW+77ref5UWofMDPmGUbvpfiZzGZyiGhy4uAzSZvQau1AL/yBK',
        'vHgQNX4WapdqNPSTNyOfJoxHeOCdoe2QAd0H9l63CNYuKiUgSohg020hUq3WCkTGT1OwVa0QWKrK',
        'p3PuFCynVk9+omrFiF+3HWVzPDCt0Yr9BC/tgdATlIiDRS8LYmJR3FrW+p06iylLokBm7fFg+cOR',
        '9dkhIJudvXK7RH3fSI/y4mpK+eeRqtqCjWeJz4FCMELNpe82E0Ymi5gVPeMH8j8KFb729ihC4GXC',
        'PskvU+5/fPTx5fvtiK+gSAywCzC3J5vK2+5sUlH1daphzZTSzk2G5T3jiscu3Vy2mNaatBd1eqv7',
        'LqCOsn2aSqDVDHQm+fXV/U31XbxWV5qfCeYMB4N8SUJXFkTrnlpOjc7iqJ+26DVNVQB2cHWMXC6o',
        'QA0jMwV14nXKT2FWvWBEg5REA9WJfeg3ueA6TquiEuucwSn4hUHi5fxAH+Hg2OyIC1Vq/uHnbvDn',
        '42y+gEp+uRTrpse/YF+2GIoSyuT/jn019GZUooFNiI04pV6WhBuCA/Y0eMOac4mN6QchAXJTntsU',
        'ok3W6iTtqJDxYlop7puHCr3Tf5w500WNf/0gD0/C9kTFiL1aucpa7jmkUPlr0KiI8z0sZ9yM8925',
        'Hu9rm5nBg0aWf9gZOu4Iz8BLXHYf2syXPYG9L/V3J6QoyM4b9MtpLxdlU+JllnazvYuHLbCHVJwQ',
        'ira/nv9H3f9m4qf931/4N73/j5z/mp2s/w9y2v8dC3+jv5kFGIuFwCUwCCgcAYOCwaZYM3MYVBwK',
        'h0DEQWAMFIuAYsz0/vUa/6L+B4uD4f/d/wcHQ0/r/+PgcUp4jEp0riqD8tZ+WC2/lP7Zy6wOmhrn',
        'eS/q6T+XFrHT4FPKGjQ+rFmdEZkYHzNM63jCfDlxx9vLK37uscCuhbR0mKE55c/9Jnry6+Z5fRXk',
        'eQ+Tv+4EhXihI7rmKAB59g7qnr/VGkrIVAzJms9pN7XW+AlyM+CHVAV+WbH81mz3IGp24NRnN3FL',
        'nQJqnQ5f3kp7WLfOZr1N3BMoyTWj/O7TClBj5kSnZPsJ+nwYdLdsxKxT7vgubWSvH8pPOzzHLaeA',
        '9dx/H3xf7tf0b9u5TEvGgqKYncrmJru+G1L8rInOSVsnpctFoqfwERVCWm1bVbQ7M4F5J/VmY0Ld',
        'g8W0PGmLp1OSGPvtjIjelXVcQvK8oAp3fR/augw4+OHgrXJDf7GJqJcssqWAhz49sq5wQrQ9r/Pi',
        'BECmyl9AxSGh4kkBHXLmjtswrMBLbrJgT29MT1aqT+C68SPfsAJ5p/nlfS51n3PDDWt2dNaWF5sq',
        '0UtuziCkoo6JZ7auywS7lRoptrL1i6gwwb5wY8X+Cq9iubl9aKZzcmIfC+GtRBH+1sJ+b0IVfM3U',
        '7KNzcgLnQIlY2kw+r6Z+54B+u4SpBffVzoWLaLvM0DGGsbiltL0SHMBW+Iu3hQdmSbbyjrEXRzxH',
        'AlbLn92ATILjpSnzc14bVlbymSpWco3tCT+qFHWZ/kejBALv+JxTrCqJ4Z0LlYVE9vJFjmc21pz0',
        '6osCnqgvn/0pJtNxMfE1rzDIlaecVc3TMdQzJIa7TbNquAx7WeeX8bUvW37WGzjcZjEiClVqxJUu',
        'mqxlmH6QZExyNTMyxa0TBQfhJeWGt8ejQ8y059pgOt9uJGmYTSBFH+OrR0ok/eaTbLOzwGevWWRG',
        'DWq3xge7thdN807E8lC6XioKkXvuSbox05lwOSNmIzmVaReZSM+xNbKa9ySUObYnvFgdSYouyJ6A',
        '3c8JghPJ8CbmxVn3bOrkCCr9hm4hnufRrGWukS/vPBdoK8rzIq6+5reLG+7M1bAldSnboy388A3p',
        'e+05RYeMPr51zM1y2lPG+Hfahie9G4+fI/MfJ3T//6P+O53/HQtH6Y84af8H7FT/4+DI+e9Jvf/C',
        'IafvP8fI3+gPgoLFQVAIVNwMDgZDLeAgBBwLh0sgoKYILNYUBjfFgkyhsP+D/A8MjPir/hAwBHFa',
        '/x8L/5z/STFm6VLNz3iX3WODfD9sX3GQKCEiH2Rafu5Nn52G9ZvYxK/fwsufRuPvDQVT6kfc2WdO',
        '8ZjCN76irgr85/jPEhN1GeG7H53VjQ09B6P9tMbejbqnWytjdNrKAZoyWYLgqOcgZQeGAAb9ucyg',
        '72TXnO0j+ktRSkRjVeE1aaGZdpqJkKI3kSZX5eJi5p+wzG6XaeQ+tJsy439TT/4s0W32QgLhXGYK',
        'qm7CCY8OdSU4TDjfWaEuGc1WSXdNpa8l8FzqCwxamliRQqT0wLlndz0rt2pS4VtR66+l6zdIPSu1',
        'yz2yGxEuDqx5Arrv9QjjTQLPvX4LuIOmHMLv8i9YSbm5IFzKx7Jc96VibIaEDj6n0t3fqS/lFrvT',
        'LWMOzVB4lAHmR2DpW3k4lNw/WEuYy3Q7yrh5eLMr2YHctoHrM44p2NelairbYOnalZyQ4BqobNhG',
        'rmE+Oi8N6cY6ZzNavUYrOjQ8su7kqf9DgjMclRICTDQYkO93kRuVMb/ND7g20Dy7KQik+4xu5Ipu',
        'FwrjZrFFDPdqSdLjyqtl0b3Co7Q3bq8J79NMOipwMdSRGQk3xZKchleUo9tkH3rCH34dBHlHEmWM',
        'y5N9Sq0fF3AVsz9qsss1UH6F8NB1uPDKQ/Ixw23B1efSnM8yrxY18jUP28wQR6MOEjV8aVs/lCdq',
        'EzTFXtmR5JTWKGWCJ6fm2UgXz1A0a+lM0x0YTArEvA4r9SGzLQ4+HKDMojPM/+wlPMWNklJcY6kD',
        'yk6H3FzrdSzVKFw2q/ZCqX5Tcf2R7GUA9WwoNMAKW3nw2Z4Z3DRIGu9DZs7l8RHfcYIWAjPFXB2Y',
        '2oGqn9SzSXHhK50WFo2A+HDU290FwuroW/tkde1yu+QcnylLWkGip7pIcb7y3QewIYRapOXYb9RN',
        '/sAYNYBvm9zNGco4vBV95z0+JyUpE8ZnCkU9Cvk1+He2+g+ghT8L8mo8fAXnHunu2r/9nbVioSga',
        'N95siJ4zxCXhfBP34UnLh8aAbdq0pq0xBpozhwAjciqxCslEv8BeWw0NfzzwrSNtSqf7OGDvoH1N',
        'ZCb0Jtf2amkwpnGj7pCihHSt1e3JNQr/Kd+mYabFQ/px+hKdk97O/2OOPP9Pdv4LhZ7Wf8fCUfqD',
        'Tyj//Y/+D3Kq/3FwZP7vpP1/4FP9j4Mj9bc4Wf0Rp/3fsXCU/hIn9v8P8f96/zs9/4+Fv9FfEmRq',
        'bgrCwKAIcbi4hbikxJ+iwMyhWAkLczgEbP7nBwpBYP/3/j8oBAb/q/4QMAx6mv8+Fv7T/+drx4zk',
        'aHSbXCwfryknXMLd6+hWURYE1VzrYKWxy+SiEF6soOHTjyTWpC3i+2sj/E0hAc2MvowBfJdoBPZf',
        'azKJs/MwtdEAeNhBAXIa18HyAWFJfsrym+ORFW6V4f5g0a6V6DLvVZfJ1S3vyVqfCe/KhuE4i7oO',
        '8AvFkmSWRJsAAAOAFXT+FkP1dux981vf2N4sXDc1n2WC+pTp85hViRKvOl5V7WcvYxCpo1oFUxTF',
        'PuoVu3XmGr2Ld0vSJaM+hmQmZme5QUb1D7VBUfd3ypxY8wgPwnZd5vK8Ll0pBly56xri8ZWHjW2I',
        'c/ziJqJsJ5G3+xxJOLEZq/YqUfMyNlc+2OvL5XLk3GgVWf7KgFbZAvkL9NVvOtrEQK3ASpi46C/L',
        'VtdQW089MPvGtCZh/xmpeUEtKbdTa6H5LJg9WSD5fPtjGbQIr2LiRwv/jwvPm4rmxRV4WvBpHW62',
        'NS8uEXJomSzXP8APO+Ec5uhK8QD83fXpfGR2Ras4VZIBIeBCgolHzUBeRz6NKGqCwfADkFg0oGYI',
        'xURanlWqvaIaWl/wc3kt50KXWcdPUWEF0oWNgC0eZllDrVlJbgIA15U2l5iDHpPCAertgLFpGTdc',
        'wutF3ZcW13WiyzydRh2rsWiFBrsrvi7LOpw+2dyKTatrF7zoVlvujYExLW2+/tXiOjcGo2c9dhRV',
        'aVTSKdKf9NYojVn7h5VcEn7CXaeywZh2bs8m1xaLHEKOdKfdvOWuAnnB8L6mmmbyQXWSuq6uNBmf',
        '5JfW7bA7a1LBEQIj3diPvyUZYi4Kmg99xnPIDxzeo5ho6TzkiGiGVYHwz7AtQbTPTd8pBsLpa8iE',
        'ouy1iyVec1hXUHSSR2vgOcKanp33JLMOZaptUen1vSJ1rykURObScWWW2jzG1ayl7ZJ80/cs8BKM',
        'RkAnxrK0IHXEqIG/BlYOODSfkPU8W0hjYGSfKttX4rY3UjhihkRktP+Rqiae5r19UPJLrJ5x55Hx',
        'elmgkrGG4uHEb1zvoMa2ClMxO0Vaoe+HrVax9lX11Oo9+W9DCz1NB7Bgy14G+2lD0jmDSjZfca2m',
        '2DL2vhBK3Sk/ysjVlc0Kan4fxGJ93haaH47ykGK+7QL8Nd+w01YY2Iiue7yQ7X6/HvrrDmhi32QK',
        '32ZN9Kx0JQqux2lDpT1qrwfoPx5BzXY84VLpXGl7Ee4CU+ZDK7b2ahk1SHVO2aQMFRciyUHAPqwA',
        'AyL0wFDsVXd8WuC4GGs6XZ3DpHF/jAxu99N9fBdfZNlNXBY3m5kfxiWn8DbqAU81u7rbsiMJV129',
        '1V+YfS1RrpHrd6gSDm2kKyJ7+LBPvonxK1mq4+URsqIWuFoCCDPzW1MNw8teaZz8mX1LI+8zO0Je',
        'H0v90BRiO5YukAizl+Uq/UgKTPFD1TrZdvyQK9i/8Jj/hzjRoaWtsSXqZ/mFHCOlQ2WlAYWSOG6U',
        'zZ03rVEVYQfhb/x0DhcP8hxagp46US7sOPycDHq6Truwc//1rzNS9wsB6cFfzIXdaW5cr1MYxVst',
        'Juy9YP64Tj/9vZtdAWEbc08kgdhIy/yNDUv6c5ltJU4Ovvw28gDXF7bT7jF+mW3kOnJ41PgZkmuE',
        '1pAQbwZXCUDsiVdo1MO5BboPwF615TtZM4D4QWRFYtJdY2frMVdkPDsFW1U1QtixdIXbuCJ1Um4U',
        'i/Osf2ccVRr5bb/asUaI+fw4x7jMebY32o2m4mhdZZRxTY6wNtEFEtKnrkY3v75v3B7NP6DrpkKL',
        'N0fSee/YlW3YD8XvQNzykD4AJkYJMGFELKQiCcH6tRN0fXhkgTumj5PDUpPrh3FJ1Q4fryTzptgG',
        'Nalb/UD3rAI6DtO5RZC6GzF3s7v0ywrrquMfIdKcG5efjMlsAKcB9xkcqRqCIj8xr8GXAh8WM870',
        '/RGuQeXmn1qn6iOHL0FgZrCOGiBaWkMgE90vlZy07nLanCHTl8Ku/g8eOnMTe1fls0PuOqfmXEoG',
        'dlDfD9HWgXDTC0F1giIxvsBdL+oYSz0ZvVuFHzUZ30pT2xAvxqXMGliNnM83csSbhKn3GHb3WAQL',
        'duoMDr7z0mfXMo1poxOZ0gRCUJIb3A8zZ0H+MS97N4fOpAjIJnSqEhufydxuKlpjSQlqXWNf8iP/',
        'EQgCBkuCWOg+NX7q+toU9PQ95U5fntGmqEi2UBdKgFaFf9droW+Zq39e5bYdLMPN6ipL89wEebmV',
        'QP+8FbWu2Fe0dXje6Kjh2X6DOcbSpOxz1NYTM30r7pc9ea9GzWMG5RaCBB+J7NE7K32kP+nb8t+P',
        'I+c/J53/PfV/HgtH6Q+Fnc5//8L/M/1BmBPOf8NP9T8OjvT/SJyw/+f0/D8Wjsx//Ad75xXVZLet',
        'YUGQojRDE0VBKYIRQoAkFKUJIkVEOgEhIQm9KB1EuihVBATpVemKSO8iIE0QpYjSpCtNAUGkHD3j',
        'nDPOv8eO/77Y+/tuvueWi2TkZa0115zvnAvk9Y+G8v+AQE5/FEj93/+3/qH8LyCQ05+EBLn+B8V/',
        'gEB2/YNb/xUXh+I/QCDb/wX2/Bdo/hMgkF3/IMd/UP8vMJDVH+z+D0h/QCCnPxLs8x+K/wGBrP+L',
        'BPL9H1r/gEA2/wv2+of6PwGBnP5EsP2fUPwPCGT7P0Ce/y0G+X8BgWz9B+T6P5T/BQay9R+w938o',
        '/wsIZNc/yP4PCaj+Bwhk/f/gzv+H/D8AQdb/Bbb/A7r/AQLZ/B9I8f//9n9B/b/A8Af9cTgUUgIj',
        'LoUmSWCk8EQ8SQyHIRBIKHGkhBTGgoRAYFAEwr/j/U+0pMQ/9n+hUdD8d0D43f/laAcjcjZspH4w',
        'rSn2HWCwMe1NOvjM2NzFNxzLlyXfRG2VkcRyx+PYF9dzA2VZ7ilKct3mma2+YmICfObhFw5P0jTl',
        'KvBfeMmUwGlxKPNAw2ZTV3rTJkV4wpy37/DoCGqXr5X4/qf6cH3P8uiGZ+pG3c6od3V9oEx5R/5L',
        'y7AcMe3pmtO3Fe8Yt62yKnuudF7sb/zEYVHC4urskasn5+xqbFlZ/J7Bidu4l+I5i31t8JuDVNqR',
        'DOshLPwcyL7sGzxRHLrMnTbaFcETJlWIfFqxybf8/dczDA8mfpszmD60xJOPr8ecDA/jz0tPZK/h',
        'TbnDxL/pP9X68vItEvp83JTmbUf0ad7y+2hsSvTjAQoOG2uFkOPaLT6e7Qs2txCPVjM2t4ruJiaV',
        'aPRU5s4EcUZKxw4lqz6ZOqNrGZFPldRkuDX18qqA6OS8sRB/25OxROOG+wZTgnzqd/CtNnnfElq6',
        'ZlpYwmIC7ogy8rsx4gRp2p7TPtJ7IF527ZR+4Ng9NUbGnMmE8baLZwpeOWox2YU9dv+KPVwDh1Vo',
        'YA4btTzN8OhMruiv5RYr1d7g+6RYVGQlXCy66ZGHEYnTNbpqc3a4fEEy1fFEHGMi7CHz6nQH6+JD',
        '7NaH+20Wp6JFEhnsHVLODA5f4dW3nWwwQU+2LMNpWbcp5F4YONFHmgf6+Y03NAbCXRaOiF6C3Sgp',
        'pB1+fJLjoK0Di/tAU+hAbW4u5SjfdCGciB8xHbGV9q7xWnYkpZ81EWbXK3QQOqPltgBT5x5h2Ne5',
        'HYcoEeDKvEXECEbc7R8qOUA1IRC4YDKio2W9JafY5G34g2PcuZJernsjk4WZ2RzuzyhhsWp+FEf9',
        'zJ7e9ZaV0oMiP2s/Ym4fdyJP6DHvfYu3n34Yfz4rJ58Q4Jw+zkRJSckg09SDr6wxbUxnuxZvu+Vq',
        'tprHMXpzcUeu4ZDyltao97dPcc5tFWlM3+tcF+NbDcyG5Xa+DFueN/QRMFVdumxq6mU2VjKrIpiX',
        'Z8F04ibuaKmXBx5ekDDGevX5/BFcj+bQqmXdB5SpxljMx85inwXjHx1HL/eMLe46VT/uOrezNJ76',
        'rq1N9JhPzvRSCGezKzrmLPvPe0vM2hLdPHJucy5Oa07jTzrb7+5k9SwvLjt2mWXt191Gny0ZcFj8',
        'weR0O6Bzc96XVM+R8uvXRj0Naqp2X9JIL31tGuzQWvihSpJ5B8YMN5xTc7fhIhEZdmTUKYp2K3YM',
        '0vz8zN/Ie+/Cfv/5slWP7wHTzF5+L46dkzc1mfpONKF1XcrrRR5gneRuvDi5y5v8sZDapXRm/h1l',
        '3h4dLbuo6o6W/FOPieC3rBdT0uf27WBqw0NXhFwp17vd9dGNrFhH3TKVu7m5mcJn8mUL3xlfw2YY',
        'Tv74PoGvSlfjxxKxRVUL/PHEhKLPSNNxykHnLMoKlMmd/uH6vBoTk9NcyjqhqT4Us6JEYb2HnxBf',
        '7FedTN5d3smvUjN6CLOIe1SkUdbTPv8CJdZtyvfNl1vrWseNDCdO7k/himZyR+1rnb8jzWYdyjpE',
        'SaWh2QN2P/GGBZ6W/vZBVdY+pnS4ykhzuaSr8y8bZ6YY1G6cTxmKVRW8f/RwoOztKrhz7HKF64Nj',
        'nf5GX0hKMpnODE4fr/PO8Mhe28IKy6fljNPfZJqdgG3Ba/eHTBidbs5C7KvrPUCx5ejlVU/UkkfI',
        'BLgyK4TG9tAbSYnSJYqUeaHmrnb8fHc/6MiijDtin7aMDifD1lmTS1xCsy1Xs5hO6r1mVi/lkM7X',
        'lbz9hGI4305FRNILvp/B5/bnjLbLuGlri0AHdfPVgDHqTtMtia4FymNw9AzxkcpPw7PZrZx3PTkN',
        'Zwbsj43RxIWprJCMO6ttkqJ3cg+3EMq25OTNssfdNynq/d6ulBfvXnrVyO6xb5hXdU/JrJWblpad',
        'cU3piVsZTWGykJDvlQ8ydjYb1iEOlrkjBdwlZbl1TdK9zMmzjHMKpXl1o1xDteOMui/tSbP0mHev',
        '51ls8ZaB4uVV0ovJjsp7GmUN0Yxzq06f074336iI2+HIVaZd/mf7P9n3n8Ce/wL5vwCBrP4g5f8k',
        'xSSh9z8B5A/6Y9B4C0kJFBItZSFBwJOIKDE0EUcSt0CLIxEkNJGAREqhiMR/4TP+Jv5HIP7f/Mf/',
        '1v9X/A/NfwAGj+S3WmrwvsFPlPtoJShegf11IACG7PuvYM//gvI/gEDW/wl2/h+q/wMC2f5/sOd/',
        'QusfEMju/yDf/6D5f8BA1v8Jsv8X8n8BA1n/D7jnP/T+C0CQ3f9Bfv8HDZ3/gEC2/guy/w/q/wMG',
        'svqD3f8L7f+AQNb/B7b+UP0HEMjm/8Ge/wb1/wAC2fkP4M5/gfyfAEHW/wt2/R+K/wCBbP4fbP2h',
        '/R8QyOlPAPf8h/w/AEF2/hvI+kP+H2AgG/+DPf8R6v8DBLL9/2D1f6EQkP4A8gf9MTg8miiBQ4qj',
        'CeIYCzQCR8RJWuAkJKUIGAsiCYUmihOI4v+KR++3wH/0/yEl/6o/UgyNRkH+PyDwSD6o1i1Te7UJ',
        'XfYcIxlycJ/AB2pBsL8UBGCQ9f+AfP5D/d/AQHb+A8j3fyj+Bway9R+w+/+h9Q8IZP1fIPt/kZD+',
        'gEA2/ge5/gP5v4GB7PxHcOv/kP8TIMjm/8Hy/6OQUP0HQP6gv7gEQQJPQCIlJaRIaAsJIoqEkyBI',
        'iWFQSIS4FAEhgbPAIUnEf8P732hJ5F/1R4qhJaD+T0D4Pf+FwU5nkO3WRoqlnFxbZEp2Hp32UNgV',
        'FbgOXgb1cjuc6tNVKr5xQqZVwUfLBstZFsHLO7DdC/0u/c+1xYXfXLLvz6Sf6xV21WkWrn7A6Kqs',
        'ezZlSAVbXpz0vMENrrHQ0D/UuvTz+vZomFLNJeyrrfM/ZkeXU52Xqja2kt3tGvztk1oCGh89q2Ip',
        'bnQ7zOrP/Kb7GZUC5mvpg9zG0LTYR1F3om7HVC6eGz5J1yyZt+xn2xu+pFCXu2Fh1QPbtCqwtIed',
        'fKl9bSilQTAQLfGa7aBR1QktRraLIatf/b45wODPBXvDBWymtKPbKE38Owlvv4qJCIazv0t19YEP',
        'i3MS0d/XVZYJlqElOz6FHU8X6dmY4MR+/V5vX3Rv6gxprfE7Zfwr2vzcLZW8VhrvAftI/a7wlQmx',
        'YgQr07gRTdbRzJIcu4f0+rnfCOKcEV3M8ZPiJ68VP+hgKvmC2niC7ZhpqQsghcKfLLK9ZNFEx030',
        'cZ3KOnb8WPio1J2IhSPRpCveytFPVHPKuN18fMcazQtpYAtDTp4c74tKIrBeeJkhzTfcicG27Avr',
        'oypG5851lx4v8DpGUYZ7H2KlZXsmcS4afRMezw27Qa39ysA42+HDGdkP5+EL2hzI18TQaf7P5wZf',
        'fUluq77mKGi9bmeV0j0YaiN7xXpSS0TpkoKD/0kK36C0F9SYu0kCGgmbjnhJBm73iOmg62LYrrac',
        'jdoEbk234ADTDDZe38oC9ZVVofMtwR6Rk7AzlHlOHp58c0ybP15fuZHB9uA5qfImb6TO44TD1OeZ',
        'Px/6ERj9elqBRj6swA4xJB/QE6fFSHfq4xqcgibg0YHgQyIHP9AJ7abxjrN5HBgNaHghOuPlrJGA',
        '0GaL6PUnZTNUMz7gMZV+Kp1OHReSQXHz1b39qgcSOYOdX1x7EXmVqpbB1o/PjF99CrXG5HIgZ3y3',
        '6trSHlP5B1P9QwJBfophKShs59JkapqGdL0ea9rRjZ2Vhfunoqr2UP3oeudKl43hMteHTMU6UYIX',
        'OqqD997FCYqeOL5j8oVyKzkwAbP7/hyNTC4vb0x7qzvR+cgtyVNHVaguXc2lOLrkO6F4s3lqXXnM',
        'xGfOpdOfJupKzMuME/HGo7XuIyEGx3FLNRrVd1PHLunzxHW32nFVJD4th1UuJ0tXTFf1JKc93+HX',
        'rSa4fInc//iWq0tLS98QOtXr3NaFeZuteLlVmU6HCS/n00lhvWxRnBzlUp7zn9/DsGYFZj/KmEQG',
        'fdZ7L2d1W0cpshQorG93vEqSG6vrKtvDzxOyJ7bbjfT2s4QpuJrrdaZOvMhqS0uVHpb5tsExlqon',
        'yZlxKP9Bict3Yg1x8XO7d5TsXsjGWJpemTOblW7OCDqUy33MWF/TSWq75OmlpBMF2kz+lNO0DS4L',
        'K3fzQvpMlObGv2OVSQVv5u7X5L01uOWsuymB5ZL9OkvT8DO47smC0shQ8g3GOjqxkcinmkma17vu',
        '2nNxZ+herO1bH2O0p6r/nuYvsRAitCltF4gypA6ZRZE0Eiov9apO8KAf2dV0ca2ePVXaKmsTY2Qh',
        'kE0cSJgqRNp2mbE/lHwkoEaKoKMkDCSo2Dg0wS00qNS2Q+g2UccCA58exNA2P5ngMvHTdK5bFND4',
        'GJvL5ap2HtHKoaM3oB+XU/BsIL8wR1hdb50iv2PgSmx3q2rDXvQKFcVYm+ivf/BKT2aerxz76RVb',
        '1avWerBohQXvEM/d9p3FmJAnj6N+tA7G1nvMN87G8xbis/LzK9Fy23h1a1jKQpfvcpqMaGrEntuE',
        '9fyW1gspSrPVnu5FvGYC4msaLjpZkBS7bZPKw9Eh5Z+HgpvuCa2J+I/yu98z8z9xb//UQafoFb2k',
        'FlLO7Eha8grd7qmPsoWeLobOLoMBrHu0ozwrzUF0sFVOhQuSo8pu2c+MblGPxa05R5sb9J64sxl8',
        'Qfg0X4yCP/b4PL3g9t0L8uP7TQznTmZvnWgKobr1+wl3GCUX1xEhHcwEf4BAKr8quurgcUUjj4ce',
        '7EXJtssOfnQFpnk2cYfFzMbp8fU8AqtxD3QMTut7lJ97eDaSPSi5+FlKcdUaf7yKpoFrnoCTP/b3',
        'pJ/M40y6w2WwMLvnbbNKMM/3P7UXGhrjC57VZtgOFjUjTo+sYzmF4+OUTNI/vovL25I8ojnHLvBw',
        '1WvPoEJwzCbnnKbmHIWx0Nq0d1acgbHZTOfHtqJvpXm6107rbm119+qvtlJiH86c+vLlG2uotWi5',
        's7cvvPq60hiD2g0Ue905rrDvRVGMTzk/l3ddrd/DbtyfdEofQOj3oMbs6G/DomgOXYiIqdWobBfn',
        '+3HKg2k0wKBd3Ik+NSBk4rxqM8/aC6TVgcTttyYhdXQwvV4rCtE4DmSeh1oX7XWh09roN1QGprTD',
        'nJ7ykWz+bS/csPUJp6c+j4zrGFMOXKm/N1jT2BZblXxPhFRpYnrW4/F0bGVVCx3323dr4WWMhrJF',
        'XWFV1VwRmYfzj8sErbMyKl6qSH2DyWKstOslxfgY1Dm/7b1NUHRlsCnqiz2chZlJT1eOblx9K+LX',
        'kvtsU7bKL4hRrebodMr9L75oz7t+Y80yK9K1VFrNFivEIQZZuwARqxCvb9JSwdQaCQly9xy9+kMq',
        'sZ1516fyaUT7QjyKXGIWw5ndq7Pv5AdVKhXkUhl1U9Jm2fA6DF4uZEQs3bhQMPr+423mx6bTnPrb',
        'zzrEPyNTWjLovDpyojzvDeIOefYRVcP2KF87Kxr98/OfbP4PtPef/mf+C9T/Awh/0B+Fwf26AlgQ',
        '8RYEBAZNQKPFLaRIJEkUhkTE4H7JRUKi8JJ41b//jL+J/39Jj/6r/kik2G//LxT//+f5Ff8n9Ni+',
        'k2dts/zWNxJhKAtXn2s3UivJGElciHLDKpvY5vYbu9YEsKlplJaO9I4soNrZC2jyLjbtZdHshR1f',
        'E2WWpf3UJNcYKMhEelbULtz/X+ydeTzV27vHK5lDSVQk51emTHvem44kESJzSuHsbe8tkjLvUArR',
        'QMkhCVFECY1mKhkTpUEkRJmSiqSQcuvef273vlbn3NfrnLVe93fW+5/+Oi86n9b6rud5Ps/zSIeH',
        'pkg0EEZJ7xo/HWiQrasPU3yWlfHA61mmhYjx2oXpr8MVwjPSFK1mzzaPaiROrpSs32MiXhkQdytI',
        'yLNyS7cwe+R0mlxlwvyX1ZbW8kuqS0t25hWeX5+75KJhtN+zQ2LzR+3KrR+Z5Hq7mChfEWSXuQWa',
        'Wz9a2qxkks7x5LG9ijO1BppN1tRNlffGO+RLbjIOeCRk33lJszPX1v7Okj357y6qnFu7xkmuus5/',
        '8MOFwmaRG4srhyZSdnlW0cjXW5cXB4nFeiuFOS8+s0uvNYnqtXrFtRivWo9HfNHEYs2OAt3Sl1UL',
        'PvsnCTDbOuq2Uc1rz+TWWWad7m1zTC3+uGz3R3Kgb+eZiP1lifKGxcuVetQS099znSJqJs/0M1Rd',
        'r/EKJZXa/FY6Dk7Sxs4eJ8T4spVfJvn2r+4peDq45MYB/f1XxvhIfM6GqkLCu38Jiquva24668U7',
        'HblZ8NLc4APHhjUX7mIk23RlCyW0lOS0+wWe9tr08bFLrEBdFLVga63Eipdxho6u9e6Z/CeLujab',
        't9FCBBpSawmxb2XzVcfkWP0hSysEWyN3Dk9cfz4rslJ73qKiqJCLsiwR0slmPuPK2V/PCQhJRhnv',
        'kS2d5r88t0xYRzxy9NLmudbvTy3PrJn9/I120sbik1oyrofdL2zNdC+32nmyusTtSYvrdaMLFN0k',
        'e2q+k4qDRICThYNuy7mjFp6v4pM4ttWqKfyLpWnnDRyf1fNvX6/tp5PlRs0lJKSmPDI9ZiF5M6r1',
        'i9atoNmpoUcru6cEjRVeXm4VktFP+Gqr0fpFb9j+WGV38qLWL/fv60X60CZmKpxcNY76uPzbAbz/',
        'Uef/cf4PCsD8P+r5T7j+BwVg/z/a+T+4/gMJoP8btf8Dx39QAPo/EPd/k3D/DxSA83+Qvf8o+PsP',
        'kZ/oT3GhMNlsKodBYXNoTAqJQeKwWCxtijaXrE3lchkEDo1G/DMafRf4p/5/GvFH/b/P/8X7P6DA',
        'Sx5dr2G/a4NGvehos5/oRhVTjTqte+p3tLJtje9qWauYaGg+WC9QP6joumvmDL3F4u2of2HMXwrQ',
        '/4Ho/Uclkf8r/4/7v6DwE/2p3/u/GGQqnUqgM6gsApVMZVO4LBaTzqZqc7RJRBaRS2H8iZ/xh/c/',
        'kfKj/iQilUDG9z8MeMnr797vnzWD78FMC9S/CwY+oPPPROz/JeD8LxSA+R/E839x/xccgPMfEc9/',
        'xfsf4AA8/2jrP7j/FxLA+g/a+i+FiOM/KAD7v1HX/3H+HwrA+Z+o639YfygA8z+I3/90HP9BATj/',
        'E/H8fxz/wQGoP+r7H/t/oAD0f9Dw/Icf+IfpT0Q9/x/rDwVg/gfx+cfz/+EAnP+M2v+N4z8oAOt/',
        'qOe/4/sfCkD/H+r9r3j/BxRA+vvyPP+yfwD/F/2ppP/s/6Zj/xccgPVf1Pt/cf0PCsD+T9T+D/z9',
        'hwIw/kPb/4frf5AAzn9F7f/A7z8oAO9/xPrj/A8cgPl/xPUf7P+CA/D+18b93z/wD9PfBW3+H+d/',
        'IQH0/yGb/4fjf5gA73/U9V98/qEAvP9R73/A+V8oAO9/1Ptfcf4PCsD8H2r/P87/QAGY/0Ht/8Hn',
        'HwpA/y/i9x/O/8EB2P+B2v+B4z8oAL//qON/fP9DAej/RPv+x/sfIAGM/1D7P7D+UAD2fyCu/+D5',
        '73AAzn9A3P+J/b9wAOlPRt3/h/P/UADuf0Bc/8X933AAfv9Rz//C9R8oAPO/qPN/+P0PBeD8H9Tx',
        'H37/QQHo/0Tt/8f3PxSA+iOO//D8TzgA6z+I8/90rD8UgPE/4v1P+P6HAzD/h/r84/gfCsDzj7r/',
        'A59/KAD1R13/x/pDAZj/Qe3/xfE/FIDff9Tzn7D+UAD6P1Hn/3H9DwrA+T+o/f/4+w8F4Pw/1Ps/',
        'cPwHBeD5R+3/wP5vKAD7f1HHfzj/CwWg/5uF+z9+4B+mP4eAz/8P/MP0p6H2f+D3PxSA9V/E8T/2',
        'f8MBmP9F7P/B9R84AP1/aL//2P8PCeD5R13/xfpDAZj/Q9z/i+e/wQGoP+L8Dw33f0IBWP9Fe//j',
        '/i9IAL//qPe/4vwPFID5P9T1Pxz/QwH4/Ued/8HxPxSA8T/i+V/4/ocDsP8L9fxnrD8UgP3/iPt/',
        'SDj+gwKw/o92/wve/wYJ4Psf9fxPnP+FAvD9hzr/j+9/KAD1R+3/w/pDAej/Rf3+x/kfKAD7PxH7',
        'P7H/Dw5A/VHvf8XvfygA73/U7z9c/4MC8Pwj9v/i/k84APN/qL//+PxDAZj/Qz3/B7//oQDc/4gs',
        '/0vB+V+I/ER/Conx7RgSqAwGh8DmsrlMCpvJZLPoRC6VStMmEYgENpniYvDHP+O7wDQKBaQ/iUYh',
        '/6g/iUijEWcoEP7+v/4/Xn9e8hsHcw9ZQ6nTTwudnnlNzc+qDlfZlm5NzduyWNk/znbqsUmh3bMY',
        'S+Um6Qs5tBEdpxeteQEdNMM1GYP8VRYScnPlihTED8o7MGbGpyV3ha7YZmV1d6ZwRrbCXEHbC2sI',
        'rzvG3uqYbBGniCRavH/bQCe/K08cS/oieb/qq5Xh7xPBxTrKbsF6rHk12wTvLwuf1TN/DY3xnrnE',
        'L+pt1Iy2yWst6QERWwLLF3jqK68KDXiREkAMjFfo7mUdC/gtI877vdeY8XH18yrN173LNTfIuJ/b',
        'bGe2OCB1aKQmNoX3i+VOO++MsAyT6vdRm45ne5uQt7cbb6wzE5k6fSXltv5eL/re4Cj6ohu1aVU9',
        '0p3qY9UWb2fszLk2P1UnzcTG8OmQe+emR+NsLs/2cmND78MFJmoHTV74WbdqsAuHpZtdbU5cM4xK',
        't9DP7jsskpj3u7Xo1PDA+LC0k92GOnGZ3uRrmao+R91vqDQyouKWP2SoyiUvN+rr95Xg5mb2DDmR',
        '74nokVbRiImNc0MvNThcrNNL7BhdqxR08LmynLdZO7GbF6x2t5qUcnfVxlGv5Y2h/buHov0CXh4w',
        '21Oh1uxtmOd+OKdNsSZ/iHb/U0P9aN/ao6bVp6iGAT1Hv+Z13WnllaV2s0pLE440y69QekOa6Jt8',
        'qlAcmBbRJVEk4FwZmWGsrz/i/GKehMfKrRfq18V7my8YK82OW2+tqcuIPyXfYKEY1Ls1j54qP2Q+',
        'lilgvWdi/9SHJsvA1SceTdi+41P3GNQ0zVaKzbW5o+atmThoMfZRbCxL0GFmye0TFeyVfIcu6zjG',
        '9GdEJLDVIo76hw0FMTpCP0zphXWJMvgSuizGvc7JRIaGskxyInfwHhR0u4WZt+YvuZzbdCCXuy1y',
        'tmcb30I933GxDSHT5F2zKjjeZ9LSitbxIoR9Zn+VEpCuDVv1/Pixe7q5e6V/1fN2EHb+dbjTZUj7',
        'w2S8fcmNPczxPq2dk85le5m+7/JdLb4Mtw/fkU0tG7kXwyPHOd9cpFH2+fHUReGC5UuXvXJUvXh4',
        'Y969T7+xnvVXHmRuColp9SyoXmvZZ08i5N2cXlQU3iVQbGlpmbM3x1VbnL5TuUHn9mQSfday6ZA1',
        'u45sVVFR+Rpy/p3gkeUz7k4G73WcLv160UJf/wo37ea021cbyxzzgXch0s6G4yt8Du4W0yTNb6gd',
        'FjNVb26p3W3fcSz5ckSIUJbE9gW6OQt08leP6fFXt/E3T0tJbZfRZxwrG1Bn/auByIi+O6ytc6Tw',
        'kq9tYcWsFtZ5B4N/ZZ/KUM3ONLvorzogefib+GYJ2xXN80ridhj4FCsmuJxc75eodPCWWPuGrrbK',
        '6gxHcY1fB0ZrJIXL0p6kax0f700+V/840/3Idnnz6MkLzrm/3akPjbt++JuIp22uCbdU+fqMjJfr',
        'nA5e2h/t59tl42Qw0HLfSNb+0pa+luJSu4neYKd4aulbpzlZp3ZKt0YkrRzcfvcAU2fD0NDi+yJB',
        '/sc6jGJHNzMTYhbUGIULn7619QSn5/NNnfUbHu7f8/h+9UflE7JNH6qq2fPnCZESrMKL93XRi2Y5',
        '385q4B+f0RmR0K0iF1F+eYl8KK1Pq2/SNEuXv/rhwUCFo3bFJ2VGTa2kx8razQb8Ux2SHmaTmIMh',
        'PuuWxWw7y01nCtrHu7obXmiTDuWFka9yHOcWUM9H/G6p5rHtsva5NuldcyiSlCez3OiG1hZLZcdW',
        'yiWJMZd6fGjuZxrJfLxtFJJCMBD1KlmXQnghcrmJ46vw1uA36uIbLcTApuiSV4woDWWli6+nhUaX',
        'hD7vDhoXuVf5eVi6bHbtL4Kff5sbqFA/1diusERFZaPEYKRTxx15L2+u0YH0yc5u048xe+v8ra4K',
        'Vl6euTK2vFm7ybjo1peYmvWKQUceXXnadeyW0ZCZv3MYwWD0iW7bnXuGMjt85L8kX4qf9ki/OU/8',
        'cf+D5Gmz1jYbs49Lg1tLi/7W+x8Y/6Oe/4Tff1AAvv8Q539w/R8OwP5/1Pkf3P8FBWD/P+r5L/j+',
        'hwLw/KOe/4fPPxSA5x/1/B/c/wEF4P5f1P1/WH8oAP1/qOd/4/sfCsD5j6j3P+H4DwrA7z/q/c9Y',
        'fygA5z+gnv+C9YcC0P+Buv8b6w8FYP8/4vkvuP8DDsD5L4jPP57/Aweg/qjz//j8QwEY/6Oe/4r7',
        'f6AArP+jjv9x/gcKQP1R73/H/m8oAOc/IPJ/U0lkPP8JIj/Rn+NCZjMYLBqJw+JSSTQOhUNlUrhc',
        'GpFGYpKZTAqDSWPRGX/iZ3wX+Cf+bwKB+D/0JxHp3+v/2P/998NL5vv+x0zUvwcGDcD8H9r3H5WA',
        '438oAN9/iOu/ZBz/QwE4/xFx/z/e/wEHoP8HVf6PhvN/MPmJ/mwmQZvFYBLobBKDRdImf3v605lk',
        'EotO+xYXuDBobDqDSXWx/OOf8QfvfzqdRvtRfxKJQMT9n1DgJb+pmPn99R+qMH3J03g1DgQwGAwG',
        'g8FgMBgMBoP5/0+wLN+t/17tA/Z/0LD/7wf+TfM/QP834v0vuP8DDsD+f7Tnn0zB/b9QAOb/Uc9/',
        'wvc/FID+b9TnH/s/oQD0/6De/431hwKw/xf1/Dfs/4cC0P+LVn/s/4ME8P5Hvf8Rv/+gALz/Ucf/',
        'WH8oAPe/oN7/heN/KAD932jn/5HJ+PsPBeD7D23/J87/QgL4/Uc9/xff/1AAzn9Hnf/B+kMBGP8h',
        'rv9i/eEAPP9o3/94/wckgPoj2v9OJRHw/m+I/ER/FtOFSeNSSGwalUNhkohsF20Wk02jsTlMKpFK',
        'YrIYRDL7r5n/QPlRfxKJSMD9X1DgJa83VX8xawZfyszVqH8XDHyA/j9U/f80/P6DyU/0ZzNcCExt',
        'EoNEIGoTOCwuiePiQuIwadoMKovAYhE5bCKTyvkr9r+S/lf/L/H7/H98///9fN//6ughZSd1I+De',
        'UINnYUdHwS3rpFg3IbcW/vqCx0tnHdeYY85feFVjni1pUcm1DpPClRxX6cNKNYsl54UMW0UPiGpN',
        'sm5VCYYvNA9ukmFJiQk4BxNWLIyWqApfWzZaSL52bt8hU/d3CUMNuq73dPvbg4LeBj8rdykNuBDx',
        'L6n8weseQnwHDRem54bum3s41mLO7q66yLE7Czvkra6/FVzFXlzpZ+MZ8brpkMSLN7zPSz44LTce',
        '3fr+jHjUY1P9xGaJE3fCTb0bm9Q3n1JpXmFTEnflHVOxSpmcwtN017UTrVpN67lKU22sszvHYkgW',
        'amhxaw2Zywsd29Ua6PONfELT5wXuiI/brMbI/frYZ8t+C3//F+nHFr3ff/XFSR36E69Vp5e0KQZ6',
        'ZoqGka9axZKWDxYx1PtaRPKyn9sUV/S5BdMUix1ULrKL9sY7SUb4R4vnLvAdU62WWaM2rmiQ8vFQ',
        '9dVfX/lYH79T6eX1juHlzW6L6jKm5ndtNp/kZz7b5unxa6qiCJNbn2LY/WCqbtnTqVXMrKL79F3P',
        '99lc8FNPaPDXLMq5581ONhpwa1AuLR3Z+uTUcGI2Ld1WzO5BufT8k/4WWh82qK5L3i8Xe5cacLCi',
        'Yc/H/T0mz23d93XHVnaI9g76xZOckqZ6jVqlvXtXGw2vDBaU6TLgJQlJSUlNz6H5NBWGZGbLWeef',
        '0kwMcGxeIft27qfwtKxS9dcRawtM8s+vONBQsz9adnKpXJHWo5rbg7125mTjPWqGfgWXzrs1znD0',
        'ohtnpuz33ZqqovrIW9c7c5HButLcIWe91vCGSb1DXXsYwjLdNkUCz5+/UU1PS1PYJL6H1/mkIn+N',
        'hLiH8BlGTs2N6H07IjSHThmszPCqlL+1Nm0Z63XDJz0+xdmpMaL8/NU3Q8vNkpcd9JjDdyTI4Xa3',
        'bnu99/Pf7+uNcLcPDPe27/X8MMTvNt4QbfduxyqvvNfjR3Z+4fndvSLxJCTuUo/80vKCF53mWUct',
        'Gs8W2wQdLWuvzUtlUBwfp2jJJ13pXbQsgum1cqR94JBu2IatgRdeX3SN+Fo06dDzNPXk7PqvrMlX',
        'SuEVwdPkXfMqFnxfQLtpatO92C9pXmHME/N2ZHj5pI1Z6I/ohdlNR8d7toQsW0SYCOm6Ofntf+t6',
        'gb7PqU/0+Ksfhk98dR3hhGuRP5+07xLlRmmLOp/90OhRKXdzYG9PuOKUAcMxx/D6poBXj2f1Tn/7',
        'D2Xipm0s1R2v39pdlf7ooAkhstsup6J2gNLpW3ithL+vSCmpuU3Kis42aBbzW/PJQWPm2YFXBzqF',
        'czcS49z3PX2ibR/jPVHCvynhZejc9oFcobwWx3rem9b2bEKqWdZIsifH+ZcJNf6E+Fdi/8HeeT41',
        '2W5r/KVIlWYBpJdQBSEVQhERQToI0msooUPoxSDyIoYuooAQUJqgNOldIESaCihNQLo0RemEImXr',
        '7Jkz835gnzNz9oQv+f0Dycw1z/Pca13XvZadoS0XA1OPYYta94tkIfhXGAUEKpuvxhYenrZi8Or4',
        'e9AGXYJegN90bpPOVknlJ0IyUz9aK7BAwuYyq3/72ty0w+4oWhBXgx9PsWnkd/8sNuvU6WzNdal3',
        '4u8k+mBvzFM23y9vgeGtaSlnln5NyWnpvgufODeT1DhA7+9s6i02kBWVfqCEQ8dPm9RRPsfBg6it',
        'MfYoduFpRB1vSFby3uO9NHZ9fQmOKiE+7mE36rjCHPXewJWaOj0TI2E3x/oxjojbVk6pHRWsS0VF',
        'Ma5pWyLJPG4Phvw9lEtq1EK5f7pqw3C2+qrfP/z4wrp+JoDnLb5MuPjyfA7WKgMDZdGayWP9+iEq',
        'v623COqgBpkPMbjF2vDjKiCXvY4QIPee3ZfLf7RbKGGiLmF47ph2gZOyBVe7Bs/ABM4wBJEhs/+S',
        'nyFbioQtUPlG/vmPPKYY64jt52vLGQKxtcp01MtKSJ6GTJNjyQUWC5tDmV/yY2fYrGgjdpHbU3NM',
        'PxSiALYMhM9hdaUog68lUdaHvsZvrx1iPvg2ex5W+t+R5y7r+GR6rD0+geT6aTMl2PWFeO//E/O/',
        'p+3/kfJ/ROFE//+U839Akv5E4cT+/2npD5P+d/6PVP8Rhf+gvzQEbv+7NINLQ37L4gCDAJGO9ghp',
        'RzgcDHOE2oGAQFlpGXvY/+E3/tf+HwjyT/1BQDAYRKr/iEFQxnv1nu5uE9E+qXcaM+R/8fhQOp32',
        'fyJBPE68/3NK/s//3P8h+X9E4cT5n6e8/xlEyv8ShRP7v8BTPv+T/F+icGL+85T3P5L8H+Jw4vyH',
        'U97/Lk3K/xKFk/QHndr+F8i/z3+k/D9R+A/6I6D2Dgg4EGEHlbVHOMjCkRCgHRgsKwuEy0L/7Iax',
        'sweCkZD/v/8L+VPz/0N/EEgaBCPV/8Tgj//LhBpYuJi65WCxwculHkFr+bIi4dxzsXKH0XIVOZpp',
        'EKM6Hy215ef6CiEhKhFdD5Frl8b7q3+p4G1v1n6pS1km47RYHbPG1wVc++zv+GP4I1DNwtOjtrGi',
        'hJti+HBZ1PHyL4nuGomMmi3P96bbjZWHzTuhq1M7e4qKo98tyyFpj9nhmd8u+mj/JQjk6lwAnn2A',
        '8NxKv5dsqyplINNRzvSAs3GeASvUwn5lsVO37uzAWuNa5G4bE39oZqtTZcjC8Kxphd4TmMOqZjZD',
        'PC1FfRUoQSiEan/Y0b54X0ODFYvJfMAEfzKaF8VKA+dPXknYpr/0SqogvpheLqfE6hbS+0vbkuvC',
        'd0ftO5y53k+pki/aOwE52oNX06hhfVUuu2+KfK4rCLloVTMZGE4OyYW5VOXlm5wl0Nc/Y48tDlBl',
        'U5VHAs+wmthdjLNq/1TyjQVkJx7dNlRZofjh4MXPTSfg6NxEUu8N88GHPt3WE/q6tBNyQqHk6T9i',
        'Sleua8IIC5LOueF5puZDJbzS9GNf5BrlzFkivtmYjKgigztnHUu43Z4KfYI2tPqT3XlTm2ist1wF',
        'fGgkz2gsKIFFYQuaeXhdjWBNSo9XUkH1eU8kb90YrUnd1cbLueSydmUlcojeN12p9wxN21QfXmZg',
        'Nu7L2S9HS0Z3jJyLb66HfLu+ysreOv16zU2WjJrZpe1+9DHA3Nm5KVdJkEF4gGLU9AiM1d6JRt9j',
        '26vr8la+KiAjFCQGoG9iR+D5e3mjou5aq9NkSOi8sEWGanYt2L30E57PA1So9/epkj9KPJ/Va6XP',
        '/lK4dbFXOCTPOts50n9W36IEVD+qLJ/Sd3wTGMTIE5k6zYtCNsl03qc9f/EWRfyukWPEMxrweoXP',
        'NXg2o5S2CnQXpcLA5/zyhdEz+h0E4w8AlZTKBefZM7NubzUPbHbBdWd3+Ajd64x76eVj8zUCHxUY',
        'AVfxqrdqW+5utr6obbmz2wx/fscWr6fQ2MXi/uv1g2XrcdBx5uZUnlIYodJGrv/iVNDW0M59yYe1',
        'DmmxbFfX45VGy7JDL8t4YvV6dc8S3pkB05R1uMvJsRGQr+zKiVWcLjzww+FPh+/DYK/25Nn6dA/2',
        'CdWNgUZv7pR6oH42zMgv9mY+bVC/jShhS0x3PBBv7H2FTXSeeBD6zORmxhSvOg/X1uHGknbu0sUN',
        'XFBS1ZXzXgrHa6OZobWf9cq5fQt1oUmKBuLJCSV6TgZ29CWixui2okezBBURu1i1Uu2wJsdfjxpK',
        'dlZhahZrr1jm/Boi+uuNYvOs+A7Wuu3fM9519bsfguW96uG3PGDx/GDiWb4h/vBh6K+9JRW8l/8x',
        'QeauCU5y0IuQB16i6UJie4vcxsSmsSVUo58WumJfez39OYl23b2WF7LVOlLhXtnO9wVe4Pm1YbyW',
        'oOjj2dc2GcZzN9jdrDQN0mW+3zjQNybpa6FW8+PudymvZpcn1RccKXyzAFqLeri64SC64Taxt5Ha',
        'GBV5ynODggcbk5m/+n4usRbueRQJi3ybV3MErwIeZhzjeb4hkXVux1Orx+tsK3c9z19l+tkYwkj/',
        'tXuFiutbnugQP1u/i9+9Qa61MVw8PEqLvYGgRDFRdqkXt2N75sM9eNnjuvvC2k93w24wRJ8bDvLo',
        'mmLQ63BT+xJ2Qwnmq4zcmgtVsjFTmnhVDE1xb2yDDluYz3Asi75AxRt9zEfFb/cUouKT3+eg4qHR',
        't+s6yMnoPpuqccnwXfGQoAyoY/H9Kx7HZCvlHrHNd435LlPqDKUEGYdOuvS6970kcMcP/eVw4HLG',
        'xcpe61sUVt6SRo/cirqqjefMxX26Y65ea69mTMoTlePlJ2NCrNFvIf88WtxllA1AvnAZ63ebx009',
        '+DKd1x/1FJvqEQ6LyStvmnEWmJagkGUn5Ff1+wKXY25Lhk6jJtbOWQgkhs68qS5I6DiS3VY/2lEi',
        'T0AspYe5oyOgiLa3N/F+L3Qthzbc/P9+WjhI2dDvd7SvN/sasev2lmO2CJW0BGDEiX0w6aZzrLYK',
        'DPNp69x9iNosK83FcxQUsFqu8SH5E+xQGUFsB3yC/fejl+maIovfRHQdp0x2+Xor03ijZZldkvZQ',
        'gCvtjsY0sTlcdCg0ICPx8AilfuEQEScSYxBexr7e2V5nQ0OVvssslZ2s0Hmm/R5XylvmzpglyVZo',
        'tzpdjAHXFEBQ6y6Z2Ny8vuyHPQodS8A5TUqhZjGuBBnVAwM+ZjoBsQrR9g0Q74V52efThywwQCyu',
        'LE6b7rpsb+XRPYCIgDLwQmo96iqkJdbg6/UtEWh6tSAvJL3yvKpc64wDQ4VAwYDdY3bd3UgaDNru',
        'EZt65hN1VBJzzP0I7zCFXk2By5QvpPOPjsO4IgZQVFdavYOc0H39v183vAwBL9uHXBFiSRJmALho',
        'ElkbFdSv2aBu2uXDA8VwFqO3ISPhBTNmP2ibgpsEg3nPmv10NSmqiRrNSNRC+nbfqLlR/46j/dxz',
        'F/U86n0fg/ziSkmC5bxkqm5x8XL/7Syqz+t3GTw1FDpSlmhVBsSwnYt4TEXMTmhXmIaEn6MZ/qM1',
        'KwYBOh70Ki34aEygcQ+XcDN8bw8qFKWXeN27fOil+Exh1HVzcHh2Z2xQzSYYZh5am1y7R7eKD9hz',
        'MevVrO+vQ4O3JprKccY9aNswK+nsQoKAYm114A02Kdc7z1MyvkVNsv6MqpGMCgDRjNXEfWq2Gaic',
        'E+XusnApcor0kiDDR3eaUuBnBI4eGLFgpGfPNpFJ4fSib/kdhzETcIymTAJ+1+mDZqjYV4tlEG1/',
        '1V70bKs00BxunkvhoA/SiOQXMTSQeJ61qxDn/VU6wnhEc1hdIMn/1mh40BVBjXINo+nLjFWNm66B',
        'R4/g8amPGqgNrncAvcmDYumNYqFXnCVKL49lzAYscA+M2IFUdPyWbgwNbo/wm/Ze10hLfXZj+eCT',
        '8PiFD1lPaCDURe7SKgxWmwl+SfoFPonKWE46i1xzwXzmj+rfd7LK5DWPbVQzOqb71ugUmPTwgmX0',
        'zeSc+itS4dKQVsAsnTejpDRidl+5+koEvYZhqL9xo0g4m5Ted1poctLY/JU+lwJrfR1Q5wWa2WSD',
        'sVKZYp2PRpFnh5tyC8+KefimpsoU2ZUXN5hlNOSJTMzDmTS5Op6IjD+w2/Uy8F2JOu3z2n+bE8//',
        'p5z/IPV/icOJ8x9Pt/9Lyv8QiROff8Qp9/9J/R+icKL/e8r730Ek/5conKQ/9LTnv5Lm/xGFE/3/',
        '097/Svr+E4UT83+n7f+T9CcKJ57/Tnf+ExhM+v4ThRPrv9PWn1T/E4WT9IecUv4f+vvgT5r/STz+',
        'g/4QqAPSDmIPkYYDYUgHqCwSAoMgELJ2v2tzsCPSHg51gNkhQf+F+98yUNg/9QeBpMGk+99E4Y//',
        '7+FsYcTevNgsI6MxgIZ8Y7VPmzTv4Bio1lJqZe1M8RiVWD6XamyJEZyEyvRUu/TPT/plChikZdnH',
        'yB6AR66xR/EqcVFgeO1lLwweMqdtx+6phMXhLmCUhGWV+w41V9547JDZiSkyAXaa+hQXQ6d8PZ8/',
        'lum5NKkIeEeeKmUiHJy2nM1Ox+8K7rx1YR4nxWtEHWsT1602+HMme9jGW+5JL8qwnHNGuCqLwplz',
        'LIA6N5XWThfWJPa3aPmT88g8CtWmLLNm8VQjeXnjlynCee6WjQ2UGDCXl3Ilz3bpUzx3UJpUSmel',
        'IVpVtLuh3bMHcF5Qw1O6WrIeg52K1yqQzZwZg4pzrk5vGQdu7fVMn62tysI/fH9wlJMVOCQVt4IG',
        'lDuKmgWC05axs/PXYQm5CYBAAJg7Q9n/s5jgiELHYEGriY8qi95nbY0znu/1Q7SoRaL16POZ5zNG',
        'k9o1HqdJbXwVFhHimeLSWLmE02XUEWfxKxJf+P6mo4eqFprJwdMNkMbp6LAXdSmmXxpP7orzkX+c',
        'mx+NFeaKL3UaQeU4fuosVEqom0QpRGTMDBtDsPps6EKDZ+fEK0TVt1k9zAhyfdV6NJitaO3N9kKg',
        'qttC/lHK3PqIcxUGiZc3Z3tffRXC+bp23aYUuz4byFpx4YBMCWeyRpbRSkVGhgsLx5gtjckYseS3',
        '+xi9EzfRkEiFjfOv+Lc2DAtIdZs1acQE3+pqv3MnRMZvZ2drBwr+yPrqyXuQcW0im1MDYTO5NEiw',
        'laOFRZgnh38htYOuDWATy+11g5nWxSHQyYbixdIx5Rmnso3FaXAdWYvEdg4LM7Nt8d9XIF6btlhb',
        '7gp3OkalKv4B41ZIG4eDH4b5baIHU9YEs7dbxsYeE3P2tGHiWuS9e9dwPGuV7CvWbNLOmZ/n99Er',
        'JdQvJ24GCF/PXizNaH7WIvvxuKcPnHnkJMWtOMDbELTw/TWnDbplE53/ZAzbXL6PCze8KYx8OWr4',
        'kDskmG0ADlNMS93yonMx34krW2QmjI/j36yHdCqvlmdXvuDMXY0bVQxr2fkx1Fy4GbnzYOeFUojv',
        'ypyb5I5Sm3PpnTR0vcK925ioHBeqvXKVxojHnRRhVlo6SlDvrW4dbHpMWD+Z+Do2ydTNcu+AWlYP',
        'F1xH5av1ZomSkjIw3Ojs8JtQJ/Lk6lVxPzByglCSnR9WfBfNz8dnelU/2fMzRKLLdb82lVzz0Gvv',
        '9dXf4sSEb+zpbeldKwNlo4/AS6DIKwO5W5fXoIraYTSpL0euh1oV+44edtFe3sWgNspSPuHNcwzp',
        '845E+Ezr+baVyHDvItl6b+l+h6hSL0WsiYvjRQZdIGWDZhPkle4RxkOuQjmAzQrR7tmnt72CJSPt',
        'MhfHOt8NJUnclkj2KFmoSLHMR6QUagWG01v2ldCN76+5N00SeiYXfR7YA//V3r0GRVWGcQAHxAFP',
        'CF4QdPBWKpoInrPnrpWBiISx3mBAzOTsuYRoxMVVzEKMEY1JRbTkYiKKqaCi4mU0w8QJNRXLSFwY',
        'lJvCcDETtFRgW/jQ5NSpD+V5Z/L5fWKYZXeH5+y+73nfc/4P5bHPtDW6y6d077ejs0zhGWtbv4ht',
        'CvEaLMurN7oFzsiI/Hxz1RrZkPJlRLt50vbTmcbkUf2X3tg36HGTKaK1apFnW/BdfX5H7GsT+8RW',
        'HfdKPpl5wvVGWHrl9XI/+ytG5yem7aWj7ZbnRs1Oi8uofyi5Z1RkDr2Dt8vzcj84k19YH+p42Y91',
        'uzls/pLE1o/fqWOK63af2W3T3htLWDui+oVf04usfiqOie+Td8/WwWfKUe/ErqyiU2VHLP+Vwd52',
        'vptGO7eZToc6H0nx+vR9pjEt78ntlMQhtycvt0w/POZm2j8OfGva4AKXRwGbnXbS+oEzCl1PRQZ5',
        'Jhf0r4hcot9PrXizF/1hstuOqwHC7ZfFpKi54Z3Jt3ovXtBEtbfY6EPzr8yYuf7h4QD/Nd8PaMMP',
        'lRyo4IqcPskKu6eEXTylNG/qvLBuZ2FA54gdZ3ZVT+qwX1jMx88PNjvUh499xba1X645p6DG0db2',
        'kl1TTkh+RS9Fv3XLlL0tw1yOPdky9URb2d3gDTcyy25GDN/bpzTOqT1nTdnNcMf9t6LtHGKaTSdt',
        'mGOTf1xnPFx4dkXltql8w+LG7H0Pyq9ZG4cv6/whIbSZKTTbzAvvIv78/a+a/4V4/Y+F8z9NqF7/',
        'j3j/B+qvDdX6o87/hfprQnX9D3H/F8h/1Ybq/g/q+sP9X5pQXf9BXX/Y/9WE6v4/4v4PsP+nDdXz',
        'P9T9P+D+b02o5n8hzn+A/k/aUM3/Q93/Dfb/NKFaf8T7/3D/tzZU8x9Q93+D8V8TqvN/1PmvUH9N',
        'qPb/Q9z/C87/taF6/S/q73+Y/2lCdf8H9fk/rP9oQvXzjzj/C/Z/tKF6/of6/l8Y/zWhuv6LOP8R',
        'xn9tqJ7/o57/w/ivCdX1f1TjPwP5H1r6m/pTIk8ZDITEiZQBZxmSJFlSlBSZESRSpGhFFgjBIM76',
        '59foLrD6/R8ky7DU0/XX6QiGhfs/tBC/reWstbXlh8TUroK79hjq9wMAAAAAAAAAAIBnLdGU1Kt7',
        'McBqpLncb1YwAAAAAAAAAAAA/u8EJ0fbnkWBjoVu3qjfDAAAAAAAAAAAAJ653a9yI7vXAvrdNxun',
        '2iC8QgEAAAAAAAAAAHh+fB3sZGVl/V88k2r+A4c4/4mB/A8tqNZfhvy3pzxn9TegzX+jCMj/1oRa',
        '/SXU/d8g/1kTqvmvqPt/Qv6jJlTrT0P+41Oes/pTiD//kP+oDdXxH23/J5j/a+Qv6u/V87too2HJ',
        'orgIWfr3r9FdYPX8R8uxwRK/15/WWY4TgtF19/+F/Mdnb6qOE2hREnhCMNCcKEgcr1CEgSQ5juA5',
        'WhZJySASpEJhPj2rMtgcieqOBJV4TsFxgw6nZJ7DcZ7nZFHhKB0rY748jmNzSWx61HuY/o9HFhbK',
        'i6JMKIqO4QSWZFiaJAiDIko0SZGWklM4IZAKSwoi5i9IljkgSzACTeECIeIijwsKKdAsKbIsLlke',
        'xUui5e+woJ5mUTqe5FnM0xOTdTinowlOoi2P0YkUKXOETLAMJ5GWJ+MNnEjIFCUYsINnH/jT2d9t',
        '8Ohb7Pr2uNqs8nirCdV77ieEOQytSengG/0PXr0TkZzmcac2Jmfp/NMlWRd8y15a5XZ5zvEtG6sD',
        'e588FxWdZKefcgDP+2jrxbChcSttxpfnu7zhPmq96y+1q9PXOjx2LDpoM6TG5XqSHvMMqs0g6/t9',
        'VV7p/XplX6eUvo6HlkUMSp74ekTqzPjP6vqXj8m+WFwW/U3mnjFps30u1aW6D3S+n+1/ybTpQNDP',
        'Tc3vurfmGs3n7XwntY33i13wqCry6tGx541xMY9ePLJ6ekfetPSG0sYBJeeUCZ3R/HqHrJD4jPNk',
        '6Y5d16RUP/PxcYEtq1JDrq9MWpThnUU11JSkNgRkJ6A+8AAAAAAAAACa+w2Ei+O8AEgDAA==',
        ''
    ])
Exemple #4
0
class MockRepository:
    """ Generates a mock CVMFS repository for unit testing purposes """
    repo_extract_dir = "repo"

    def __init__(self):
        self.running = False
        self.sandbox = FileSandbox("py_cvmfs_mock_repo_")
        self.repo_name = MockRepository.repo_name
        self._extract_dir = os.path.join(self.sandbox.temporary_dir,
                                         MockRepository.repo_extract_dir)
        self.dir = os.path.join(self._extract_dir, "cvmfs", self.repo_name)
        self._setup_repository()

    def __del__(self):
        if self.running:
            self._shut_down_http_server()


    def serve_via_http(self, port = 8000):
        self._spawn_http_server(self._extract_dir, port)
        self.url = "http://localhost:" + str(port) + "/cvmfs/" + self.repo_name


    def make_valid_whitelist(self):
        tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1)
        self._resign_whitelist(tomorrow)


    def make_expired_whitelist(self):
        yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=2)
        self._resign_whitelist(yesterday)


    def _resign_whitelist(self, expiry_date):
        old_whitelist = os.path.join(self.dir, ".cvmfswhitelist")
        new_whitelist = os.path.join(self.dir, ".cvmfswhitelist.new")
        wl_hash = hashlib.sha1()
        new_wl = open(new_whitelist, 'w+')
        old_wl = open(old_whitelist)
        pos = old_wl.tell()
        while True:
            line = old_wl.readline()
            if len(line) >= 3 and line[0:3] == 'E20': #fails in 85 years
                line = 'E' + expiry_date.strftime("%Y%m%d%H%M%S") + '\n'
            if line[0:2] == "--":
                break
            if pos == old_wl.tell():
                raise Exception("Signature not found in whitelist")
            wl_hash.update(line)
            new_wl.write(line)
            pos = old_wl.tell()
        old_wl.close()
        new_wl.write("--\n")
        new_wl.write(wl_hash.hexdigest())
        new_wl.write("\n")
        key = RSA.load_key(self.master_key)
        sig = key.private_encrypt(wl_hash.hexdigest(), RSA.pkcs1_padding)
        new_wl.write(sig)
        new_wl.close()
        os.rename(new_whitelist, old_whitelist)


    def _setup_repository(self):
        self.sandbox.create_directory(self._extract_dir)
        repo = StringIO.StringIO(base64.b64decode(MockRepository.repo_data))
        repo_tar = tarfile.open(None, "r:gz", repo)
        repo_tar.extractall(self._extract_dir)
        pubkey = self.sandbox.write_to_temporary(MockRepository.repo_pubkey)
        self.public_key = pubkey
        privkey = self.sandbox.write_to_temporary(MockRepository.repo_privkey)
        self.private_key = privkey
        mkey = self.sandbox.write_to_temporary(MockRepository.repo_masterkey)
        self.master_key = mkey


    def _spawn_http_server(self, document_root, port):
        handler = CvmfsRequestHandler
        address = ("localhost", port)
        self.httpd = CvmfsTestServer(document_root, address, handler)
        self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
        self.httpd_thread.setDaemon(True)
        self.httpd_thread.start()
        self.running = True

    def _shut_down_http_server(self):
        self.httpd.shutdown()
        self.url = None


################################################################################


    repo_name = "test.cern.ch"

    repo_pubkey = '\n'.join([
'-----BEGIN PUBLIC KEY-----',
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueS/yBwR4UlRsgkv7hcM',
'ljvt/KyrhkI5y7n7ksLBFumjPhieaWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCH',
'erxxjuei4Vu9yG6BFqow0ZdmjqJzU4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+C',
'hQ42pB+PD0CcEZp3VEsFV4wI9IY7EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEu',
'RM76cnfxh/0qui8Vbs93St2VhsahJWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW',
'2xAkfJ5d6NZJdrmRBD3agh5Zj8DuK0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuq',
'lQIDAQAB',
'-----END PUBLIC KEY-----',
''
        ])

    repo_privkey = '\n'.join([
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEowIBAAKCAQEA5TQNCMPftxI8Z27ReqQKgi1MNNmfW/l0Ns1Ax2KxspBYnbE3',
'FBhC1/B/uzIhW8mrYcXlFQ0400WfJHC+/pquDR/IXCrWB+1dyz9dp/7l4HcxcDNH',
'fc/g2hSvtdcw1ZCHNGONcCOxOYE3Rx10eZjviQZiGkzVkHwpnjgWNxbT7bkIV6iu',
'R8YWNUfGABwhTB5diq3UPtw5LEwYYj/CKFR9BkTK0VmZJbXV5bAvGQyINfT5296r',
'XJWD4WItJJ95R/zkBQgBjvLTNpFPRiOln8FTFpEDVWdteezDDXGWNWma3jYwvJg8',
'PBZEEcUmZBDQT9k0nfRizhsTyO7xzDmcAPfVHQIDAQABAoIBAB1JV1kFXjKQO/Oj',
'b1TSXR1hGFmwbPJdn4HZHCvd6oK8evY7TKRerTvWWRvcPfLyg9mMZccY12f3f2wy',
'k9UIgrDenMVaG9sLc26i/B6ZLVpPIJwLkVj8FOkIt6LuiijfvMbu6YWoqd6FKkEF',
'/HoFFqZVkHd31doOY2r6E6yaWB4JxqXcNJLAQRnm0mCytZUkzHodr+Tsy1GkSJC2',
'I3saPSIG5u4TL3XVsIBs5mE47FLK/YKRHJyp3tTLgTkhDvpLPEJC8ij1oWJ4PSzo',
'jeaK8xpv+LAjqu8brL9WZQI812Fmx+nPncSmKZrmjAYr4HaZ4aAgsWKOnqnyzrZv',
'QSao9EECgYEA8p1g/4iZVXkUjNVL6a+Cmj+g6+ji9wPXDOXFiwlOgXCDRFxunaMM',
'ZbZtxe26XHQdVm4e2hC4Zv2nn7Sn9gGg+7NCso5Y5qDH2HMJ/w+ya6XUIcE/YDMb',
'PqK6llLqsyLg+nrtV0ZkA5UN8Rlp116fWE0ensszH9qF43pB+7m8ANkCgYEA8dlA',
'sv9ZJW4/MtK8dbxzmrpyCRbLFsuYYN7k1ybrUVvK6WpGljqKbduTJ3/XC6qhJuWR',
'YAH9aTwwvRvEJor0EnYc+QpEnv5KHfNkKFU2x3tnnYZT24t2PLpY9AECjgeSUDk+',
'kHQzUGmig5eSSYMpGdLvRtD0BYXQLoD8eAy3y+UCgYEAtQ0jFK7Qlotr/YkzRGm4',
'kfmH0mUR8vqHolVZ7N7+GfRn0T0VQ0go+UKBeuJkX5g7SIOXPG6b3ifOzozXhutC',
'QnNNA8jcqQc0+98lh5UkNdcjjikTbWvWGhEAIywvf404zVOtCKM8AbxbEiA/7vvq',
'989dWW0UcuH1ZoOW+A5sMUkCgYB/E+zPISUyac+DYP/tzWvhLX6mD/f+rlQO8o/E',
'DYswYM8p/tHANlpuhyW3Z5ETbEDpM09D50fEeAAUHfbfWbwNx0pKAX81G+DOBAno',
't33lK46yUtbVUV57Yl9DNxSklI3o4Wtic+xSoG7oPkh7oBOEojVgPIM8M6fEB7qh',
'Se15kQKBgBQ8/Z4A95yoGlv1RYOBuZpOpEtbgi/NiJdRXnzrmQ1m31heRbkfu3w0',
'5WzlYjaQQ3g3rsh+0Ond9bLFcZor6azcPSsu+cjC3Gsxm/0KZKPAroi73Gd4O0QH',
'ih/vJDlTHRS2ArfdYc9cUYTFvs8YuLy7y9Uho35ey6PLX6CEsJel',
'-----END RSA PRIVATE KEY-----',
''
])

    repo_masterkey = '\n'.join([
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEowIBAAKCAQEAueS/yBwR4UlRsgkv7hcMljvt/KyrhkI5y7n7ksLBFumjPhie',
'aWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCHerxxjuei4Vu9yG6BFqow0ZdmjqJz',
'U4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+ChQ42pB+PD0CcEZp3VEsFV4wI9IY7',
'EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEuRM76cnfxh/0qui8Vbs93St2Vhsah',
'JWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW2xAkfJ5d6NZJdrmRBD3agh5Zj8Du',
'K0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuqlQIDAQABAoIBAASWKUk1sBc/6N0c',
'rusP9IaMaf3PANhqL+Tf7N4dIgh/sUBp+Rae0qaAuojCJShFCsKmp++itOcrCIjy',
'Vr9FZYJYvfCJtJIc4lzcpSC7CENTmfsw9Ol9yK4ozW5YdNWnfNxLILZBkbK1qqcC',
'sLfYgB7qT9zSzoPQ00j357PTugkD56eiJcNZu80nRy0Ud3D/3dDFJADF1hQkebwu',
'82NLqNQnTO2/KF1fJLgsIU3ymMdOV68k9rjtGfLRoK4qfX0lb8BNrAY2urPzU0yV',
'Y2unrWWbmWT2lDOIqRCfLbGSQuVfLbY7JOq+PwA+H7C2Py6GQLuFi8t5DTVuGke/',
'NtZpHkECgYEA9u+OPbZLISvmGNFZg4hn6k8PfH0GLrEcx0tPv4ReONqwXVobeYKX',
'/x0b6o2BC0bmICMjnGsDrZSZj2wiXAYknSQCxMAGAj0PoBI9KQNU1Simwb/IA0EE',
'd+c6BdR0YdVIQ7esSNaCaAb0zX1/y98U7HOQ2/ornhAM4wKKRtwykMUCgYEAwLeV',
'IvRHnwXls8kux+KOEAHGoWf4KqaOYUbqktSdVB5DziqN2Ktj46/wQxcmaS5gbMNR',
'B+lvveP7t3qKzMMMeBtKEKou1lGC+K7yWo//v1st25p8j9Ue0xlaw5ZiVRyYzZYV',
'uwnaBNFiNk8YH+to8UdwYGDPuNNZjE7JuFcdr5ECgYEAtsTKWBzj8LJoRXg2M+ez',
'WjaYNMDo4YhPz6aLaSpU/unGXeICseYaEEYAUpPXrnwUejbn9a8zcrepDQGxUMFv',
'OivcLLof+GovdX/qar+/e2HyQzdqmBX4c7LePFBqr7rIGO8KgoLa1JpJeQrpmwEL',
'oJNM5bR9sikZELDhnd7/Qi0CgYAV8VEzx6iX/K3oyJFhBPSz8d/R5OqmwIwZm19+',
'FGNNfpytzr6T2v/mntO2b95Zv4QPHjYNtpCYiGrSu0sugU7cJg9K0nW+xU0qT5Ec',
'qqSt/w27oV1paxS1aH+jIW5Uzoq/bcVPpJGEVurd0CepCr7KKh4rexprqvTZOudQ',
'6+pfYQKBgBmC5quiKh2ILLh5IJ1g5UXQPzFgE6J9XcVY3BumXBr41g2CYPC5oklM',
'v5PZ3wY76x8O+2S+sYBKzDHOv3Q8AJPC2PEIJORzTK6XfIetpnN3TR0LZvHiUpES',
'hmCojC2QE3Y7i+XTL2d9rbXLSIbMEWDHdBHKzTWczDIDo+tFPEFo',
'-----END RSA PRIVATE KEY-----',
''
])

    repo_data = '\n'.join([
'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',
''
        ])
Exemple #5
0
class TestRepositoryWrapper(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_ut_repo_")
        self.mock_repo = MockRepository()

        self.cern_public_key = "\n".join(
            [
                "-----BEGIN PUBLIC KEY-----",
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukBusmYyFW8KJxVMmeCj",
                "N7vcU1mERMpDhPTa5PgFROSViiwbUsbtpP9CvfxB/KU1gggdbtWOTZVTQqA3b+p8",
                "g5Vve3/rdnN5ZEquxeEfIG6iEZta9Zei5mZMeuK+DPdyjtvN1wP0982ppbZzKRBu",
                "BbzR4YdrwwWXXNZH65zZuUISDJB4my4XRoVclrN5aGVz4PjmIZFlOJ+ytKsMlegW",
                "SNDwZO9z/YtBFil/Ca8FJhRPFMKdvxK+ezgq+OQWAerVNX7fArMC+4Ya5pF3ASr6",
                "3mlvIsBpejCUBygV4N2pxIcPJu/ZDaikmVvdPTNOTZlIFMf4zIP/YHegQSJmOyVp",
                "HQIDAQAB",
                "-----END PUBLIC KEY-----" "",
            ]
        )
        pubkey = self.sandbox.write_to_temporary(self.cern_public_key)
        self.public_key_file = pubkey

    def tearDown(self):
        del self.mock_repo

    def test_open_repository_http(self):
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo, cvmfs.RemoteRepository))
        self.assertEqual(self.mock_repo.repo_name, repo.manifest.repository_name)
        self.assertEqual(self.mock_repo.url, repo.endpoint)

    def test_open_repository_local(self):
        repo = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo, cvmfs.LocalRepository))
        self.assertEqual(self.mock_repo.repo_name, repo.manifest.repository_name)
        self.assertEqual(self.mock_repo.dir, repo.endpoint)

    def test_open_repository_verification(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo1 = cvmfs.open_repository(self.mock_repo.url, self.mock_repo.public_key)
        self.assertTrue(isinstance(repo1, cvmfs.RemoteRepository))
        self.assertTrue(repo1.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name, repo1.manifest.repository_name)

        repo2 = cvmfs.open_repository(self.mock_repo.dir, self.mock_repo.public_key)
        self.assertTrue(isinstance(repo2, cvmfs.LocalRepository))
        self.assertTrue(repo2.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name, repo2.manifest.repository_name)

        repo3 = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo3, cvmfs.RemoteRepository))
        self.assertTrue(repo3.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name, repo3.manifest.repository_name)

        repo4 = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo4, cvmfs.LocalRepository))
        self.assertTrue(repo4.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name, repo4.manifest.repository_name)

    def test_wrong_public_key(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(
            cvmfs.RepositoryVerificationFailed, cvmfs.open_repository, self.mock_repo.url, self.public_key_file
        )
        self.assertRaises(
            cvmfs.RepositoryVerificationFailed, cvmfs.open_repository, self.mock_repo.dir, self.public_key_file
        )

    def test_expired_whitelist(self):
        self.mock_repo.make_expired_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(
            cvmfs.RepositoryVerificationFailed, cvmfs.open_repository, self.mock_repo.url, self.mock_repo.public_key
        )
        self.assertRaises(
            cvmfs.RepositoryVerificationFailed, cvmfs.open_repository, self.mock_repo.dir, self.mock_repo.public_key
        )

    def test_download_non_existent_file(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url, self.mock_repo.public_key)
        self.assertRaises(cvmfs.FileNotFoundInRepository, repo.retrieve_file, "unobtainium.txt")
class MockRepository:
    """ Generates a mock CVMFS repository for unit testing purposes """
    repo_extract_dir = "repo"

    def __init__(self):
        self.running = False
        self.sandbox = FileSandbox("py_cvmfs_mock_repo_")
        self.repo_name = MockRepository.repo_name
        self._extract_dir = os.path.join(self.sandbox.temporary_dir,
                                         MockRepository.repo_extract_dir)
        self.dir = os.path.join(self._extract_dir, "cvmfs", self.repo_name)
        self._setup_repository()

    def __del__(self):
        if self.running:
            self._shut_down_http_server()


    def serve_via_http(self, port = 8000):
        self._spawn_http_server(self._extract_dir, port)
        self.url = "http://localhost:" + str(port) + "/cvmfs/" + self.repo_name


    def make_valid_whitelist(self):
        tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1)
        self._resign_whitelist(tomorrow)


    def make_expired_whitelist(self):
        yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=2)
        self._resign_whitelist(yesterday)


    def _resign_whitelist(self, expiry_date):
        old_whitelist = os.path.join(self.dir, ".cvmfswhitelist")
        new_whitelist = os.path.join(self.dir, ".cvmfswhitelist.new")
        wl_hash = hashlib.sha1()
        with open(new_whitelist, 'w+') as new_wl: # TODO: more elegant is Py 2.7
            with open(old_whitelist) as old_wl:
                pos = old_wl.tell()
                while True:
                    line = old_wl.readline()
                    if len(line) >= 3 and line[0:3] == 'E20': #fails in 85 years
                        line = 'E' + expiry_date.strftime("%Y%m%d%H%M%S") + '\n'
                    if line[0:2] == "--":
                        break
                    if pos == old_wl.tell():
                        raise Exception("Signature not found in whitelist")
                    wl_hash.update(line)
                    new_wl.write(line)
                    pos = old_wl.tell()
            new_wl.write("--\n")
            new_wl.write(wl_hash.hexdigest())
            new_wl.write("\n")
            key = RSA.load_key(self.master_key)
            sig = key.private_encrypt(wl_hash.hexdigest(), RSA.pkcs1_padding)
            new_wl.write(sig)
        os.rename(new_whitelist, old_whitelist)


    def _setup_repository(self):
        self.sandbox.create_directory(self._extract_dir)
        repo = StringIO.StringIO(base64.b64decode(MockRepository.repo_data))
        repo_tar = tarfile.open(None, "r:gz", repo)
        repo_tar.extractall(self._extract_dir)
        pubkey = self.sandbox.write_to_temporary(MockRepository.repo_pubkey)
        self.public_key = pubkey
        privkey = self.sandbox.write_to_temporary(MockRepository.repo_privkey)
        self.private_key = privkey
        mkey = self.sandbox.write_to_temporary(MockRepository.repo_masterkey)
        self.master_key = mkey


    def _spawn_http_server(self, document_root, port):
        handler = CvmfsRequestHandler
        address = ("localhost", port)
        self.httpd = CvmfsTestServer(document_root, address, handler)
        self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
        self.httpd_thread.setDaemon(True)
        self.httpd_thread.start()
        self.running = True

    def _shut_down_http_server(self):
        self.httpd.shutdown()
        self.url = None


################################################################################

#
# Note: This packed up repository can be recreated by the script
#       aux/make_mock_repo.sh
#

    repo_name = "test.cern.ch"

    repo_pubkey = '\n'.join([
'-----BEGIN PUBLIC KEY-----',
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueS/yBwR4UlRsgkv7hcM',
'ljvt/KyrhkI5y7n7ksLBFumjPhieaWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCH',
'erxxjuei4Vu9yG6BFqow0ZdmjqJzU4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+C',
'hQ42pB+PD0CcEZp3VEsFV4wI9IY7EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEu',
'RM76cnfxh/0qui8Vbs93St2VhsahJWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW',
'2xAkfJ5d6NZJdrmRBD3agh5Zj8DuK0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuq',
'lQIDAQAB',
'-----END PUBLIC KEY-----',
''
        ])

    repo_privkey = '\n'.join([
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEowIBAAKCAQEA5TQNCMPftxI8Z27ReqQKgi1MNNmfW/l0Ns1Ax2KxspBYnbE3',
'FBhC1/B/uzIhW8mrYcXlFQ0400WfJHC+/pquDR/IXCrWB+1dyz9dp/7l4HcxcDNH',
'fc/g2hSvtdcw1ZCHNGONcCOxOYE3Rx10eZjviQZiGkzVkHwpnjgWNxbT7bkIV6iu',
'R8YWNUfGABwhTB5diq3UPtw5LEwYYj/CKFR9BkTK0VmZJbXV5bAvGQyINfT5296r',
'XJWD4WItJJ95R/zkBQgBjvLTNpFPRiOln8FTFpEDVWdteezDDXGWNWma3jYwvJg8',
'PBZEEcUmZBDQT9k0nfRizhsTyO7xzDmcAPfVHQIDAQABAoIBAB1JV1kFXjKQO/Oj',
'b1TSXR1hGFmwbPJdn4HZHCvd6oK8evY7TKRerTvWWRvcPfLyg9mMZccY12f3f2wy',
'k9UIgrDenMVaG9sLc26i/B6ZLVpPIJwLkVj8FOkIt6LuiijfvMbu6YWoqd6FKkEF',
'/HoFFqZVkHd31doOY2r6E6yaWB4JxqXcNJLAQRnm0mCytZUkzHodr+Tsy1GkSJC2',
'I3saPSIG5u4TL3XVsIBs5mE47FLK/YKRHJyp3tTLgTkhDvpLPEJC8ij1oWJ4PSzo',
'jeaK8xpv+LAjqu8brL9WZQI812Fmx+nPncSmKZrmjAYr4HaZ4aAgsWKOnqnyzrZv',
'QSao9EECgYEA8p1g/4iZVXkUjNVL6a+Cmj+g6+ji9wPXDOXFiwlOgXCDRFxunaMM',
'ZbZtxe26XHQdVm4e2hC4Zv2nn7Sn9gGg+7NCso5Y5qDH2HMJ/w+ya6XUIcE/YDMb',
'PqK6llLqsyLg+nrtV0ZkA5UN8Rlp116fWE0ensszH9qF43pB+7m8ANkCgYEA8dlA',
'sv9ZJW4/MtK8dbxzmrpyCRbLFsuYYN7k1ybrUVvK6WpGljqKbduTJ3/XC6qhJuWR',
'YAH9aTwwvRvEJor0EnYc+QpEnv5KHfNkKFU2x3tnnYZT24t2PLpY9AECjgeSUDk+',
'kHQzUGmig5eSSYMpGdLvRtD0BYXQLoD8eAy3y+UCgYEAtQ0jFK7Qlotr/YkzRGm4',
'kfmH0mUR8vqHolVZ7N7+GfRn0T0VQ0go+UKBeuJkX5g7SIOXPG6b3ifOzozXhutC',
'QnNNA8jcqQc0+98lh5UkNdcjjikTbWvWGhEAIywvf404zVOtCKM8AbxbEiA/7vvq',
'989dWW0UcuH1ZoOW+A5sMUkCgYB/E+zPISUyac+DYP/tzWvhLX6mD/f+rlQO8o/E',
'DYswYM8p/tHANlpuhyW3Z5ETbEDpM09D50fEeAAUHfbfWbwNx0pKAX81G+DOBAno',
't33lK46yUtbVUV57Yl9DNxSklI3o4Wtic+xSoG7oPkh7oBOEojVgPIM8M6fEB7qh',
'Se15kQKBgBQ8/Z4A95yoGlv1RYOBuZpOpEtbgi/NiJdRXnzrmQ1m31heRbkfu3w0',
'5WzlYjaQQ3g3rsh+0Ond9bLFcZor6azcPSsu+cjC3Gsxm/0KZKPAroi73Gd4O0QH',
'ih/vJDlTHRS2ArfdYc9cUYTFvs8YuLy7y9Uho35ey6PLX6CEsJel',
'-----END RSA PRIVATE KEY-----',
''
])

    repo_masterkey = '\n'.join([
'-----BEGIN RSA PRIVATE KEY-----',
'MIIEowIBAAKCAQEAueS/yBwR4UlRsgkv7hcMljvt/KyrhkI5y7n7ksLBFumjPhie',
'aWz3L44s4Y1dUJ2H8krRqLXVjQ0X5x/F/nCHerxxjuei4Vu9yG6BFqow0ZdmjqJz',
'U4swRylBkSjf4QVOVSxUcbd7sL2QVbRH9g+ChQ42pB+PD0CcEZp3VEsFV4wI9IY7',
'EMRUC6dwM+LG0jubvhGbvlaqtufGXDESRJEuRM76cnfxh/0qui8Vbs93St2Vhsah',
'JWcNGdeIaXlVsMyI77u1QhztPvF39+oDWEsW2xAkfJ5d6NZJdrmRBD3agh5Zj8Du',
'K0VttsvXwsSMw5gwBSRGLJIB8qXf254uouuqlQIDAQABAoIBAASWKUk1sBc/6N0c',
'rusP9IaMaf3PANhqL+Tf7N4dIgh/sUBp+Rae0qaAuojCJShFCsKmp++itOcrCIjy',
'Vr9FZYJYvfCJtJIc4lzcpSC7CENTmfsw9Ol9yK4ozW5YdNWnfNxLILZBkbK1qqcC',
'sLfYgB7qT9zSzoPQ00j357PTugkD56eiJcNZu80nRy0Ud3D/3dDFJADF1hQkebwu',
'82NLqNQnTO2/KF1fJLgsIU3ymMdOV68k9rjtGfLRoK4qfX0lb8BNrAY2urPzU0yV',
'Y2unrWWbmWT2lDOIqRCfLbGSQuVfLbY7JOq+PwA+H7C2Py6GQLuFi8t5DTVuGke/',
'NtZpHkECgYEA9u+OPbZLISvmGNFZg4hn6k8PfH0GLrEcx0tPv4ReONqwXVobeYKX',
'/x0b6o2BC0bmICMjnGsDrZSZj2wiXAYknSQCxMAGAj0PoBI9KQNU1Simwb/IA0EE',
'd+c6BdR0YdVIQ7esSNaCaAb0zX1/y98U7HOQ2/ornhAM4wKKRtwykMUCgYEAwLeV',
'IvRHnwXls8kux+KOEAHGoWf4KqaOYUbqktSdVB5DziqN2Ktj46/wQxcmaS5gbMNR',
'B+lvveP7t3qKzMMMeBtKEKou1lGC+K7yWo//v1st25p8j9Ue0xlaw5ZiVRyYzZYV',
'uwnaBNFiNk8YH+to8UdwYGDPuNNZjE7JuFcdr5ECgYEAtsTKWBzj8LJoRXg2M+ez',
'WjaYNMDo4YhPz6aLaSpU/unGXeICseYaEEYAUpPXrnwUejbn9a8zcrepDQGxUMFv',
'OivcLLof+GovdX/qar+/e2HyQzdqmBX4c7LePFBqr7rIGO8KgoLa1JpJeQrpmwEL',
'oJNM5bR9sikZELDhnd7/Qi0CgYAV8VEzx6iX/K3oyJFhBPSz8d/R5OqmwIwZm19+',
'FGNNfpytzr6T2v/mntO2b95Zv4QPHjYNtpCYiGrSu0sugU7cJg9K0nW+xU0qT5Ec',
'qqSt/w27oV1paxS1aH+jIW5Uzoq/bcVPpJGEVurd0CepCr7KKh4rexprqvTZOudQ',
'6+pfYQKBgBmC5quiKh2ILLh5IJ1g5UXQPzFgE6J9XcVY3BumXBr41g2CYPC5oklM',
'v5PZ3wY76x8O+2S+sYBKzDHOv3Q8AJPC2PEIJORzTK6XfIetpnN3TR0LZvHiUpES',
'hmCojC2QE3Y7i+XTL2d9rbXLSIbMEWDHdBHKzTWczDIDo+tFPEFo',
'-----END RSA PRIVATE KEY-----',
''
])

    repo_data = '\n'.join([
'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',
'DAbzP+tvIfjNRABwAwA=',
''
        ])
Exemple #7
0
class TestRepositoryWrapper(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_ut_repo_")
        self.mock_repo = MockRepository()

        self.cern_public_key = '\n'.join([
            '-----BEGIN PUBLIC KEY-----',
            'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukBusmYyFW8KJxVMmeCj',
            'N7vcU1mERMpDhPTa5PgFROSViiwbUsbtpP9CvfxB/KU1gggdbtWOTZVTQqA3b+p8',
            'g5Vve3/rdnN5ZEquxeEfIG6iEZta9Zei5mZMeuK+DPdyjtvN1wP0982ppbZzKRBu',
            'BbzR4YdrwwWXXNZH65zZuUISDJB4my4XRoVclrN5aGVz4PjmIZFlOJ+ytKsMlegW',
            'SNDwZO9z/YtBFil/Ca8FJhRPFMKdvxK+ezgq+OQWAerVNX7fArMC+4Ya5pF3ASr6',
            '3mlvIsBpejCUBygV4N2pxIcPJu/ZDaikmVvdPTNOTZlIFMf4zIP/YHegQSJmOyVp',
            'HQIDAQAB', '-----END PUBLIC KEY-----'
            ''
        ])
        pubkey = self.sandbox.write_to_temporary(self.cern_public_key)
        self.public_key_file = pubkey

    def tearDown(self):
        del self.mock_repo

    def test_open_repository_http(self):
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo, cvmfs.RemoteRepository))
        self.assertEqual(self.mock_repo.repo_name,
                         repo.manifest.repository_name)
        self.assertEqual(self.mock_repo.url, repo.endpoint)

    def test_open_repository_local(self):
        repo = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo, cvmfs.LocalRepository))
        self.assertEqual(self.mock_repo.repo_name,
                         repo.manifest.repository_name)
        self.assertEqual(self.mock_repo.dir, repo.endpoint)

    def test_open_repository_verification(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo1 = cvmfs.open_repository(self.mock_repo.url,
                                      self.mock_repo.public_key)
        self.assertTrue(isinstance(repo1, cvmfs.RemoteRepository))
        self.assertTrue(repo1.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo1.manifest.repository_name)

        repo2 = cvmfs.open_repository(self.mock_repo.dir,
                                      self.mock_repo.public_key)
        self.assertTrue(isinstance(repo2, cvmfs.LocalRepository))
        self.assertTrue(repo2.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo2.manifest.repository_name)

        repo3 = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo3, cvmfs.RemoteRepository))
        self.assertTrue(repo3.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo3.manifest.repository_name)

        repo4 = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo4, cvmfs.LocalRepository))
        self.assertTrue(repo4.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo4.manifest.repository_name)

    def test_wrong_public_key(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository, self.mock_repo.url,
                          self.public_key_file)
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository, self.mock_repo.dir,
                          self.public_key_file)

    def test_expired_whitelist(self):
        self.mock_repo.make_expired_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository, self.mock_repo.url,
                          self.mock_repo.public_key)
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository, self.mock_repo.dir,
                          self.mock_repo.public_key)

    def test_download_non_existent_file(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url,
                                     self.mock_repo.public_key)
        self.assertRaises(cvmfs.FileNotFoundInRepository, repo.retrieve_file,
                          "unobtainium.txt")
Exemple #8
0
class TestWhitelist(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_whitelist_ut_")

        self.cern_public_key = '\n'.join([
            '-----BEGIN PUBLIC KEY-----',
            'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukBusmYyFW8KJxVMmeCj',
            'N7vcU1mERMpDhPTa5PgFROSViiwbUsbtpP9CvfxB/KU1gggdbtWOTZVTQqA3b+p8',
            'g5Vve3/rdnN5ZEquxeEfIG6iEZta9Zei5mZMeuK+DPdyjtvN1wP0982ppbZzKRBu',
            'BbzR4YdrwwWXXNZH65zZuUISDJB4my4XRoVclrN5aGVz4PjmIZFlOJ+ytKsMlegW',
            'SNDwZO9z/YtBFil/Ca8FJhRPFMKdvxK+ezgq+OQWAerVNX7fArMC+4Ya5pF3ASr6',
            '3mlvIsBpejCUBygV4N2pxIcPJu/ZDaikmVvdPTNOTZlIFMf4zIP/YHegQSJmOyVp',
            'HQIDAQAB',
            '-----END PUBLIC KEY-----'
            ''
        ])
        self.public_key_file = self.sandbox.write_to_temporary(self.cern_public_key)

        self.compressed_certificate = '\n'.join([
            'eJxllLmOq1gQQHO+YnLUMtDG4OAFdwODWc1iIDOYxew0q/n6cU8wyauodHSkUqmWr69PQCIrxj+I',
            '3FxFUhBwyS/8onRFQccSIZB0CNl4CkPLYrtHIGJgwLwaiuoln1cGAtuTAAa77pJVW0Ps2zYm76Kg',
            'nvJWJG++jAJ11hqjU4hRJw1/dx1W9t7Qie5bHQZGrRC2iBup/Xi7Do8Bdsmm42SjTJfwOvY2Q+p+',
            '4fsXmvh/tur7R0SiDFiPgE3vwuA2hoE+h5z/jl98ST0QxL/V/YqZbrWee40/RndpjxxohHe+iD5i',
            'sn9adJQV26F67SKlWBID2ARCG2Aqz4kF8EewO/TJIdBjNEnfUxspTHSCpaem45Yb8syRc9TTIx2+',
            'uFzoWgk0D604y9RQbvIkDius8Njmg9nLHWqP/TTlImm2Q+uxOo5VMDyuomey65ZMoYNy9ZAflRqx',
            'zEi59Ok6CBUOaU8RnRMyewW5AuQjjWuKPGba5GQBXcNbX17opJaP9Perjq2n1p84LJ0NqkSZSdTG',
            'YGm59Gttq4dMzLlHF4usbmzZ2sYX3ZTENYZ25TyEBnZSx3Z+xrnv+RT/KNT67UQ8mI5qxhUnw4sv',
            'c7vlXI+nLUWcxJQ3lWltV2+wOMWhnZBxaxdVBSGj5cTOpplKpS01Jk5hD1M43OJntNy6JQiRkJAS',
            'stkhUiSzcggCKwHA/Xu5KADMzwQIWJkytdPoGgp75OFEep4SeQo6v6rol4rsq+CZmRtfZWScBfm+',
            'BMf4ehh4kFIOdLRzvtepQEf38bK0l+v48NkIG8EG0ltj2NNxSplDLXqXlNi+V7N2pvhLKfpHf9YY',
            'qk7qyIxzQW6Syqqf0ZhIqW+9cLjT6CbNZyxwlpMqluqqfXDPfe5aOeXhwDn+4g5yaFEwYuF5Bm27',
            '6I3/cPKfMRgyvp/gyVwaa17S6soH9n5d70X582Mo+IQux83JaVQcGJM9U/39eOG9bm93etYjiTH4',
            'gj0NUHy+jF7bnPGSvZ+zZ4ZBy77nn37a4XAZXqNys2BYZqJDOeL9wVSZFJmCtyfaUAvWub3Ygee7',
            'HgJ//lD/XT0x8N+f4F81GFZb',
            ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(compressed_cert)

        self.sane_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '9A:C3:7D:9E:C5:EB:CF:C8:92:EF:1D:7C:DB:56:DC:9C:83:9A:15:71 # ATLAS Release Manager, expires Sep 2011',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4 # Jakob Blomer, expires Jul 2011',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--',
            '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
        ]))
        self.file_whitelist = self.sandbox.write_to_temporary(self.sane_whitelist.getvalue())
        self.assertNotEqual(None, self.file_whitelist)

        self.insane_whitelist_signature_mismatch = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            '--',
            '3fa17242c67b5f3e54b85bc6e4544cd9d80a8ac7',
            '-yw\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
        ]))

        self.insane_whitelist_broken_signature = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '9A:C3:7D:9E:C5:EB:CF:C8:92:EF:1D:7C:DB:56:DC:9C:83:9A:15:71 # ATLAS Release Manager, expires Sep 2011',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4 # Jakob Blomer, expires Jul 2011',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--',
            '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yx\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
            #  ^-- that byte has been tampered with (was 'w')
        ]))

        self.unknown_field_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            'Foo',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--',
            '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.invalid_fingerprint_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            'C1:2C:2G:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4', # 2G
            '--',
            '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.invalid_timestamp_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E2015070409527', # <--
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            ''
        ]))

        self.missing_field_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            #'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            '--',
            '006982cdd72e5342283e96db5c312ec65ab7f652',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.no_fingerprints_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '--',
            '7ceba0a0533bfae3d1812beac2e87e4ecb684ecd',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.missing_signature = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '--',
            ''
        ]))

        self.broken_signature = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '--',
            '65a3foobarbbb',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.incomplete_signature = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            '--',
            '7ceba0a0533bfae3d1812beac2e87e4ecb684ecd',
            ''
        ]))

        yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
        self.expired_whitelist = StringIO.StringIO('\n'.join([
            '20010603095527',
            'E' + yesterday.strftime("%Y%m%d%H%M%S"),
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            ''
        ]))

        tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
        self.time_valid_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E' + tomorrow.strftime("%Y%m%d%H%M%S"),
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            ''
        ]))


    def test_whitelist_creation(self):
        whitelist = cvmfs.Whitelist(self.sane_whitelist)
        last_modified = datetime.datetime(2015, 6, 3, 9, 55, 27, tzinfo=tzutc())
        expires       = datetime.datetime(2015, 7, 4, 9, 55, 27, tzinfo=tzutc())
        self.assertTrue(hasattr(whitelist, 'last_modified'))
        self.assertTrue(hasattr(whitelist, 'expires'))
        self.assertTrue(hasattr(whitelist, 'repository_name'))
        self.assertTrue(hasattr(whitelist, 'fingerprints'))
        self.assertEqual(last_modified   , whitelist.last_modified)
        self.assertEqual(expires         , whitelist.expires)
        self.assertEqual('atlas.cern.ch' , whitelist.repository_name)
        self.assertEqual(3               , len(whitelist.fingerprints))


    def test_whitelist_creation_from_file(self):
        whitelist = cvmfs.Whitelist.open(self.file_whitelist)
        last_modified = datetime.datetime(2015, 6, 3, 9, 55, 27, tzinfo=tzutc())
        expires       = datetime.datetime(2015, 7, 4, 9, 55, 27, tzinfo=tzutc())
        self.assertTrue(hasattr(whitelist, 'last_modified'))
        self.assertTrue(hasattr(whitelist, 'expires'))
        self.assertTrue(hasattr(whitelist, 'repository_name'))
        self.assertTrue(hasattr(whitelist, 'fingerprints'))
        self.assertEqual(last_modified   , whitelist.last_modified)
        self.assertEqual(expires         , whitelist.expires)
        self.assertEqual('atlas.cern.ch' , whitelist.repository_name)
        self.assertEqual(3               , len(whitelist.fingerprints))


    def test_unknown_field(self):
        self.assertRaises(cvmfs.UnknownWhitelistLine,
                          cvmfs.Whitelist, self.unknown_field_whitelist)


    def test_invalid_fingerprint(self):
        self.assertRaises(cvmfs.UnknownWhitelistLine,
                          cvmfs.Whitelist, self.invalid_fingerprint_whitelist)


    def test_invalid_timestamp(self):
        self.assertRaises(cvmfs.InvalidWhitelistTimestamp,
                          cvmfs.Whitelist, self.invalid_timestamp_whitelist)


    def test_missing_field(self):
        self.assertRaises(cvmfs.WhitelistValidityError,
                          cvmfs.Whitelist, self.missing_field_whitelist)


    def test_empty_whitelist(self):
        self.assertRaises(cvmfs.WhitelistValidityError,
                          cvmfs.Whitelist, self.no_fingerprints_whitelist)



    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Whitelist, self.missing_signature)


    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Whitelist, self.broken_signature)


    def test_incomplete_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Whitelist, self.incomplete_signature)


    def test_verify_signature(self):
        whitelist = cvmfs.Whitelist(self.sane_whitelist)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertTrue(signature_valid)


    def test_verify_mismatching_signature(self):
        whitelist = cvmfs.Whitelist(self.insane_whitelist_signature_mismatch)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertFalse(signature_valid)


    def test_verify_malformed_signature(self):
        whitelist = cvmfs.Whitelist(self.insane_whitelist_broken_signature)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertFalse(signature_valid)


    def test_contains_certificate(self):
        cert = open(self.certificate_file)
        whitelist   = cvmfs.Whitelist(self.sane_whitelist)
        certificate = cvmfs.Certificate(cert)
        cert.close()
        self.assertTrue(whitelist.contains(certificate))


    def test_doesnt_contain_certificate(self):
        cert = open(self.certificate_file)
        whitelist   = cvmfs.Whitelist(self.insane_whitelist_signature_mismatch)
        certificate = cvmfs.Certificate(cert)
        cert.close()
        self.assertFalse(whitelist.contains(certificate))


    def test_expired_whitelist(self):
        whitelist = cvmfs.Whitelist(self.expired_whitelist)
        self.assertTrue(whitelist.expired())


    def test_non_expired_whitelist(self):
        whitelist = cvmfs.Whitelist(self.time_valid_whitelist)
        self.assertFalse(whitelist.expired())
Exemple #9
0
class TestCertificate(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_certificate_ut_")

        self.compressed_certificate = '\n'.join([
            'eJyVlDmvq0gQhXN+xeTWlTFgbAcTNHSDm53LYkPWYNYGA+ay+deP3wtGI71oKqjgVEmfjo6qvr4+',
            'JSEVW3/J6NvHCpaBj36JX4yJsXysZRmkgiy78kgWxfaqXTe/gSUVdChppV4WVgJuoAAIDqZvLqYb',
            'wdB1Idr6kSGqVRmt1aStMEVcUbhcuCVNbBlB7GPUBFjRmkQNy5QLCv+mTNHt0aRPc7mWqWX6eGE+',
            'jTX9gLWgu91+ix+Cj9//avWf1P9Cmf9DLQpUmYBVZW9QPZzw0EUS8/EGgIAluIBfCzro8Mcv1DG5',
            'uXXiJVvch6UVmncOxjsx4OeXezj2km91Lkv2YcoUwDQe2dPO63GXs7XArfSeT2ZrZwUeW0OwrmvG',
            'Cq1A+zDv+kgO2h88H0YjvOT+t/0omYYGr1NYNbjn0+aGnrNRBJG2hGzpPuMyL+M+u+3oco6+UzGb',
            'XDF5y/OmhLsmx4kjT4SJyDRSEiihfs/ZJldeb5PuzcvEvqqATGtipEPGnxpOQa8mLwih97dGLFGU',
            'dE+rj+6ZUQV5HDa1mO7aYb2AWSHTZrSLnIshD/1OPJXyXCx5aXeZjYxj1DpUIXUa45Mz4mSGjCEJ',
            'J4dvB/Tuif/TddkpihbT7oVQuHlt1qw2T4f27noacEBhSgCgBbqRpncxLufU+qSAFMkF8JPAZ0hh',
            'BEt6PmAhFbrxGImb+mBn9nVRLn4pWJofPvQDUkA7vjxqcFH0ZHRnuMv4hy6sthzDV3+lQ+yRya+v',
            'KtKFbLlVMKmMvX47bHsL9aXonh1pvTzaM/LsTZKYkWtIALOd1xVrmL/pdztCObiofbB+n8ZZJZJu',
            'U5LzY7ubDUrKmMh+dTKhtM116awDgzK0r9flshqOJGK450+CrBaOgPLJJnl/BZFs7nX9B8WHlK20',
            'Kz3UaU15XjPJ3LaKz+RWdoqV6bArinRP+vfDXVulyiLzrKRJbIFr5ltSfufKAa0XuCufaRFkhnHO',
            'Qqo/NjQysr/VLo2Hl7M3mjmcF9torwQAEu9hQW8iNwZ/M78vH1nwz2/wD2fJXIA=',
            ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(compressed_cert)
        self.fingerprint = "81:8D:BE:49:A7:3E:F1:C4:DA:01:50:F0:80:D4:CB:27:96:80:1D"

        self.message_digest = "e380c3276b33440dd3e80af116fd6ee307b8ca6a"
        self.signature = 'q?\xd8p\r\x98w}3A\xc9/\x05\xd5\xf7\x19\xe1B-4\xcec\xe1\xa7\xb8\xb2&\xdbG\xd9\xc9\x81T\xfa\xdeX\xda\xd3\x11s\\H\xce\x82\xab\xcc\nP+*\x1eO\x02Q\x8f\xb0L\x11\xcd\x8fm\x10\xe7\xcc\xce\xc6\xd4hU\xf0d\x84\x8f\x82\xc3.G<\x1dt\x11\xd51\xb4\x13L t\x92\x02FO\x9e\x8e\xd7\x07#\xb9\xcd\x03)b\xffM\x13\x05v\xa43\xe9t\xbf\xfda\xa9\xb2K\x7f\x8b\xee\xd2\xa3\xb27\xb4\xa6\xc4\x88\xf5\xf9~}0\xdc\x8e\xfa\xf8q\xe6\x90\xf73w=\xd0\xff\xcbX\xdd\x10\x18e\x15\xec\xbb\xbf\xd6\x03\xc4\xf7\x86\x1fr\x11+\xbffmY\xff\x84q\xa1*{ \xca\xf2\nN\x8b\xfc\x85x3`\xab\xd5&\xb3\xb1\x03\xc6G5\xee\xebh\x91\xe3\xd5Y2\x96\x87\xe7\xd2\xaeu\xf7\xea\x80\x9c\xfc\x05HjZ\x81\xb7Qz\xd2p\x8e\xf6\xad\x0c\x9a\xf2\xdd"\x02\xf3\xab\xff2E\x03\xea\xd7W\x9b\xe2^`\xdc\xec5\x92z\xed\x02\xe7\xaa&'


    def test_load(self):
        cert_file = open(self.certificate_file)
        cert = cvmfs.Certificate(cert_file)
        cert_file.close()


    def test_get_openssl_x509(self):
        cert_file = open(self.certificate_file)
        cert = cvmfs.Certificate(cert_file)
        cert_file.close()
        x509 = cert.get_openssl_certificate()
        self.assertTrue(isinstance(x509, X509))


    def test_verify_message(self):
        cert_file = open(self.certificate_file)
        cert = cvmfs.Certificate(cert_file)
        cert_file.close()
        self.assertTrue(cert.verify(self.signature, self.message_digest))


    def test_verify_tampered_message(self):
        cert_file = open(self.certificate_file)
        cert = cvmfs.Certificate(cert_file)
        cert_file.close()
        self.assertFalse(cert.verify(self.signature, "I am Malory!"))


    def test_certificate_fingerprint(self):
        cert_file = open(self.certificate_file)
        cert = cvmfs.Certificate(cert_file)
        cert_file.close()
        self.assertEqual(self.fingerprint, cert.get_fingerprint())
class TestRepositoryWrapper(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_ut_repo_")
        self.mock_repo = MockRepository()

        self.cern_public_key = '\n'.join([
            '-----BEGIN PUBLIC KEY-----',
            'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukBusmYyFW8KJxVMmeCj',
            'N7vcU1mERMpDhPTa5PgFROSViiwbUsbtpP9CvfxB/KU1gggdbtWOTZVTQqA3b+p8',
            'g5Vve3/rdnN5ZEquxeEfIG6iEZta9Zei5mZMeuK+DPdyjtvN1wP0982ppbZzKRBu',
            'BbzR4YdrwwWXXNZH65zZuUISDJB4my4XRoVclrN5aGVz4PjmIZFlOJ+ytKsMlegW',
            'SNDwZO9z/YtBFil/Ca8FJhRPFMKdvxK+ezgq+OQWAerVNX7fArMC+4Ya5pF3ASr6',
            '3mlvIsBpejCUBygV4N2pxIcPJu/ZDaikmVvdPTNOTZlIFMf4zIP/YHegQSJmOyVp',
            'HQIDAQAB', '-----END PUBLIC KEY-----'
            ''
        ])
        pubkey = self.sandbox.write_to_temporary(self.cern_public_key)
        self.public_key_file = pubkey

    def tearDown(self):
        del self.mock_repo

    def test_open_repository_http(self):
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo, cvmfs.Repository))
        self.assertEqual(self.mock_repo.repo_name,
                         repo.manifest.repository_name)

    def test_open_repository_local(self):
        repo = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo, cvmfs.Repository))
        self.assertEqual(self.mock_repo.repo_name,
                         repo.manifest.repository_name)

    def test_open_repository_verification(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo1 = cvmfs.open_repository(self.mock_repo.url,
                                      public_key=self.mock_repo.public_key)
        self.assertTrue(isinstance(repo1, cvmfs.Repository))
        self.assertTrue(repo1.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo1.manifest.repository_name)

        repo2 = cvmfs.open_repository(self.mock_repo.dir,
                                      public_key=self.mock_repo.public_key)
        self.assertTrue(isinstance(repo2, cvmfs.Repository))
        self.assertTrue(repo2.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo2.manifest.repository_name)

        repo3 = cvmfs.open_repository(self.mock_repo.url)
        self.assertTrue(isinstance(repo3, cvmfs.Repository))
        self.assertTrue(repo3.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo3.manifest.repository_name)

        repo4 = cvmfs.open_repository(self.mock_repo.dir)
        self.assertTrue(isinstance(repo4, cvmfs.Repository))
        self.assertTrue(repo4.verify(self.mock_repo.public_key))
        self.assertEqual(self.mock_repo.repo_name,
                         repo4.manifest.repository_name)

    def test_wrong_public_key(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository,
                          self.mock_repo.url,
                          public_key=self.public_key_file)
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository,
                          self.mock_repo.dir,
                          public_key=self.public_key_file)

    def test_expired_whitelist(self):
        self.mock_repo.make_expired_whitelist()
        self.mock_repo.serve_via_http()
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository,
                          self.mock_repo.url,
                          public_key=self.mock_repo.public_key)
        self.assertRaises(cvmfs.RepositoryVerificationFailed,
                          cvmfs.open_repository,
                          self.mock_repo.dir,
                          public_key=self.mock_repo.public_key)

    def test_lookup(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url,
                                     public_key=self.mock_repo.public_key)
        rev = repo.get_current_revision()
        dirent = rev.lookup('/.cvmfsdirtab')
        self.assertIsNotNone(dirent)
        dirent = rev.lookup('/bar/4/foo')
        self.assertIsNotNone(dirent)
        dirent = rev.lookup('/bar/4/foobar')
        self.assertIsNone(dirent)
        # with trailing slash this time
        dirent1 = rev.lookup('/bar/4/foo/')
        self.assertIsNotNone(dirent1)
        dirent2 = rev.lookup('/bar/4/../4/foo/')
        self.assertIsNotNone(dirent2)
        self.assertEquals(dirent1.name, dirent2.name)

    def test_list(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url,
                                     public_key=self.mock_repo.public_key)
        rev = repo.get_current_revision()
        dirents = rev.list_directory('/')
        self.assertIsNotNone(dirents)
        self.assertEqual(3, len(dirents))
        dirents = rev.list_directory('/bar/3')
        self.assertIsNotNone(dirents)
        self.assertEqual(4, len(dirents))
        self.assertEquals('.cvmfscatalog', dirents[0].name)
        self.assertEquals('1', dirents[1].name)
        self.assertEquals('2', dirents[2].name)
        self.assertEquals('3', dirents[3].name)
        dirents = rev.list_directory('/bar/4/foo')
        self.assertIsNone(dirents)
        dirents = rev.list_directory('/fakedir')
        self.assertIsNone(dirents)
        # with trailing slash this time
        dirents = rev.list_directory('/bar/3/')
        self.assertIsNotNone(dirents)
        self.assertEqual(4, len(dirents))

    def test_revision(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url,
                                     public_key=self.mock_repo.public_key)
        rev3 = repo.get_current_revision()
        self.assertEqual(3, rev3.revision_number)
        dirent = rev3.lookup('/bar/3')
        self.assertIsNotNone(dirent)
        self.assertTrue(dirent.is_directory())

        rev1 = repo.get_revision(1)
        self.assertEqual(1, rev1.revision_number)
        dirent = rev1.lookup('/bar/3')
        self.assertIsNone(dirent)

    def test_catalog_lookup(self):
        self.mock_repo.make_valid_whitelist()
        self.mock_repo.serve_via_http()
        repo = cvmfs.open_repository(self.mock_repo.url,
                                     public_key=self.mock_repo.public_key)
        rev = repo.get_current_revision()
        for catalog in rev.catalogs():
            if catalog.root_prefix == '/bar/4':
                self.assertIsNone(
                    catalog.find_nested_for_path('/bar/4/foobar'))
                self.assertIsNone(catalog.find_nested_for_path('/bar/4/foo'))
                break
Exemple #11
0
class TestManifest(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_manifest_ut_")

        self.compressed_certificate = '\n'.join([
            'eJxllLmOq1gQQHO+YnLUMtDG4OAFdwODWc1iIDOYxew0q/n6cU8wyauodHSkUqmWr69PQCIrxj+I',
            '3FxFUhBwyS/8onRFQccSIZB0CNl4CkPLYrtHIGJgwLwaiuoln1cGAtuTAAa77pJVW0Ps2zYm76Kg',
            'nvJWJG++jAJ11hqjU4hRJw1/dx1W9t7Qie5bHQZGrRC2iBup/Xi7Do8Bdsmm42SjTJfwOvY2Q+p+',
            '4fsXmvh/tur7R0SiDFiPgE3vwuA2hoE+h5z/jl98ST0QxL/V/YqZbrWee40/RndpjxxohHe+iD5i',
            'sn9adJQV26F67SKlWBID2ARCG2Aqz4kF8EewO/TJIdBjNEnfUxspTHSCpaem45Yb8syRc9TTIx2+',
            'uFzoWgk0D604y9RQbvIkDius8Njmg9nLHWqP/TTlImm2Q+uxOo5VMDyuomey65ZMoYNy9ZAflRqx',
            'zEi59Ok6CBUOaU8RnRMyewW5AuQjjWuKPGba5GQBXcNbX17opJaP9Perjq2n1p84LJ0NqkSZSdTG',
            'YGm59Gttq4dMzLlHF4usbmzZ2sYX3ZTENYZ25TyEBnZSx3Z+xrnv+RT/KNT67UQ8mI5qxhUnw4sv',
            'c7vlXI+nLUWcxJQ3lWltV2+wOMWhnZBxaxdVBSGj5cTOpplKpS01Jk5hD1M43OJntNy6JQiRkJAS',
            'stkhUiSzcggCKwHA/Xu5KADMzwQIWJkytdPoGgp75OFEep4SeQo6v6rol4rsq+CZmRtfZWScBfm+',
            'BMf4ehh4kFIOdLRzvtepQEf38bK0l+v48NkIG8EG0ltj2NNxSplDLXqXlNi+V7N2pvhLKfpHf9YY',
            'qk7qyIxzQW6Syqqf0ZhIqW+9cLjT6CbNZyxwlpMqluqqfXDPfe5aOeXhwDn+4g5yaFEwYuF5Bm27',
            '6I3/cPKfMRgyvp/gyVwaa17S6soH9n5d70X582Mo+IQux83JaVQcGJM9U/39eOG9bm93etYjiTH4',
            'gj0NUHy+jF7bnPGSvZ+zZ4ZBy77nn37a4XAZXqNys2BYZqJDOeL9wVSZFJmCtyfaUAvWub3Ygee7',
            'HgJ//lD/XT0x8N+f4F81GFZb',
            ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(compressed_cert)

        self.sane_manifest = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20',
            'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D900',
            'S8722',
            'Natlas.cern.ch',
            'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
            'T1433937750',
            '--',
            'e7d58bfaaf75e9b725aa23c3a666daffd6351b8f',
            '"P4\x99a>LR\x8eE\x91\xdb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
        ]))
        self.file_manifest = self.sandbox.write_to_temporary(self.sane_manifest.getvalue())
        self.assertNotEqual(None, self.file_manifest)

        self.insane_manifest_tampered = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20',
            'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D900',
            'S8722',
            'Natlas-malicious.cern.ch',
            'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
            'T1433937750',
            '--',
            '892f6997d6046669640b8520842bc7c4f17cf269',
            '"P4\x99a>LR\x8eE\x91\xdb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
        ]))

        self.insane_manifest_broken_signature = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20',
            'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D900',
            'S8722',
            'Natlas.cern.ch',
            'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
            'T1433937750',
            '--',
            'e7d58bfaaf75e9b725aa23c3a666daffd6351b8f',
            '"P4\x99a>LR\x8eE\x91\xeb\xf13\xd9\xec!\xfe\x81\xf4\x1d\x98\xbe\xa7\x80:D\xcd0\x12\x06\x84\x0c(\x89\xe3\x01\x03s\x02\r\x14\xe3\x00{E\x18\x11E/\xa1)\xd7\xc7\x1a\x7f\xf1k"\x08\xbf\xdb3\xa1\xec-8\xb3z\xd5\x95\xcel\x82\x8a\x9a\xb5\xb6\x14\xd8sD\x80\xa7X\x0fx\x03T\x07\x12\xa6\'E\x04\x06\xf9\x17\'s\xeb\xfe\x19`l\xe7\xf4\x96,2\x84-\xa0\xbd.\x86#\xe0\xc09l\xc0\xcbZ\x95\x14# \xc4\xc7\xe1\x00\xc0\x84>\x8a\xae\x86\xc0\xe5\xa82\xc9\x86\xe5\x19\xe7\x85n\xac\xb4\xd8\x0bX\x81q\xdb\x97q\xe8\xacz\xd5\xfa+\n(\xe1\x0c\xff.\x91\xa2\x00\xfa"\xa5IS\xc5\xac\x13&\xda\x96+\xc6mU3\xb0\xd8\x92)Jd\xc14O\x02Gd\x90!\xdf\x06\x9f\xf4\xa5\xd2y\xab\x8c\xf6\x13\xe0d)\x90\xd7\x14\xb5\xf2f%\x80D\x94\xe0d\xb8\xe3\x17\xc4\x0f\xa5\x14\x08\xf4x\xd4\x7f\xa0T\xd4\xb9\xc0\x81d\x9bD\xe8V\xe5\x90\x16'
            #                      ^-- this byte used to be a 'd'
        ]))

        self.full_manifest = StringIO.StringIO('\n'.join([
            'C044206fcff4545283aaa452b80edfd5d8c740b20',
            'B75834368',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D900',
            'L0000000000000000000000000000000000000000',
            'S8722',
            'Natlas.cern.ch',
            'X0b457ac12225018e0a15330364c20529e15012ab',
            'H50c37f5517aea2ce9a22c3f17a7056a4f60e7d07',
            'T1433937750',
            'Gno',
            '--',
            'd79461faeaedc6875ee1592967811deee3fe2b99',
            'invalid signature'
        ]))

        self.unknown_field_manifest = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'D3600',
            'L0000000000000000000000000000000000000000',
            'Natlas.cern.ch',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'S4264',
            'Qi_am_unexpected!',
            ''
        ]))

        self.minimal_manifest_entries = [
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D3600',
            'S4264',
            'Natlas.cern.ch'
        ]

        self.minimal_manifest = StringIO.StringIO(
            '\n'.join(self.minimal_manifest_entries) + '\n')

        self.missing_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D3600',
            'S4264',
            'Natlas.cern.ch',
            '--',
            ''
        ]))

        self.broken_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D3600',
            'S4264',
            'Natlas.cern.ch',
            '--',
            'foobar',
            ''
        ]))

        self.incomplete_signature = StringIO.StringIO('\n'.join([
            'C600230b0ba7620426f2e898f1e1f43c5466efe59',
            'Rd41d8cd98f00b204e9800998ecf8427e',
            'D3600',
            'S4264',
            'Natlas.cern.ch',
            '--',
            'b748926022513a4398743d31c49578bc3a5fc3ef',
            ''
        ]))


    def test_manifest_creation(self):
        manifest = cvmfs.Manifest(self.sane_manifest)
        last_modified = datetime.datetime(2015, 6, 10, 12, 2, 30, tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20', manifest.root_catalog)
        self.assertEqual(900                                       , manifest.ttl)
        self.assertEqual('atlas.cern.ch'                           , manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e'        , manifest.root_hash)
        self.assertEqual(8722                                      , manifest.revision)
        self.assertEqual(last_modified                             , manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab', manifest.certificate)
        self.assertEqual(75834368                                  , manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', manifest.history_database)


    def test_mainfest_from_file(self):
        manifest = cvmfs.Manifest.open(self.file_manifest)
        last_modified = datetime.datetime(2015, 6, 10, 12, 2, 30, tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20', manifest.root_catalog)
        self.assertEqual(900                                       , manifest.ttl)
        self.assertEqual('atlas.cern.ch'                           , manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e'        , manifest.root_hash)
        self.assertEqual(8722                                      , manifest.revision)
        self.assertEqual(last_modified                             , manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab', manifest.certificate)
        self.assertEqual(75834368                                  , manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', manifest.history_database)


    def test_full_manifest(self):
        manifest = cvmfs.Manifest(self.full_manifest)
        last_modified = datetime.datetime(2015, 6, 10, 12, 2, 30, tzinfo=tzutc())
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'micro_catalog'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'last_modified'))
        self.assertTrue(hasattr(manifest, 'certificate'))
        self.assertTrue(hasattr(manifest, 'root_catalog_size'))
        self.assertTrue(hasattr(manifest, 'history_database'))
        self.assertTrue(hasattr(manifest, 'garbage_collectable'))
        self.assertEqual('044206fcff4545283aaa452b80edfd5d8c740b20', manifest.root_catalog)
        self.assertEqual(900                                       , manifest.ttl)
        self.assertEqual('0000000000000000000000000000000000000000', manifest.micro_catalog)
        self.assertEqual('atlas.cern.ch'                           , manifest.repository_name)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e'        , manifest.root_hash)
        self.assertEqual(8722                                      , manifest.revision)
        self.assertEqual(last_modified                             , manifest.last_modified)
        self.assertEqual('0b457ac12225018e0a15330364c20529e15012ab', manifest.certificate)
        self.assertEqual(75834368                                  , manifest.root_catalog_size)
        self.assertEqual('50c37f5517aea2ce9a22c3f17a7056a4f60e7d07', manifest.history_database)
        self.assertFalse(                                             manifest.garbage_collectable)


    def test_minimal_manifest(self):
        manifest = cvmfs.Manifest(self.minimal_manifest)
        self.assertTrue(hasattr(manifest, 'root_catalog'))
        self.assertTrue(hasattr(manifest, 'root_hash'))
        self.assertTrue(hasattr(manifest, 'ttl'))
        self.assertTrue(hasattr(manifest, 'revision'))
        self.assertTrue(hasattr(manifest, 'repository_name'))
        self.assertEqual('600230b0ba7620426f2e898f1e1f43c5466efe59', manifest.root_catalog)
        self.assertEqual('d41d8cd98f00b204e9800998ecf8427e'        , manifest.root_hash)
        self.assertEqual(3600                                      , manifest.ttl)
        self.assertEqual(4264                                      , manifest.revision)
        self.assertEqual('atlas.cern.ch'                           , manifest.repository_name)


    def test_unknown_manifest_field(self):
        self.assertRaises(cvmfs.UnknownManifestField,
                          cvmfs.Manifest, self.unknown_field_manifest)


    def test_invalid_manifest(self):
        for i in range(len(self.minimal_manifest_entries)):
            incomplete_manifest_entries = list(self.minimal_manifest_entries)
            del incomplete_manifest_entries[i]
            incomplete_manifest = StringIO.StringIO('\n'.join(incomplete_manifest_entries))
            self.assertRaises(cvmfs.ManifestValidityError,
                              cvmfs.Manifest, incomplete_manifest)


    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Manifest, self.missing_signature)


    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Manifest, self.broken_signature)


    def test_incomplete_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature,
                          cvmfs.Manifest, self.incomplete_signature)


    def test_verify_signature(self):
        manifest = cvmfs.Manifest(self.sane_manifest)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertTrue(is_valid)


    def test_verify_invalid_signature(self):
        manifest = cvmfs.Manifest(self.insane_manifest_tampered)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertFalse(is_valid)


    def test_verify_inconsistent_signature(self):
        manifest = cvmfs.Manifest(self.insane_manifest_broken_signature)
        cert = cvmfs.Certificate(open(self.certificate_file))
        is_valid = manifest.verify_signature(cert)
        self.assertFalse(is_valid)
Exemple #12
0
class TestWhitelist(unittest.TestCase):
    def setUp(self):
        self.sandbox = FileSandbox("py_whitelist_ut_")

        self.cern_public_key = '\n'.join([
            '-----BEGIN PUBLIC KEY-----',
            'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukBusmYyFW8KJxVMmeCj',
            'N7vcU1mERMpDhPTa5PgFROSViiwbUsbtpP9CvfxB/KU1gggdbtWOTZVTQqA3b+p8',
            'g5Vve3/rdnN5ZEquxeEfIG6iEZta9Zei5mZMeuK+DPdyjtvN1wP0982ppbZzKRBu',
            'BbzR4YdrwwWXXNZH65zZuUISDJB4my4XRoVclrN5aGVz4PjmIZFlOJ+ytKsMlegW',
            'SNDwZO9z/YtBFil/Ca8FJhRPFMKdvxK+ezgq+OQWAerVNX7fArMC+4Ya5pF3ASr6',
            '3mlvIsBpejCUBygV4N2pxIcPJu/ZDaikmVvdPTNOTZlIFMf4zIP/YHegQSJmOyVp',
            'HQIDAQAB', '-----END PUBLIC KEY-----'
            ''
        ])
        self.public_key_file = self.sandbox.write_to_temporary(
            self.cern_public_key)

        self.compressed_certificate = '\n'.join([
            'eJxllLmOq1gQQHO+YnLUMtDG4OAFdwODWc1iIDOYxew0q/n6cU8wyauodHSkUqmWr69PQCIrxj+I',
            '3FxFUhBwyS/8onRFQccSIZB0CNl4CkPLYrtHIGJgwLwaiuoln1cGAtuTAAa77pJVW0Ps2zYm76Kg',
            'nvJWJG++jAJ11hqjU4hRJw1/dx1W9t7Qie5bHQZGrRC2iBup/Xi7Do8Bdsmm42SjTJfwOvY2Q+p+',
            '4fsXmvh/tur7R0SiDFiPgE3vwuA2hoE+h5z/jl98ST0QxL/V/YqZbrWee40/RndpjxxohHe+iD5i',
            'sn9adJQV26F67SKlWBID2ARCG2Aqz4kF8EewO/TJIdBjNEnfUxspTHSCpaem45Yb8syRc9TTIx2+',
            'uFzoWgk0D604y9RQbvIkDius8Njmg9nLHWqP/TTlImm2Q+uxOo5VMDyuomey65ZMoYNy9ZAflRqx',
            'zEi59Ok6CBUOaU8RnRMyewW5AuQjjWuKPGba5GQBXcNbX17opJaP9Perjq2n1p84LJ0NqkSZSdTG',
            'YGm59Gttq4dMzLlHF4usbmzZ2sYX3ZTENYZ25TyEBnZSx3Z+xrnv+RT/KNT67UQ8mI5qxhUnw4sv',
            'c7vlXI+nLUWcxJQ3lWltV2+wOMWhnZBxaxdVBSGj5cTOpplKpS01Jk5hD1M43OJntNy6JQiRkJAS',
            'stkhUiSzcggCKwHA/Xu5KADMzwQIWJkytdPoGgp75OFEep4SeQo6v6rol4rsq+CZmRtfZWScBfm+',
            'BMf4ehh4kFIOdLRzvtepQEf38bK0l+v48NkIG8EG0ltj2NNxSplDLXqXlNi+V7N2pvhLKfpHf9YY',
            'qk7qyIxzQW6Syqqf0ZhIqW+9cLjT6CbNZyxwlpMqluqqfXDPfe5aOeXhwDn+4g5yaFEwYuF5Bm27',
            '6I3/cPKfMRgyvp/gyVwaa17S6soH9n5d70X582Mo+IQux83JaVQcGJM9U/39eOG9bm93etYjiTH4',
            'gj0NUHy+jF7bnPGSvZ+zZ4ZBy77nn37a4XAZXqNys2BYZqJDOeL9wVSZFJmCtyfaUAvWub3Ygee7',
            'HgJ//lD/XT0x8N+f4F81GFZb', ''
        ])
        compressed_cert = base64.b64decode(self.compressed_certificate)
        self.certificate_file = self.sandbox.write_to_temporary(
            zlib.decompress(compressed_cert))

        self.sane_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch',
            '9A:C3:7D:9E:C5:EB:CF:C8:92:EF:1D:7C:DB:56:DC:9C:83:9A:15:71 # ATLAS Release Manager, expires Sep 2011',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4 # Jakob Blomer, expires Jul 2011',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--', '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
        ]))
        self.file_whitelist = self.sandbox.write_to_temporary(
            self.sane_whitelist.getvalue())
        self.assertNotEqual(None, self.file_whitelist)

        self.insane_whitelist_signature_mismatch = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            '--', '3fa17242c67b5f3e54b85bc6e4544cd9d80a8ac7',
            '-yw\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
        ]))

        self.insane_whitelist_broken_signature = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch',
            '9A:C3:7D:9E:C5:EB:CF:C8:92:EF:1D:7C:DB:56:DC:9C:83:9A:15:71 # ATLAS Release Manager, expires Sep 2011',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4 # Jakob Blomer, expires Jul 2011',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--', '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yx\xfc\xa1\xa9\xc4Z%\xfd\x1d\xdf\x17\xa7\xa8\x99sB\x8a\xd9\xe0\xb1?f\xba\x1a\xa61\xa2v_\t\x1bl\x1a\xd9\x7f\n\x01\x9f\xf5\xf8j;\xf6\xaa;\xc7n\xfc\xc8\xa9{\xb5\xe2E.<?G\xfc|[f\'Xd\x05=u]\xc1\t?M\xb4\xab\x164\xc8\x0b\xec-<\xa0\xf6;E\x08\xdb@\xd6\x88!|\xb0f\x9c\xe3\x1a.\x04l\x8b)o\x89i\xdb\\\xf9\xff\xec\xebM\xc3\x87\x98\x8f\xc9\xea\xf1L\xa4\x08\x1b\xe9\xda\xf6\xff\xd1\xd3GN\x82AD\xd9\xc3\xd1\xdf\x17\x06\xd4Ad\xafS"\xc0\x8b\xc3\xeb\xa6\xe9\xaa\xf6\x90\xc7\x11PV\xa5Ls\xb5\x9b\xd2;\xdci\xdb\xa9\x03\x02\xf1\x8c\xb8^l\xb5\xb9\x11\xe1\xa0aM\x17\xe8\x8e\x7f;u\xa5S\xdc\xb2V\xa1\xe0\x91\x14\x02\xee\xe0a\x99\xda\xbc\x9cK\x1d\xa5\xf7\x13$:W\x91\xe6\xe6\x1f\xc4\xd7\x04\xbc\xe0\x86\xcb\x97\x17\x16 \xc0>\x04\x13\xbfr\xc6\xdeYn\xe8t+\xb4#\x05\xa6\xda\xef\xfd\xf6\x9c\xbf'
            #  ^-- that byte has been tampered with (was 'w')
        ]))

        self.unknown_field_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch', 'Foo',
            'EA:74:15:E5:F5:A4:34:7D:79:37:37:88:33:6A:1E:0D:D0:A4:18:25 # Steve\'s certificate',
            '--', '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw????Z%?????sB????f??1?v_ ', ''
        ]))

        self.invalid_fingerprint_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            'Natlas.cern.ch',
            'C1:2C:2G:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',  # 2G
            '--',
            '65a35687479260c90d38e4e114dbc345fde90bbb',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.invalid_timestamp_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E2015070409527',  # <--
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            ''
        ]))

        self.missing_field_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527',
            'E20150704095527',
            #'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4',
            '--',
            '006982cdd72e5342283e96db5c312ec65ab7f652',
            '-yw????Z%?????sB????f??1?v_ ',
            ''
        ]))

        self.no_fingerprints_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch', '--',
            '7ceba0a0533bfae3d1812beac2e87e4ecb684ecd',
            '-yw????Z%?????sB????f??1?v_ ', ''
        ]))

        self.missing_signature = StringIO.StringIO('\n'.join(
            ['20150603095527', 'E20150704095527', 'Natlas.cern.ch', '--', '']))

        self.broken_signature = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch', '--',
            '65a3foobarbbb', '-yw????Z%?????sB????f??1?v_ ', ''
        ]))

        self.incomplete_signature = StringIO.StringIO('\n'.join([
            '20150603095527', 'E20150704095527', 'Natlas.cern.ch', '--',
            '7ceba0a0533bfae3d1812beac2e87e4ecb684ecd', ''
        ]))

        yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
        self.expired_whitelist = StringIO.StringIO('\n'.join([
            '20010603095527', 'E' + yesterday.strftime("%Y%m%d%H%M%S"),
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4', ''
        ]))

        tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
        self.time_valid_whitelist = StringIO.StringIO('\n'.join([
            '20150603095527', 'E' + tomorrow.strftime("%Y%m%d%H%M%S"),
            'Natlas.cern.ch',
            'C1:2C:2F:7B:B6:8E:82:CF:50:8A:1D:2B:05:5F:14:1B:69:E6:44:E4', ''
        ]))

    def test_whitelist_creation(self):
        whitelist = cvmfs.Whitelist(self.sane_whitelist)
        last_modified = datetime.datetime(2015,
                                          6,
                                          3,
                                          9,
                                          55,
                                          27,
                                          tzinfo=tzutc())
        expires = datetime.datetime(2015, 7, 4, 9, 55, 27, tzinfo=tzutc())
        self.assertTrue(hasattr(whitelist, 'last_modified'))
        self.assertTrue(hasattr(whitelist, 'expires'))
        self.assertTrue(hasattr(whitelist, 'repository_name'))
        self.assertTrue(hasattr(whitelist, 'fingerprints'))
        self.assertEqual(last_modified, whitelist.last_modified)
        self.assertEqual(expires, whitelist.expires)
        self.assertEqual('atlas.cern.ch', whitelist.repository_name)
        self.assertEqual(3, len(whitelist.fingerprints))

    def test_whitelist_creation_from_file(self):
        whitelist = cvmfs.Whitelist.open(self.file_whitelist)
        last_modified = datetime.datetime(2015,
                                          6,
                                          3,
                                          9,
                                          55,
                                          27,
                                          tzinfo=tzutc())
        expires = datetime.datetime(2015, 7, 4, 9, 55, 27, tzinfo=tzutc())
        self.assertTrue(hasattr(whitelist, 'last_modified'))
        self.assertTrue(hasattr(whitelist, 'expires'))
        self.assertTrue(hasattr(whitelist, 'repository_name'))
        self.assertTrue(hasattr(whitelist, 'fingerprints'))
        self.assertEqual(last_modified, whitelist.last_modified)
        self.assertEqual(expires, whitelist.expires)
        self.assertEqual('atlas.cern.ch', whitelist.repository_name)
        self.assertEqual(3, len(whitelist.fingerprints))

    def test_unknown_field(self):
        self.assertRaises(cvmfs.UnknownWhitelistLine, cvmfs.Whitelist,
                          self.unknown_field_whitelist)

    def test_invalid_fingerprint(self):
        self.assertRaises(cvmfs.UnknownWhitelistLine, cvmfs.Whitelist,
                          self.invalid_fingerprint_whitelist)

    def test_invalid_timestamp(self):
        self.assertRaises(cvmfs.InvalidWhitelistTimestamp, cvmfs.Whitelist,
                          self.invalid_timestamp_whitelist)

    def test_missing_field(self):
        self.assertRaises(cvmfs.WhitelistValidityError, cvmfs.Whitelist,
                          self.missing_field_whitelist)

    def test_empty_whitelist(self):
        self.assertRaises(cvmfs.WhitelistValidityError, cvmfs.Whitelist,
                          self.no_fingerprints_whitelist)

    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Whitelist,
                          self.missing_signature)

    def test_missing_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Whitelist,
                          self.broken_signature)

    def test_incomplete_signature(self):
        self.assertRaises(cvmfs.IncompleteRootFileSignature, cvmfs.Whitelist,
                          self.incomplete_signature)

    def test_verify_signature(self):
        whitelist = cvmfs.Whitelist(self.sane_whitelist)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertTrue(signature_valid)

    def test_verify_mismatching_signature(self):
        whitelist = cvmfs.Whitelist(self.insane_whitelist_signature_mismatch)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertFalse(signature_valid)

    def test_verify_malformed_signature(self):
        whitelist = cvmfs.Whitelist(self.insane_whitelist_broken_signature)
        signature_valid = whitelist.verify_signature(self.public_key_file)
        self.assertFalse(signature_valid)

    def test_contains_certificate(self):
        with open(self.certificate_file) as cert:
            whitelist = cvmfs.Whitelist(self.sane_whitelist)
            certificate = cvmfs.Certificate(cert)
            self.assertTrue(whitelist.contains(certificate))

    def test_doesnt_contain_certificate(self):
        with open(self.certificate_file) as cert:
            whitelist = cvmfs.Whitelist(
                self.insane_whitelist_signature_mismatch)
            certificate = cvmfs.Certificate(cert)
            self.assertFalse(whitelist.contains(certificate))

    def test_expired_whitelist(self):
        whitelist = cvmfs.Whitelist(self.expired_whitelist)
        self.assertTrue(whitelist.expired())

    def test_non_expired_whitelist(self):
        whitelist = cvmfs.Whitelist(self.time_valid_whitelist)
        self.assertFalse(whitelist.expired())