Ejemplo n.º 1
0
    def verify(
        self,
        key: Mapping[str, Any],
        signed_serializer: Optional[SignedSerializer] = None,
    ) -> bool:
        """Verifies 'signatures' over 'signed' that match the passed key by id.

        Arguments:
            key: A securesystemslib-style public key object.
            signed_serializer: A SignedSerializer subclass instance that
                implements the desired canonicalization format. Per default a
                CanonicalJSONSerializer is used.

        Raises:
            # TODO: Revise exception taxonomy
            tuf.exceptions.Error: None or multiple signatures found for key.
            securesystemslib.exceptions.FormatError: Key argument is malformed.
            tuf.api.serialization.SerializationError:
                'signed' cannot be serialized.
            securesystemslib.exceptions.CryptoError, \
                    securesystemslib.exceptions.UnsupportedAlgorithmError:
                Signing errors.

        Returns:
            A boolean indicating if the signature is valid for the passed key.

        """
        signatures_for_keyid = list(
            filter(lambda sig: sig.keyid == key["keyid"], self.signatures)
        )

        if not signatures_for_keyid:
            raise exceptions.Error(f"no signature for key {key['keyid']}.")

        if len(signatures_for_keyid) > 1:
            raise exceptions.Error(
                f"{len(signatures_for_keyid)} signatures for key "
                f"{key['keyid']}, not sure which one to verify."
            )

        if signed_serializer is None:
            # Use local scope import to avoid circular import errors
            # pylint: disable=import-outside-toplevel
            from tuf.api.serialization.json import CanonicalJSONSerializer

            signed_serializer = CanonicalJSONSerializer()

        return verify_signature(
            key,
            signatures_for_keyid[0].to_dict(),
            signed_serializer.serialize(self.signed),
        )
Ejemplo n.º 2
0
    def verify_signature(
        self,
        metadata: Metadata,
        signed_serializer: Optional[SignedSerializer] = None,
    ) -> None:
        """Verifies that the 'metadata.signatures' contains a signature made
        with this key, correctly signing 'metadata.signed'.

        Arguments:
            metadata: Metadata to verify
            signed_serializer: Optional; SignedSerializer to serialize
                'metadata.signed' with. Default is CanonicalJSONSerializer.

        Raises:
            UnsignedMetadataError: The signature could not be verified for a
                variety of possible reasons: see error message.
        """
        try:
            signature = metadata.signatures[self.keyid]
        except KeyError:
            raise exceptions.UnsignedMetadataError(
                f"no signature for key {self.keyid} found in metadata",
                metadata.signed,
            ) from None

        if signed_serializer is None:
            # pylint: disable=import-outside-toplevel
            from tuf.api.serialization.json import CanonicalJSONSerializer

            signed_serializer = CanonicalJSONSerializer()

        try:
            if not sslib_keys.verify_signature(
                    self.to_securesystemslib_key(),
                    signature.to_dict(),
                    signed_serializer.serialize(metadata.signed),
            ):
                raise exceptions.UnsignedMetadataError(
                    f"Failed to verify {self.keyid} signature",
                    metadata.signed,
                )
        except (
                sslib_exceptions.CryptoError,
                sslib_exceptions.FormatError,
                sslib_exceptions.UnsupportedAlgorithmError,
        ) as e:
            raise exceptions.UnsignedMetadataError(
                f"Failed to verify {self.keyid} signature",
                metadata.signed,
            ) from e
