Exemple #1
0
    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.'
Exemple #4
0
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.'
        )