def demo_create_and_sign_key_mgr(): prikey_keymgr = cct_common.PrivateKey.from_hex(KEYMGR_PRIVATE_HEX) # pubkey_keymgr = cct_common.PublicKey.from_bytes(KEYMGR_PUBLIC_BYTES) # print('public test key for keymgr: ' + pubkey_keymgr.to_hex()) # print('private test key for keymgr: ' + prikey_keymgr.to_hex()) key_mgr = cct_metadata_construction.build_delegating_metadata( metadata_type='key_mgr', # 'root' or 'key_mgr' delegations={ 'pkg_mgr': { 'pubkeys': [PKGMGR_PUBLIC_HEX], 'threshold': 1 } }, version=1, #timestamp default: now #expiration default: now plus root expiration default duration ) key_mgr = cct_signing.wrap_as_signable(key_mgr) # sign dictionary in place cct_signing.sign_signable(key_mgr, prikey_keymgr) with open(KEYMGR_FNAME, 'wb') as fobj: fobj.write(cct_common.canonserialize(key_mgr)) return key_mgr
def test_verify_existing_root_md(): # It's important that we are able to verify root metadata without anything # except old root metadata, so in particular we don't want to need the # full GPG public key object. Ideally, we want only the Q value of the # key, but if we also need to retain the GPG key fingerprint (listed in the # signature itself), we can do that..... # with open('T_full_rmd.json', 'rb') as fobj: # signed_rmd = json.load(fobj) # # gpg_sig = signed_rmd['signatures'][ # 'bfbeb6554fca9558da7aa05c5e9952b7a1aa3995dede93f3bb89f0abecc7dc07'] # # canonical_signed_portion = common.canonserialize(signed_rmd['signed']) # # with open('T_gpg_key_obj.json', 'rb') as fobj: # gpg_key_obj = json.load(fobj) # q = gpg_key_obj['keyval']['public']['q'] # fingerprint = gpg_key_obj['keyid'] canonical_signed_portion = common.canonserialize(SAMPLE_ROOT_MD_CONTENT) # # First, try using securesystemslib's GPG signature verifier directly. # verified = securesystemslib.gpg.functions.verify_signature( # SAMPLE_GPG_SIG, # SAMPLE_GPG_KEY_OBJ, # <-- We don't want conda to have to provide this. # canonical_signed_portion) # assert verified # # Second, try it using my adapter, skipping a bit of ssl's process. # verified = root_signing.verify_gpg_sig_using_ssl( # SAMPLE_GPG_SIG, # SAMPLE_FINGERPRINT, # SAMPLE_KEYVAL, # canonical_signed_portion) # assert verified # Third, use internal code only. (This is what we're actually going to # use in conda.) # Verify using verify_gpg_signature. authentication.verify_gpg_signature( # authentication.verify_gpg_signature( SAMPLE_GPG_SIG, SAMPLE_KEYVAL, canonical_signed_portion) print('--TEST SUCCESS✅: GPG signature verification without using GPG or ' 'securesystemslib') # Verify using verify_signable. authentication.verify_signable(SAMPLE_SIGNED_ROOT_MD, [SAMPLE_KEYVAL], 1, gpg=True)
def test_root_gen_sign_verify(): # Integration test # Build a basic root metadata file with empty key_mgr delegation and one # root key, threshold 1, version 1. rmd = metadata_construction.build_root_metadata( root_version=1, root_pubkeys=[SAMPLE_KEYVAL], root_threshold=1, key_mgr_pubkeys=[], key_mgr_threshold=1) rmd = signing.wrap_as_signable(rmd) signed_portion = rmd['signed'] canonical_signed_portion = common.canonserialize(signed_portion) if not SSLIB_AVAILABLE: pytest.skip('--TEST SKIPPED⚠️ : Unable to perform GPG signing without ' 'securesystemslib and GPG.') return # gpg_key_obj = securesystemslib.gpg.functions.export_pubkey( # SAMPLE_FINGERPRINT) gpg_sig = root_signing.sign_via_gpg(canonical_signed_portion, SAMPLE_FINGERPRINT) signed_rmd = copy.deepcopy(rmd) signed_rmd['signatures'][SAMPLE_KEYVAL] = gpg_sig # # Dump working files # with open('T_gpg_sig.json', 'wb') as fobj: # fobj.write(common.canonserialize(gpg_sig)) # with open('T_gpg_key_obj.json', 'wb') as fobj: # fobj.write(common.canonserialize(gpg_key_obj)) # with open('T_canonical_sigless_md.json', 'wb') as fobj: # fobj.write(canonical_signed_portion) # with open('T_full_rmd.json', 'wb') as fobj: # fobj.write(common.canonserialize(signed_rmd)) # Verify using the SSL code and the expected pubkey object. # # (Purely as a test -- we wouldn't normally do this.) # verified = securesystemslib.gpg.functions.verify_signature( # gpg_sig, gpg_key_obj, canonical_signed_portion) # assert verified authentication.verify_gpg_signature(gpg_sig, SAMPLE_KEYVAL, canonical_signed_portion) print('--TEST SUCCESS✅: GPG signing (using GPG and securesystemslib) and ' 'GPG signature verification (using only cryptography)')
def create_key_mgr(self, keys): private_key_key_mgr = cct_common.PrivateKey.from_hex( keys["key_mgr"][0]["private"]) pkg_mgr_pub_keys = [k["public"] for k in keys["pkg_mgr"]] key_mgr = cct_metadata_construction.build_delegating_metadata( metadata_type="key_mgr", # 'root' or 'key_mgr' delegations={ "pkg_mgr": { "pubkeys": pkg_mgr_pub_keys, "threshold": 1 } }, version=1, # timestamp default: now # expiration default: now plus root expiration default duration ) key_mgr = cct_signing.wrap_as_signable(key_mgr) # sign dictionary in place cct_signing.sign_signable(key_mgr, private_key_key_mgr) key_mgr_serialized = cct_common.canonserialize(key_mgr) with open(self.folder / "key_mgr.json", "wb") as fobj: fobj.write(key_mgr_serialized) # let's run a verification root_metadata = cct_common.load_metadata_from_file(self.folder / "1.root.json") key_mgr_metadata = cct_common.load_metadata_from_file(self.folder / "key_mgr.json") cct_common.checkformat_signable(root_metadata) if "delegations" not in root_metadata["signed"]: raise ValueError('Expected "delegations" entry in root metadata.') root_delegations = root_metadata["signed"][ "delegations"] # for brevity cct_common.checkformat_delegations(root_delegations) if "key_mgr" not in root_delegations: raise ValueError( 'Missing expected delegation to "key_mgr" in root metadata.') cct_common.checkformat_delegation(root_delegations["key_mgr"]) # Doing delegation processing. cct_authentication.verify_delegation("key_mgr", key_mgr_metadata, root_metadata) console.print( "[green]Success: key mgr metadata verified based on root metadata." ) return key_mgr
def create_root(self, keys): root_keys = keys["root"] root_pubkeys = [k["public"] for k in root_keys] key_mgr_pubkeys = [k["public"] for k in keys["key_mgr"]] root_version = 1 root_md = cct_metadata_construction.build_root_metadata( root_pubkeys=root_pubkeys[0:1], root_threshold=1, root_version=root_version, key_mgr_pubkeys=key_mgr_pubkeys, key_mgr_threshold=1, ) # Wrap the metadata in a signing envelope. root_md = cct_signing.wrap_as_signable(root_md) root_md_serialized_unsigned = cct_common.canonserialize(root_md) root_filepath = self.folder / f"{root_version}.root.json" print("Writing out: ", root_filepath) # Write unsigned sample root metadata. with open(root_filepath, "wb") as fout: fout.write(root_md_serialized_unsigned) # This overwrites the file with a signed version of the file. cct_root_signing.sign_root_metadata_via_gpg( root_filepath, root_keys[0]["fingerprint"]) # Load untrusted signed root metadata. signed_root_md = cct_common.load_metadata_from_file(root_filepath) cct_authentication.verify_signable(signed_root_md, root_pubkeys, 1, gpg=True) console.print("[green]Root metadata signed & verified!")
def demo_root_signing_and_verifying_and_chaining(): # Build sample root metadata. ('metadata' -> 'md') root_md = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX], root_threshold=1, root_version=1, key_mgr_pubkeys=[KEYMGR_PUBLIC_HEX], key_mgr_threshold=1) # Wrap the metadata in a signing envelope. root_md = cct_signing.wrap_as_signable(root_md) root_md_serialized_unsigned = cct_common.canonserialize(root_md) print('\n-- Unsigned root metadata version 1 generated.\n') # # This is the part of the data over which signatures are constructed. # root_md_serialized_portion_to_sign = cct_common.canonserialize( # root_md['signed']) # TODO: ✅ Format-validate constructed root metadata using checkformat # function. if not os.path.exists('demo'): os.mkdir('demo') # Write unsigned sample root metadata. with open(ROOT_FNAME_V1, 'wb') as fobj: fobj.write(root_md_serialized_unsigned) print('\n-- Unsigned root metadata version 1 written.\n') # Sign sample root metadata. junk = input_func( 'Preparing to request root signature. Please plug in your ' 'YubiKey and prepare to put in your user PIN in a GPG dialog box. ' ' When the YubiKey is plugged in and you are READY TO ENTER your ' 'pin, hit enter to begin.') # This overwrites the file with a signed version of the file. cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V1, ROOT_PUBKEY_GPG_FINGERPRINT) cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V1, ROOT_PUBKEY_GPG_FINGERPRINT) junk = input_func( '\n-- Root metadata v1 signed. Next: load signed root v1.\n') # Load untrusted signed root metadata. signed_root_md = cct_common.load_metadata_from_file(ROOT_FNAME_V1) junk = input_func( '\n-- Signed root metadata v1 loaded. Next: verify signed root v1\n') # Verify untrusted signed root metadata. (Normally, one uses the prior # version of root, but here we're bootstrapping for the demo. We'll verify # with a prior version lower down in this demo.) cct_authentication.verify_signable(signed_root_md, [ROOT_PUBKEY_HEX], 1, gpg=True) junk = input_func( '\n-- Root metadata v1 fully verified. Next: build root metadata v2.\n' ) # Build sample second version of root metadata. In this case, let's try # adding another authorized key and requiring signatures from both keys. root_md2 = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX, ROOT_PUBKEY_2_HEX], root_threshold=2, root_version=2, key_mgr_pubkeys=[KEYMGR_PUBLIC_HEX], key_mgr_threshold=1) # Wrap the version 2 metadata in a signing envelope, canonicalize it, and # serialize it to write to disk. root_md2 = cct_signing.wrap_as_signable(root_md2) root_md2 = cct_common.canonserialize(root_md2) # Write unsigned sample root metadata. with open(ROOT_FNAME_V2, 'wb') as fobj: fobj.write(root_md2) junk = input_func( '\n-- Unsigned root metadata version 2 generated and written. Next: sign root v2\n' ) # This overwrites the file with a signed version of the file. # We'll sign with both keys specified. cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V2, ROOT_PUBKEY_GPG_FINGERPRINT) cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V2, ROOT_PUBKEY_2_GPG_FINGERPRINT) junk = input_func( '\n-- Root metadata v2 signed. Next: load and verify signed root v2 based on root v1 (root chaining).\n' ) # Load the now-signed version from disk. signed_root_md2 = cct_common.load_metadata_from_file(ROOT_FNAME_V2) # Test root chaining (verifying v2 using v1) cct_authentication.verify_root(signed_root_md, signed_root_md2) print('\n-- Root metadata v2 fully verified based directly on Root ' 'metadata v1 (root chaining success)\n') print('\n-- Success. :)\n') # Build sample third version of root metadata. In this case, let's reduce # the number of required keys to one. root_md3 = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX, ROOT_PUBKEY_2_HEX], root_threshold=1, root_version=3, key_mgr_pubkeys=[KEYMGR_PUBLIC_HEX], key_mgr_threshold=1) # Wrap the version 2 metadata in a signing envelope, canonicalize it, and # serialize it to write to disk. root_md3 = cct_signing.wrap_as_signable(root_md3) root_md3 = cct_common.canonserialize(root_md3) # Write unsigned sample root metadata. with open(ROOT_FNAME_V3, 'wb') as fobj: fobj.write(root_md3) junk = input_func( '\n-- Unsigned root metadata version 2 generated and written. Next: sign root v2\n' ) # This overwrites the file with a signed version of the file. # We'll sign with both keys specified. cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V3, ROOT_PUBKEY_GPG_FINGERPRINT) cct_root_signing.sign_root_metadata_via_gpg(ROOT_FNAME_V3, ROOT_PUBKEY_2_GPG_FINGERPRINT) junk = input_func( '\n-- Root metadata v2 signed. Next: load and verify signed root v2 based on root v1 (root chaining).\n' ) # Load the now-signed version from disk. signed_root_md3 = cct_common.load_metadata_from_file(ROOT_FNAME_V3) # Test root chaining (verifying v2 using v1) cct_authentication.verify_root(signed_root_md2, signed_root_md3) print('\n-- Root metadata v3 fully verified based directly on Root ' 'metadata v2 (root chaining success)\n') print('\n-- Success. :)\n') return signed_root_md, signed_root_md2, signed_root_md3