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 demo_verify_key_mgr_using_root(key_mgr_metadata, root_metadata): # Some argument validation 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) print('\n-- Success: key mgr metadata verified based on root metadata.')
def demo_verify_pkg_sig_via_key_mgr(key_mgr): packages = { "pytorch-1.2.0-cuda92py27hd3e106c_0.tar.bz2": { "build": "cuda92py27hd3e106c_0", "build_number": 0, "depends": [ "_pytorch_select 0.2", "blas 1.0 mkl", "cffi", "cudatoolkit 9.2.*", "cudnn >=7.3.0,<=8.0a0", "future", "libgcc-ng >=7.3.0", "libstdcxx-ng >=7.3.0", "mkl >=2019.4,<2021.0a0", "mkl-service >=2,<3.0a0", "ninja", "numpy >=1.11.3,<2.0a0", "python >=2.7,<2.8.0a0" ], "license": "BSD 3-Clause", "license_family": "BSD", "md5": "793c6af90ed62c964e28b046e0b071c6", "name": "pytorch", "sha256": "a53f772a224485df7436d4b2aa2c5d44e249e2fb43eee98831eeaaa51a845697", "size": 282176733, "subdir": "linux-64", "timestamp": 1566783471689, "version": "1.2.0" } } print('\n\n\nHere is a sample package entry from repodata.json:') from pprint import pprint pprint(packages) junk = input_func('\n\nNext: sign it with the pkg_mgr key.') signable = cct_signing.wrap_as_signable( packages['pytorch-1.2.0-cuda92py27hd3e106c_0.tar.bz2']) # Sign in place. cct_signing.sign_signable( signable, cct_common.PrivateKey.from_hex( 'f3cdab14740066fb277651ec4f96b9f6c3e3eb3f812269797b9656074cd52133') ) print('Signed envelope around this pytorch package metadata:\n\n') pprint(signable) junk = input_func( '\n\nNext: verify the signature based on what the now-trusted ' 'key manager role told us to expect.\n') # Some argument validation for the key manager role. cct_common.checkformat_signable(key_mgr) if 'delegations' not in key_mgr['signed']: raise ValueError( 'Expected "delegations" entry in key manager metadata.') key_mgr_delegations = key_mgr['signed']['delegations'] # for brevity cct_common.checkformat_delegations(key_mgr_delegations) if 'pkg_mgr' not in key_mgr_delegations: raise ValueError( 'Missing expected delegation to "pkg_mgr" in key manager metadata.' ) cct_common.checkformat_delegation(key_mgr_delegations['pkg_mgr']) # Doing delegation processing. cct_authentication.verify_delegation('pkg_mgr', signable, key_mgr) print('\n\nSuccess: signature over package metadata verified based on ' 'trusted key manager metadata.') # Additional scenario: tampered metadata / incorrectly signed package # Man-in-the-middle adds false dependency signable['signed']['depends'] += ['django'] try: cct_authentication.verify_delegation('pkg_mgr', signable, key_mgr) except cct_common.SignatureError: print( 'Modified metadata results in verification failure: attack prevented' ) else: assert False, 'Demo test failed.'
def cli(args=None): p = ArgumentParser(description="Signing and verification tools for Conda", conflict_handler='resolve') p.add_argument( '-V', '--version', action='version', help='Show the conda-content-trust version number and exit.', version="conda-content-trust %s" % __version__, ) # Create separate parsers for the subcommands. sp = p.add_subparsers(title='subcommands', dest='subcommand_name') # subcommand: sign-artifacts p_signrepo = sp.add_parser( 'sign-artifacts', help= ('Given a repodata.json ' 'file, produce signatures over the metadata for each artifact listed, ' 'and update the repodata.json file with their individual signatures.' )) p_signrepo.add_argument( 'repodata_fname', help=('the filename of a repodata.json file from ' 'which to retrieve metadata for individual artifacts.')) p_signrepo.add_argument('private_key_hex', help=('the ed25519 private key to be used to ' 'sign each artifact\'s metadata')) # subcommand: verify-metadata p_verifymd = sp.add_parser( 'verify-metadata', help=('Uses the first (trusted) metadata file ' 'to verify the second (not yet trusted) metadata file. For ' 'example, ' '"conda-content-trust verify-metadata 4.root.json 5.root.json"' ' to verify version 5 of root based on version 4 of root, or ' '"conda-content-trust verify-metadata 4.root.json key_mgr.json" ' 'to verify key manager metadata based on version 4 of root.')) p_verifymd.add_argument( 'trusted_metadata_filename', help=( 'the filename of the ' 'already-trusted metadata file that sets the rules for verifying ' 'the untrusted metadata file')) p_verifymd.add_argument('untrusted_metadata_filename', help=('the filename of the ' '(untrusted) metadata file to verify')) # subcommand: modify-metadata p_modifymd = sp.add_parser( 'modify-metadata', help=( 'Interactive metadata modification. Use ' 'this to produce a new version of a metadata file (like root.json ' 'or key_mgr.json), or correct an error in an unpublished metadata ' 'file, or review and sign a metadata file. This increments ' 'version number / timestamp, reports changes on console, etc. For ' 'example, "conda-content-trust modify-metadata 8.root.json" ' 'for assistance in ' 'producing a new version of root (version 9) using version 8.')) p_modifymd.add_argument('metadata_filename', help=('the filename of the existing metadata ' 'file to modify')) # If we're missing optional requirements for the next few options, note # that in their help strings. opt_reqs_str = '' if not cct_root_signing.SSLIB_AVAILABLE: opt_reqs_str = ('[Unavailable]: Requires optional ' 'dependencies: securesystemslib and gpg. ') # subcommand: gpg-key-lookup p_gpglookup = sp.add_parser( 'gpg-key-lookup', help= (opt_reqs_str + 'Given the OpenPGP fingerprint of an ed25519-type OpenPGP key, fetch ' 'the actual ed25519 public key value of the underlying key.')) p_gpglookup.add_argument( 'gpg_key_fingerprint', help=('the 40-hex-character key fingerprint (long keyid) for the ' 'OpenPGP/GPG key that you want to sign something with. Do not ' 'add prefix "0x".')) # subcommand: gpg-sign p_gpgsign = sp.add_parser( 'gpg-sign', help=(opt_reqs_str + 'Sign a given ' 'piece of metadata using GPG instead of the usual signing ' 'mechanisms. Takes an OpenPGP key fingerprint and a filename.')) p_gpgsign.add_argument( 'gpg_key_fingerprint', help=('the 40-hex-character key fingerprint (long keyid) for the ' 'OpenPGP/GPG key that you want to sign something with. Do not ' 'add prefix "0x".')) p_gpgsign.add_argument( 'filename', help=('the filename of the file that will be signed')) args = p.parse_args(args) if args.subcommand_name == 'gpg-sign': # TODO: Validate arguments. # Strip any whitespace from the key fingerprint and lowercase it. # GPG pops out keys in a variety of whitespace arrangements and cases, # so this is necessary for convenience. gpg_key_fingerprint = ''.join(args.gpg_key_fingerprint.split()).lower() cct_root_signing.sign_root_metadata_via_gpg(args.filename, gpg_key_fingerprint) elif args.subcommand_name == 'sign-artifacts': cct_signing.sign_all_in_repodata(args.repodata_fname, args.private_key_hex) elif args.subcommand_name == 'gpg-key-lookup': gpg_key_fingerprint = ''.join(args.gpg_key_fingerprint.split()).lower() keyval = cct_root_signing.fetch_keyval_from_gpg(gpg_key_fingerprint) print('Underlying ed25519 key value: ' + str(keyval)) elif args.subcommand_name == 'modify-metadata': # `conda-content-trust update-metadata <metadata file to produce new version of>` # underlying functions: build_delegating_metadata, # load_metadata_from_file # given a metadata file, increment the version number and timestamps, # reporting the changes on the console # strip signatures # indicate what signatures are required # ask if the user wants to sign; query for the key hex or fname; # ideally, offer this functionality for both root and non-root keys. # For root metadata, we can (and should) also report which keys are # expected / still needed in order for the metadata to be verifiable # according to the old metadata and the new metadata old_metadata = load_metadata_from_file(args.metadata_filename) # new_metadata = cct_metadata_construction.interactive_modify_metadata(old_metadata) # if new_metadata is not None and new_metadata: # write_metadata_to_file(new_metadata, args.metadata_filename) interactive_modify_metadata(old_metadata) elif args.subcommand_name == 'verify-metadata': # `conda-content-trust verify-metadata <trusted delegating metadata> <untrusted # metadata> <(optional) role name>` # underlying functions: cct_authentication.verify_delegation, # load_metadata_from_file # takes two metadata files, the first being a trusted file that should # provide the verification criteria (expected keys and expected number # of keys) for the second file. This should support root-root # verification (root chaining as currently implemented in # conda-content-trust) and delegation from one metadata type to another # (e.g. root to key_mgr) # conveys to the user whether or not the file is trusted, and for what # role. e.g., would convey that the first file is (e.g.) a root # metadata file, that it provides a delegation to <role name>, and that # the <untrusted metadata> file provides <role name> and is signed # appropriately based on what the root metadata file requires of that # delegation. untrusted_metadata = load_metadata_from_file( args.untrusted_metadata_filename) trusted_metadata = load_metadata_from_file( args.trusted_metadata_filename) # TODO✅: Argument validation via the check_format_* calls. metadata_type = untrusted_metadata['signed']['type'] if metadata_type == 'root': # Verifying root has additional steps beyond verify_delegation. try: cct_authentication.verify_root(trusted_metadata, untrusted_metadata) print('Root metadata verification successful.') return 0 # success except CCT_Error as e: errorcode = 10 errorstring = str(e) else: # Verifying anything other than root just uses verify_delegation # directly. try: cct_authentication.verify_delegation( delegation_name=metadata_type, untrusted_delegated_metadata=untrusted_metadata, trusted_delegating_metadata=trusted_metadata) print('Metadata verification successful.') return 0 # success except CCT_Error as e: errorcode = 20 errorstring = str(e) # We should only get here if verification failed. print('Verification of untrusted metadata failed. Metadata ' 'type was "' + metadata_type + '". Error reads:\n "' + errorstring + '"') return errorcode # failure; exit code else: print( 'No command provided. Please use "conda-content-trust -h" for help.' )