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__}")
    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
Exemple #3
0
def test_symmetric_key_wrap(kid, alg, key_ops, base_iv, k, pl, algo, ct):
    key = SymmetricKey(kid=kid, alg=alg, key_ops=key_ops, base_iv=base_iv, k=k)

    assert ct == key.key_wrap(pl, algo)

    # switch to another key operation
    key.key_ops = KeyOps.UNWRAP
    assert pl == key.key_unwrap(ct, algo)
Exemple #4
0
def test_symmetric_key_derivation(kid, alg, key_ops, base_iv, k, salt, algo,
                                  ctx_alg, u, v, pub, priv, context, cek):
    key = SymmetricKey(kid=kid, alg=alg, key_ops=key_ops, base_iv=base_iv, k=k)

    ctx = CoseKDFContext(ctx_alg, u, v, pub, priv)
    assert ctx.encode() == context

    assert key.hmac_key_derivation(ctx, algo, salt) == cek
Exemple #5
0
def test_symmetric_mac(kid, alg, key_ops, base_iv, k, pl, algo, ct):
    key = SymmetricKey(kid=kid, alg=alg, key_ops=key_ops, base_iv=base_iv, k=k)

    assert ct == key.compute_tag(pl, algo)

    # switch to another key operation
    key.key_ops = KeyOps.MAC_VERIFY
    assert key.verify_tag(ct, pl, algo)
Exemple #6
0
def test_encrypt_hkdf_hmac_direct_decode(
        setup_encrypt_hkdf_hmac_direct_tests: tuple) -> None:
    _, test_input, test_output, test_intermediate, fail = setup_encrypt_hkdf_hmac_direct_tests

    # parse message and test for headers
    md: EncMessage = CoseMessage.decode(unhexlify(test_output))
    assert md.phdr == extract_phdr(test_input, 'enveloped')
    assert md.uhdr == extract_uhdr(test_input, 'enveloped', 0)

    # check for external data and verify internal _enc_structure
    md.external_aad = unhexlify(test_input['enveloped'].get('external', b''))
    assert md._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    recipient = test_input['enveloped']['recipients'][0]
    assert md.recipients[0].phdr == recipient.get('protected', {})

    # create HKDF contect
    v = PartyInfo(
        identity=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_V_IDENTITY),
        nonce=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_V_NONCE),
        other=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_V_OTHER))
    u = PartyInfo(
        identity=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_U_IDENTITY),
        nonce=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_U_NONCE),
        other=md.recipients[0].uhdr.get(CoseHeaderKeys.PARTY_U_OTHER))

    public_data = test_input['enveloped']['recipients'][0].get(
        'unsent', {}).get('pub_other')
    s = SuppPubInfo(
        len(test_intermediate['CEK_hex']) * 4, md.recipients[0].encode_phdr(),
        public_data.encode('utf-8')
        if public_data is not None else public_data)

    priv_data = test_input['enveloped']['recipients'][0].get('unsent', {}).get(
        'priv_other', b'')
    hkdf_context = CoseKDFContext(
        md.phdr[CoseHeaderKeys.ALG], u, v, s,
        priv_data.encode('utf-8') if priv_data != b'' else priv_data)

    assert hkdf_context.encode() == unhexlify(
        test_intermediate["recipients"][0]['Context_hex'])

    # set shared secret key
    shared_secret = SymmetricKey(
        k=CoseKey.base64decode(test_input['enveloped']['recipients'][0]['key'][
            SymmetricKey.SymPrm.K]))

    kek = md.recipients[0].derive_kek(
        shared_secret,
        alg=md.recipients[0].phdr[CoseHeaderKeys.ALG],
        context=hkdf_context,
        salt=md.recipients[0].uhdr.get(CoseHeaderKeys.SALT))

    assert kek == unhexlify(test_intermediate["CEK_hex"])

    cek = SymmetricKey(k=kek, alg=extract_alg(test_input['enveloped']))
    assert md.decrypt(key=cek, nonce=extract_nonce(
        test_input, 0)) == test_input['plaintext'].encode('utf-8')
