Beispiel #1
0
 def test_load_channel_bad_signature_gets_fixed(self, config_d):
     # Like above, but the second download of the image signing key results
     # in a properly signed channels.json file.
     sign(self._channels_path, 'spare.gpg')
     setup_keyrings()
     self._state.run_thru('get_channel')
     # At this point, the state machine has determined that the
     # channels.json file is not signed with the cached image signing key,
     # so it will try to download a new imaging signing key.  Let's put one
     # on the server, but it will not match the key that channels.json is
     # signed with.
     self.assertIsNone(self._state.channels)
     setup_keyring_txz(
         'spare.gpg', 'image-master.gpg', dict(type='image-signing'),
         os.path.join(self._serverdir, 'gpg', 'image-signing.tar.xz'))
     # This will succeed by grabbing a new image-signing key.
     config = Configuration(config_d)
     with open(config.gpg.image_signing, 'rb') as fp:
         checksum = hashlib.md5(fp.read()).digest()
     next(self._state)
     with open(config.gpg.image_signing, 'rb') as fp:
         self.assertNotEqual(checksum, hashlib.md5(fp.read()).digest())
     # The next state transition will find that the channels.json file is
     # properly signed.
     next(self._state)
     self.assertIsNotNone(self._state.channels)
     self.assertEqual(
         self._state.channels.daily.devices.nexus7.keyring.signature,
         '/daily/nexus7/device-keyring.tar.xz.asc')
Beispiel #2
0
    def test_download_winners(self):
        # Check that all the winning path's files are downloaded.
        setup_keyrings()
        state = State()
        touch_build(100)
        # Run the state machine until we download the files.
        state.run_thru('download_files')

        # The B path files contain their checksums.
        def assert_file_contains(filename, contents):
            path = os.path.join(config.updater.cache_partition, filename)
            with open(path, encoding='utf-8') as fp:
                self.assertEqual(fp.read(), contents)

        assert_file_contains('5.txt', '345')
        assert_file_contains('6.txt', '456')
        assert_file_contains('7.txt', '567')
        # Delta B.1 files.
        assert_file_contains('8.txt', '678')
        assert_file_contains('9.txt', '789')
        assert_file_contains('a.txt', '89a')
        # Delta B.2 files.
        assert_file_contains('b.txt', '9ab')
        assert_file_contains('d.txt', 'fed')
        assert_file_contains('c.txt', 'edc')
Beispiel #3
0
    def test_download_winners_overwrite(self):
        # Check that all the winning path's files are downloaded, even if
        # those files already exist in their destination paths.
        setup_keyrings()
        state = State()
        touch_build(100)
        # Run the state machine until we download the files.
        for basename in '56789abcd':
            base = os.path.join(config.updater.cache_partition, basename)
            path = base + '.txt'
            with open(path, 'w', encoding='utf-8') as fp:
                print('stale', file=fp)
        state.run_thru('download_files')

        # The B path files contain their checksums.
        def assert_file_contains(filename, contents):
            path = os.path.join(config.updater.cache_partition, filename)
            with open(path, encoding='utf-8') as fp:
                self.assertEqual(fp.read(), contents)

        assert_file_contains('5.txt', '345')
        assert_file_contains('6.txt', '456')
        assert_file_contains('7.txt', '567')
        # Delta B.1 files.
        assert_file_contains('8.txt', '678')
        assert_file_contains('9.txt', '789')
        assert_file_contains('a.txt', '89a')
        # Delta B.2 files.
        assert_file_contains('b.txt', '9ab')
        assert_file_contains('d.txt', 'fed')
        assert_file_contains('c.txt', 'edc')
