Beispiel #1
0
def test_demo_timeserver():
    """
  Test the demo timeserver.
  # TODO: Consider moving these tests into a demo integration test module.
  """

    I_TO_PRINT = TO_PRINT + uptane.YELLOW + '[test_demo_timeserver()]: ' + uptane.ENDCOLORS
    #TODO: Print to be deleted
    print(str('%s %s' % (I_TO_PRINT, 'Testing the Demo Timeserver')))
    #TODO: Until here

    # Prepare to validate signatures.
    timeserver_key_pub = demo.import_public_key('timeserver')
    tuf.formats.ANYKEY_SCHEMA.check_match(timeserver_key_pub)

    # Fetch a normal signed time attestation, without ASN.1 format or DER
    # encoding, and validate the signature.
    signed_time = timeserver.get_signed_time([1, 2])

    assert len(
        signed_time['signatures']) == 1, 'Unexpected number of signatures.'
    assert uptane.common.verify_signature_over_metadata(
        timeserver_key_pub,
        signed_time['signatures'][0],
        signed_time['signed'],
        DATATYPE_TIME_ATTESTATION,
        metadata_format='json'
    ), 'Demo Timeserver self-test fail: unable to verify signature over JSON.'

    # Fetch a DER-encoded converted-to-ASN.1 signed time attestation, with a
    # signature over the DER encoding.
    der_signed_time = timeserver.get_signed_time_der([2, 9, 151])

    # Encapsulate that in a Binary object for XML-RPC.
    xb_der_signed_time = xmlrpc_client.Binary(der_signed_time)
    assert der_signed_time == xb_der_signed_time.data, \
        'Demo Timeserver self-test fail: xmlrpc Binary encapsulation issue'

    # Validate that signature.
    for pydict_again in [
            asn1_codec.convert_signed_der_to_dersigned_json(
                der_signed_time, DATATYPE_TIME_ATTESTATION),
            asn1_codec.convert_signed_der_to_dersigned_json(
                xb_der_signed_time.data, DATATYPE_TIME_ATTESTATION)
    ]:

        assert uptane.common.verify_signature_over_metadata(
            timeserver_key_pub,
            pydict_again['signatures'][0],
            pydict_again['signed'],
            DATATYPE_TIME_ATTESTATION,
            metadata_format='der'
        ), 'Demo Timeserver self-test fail: unable to verify signature over DER'
Beispiel #2
0
  def test_25_generate_signed_ecu_manifest(self):
    """
    Tests uptane.clients.secondary.Secondary::generate_signed_ecu_manifest()
    """

    # We'll just test one of the three client instances, since it shouldn't
    # make a difference.
    ecu_manifest = secondary_instances[0].generate_signed_ecu_manifest()

    # If the ECU Manifest is in DER format, check its format and then
    # convert back to JSON so that we can inspect it further.
    if tuf.conf.METADATA_FORMAT == 'der':
      uptane.formats.DER_DATA_SCHEMA.check_match(ecu_manifest)
      ecu_manifest = asn1_codec.convert_signed_der_to_dersigned_json(
          ecu_manifest, DATATYPE_ECU_MANIFEST)

    # Now it's not in DER format, whether or not it started that way.
    # Check its format and inspect it.
    uptane.formats.SIGNABLE_ECU_VERSION_MANIFEST_SCHEMA.check_match(
        ecu_manifest)

    # Test contents of the ECU Manifest.
    # Make sure there is exactly one signature. (Not specified by the
    # Implementation Specification, but the way we do it. Using more is
    # unlikely to be particularly useful).
    self.assertEqual(1, len(ecu_manifest['signatures']))

    # TODO: Check some values from the ECU Manifest

    # Check the signature on the ECU Manifest.
    self.assertTrue(uptane.common.verify_signature_over_metadata(
        TestSecondary.secondary_ecu_key,
        ecu_manifest['signatures'][0], # TODO: Deal with 1-sig assumption?
        ecu_manifest['signed'],
        DATATYPE_ECU_MANIFEST))
Beispiel #3
0
  def test_25_generate_signed_vehicle_manifest(self):

    vehicle_manifest = TestPrimary.instance.generate_signed_vehicle_manifest()

    # If the vehicle manifest is in DER format, check its format and then
    # convert back to JSON so that we can inspect it further.
    if tuf.conf.METADATA_FORMAT == 'der':
      uptane.formats.DER_DATA_SCHEMA.check_match(vehicle_manifest)
      vehicle_manifest = asn1_codec.convert_signed_der_to_dersigned_json(
          vehicle_manifest, DATATYPE_VEHICLE_MANIFEST)

    # Now it's not in DER format, whether or not it started that way.
    # Check its format and inspect it.
    uptane.formats.SIGNABLE_VEHICLE_VERSION_MANIFEST_SCHEMA.check_match(
        vehicle_manifest)

    # Test contents of vehicle manifest.
    # Make sure there is exactly one signature.
    self.assertEqual(1, len(vehicle_manifest['signatures']))
    # Make sure that the Secondary's ECU Manifest (from the register ECU
    # ECU Manifest test above) is listed in the Vehicle Manifest.
    self.assertIn(
        'TCUdemocar', vehicle_manifest['signed']['ecu_version_manifests'])

    # TODO: More testing of the contents of the vehicle manifest.


    # Check the signature on the vehicle manifest.
    self.assertTrue(uptane.common.verify_signature_over_metadata(
        TestPrimary.ecu_key,
        vehicle_manifest['signatures'][0], # TODO: Deal with 1-sig assumption?
        vehicle_manifest['signed'],
        DATATYPE_VEHICLE_MANIFEST))
