Ejemplo n.º 1
0
    def _compute_kek(self, target_alg: '_EncAlg', ops: 'str') -> bytes:

        if self.key is None:
            #  try to derive from this recipients' recipient list
            if not len(self.recipients):
                raise CoseException(f"No key found to {ops} the CEK")
            else:
                r_types = CoseRecipient.verify_recipients(self.recipients)

                if ops == 'encrypt':

                    if DirectKeyAgreement in r_types:
                        self.key = self.recipients[0].compute_cek(target_alg)

                    elif KeyWrap in r_types or KeyAgreementWithKeyWrap in r_types:
                        key_bytes = os.urandom(self.get_attr(
                            headers.Algorithm))
                        for r in self.recipients:
                            r.payload = key_bytes
                        self.key = SymmetricKey(k=key_bytes)
                    else:
                        raise CoseException('Unsupported COSE recipient class')
                else:
                    if DirectKeyAgreement in r_types or KeyWrap in r_types or KeyAgreementWithKeyWrap in r_types:
                        self.key = self.recipients[0].decrypt(
                            self.get_attr(headers.Algorithm))
                    else:
                        raise CoseException('Unsupported COSE recipient class')

        if self.key is None:
            raise CoseException("No key found to decrypt the CEK")

        return self.key.k
Ejemplo n.º 2
0
    def encrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)

        if A128KW.identifier >= alg.identifier >= A256KW.identifier:
            if len(self.phdr):
                raise CoseException(
                    f"Protected header must be empty when using an AE algorithm: {alg}"
                )

            if alg is None:
                raise CoseException(
                    "The algorithm parameter should at least be included in the unprotected header"
                )

            self.key = SymmetricKey(k=self._compute_kek(target_alg,
                                                        ops='encrypt'),
                                    optional_params={
                                        KpAlg: alg,
                                        KpKeyOps: [WrapOp, EncryptOp]
                                    })
            self.key.verify(SymmetricKey, alg, [WrapOp, EncryptOp])

            return alg.key_wrap(self.key, self.payload)
        else:
            raise CoseIllegalAlgorithm(f"Algorithm {alg} for {self.__name__}")
Ejemplo n.º 3
0
    def encrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)

        if len(self.phdr):
            raise CoseException(
                f"Protected header must be empty when using an AE algorithm: {alg}"
            )

        if alg is None:
            raise CoseException(
                "The algorithm parameter should at least be included in the unprotected header"
            )

        elif alg in {A128KW, A192KW, A256KW}:
            key_ops = [WrapOp, EncryptOp]
            kek = SymmetricKey(k=self._compute_kek(target_alg, ops='encrypt'),
                               optional_params={
                                   KpAlg: alg,
                                   KpKeyOps: key_ops
                               })
            kek.verify(SymmetricKey, alg, [WrapOp, EncryptOp])
        elif alg in {RsaesOaepSha512, RsaesOaepSha256, RsaesOaepSha1}:
            kek = self.key
            kek.verify(RSAKey, alg, [WrapOp, EncryptOp])
        else:
            raise CoseIllegalAlgorithm(f"Algorithm {alg} for {self.__name__}")

        return alg.key_wrap(kek, self.payload)
Ejemplo n.º 4
0
    def _key_verification(self, alg: Type['CoseAlg'], ops: Type['KEYOPS']):

        if self.key is None:
            raise CoseException("Key cannot be None")

        if isinstance(self.key, EC2Key):
            self.key.verify(EC2Key, alg, [ops])
        elif isinstance(self.key, OKPKey):
            self.key.verify(OKPKey, alg, [ops])
        else:
            raise CoseException('Wrong key type')
Ejemplo n.º 5
0
 def signers(self, signers: List['Signer']):
     if isinstance(signers, list):
         for s in signers:
             s._parent = self
         self._signers = signers
     else:
         raise CoseException("Signers must be of type list")
