#!/usr/bin/env python # Copyright 2012 - 2017, New York University and the TUF contributors # SPDX-License-Identifier: MIT OR Apache-2.0 """ test_sig.py Geremy Condra Vladimir Diaz February 28, 2012. Based on a previous version of this module. See LICENSE-MIT OR LICENSE for licensing information. Test cases for sig.py. """ import unittest import logging import copy import sys import tuf import tuf.log import tuf.formats import tuf.keydb import tuf.roledb import tuf.sig import tuf.exceptions from tests import utils import securesystemslib import securesystemslib.keys logger = logging.getLogger(__name__) # Setup the keys to use in our test cases. KEYS = [] for _ in range(3): KEYS.append(securesystemslib.keys.generate_rsa_key(2048)) class TestSig(unittest.TestCase): def setUp(self): pass def tearDown(self): tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() def test_get_signature_status_no_role(self): signable = {'signed': 'test', 'signatures': []} # A valid, but empty signature status. sig_status = tuf.sig.get_signature_status(signable) self.assertTrue(tuf.formats.SIGNATURESTATUS_SCHEMA.matches(sig_status)) self.assertEqual(0, sig_status['threshold']) self.assertEqual([], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) # A valid signable, but non-existent role argument. self.assertRaises(tuf.exceptions.UnknownRoleError, tuf.sig.get_signature_status, signable, 'unknown_role') # Should verify we are not adding a duplicate signature # when doing the following action. Here we know 'signable' # has only one signature so it's okay. signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) tuf.keydb.add_key(KEYS[0]) # Improperly formatted role. self.assertRaises(securesystemslib.exceptions.FormatError, tuf.sig.get_signature_status, signable, 1) # Not allowed to call verify() without having specified a role. args = (signable, None) self.assertRaises(securesystemslib.exceptions.Error, tuf.sig.verify, *args) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) def test_get_signature_status_bad_sig(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) signable['signed'] += 'signature no longer matches signed data' tuf.keydb.add_key(KEYS[0]) threshold = 1 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertEqual(1, sig_status['threshold']) self.assertEqual([], sig_status['good_sigs']) self.assertEqual([KEYS[0]['keyid']], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) self.assertFalse(tuf.sig.verify(signable, 'Root')) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) # Remove the role. tuf.roledb.remove_role('Root') def test_get_signature_status_unknown_signing_scheme(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) valid_scheme = KEYS[0]['scheme'] KEYS[0]['scheme'] = 'unknown_signing_scheme' tuf.keydb.add_key(KEYS[0]) threshold = 1 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid']], threshold=threshold) tuf.roledb.add_role('root', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'root') self.assertEqual(1, sig_status['threshold']) self.assertEqual([], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([KEYS[0]['keyid']], sig_status['unknown_signing_schemes']) self.assertFalse(tuf.sig.verify(signable, 'root')) # Done. Let's remove the added key(s) from the key database. KEYS[0]['scheme'] = valid_scheme tuf.keydb.remove_key(KEYS[0]['keyid']) # Remove the role. tuf.roledb.remove_role('root') def test_get_signature_status_single_key(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) threshold = 1 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) tuf.keydb.add_key(KEYS[0]) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertEqual(1, sig_status['threshold']) self.assertEqual([KEYS[0]['keyid']], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) self.assertTrue(tuf.sig.verify(signable, 'Root')) # Test for an unknown signature when 'role' is left unspecified. sig_status = tuf.sig.get_signature_status(signable) self.assertEqual(0, sig_status['threshold']) self.assertEqual([], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([KEYS[0]['keyid']], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) # Remove the role. tuf.roledb.remove_role('Root') def test_get_signature_status_below_threshold(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) tuf.keydb.add_key(KEYS[0]) threshold = 2 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid'], KEYS[2]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertEqual(2, sig_status['threshold']) self.assertEqual([KEYS[0]['keyid']], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) self.assertFalse(tuf.sig.verify(signable, 'Root')) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) # Remove the role. tuf.roledb.remove_role('Root') def test_get_signature_status_below_threshold_unrecognized_sigs(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') # Two keys sign it, but only one of them will be trusted. signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[2], signed)) tuf.keydb.add_key(KEYS[0]) tuf.keydb.add_key(KEYS[1]) threshold = 2 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid'], KEYS[1]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertEqual(2, sig_status['threshold']) self.assertEqual([KEYS[0]['keyid']], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([KEYS[2]['keyid']], sig_status['unknown_sigs']) self.assertEqual([], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) self.assertFalse(tuf.sig.verify(signable, 'Root')) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) tuf.keydb.remove_key(KEYS[1]['keyid']) # Remove the role. tuf.roledb.remove_role('Root') def test_get_signature_status_below_threshold_unauthorized_sigs(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') # Two keys sign it, but one of them is only trusted for a different # role. signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[1], signed)) tuf.keydb.add_key(KEYS[0]) tuf.keydb.add_key(KEYS[1]) threshold = 2 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid'], KEYS[2]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[1]['keyid'], KEYS[2]['keyid']], threshold=threshold) tuf.roledb.add_role('Release', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertEqual(2, sig_status['threshold']) self.assertEqual([KEYS[0]['keyid']], sig_status['good_sigs']) self.assertEqual([], sig_status['bad_sigs']) self.assertEqual([], sig_status['unknown_sigs']) self.assertEqual([KEYS[1]['keyid']], sig_status['untrusted_sigs']) self.assertEqual([], sig_status['unknown_signing_schemes']) self.assertFalse(tuf.sig.verify(signable, 'Root')) self.assertRaises(tuf.exceptions.UnknownRoleError, tuf.sig.get_signature_status, signable, 'unknown_role') # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) tuf.keydb.remove_key(KEYS[1]['keyid']) # Remove the roles. tuf.roledb.remove_role('Root') tuf.roledb.remove_role('Release') def test_check_signatures_no_role(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) tuf.keydb.add_key(KEYS[0]) # No specific role we're considering. It's invalid to use the # function tuf.sig.verify() without a role specified because # tuf.sig.verify() is checking trust, as well. args = (signable, None) self.assertRaises(securesystemslib.exceptions.Error, tuf.sig.verify, *args) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) def test_verify_single_key(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) tuf.keydb.add_key(KEYS[0]) threshold = 1 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) # This will call verify() and return True if 'signable' is valid, # False otherwise. self.assertTrue(tuf.sig.verify(signable, 'Root')) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) # Remove the roles. tuf.roledb.remove_role('Root') def test_verify_must_not_count_duplicate_keyids_towards_threshold(self): # Create and sign dummy metadata twice with same key # Note that we use the non-deterministic rsassa-pss signing scheme, so # creating the signature twice shows that we don't only detect duplicate # signatures but also different signatures from the same key. signable = {"signed" : "test", "signatures" : []} signed = securesystemslib.formats.encode_canonical( signable["signed"]).encode("utf-8") signable["signatures"].append( securesystemslib.keys.create_signature(KEYS[0], signed)) signable["signatures"].append( securesystemslib.keys.create_signature(KEYS[0], signed)) # 'get_signature_status' uses keys from keydb for verification tuf.keydb.add_key(KEYS[0]) # Assert that 'get_signature_status' returns two good signatures ... status = tuf.sig.get_signature_status( signable, "root", keyids=[KEYS[0]["keyid"]], threshold=2) self.assertTrue(len(status["good_sigs"]) == 2) # ... but only one counts towards the threshold self.assertFalse( tuf.sig.verify(signable, "root", keyids=[KEYS[0]["keyid"]], threshold=2)) # Clean-up keydb tuf.keydb.remove_key(KEYS[0]["keyid"]) def test_verify_count_different_keyids_for_same_key_towards_threshold(self): # Create and sign dummy metadata twice with same key but different keyids signable = {"signed" : "test", "signatures" : []} key_sha256 = copy.deepcopy(KEYS[0]) key_sha256["keyid"] = "deadbeef256" key_sha512 = copy.deepcopy(KEYS[0]) key_sha512["keyid"] = "deadbeef512" signed = securesystemslib.formats.encode_canonical( signable["signed"]).encode("utf-8") signable["signatures"].append( securesystemslib.keys.create_signature(key_sha256, signed)) signable["signatures"].append( securesystemslib.keys.create_signature(key_sha512, signed)) # 'get_signature_status' uses keys from keydb for verification tuf.keydb.add_key(key_sha256) tuf.keydb.add_key(key_sha512) # Assert that the key only counts toward the threshold once keyids = [key_sha256["keyid"], key_sha512["keyid"]] self.assertFalse( tuf.sig.verify(signable, "root", keyids=keyids, threshold=2)) # Clean-up keydb tuf.keydb.remove_key(key_sha256["keyid"]) tuf.keydb.remove_key(key_sha512["keyid"]) def test_verify_unrecognized_sig(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') # Two keys sign it, but only one of them will be trusted. signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[2], signed)) tuf.keydb.add_key(KEYS[0]) tuf.keydb.add_key(KEYS[1]) threshold = 2 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[0]['keyid'], KEYS[1]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) self.assertFalse(tuf.sig.verify(signable, 'Root')) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[0]['keyid']) tuf.keydb.remove_key(KEYS[1]['keyid']) # Remove the roles. tuf.roledb.remove_role('Root') def test_generate_rsa_signature(self): signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) self.assertEqual(1, len(signable['signatures'])) signature = signable['signatures'][0] self.assertEqual(KEYS[0]['keyid'], signature['keyid']) returned_signature = tuf.sig.generate_rsa_signature(signable['signed'], KEYS[0]) self.assertTrue(securesystemslib.formats.SIGNATURE_SCHEMA.matches(returned_signature)) signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[1], signed)) self.assertEqual(2, len(signable['signatures'])) signature = signable['signatures'][1] self.assertEqual(KEYS[1]['keyid'], signature['keyid']) def test_may_need_new_keys(self): # One untrusted key in 'signable'. signable = {'signed' : 'test', 'signatures' : []} signed = securesystemslib.formats.encode_canonical(signable['signed']).encode('utf-8') signable['signatures'].append(securesystemslib.keys.create_signature( KEYS[0], signed)) tuf.keydb.add_key(KEYS[1]) threshold = 1 roleinfo = tuf.formats.build_dict_conforming_to_schema( tuf.formats.ROLE_SCHEMA, keyids=[KEYS[1]['keyid']], threshold=threshold) tuf.roledb.add_role('Root', roleinfo) sig_status = tuf.sig.get_signature_status(signable, 'Root') self.assertTrue(tuf.sig.may_need_new_keys(sig_status)) # Done. Let's remove the added key(s) from the key database. tuf.keydb.remove_key(KEYS[1]['keyid']) # Remove the roles. tuf.roledb.remove_role('Root') def test_signable_has_invalid_format(self): # get_signature_status() and verify() validate 'signable' before continuing. # 'signable' must be of the form: {'signed': , 'signatures': [{}]}. # Object types are checked as well. signable = {'not_signed' : 'test', 'signatures' : []} args = (signable['not_signed'], KEYS[0]) self.assertRaises(securesystemslib.exceptions.FormatError, tuf.sig.get_signature_status, *args) # 'signatures' value must be a list. Let's try a dict. signable = {'signed' : 'test', 'signatures' : {}} args = (signable['signed'], KEYS[0]) self.assertRaises(securesystemslib.exceptions.FormatError, tuf.sig.get_signature_status, *args) # Run unit test. if __name__ == '__main__': utils.configure_test_logging(sys.argv) unittest.main()