Beispiel #4
0
    def test_06_encode_and_validate_resigned_time_attestation(self):
        """
    Test timeserver attestation encoding and decoding, with signing over DER
    ('re-sign' functionality in asn1_codec) and signature validation.
    """

        signable_attestation = {
            str('signatures'): [{
                str('keyid'):
                str('79c796d7e87389d1ebad04edce49faef611d139ee41ea9fb1931732afbfaac2e'
                    ),
                str('sig'):
                str('a5ea6a3b685ad64f96c8c12145beda4efafddfac60bcdb45def35fe43c7d1150a182a1b50a1463bfffb0ef8d30b6203aa8b5365b0b7176312e1e9d7e355e550e'
                    ),
                str('method'):
                str('ed25519')
            }],
            str('signed'): {
                str('nonces'): [1],
                str('time'): str('2017-03-08T17:09:56Z')
            }
        }

        # Load the timeserver's private key to sign a time attestation, and public
        # key to verify that signature.
        timeserver_key = demo.import_private_key('timeserver')
        timeserver_key_pub = demo.import_public_key('timeserver')
        tuf.formats.ANYKEY_SCHEMA.check_match(timeserver_key)
        tuf.formats.ANYKEY_SCHEMA.check_match(timeserver_key_pub)

        # First, calculate what we'll be verifying at the end of this test.
        # The re-signing in the previous line produces a signature over the SHA256
        # hash of the DER encoding of the ASN.1 format of the 'signed' portion of
        # signable_attestation. We produce it here so that we can check it against
        # the result of encoding, resigning, and decoding.
        der_signed = asn1_codec.convert_signed_metadata_to_der(
            signable_attestation, only_signed=True)
        der_signed_hash = hashlib.sha256(der_signed).digest()

        # Now perform the actual conversion to ASN.1/DER of the full
        # signable_attestation, replacing the signature (which was given as
        # signatures over the Python 'signed' dictionary) with a signature over
        # the hash of the DER encoding of the 'signed' ASN.1 data.
        # This is the final product to be distributed back to a Primary client.
        der_attestation = asn1_codec.convert_signed_metadata_to_der(
            signable_attestation, private_key=timeserver_key, resign=True)

        # Now, in order to test the final product, decode it back from DER into
        # pyasn1 ASN.1, and convert back into Uptane's standard Python dictionary
        # form.
        pydict_again = asn1_codec.convert_signed_der_to_dersigned_json(
            der_attestation)

        # Check the extracted signature against the hash we produced earlier.
        self.assertTrue(
            tuf.keys.verify_signature(timeserver_key_pub,
                                      pydict_again['signatures'][0],
                                      der_signed_hash))
Beispiel #5
0
    def test_04_encode_full_signable_attestation(self):
        """
    Tests the conversion of signable time attestations from Python dictionaries
    to ASN.1/DER objects, and back.

    Similar to test 03 above, except that it encodes the signable dictionary
    (signed and signatures) instead of what is effectively just the 'signed'
    portion.

    Employing the asn1_codec code instead of using
     - the lower level code from pyasn1.der, asn1_spec, and
       timeserver_asn1_coder.
     - using a signable version of the time attestation (encapsulated in
       'signed', with 'signatures')

    This test doesn't re-sign the attestation over the hash of the DER encoding.
    One of the other tests below does that. The signatures here remain over
    the human-readable internal representation of the time attestation.

    """
        # Using str() here because in Python2, I'll get u'' if I don't, and the
        # self.assertEqual(signable_attestation, attestation_again) will fail
        # because they won't both have u' prefixes.
        signable_attestation = {
            str('signatures'): [{
                str('keyid'):
                str('79c796d7e87389d1ebad04edce49faef611d139ee41ea9fb1931732afbfaac2e'
                    ),
                str('sig'):
                str('a5ea6a3b685ad64f96c8c12145beda4efafddfac60bcdb45def35fe43c7d1150a182a1b50a1463bfffb0ef8d30b6203aa8b5365b0b7176312e1e9d7e355e550e'
                    ),
                str('method'):
                str('ed25519')
            }],
            str('signed'): {
                str('nonces'): [1],
                str('time'): str('2017-03-08T17:09:56Z')
            }
        }
        # str('signed'): {str('nonces'): [834845858], str('time'): str('2017-03-01T19:30:45Z')},
        # str('signatures'): [{str('keyid'): str('12'), str('method'): str('ed25519'), str('sig'): str('123495')}]}
        # u'signed': {u'nonces': [834845858], u'time': u'2017-03-01T19:30:45Z'},
        # u'signatures': [{u'keyid': u'12', u'method': u'ed25519', u'sig': u'12345'}]}

        uptane.formats.SIGNABLE_TIMESERVER_ATTESTATION_SCHEMA.check_match(
            signable_attestation)

        # Converts it, without re-signing (default for this method).
        der_attestation = asn1_codec.convert_signed_metadata_to_der(
            signable_attestation, DATATYPE_TIME_ATTESTATION)

        self.assertTrue(is_valid_nonempty_der(der_attestation))

        attestation_again = asn1_codec.convert_signed_der_to_dersigned_json(
            der_attestation, DATATYPE_TIME_ATTESTATION)

        self.assertEqual(attestation_again, signable_attestation)
Beispiel #6
0
    def test_10_validate_ecu_manifest(self):

        # Load the sample manifest from ECU 'ecu11111'.

        if tuf.conf.METADATA_FORMAT == 'der':
            # Load the sample manifest from ECU 'ecu11111'.
            der_data = open(
                os.path.join(
                    'samples', 'sample_ecu_manifest_ecu11111.' +
                    tuf.conf.METADATA_FORMAT), 'rb').read()

            # Use asn1_codec to convert to a JSON-compatible dictionary.
            sample_manifest = asn1_codec.convert_signed_der_to_dersigned_json(
                der_data, datatype='ecu_manifest')

        else:
            sample_manifest = tuf.util.load_file(
                os.path.join(
                    'samples', 'sample_ecu_manifest_ecu11111.' +
                    tuf.conf.METADATA_FORMAT))

        # Try validating with incorrectly formatted arguments, expecting error.
        for serial, manifest in [(42, 42), ('ecu11111', 42),
                                 (42, sample_manifest)]:
            with self.assertRaises(tuf.FormatError):
                TestDirector.instance.validate_ecu_manifest(serial, manifest)

        # Try validating a manifest that doesn't match the serial passed in as an
        # argument, expecting error.
        with self.assertRaises(uptane.Spoofing):
            TestDirector.instance.validate_ecu_manifest(
                'not_the_real_ecu_serial', sample_manifest)

        # Try validating the manifest now, before ECU 'ecu11111' is registered,
        # expecting an error.
        with self.assertRaises(uptane.UnknownECU):
            TestDirector.instance.validate_ecu_manifest(
                'ecu11111', sample_manifest)

        # TODO: Try validating a manifest with a bad signature, expecting
        #       tuf.BadSignatureError.
        #       Using JSON, we can just tweak any value and try validating, but
        #       using DER we have to be more careful.
        #       The best answer is probably to change the public key listed for
        #       the ECU in the inventorydb so that the signed manifest signature
        #       is regarded as invalid because it's signed with the wrong key.

        # Register the ECU whose sample manifest we'll try to validate.
        TestDirector.instance.register_ecu_serial('ecu11111',
                                                  keys_pub['secondary'],
                                                  'democar',
                                                  is_primary=False)

        # Attempt validation. If no error is raised, it was valid as expected.
        TestDirector.instance.validate_ecu_manifest('ecu11111',
                                                    sample_manifest)