Ejemplo n.º 6
0
    def get_attr(self,
                 attribute: Type[CoseHeaderAttribute],
                 default: Any = None) -> Optional[Any]:
        """
        Fetches an header attribute from the COSE header buckets.

        :param attribute: A header parameter to fetch from the buckets.
        :param default: A default return value in case the attribute was not found

        :raise CoseException: When the same attribute is found in both the protected and unprotected header.
        :returns: If found returns a header attribute else 'None' or the default value
        """

        p_attr = self._phdr.get(attribute, default)
        u_attr = self._uhdr.get(attribute, default)

        if p_attr is not None and u_attr is not None:
            raise CoseException(
                "MALFORMED: different values for the same header parameters in the header buckets"
            )

        if p_attr is not None:
            return p_attr
        else:
            return u_attr
Ejemplo n.º 7
0
    def compute_tag(self, *args, **kwargs) -> bytes:
        target_algorithm = self.get_attr(headers.Algorithm)

        r_types = CoseRecipient.verify_recipients(self.recipients)

        if DirectEncryption in r_types:
            # key should already be known
            payload = super(MacMessage, self).compute_tag()

        elif DirectKeyAgreement in r_types:
            self.key = self.recipients[0].compute_cek(target_algorithm,
                                                      "encrypt")
            payload = super(MacMessage, self).compute_tag()

        elif KeyWrap in r_types or KeyAgreementWithKeyWrap in r_types:
            key_bytes = os.urandom(
                self.get_attr(headers.Algorithm).get_key_length())

            for r in self.recipients:
                if r.payload == b'':
                    r.payload = key_bytes
                else:
                    key_bytes = r.payload
                r.encrypt(target_algorithm)
            self.key = SymmetricKey(k=key_bytes,
                                    alg=target_algorithm,
                                    key_ops=[MacCreateOp])
            payload = super(MacMessage, self).compute_tag()

        else:
            raise CoseException('Unsupported COSE recipient class')

        return payload
Ejemplo n.º 8
0
    def verify_recipients(cls, recipients: List['Recipient']) -> set:
        r_types = set()

        for r in recipients:
            r_types.add(r.__class__)

        if DirectEncryption in r_types and len(r_types) > 1:
            CoseException(
                'When using DIRECT_ENCRYPTION mode, it must be the only mode used on the message'
            )

        if DirectKeyAgreement in r_types and len(recipients) > 1:
            CoseException(
                'When using DIRECT_KEY_AGREEMENT, it must be only one recipient in the message.'
            )

        return r_types
Ejemplo n.º 9
0
    def encode(self, *args, **kwargs) -> list:
        alg = self.get_attr(headers.Algorithm)

        if alg is None:
            raise CoseMalformedMessage(
                "The algorithm parameter should be included in either the protected header or "
                "unprotected header")

        # static receiver key
        peer_key: 'EC2Key' = self.local_attrs.get(headers.StaticKey)

        if peer_key is None:
            raise CoseException(
                "Static receiver key cannot be None. Should be configured in 'local_attrs' of the msg."
            )

        # if ephemeral and not set, generate ephemeral key pair
        if self.key is None:
            if alg in {EcdhEsHKDF256, EcdhEsHKDF512}:
                self._setup_ephemeral_key(peer_key)
            else:
                # alg uses a static sender
                raise CoseException("Static sender key cannot be None")

        if len(self.recipients) > 1:
            raise CoseMalformedMessage(
                f'Recipient class DIRECT_KEY_AGREEMENT cannot carry more recipients'
            )

        # only the ephemeral sender key MUST be included in the header, for the static sender it is recommended by not
        # obligated
        if self.get_attr(headers.EphemeralKey) is None and alg in {
                EcdhEsHKDF512, EcdhEsHKDF256
        }:
            raise CoseMalformedMessage(
                f'Recipient class DIRECT_KEY_AGREEMENT must carry an ephemeral COSE key object'
            )

        recipient = [self.phdr_encoded, self.uhdr_encoded, b'']

        if len(self.recipients):
            recipient.append(
                [r.encode(*args, **kwargs) for r in self.recipients])

        return recipient
