Ejemplo n.º 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,
                },
            )
Ejemplo n.º 2
0
    def validate_expiry(self):
        """
        Validate the expiry date of the trust data.

        Raise a `ValidationError` should the date be expired.
        """
        expire = parser.parse(self.signed.get("expires"))
        now = datetime.now(pytz.utc)

        if expire < now:
            msg = "Trust data {trust_data_kind} has expired."
            raise ValidationError(message=msg, trust_data_kind=self.kind)
Ejemplo n.º 3
0
    def _validate_schema(self, data: dict):
        """
        Validates the schema of the given trust `data`.

        Raises a `ValidationError` should the schema not conform.
        """
        with open(self.schema_path, "r") as schema_file:
            schema = json.load(schema_file)

        try:
            json_validate(instance=data,
                          schema=schema,
                          format_checker=JFormatChecker())
        except JValidationError:
            raise ValidationError("trust data has invalid format.",
                                  {"trust_data_type": self.kind})
Ejemplo n.º 4
0
    def _validate_expiry(self):
        """
        Validates the expiry date of the trust data.

        Raises a `ValidationError` should the date be expired.
        """
        expire = parser.parse(self.signed.get("expires"))
        now = datetime.now(pytz.utc)

        if expire < now:
            raise ValidationError(
                "trust data expired.",
                {
                    "expire": str(expire),
                    "trust_data_type": self.signed.get("_type")
                },
            )
Ejemplo n.º 5
0
    def validate_signature(self, keystore: KeyStore):
        """
        Validate the signature of the trust data, using keys from a
        `keystore`.

        Raise 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 as err:
                msg = "Failed to verify signature of trust data {trust_data_kind}."
                raise ValidationError(message=msg, trust_data_kind=self.kind) from err
Ejemplo n.º 6
0
    def validate_hash(self, keystore: KeyStore):
        """
        Validate the given hash from a `keystore` corresponds to the trust
        data's calculated hash.

        Raise 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:
            msg = "Failed to validate hash of trust data {trust_data_kind}."
            raise ValidationError(message=msg, trust_data_kind=self.kind)
Ejemplo n.º 7
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")
                    },
                )
Ejemplo n.º 8
0
 async def validate(self, image: Image, **kwargs):
     if not self.approve:
         msg = "Static deny."
         raise ValidationError(message=msg)
Ejemplo n.º 9
0
 def __get_cosign_validated_digests(self, image: str, key: str):
     """
     Get and process Cosign validation output for a given `image` and `key`
     and either return a list of valid digests or raise a suitable exception
     in case no valid signature is found or Cosign fails.
     """
     returncode, stdout, stderr = self.__invoke_cosign(image, key)
     logging.info(
         "COSIGN output for image: %s; RETURNCODE: %s; STDOUT: %s; STDERR: %s",
         image,
         returncode,
         stdout,
         stderr,
     )
     digests = []
     if returncode == 0:
         for sig in stdout.splitlines():
             try:
                 sig_data = json.loads(sig)
                 try:
                     digest = sig_data["critical"]["image"].get(
                         "docker-manifest-digest", "")
                     if re.match(r"sha256:[0-9A-Fa-f]{64}", digest) is None:
                         msg = "Digest '{digest}' does not match expected digest pattern."
                         raise InvalidFormatException(message=msg,
                                                      digest=digest)
                 except Exception as err:
                     msg = (
                         "Could not retrieve valid and unambiguous digest from data "
                         "received by Cosign: {err_type}: {err}")
                     raise UnexpectedCosignData(message=msg,
                                                err_type=type(err).__name__,
                                                err=str(err)) from err
                 # remove prefix 'sha256'
                 digests.append(digest.removeprefix("sha256:"))
             except json.JSONDecodeError:
                 logging.info("non-json signature data from Cosign: %s",
                              sig)
                 pass
     elif "Error: no matching signatures:\nfailed to verify signature\n" in stderr:
         msg = "Failed to verify signature of trust data."
         raise ValidationError(
             message=msg,
             trust_data_type="dev.cosignproject.cosign/signature",
             stderr=stderr,
         )
     elif "Error: no matching signatures:\n\nmain.go:" in stderr:
         msg = 'No trust data for image "{image}".'
         raise NotFoundException(
             message=msg,
             trust_data_type="dev.cosignproject.cosign/signature",
             stderr=stderr,
             image=str(image),
         )
     else:
         msg = 'Unexpected Cosign exception for image "{image}": {stderr}.'
         raise CosignError(
             message=msg,
             trust_data_type="dev.cosignproject.cosign/signature",
             stderr=stderr,
             image=str(image),
         )
     if not digests:
         msg = ("Could not extract any digest from data received by Cosign "
                "despite successful image verification.")
         raise UnexpectedCosignData(message=msg)
     return digests