def verify(self, to_be_signed: bytes, signature: bytes, alg: Optional[CoseAlgorithms] = None, curve: Optional[EllipticCurveType] = None) -> bool: """ Verifies the digital signature over 'to_be_signed'. The parameter 'alg' and 'curve' parameters are optional in case they are already provided by one of the COSE key objects. :param to_be_signed: data that was signed :param signature: signature to verify :param alg: an optional algorithm parameter (specifies the exact algorithm used for the signature). :param curve: an optional curve """ self._check_key_conf(algorithm=alg, key_operation=KeyOps.VERIFY, curve=curve) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) p = Point(curve=alg_cfg.curve.curve, x=int(hexlify(self.x), 16), y=int(hexlify(self.y), 16)) vk = VerifyingKey.from_public_point(p, alg_cfg.curve, alg_cfg.hash, validate_point=True) return vk.verify(signature, to_be_signed)
def sign(self, to_be_signed: bytes, alg: Optional[CoseAlgorithms] = None, curve: EllipticCurveType = None) -> bytes: """ Computes a digital signature over 'to_be_signed'. The parameter 'alg' and 'curve' parameters are optional in case they are already provided by one of the COSE key objects. :param to_be_signed: data over which the signature is calculated :param alg: an optional algorithm parameter (specifies the exact algorithm used for the signature). :param curve: an optional curve """ self._check_key_conf(algorithm=alg, key_operation=KeyOps.SIGN, curve=curve) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) sk = SigningKey.from_secret_exponent(int(hexlify(self.d), 16), curve=alg_cfg.curve) return sk.sign_deterministic(to_be_signed, hashfunc=alg_cfg.hash)
def _prepare_cipher(self): alg_cfg = config(CoseAlgorithms(self.alg)) if alg_cfg.tag_length is not None: cipher: Union[AESGCM, AESCCM] = alg_cfg.primitive( self.k, tag_length=alg_cfg.tag_length) else: cipher = alg_cfg.primitive(self.k) return cipher
def key_unwrap(self, wrapped_key: bytes, alg: Optional[CoseAlgorithms] = None) -> bytes: self._check_key_conf(alg, KeyOps.UNWRAP) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) return alg_cfg.primitive.aes_key_unwrap(self.k, wrapped_key, default_backend())
def ecdh_key_derivation( self, public_key: 'EC2', context: CoseKDFContext, alg: Optional[CoseAlgorithms] = None, curve: Optional[EllipticCurveType] = None) -> Tuple[bytes, bytes]: """ Derives a CEK with ECDH + HKDF algorithm. The parameter 'alg' and 'curve' parameters are optional in case they are already provided by one of the COSE key objects. :param public_key: an EC2 key containing public key bytes. :param context: a CoseKDFContext for the HKDF algorithm. :param alg: an optional algorithm parameter (specifies the exact algorithm used for the key derivation). :param curve: an optional curve """ self._check_key_conf(alg, KeyOps.DERIVE_KEY, public_key, curve) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) try: curve = self.KEY_DERIVATION_CURVES[self.crv]() except KeyError: raise CoseIllegalCurve(curve) d = ec.derive_private_key(int(hexlify(self.d), 16), curve, default_backend()) p = ec.EllipticCurvePublicNumbers(int(hexlify(public_key.x), 16), int(hexlify(public_key.y), 16), curve) p = p.public_key(default_backend()) shared_key = d.exchange(ECDH(), p) derived_key = alg_cfg.kdf( algorithm=alg_cfg.hash(), length=int(context.supp_pub_info.key_data_length / 8), salt=None, info=context.encode(), backend=default_backend()).derive(shared_key) return shared_key, derived_key
def compute_tag(self, to_be_maced: bytes, alg: Optional[CoseAlgorithms] = None) -> bytes: """ Calculate the MAC over the payload """ self._check_key_conf(alg, KeyOps.MAC_CREATE) iv = unhexlify(b"".join([b"00"] * 16)) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) if self.alg in { CoseAlgorithms.AES_MAC_128_128, CoseAlgorithms.AES_MAC_128_64, CoseAlgorithms.AES_MAC_256_64, CoseAlgorithms.AES_MAC_256_128 }: encryptor = Cipher(alg_cfg.primitive(self.k), modes.CBC(iv), backend=default_backend()).encryptor() while len(to_be_maced) % 16 != 0: to_be_maced += unhexlify(b"00") ciphertext = encryptor.update(to_be_maced) + encryptor.finalize() if alg_cfg.tag_length is not None: # truncate the result to the first 64 bits ciphertext = ciphertext[:-8] digest = ciphertext[-8:] else: digest = ciphertext[-16:] else: h = alg_cfg.primitive(self.k, alg_cfg.hash(), backend=default_backend()) h.update(to_be_maced) digest = h.finalize() if CoseAlgorithms(self.alg) == CoseAlgorithms.HMAC_256_64: # truncate the result to the first 64 bits digest = digest[:8] return digest
def hmac_key_derivation(self, context: CoseKDFContext, alg: Optional[CoseAlgorithms] = None, salt: bytes = b'') -> bytes: self._check_key_conf(alg, KeyOps.DERIVE_KEY) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) derived_key = alg_cfg.kdf( algorithm=alg_cfg.hash(), length=int(context.supp_pub_info.key_data_length / 8), salt=salt, info=context.encode(), backend=default_backend()).derive(self.k) return derived_key
def key_wrap(self, plaintext_key: bytes, alg: Optional[CoseAlgorithms] = None) -> bytes: self._check_key_conf(alg, KeyOps.WRAP) try: alg_cfg = config(CoseAlgorithms(self.alg)) except KeyError as err: raise CoseInvalidAlgorithm(err) if self.alg in { CoseAlgorithms.A128KW, CoseAlgorithms.A192KW, CoseAlgorithms.A256KW }: return alg_cfg.primitive.aes_key_wrap(self.k, plaintext_key, default_backend()) elif self.alg == CoseAlgorithms.DIRECT: return b'' else: raise CoseInvalidAlgorithm( f"Key wrap requires one of the following algorithms: \ {(CoseAlgorithms.A256KW, CoseAlgorithms.A192KW, CoseAlgorithms.A128KW, CoseAlgorithms.DIRECT)}" )