Beispiel #7
0
    def test_11_ecu_manifest_der_conversion(self):

        conversion_tester(SAMPLE_ECU_MANIFEST_SIGNABLE, 'ecu_manifest', self)

        # Redundant tests. The above call should cover all of the following tests.
        der_ecu_manifest = asn1_codec.convert_signed_metadata_to_der(
            SAMPLE_ECU_MANIFEST_SIGNABLE,
            only_signed=True,
            datatype='ecu_manifest')

        der_ecu_manifest = asn1_codec.convert_signed_metadata_to_der(
            SAMPLE_ECU_MANIFEST_SIGNABLE, datatype='ecu_manifest')

        pydict_ecu_manifest = asn1_codec.convert_signed_der_to_dersigned_json(
            der_ecu_manifest, datatype='ecu_manifest')

        self.assertEqual(pydict_ecu_manifest, SAMPLE_ECU_MANIFEST_SIGNABLE)
Beispiel #8
0
    def test_signing(self):
        """
    Tests:
     - sign_over_metadata()
     - sign_signable()
     - verify_signature_over_metadata()

    using sample data from samples/. sign_signable() wraps sign_over_metadata(),
    and verify_signature_over_metadata() is tested together with them so that
    they can be tested against each other.

    These functions are also tested in the course of testing other modules.
    """

        # Load sample data, either JSON or ASN.1/DER depending on METADATA_FORMAT.
        if tuf.conf.METADATA_FORMAT == 'json':
            sample_time_attestation = json.load(
                open(
                    os.path.join(SAMPLES_DIR,
                                 'sample_timeserver_attestation.json')))

            sample_vehicle_manifest = json.load(
                open(
                    os.path.join(
                        SAMPLES_DIR,
                        'sample_vehicle_version_manifest_democar.json')))

            sample_ecu_manifest = json.load(
                open(
                    os.path.join(SAMPLES_DIR,
                                 'sample_ecu_manifest_TCUdemocar.json')))

            fresh_time_attestation = tuf.formats.make_signable(
                sample_time_attestation['signed'])

            fresh_vehicle_manifest = tuf.formats.make_signable(
                sample_vehicle_manifest['signed'])

            fresh_ecu_manifest = tuf.formats.make_signable(
                sample_ecu_manifest['signed'])

        elif tuf.conf.METADATA_FORMAT == 'der':
            sample_time_attestation = \
                asn1_codec.convert_signed_der_to_dersigned_json(open(os.path.join(
                SAMPLES_DIR, 'sample_timeserver_attestation.der'), 'rb').read(),
                DATATYPE_TIME_ATTESTATION)

            sample_vehicle_manifest = \
                asn1_codec.convert_signed_der_to_dersigned_json(open(os.path.join(
                SAMPLES_DIR, 'sample_vehicle_version_manifest_democar.der'),
                'rb').read(), DATATYPE_VEHICLE_MANIFEST)

            sample_ecu_manifest = \
                asn1_codec.convert_signed_der_to_dersigned_json(open(os.path.join(
                SAMPLES_DIR, 'sample_ecu_manifest_TCUdemocar.der'), 'rb').read(),
                DATATYPE_ECU_MANIFEST)

            fresh_time_attestation = tuf.formats.make_signable(
                sample_time_attestation['signed'])

            fresh_vehicle_manifest = tuf.formats.make_signable(
                sample_vehicle_manifest['signed'])

            fresh_ecu_manifest = tuf.formats.make_signable(
                sample_ecu_manifest['signed'])

        else:
            assert False, 'Unknown metadata format: test code needs rewriting?'

        # Produce a few unsigned signable copies for additional tests.
        fresh_time_attestation2 = copy.deepcopy(fresh_time_attestation)
        fresh_ecu_manifest2 = copy.deepcopy(fresh_ecu_manifest)
        fresh_ecu_manifest3 = copy.deepcopy(fresh_ecu_manifest)
        fresh_ecu_manifest4 = copy.deepcopy(fresh_ecu_manifest)

        # Correctly sign each of the three pieces of metadata with one key,
        # once using sign_over_metadata directly and once using sign_signable.
        # Check these in three ways (in this order, for test code readability):
        # 1. Make sure that the signatures created using the higher and lower level
        #    methods are identical.
        # 2. Compare the signatures produced to the previously-produced signatures
        #    in the sample data.
        # 3. Run common.verify_signature_over_metadata and ensure that the
        #    signature is deemed valid.
        # The combination also tests common.verify_signature_over_metadata.

        # Time Attestation
        sig_alone = common.sign_over_metadata(keys_pri['timeserver'],
                                              fresh_time_attestation['signed'],
                                              DATATYPE_TIME_ATTESTATION)
        common.sign_signable(fresh_time_attestation, [keys_pri['timeserver']],
                             DATATYPE_TIME_ATTESTATION)
        self.assertEqual(sig_alone, fresh_time_attestation['signatures'][0])
        self.assertEqual(fresh_time_attestation['signatures'],
                         sample_time_attestation['signatures'])
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['timeserver'],
                fresh_time_attestation['signatures'][0],
                fresh_time_attestation['signed'], DATATYPE_TIME_ATTESTATION))

        # Vehicle Manifest
        sig_alone = common.sign_over_metadata(keys_pri['primary'],
                                              fresh_vehicle_manifest['signed'],
                                              DATATYPE_VEHICLE_MANIFEST)
        common.sign_signable(fresh_vehicle_manifest, [keys_pri['primary']],
                             DATATYPE_VEHICLE_MANIFEST)
        self.assertEqual(sig_alone, fresh_vehicle_manifest['signatures'][0])
        self.assertEqual(fresh_vehicle_manifest['signatures'],
                         sample_vehicle_manifest['signatures'])
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['primary'], fresh_vehicle_manifest['signatures'][0],
                fresh_vehicle_manifest['signed'], DATATYPE_VEHICLE_MANIFEST))

        # ECU Manifest
        sig_alone = common.sign_over_metadata(keys_pri['secondary'],
                                              fresh_ecu_manifest['signed'],
                                              DATATYPE_ECU_MANIFEST)
        common.sign_signable(fresh_ecu_manifest, [keys_pri['secondary']],
                             DATATYPE_ECU_MANIFEST)
        self.assertEqual(sig_alone, fresh_ecu_manifest['signatures'][0])
        self.assertEqual(fresh_ecu_manifest['signatures'],
                         sample_ecu_manifest['signatures'])
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['secondary'], fresh_ecu_manifest['signatures'][0],
                fresh_ecu_manifest['signed'], DATATYPE_ECU_MANIFEST))

        # Expect the signatures to come out the same even if a key is specified
        # twice. Try only with ECU Manifests for brevity. (Shouldn't matter)
        common.sign_signable(fresh_ecu_manifest2,
                             [keys_pri['secondary'], keys_pri['secondary']],
                             DATATYPE_ECU_MANIFEST)
        self.assertEqual(1, len(fresh_ecu_manifest2['signatures']))
        self.assertEqual(fresh_ecu_manifest2['signatures'],
                         sample_ecu_manifest['signatures'])
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['secondary'], fresh_ecu_manifest2['signatures'][0],
                fresh_ecu_manifest2['signed'], DATATYPE_ECU_MANIFEST))

        # Try signing with two keys.
        common.sign_signable(fresh_ecu_manifest3,
                             [keys_pri['secondary'], keys_pri['primary']],
                             DATATYPE_ECU_MANIFEST)
        self.assertEqual(2, len(fresh_ecu_manifest3['signatures']))
        sigs = fresh_ecu_manifest3['signatures']
        if sigs[0]['keyid'] == keys_pri['primary']['keyid']:
            self.assertEqual(sigs[1]['keyid'], keys_pri['secondary']['keyid'])
            primary_sig = sigs[0]
            secondary_sig = sigs[1]
        else:
            self.assertEqual(sigs[1]['keyid'], keys_pri['primary']['keyid'])
            self.assertEqual(sigs[0]['keyid'], keys_pri['secondary']['keyid'])
            primary_sig = sigs[1]
            secondary_sig = sigs[0]

        # We can check the secondary key's signature against the pre-existing
        # sample. There is no pre-existing sample for the primary key's signature
        # on an ECU Manifest, but there's already plenty of testing at this point,
        # and we'll verify both signatures live next.
        self.assertEqual(sample_ecu_manifest['signatures'][0]['sig'],
                         secondary_sig['sig'])

        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['secondary'], secondary_sig,
                sample_ecu_manifest['signed'], DATATYPE_ECU_MANIFEST))
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['primary'], primary_sig,
                sample_ecu_manifest['signed'], DATATYPE_ECU_MANIFEST))

        # Expect a signable to be unchanged if the keys provided already signed the
        # signable. Copy an already-signed piece of metadata, sign it again, and
        # compare it to the original, expecting no change.
        duped_vehicle_manifest = copy.deepcopy(fresh_vehicle_manifest)
        common.sign_signable(duped_vehicle_manifest, [keys_pri['primary']],
                             DATATYPE_VEHICLE_MANIFEST)

        self.assertEqual(len(fresh_vehicle_manifest['signatures']),
                         len(duped_vehicle_manifest['signatures']))
        self.assertEqual(fresh_vehicle_manifest, duped_vehicle_manifest)

        # Add a signature from a different key to an already-signed piece of
        # metadata, expecting to find both old and new signatures, valid in both
        # cases. In particular, this signable already has a signature from key
        # 'secondary' and we'll add a signature from key 'primary'. I figure the
        # second signature (from key 'primary') will be second in the list, but
        # because we don't require that behavior, I won't make that assumption in a
        # test, so I'll proceed as if we don't know the signature order in this
        # test.
        common.sign_signable(fresh_ecu_manifest, [keys_pri['primary']],
                             DATATYPE_ECU_MANIFEST)
        self.assertEqual(2, len(fresh_ecu_manifest['signatures']))
        sigs = fresh_ecu_manifest['signatures']
        if sigs[0]['keyid'] == keys_pri['primary']['keyid']:
            self.assertEqual(
                sigs[1]['keyid'] == keys_pri['secondary']['keyid'])
            primary_sig = sigs[0]
            secondary_sig = sigs[1]
        else:
            self.assertEqual(sigs[1]['keyid'], keys_pri['primary']['keyid'])
            self.assertEqual(sigs[0]['keyid'], keys_pri['secondary']['keyid'])
            primary_sig = sigs[1]
            secondary_sig = sigs[0]

        self.assertEqual(sample_ecu_manifest['signatures'][0]['sig'],
                         secondary_sig['sig'])

        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['secondary'], secondary_sig,
                sample_ecu_manifest['signed'], DATATYPE_ECU_MANIFEST))
        self.assertTrue(
            common.verify_signature_over_metadata(
                keys_pub['primary'], primary_sig,
                sample_ecu_manifest['signed'], DATATYPE_ECU_MANIFEST))

        # Paranoid: duplicates should have the same signed element.
        self.assertEqual(sample_ecu_manifest['signed'],
                         fresh_ecu_manifest['signed'])

        # Try signing with a public key instead of a private key, expecting a
        # tuf.FormatError.
        with self.assertRaises(tuf.FormatError):
            common.sign_signable(fresh_time_attestation2,
                                 [keys_pub['secondary']],
                                 DATATYPE_TIME_ATTESTATION)