Ejemplo n.º 10
0
    def encode(self, *args, **kwargs) -> list:
        alg = self.get_attr(headers.Algorithm)

        if alg == Direct and len(self.phdr) != 0:
            raise CoseException("Protected header must be empty")

        if alg is None:
            raise CoseException(
                "Message must carry an algorithm parameter when using DIRECT_ENCRYPTION mode"
            )

        if len(self.recipients):
            raise CoseException(
                f"Recipient class DIRECT_ENCRYPTION cannot carry recipients.")

        recipient = [self.phdr_encoded, self.uhdr_encoded, b'']

        return recipient
Ejemplo n.º 11
0
    def sign(cls, key: 'OKP', data: bytes) -> bytes:
        if key.crv.fullname == 'ED25519':
            sk = Ed25519PrivateKey.from_private_bytes(key.d)
        elif key.crv.fullname == 'ED448':
            sk = Ed448PrivateKey.from_private_bytes(key.d)
        else:
            raise CoseException(f"Illegal curve for OKP singing: {key.crv}")

        return sk.sign(data)
Ejemplo n.º 12
0
 def key(self, key: Optional['CK']):
     if isinstance(key, SymmetricKey):
         if key.k == b'':
             raise CoseException("Key does not contain secret bytes")
         self._key = key
     elif isinstance(key, EC2Key):
         if key.d == b'' and key.x == b'' and key.y == b'':
             raise CoseException(
                 "Key does not contain private bytes or public bytes")
         self._key = key
     elif isinstance(key, OKPKey):
         if key.d == b'' and key.x == b'':
             raise CoseException(
                 "Key does not contain private bytes or public bytes")
         self._key = key
     elif key is None:
         self._key = key
     else:
         raise CoseException("Invalid COSE key")
Ejemplo n.º 13
0
    def verify_tag(self, *args, **kwargs) -> bool:
        """ Verifies the authentication tag of a received message. """

        alg = self.get_attr(headers.Algorithm)

        if self.key is None:
            raise CoseException("Key cannot be None")

        self.key.verify(SymmetricKey, alg, [MacVerifyOp])

        return alg.verify_tag(key=self.key, tag=self.auth_tag, data=self._mac_structure)
Ejemplo n.º 14
0
    def decrypt(self, recipient: 'Recipient', *args, **kwargs) -> bytes:
        target_algorithm = self.get_attr(headers.Algorithm)

        # check if recipient exists
        if not CoseRecipient.has_recipient(recipient, self.recipients):
            raise CoseException(f"Cannot find recipient: {recipient}")

        r_types = CoseRecipient.verify_recipients(self.recipients)

        if DirectEncryption in r_types:
            # key should already be known
            payload = super(EncMessage, self).decrypt()

        elif DirectKeyAgreement in r_types or KeyWrap in r_types or KeyAgreementWithKeyWrap in r_types:
            self.key = recipient.compute_cek(target_algorithm, "decrypt")
            payload = super(EncMessage, self).decrypt()
        else:
            raise CoseException('Unsupported COSE recipient class')

        return payload