Ejemplo n.º 3
0
    def test_sign_verify(self) -> None:
        root_path = os.path.join(self.repo_dir, "metadata", "root.json")
        root = Metadata[Root].from_file(root_path).signed

        # Locate the public keys we need from root
        targets_keyid = next(iter(root.roles[Targets.type].keyids))
        targets_key = root.keys[targets_keyid]
        snapshot_keyid = next(iter(root.roles[Snapshot.type].keyids))
        snapshot_key = root.keys[snapshot_keyid]
        timestamp_keyid = next(iter(root.roles[Timestamp.type].keyids))
        timestamp_key = root.keys[timestamp_keyid]

        # Load sample metadata (targets) and assert ...
        path = os.path.join(self.repo_dir, "metadata", "targets.json")
        md_obj = Metadata.from_file(path)

        # ... it has a single existing signature,
        self.assertEqual(len(md_obj.signatures), 1)
        # ... which is valid for the correct key.
        targets_key.verify_signature(md_obj)
        with self.assertRaises(exceptions.UnsignedMetadataError):
            snapshot_key.verify_signature(md_obj)

        # Test verifying with explicitly set serializer
        targets_key.verify_signature(md_obj, CanonicalJSONSerializer())
        with self.assertRaises(exceptions.UnsignedMetadataError):
            targets_key.verify_signature(
                md_obj, JSONSerializer())  # type: ignore[arg-type]

        sslib_signer = SSlibSigner(self.keystore[Snapshot.type])
        # Append a new signature with the unrelated key and assert that ...
        sig = md_obj.sign(sslib_signer, append=True)
        # ... there are now two signatures, and
        self.assertEqual(len(md_obj.signatures), 2)
        # ... both are valid for the corresponding keys.
        targets_key.verify_signature(md_obj)
        snapshot_key.verify_signature(md_obj)
        # ... the returned (appended) signature is for snapshot key
        self.assertEqual(sig.keyid, snapshot_keyid)

        sslib_signer = SSlibSigner(self.keystore[Timestamp.type])
        # Create and assign (don't append) a new signature and assert that ...
        md_obj.sign(sslib_signer, append=False)
        # ... there now is only one signature,
        self.assertEqual(len(md_obj.signatures), 1)
        # ... valid for that key.
        timestamp_key.verify_signature(md_obj)
        with self.assertRaises(exceptions.UnsignedMetadataError):
            targets_key.verify_signature(md_obj)
Ejemplo n.º 4
0
    def sign(
        self,
        signer: Signer,
        append: bool = False,
        signed_serializer: Optional[SignedSerializer] = None,
    ) -> Dict[str, Any]:
        """Creates signature over 'signed' and assigns it to 'signatures'.

        Arguments:
            signer: An object implementing the securesystemslib.signer.Signer
                interface.
            append: A boolean indicating if the signature should be appended to
                the list of signatures or replace any existing signatures. The
                default behavior is to replace signatures.
            signed_serializer: A SignedSerializer subclass instance that
                implements the desired canonicalization format. Per default a
                CanonicalJSONSerializer is used.

        Raises:
            tuf.api.serialization.SerializationError:
                'signed' cannot be serialized.
            securesystemslib.exceptions.CryptoError, \
                    securesystemslib.exceptions.UnsupportedAlgorithmError:
                Signing errors.

        Returns:
            A securesystemslib-style signature object.

        """
        if signed_serializer is None:
            # Use local scope import to avoid circular import errors
            # pylint: disable=import-outside-toplevel
            from tuf.api.serialization.json import CanonicalJSONSerializer

            signed_serializer = CanonicalJSONSerializer()

        signature = signer.sign(signed_serializer.serialize(self.signed))

        if append:
            self.signatures.append(signature)
        else:
            self.signatures = [signature]

        return signature
Ejemplo n.º 5
0
    def sign(
        self,
        signer: Signer,
        append: bool = False,
        signed_serializer: Optional[SignedSerializer] = None,
    ) -> Signature:
        """Creates signature over 'signed' and assigns it to 'signatures'.

        Arguments:
            signer: A securesystemslib.signer.Signer implementation.
            append: A boolean indicating if the signature should be appended to
                the list of signatures or replace any existing signatures. The
                default behavior is to replace signatures.
            signed_serializer: A SignedSerializer that implements the desired
                serialization format. Default is CanonicalJSONSerializer.

        Raises:
            tuf.api.serialization.SerializationError:
                'signed' cannot be serialized.
            securesystemslib.exceptions.CryptoError, \
                    securesystemslib.exceptions.UnsupportedAlgorithmError:
                Signing errors.

        Returns:
            Securesystemslib Signature object that was added into signatures.
        """

        if signed_serializer is None:
            # Use local scope import to avoid circular import errors
            # pylint: disable=import-outside-toplevel
            from tuf.api.serialization.json import CanonicalJSONSerializer

            signed_serializer = CanonicalJSONSerializer()

        signature = signer.sign(signed_serializer.serialize(self.signed))

        if not append:
            self.signatures.clear()

        self.signatures[signature.keyid] = signature

        return signature