Beispiel #9
0
  def test_10_register_ecu_manifest(self):

    # Throughout this function, I'll use a different nonces in each call to
    # register_ecu_manifest, and check that the ones in calls expected to
    # succeed have been noted and that the ones in calls expected to fail have
    # not been noted.

    # Starting with an empty ecu manifest dictionary.
    self.assertEqual(dict(), TestPrimary.instance.ecu_manifests)

    # Make sure we're starting with no nonces sent or to send.
    self.assertEqual([], TestPrimary.instance.nonces_to_send)
    self.assertEqual([], TestPrimary.instance.nonces_sent)


    # Load the manifests we'll use in these tests.
    # Note that the .json and .der manifest samples aren't identical; they're
    # signed over different data, so to get the JSON version of the DER
    # manifests, we'll convert them.
    # We'll always need the JSON encodings for testing, and we'll load the
    # ASN.1/DER manifests only if we're in DER mode.
    # 1: Correctly signed ECU manifest from ECU TCUdemocar (good sample)
    # 2: Correctly signed ECU manifest from ECU unknown_ecu
    # 3: ECU Manifest from ECU TCUdemocar signed by the wrong key
    #    (demo's Image Repo timestamp key in particular, instead of demo's
    #     Secondary key)
    # 4: Correctly signed ECU manifest from TCUdemocar w/ attack report

    if tuf.conf.METADATA_FORMAT == 'json':
      manifest1 = manifest1_json = json.load(open(os.path.join(SAMPLE_DATA_DIR,
          'sample_ecu_manifest_TCUdemocar.json')))

      manifest2 = manifest2_json = json.load(open(os.path.join(TEST_DATA_DIR,
          'flawed_manifests', 'em2_unknown_ecu_manifest.json')))

      manifest3 = manifest3_json = json.load(open(os.path.join(TEST_DATA_DIR,
          'flawed_manifests', 'em3_ecu_manifest_signed_with_wrong_key.json')))

      manifest4 = manifest4_json = json.load(open(os.path.join(TEST_DATA_DIR,
          'flawed_manifests', 'em4_attack_detected_in_ecu_manifest.json')))

    else:
      assert tuf.conf.METADATA_FORMAT == 'der', 'Test code is flawed.'

      manifest1 = open(os.path.join(SAMPLE_DATA_DIR,
          'sample_ecu_manifest_TCUdemocar.der'), 'rb').read()

      manifest1_json = asn1_codec.convert_signed_der_to_dersigned_json(
          manifest1, DATATYPE_ECU_MANIFEST)

      manifest2 = open(os.path.join(TEST_DATA_DIR, 'flawed_manifests',
          'em2_unknown_ecu_manifest.der'), 'rb').read()

      manifest2_json = asn1_codec.convert_signed_der_to_dersigned_json(
          manifest2, DATATYPE_ECU_MANIFEST)

      manifest3 = open(os.path.join(TEST_DATA_DIR, 'flawed_manifests',
          'em3_ecu_manifest_signed_with_wrong_key.der'), 'rb').read()

      manifest3_json = asn1_codec.convert_signed_der_to_dersigned_json(
          manifest3, DATATYPE_ECU_MANIFEST)

      manifest4 = open(os.path.join(TEST_DATA_DIR, 'flawed_manifests',
          'em4_attack_detected_in_ecu_manifest.der'), 'rb').read()

      manifest4_json = asn1_codec.convert_signed_der_to_dersigned_json(
          manifest4, DATATYPE_ECU_MANIFEST)


    # Register two Secondaries with the Primary.
    TestPrimary.instance.register_new_secondary('TCUdemocar')
    TestPrimary.instance.register_new_secondary('ecu11111')


    # Start with a sequence of tests with bad arguments but an otherwise
    # correct ECU Manifest, manifest1.

    # Try using a VIN that is not the Primary's VIN (ECU Manifest apparently
    # from another car!)
    with self.assertRaises(uptane.UnknownVehicle):
      TestPrimary.instance.register_ecu_manifest(
          vin='13105941', # unexpected VIN
          ecu_serial='TCUdemocar', nonce=1,
          signed_ecu_manifest=manifest1)

    # Try using the wrong ECU Serial - one that is registered, but which does
    # not match the ECU Serial listed in the ECU Manifest itself.
    with self.assertRaises(uptane.Spoofing):
      TestPrimary.instance.register_ecu_manifest(
          vin=VIN,
          ecu_serial='ecu11111', # not the same ECU Serial in the manifest
          nonce=2, signed_ecu_manifest=manifest1)

    # Try using an ECU Serial that the Primary is not aware of.
    with self.assertRaises(uptane.UnknownECU):
      TestPrimary.instance.register_ecu_manifest(
          vin=VIN, # unexpected VIN
          ecu_serial='an unknown secondary ecu serial', # unexpected ECU Serial
          nonce=3,
          signed_ecu_manifest=manifest1)


    # Register the ECU Manifest correctly this time.
    TestPrimary.instance.register_ecu_manifest(
        vin=VIN, ecu_serial='TCUdemocar', nonce=10,
        signed_ecu_manifest=manifest1)

    # Make sure the provided manifest is now in the Primary's ecu manifests
    # dictionary. Note that the Primary holds manifests as JSON-compatible
    # Python dictionaries regardless of the format it receives them in.
    self.assertIn('TCUdemocar', TestPrimary.instance.ecu_manifests)
    self.assertIn(
        manifest1_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])

    # Make sure the nonce provided was noted in the right place.
    self.assertIn(10, TestPrimary.instance.nonces_to_send)
    self.assertEqual([], TestPrimary.instance.nonces_sent)


    # Though this is not required functionality, test register_ecu_manifest
    # with JSON manifests as well, even if we're running in DER mode.
    # And make sure force_pydict=True doesn't break if we're already in JSON
    # mode, either.
    TestPrimary.instance.register_ecu_manifest(
        VIN, 'TCUdemocar', nonce=11, signed_ecu_manifest=manifest1_json,
        force_pydict=True)



    # The next tests use ECU Manifests that contain problematic values.
    # (We're now testing things beyond just the arguments provided.
    # If we're running in DER mode, we'll try both DER and JSON manifests.
    # If we're running in JSON mode, we'll only try JSON manifests
    #    (though in JSON mode, we'll run twice, once with force_pydict on
    #    to make sure that run doesn't break despite the redundant argument).

    # The list again is:
    # 2: Correctly signed ECU manifest from ECU unknown_ecu
    # 3: ECU Manifest from ECU TCUdemocar signed by the wrong key
    # 4: Correctly signed ECU manifest from TCUdemocar w/ attack report


    # Case 2: We won't save the ECU Manifest from an unknown ECU Serial.
    self.assertNotIn('unknown_ecu', TestPrimary.instance.ecu_manifests)
    self.assertNotIn(
        manifest2_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])

    with self.assertRaises(uptane.UnknownECU):
      TestPrimary.instance.register_ecu_manifest(
          'democar', 'unknown_ecu', nonce=4, signed_ecu_manifest=manifest2)

    with self.assertRaises(uptane.UnknownECU):
      TestPrimary.instance.register_ecu_manifest(
          'democar', 'unknown_ecu', nonce=5,
          signed_ecu_manifest=manifest2_json, force_pydict=True)

    self.assertNotIn('unknown_ecu', TestPrimary.instance.ecu_manifests)
    self.assertNotIn( # Make sure it's not in the wrong list of ECU Manifests
        manifest2_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])


    # Case 3: ECU Manifest signed with the wrong key: we save it anyway and
    #         send it on to the Director like any other; Primaries don't check
    #         the signatures on ECU Manifests: they can't be expected to know
    #         the right public or symmetric keys.
    self.assertNotIn(
        manifest3_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])

    TestPrimary.instance.register_ecu_manifest(
        'democar', 'TCUdemocar', nonce=12, signed_ecu_manifest=manifest3)

    TestPrimary.instance.register_ecu_manifest(
        'democar', 'TCUdemocar', nonce=13, signed_ecu_manifest=manifest3_json,
        force_pydict=True)

    self.assertIn(
        manifest3_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])


    # Case 4: ECU Manifest containing an attack report. Make sure it doesn't
    #         fail to be registered.
    self.assertNotIn(
        manifest4_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])

    TestPrimary.instance.register_ecu_manifest(
        'democar', 'TCUdemocar', nonce=14, signed_ecu_manifest=manifest4)

    TestPrimary.instance.register_ecu_manifest(
        'democar', 'TCUdemocar', nonce=15, signed_ecu_manifest=manifest4_json,
        force_pydict=True)

    self.assertIn(
        manifest4_json, TestPrimary.instance.ecu_manifests['TCUdemocar'])



    # Confirm that we've succeeded in registering the right nonces.
    for this_nonce in [1, 2, 3, 4, 5]:
      self.assertNotIn(this_nonce, TestPrimary.instance.nonces_to_send)

    for this_nonce in [10, 11, 12, 13, 14, 15]:
      self.assertIn(this_nonce, TestPrimary.instance.nonces_to_send)