Ejemplo n.º 15
0
    def _get_nonce(self):
        nonce = self.get_attr(headers.IV)

        if nonce is None and self.key.base_iv != b'':
            partial_iv = self.get_attr(headers.PartialIV)
            nonce = int.from_bytes(partial_iv, "big") ^ int.from_bytes(self.key.base_iv, "big")
            nonce = nonce.to_bytes((nonce.bit_length() + 7) // 8, byteorder="big")

        if nonce is None and self.key.base_iv == b'':
            raise CoseException('No IV found')

        return nonce
Ejemplo n.º 16
0
    def compute_tag(self, *args, **kwargs) -> bytes:
        """ Computes the authentication tag of a COSE_Mac or COSE_Mac0 message. """

        alg = self.get_attr(headers.Algorithm)

        if self.key is None:
            raise CoseException("Key cannot be None")

        self.key.verify(SymmetricKey, alg, [MacCreateOp])

        self.auth_tag = alg.compute_tag(key=self.key, data=self._mac_structure)
        return self.auth_tag
Ejemplo n.º 17
0
    def create_recipient(cls, recipient: list, context: str):
        p_alg = cls._parse_header(cbor2.loads(recipient[0])).get(
            headers.Algorithm) if recipient[0] != b'' else None
        u_alg = cls._parse_header(recipient[1]).get(headers.Algorithm)

        if p_alg is not None:
            return cls._RCPT_CLASSES[p_alg].from_cose_obj(recipient, context)
        elif u_alg is not None:
            return cls._RCPT_CLASSES[u_alg].from_cose_obj(recipient, context)
        else:
            raise CoseException(
                "No algorithm specified in recipient structure")
Ejemplo n.º 18
0
    def verify(cls, key: 'OKP', data: bytes, signature: bytes) -> bool:
        if key.crv.fullname == 'ED25519':
            vk = Ed25519PublicKey.from_public_bytes(key.x)
        elif key.crv.fullname == 'ED448':
            vk = Ed448PublicKey.from_public_bytes(key.x)
        else:
            raise CoseException(f"Illegal curve for OKP singing: {key.crv}")

        try:
            vk.verify(signature, data)
            return True
        except InvalidSignature:
            return False
Ejemplo n.º 19
0
    def _setup_ephemeral_key(self, peer_key, optional_params: dict = None):
        self.key = EC2Key.generate_key(peer_key.crv, optional_params)

        if self.get_attr(headers.EphemeralKey) is not None:
            # public key was already set in the header bucket but we just generated a new ephemeral key.
            raise CoseException(
                'Unrelated ephemeral public key found in COSE message header')
        else:
            # strip private bytes from key
            ephemeral_public_key = dict(self.key)
            del ephemeral_public_key[EC2KpD]

            # add to unprotected header
            self.uhdr_update({headers.EphemeralKey: ephemeral_public_key})
Ejemplo n.º 20
0
    def encrypt(self, target_alg) -> bytes:
        # static receiver key
        _ = target_alg
        peer_key: 'EC2Key' = self.local_attrs.get(headers.StaticKey)

        if peer_key is None:
            raise CoseException(
                "Static receiver key cannot be None. Should be configured in 'local_attrs' of the msg."
            )

        alg = self.get_attr(headers.Algorithm)

        if alg is None:
            raise CoseException(
                "The algorithm parameter should at least be included in the unprotected header"
            )

        # if ephemeral and not set, generate ephemeral key pair
        if self.key is None:
            if alg in {EcdhEsA128KW, EcdhEsA192KW, EcdhEsA256KW}:
                self._setup_ephemeral_key(peer_key)
            else:
                # alg uses a static sender
                raise CoseException("Static sender key cannot be None.")

        key_bytes = self._compute_kek(
            (self.get_attr(headers.Algorithm)).get_key_wrap_func(), peer_key,
            self.key, alg)
        wrap_func = alg.get_key_wrap_func()

        return wrap_func.key_wrap(
            SymmetricKey(k=key_bytes,
                         optional_params={
                             KpAlg: alg,
                             KpKeyOps: [DeriveKeyOp]
                         }), self.payload)
Ejemplo n.º 21
0
    def decrypt(self, *args, **kwargs) -> bytes:
        """
        Decrypts the payload.

        :raises CoseException: When the key is not of type 'SymmetricKey'.
        :returns: plaintext as bytes
        """

        alg = self.get_attr(headers.Algorithm)
        nonce = self._get_nonce()

        if self.key is None:
            raise CoseException("Key cannot be None")

        self.key.verify(SymmetricKey, alg, [DecryptOp])

        return alg.decrypt(key=self.key, ciphertext=self.payload, external_aad=self._enc_structure, nonce=nonce)
Ejemplo n.º 22
0
    def encrypt(self, *args, **kwargs) -> bytes:
        """
        Encrypts the payload.

        :raises CoseException: When the key is not of type 'SymmetricKey'.
        :returns: ciphertext as bytes
        """

        # first check if key is set (since a part of the nonce can be stored in the key)
        if self.key is None:
            raise CoseException("Key cannot be None")

        alg = self.get_attr(headers.Algorithm)
        nonce = self._get_nonce()

        self.key.verify(SymmetricKey, alg, [EncryptOp])

        return alg.encrypt(key=self.key, data=self.payload, external_aad=self._enc_structure, nonce=nonce)
Ejemplo n.º 23
0
    def decrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)

        key_ops = [DecryptOp, UnwrapOp]

        if alg in {A128KW, A192KW, A256KW}:
            kek = SymmetricKey(k=self._compute_kek(target_alg, 'decrypt'),
                               optional_params={
                                   KpAlg: alg,
                                   KpKeyOps: key_ops
                               })
            kek.verify(SymmetricKey, alg, [UnwrapOp, DecryptOp])
        elif alg in {RsaesOaepSha512, RsaesOaepSha256, RsaesOaepSha1}:
            kek = self.key
            kek.verify(RSAKey, alg, [UnwrapOp, DecryptOp])
        else:
            raise CoseException(
                f"Unsupported algorithm for key unwrapping: {alg}")

        return alg.key_unwrap(kek, self.payload)