Exemple #7
0
def test_symmetric_key_aeads(kid, alg, key_ops, base_iv, k, pl, aad, nonce,
                             algo, ct):
    key = SymmetricKey(kid=kid, alg=alg, key_ops=key_ops, base_iv=base_iv, k=k)

    assert ct == key.encrypt(pl, aad, nonce, algo)

    # switch to another key operation
    key.key_ops = KeyOps.DECRYPT
    assert pl == key.decrypt(ct, aad, nonce, algo)
    def decrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)

        kek = SymmetricKey(k=self._compute_kek(target_alg, 'decrypt'),
                           optional_params={
                               KpAlg: alg,
                               KpKeyOps: [DecryptOp, UnwrapOp]
                           })
        kek.verify(SymmetricKey, alg, [UnwrapOp, DecryptOp])

        return alg.key_unwrap(kek, self.payload)
Exemple #9
0
    def decrypt(self, key: SymmetricKey, alg: Optional['CoseAlgorithms'] = None) -> bytes:
        """ Key unwrapping. """

        if key is None:
            raise CoseIllegalKeyType("COSE Key cannot be None")

        return key.key_unwrap(self.payload, alg=alg)
Exemple #10
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
Exemple #11
0
    def encrypt(self,
                nonce: bytes,
                key: SymmetricKey,
                alg: Optional[CoseAlgorithms] = None) -> bytes:
        """
        Encrypts the payload.

        :param nonce: Nonce for decryption. Length tof the nonce depends on the AEAD. Nonce cannot be empty or None.
        :param key: A Symmetric COSE key object containing the symmetric key bytes and a optionally an AEAD algorithm.
        :param alg: If the 'alg' parameter is unset in the COSE key object, this parameter cannot be None.

        :raises ValueError: When the nonce is empty or None
        :raises CoseIllegalKeyType: When the key is not of type 'SymmetricKey'.

        :returns: ciphertext as bytes
        """

        if nonce == b"" or nonce is None:
            raise ValueError(f"{nonce} is not a valid nonce value")

        if not isinstance(key, SymmetricKey):
            raise CoseIllegalKeyType(
                "COSE key should be of type 'SymmetricKey', got {}".format(
                    type(key)))

        return key.encrypt(plaintext=self.payload,
                           aad=self._enc_structure,
                           nonce=nonce,
                           alg=alg)
Exemple #12
0
def create_cose_key(
        key_type: Type[CoseKey],
        input_data: dict,
        alg: Optional[CoseAlgorithms] = None,
        usage: Optional[KeyOps] = None) -> Union[EC2, SymmetricKey, OKP]:
    if key_type == EC2:
        key = EC2(
            kid=input_data.get(CoseKey.Common.KID),
            key_ops=usage,
            alg=alg,
            crv=input_data.get(EC2.EC2Prm.CRV),
            x=CoseKey.base64decode(input_data.get(EC2.EC2Prm.X)),
            y=CoseKey.base64decode(input_data.get(EC2.EC2Prm.Y)),
            d=CoseKey.base64decode(input_data.get(EC2.EC2Prm.D)),
        )
    elif key_type == SymmetricKey:
        key = SymmetricKey(kid=input_data.get(CoseKey.Common.KID),
                           alg=alg,
                           key_ops=usage,
                           k=CoseKey.base64decode(
                               input_data.get(SymmetricKey.SymPrm.K)))
    elif key_type == OKP:
        key = OKP(
            kid=input_data.get(CoseKey.Common.KID),
            alg=alg,
            key_ops=usage,
            crv=input_data.get(OKP.OKPPrm.CRV),
            x=unhexlify(input_data.get(OKP.OKPPrm.X)),
            d=unhexlify(input_data.get(OKP.OKPPrm.D)),
        )
    else:
        raise Exception

    return key
Exemple #13
0
 def compute_cek(self, target_alg: '_EncAlg', ops: str) -> Optional['SK']:
     if ops == "encrypt":
         if self.payload == b'':
             return None
         else:
             return SymmetricKey(k=self.payload,
                                 optional_params={
                                     KpAlg: target_alg,
                                     KpKeyOps: [EncryptOp]
                                 })
     else:
         return SymmetricKey(k=self.decrypt(target_alg),
                             optional_params={
                                 KpAlg: target_alg,
                                 KpKeyOps: [DecryptOp]
                             })