Beispiel #10
0
    def update_time(self, timeserver_attestation):
        """
    The function attemps to verify the time attestation from the Time Server,
    distributed to us by the Primary.
    If timeserver_attestation is correctly signed by the expected Timeserver
    key, and it lists the nonce we expected it to list (the one we last used
    in a request for the time), then this Secondary's time is updated.
    The new time will be used by this client (via TUF) in in place of system
    time when checking metadata for expiration.

    If the Secondary is using ASN.1/DER metadata, then timeserver_attestation
    is expected to be in that format, as a byte string.
    Otherwise, we're using simple Python dictionaries and timeserver_attestation
    conforms to uptane.formats.SIGNABLE_TIMESERVER_ATTESTATION_SCHEMA.

    If verification is successful, switch to a new nonce for next time.
    """

        I_TO_PRINT = TO_PRINT + uptane.YELLOW + '[Secondary.update_time(self, timeserver_attestation)]: ' + uptane.ENDCOLORS
        #TODO: Print to be deleted
        print(
            str('%s %s' % (
                I_TO_PRINT,
                'The function attemps to verify the time attestation from the Time Server, distributed to us by the Primary. If timeserver_attestation is correctly signed by the expected Timeserver key, and it lists the nonce we expected it to list (the one we last used in a request for the time), then this Secondary\'s time is updated. The new time will be used by this client (via TUF) in in place of system time when checking metadata for expiration.'
            )))
        #TODO: Until here

        # If we're using ASN.1/DER format, convert the attestation into something
        # comprehensible (JSON-compatible dictionary) instead.
        if tuf.conf.METADATA_FORMAT == 'der':
            timeserver_attestation = asn1_codec.convert_signed_der_to_dersigned_json(
                timeserver_attestation, DATATYPE_TIME_ATTESTATION)

        # Check format.
        uptane.formats.SIGNABLE_TIMESERVER_ATTESTATION_SCHEMA.check_match(
            timeserver_attestation)

        # Assume there's only one signature.
        assert len(timeserver_attestation['signatures']) == 1

        verified = uptane.common.verify_signature_over_metadata(
            self.timeserver_public_key,
            timeserver_attestation['signatures'][0],
            timeserver_attestation['signed'], DATATYPE_TIME_ATTESTATION)

        if not verified:
            raise tuf.BadSignatureError(
                'Timeserver returned an invalid signature. '
                'Time is questionable, so not saved. If you see this persistently, '
                'it is possible that there is a Man in the Middle attack underway.'
            )

        # If the most recent nonce we sent is not in the timeserver attestation,
        # then we don't trust the timeserver attestation.
        if self.last_nonce_sent is None:
            # This ECU is fresh and hasn't actually ever sent a nonce to the Primary
            # yet. It would be impossible to validate a timeserver attestation.
            log.warning(
                YELLOW + 'Cannot verify a timeserver attestation yet: '
                'this fresh Secondary ECU has never communicated a nonce and ECU '
                'Version Manifest to the Primary.' + ENDCOLORS)
            return

        elif self.last_nonce_sent not in timeserver_attestation['signed'][
                'nonces']:
            # TODO: Create a new class for this Exception in this file.
            raise uptane.BadTimeAttestation(
                'Primary provided a time attestation '
                'that did not include any of the nonces this Secondary has sent '
                'recently. This Secondary cannot trust the time provided and will '
                'not register it. Because of the asynchrony in the Primary-Secondary '
                'communications, this can happen occasionally. If this occurs '
                'repeatedly for a sustained amount of time, it is possible that the '
                'Primary is compromised or that there is a Man in the Middle attack '
                'underway between the vehicle and the servers, or within the '
                'vehicle.')

        # Extract actual time from the timeserver's signed attestation.
        new_timeserver_time = timeserver_attestation['signed']['time']

        # Make sure the format is understandable to us before saving the
        # time.  Convert to a UNIX timestamp.
        new_timeserver_time_unix = int(
            tuf.formats.datetime_to_unix_timestamp(
                iso8601.parse_date(new_timeserver_time)))
        tuf.formats.UNIX_TIMESTAMP_SCHEMA.check_match(new_timeserver_time_unix)

        # Save verified time.
        self.all_valid_timeserver_times.append(new_timeserver_time)

        # Set the client's clock.  This will be used instead of system time by TUF.
        tuf.conf.CLOCK_OVERRIDE = new_timeserver_time_unix

        # Use a new nonce next time, since the nonce we were using has now been
        # used to successfully verify a timeserver attestation.
        self.change_nonce()