Ejemplo n.º 6
0
    def test_sign_verify(self):
        root_path = os.path.join(self.repo_dir, 'metadata', 'root.json')
        root:Root = Metadata.from_file(root_path).signed

        # Locate the public keys we need from root
        targets_keyid = next(iter(root.roles["targets"].keyids))
        targets_key = root.keys[targets_keyid]
        snapshot_keyid = next(iter(root.roles["snapshot"].keyids))
        snapshot_key = root.keys[snapshot_keyid]
        timestamp_keyid = next(iter(root.roles["timestamp"].keyids))
        timestamp_key = root.keys[timestamp_keyid]

        # Load sample metadata (targets) and assert ...
        path = os.path.join(self.repo_dir, 'metadata', 'targets.json')
        metadata_obj = Metadata.from_file(path)

        # ... it has a single existing signature,
        self.assertEqual(len(metadata_obj.signatures), 1)
        # ... which is valid for the correct key.
        targets_key.verify_signature(metadata_obj)
        with self.assertRaises(exceptions.UnsignedMetadataError):
            snapshot_key.verify_signature(metadata_obj)

        # Test verifying with explicitly set serializer
        targets_key.verify_signature(metadata_obj, CanonicalJSONSerializer())
        with self.assertRaises(exceptions.UnsignedMetadataError):
            targets_key.verify_signature(metadata_obj, JSONSerializer())

        sslib_signer = SSlibSigner(self.keystore['snapshot'])
        # Append a new signature with the unrelated key and assert that ...
        sig = metadata_obj.sign(sslib_signer, append=True)
        # ... there are now two signatures, and
        self.assertEqual(len(metadata_obj.signatures), 2)
        # ... both are valid for the corresponding keys.
        targets_key.verify_signature(metadata_obj)
        snapshot_key.verify_signature(metadata_obj)
        # ... the returned (appended) signature is for snapshot key
        self.assertEqual(sig.keyid, snapshot_keyid)

        sslib_signer = SSlibSigner(self.keystore['timestamp'])
        # Create and assign (don't append) a new signature and assert that ...
        metadata_obj.sign(sslib_signer, append=False)
        # ... there now is only one signature,
        self.assertEqual(len(metadata_obj.signatures), 1)
        # ... valid for that key.
        timestamp_key.verify_signature(metadata_obj)
        with self.assertRaises(exceptions.UnsignedMetadataError):
            targets_key.verify_signature(metadata_obj)

        # Test failure on unknown scheme (securesystemslib UnsupportedAlgorithmError)
        scheme = timestamp_key.scheme
        timestamp_key.scheme = "foo"
        with self.assertRaises(exceptions.UnsignedMetadataError):
            timestamp_key.verify_signature(metadata_obj)
        timestamp_key.scheme = scheme

        # Test failure on broken public key data (securesystemslib CryptoError)
        public = timestamp_key.keyval["public"]
        timestamp_key.keyval["public"] = "ffff"
        with self.assertRaises(exceptions.UnsignedMetadataError):
            timestamp_key.verify_signature(metadata_obj)
        timestamp_key.keyval["public"] = public

        # Test failure with invalid signature (securesystemslib FormatError)
        sig = metadata_obj.signatures[timestamp_keyid]
        correct_sig = sig.signature
        sig.signature = "foo"
        with self.assertRaises(exceptions.UnsignedMetadataError):
            timestamp_key.verify_signature(metadata_obj)

        # Test failure with valid but incorrect signature
        sig.signature = "ff"*64
        with self.assertRaises(exceptions.UnsignedMetadataError):
            timestamp_key.verify_signature(metadata_obj)
        sig.signature = correct_sig