Exemple #14
0
def test_mac_direct_decoding(setup_mac_tests: tuple) -> None:
    _, test_input, test_output, test_intermediate, fail = setup_mac_tests

    if fail:
        skip("invalid test input")

    msg: MacMessage = CoseMessage.decode(unhexlify(test_output))

    assert msg.phdr == test_input['mac'].get('protected', {})
    assert msg.uhdr == test_input['mac'].get('unprotected', {})
    assert msg.payload == test_input['plaintext'].encode('utf-8')

    # set up potential external data
    msg.external_aad = unhexlify(test_input['mac'].get("external", b''))
    assert msg._mac_structure == unhexlify(test_intermediate['ToMac_hex'])

    alg = extract_alg(test_input['mac'])
    cek = create_cose_key(SymmetricKey, test_input['mac']["recipients"][0]["key"], usage=KeyOps.MAC_VERIFY, alg=alg)
    assert cek.k == unhexlify(test_intermediate['CEK_hex'])

    # verify recipients
    for r1, r2 in zip(msg.recipients, test_input['mac']['recipients']):
        assert r1.phdr == r2.get('protected', {})
        assert r1.uhdr == r2.get('unprotected', {})

    assert msg.verify_tag(cek)

    # re-encode and verify we are back where we started
    kek = SymmetricKey(key_ops=KeyOps.WRAP, alg=CoseAlgorithms.DIRECT.id)
    cek.key_ops = KeyOps.MAC_CREATE
    assert msg.encode(key=cek, mac_params=[RcptParams(key=kek)]) == unhexlify(test_output)
Exemple #15
0
    def verify_tag(self,
                   key: SymmetricKey,
                   alg: Optional[CoseAlgorithms] = None) -> bool:
        """ Verifies the authentication tag of a received message. """

        if not isinstance(key, SymmetricKey):
            raise CoseIllegalKeyType(
                "COSE key should be of type 'SymmetricKey', got {}".format(
                    type(key)))

        return key.verify_tag(self.auth_tag, self._mac_structure, alg)
Exemple #16
0
    def compute_tag(self,
                    key: SymmetricKey,
                    alg: Optional[CoseAlgorithms] = None) -> bytes:
        """ Computes the authentication tag of a COSE_Mac or COSE_Mac0 message. """

        if not isinstance(key, SymmetricKey):
            raise CoseIllegalKeyType(
                "COSE key should be of type 'SymmetricKey', got {}".format(
                    type(key)))

        self.auth_tag = key.compute_tag(self._mac_structure, alg)
        return self.auth_tag
Exemple #17
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)
Exemple #18
0
    def decrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)
        _ = target_alg

        if alg in {EcdhEsA256KW, EcdhEsA192KW, EcdhEsA128KW}:
            peer_key = self.get_attr(headers.EphemeralKey)
        elif alg in {EcdhSsA256KW, EcdhSsA192KW, EcdhSsA128KW}:
            peer_key = self.get_attr(headers.StaticKey)
        else:
            raise CoseIllegalAlgorithm(
                f"Algorithm {alg} unsupported for {self.__name__}")

        kek = SymmetricKey(k=self._compute_kek(alg.get_key_wrap_func(),
                                               peer_key, self.key, alg),
                           optional_params={
                               KpAlg: alg,
                               KpKeyOps: [UnwrapOp, DecryptOp]
                           })

        kek.verify(SymmetricKey, alg, [UnwrapOp, DecryptOp])

        return alg.get_key_wrap_func().key_unwrap(kek, self.payload)