Beispiel #11
0
    def register_vehicle_manifest(self, vin, primary_ecu_serial,
                                  signed_vehicle_manifest):
        """
    Saves the vehicle manifest in the InventoryDB, validating first the
    Primary's key on the full vehicle manifest, then each individual ECU
    Manifest's signature.

    If the Primary's signature over the whole Vehicle Manifest is invalid, then
    this raises an error (either tuf.BadSignatureError, uptane.Spoofing, or
    uptane.UnknownECU).

    Otherwise, if any of the individual ECU Manifests are invalid, those
    individual ECU Manifests are discarded, and others are processed. (No
    error is raised - only a warning.)

    Arguments:
      vin: vehicle's unique identifier, uptane.formats.VIN_SCHEMA
      primary_ecu_serial: Primary ECU's unique identifier,
                          uptane.formats.ECU_SERIAL_SCHEMA
      manifest: the vehicle manifest, as specified in the implementation
                specification and compliant with
                uptane.formats.SIGNABLE_VEHICLE_VERSION_MANIFEST_SCHEMA
                If, the metadata format is set to ASN.1/DER, then this will
                instead be compliant with uptane.formats.DER_DATA_SCHEMA,
                and will be decoded and converted back to be compliant with
                uptane.formats.SIGNABLE_VEHICLE_VERSION_MANIFEST_SCHEMA


    Exceptions:

        tuf.BadSignatureError
          if the Primary's signature on the vehicle manifest is invalid
          (An individual Secondary's signature on an ECU Version Manifests
          being invalid does not raise an exception, but instead results in
          a warning and that ECU Version Manifest alone being discarded.)

        uptane.Spoofing
          if the primary_ecu_serial argument does not match the ECU Serial
          for the Primary in the signed Vehicle Version Manifest.
          (As above, an ECU Version Manifest that is wrong in this respect is
          individually discarded with only a warning.)

        uptane.UnknownECU
          if the ECU Serial provided for the Primary is not known to this
          Director.
          (As above, an unknown Secondary ECU in an ECU Version Manifest is
          individually discarded with only a warning.)

        uptane.UnknownVehicle
          if the VIN provided is not known to this Director

    """
        uptane.formats.VIN_SCHEMA.check_match(vin)
        uptane.formats.ECU_SERIAL_SCHEMA.check_match(primary_ecu_serial)

        if tuf.conf.METADATA_FORMAT == 'der':
            # Check format and convert back to expected vehicle manifest format.
            uptane.formats.DER_DATA_SCHEMA.check_match(signed_vehicle_manifest)
            signed_vehicle_manifest = asn1_codec.convert_signed_der_to_dersigned_json(
                signed_vehicle_manifest, DATATYPE_VEHICLE_MANIFEST)

        uptane.formats.SIGNABLE_VEHICLE_VERSION_MANIFEST_SCHEMA.check_match(
            signed_vehicle_manifest)

        inventory.check_vin_registered(vin)
        # if vin not in inventory.ecus_by_vin:
        #   raise uptane.UnknownVehicle('Received a vehicle manifest purportedly '
        #       'from a vehicle with a VIN that is not known to this Director.')

        # Process Primary's signature on full manifest here.
        # If it doesn't match expectations, error out here.
        self.validate_primary_certification_in_vehicle_manifest(
            vin, primary_ecu_serial, signed_vehicle_manifest)

        # If the Primary's signature is valid, save the whole vehicle manifest to
        # the inventorydb.
        inventory.save_vehicle_manifest(vin, signed_vehicle_manifest)

        log.info(GREEN + ' Received a Vehicle Manifest from Primary ECU ' +
                 repr(primary_ecu_serial) +
                 ', with a valid signature from that ECU.' + ENDCOLORS)
        # TODO: Note that the above hasn't checked that the signature was from
        # a Primary, just from an ECU. Fix.

        # Validate signatures on and register all individual ECU manifests for each
        # ECU (may have multiple manifests per ECU).
        all_ecu_manifests = \
            signed_vehicle_manifest['signed']['ecu_version_manifests']

        for ecu_serial in all_ecu_manifests:
            ecu_manifests = all_ecu_manifests[ecu_serial]
            for manifest in ecu_manifests:
                try:
                    # This calls validate_ecu_manifest, which can raise the errors
                    # caught below.
                    self.register_ecu_manifest(vin, ecu_serial, manifest)
                except uptane.Spoofing as e:
                    log.warning(
                        RED +
                        'Discarding a spoofed or malformed ECU Manifest. Error '
                        ' from validating that ECU manifest follows:\n' +
                        ENDCOLORS + repr(e))
                except uptane.UnknownECU as e:
                    log.warning(
                        RED +
                        'Discarding an ECU Manifest from unknown ECU. Error from '
                        'validation attempt follows:\n' + ENDCOLORS + repr(e))
                except tuf.BadSignatureError as e:
                    log.warning(
                        RED +
                        'Rejecting an ECU Manifest whose signature is invalid, '
                        'from within an otherwise valid Vehicle Manifest. Error from '
                        'validation attempt follows:\n' + ENDCOLORS + repr(e))
