diff --git a/README.rst b/README.rst index a39b317..40c2f8b 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ ############################################################################## -car: Conda Authentication Resources: Signing and verification tools for Conda +car: Conda Content Trust: Signing and verification tools for Conda ############################################################################## @@ -18,9 +18,9 @@ Installation Installation can be accomplished by: 1. obtaining this code (download a zip and expand it or git clone the repository). e.g.: - ``git clone https://github.com/conda/conda-authentication-resources`` + ``git clone https://github.com/conda/conda-content-trust`` -2. ``cd conda-authentication-resources`` +2. ``cd conda-content-trust`` 3. ``pip install .`` diff --git a/appveyor.yml b/appveyor.yml index e1d455d..5e66c0f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -41,7 +41,7 @@ install: - conda info # this is to ensure dependencies - conda build conda.recipe --no-test - - conda install --use-local car + - conda install --use-local conda-content-trust # Not a .NET project, we build package in the install step instead diff --git a/car/__main__.py b/car/__main__.py deleted file mode 100644 index f782d0a..0000000 --- a/car/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from car import cli - -cli.cli() diff --git a/car/__init__.py b/conda_content_trust/__init__.py similarity index 100% rename from car/__init__.py rename to conda_content_trust/__init__.py diff --git a/conda_content_trust/__main__.py b/conda_content_trust/__main__.py new file mode 100644 index 0000000..006a5a6 --- /dev/null +++ b/conda_content_trust/__main__.py @@ -0,0 +1,3 @@ +from conda_content_trust import cli + +cli.cli() diff --git a/car/_version.py b/conda_content_trust/_version.py similarity index 100% rename from car/_version.py rename to conda_content_trust/_version.py diff --git a/car/authentication.py b/conda_content_trust/authentication.py similarity index 99% rename from car/authentication.py rename to conda_content_trust/authentication.py index fb791a8..ca4214b 100644 --- a/car/authentication.py +++ b/conda_content_trust/authentication.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -""" car.authentication +""" conda_content_trust.authentication This module contains functions that verify signatures and thereby authenticate data. @@ -519,7 +519,7 @@ def verify_gpg_signature(signature, key_value, data): checkformat_gpg_signature(signature) checkformat_hex_key(key_value) checkformat_byteslike(data) - # if not isinstance(data, bytes): # TODO: ✅ use the byteslike checker in car.common. + # if not isinstance(data, bytes): # TODO: ✅ use the byteslike checker in conda_content_trust.common. # raise TypeError() public_key = PublicKey.from_hex(key_value) diff --git a/car/cli.py b/conda_content_trust/cli.py similarity index 92% rename from car/cli.py rename to conda_content_trust/cli.py index 02c29e2..e140c53 100644 --- a/car/cli.py +++ b/conda_content_trust/cli.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- -""" car.cli -This module provides the CLI interface for conda-authentication-resources. +""" conda_content_trust.cli +This module provides the CLI interface for conda-content-trust. This is intended to provide a command-line signing and metadata update interface. """ @@ -13,15 +13,15 @@ from argparse import ArgumentParser import copy -from car.common import ( +from conda_content_trust.common import ( canonserialize, load_metadata_from_file, write_metadata_to_file, - CAR_Error, PrivateKey, is_gpg_fingerprint, is_hex_key) + CCT_Error, PrivateKey, is_gpg_fingerprint, is_hex_key) -from car import __version__ -import car.root_signing -import car.signing -import car.authentication -import car.metadata_construction +from conda_content_trust import __version__ +import conda_content_trust.root_signing as cct_root_signing +import conda_content_trust.signing as cct_signing +import conda_content_trust.authentication as cct_authentication +import conda_content_trust.metadata_construction as cct_metadata_construction # In Python2, input() performs evaluation and raw_input() does not. In # Python3, input() does not perform evaluation and there is no raw_input(). @@ -48,8 +48,8 @@ def cli(args=None): p.add_argument( '-V', '--version', action='version', - help='Show the conda-authentication-resources version number and exit.', - version="car %s" % __version__, + help='Show the conda-content-trust version number and exit.', + version="conda-content-trust %s" % __version__, ) # Create separate parsers for the subcommands. @@ -106,7 +106,7 @@ def cli(args=None): # If we're missing optional requirements for the next few options, note # that in their help strings. opt_reqs_str = '' - if not car.root_signing.SSLIB_AVAILABLE: + if not cct_root_signing.SSLIB_AVAILABLE: opt_reqs_str = ('[Unavailable]: Requires optional ' 'dependencies: securesystemslib and gpg. ') @@ -149,21 +149,21 @@ def cli(args=None): # so this is necessary for convenience. gpg_key_fingerprint = ''.join(args.gpg_key_fingerprint.split()).lower() - car.root_signing.sign_root_metadata_via_gpg( + cct_root_signing.sign_root_metadata_via_gpg( args.filename, gpg_key_fingerprint) elif args.subcommand_name == 'sign-artifacts': - car.signing.sign_all_in_repodata( + 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 = car.root_signing.fetch_keyval_from_gpg(gpg_key_fingerprint) + keyval = cct_root_signing.fetch_keyval_from_gpg(gpg_key_fingerprint) print('Underlying ed25519 key value: ' + str(keyval)) @@ -189,7 +189,7 @@ def cli(args=None): old_metadata = load_metadata_from_file(args.metadata_filename) - # new_metadata = car.metadata_construction.interactive_modify_metadata(old_metadata) + # 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) @@ -203,7 +203,7 @@ def cli(args=None): # `car verify-metadata <(optional) role name>` - # underlying functions: car.authentication.verify_delegation, + # underlying functions: cct_authentication.verify_delegation, # load_metadata_from_file # takes two metadata files, the first being a trusted file that should @@ -233,11 +233,11 @@ def cli(args=None): if metadata_type == 'root': # Verifying root has additional steps beyond verify_delegation. try: - car.authentication.verify_root(trusted_metadata, untrusted_metadata) + cct_authentication.verify_root(trusted_metadata, untrusted_metadata) print('Root metadata verification successful.') return 0 # success - except CAR_Error as e: + except CCT_Error as e: errorcode = 10 errorstring = str(e) @@ -245,14 +245,14 @@ def cli(args=None): # Verifying anything other than root just uses verify_delegation # directly. try: - car.authentication.verify_delegation( + 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 CAR_Error as e: + except CCT_Error as e: errorcode = 20 errorstring = str(e) @@ -332,7 +332,7 @@ def fn_abort(): return 1 def fn_addsig(): - if not car.root_signing.SSLIB_AVAILABLE: + if not cct_root_signing.SSLIB_AVAILABLE: print(F_OPTS + 'Signing. ' + RED + 'Please ABORT (control-c) if ' 'the metadata above is not EXACTLY what you want to sign!' + ENDC) @@ -347,12 +347,12 @@ def fn_addsig(): if is_hex_key(key): private_key = PrivateKey.from_hex(key) - car.signing.sign_signable(metadata, private_key) + cct_signing.sign_signable(metadata, private_key) print(F_OPTS + '\n\n--- Successfully signed! Please save.' + ENDC) elif is_gpg_fingerprint(key): try: - car.root_signing.sign_root_metadata_dict_via_gpg(metadata, key) + cct_root_signing.sign_root_metadata_dict_via_gpg(metadata, key) except: print(F_OPTS + '\n\n--- ' + RED + 'Signing FAILED.' + F_OPTS + ' Do you have this key loaded in GPG on ' diff --git a/car/common.py b/conda_content_trust/common.py similarity index 98% rename from car/common.py rename to conda_content_trust/common.py index d40f56d..700a296 100644 --- a/car/common.py +++ b/conda_content_trust/common.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -""" car.common +""" conda_content_trust.common This module contains functions that provide format validation, serialization, and some key transformations for the pyca/cryptography library. These are used -across CAR modules. +across conda_content_trust modules. Function Manifest for this Module, by Category @@ -45,7 +45,7 @@ x keyfiles_to_bytes Exceptions: - CAR_Error + CCT_Error SignatureError MetadataVerificationError UnknownRoleError @@ -65,8 +65,7 @@ import cryptography.hazmat.primitives.hashes import cryptography.hazmat.backends.openssl.ed25519 -# specification version for the metadata produced by -# conda-authentication-resources +# specification version for the metadata produced by conda-content-trust # Details in the Conda Security Metadata Specification. Note that this # version string is parsed via setuptools's packaging.version library, and so # supports PEP 440; however, we should use a limited subset that is numerical @@ -98,27 +97,27 @@ -class CAR_Error(Exception): +class CCT_Error(Exception): """ All errors we raise that are not ValueErrors, TypeErrors, or certain errors from securesystemslib should be instances of this class or of subclasses of this class. """ -class SignatureError(CAR_Error): +class SignatureError(CCT_Error): """ Indicates that a signable cannot be verified due to issues with the signature(s) inside it. """ -class MetadataVerificationError(CAR_Error): +class MetadataVerificationError(CCT_Error): """ Indicates that a chain of authority metadata cannot be verified (e.g. a metadata update is found on the repository, but could not be authenticated). """ -class UnknownRoleError(CAR_Error): +class UnknownRoleError(CCT_Error): """ Indicates that a piece of role metadata (like root.json, or key_mgr.json) was expected but not found. @@ -269,7 +268,7 @@ def from_bytes(cls, key_value_in_bytes): # # # # Before the next two lines are run, this is the situation: # # > cls.__bases__ - # # (, + # # (, # # ) # # > new_object.__class__ # # @@ -756,10 +755,10 @@ def checkformat_gpg_signature(signature_obj): def is_a_signature(signature_obj): """ Returns True if signature_obj is a dictionary representing an ed25519 - signature, either in the conda-authentication-resources normal format, or + signature, either in the conda-content-trust normal format, or the format for a GPG signature. - See car.common.checkformat_signature() docstring for more details. + See conda_content_trust.common.checkformat_signature() docstring for more details. """ try: checkformat_signature(signature_obj) @@ -1244,7 +1243,7 @@ def iso8601_time_plus_delta(delta): # made (just a few adjustments). # def _gpgsig_to_sslgpgsig(gpg_sig): # -# car.common.checkformat_gpg_signature(gpg_sig) +# conda_content_trust.common.checkformat_gpg_signature(gpg_sig) # # return { # 'keyid': copy.deepcopy(gpg_sig['key_fingerprint']), diff --git a/car/encryption.py b/conda_content_trust/encryption.py similarity index 100% rename from car/encryption.py rename to conda_content_trust/encryption.py diff --git a/car/metadata_construction.py b/conda_content_trust/metadata_construction.py similarity index 98% rename from car/metadata_construction.py rename to conda_content_trust/metadata_construction.py index 140d9af..b211386 100644 --- a/car/metadata_construction.py +++ b/conda_content_trust/metadata_construction.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -""" car.metadata_construction +""" conda_content_trust.metadata_construction This module contains functions that construct metadata and generate signing keys. @@ -197,9 +197,9 @@ def gen_keys(): Generate an ed25519 key pair and return it (private key, public key). Returns two objects: - - a car.common.PrivateKey, a subclass of + - a conda_content_trust.common.PrivateKey, a subclass of cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey - - a car.common.PublicKey, a subclass of + - a conda_content_trust.common.PublicKey, a subclass of cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey """ # Create an ed25519 key pair, employing OS random generation. diff --git a/car/root_signing.py b/conda_content_trust/root_signing.py similarity index 96% rename from car/root_signing.py rename to conda_content_trust/root_signing.py index a0a6fb9..ee5136b 100644 --- a/car/root_signing.py +++ b/conda_content_trust/root_signing.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -""" car.root_signing +""" conda_content_trust.root_signing This module contains functions that sign data in an OpenPGP-compliant (i.e. GPG-friendly) way. Root metadata may be signed in this manner. Functions that perform simpler, direct signing using raw ed25519 keys are provided in -car.signing instead. +conda_content_trust.signing instead. This library takes advantage of the securesystemslib library for its gpg signing interface. @@ -19,7 +19,7 @@ _gpg_pubkey_in_ssl_format _verify_gpg_sig_using_ssl # requires securesystemslib -Note that there is a function in car.authentication that verifies these +Note that there is a function in conda_content_trust.authentication that verifies these signatures without requiring securesystemslib. """ @@ -62,7 +62,7 @@ def sign_via_gpg(data_to_sign, gpg_key_fingerprint, include_fingerprint=False): """ - This is an alternative to the car.common.PrivateKey.sign() method, for + This is an alternative to the conda_content_trust.common.PrivateKey.sign() method, for use with OpenPGP keys, allowing us to use protected keys in YubiKeys (which provide an OpenPGP interface) to sign data. @@ -133,7 +133,7 @@ def sign_via_gpg(data_to_sign, gpg_key_fingerprint, include_fingerprint=False): 'signature': } - This is unlike car.signing.sign(), which simply returns 64 bytes of raw + This is unlike conda_content_trust.signing.sign(), which simply returns 64 bytes of raw ed25519 signature. @@ -333,7 +333,7 @@ def fetch_keyval_from_gpg(fingerprint): if not SSLIB_AVAILABLE: # TODO✅: Consider a missing-optional-dependency exception class. raise Exception( - 'sign_root_metadata_via_gpg requires the securesystemslib library, which ' + 'fetch_keyval_from_gpg requires the securesystemslib library, which ' 'appears to be unavailable.') checkformat_gpg_fingerprint(fingerprint) @@ -347,7 +347,7 @@ def fetch_keyval_from_gpg(fingerprint): def _verify_gpg_sig_using_ssl(signature, gpg_key_fingerprint, key_value, data): """ THIS IS PROVIDED ONLY FOR TESTING PURPOSES. - We will verify signatures using our own code in car.authentication, not + We will verify signatures using our own code in conda_content_trust.authentication, not by using the securesystemslib.gpg.functions.verify_signature call that sits here. @@ -434,7 +434,7 @@ def _gpg_pubkey_in_ssl_format(fingerprint, q): # def _gpgsig_to_sslgpgsig(gpg_sig): # -# car.common.checkformat_gpg_signature(gpg_sig) +# conda_content_trust.common.checkformat_gpg_signature(gpg_sig) # # return { # 'keyid': copy.deepcopy(gpg_sig['key_fingerprint']), diff --git a/car/signing.py b/conda_content_trust/signing.py similarity index 98% rename from car/signing.py rename to conda_content_trust/signing.py index 7bbcf9b..876a24e 100644 --- a/car/signing.py +++ b/conda_content_trust/signing.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -""" car.signing +""" conda_content_trust.signing This module contains functions that sign data using ed25519 keys, via the pyca/cryptography library. Functions that perform OpenPGP-compliant (e.g. GPG) signing are provided instead in root_signing. @@ -29,7 +29,7 @@ #import cryptography.hazmat.backends -# car modules +# conda-content-trust modules from .common import ( SUPPORTED_SERIALIZABLE_TYPES, canonserialize, load_metadata_from_file, write_metadata_to_file, @@ -56,7 +56,7 @@ def serialize_and_sign(obj, private_key): Arguments: obj: a JSON-compatible object -- see common.canonserialize() - private_key: a car.common.PrivateKey object + private_key: a conda_content_trust.common.PrivateKey object # TODO ✅: Consider taking the private key data as a hex string instead? # On the other hand, it's useful to support an object that could diff --git a/demo.py b/demo.py index e7e6c11..36c5fd8 100644 --- a/demo.py +++ b/demo.py @@ -6,14 +6,14 @@ import os import json -import car.metadata_construction -import car.common -import car.signing -import car.root_signing -import car.authentication +import conda_content_trust.metadata_construction as cct_metadata_construction +import conda_content_trust.common as cct_common +import conda_content_trust.signing as cct_signing +import conda_content_trust.root_signing as cct_root_signing +import conda_content_trust.authentication as cct_authentication -PRESENT_SLOWLY = True +PRESENT_SLOWLY = False ROOT_PUBKEY_HEX = 'c8bd83b3bfc991face417d97b9c0db011b5d256476b602b92fec92849fc2b36c' ROOT_PUBKEY_GPG_FINGERPRINT = '917adb684e2e9fb5ed4e59909ddd19a1268b62d0' @@ -71,7 +71,7 @@ def main(): # demo_root_chaining(root_v1, root_v2) # redundant test for my dev purposes # To load metadata from a file - # key_mgr = car.common.load_metadata_from_file('test_key_mgr.json') + # key_mgr = cct_common.load_metadata_from_file('test_key_mgr.json') # If loading a key from file, for example.... # with open(name + '.pri', 'rb') as fobj: @@ -100,12 +100,12 @@ def main(): def demo_create_and_sign_key_mgr(): - prikey_keymgr = car.common.PrivateKey.from_hex(KEYMGR_PRIVATE_HEX) - # pubkey_keymgr = car.common.PublicKey.from_bytes(KEYMGR_PUBLIC_BYTES) + 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 = car.metadata_construction.build_delegating_metadata( + key_mgr = cct_metadata_construction.build_delegating_metadata( metadata_type='key_mgr', # 'root' or 'key_mgr' delegations={'pkg_mgr': { 'pubkeys': [PKGMGR_PUBLIC_HEX], @@ -115,13 +115,13 @@ def demo_create_and_sign_key_mgr(): #expiration default: now plus root expiration default duration ) - key_mgr = car.signing.wrap_as_signable(key_mgr) + key_mgr = cct_signing.wrap_as_signable(key_mgr) # sign dictionary in place - car.signing.sign_signable(key_mgr, prikey_keymgr) + cct_signing.sign_signable(key_mgr, prikey_keymgr) with open(KEYMGR_FNAME, 'wb') as fobj: - fobj.write(car.common.canonserialize(key_mgr)) + fobj.write(cct_common.canonserialize(key_mgr)) return key_mgr @@ -132,19 +132,19 @@ def demo_create_and_sign_key_mgr(): def demo_verify_key_mgr_using_root(key_mgr_metadata, root_metadata): # Some argument validation - car.common.checkformat_signable(root_metadata) + 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 - car.common.checkformat_delegations(root_delegations) + cct_common.checkformat_delegations(root_delegations) if 'key_mgr' not in root_delegations: raise ValueError( 'Missing expected delegation to "key_mgr" in root metadata.') - car.common.checkformat_delegation(root_delegations['key_mgr']) + cct_common.checkformat_delegation(root_delegations['key_mgr']) # Doing delegation processing. - car.authentication.verify_delegation( + cct_authentication.verify_delegation( 'key_mgr', key_mgr_metadata, root_metadata) print('\n-- Success: key mgr metadata verified based on root metadata.') @@ -153,7 +153,7 @@ def demo_verify_key_mgr_using_root(key_mgr_metadata, root_metadata): def demo_root_signing_and_verifying_and_chaining(): # Build sample root metadata. ('metadata' -> 'md') - root_md = car.metadata_construction.build_root_metadata( + root_md = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX], root_threshold=1, root_version=1, @@ -161,14 +161,14 @@ def demo_root_signing_and_verifying_and_chaining(): key_mgr_threshold=1) # Wrap the metadata in a signing envelope. - root_md = car.signing.wrap_as_signable(root_md) + root_md = cct_signing.wrap_as_signable(root_md) - root_md_serialized_unsigned = car.common.canonserialize(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 = car.common.canonserialize( + # root_md_serialized_portion_to_sign = cct_common.canonserialize( # root_md['signed']) @@ -192,28 +192,28 @@ def demo_root_signing_and_verifying_and_chaining(): 'pin, hit enter to begin.') # This overwrites the file with a signed version of the file. - car.root_signing.sign_root_metadata_via_gpg( + cct_root_signing.sign_root_metadata_via_gpg( ROOT_FNAME_V1, ROOT_PUBKEY_GPG_FINGERPRINT) - car.root_signing.sign_root_metadata_via_gpg( + 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 = car.common.load_metadata_from_file(ROOT_FNAME_V1) + 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.) - car.authentication.verify_signable( + 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 = car.metadata_construction.build_root_metadata( + root_md2 = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX, ROOT_PUBKEY_2_HEX], root_threshold=2, root_version=2, @@ -222,8 +222,8 @@ def demo_root_signing_and_verifying_and_chaining(): # Wrap the version 2 metadata in a signing envelope, canonicalize it, and # serialize it to write to disk. - root_md2 = car.signing.wrap_as_signable(root_md2) - root_md2 = car.common.canonserialize(root_md2) + root_md2 = cct_signing.wrap_as_signable(root_md2) + root_md2 = cct_common.canonserialize(root_md2) # Write unsigned sample root metadata. @@ -233,17 +233,17 @@ def demo_root_signing_and_verifying_and_chaining(): # This overwrites the file with a signed version of the file. # We'll sign with both keys specified. - car.root_signing.sign_root_metadata_via_gpg( + cct_root_signing.sign_root_metadata_via_gpg( ROOT_FNAME_V2, ROOT_PUBKEY_GPG_FINGERPRINT) - car.root_signing.sign_root_metadata_via_gpg( + 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 = car.common.load_metadata_from_file(ROOT_FNAME_V2) + signed_root_md2 = cct_common.load_metadata_from_file(ROOT_FNAME_V2) # Test root chaining (verifying v2 using v1) - car.authentication.verify_root(signed_root_md, signed_root_md2) + 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') @@ -254,7 +254,7 @@ def demo_root_signing_and_verifying_and_chaining(): # Build sample third version of root metadata. In this case, let's reduce # the number of required keys to one. - root_md3 = car.metadata_construction.build_root_metadata( + root_md3 = cct_metadata_construction.build_root_metadata( root_pubkeys=[ROOT_PUBKEY_HEX, ROOT_PUBKEY_2_HEX], root_threshold=1, root_version=3, @@ -263,8 +263,8 @@ def demo_root_signing_and_verifying_and_chaining(): # Wrap the version 2 metadata in a signing envelope, canonicalize it, and # serialize it to write to disk. - root_md3 = car.signing.wrap_as_signable(root_md3) - root_md3 = car.common.canonserialize(root_md3) + root_md3 = cct_signing.wrap_as_signable(root_md3) + root_md3 = cct_common.canonserialize(root_md3) # Write unsigned sample root metadata. @@ -274,17 +274,17 @@ def demo_root_signing_and_verifying_and_chaining(): # This overwrites the file with a signed version of the file. # We'll sign with both keys specified. - car.root_signing.sign_root_metadata_via_gpg( + cct_root_signing.sign_root_metadata_via_gpg( ROOT_FNAME_V3, ROOT_PUBKEY_GPG_FINGERPRINT) - car.root_signing.sign_root_metadata_via_gpg( + 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 = car.common.load_metadata_from_file(ROOT_FNAME_V3) + signed_root_md3 = cct_common.load_metadata_from_file(ROOT_FNAME_V3) # Test root chaining (verifying v2 using v1) - car.authentication.verify_root(signed_root_md2, signed_root_md3) + 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') @@ -309,9 +309,9 @@ def demo_root_chaining_w_files(trusted_root_fname, new_untrusted_root_fname): # variables, for example, can be compromised by random packages # adding environment variables? - trusted_root = car.common.load_metadata_from_file(trusted_root_fname) + trusted_root = cct_common.load_metadata_from_file(trusted_root_fname) - untrusted_root = car.common.load_metadata_from_file(new_untrusted_root_fname) + untrusted_root = cct_common.load_metadata_from_file(new_untrusted_root_fname) # Use that to verify the next root. @@ -358,13 +358,13 @@ def demo_verify_pkg_sig_via_key_mgr(key_mgr): pprint(packages) junk = input_func('\n\nNext: sign it with the pkg_mgr key.') - signable = car.signing.wrap_as_signable( + signable = cct_signing.wrap_as_signable( packages['pytorch-1.2.0-cuda92py27hd3e106c_0.tar.bz2']) # Sign in place. - car.signing.sign_signable( + cct_signing.sign_signable( signable, - car.common.PrivateKey.from_hex('f3cdab14740066fb277651ec4f96b9f6c3e3eb3f812269797b9656074cd52133')) + cct_common.PrivateKey.from_hex('f3cdab14740066fb277651ec4f96b9f6c3e3eb3f812269797b9656074cd52133')) print('Signed envelope around this pytorch package metadata:\n\n') pprint(signable) @@ -376,20 +376,20 @@ def demo_verify_pkg_sig_via_key_mgr(key_mgr): # Some argument validation for the key manager role. - car.common.checkformat_signable(key_mgr) + 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 - car.common.checkformat_delegations(key_mgr_delegations) + 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.') - car.common.checkformat_delegation( + cct_common.checkformat_delegation( key_mgr_delegations['pkg_mgr']) # Doing delegation processing. - car.authentication.verify_delegation('pkg_mgr', signable, key_mgr) + cct_authentication.verify_delegation('pkg_mgr', signable, key_mgr) print( '\n\nSuccess: signature over package metadata verified based on ' @@ -401,8 +401,8 @@ def demo_verify_pkg_sig_via_key_mgr(key_mgr): signable['signed']['depends'] += ['django'] try: - car.authentication.verify_delegation('pkg_mgr', signable, key_mgr) - except car.common.SignatureError: + 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.' diff --git a/setup.cfg b/setup.cfg index 28a3424..3374556 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [flake8] max-line-length = 100 ignore = E122,E123,E126,E127,E128,E731,E722 -exclude = build,car/_version.py,tests,conda.recipe,.git,versioneer.py,benchmarks,.asv +exclude = build,conda_content_trust/_version.py,tests,conda.recipe,.git,versioneer.py,benchmarks,.asv [tool:pytest] norecursedirs= .* *.egg* build dist conda.recipe @@ -20,10 +20,10 @@ markers = [versioneer] VCS = git -versionfile_source = car/_version.py -versionfile_build = car/_version.py +versionfile_source = conda_content_trust/_version.py +versionfile_build = conda_content_trust/_version.py tag_prefix = -parentdir_prefix = conda-authentication-resources- +parentdir_prefix = conda-content-trust- [bdist_wheel] universal=1 diff --git a/setup.py b/setup.py index 084d945..3a94b96 100644 --- a/setup.py +++ b/setup.py @@ -8,28 +8,27 @@ ] setup( - name='conda-authentication-resources', + name='conda-content-trust', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), description="Signing and verification tools for conda", license="BSD", author="Anaconda, Inc.", author_email='conda@anaconda.com', - url='https://github.com/conda/conda-authentication-resources', - packages=['car'], + url='https://github.com/conda/conda-content-trust', + packages=['conda_content_trust'], entry_points={ 'console_scripts': [ - 'car=car.cli:cli' + 'conda-content-trust=conda_content_trust.cli:cli' ] }, install_requires=requirements, - # Until the ed25519-gpg-support is merged into the main branch of - # securesystemslib, we'll use this git branch. Note that this is an - # optional dependency, required only to produce gpg-based signatures - # (instead of plain ed25519 sigs via pyca/cryptography). - # ⚠️ DEPENDENCY ON SECURESYSTEMSLIB PINNED. + # Note that the securesystemslib optional dependency is only required to + # produce gpg-based signatures (instead of plain ed25519 sigs via + # pyca/cryptography). + # WARNING: DEPENDENCY ON SECURESYSTEMSLIB PINNED. extras_require = {'gpgsigning': ['securesystemslib==0.13.1']}, - keywords='conda-authentication-resources', + keywords='conda-content-trust conda-authentication-resources conda signing secure verify authentication key compromise', classifiers=[ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.6', diff --git a/tests/test_authentication.py b/tests/test_authentication.py index d4ed20f..335e21b 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -2,7 +2,7 @@ """ tests.test_authentication -Unit tests for conda-authentication-resources/car/authentication.py +Unit tests for conda-content-trust/car/authentication.py as well as integration tests for the signing.py + authentication.py. Run the tests this way: @@ -22,15 +22,15 @@ import cryptography.exceptions # this codebase -from car.authentication import * -from car.metadata_construction import ( +from conda_content_trust.authentication import * +from conda_content_trust.metadata_construction import ( gen_keys, gen_and_write_keys, # for new-key tests # build_repodata_verification_metadata ) -from car.common import ( +from conda_content_trust.common import ( PrivateKey, PublicKey, keyfiles_to_bytes, keyfiles_to_keys, SignatureError, MetadataVerificationError) -from car.signing import wrap_as_signable, sign_signable +from conda_content_trust.signing import wrap_as_signable, sign_signable # Some REGRESSION test data. REG__KEYPAIR_NAME = 'keytest_old' @@ -144,7 +144,7 @@ -# ⚠️ NOTE to dev: +# NOTE to dev: # test_authenticate was originally a long sequence of tests in a single # function. I pulled out most of it, and what remains is has to be compared # to the new tests to see if it's still useful. @@ -404,7 +404,7 @@ def test_verify_signature(): # verify_root is also tested in test_root.py (but test_root.py expects GPG) def test_verify_root(): """ - Tests car.authentication.verify_root + Tests conda_content_trust.authentication.verify_root """ # Root chaining: normal test @@ -463,6 +463,6 @@ def test_verify_root(): # def test_verify_delegation(): # """ -# Tests car.authentication.verify_delegation +# Tests conda_content_trust.authentication.verify_delegation # """ # raise NotImplementedError('verify_delegation requires unit tests.') diff --git a/tests/test_cli.py b/tests/test_cli.py index e5dee55..5ee1a7f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,7 +2,7 @@ """ tests.test_cli -Integration tests for conda-authentication-resources/car/cli.py. +Integration tests for conda-content-trust/conda_content_trust/cli.py. Run the tests this way: pytest tests/test_cli.py @@ -11,20 +11,20 @@ import subprocess -import car.cli +import conda_content_trust.cli def test_cli_basics(): - assert not subprocess.call(['car', '-V']) - assert not subprocess.call(['car', '--version']) - assert not subprocess.call(['car', '--help']) + assert not subprocess.call(['conda-content-trust', '-V']) + assert not subprocess.call(['conda-content-trust', '--version']) + assert not subprocess.call(['conda-content-trust', '--help']) def test_that_all_calls_complete(): - assert not subprocess.call(['car', '-V']) - assert not subprocess.call(['car', '--version']) - assert not subprocess.call(['car', '--help']) + assert not subprocess.call(['conda-content-trust', '-V']) + assert not subprocess.call(['conda-content-trust', '--version']) + assert not subprocess.call(['conda-content-trust', '--help']) def test_gpg_key_fingerprint(): - assert not subprocess.call(['car', '-V']) + assert not subprocess.call(['conda-content-trust', '-V']) raise NotImplementedError() def test_(): diff --git a/tests/test_common.py b/tests/test_common.py index 2fa352b..573440a 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -2,7 +2,7 @@ """ tests.test_common -(Mostly) unit tests for conda-authentication-resources/car/common.py. +(Mostly) unit tests for conda-content-trust/conda_content_trust/common.py. Run the tests this way: pytest tests/test_common.py @@ -16,7 +16,7 @@ import pytest -from car.common import * +from conda_content_trust.common import * # A 40-hex-character GPG public key fingerprint SAMPLE_FINGERPRINT = 'f075dd2f6f4cb3bd76134bbb81b6ca16ef9cd589' diff --git a/tests/test_metadata_construction.py b/tests/test_metadata_construction.py index 96a1ca0..01902da 100644 --- a/tests/test_metadata_construction.py +++ b/tests/test_metadata_construction.py @@ -3,7 +3,7 @@ """ tests.test_metadata_construction (Mostly) unit tests for -conda-authentication-resources/car/metadata_construction.py. +conda-content-trust/conda_content_trust/metadata_construction.py. Run the tests this way: pytest tests/test_metadata_construction.py @@ -27,11 +27,11 @@ import cryptography.exceptions # for InvalidSignature # this codebase -from car.metadata_construction import * -from car.common import ( # these aren't already imported by metadata_construction +from conda_content_trust.metadata_construction import * +from conda_content_trust.common import ( # these aren't already imported by metadata_construction keyfiles_to_bytes, keyfiles_to_keys, checkformat_key, is_a_signable, checkformat_delegating_metadata) -from car.signing import wrap_as_signable, sign_signable +from conda_content_trust.signing import wrap_as_signable, sign_signable # Some REGRESSION test data. KEYPAIR_NAME = 'keytest_old' diff --git a/tests/test_root.py b/tests/test_root.py index 5f38752..c6ba835 100644 --- a/tests/test_root.py +++ b/tests/test_root.py @@ -2,7 +2,7 @@ """ tests.test_root -Some integration tests tests for conda-authentication-resources that focus on +Some integration tests tests for conda-content-trust that focus on generation, signing, and verification of root metadata. This tests GPG integration via securesystemslib if securesystemslib can be successfully imported. @@ -33,11 +33,11 @@ except ImportError: SSLIB_AVAILABLE = False -import car.common -import car.metadata_construction -import car.signing -import car.root_signing -import car.authentication +import conda_content_trust.common as common +import conda_content_trust.metadata_construction as metadata_construction +import conda_content_trust.signing as signing +import conda_content_trust.root_signing as root_signing +import conda_content_trust.authentication as authentication # Note that changing these sample values breaks the sample signature, so you'd # have to generate a new one. @@ -76,8 +76,8 @@ } # To generate a new signature after changing SAMPLE_ROOT_MD_CONTENT: -# > serialized = car.common.canonserialize(SAMPLE_ROOT_MD_CONTENT) -# > new_sig = car.root_signing.sign_via_gpg(serialized, SAMPLE_FINGERPRINT) +# > serialized = common.canonserialize(SAMPLE_ROOT_MD_CONTENT) +# > new_sig = root_signing.sign_via_gpg(serialized, SAMPLE_FINGERPRINT) # If desired, you can add: new_sig['see_also'] = SAMPLE_FINGERPRINT to keep # the optional value listed (the OpenPGP fingerprint of the signing key). SAMPLE_GPG_SIG = { @@ -120,7 +120,7 @@ def test_gpg_signing_with_unknown_fingerprint(): # TODO✅: Adjust this to use whatever assertRaises() functionality the # testing suite we're using provides. try: - gpg_sig = car.root_signing.sign_via_gpg( + gpg_sig = root_signing.sign_via_gpg( b'1234', SAMPLE_UNKNOWN_FINGERPRINT) except securesystemslib.gpg.exceptions.CommandError as e: # TODO✅: This is a clumsy check. It's a shame we don't get better @@ -145,15 +145,15 @@ def test_root_gen_sign_verify(): # Build a basic root metadata file with empty key_mgr delegation and one # root key, threshold 1, version 1. - rmd = car.metadata_construction.build_root_metadata( + rmd = metadata_construction.build_root_metadata( root_version=1, root_pubkeys=[SAMPLE_KEYVAL], root_threshold=1, key_mgr_pubkeys=[], key_mgr_threshold=1) - rmd = car.signing.wrap_as_signable(rmd) + rmd = signing.wrap_as_signable(rmd) signed_portion = rmd['signed'] - canonical_signed_portion = car.common.canonserialize(signed_portion) + canonical_signed_portion = common.canonserialize(signed_portion) if not SSLIB_AVAILABLE: @@ -165,7 +165,7 @@ def test_root_gen_sign_verify(): # gpg_key_obj = securesystemslib.gpg.functions.export_pubkey( # SAMPLE_FINGERPRINT) - gpg_sig = car.root_signing.sign_via_gpg( + gpg_sig = root_signing.sign_via_gpg( canonical_signed_portion, SAMPLE_FINGERPRINT) signed_rmd = copy.deepcopy(rmd) @@ -176,16 +176,16 @@ def test_root_gen_sign_verify(): # # Dump working files # with open('T_gpg_sig.json', 'wb') as fobj: - # fobj.write(car.common.canonserialize(gpg_sig)) + # fobj.write(common.canonserialize(gpg_sig)) # with open('T_gpg_key_obj.json', 'wb') as fobj: - # fobj.write(car.common.canonserialize(gpg_key_obj)) + # 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(car.common.canonserialize(signed_rmd)) + # fobj.write(common.canonserialize(signed_rmd)) @@ -196,7 +196,7 @@ def test_root_gen_sign_verify(): # assert verified - car.authentication.verify_gpg_signature( + authentication.verify_gpg_signature( gpg_sig, SAMPLE_KEYVAL, canonical_signed_portion) print( @@ -219,7 +219,7 @@ def test_verify_existing_root_md(): # gpg_sig = signed_rmd['signatures'][ # 'bfbeb6554fca9558da7aa05c5e9952b7a1aa3995dede93f3bb89f0abecc7dc07'] # - # canonical_signed_portion = car.common.canonserialize(signed_rmd['signed']) + # canonical_signed_portion = common.canonserialize(signed_rmd['signed']) # # with open('T_gpg_key_obj.json', 'rb') as fobj: # gpg_key_obj = json.load(fobj) @@ -227,7 +227,7 @@ def test_verify_existing_root_md(): # fingerprint = gpg_key_obj['keyid'] - canonical_signed_portion = car.common.canonserialize( + canonical_signed_portion = common.canonserialize( SAMPLE_ROOT_MD_CONTENT) # # First, try using securesystemslib's GPG signature verifier directly. @@ -239,7 +239,7 @@ def test_verify_existing_root_md(): # assert verified # # Second, try it using my adapter, skipping a bit of ssl's process. - # verified = car.root_signing.verify_gpg_sig_using_ssl( + # verified = root_signing.verify_gpg_sig_using_ssl( # SAMPLE_GPG_SIG, # SAMPLE_FINGERPRINT, # SAMPLE_KEYVAL, @@ -251,8 +251,8 @@ def test_verify_existing_root_md(): # use in conda.) # Verify using verify_gpg_signature. - car.authentication.verify_gpg_signature( - # car.authentication.verify_gpg_signature( + authentication.verify_gpg_signature( + # authentication.verify_gpg_signature( SAMPLE_GPG_SIG, SAMPLE_KEYVAL, canonical_signed_portion) @@ -262,7 +262,7 @@ def test_verify_existing_root_md(): 'securesystemslib') # Verify using verify_signable. - car.authentication.verify_signable( + authentication.verify_signable( SAMPLE_SIGNED_ROOT_MD, [SAMPLE_KEYVAL], 1, gpg=True) diff --git a/tests/test_signing.py b/tests/test_signing.py index 6ea073c..33ecbf5 100644 --- a/tests/test_signing.py +++ b/tests/test_signing.py @@ -2,10 +2,10 @@ """ tests.test_signing -Unit tests for conda-authentication-resources/car/signing.py +Unit tests for conda-content-trust/conda_content_trust/signing.py -⚠️ NOTE that much of the signing module is currently tested in + NOTE that much of the signing module is currently tested in test_authentication.py instead. Some unit tests are missing. @@ -21,9 +21,9 @@ import shutil import copy -from car.common import * -from car.authentication import * -from car.signing import * +from conda_content_trust.common import * +from conda_content_trust.authentication import * +from conda_content_trust.signing import * import pytest