Example #1
0
    def _validate_hash(self, keystore: KeyStore):
        """
        Validates the given hash from a `keystore` corresponds to the trust
        data's calculated hash.

        Raises a `ValidationError` should the hashes not match.
        """
        data = {"signed": self.signed, "signatures": self.signatures}
        data_dump = bytearray(json.dumps(data, separators=(",", ":")), "utf-8")

        hash_b64, len_ = keystore.get_hash(self.kind)
        hash_ = base64.b64decode(hash_b64).hex()

        data_hash = hashlib.sha256(data_dump).hexdigest()
        data_len = len(data_dump)

        if hash_ != data_hash or len_ != data_len:
            raise ValidationError(
                "failed validating trust data hash.",
                {
                    "given_hash": hash_,
                    "calculated_hash": data_hash,
                    "given_len": len_,
                    "calculated_len": data_len,
                },
            )
Example #2
0
def test_validate_hash_error(td, mock_schema_path, mock_keystore):
    data = trust_data("tests/data/sample_root.json")
    data["signatures"][0]["sig"] = "Q" + data["signatures"][0]["sig"][1:]
    trust_data_ = td.TrustData(data, "root")
    ks = KeyStore()

    with pytest.raises(ValidationError) as err:
        trust_data_._validate_hash(ks)
    assert "failed validating trust data hash." in str(err.value)
Example #3
0
    def _validate_signature(self, keystore: KeyStore):
        """
        Validates the signature of the trust data, using keys from a
        `keystore`.

        Raises a `ValidationError` should the the signature be faulty.
        """
        msg = json.dumps(self.signed, separators=(",", ":"))
        for signature in self.signatures:
            key_id = "root" if self.kind == "root" else signature["keyid"]
            pub_key = keystore.get_key(key_id)
            sig = signature["sig"]

            try:
                verify_signature(pub_key, sig, msg)
            except Exception:
                raise ValidationError(
                    "failed to verify signature of trust data.",
                    {
                        "key_id": key_id,
                        "trust_data_type": self.signed.get("_type")
                    },
                )
Example #4
0
def test_validate(td, mock_schema_path, mock_keystore, data: dict, role: str):
    ks = KeyStore()
    _trust_data = td.TrustData(data, role)
    _trust_data.validate(ks)
Example #5
0
def test_validate_hash(td, mock_schema_path, mock_keystore, data: dict,
                       role: str):
    ks = KeyStore()
    trust_data_ = td.TrustData(data, role)
    assert trust_data_._validate_hash(ks) is None
Example #6
0
def process_chain_of_trust(
    host: str, image: Image, req_delegations: list
):  # pylint: disable=too-many-branches
    """
    Processes the whole chain of trust, provided by the notary server (`host`)
    for any given `image`. The 'root', 'snapshot', 'timestamp', 'targets' and
    potentially 'targets/releases' are requested in this order and afterwards
    validated, also according to the `policy_rule`.

    Returns the signed image targets, which contain the digests.

    Raises `NotFoundExceptions` should no required delegetions be present in
    the trust data, or no image targets be found.
    """
    tuf_roles = ["root", "snapshot", "timestamp", "targets"]
    trust_data = {}
    key_store = KeyStore()

    # get all trust data and collect keys (from root and targets), as well as
    # hashes (from snapshot and timestamp)
    for role in tuf_roles:
        trust_data[role] = get_trust_data(host, image, TUFRole(role))
        key_store.update(trust_data[role])

    # if the 'targets.json' has delegation roles defined, get their trust data
    # as well
    if trust_data["targets"].has_delegations():
        for delegation in trust_data["targets"].get_delegations():
            trust_data[delegation] = get_delegation_trust_data(
                host, image, TUFRole(delegation)
            )

    # validate all trust data's signatures, expiry dates and hashes.
    # when delegations are added to the repository, but weren't yet used for signing, the
    # delegation files don't exist yet and are `None`. in this case they can't be
    # validated and must be skipped
    for role in trust_data:
        if trust_data[role] is not None:
            trust_data[role].validate(key_store)

    # validate needed delegations
    if req_delegations:
        if trust_data["targets"].has_delegations():
            delegations = trust_data["targets"].get_delegations()

            req_delegations_set = set(req_delegations)
            delegations_set = set(delegations)

            delegations_set.discard("targets/releases")

            # make an intersection between required delegations and actually
            # present ones
            if not req_delegations_set.issubset(delegations_set):
                missing = list(req_delegations_set - delegations_set)
                raise NotFoundException(
                    "could not find delegation roles {} in trust data.".format(
                        str(missing)
                    )
                )
        else:
            raise NotFoundException("could not find any delegations in trust data.")

    # if certain delegations are required, then only take the targets fields of the
    # required delegation JSON's. otherwise take the targets field of the targets JSON, as
    # long as no delegations are defined in the targets JSON. should there be delegations
    # defined in the targets JSON the targets field of the releases JSON will be used.
    # unfortunately there is a case, where delegations could have been added to a
    # repository, but no signatures were created using the delegations. in this special
    # case, the releases JSON doesn't exist yet and the targets JSON must be used instead
    if req_delegations:
        if not all(trust_data[target_role] for target_role in req_delegations):
            tuf_roles = [
                target_role
                for target_role in req_delegations
                if not trust_data[target_role]
            ]
            msg = f"no trust data for delegation roles {tuf_roles} for image {image}"
            raise NotFoundException(msg, {"tuf_roles": tuf_roles})

        image_targets = [
            trust_data[target_role].signed.get("targets", {})
            for target_role in req_delegations
        ]
    else:
        targets_key = (
            "targets/releases"
            if trust_data["targets"].has_delegations()
            and trust_data["targets/releases"]
            else "targets"
        )
        image_targets = [trust_data[targets_key].signed.get("targets", {})]

    if not any(image_targets):
        raise NotFoundException("could not find any image digests in trust data.")

    return image_targets