Beispiel #12
0
def conversion_tester(signable_pydict, datatype, cls):  # cls: clunky
    """
  Tests each of the different kinds of conversions into ASN.1/DER, and tests
  converting back. In one type of conversion, compares to make sure the data
  has not changed.

  This function takes as a third parameter the unittest.TestCase object whose
  functions (assertTrue etc) it can use. This is awkward and inappropriate. :P
  Find a different means of providing modularity instead of this one.
  (Can't just have this method in the class above because it would be run as
  a test. Could have default parameters and do that, but that's clunky, too.)
  Does unittest allow/test private functions in UnitTest classes?
  """

    # Test type 1: only-signed
    # Convert and return only the 'signed' portion, the metadata payload itself,
    # without including any signatures.
    signed_der = asn1_codec.convert_signed_metadata_to_der(signable_pydict,
                                                           datatype,
                                                           only_signed=True)

    cls.assertTrue(is_valid_nonempty_der(signed_der))

    # TODO: Add function to asn1_codec that will convert signed-only DER back to
    # Python dictionary. Might be useful, and is useful for testing only_signed
    # in any case.

    # Test type 2: full conversion
    # Convert the full signable ('signed' and 'signatures'), maintaining the
    # existing signature in a new format and encoding.
    signable_der = asn1_codec.convert_signed_metadata_to_der(
        signable_pydict, datatype)
    cls.assertTrue(is_valid_nonempty_der(signable_der))

    # Convert it back.
    signable_reverted = asn1_codec.convert_signed_der_to_dersigned_json(
        signable_der, datatype)

    # Ensure the original is equal to what is converted back.
    cls.assertEqual(signable_pydict, signable_reverted)

    # Test type 3: full conversion with re-signing
    # Convert the full signable ('signed' and 'signatures'), but discarding the
    # original signatures and re-signing over, instead, the hash of the converted,
    # ASN.1/DER 'signed' element.
    resigned_der = asn1_codec.convert_signed_metadata_to_der(
        signable_pydict, datatype, resign=True, private_key=test_signing_key)
    cls.assertTrue(is_valid_nonempty_der(resigned_der))

    # Convert the re-signed DER manifest back in order to split it up.
    resigned_reverted = asn1_codec.convert_signed_der_to_dersigned_json(
        resigned_der, datatype)
    resigned_signature = resigned_reverted['signatures'][0]

    # Check the signature on the re-signed DER manifest:
    cls.assertTrue(
        uptane.common.verify_signature_over_metadata(
            test_signing_key,
            resigned_signature,
            resigned_reverted['signed'],
            datatype,
            metadata_format='der'))

    # The signatures will not match, because a new signature was made, but the
    # 'signed' elements should match when converted back.
    cls.assertEqual(signable_pydict['signed'], resigned_reverted['signed'])