Exemple #19
0
def test_encrypt_decoding(setup_encrypt_tests: tuple) -> None:
    _, test_input, test_output, test_intermediate, fail = setup_encrypt_tests

    if fail:
        skip("invalid test input")

    # parse initial message
    msg: EncMessage = CoseMessage.decode(unhexlify(test_output))

    # verify parsed protected header
    assert msg.phdr == extract_phdr(test_input, 'enveloped')
    assert msg.uhdr == extract_uhdr(test_input, 'enveloped')

    nonce = extract_nonce(
        test_input,
        0) if extract_nonce(test_input, 0) != b'' else extract_unsent_nonce(
            test_input, "enveloped")

    alg = extract_alg(test_input['enveloped'])
    cek = create_cose_key(SymmetricKey,
                          test_input['enveloped']["recipients"][0]["key"],
                          usage=KeyOps.DECRYPT,
                          alg=alg)
    assert cek.k == unhexlify(test_intermediate['CEK_hex'])

    # look for external data and verify internal enc_structure
    msg.external_aad = unhexlify(test_input['enveloped'].get('external', b''))
    assert msg._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    # verify recipients
    assert len(msg.recipients) == 1
    assert msg.recipients[0].phdr == test_input['enveloped']['recipients'][
        0].get('protected', {})
    assert msg.recipients[0].uhdr == test_input['enveloped']['recipients'][
        0].get('unprotected', {})

    # (1) verify decryption
    assert msg.decrypt(nonce=nonce,
                       key=cek) == test_input['plaintext'].encode('utf-8')

    # re-encode and verify we are back where we started
    kek = SymmetricKey(key_ops=KeyOps.WRAP, alg=CoseAlgorithms.DIRECT.id)
    assert msg.encode(encrypt=False,
                      nonce=nonce,
                      key=cek,
                      enc_params=[RcptParams(key=kek)
                                  ]) == unhexlify(test_output)
Exemple #20
0
def test_encrypt_x25519_wrap_decode(
        setup_encrypt_x25519_direct_tests: tuple) -> None:
    _, test_input, test_output, test_intermediate, fail = setup_encrypt_x25519_direct_tests
    # DECODING

    # parse message and test for headers
    md: EncMessage = CoseMessage.decode(unhexlify(test_output))
    assert md.phdr == extract_phdr(test_input, 'enveloped')
    assert md.uhdr == extract_uhdr(test_input, 'enveloped', 1)

    # check for external data and verify internal _enc_structure
    md.external_aad = unhexlify(test_input['enveloped'].get('external', b''))
    assert md._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    recipient = test_input['enveloped']['recipients'][0]
    assert md.recipients[0].phdr == recipient.get('protected', {})
    # do not verify unprotected header since it contains the ephemeral public key of the sender
    # assert m.recipients[0].uhdr == rcpt.get('unprotected', {})

    rcvr_skey, sender_key = setup_okp_receiver_keys(
        recipient, md.recipients[0].uhdr.get(CoseHeaderKeys.EPHEMERAL_KEY))

    # create context KDF
    u = PartyInfo(nonce=unhexlify(test_input['rng_stream'][0])
                  ) if "sender_key" in recipient else PartyInfo()
    s = SuppPubInfo(
        len(test_intermediate['CEK_hex']) * 4, md.recipients[0].encode_phdr())
    kdf_ctx = CoseKDFContext(md.phdr[CoseHeaderKeys.ALG], u, PartyInfo(), s)
    assert kdf_ctx.encode() == unhexlify(
        test_intermediate['recipients'][0]['Context_hex'])

    secret, kek_bytes = CoseRecipient.derive_kek(rcvr_skey,
                                                 sender_key,
                                                 context=kdf_ctx,
                                                 expose_secret=True)

    assert secret == unhexlify(
        test_intermediate['recipients'][0]['Secret_hex'])
    assert kek_bytes == unhexlify(test_intermediate['CEK_hex'])

    alg = extract_alg(test_input['enveloped'])
    cek = SymmetricKey(k=kek_bytes)
    nonce = extract_nonce(test_input, 1)
    assert md.decrypt(nonce=nonce, alg=alg,
                      key=cek) == test_input['plaintext'].encode('utf-8')
Exemple #21
0
    def _(cls,
          private_key: SymmetricKey,
          public_key=None,
          alg: Optional['CoseAlgorithms'] = None,
          context: 'CoseKDFContext' = None,
          curve=None,
          salt: bytes = b'',
          expose_secret: bool = False):

        _ = public_key
        _ = curve

        kek = private_key.hmac_key_derivation(context, alg, salt)

        if expose_secret:
            return private_key.private_bytes, kek
        else:
            return kek
