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)
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)
def __init__(self, crv: Union[Type['CoseCurve'], str, int], x: bytes = b'', d: bytes = b'', optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): """ Create an COSE OKP key. :param crv: An OKP elliptic curve. :param x: Public value of the OKP key. :param d: Private value of the OKP key. :param optional_params: A dictionary with optional key parameters. :param allow_unknown_key_attrs: Allow unknown key attributes (not registered at the IANA registry) """ transformed_dict = {KpKty: KtyOKP} if optional_params is None: optional_params = {} for _key_attribute, _value in optional_params.items(): try: # translate the key_attribute kp = OKPKeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtyOKP: raise CoseIllegalKeyType( f"Illegal key type in OKP COSE Key: {transformed_dict.get(KpKty)}" ) super(OKPKey, self).__init__(transformed_dict) if len(x) == 0 and len(d) == 0: raise CoseInvalidKey( "Either the public values or the private value must be specified" ) if crv is not None: self.crv = crv else: raise CoseInvalidKey("COSE curve cannot be None") if x != b'': self.x = x if d != b'': self.d = d
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)
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
def __init__(self, crv: Union[Type['CoseCurve'], str, int], x: bytes = b'', y: bytes = b'', d: bytes = b'', optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): transformed_dict = {} if len(x) == 0 and len(y) == 0 and len(d) == 0: raise CoseInvalidKey( "Either the public values or the private value must be specified" ) if (len(x) == 0 and len(y) != 0) or (len(x) != 0 and len(y) == 0): raise CoseInvalidKey("Missing public coordinate X/Y") new_dict = dict({KpKty: KtyEC2, EC2KpCurve: crv}) if len(x) != 0 and len(y) != 0: new_dict.update({EC2KpX: x, EC2KpY: y}) if len(d) != 0: new_dict.update({EC2KpD: d}) if optional_params is not None: new_dict.update(optional_params) for _key_attribute, _value in new_dict.items(): try: # translate the key_attribute kp = EC2KeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtyEC2: raise CoseIllegalKeyType( f"Illegal key type in EC2 COSE Key: {transformed_dict.get(KpKty)}" ) super(EC2Key, self).__init__(transformed_dict)
def verify_signature(self, public_key: Union[EC2, OKP], alg: Optional['CoseAlgorithms'] = None, curve: Optional['CoseEllipticCurves'] = None) -> bool: """ Verifies the signature of a received COSE message. :param public_key: A COSE key of type EC2 or OKP :param alg: An optional CoseAlgorithm :param curve: An optional CoseEllipticCurve :raises CoseIllegalKeyType: When the key type is not of EC2 or OKP :raises CoseIllegalAlgorithm: When the algorithm configuration is invalid :returns: True for a valid signature or False for an invalid signature """ if not isinstance(public_key, EC2) and not isinstance(public_key, OKP): raise CoseIllegalKeyType( "COSE key should be of type 'EC2' or 'OKP', got {}".format( type(public_key))) return public_key.verify(self._sig_structure, self.signature, alg, curve)
def from_dict(cls, received: dict) -> 'CK': """ Initialize a COSE key from a dictionary. :param received: Dictionary to translate to COSE key. :return: An initialized COSE Key object. """ if KpKty in received: key_obj = cls._key_types[received[KpKty]].from_dict(received) elif KpKty.identifier in received: key_obj = cls._key_types[received[KpKty.identifier]].from_dict( received) elif KpKty.fullname in received: key_obj = cls._key_types[received[KpKty.fullname]].from_dict( received) else: raise CoseIllegalKeyType( "Could not decode CoseKey type, KpKty not set or unknown.") return key_obj
def __init__(self, k: bytes, optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): transformed_dict = {} if len(k) not in [16, 24, 32]: raise CoseInvalidKey( "Key length should be either 16, 24, or 32 bytes") new_dict = dict({KpKty: KtySymmetric, SymKpK: k}) if optional_params is not None: new_dict.update(optional_params) for _key_attribute, _value in new_dict.items(): try: # translate the key_attribute kp = SymmetricKeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtySymmetric: raise CoseIllegalKeyType( f"Illegal key type in Symmetric COSE Key: {transformed_dict.get(KpKty)}" ) super(SymmetricKey, self).__init__(transformed_dict)
def compute_signature( self, private_key: Union[EC2, OKP] = None, alg: Optional['CoseAlgorithms'] = None, curve: Optional['CoseEllipticCurves'] = None) -> bytes: """ Computes the signature over a COSE message. :param private_key: A COSE key of type EC2 or OKP :param alg: An optional CoseAlgorithm :param curve: An optional CoseEllipticCurve :raises CoseIllegalKeyType: When the key type is not of EC2 or OKP :raises CoseIllegalAlgorithm: When the algorithm configuration is invalid :returns: True or False """ if not isinstance(private_key, EC2) and not isinstance( private_key, OKP): raise CoseIllegalKeyType( "COSE key should be of type 'EC2' or 'OKP', got {}".format( type(private_key))) return private_key.sign(self._sig_structure, alg, curve)
def __init__(self, k: bytes, optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): transformed_dict = {KpKty: KtySymmetric} if optional_params is None: optional_params = {} for _key_attribute, _value in optional_params.items(): try: # translate the key_attribute kp = SymmetricKeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtySymmetric: raise CoseIllegalKeyType( f"Illegal key type in Symmetric COSE Key: {transformed_dict.get(KpKty)}" ) super(SymmetricKey, self).__init__(transformed_dict) if k != b'': self.k = k else: raise CoseInvalidKey("SymKpK parameter cannot be None")
def __init__(self, crv: Union[Type['CoseCurve'], str, int], x: bytes = b'', y: bytes = b'', d: bytes = b'', optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): """Initialize a COSE key from its components Not passing a `y` component is accepted; in this case, one (of the two) valid `y` will be found for the `x`. This is good enough for everything that only operates on the `x` of any derived outputs (in "compact" mode), as per RFC 6090 Section 4.2. """ transformed_dict = {KpKty: KtyEC2} if optional_params is None: optional_params = {} for _key_attribute, _value in optional_params.items(): try: # transform the key_attribute kp = EC2KeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtyEC2: raise CoseIllegalKeyType( f"Illegal key type in EC2 COSE Key: {transformed_dict.get(KpKty)}" ) super(EC2Key, self).__init__(transformed_dict) if crv is not None: self.crv = crv else: raise CoseInvalidKey("COSE curve cannot be None") if not x and not y and not d: raise CoseInvalidKey( "Either the public values or the private value must be specified" ) if not d and not x: raise CoseInvalidKey("Missing public coordinate X") if d: public_nums = ec.derive_private_key( int.from_bytes(d, byteorder="big"), curve=self.crv.curve_obj()).public_key().public_numbers() if x: assert x == public_nums.x.to_bytes(self.crv.size, 'big') else: x = public_nums.x.to_bytes(self.crv.size, 'big') if y: assert y == public_nums.y.to_bytes(self.crv.size, 'big') else: y = public_nums.y.to_bytes(self.crv.size, 'big') if x and not y: # try to derive y from x key = ec.EllipticCurvePublicKey.from_encoded_point( self.crv.curve_obj(), # don't care which of the two possible Y values we get b'\x03' + x # or [::-1]? ) # Just to check the endianness of the conversions -- if we get the # right X back out, then the X and Y are consistent, and anyway the # crypto backend will check whether the point is on the curve assert x == key.public_numbers().x.to_bytes(self.crv.size, 'big') y = key.public_numbers().y.to_bytes(self.crv.size, 'big') if x != b'': self.x = x if y != b'': self.y = y if d != b'': self.d = d
def __init__(self, n: bytes = b'', e: bytes = b'', d: bytes = b'', p: bytes = b'', q: bytes = b'', dp: bytes = b'', dq: bytes = b'', qinv: bytes = b'', other: Optional[List[dict]] = None, r_i: bytes = b'', d_i: bytes = b'', t_i: bytes = b'', optional_params: Optional[dict] = None, allow_unknown_key_attrs: bool = True): transformed_dict = {KpKty: KtyRSA} if other is None: other = [] if optional_params is None: optional_params = {} # public key, n and e must be defined if len(n) != 0 and len(e) != 0 and all( map(lambda x: len(x) == 0, [d, p, q, dp, dq, qinv, other, r_i, d_i, t_i])): is_valid_key = True # private keys with two primes, except other, r_i, t_i and d_i, all other parameters need to be defined elif all(map(lambda x: len(x) == 0, [other, r_i, t_i, d_i])) and \ all(map(lambda x: len(x) != 0, [n, e, d, p, q, dp, dq, qinv])): is_valid_key = True # private keys with more than two primes, everything needs to be defined elif all( map(lambda x: len(x) != 0, [n, e, d, p, q, dp, dq, qinv, other, r_i, d_i, t_i])): is_valid_key = True # TODO: verify contents of other else: is_valid_key = False if not is_valid_key: raise CoseInvalidKey( "Invalid RSA key: not a public key, private key with two primes or private key " "with more than two primes") for _key_attribute, _value in optional_params.items(): try: # transform the key_attribute kp = RSAKeyParam.from_id(_key_attribute, allow_unknown_key_attrs) # parse the value of the key attribute if possible if hasattr(kp, 'value_parser') and hasattr( kp.value_parser, '__call__'): _value = kp.value_parser(_value) # store in new dict transformed_dict[kp] = _value except ValueError: transformed_dict[_key_attribute] = _value # final check if key type is correct if transformed_dict.get(KpKty) != KtyRSA: raise CoseIllegalKeyType( f"Illegal key type in RSA COSE Key: {transformed_dict.get(KpKty)}" ) super(RSAKey, self).__init__(transformed_dict) self.e = e self.n = n if d != b'': self.d = d if p != b'': self.p = p if q != b'': self.q = q if dp != b'': self.dp = dp if dq != b'': self.dq = dq if qinv != b'': self.qinv = qinv if other: self.other = other if r_i != b'': self.r_i = r_i if d_i != b'': self.d_i = d_i if t_i != b'': self.t_i = t_i