Beispiel #13
0
    def validate_time_attestation(self, timeserver_attestation):
        """
    Given a timeserver attestation, validate it (checking that the signature is
    valid and from the expected key) and ensure that the nonce we expect the
    attestation to contain is included.

    If validation is successful, switch to a new nonce for next time.
    """
        # If we're using ASN.1/DER format, convert the attestation into something
        # comprehensible (JSON-compatible dictionary) instead.
        if tuf.conf.METADATA_FORMAT == 'der':
            timeserver_attestation = asn1_codec.convert_signed_der_to_dersigned_json(
                timeserver_attestation, DATATYPE_TIME_ATTESTATION)

        # Check format.
        uptane.formats.SIGNABLE_TIMESERVER_ATTESTATION_SCHEMA.check_match(
            timeserver_attestation)

        # Assume there's only one signature.
        assert len(timeserver_attestation['signatures']) == 1

        valid = uptane.common.verify_signature_over_metadata(
            self.timeserver_public_key,
            timeserver_attestation['signatures'][0],
            timeserver_attestation['signed'], DATATYPE_TIME_ATTESTATION)

        if not valid:
            raise tuf.BadSignatureError(
                'Timeserver returned an invalid signature. '
                'Time is questionable, so not saved. If you see this persistently, '
                'it is possible that there is a Man in the Middle attack underway.'
            )

        # If the most recent nonce we sent is not in the timeserver attestation,
        # then we don't trust the timeserver attestation.
        if self.last_nonce_sent is None:
            # This ECU is fresh and hasn't actually ever sent a nonce to the Primary
            # yet. It would be impossible to validate a timeserver attestation.
            log.warning(
                YELLOW + 'Cannot validate a timeserver attestation yet: '
                'this fresh Secondary ECU has never communicated a nonce and ECU '
                'Version Manifest to the Primary.' + ENDCOLORS)
            return

        elif self.last_nonce_sent not in timeserver_attestation['signed'][
                'nonces']:
            # TODO: Create a new class for this Exception in this file.
            raise uptane.BadTimeAttestation(
                'Primary provided a time attestation '
                'that did not include any of the nonces this Secondary has sent '
                'recently. This Secondary cannot trust the time provided and will '
                'not register it. Because of the asynchrony in the Primary-Secondary '
                'communications, this can happen occasionally. If this occurs '
                'repeatedly for a sustained amount of time, it is possible that the '
                'Primary is compromised or that there is a Man in the Middle attack '
                'underway between the vehicle and the servers, or within the '
                'vehicle.')

        # Extract actual time from the timeserver's signed attestation.
        new_timeserver_time = timeserver_attestation['signed']['time']

        # Save validated time.
        self.all_valid_timeserver_times.append(new_timeserver_time)

        # Use a new nonce next time, since the nonce we were using has now been
        # used to successfully validate a timeserver attestation.
        self.change_nonce()