Beispiel #4
0
 def test_download_winners_signed_by_wrong_key(self):
     # There is a device key, but the image files are signed by the image
     # signing key, which according to the spec means the files are not
     # signed correctly.
     setup_keyrings()
     # To set up the device signing key, we need to load this channels.json
     # file and copy the device keyring to the server.
     copy('winner.channels_02.json', self._serverdir, 'channels.json')
     sign(os.path.join(self._serverdir, 'channels.json'),
          'image-signing.gpg')
     setup_keyring_txz(
         'device-signing.gpg', 'image-signing.gpg',
         dict(type='device-signing'),
         os.path.join(self._serverdir, 'stable', 'nexus7', 'device.tar.xz'))
     sign(os.path.join(self._serverdir, self._indexpath),
          'device-signing.gpg')
     # All the downloadable files are now signed with a bogus key.
     setup_index('winner.index_02.json', self._serverdir, 'spare.gpg')
     touch_build(100)
     # Run the state machine until just before we download the files.
     state = State()
     state.run_until('download_files')
     # The next state transition will fail because of the missing signature.
     self.assertRaises(SignatureError, next, state)
     # There are no downloaded files.
     txtfiles = set(filename for filename in os.listdir(config.tempdir)
                    if os.path.splitext(filename)[1] == '.txt')
     self.assertEqual(len(txtfiles), 0)
Beispiel #5
0
 def test_bad_signature(self):
     # Both files are downloaded, but the signature does not match the
     # image-master key.
     setup_keyrings()
     # Use the spare key as the blacklist, signed by itself.  Since this
     # won't match the image-signing key, the check will fail.
     server_path = os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz')
     setup_keyring_txz('spare.gpg', 'spare.gpg', dict(type='blacklist'),
                       server_path)
     with self.assertRaises(SignatureError) as cm:
         get_keyring('blacklist', 'gpg/blacklist.tar.xz', 'image-master')
     error = cm.exception
     # The local file name will be keyring.tar.xz in the cache directory.
     basename = os.path.basename
     self.assertEqual(basename(error.data_path), 'keyring.tar.xz')
     self.assertEqual(basename(error.signature_path), 'keyring.tar.xz.asc')
     # The crafted blacklist.tar.xz file will have an unpredictable
     # checksum due to tarfile variablility.
     with open(server_path, 'rb') as fp:
         checksum = hashlib.md5(fp.read()).hexdigest()
     self.assertEqual(error.data_checksum, checksum)
     # The signature file's checksum is also unpredictable.
     with open(server_path + '.asc', 'rb') as fp:
         checksum = hashlib.md5(fp.read()).hexdigest()
     self.assertEqual(error.signature_checksum, checksum)
Beispiel #6
0
 def test_archive_and_image_masters(self):
     # There is also a system image master key which is also persistent,
     # mandatory, shipped, and non-expiring.  It should never need
     # changing, but it is possible to do so if it gets compromised.
     setup_keyrings()
     keyrings = [
         config.gpg.archive_master,
         config.gpg.image_master,
         ]
     with Context(*keyrings) as ctx:
         # The context now knows about two keys.
         self.assertEqual(
             ctx.fingerprints,
             set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880',
                  '47691DEF271FB2B1FD3364513BC6AF1818E7F5FB']))
         self.assertEqual(
             ctx.key_ids,
             set(['E0979A7EADE8E880', '3BC6AF1818E7F5FB']))
         # Here are all the available uids.
         uids = []
         for key in ctx.keys:
             uids.extend(key['uids'])
         self.assertEqual(uids, [
             'Ubuntu Archive Master Signing Key (TEST) '
                 '<*****@*****.**>',
             'Ubuntu System Image Master Signing Key (TEST) '
                 '<*****@*****.**>'
             ])
Beispiel #7
0
 def test_archive_image_masters_image_signing(self):
     # In addition to the above, there is also a image signing key which is
     # generally what downloaded files are signed with.  This key is also
     # persistent, mandatory, and shipped.  It is updated regularly and
     # expires every two years.
     setup_keyrings()
     keyrings = [
         config.gpg.archive_master,
         config.gpg.image_master,
         config.gpg.image_signing,
         ]
     with Context(*keyrings) as ctx:
         # The context now knows about two keys.
         self.assertEqual(
             ctx.fingerprints,
             set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880',
                  '47691DEF271FB2B1FD3364513BC6AF1818E7F5FB',
                  'C5E39F07D159687BA3E82BD15A0DE8A4F1F1846F']))
         self.assertEqual(
             ctx.key_ids,
             set(['E0979A7EADE8E880',
                  '3BC6AF1818E7F5FB',
                  '5A0DE8A4F1F1846F']))
         # Here are all the available uids.
         uids = []
         for key in ctx.keys:
             uids.extend(key['uids'])
         self.assertEqual(uids, [
             'Ubuntu Archive Master Signing Key (TEST) '
                 '<*****@*****.**>',
             'Ubuntu System Image Master Signing Key (TEST) '
                 '<*****@*****.**>',
             'Ubuntu System Image Signing Key (TEST) '
                 '<*****@*****.**>',
             ])