Exemple #22
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)
Exemple #23
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})
Exemple #24
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)
class KeyWrap(CoseRecipient):
    @classmethod
    def from_cose_obj(cls, cose_obj: list, *args, **kwargs) -> 'KeyWrap':
        msg = super().from_cose_obj(cose_obj)
        msg.context = kwargs.get('context')

        # only AE algorithms supported thus the protected header must be empty
        alg = msg.get_attr(headers.Algorithm)
        if alg in {A128KW, A192KW, A256KW} and len(msg.phdr):
            raise CoseMalformedMessage(
                f"Recipient class KEY_WRAP with alg {alg} must have a zero-length protected header"
            )

        if msg.payload == b'':
            raise CoseMalformedMessage(
                f'Recipient class KEY_WRAP must carry the encrypted CEK in its payload'
            )

        msg.recipients = [
            CoseRecipient.create_recipient(r, context='Rec_Recipient')
            for r in msg.recipients
        ]

        return msg

    @property
    def context(self):
        return self._context

    @context.setter
    def context(self, context: str):
        self._context = context

    def encode(self, *args, **kwargs) -> list:

        recipient = [
            self.phdr_encoded, self.uhdr_encoded,
            self.encrypt(kwargs.get('target_alg'))
        ]

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

        return recipient

    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

    def compute_cek(self, target_alg: '_EncAlg', ops: str) -> Optional['SK']:
        if ops == "encrypt":
            if self.payload == b'':
                return None
            else:
                return SymmetricKey(k=self.payload,
                                    optional_params={
                                        KpAlg: target_alg,
                                        KpKeyOps: [EncryptOp]
                                    })
        else:
            return SymmetricKey(k=self.decrypt(target_alg),
                                optional_params={
                                    KpAlg: target_alg,
                                    KpKeyOps: [DecryptOp]
                                })

    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__}")

    def decrypt(self, target_alg: '_EncAlg') -> bytes:
        alg = self.get_attr(headers.Algorithm)

        kek = SymmetricKey(k=self._compute_kek(target_alg, 'decrypt'),
                           optional_params={
                               KpAlg: alg,
                               KpKeyOps: [DecryptOp, UnwrapOp]
                           })
        kek.verify(SymmetricKey, alg, [UnwrapOp, DecryptOp])

        return alg.key_unwrap(kek, self.payload)

    def __repr__(self) -> str:
        phdr, uhdr = self._hdr_repr()

        return f'<COSE_Recipient: [{phdr}, {uhdr}, {utils.truncate(self._payload)}, {str(self.recipients)}]>'
Exemple #26
0
def test_symmetric_key_generation():
    key = SymmetricKey.generate_key(CoseAlgorithms.A128GCM, KeyOps.ENCRYPT, 16)
    assert isinstance(key, SymmetricKey)
