def sign_repodata(self, repodata_fn, pkg_mgr_key): final_fn = self.in_folder / "repodata_signed.json" print("copy", repodata_fn, final_fn) shutil.copyfile(repodata_fn, final_fn) cct_signing.sign_all_in_repodata(str(final_fn), pkg_mgr_key) print(f"Signed {final_fn}")
def sign_repodata(self, repodata_fn, keys): target_folder = self.folder / repodata_fn.parent.name if not target_folder.exists(): target_folder.mkdir() final_fn = target_folder / repodata_fn.name print("copy", repodata_fn, final_fn) shutil.copyfile(repodata_fn, final_fn) pkg_mgr_key = keys["pkg_mgr"][0]["private"] cct_signing.sign_all_in_repodata(str(final_fn), pkg_mgr_key) console.print(f"[green]Signed [bold]{final_fn}[/bold]")
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.' )
def sign_repodata(self, repodata_fn, private_key): final_fn = self.in_folder / "repodata.json" cct_signing.sign_all_in_repodata(str(final_fn), private_key)