Beispiel #8
0
 def test_load_channel_good_path(self):
     # A channels.json file signed by the image signing key, no blacklist.
     sign(self._channels_path, 'image-signing.gpg')
     setup_keyrings()
     self._state.run_thru('get_channel')
     channels = self._state.channels
     self.assertEqual(channels.daily.devices.nexus7.keyring.signature,
                      '/daily/nexus7/device-keyring.tar.xz.asc')
Beispiel #9
0
 def test_asc_file_missing(self):
     # If the tar.xz.asc file cannot be downloaded, an error is raised.
     tarxz_path = os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz')
     setup_keyrings()
     setup_keyring_txz('spare.gpg', 'archive-master.gpg',
                       dict(type='blacklist'), tarxz_path)
     os.remove(tarxz_path + '.asc')
     self.assertRaises(FileNotFoundError, get_keyring, 'blacklist',
                       'gpg/blacklist.tar.xz', 'image-master')
Beispiel #10
0
 def test_load_channel_over_https_port_with_http_fails(self):
     # We maliciously put an HTTP server on the HTTPS port.
     setup_keyrings()
     state = State()
     # Try to get the blacklist.  This will fail silently since it's okay
     # not to find a blacklist.
     state.run_thru('get_blacklist_1')
     # This will fail to get the channels.json file.
     with make_http_server(self._serverdir, 8943):
         self.assertRaises(FileNotFoundError, next, state)