Exemple #27
0
def test_encrypt_ecdh_direct_decode_encode(
        setup_encrypt_ecdh_direct_tests: tuple) -> None:
    title, test_input, test_output, test_intermediate, fail = setup_encrypt_ecdh_direct_tests

    # DECODING

    # parse message and test for headers
    md: EncMessage = CoseMessage.decode(unhexlify(test_output))
    assert md.phdr == extract_phdr(test_input, 'enveloped')
    assert md.uhdr == extract_uhdr(test_input, 'enveloped', 1)

    # check for external data and verify internal _enc_structure
    md.external_aad = unhexlify(test_input['enveloped'].get('external', b''))
    assert md._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    # verify the receiver and set up the keying material
    recipient = test_input['enveloped']['recipients'][0]

    assert md.recipients[0].phdr == recipient.get('protected', {})
    # do not verify unprotected header since it contains the ephemeral public key of the sender
    # assert m.recipients[0].uhdr == rcpt.get('unprotected', {})

    rcvr_skey, sender_key = setup_ec_receiver_keys(
        recipient, md.recipients[0].uhdr.get(CoseHeaderKeys.EPHEMERAL_KEY))

    # create context KDF
    v = PartyInfo()
    u = PartyInfo(nonce=unhexlify(test_input['rng_stream'][0])
                  ) if "sender_key" in recipient else PartyInfo()
    s = SuppPubInfo(
        len(test_intermediate['CEK_hex']) * 4, md.recipients[0].encode_phdr())
    kdf_ctx = CoseKDFContext(md.phdr[CoseHeaderKeys.ALG], u, v, s)
    assert kdf_ctx.encode() == unhexlify(
        test_intermediate['recipients'][0]['Context_hex'])

    secret, kek_bytes = CoseRecipient.derive_kek(rcvr_skey,
                                                 sender_key,
                                                 context=kdf_ctx,
                                                 expose_secret=True)

    assert secret == unhexlify(
        test_intermediate['recipients'][0]['Secret_hex'])
    assert kek_bytes == unhexlify(test_intermediate['CEK_hex'])

    alg = extract_alg(test_input['enveloped'])
    cek = SymmetricKey(k=kek_bytes)
    nonce = extract_nonce(test_input, 1)
    assert md.decrypt(nonce=nonce, alg=alg,
                      key=cek) == test_input['plaintext'].encode('utf-8')

    # ENCODING

    me = EncMessage(phdr=test_input['enveloped'].get("protected", {}),
                    uhdr=test_input['enveloped'].get("unprotected", {}),
                    payload=test_input['plaintext'].encode('utf-8'))

    if 'rng_stream' in test_input:
        me.uhdr_update(
            {CoseHeaderKeys.IV: unhexlify(test_input['rng_stream'][1])})

    # Set up recipients and keys
    recipient = test_input['enveloped']['recipients'][0]

    if 'sender_key' in recipient:
        r1 = CoseRecipient(phdr=recipient.get('protected', {}))
        r1.uhdr_update(
            {CoseHeaderKeys.STATIC_KEY: sender_key.encode('crv', 'x', 'y')})
        r1.uhdr_update(recipient.get('unprotected', {}))
        r1.uhdr_update({
            CoseHeaderKeys.PARTY_U_NONCE:
            unhexlify(test_input['rng_stream'][0])
        })
    else:
        r1 = CoseRecipient(phdr=recipient.get('protected', {}))
        r1.uhdr_update(
            {CoseHeaderKeys.EPHEMERAL_KEY: sender_key.encode('crv', 'x', 'y')})
        r1.uhdr_update(recipient.get('unprotected', {}))

    # append the first and only recipient
    me.recipients.append(r1)

    # set up cek
    cek = SymmetricKey(k=kek_bytes, alg=alg)
    kek = SymmetricKey(k=kek_bytes, alg=CoseAlgorithms.DIRECT.id)

    # without sorting probably does not match because the order of the recipient elements is not the same
    assert sorted(
        me.encode(key=cek, nonce=nonce,
                  enc_params=[RcptParams(key=kek)
                              ])) == sorted(unhexlify(test_output))