Ejemplo n.º 24
0
    def verify(self, key_type: Type['CK'], algorithm: Type['CoseAlg'],
               key_ops: List[Type['KEYOPS']]):
        """ Verify attributes of the COSE_key object."""

        if not isinstance(self, key_type):
            raise CoseException("Wrong key type")

        if self.alg is not None and self.alg.identifier != algorithm.identifier:
            raise CoseIllegalAlgorithm(
                "Conflicting algorithms in key and COSE headers")

        if len(self.key_ops):
            match_key_ops = False

            for k in key_ops:
                if k in self.key_ops:
                    match_key_ops = True

            if not match_key_ops:
                raise CoseIllegalKeyOps(
                    f"Illegal key operations specified. Allowed: {key_ops}, found: {self.key_ops}"
                )
Ejemplo n.º 25
0
 def from_id(cls,
             attribute: Any,
             allow_unknown_attributes: bool = False) -> Any:
     if isinstance(attribute,
                   int) and attribute in cls.get_registered_classes():
         return cls.get_registered_classes()[attribute]
     elif isinstance(attribute,
                     str) and attribute in cls.get_registered_classes():
         return cls.get_registered_classes()[attribute.upper()]
     elif isinstance(attribute, list):
         translated_list = [cls.from_id(attr) for attr in attribute]
         return translated_list
     elif hasattr(
             attribute, 'identifier'
     ) and attribute.identifier in cls.get_registered_classes():
         return cls.get_registered_classes()[attribute.identifier]
     else:
         if allow_unknown_attributes:
             return attribute
         else:
             raise CoseException(
                 f"Unknown COSE header or key attribute with value: [{cls.__name__} - {attribute}]"
             )
Ejemplo n.º 26
0
    def compute_cek(self, target_alg: '_EncAlg', ops: str) -> 'SK':
        alg = self.get_attr(headers.Algorithm)

        if alg in {EcdhSsHKDF256, EcdhSsHKDF512, EcdhEsHKDF256, EcdhEsHKDF512}:
            if ops == "encrypt":
                peer_key = self.local_attrs.get(headers.StaticKey)
            else:
                if alg in {EcdhSsHKDF256, EcdhSsHKDF512}:
                    peer_key = self.get_attr(headers.StaticKey)
                else:
                    peer_key = self.get_attr(headers.EphemeralKey)
        else:
            raise CoseIllegalAlgorithm(
                f"Algorithm {alg} unsupported for {self.__name__}")

        if peer_key is None:
            raise CoseException("Unknown static receiver public key")

        peer_key.verify(EC2Key, alg, [DeriveKeyOp, DeriveBitsOp])
        self.key.verify(EC2Key, alg, [DeriveKeyOp, DeriveBitsOp])

        return SymmetricKey(k=self._compute_kek(target_alg, peer_key, self.key,
                                                alg),
                            optional_params={KpAlg: target_alg})