Beispiel #11
0
 def test_good_path(self):
     # Everything checks out, with the simplest possible keyring.json.
     setup_keyrings('archive-master')
     setup_keyring_txz(
         'spare.gpg', 'archive-master.gpg', dict(type='image-master'),
         os.path.join(self._serverdir, 'gpg', 'image-master.tar.xz'))
     get_keyring('image-master', 'gpg/image-master.tar.xz',
                 'archive-master')
     with Context(config.gpg.archive_master) as ctx:
         self.assertEqual(ctx.fingerprints,
                          set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880']))
Beispiel #12
0
 def test_path_blacklist(self):
     # Get the blacklist keyring.
     setup_keyrings('archive-master', 'image-master')
     setup_keyring_txz(
         'spare.gpg', 'image-master.gpg', dict(type='blacklist'),
         os.path.join(self._serverdir, 'gpg/blacklist.tar.xz'))
     url = 'gpg/blacklist.tar.xz'.format(config.channel, config.device)
     get_keyring('blacklist', url, 'image-master')
     blacklist_path = os.path.join(config.tempdir, 'blacklist.tar.xz')
     with Context(blacklist_path) as ctx:
         self.assertEqual(ctx.fingerprints,
                          set(['94BE2CECF8A5AF9F3A10E2A6526B7016C3D2FB44']))
Beispiel #13
0
 def test_bad_json_type(self):
     # This type, while the signatures match, the keyring type in the
     # keyring.json file does not match.
     setup_keyrings()
     setup_keyring_txz(
         'device-signing.gpg', 'image-master.gpg', dict(type='master'),
         os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz'))
     with self.assertRaises(KeyringError) as cm:
         get_keyring('blacklist', 'gpg/blacklist.tar.xz', 'image-master')
     self.assertEqual(
         cm.exception.message,
         'keyring type mismatch; wanted: blacklist, got: master')
Beispiel #14
0
 def test_bad_json_model(self):
     # Similar to above, but with a non-matching model name.
     setup_keyrings()
     setup_keyring_txz(
         'device-signing.gpg', 'image-master.gpg',
         dict(type='blacklist', model='nexus0'),
         os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz'))
     with self.assertRaises(KeyringError) as cm:
         get_keyring('blacklist', 'gpg/blacklist.tar.xz', 'image-master')
     self.assertEqual(
         cm.exception.message,
         'keyring model mismatch; wanted: nexus7, got: nexus0')
Beispiel #15
0
 def test_good_path_model(self):
     # Everything checks out with the model specified.
     setup_keyrings()
     setup_keyring_txz(
         'spare.gpg', 'archive-master.gpg',
         dict(type='image-master', model='nexus7'),
         os.path.join(self._serverdir, 'gpg', 'image-master.tar.xz'))
     get_keyring('image-master', 'gpg/image-master.tar.xz',
                 'archive-master')
     with Context(config.gpg.archive_master) as ctx:
         self.assertEqual(ctx.fingerprints,
                          set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880']))
Beispiel #16
0
 def test_calculate_winner(self):
     # Calculate the winning upgrade path.
     setup_keyrings()
     state = State()
     touch_build(100)
     # Run the state machine long enough to get the candidates and winner.
     state.run_thru('calculate_winner')
     # There are three candidate upgrade paths.
     descriptions = []
     for image in state.winner:
         # There's only one description per image so order doesn't matter.
         descriptions.extend(image.descriptions.values())
     self.assertEqual(descriptions, ['Full B', 'Delta B.1', 'Delta B.2'])
Beispiel #17
0
 def test_path_device_signing_keyring(self):
     # Get the device signing keyring.
     setup_keyrings('archive-master', 'image-master', 'image-signing')
     setup_keyring_txz(
         'spare.gpg', 'image-signing.gpg', dict(type='device-signing'),
         os.path.join(self._serverdir, 'gpg', 'stable', 'nexus7',
                      'device-signing.tar.xz'))
     url = 'gpg/{}/{}/device-signing.tar.xz'.format(config.channel,
                                                    config.device)
     get_keyring('device-signing', url, 'image-signing')
     with Context(config.gpg.device_signing) as ctx:
         self.assertEqual(ctx.fingerprints,
                          set(['94BE2CECF8A5AF9F3A10E2A6526B7016C3D2FB44']))
Beispiel #18
0
 def test_good_path_expiry(self):
     # Everything checks out, with the expiration date specified.
     next_year = datetime.now(tz=timezone.utc) + timedelta(days=365)
     setup_keyrings('archive-master')
     setup_keyring_txz(
         'spare.gpg', 'archive-master.gpg',
         dict(type='image-master', expiry=next_year.timestamp()),
         os.path.join(self._serverdir, 'gpg', 'image-master.tar.xz'))
     get_keyring('image-master', 'gpg/image-master.tar.xz',
                 'archive-master')
     with Context(config.gpg.archive_master) as ctx:
         self.assertEqual(ctx.fingerprints,
                          set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880']))
Beispiel #19
0
 def test_expired(self):
     # Similar to above, but the expiry key in the json names a utc
     # timestamp that has already elapsed.
     last_year = datetime.now(tz=timezone.utc) + timedelta(days=-365)
     setup_keyrings()
     setup_keyring_txz(
         'device-signing.gpg', 'image-master.gpg',
         dict(type='blacklist',
              model='nexus7',
              expiry=last_year.timestamp()),
         os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz'))
     with self.assertRaises(KeyringError) as cm:
         get_keyring('blacklist', 'gpg/blacklist.tar.xz', 'image-master')
     self.assertEqual(cm.exception.message, 'expired keyring timestamp')
Beispiel #20
0
 def test_load_index_good_path(self):
     # Load the index.json pointed to by the channels.json.  All signatures
     # validate correctly and there is no device keyring or blacklist.
     self._copysign('index.channels_05.json', 'channels.json',
                    'image-signing.gpg')
     # index.index_04.json path B will win, with no bootme flags.
     self._copysign('index.index_04.json', 'stable/nexus7/index.json',
                    'image-signing.gpg')
     setup_keyrings()
     state = State()
     state.run_thru('get_index')
     self.assertEqual(
         state.index.global_.generated_at,
         datetime(2013, 4, 29, 18, 45, 27, tzinfo=timezone.utc))
     self.assertEqual(state.index.images[0].files[1].checksum, 'bcd')
Beispiel #21
0
 def test_load_index_with_bad_keyring(self):
     # Here, the index.json file is signed with a defective device keyring.
     self._copysign('index.channels_02.json', 'channels.json',
                    'image-signing.gpg')
     # This will be signed by a keyring that is not the device keyring.
     self._copysign('index.index_04.json', 'stable/nexus7/index.json',
                    'spare.gpg')
     setup_keyrings()
     setup_keyring_txz(
         'device-signing.gpg', 'image-signing.gpg',
         dict(type='device-signing'),
         os.path.join(self._serverdir, 'stable', 'nexus7', 'device.tar.xz'))
     state = State()
     state.run_until('get_index')
     self.assertRaises(SignatureError, next, state)
Beispiel #22
0
 def test_missing_device(self):
     # The system's device does not exist.
     self._copysign('index.channels_04.json', 'channels.json',
                    'image-signing.gpg')
     # index.index_04.json path B will win, with no bootme flags.
     self._copysign('index.index_04.json', 'stable/nexus7/index.json',
                    'image-signing.gpg')
     setup_keyrings()
     # Our device (nexus7) isn't in the channels.json file, so there's
     # nothing to do.  Running the state machine to its conclusion leaves
     # us with no index file.
     state = State()
     list(state)
     # There really is nothing left to do.
     self.assertIsNone(state.index)
Beispiel #23
0
 def test_no_download_winners_with_missing_signature(self):
     # If one of the download files is missing a signature, none of the
     # files get downloaded and get_files() fails.
     setup_keyrings()
     state = State()
     touch_build(100)
     # Remove a signature.
     os.remove(os.path.join(self._serverdir, '6/7/8.txt.asc'))
     # Run the state machine to calculate the winning path.
     state.run_until('download_files')
     # The next state transition will fail because of the missing signature.
     self.assertRaises(FileNotFoundError, next, state)
     # There are no downloaded files.
     txtfiles = set(filename for filename in os.listdir(config.tempdir)
                    if os.path.splitext(filename)[1] == '.txt')
     self.assertEqual(len(txtfiles), 0, txtfiles)
Beispiel #24
0
 def test_no_download_winners_with_bad_signature(self):
     # If one of the download files has a bad a signature, none of the
     # downloaded files are available.
     setup_keyrings()
     state = State()
     touch_build(100)
     # Break a signature
     sign(os.path.join(self._serverdir, '6', '7', '8.txt'), 'spare.gpg')
     # Run the state machine to calculate the winning path.
     state.run_until('download_files')
     # The next state transition will fail because of the missing signature.
     self.assertRaises(SignatureError, next, state)
     # There are no downloaded files.
     txtfiles = set(filename for filename in os.listdir(config.tempdir)
                    if os.path.splitext(filename)[1] == '.txt')
     self.assertEqual(len(txtfiles), 0)
Beispiel #25
0
 def test_download_winners_bad_checksums(self):
     # Similar to the various good paths, except because the checksums are
     # wrong in this index.json file, we'll get a error when downloading.
     copy('winner.index_01.json', self._serverdir, self._indexpath)
     sign(os.path.join(self._serverdir, self._indexpath),
          'image-signing.gpg')
     setup_index('winner.index_01.json', self._serverdir,
                 'image-signing.gpg')
     setup_keyrings()
     state = State()
     touch_build(100)
     # Run the state machine until we're prepped to download
     state.run_until('download_files')
     # Now try to download the files and get the error.
     with self.assertRaises(FileNotFoundError) as cm:
         next(state)
     self.assertIn('HASH ERROR', str(cm.exception))
Beispiel #26
0
 def test_destination_blacklist(self):
     # Like above, but the blacklist files end up in the temporary
     # directory, since it's never persistent.
     setup_keyrings('archive-master', 'image-master')
     setup_keyring_txz(
         'spare.gpg', 'image-master.gpg', dict(type='blacklist'),
         os.path.join(self._serverdir, 'gpg', 'blacklist.tar.xz'))
     txz_path = os.path.join(config.updater.data_partition,
                             'blacklist.tar.xz')
     asc_path = txz_path + '.asc'
     self.assertFalse(os.path.exists(txz_path))
     self.assertFalse(os.path.exists(asc_path))
     get_keyring('blacklist', 'gpg/blacklist.tar.xz', 'image-master')
     self.assertTrue(os.path.exists(txz_path))
     self.assertTrue(os.path.exists(asc_path))
     with Context(config.gpg.image_master) as ctx:
         self.assertTrue(ctx.verify(asc_path, txz_path))
Beispiel #27
0
 def test_destination_image_signing(self):
     # When a keyring is downloaded, we preserve its .tar.xz and
     # .tar.xz.asc files.
     setup_keyrings('archive-master', 'image-master')
     setup_keyring_txz(
         'image-signing.gpg', 'image-master.gpg',
         dict(type='image-signing'),
         os.path.join(self._serverdir, 'gpg', 'image-signing.tar.xz'))
     asc_path = config.gpg.image_signing + '.asc'
     self.assertFalse(os.path.exists(config.gpg.image_signing))
     self.assertFalse(os.path.exists(asc_path))
     get_keyring('image-signing', 'gpg/image-signing.tar.xz',
                 'image-master')
     self.assertTrue(os.path.exists(config.gpg.image_signing))
     self.assertTrue(os.path.exists(asc_path))
     with Context(config.gpg.image_master) as ctx:
         self.assertTrue(ctx.verify(asc_path, config.gpg.image_signing))
Beispiel #28
0
 def test_blacklisted_signature(self):
     # Normally, the signature would be good, except that the fingerprint
     # of the device signing key is blacklisted.
     setup_keyrings('archive-master', 'image-master')
     blacklist = os.path.join(config.tempdir, 'gpg', 'blacklist.tar.xz')
     # Blacklist the image-master keyring.
     setup_keyring_txz('image-master.gpg', 'image-master.gpg',
                       dict(type='blacklist'), blacklist)
     setup_keyring_txz(
         'image-signing.gpg', 'image-master.gpg',
         dict(type='image-signing'),
         os.path.join(self._serverdir, 'gpg', 'image-signing.tar.xz'))
     # Now put an image-signing key on the server and attempt to download
     # it.  Because the image-master is blacklisted, this will fail.
     self.assertRaises(SignatureError, get_keyring, 'image-signing',
                       'gpg/image-signing.tar.xz', 'image-master',
                       blacklist)
Beispiel #29
0
 def test_load_index_with_device_keyring(self):
     # Here, the index.json file is signed with a device keyring.
     self._copysign('index.channels_02.json', 'channels.json',
                    'image-signing.gpg')
     # index.index_04.json.json path B will win, with no bootme flags.
     self._copysign('index.index_04.json', 'stable/nexus7/index.json',
                    'device-signing.gpg')
     setup_keyrings()
     setup_keyring_txz(
         'device-signing.gpg', 'image-signing.gpg',
         dict(type='device-signing'),
         os.path.join(self._serverdir, 'stable', 'nexus7', 'device.tar.xz'))
     state = State()
     state.run_thru('get_index')
     self.assertEqual(
         state.index.global_.generated_at,
         datetime(2013, 4, 29, 18, 45, 27, tzinfo=timezone.utc))
     self.assertEqual(state.index.images[0].files[1].checksum, 'bcd')
Beispiel #30
0
 def test_archive_master(self):
     # The archive master keyring contains the master key.  This a
     # persistent, mandatory, shipped, non-expiring key.
     setup_keyrings()
     with Context(config.gpg.archive_master) as ctx:
         # There is only one key in the master keyring.
         self.assertEqual(
             ctx.fingerprints,
             set(['289518ED3A0C4CFE975A0B32E0979A7EADE8E880']))
         self.assertEqual(
             ctx.key_ids,
             set(['E0979A7EADE8E880']))
         # Here is some useful information about the master key.
         self.assertEqual(len(ctx.keys), 1)
         master = ctx.keys[0]
         self.assertEqual(
             master['uids'],
             ['Ubuntu Archive Master Signing Key (TEST) '
              '<*****@*****.**>'])