Exemple #28
0
def test_encrypt_ecdh_wrap_decode(setup_encrypt_ecdh_wrap_tests: tuple):
    _, test_input, test_output, test_intermediate, fail = setup_encrypt_ecdh_wrap_tests
    # DECODING

    # parse message and test for headers
    md: EncMessage = CoseMessage.decode(unhexlify(test_output))
    assert md.phdr == extract_phdr(test_input, 'enveloped')
    assert md.uhdr == extract_uhdr(test_input, 'enveloped', 1)

    # check for external data and verify internal _enc_structure
    md.external_aad = unhexlify(test_input['enveloped'].get('external', b''))
    assert md._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    recipient = test_input['enveloped']['recipients'][0]
    assert md.recipients[0].phdr == recipient.get('protected', {})
    # do not verify unprotected header since it contains the ephemeral public key of the sender
    # assert m.recipients[0].uhdr == rcpt.get('unprotected', {})

    rcvr_skey, sender_key = setup_ec_receiver_keys(
        recipient, md.recipients[0].uhdr.get(CoseHeaderKeys.EPHEMERAL_KEY))

    # create context KDF
    s = SuppPubInfo(
        len(test_intermediate['recipients'][0]['KEK_hex']) * 4,
        md.recipients[0].encode_phdr())

    if md.recipients[0].phdr[CoseHeaderKeys.ALG] in {
            CoseAlgorithms.ECDH_ES_A192KW, CoseAlgorithms.ECDH_SS_A192KW
    }:
        kdf_ctx = CoseKDFContext(CoseAlgorithms.A192KW, PartyInfo(),
                                 PartyInfo(), s)
    elif md.recipients[0].phdr[CoseHeaderKeys.ALG] in {
            CoseAlgorithms.ECDH_ES_A128KW, CoseAlgorithms.ECDH_SS_A128KW
    }:
        kdf_ctx = CoseKDFContext(CoseAlgorithms.A128KW, PartyInfo(),
                                 PartyInfo(), s)
    elif md.recipients[0].phdr[CoseHeaderKeys.ALG] in {
            CoseAlgorithms.ECDH_ES_A256KW, CoseAlgorithms.ECDH_SS_A256KW
    }:
        kdf_ctx = CoseKDFContext(CoseAlgorithms.A256KW, PartyInfo(),
                                 PartyInfo(), s)
    else:
        raise ValueError("Missed an algorithm?")

    assert kdf_ctx.encode() == unhexlify(
        test_intermediate['recipients'][0]['Context_hex'])

    secret, kek = CoseRecipient.derive_kek(rcvr_skey,
                                           sender_key,
                                           context=kdf_ctx,
                                           expose_secret=True)

    assert secret == unhexlify(
        test_intermediate['recipients'][0]['Secret_hex'])
    assert kek == unhexlify(test_intermediate['recipients'][0]['KEK_hex'])

    r1 = md.recipients[0]
    cek = r1.decrypt(key=SymmetricKey(k=kek, alg=r1.phdr[CoseHeaderKeys.ALG]))

    assert cek == unhexlify(test_intermediate['CEK_hex'])

    cek = SymmetricKey(k=cek, alg=extract_alg(test_input["enveloped"]))
    pld = md.decrypt(key=cek, nonce=extract_nonce(test_input, 1))

    assert pld == test_input['plaintext'].encode('utf-8')
Exemple #29
0
    msg.external_aad = unhexlify(test_input['encrypted'].get('external', b''))
    assert msg._enc_structure == unhexlify(test_intermediate['AAD_hex'])

    # verify decryption
    assert msg.decrypt(nonce=nonce, key=key) == test_input['plaintext'].encode('utf-8')

    # re-encode and verify we are back where we started
    assert msg.encode(encrypt=False, key=key, nonce=nonce) == unhexlify(test_output)


@mark.parametrize("phdr, uhdr, payload, key",
                  [
                      ({CoseHeaderKeys.ALG: CoseAlgorithms.A128GCM},
                       {CoseHeaderKeys.IV: unhexlify(b'89F52F65A1C580933B5261A72F')},
                       b'',
                       SymmetricKey(kid=b'you_know', k=os.urandom(16), alg=CoseAlgorithms.A128GCM)),
                      ({CoseHeaderKeys.ALG: CoseAlgorithms.A192GCM},
                       {CoseHeaderKeys.IV: unhexlify(b'89F52F65A1C580933B5261A72F')},
                       os.urandom(50),
                       SymmetricKey(kid=b'you_know', k=os.urandom(16), alg=CoseAlgorithms.A192GCM)),
                      ({CoseHeaderKeys.ALG: CoseAlgorithms.A256GCM},
                       {CoseHeaderKeys.IV: unhexlify(b'89F52F65A1C580933B5261A72F')},
                       os.urandom(100),
                       SymmetricKey(kid=b'you_know', k=os.urandom(16), alg=CoseAlgorithms.A256GCM))
                  ], ids=['test_encode_decode_1', 'test_encode_decode_2', 'test_encode_decode_3'])
def test_encode_decode_encrypt0(phdr, uhdr, payload, key):
    # create and encode a message
    original: Enc0Message = Enc0Message(phdr, uhdr, payload)
    encoded = original.encode(key=key, nonce=original.uhdr[CoseHeaderKeys.IV])

    # decode the message