def test_ed448_unsupported(backend): with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM): Ed448PublicKey.from_public_bytes(b"0" * 57) with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM): Ed448PrivateKey.from_private_bytes(b"0" * 57) with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM): Ed448PrivateKey.generate()
def test_ed448_unsupported(backend): with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): Ed448PublicKey.from_public_bytes(b"0" * 57) with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): Ed448PrivateKey.from_private_bytes(b"0" * 57) with raises_unsupported_algorithm( _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): Ed448PrivateKey.generate()
def test_pub_priv_bytes_raw(self, vector, backend): sk = binascii.unhexlify(vector["secret"]) pk = binascii.unhexlify(vector["public"]) private_key = Ed448PrivateKey.from_private_bytes(sk) assert ( private_key.private_bytes( serialization.Encoding.Raw, serialization.PrivateFormat.Raw, serialization.NoEncryption(), ) == sk ) assert ( private_key.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) == pk ) public_key = Ed448PublicKey.from_public_bytes(pk) assert ( public_key.public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) == pk )
def cryptography_okp_public_key( credential_public_key: OKPCredentialPublicKey) -> OKPPublicKey: """Convert an `OKPCredentialPublicKey` into a cryptography `OKPPublicKey`. Args: credential_public_key (EC2CredentialPublicKey): The key to convert. Returns: A cryptography `EC2PublicKey`. Raises: UnimplementedError: If the conversion logic for the given type of CredentialPublicKey has not been implemented. PublicKeyConversionError: If the provided key could not be converted into a valid cryptography `EC2PublicKey`. """ try: if credential_public_key.crv.name == 'ED25519': return Ed25519PublicKey.from_public_bytes(credential_public_key.x) elif credential_public_key.crv.name == 'ED448': return Ed448PublicKey.from_public_bytes(credential_public_key.x) else: raise UnimplementedError( 'Unsupported cryptography OKP curve {}'.format( credential_public_key.crv.name)) except ValueError: raise PublicKeyConversionError('Invalid OKP public key')
def verify(self, to_be_verified: bytes, signature: bytes, alg: Optional[CoseAlgorithms] = None, curve: Optional[CoseEllipticCurves] = None) -> bool: """ Verifies the digital signature over 'to_be_verified'. The parameter 'alg' and 'curve' parameters are optional in case they are already provided by the key object self. :param to_be_verified: Data that was signed. :param signature: Signature to verify. :param alg: An optional algorithm parameter. :param curve: An optional curve :return: True when the signature is valid and False if the signature is invalid. """ self._check_key_conf(algorithm=alg, key_operation=KeyOps.VERIFY, curve=curve) if self.crv == CoseEllipticCurves.ED25519: vk = Ed25519PublicKey.from_public_bytes(self.x) elif self._crv == CoseEllipticCurves.ED448: vk = Ed448PublicKey.from_public_bytes(self.x) else: raise CoseIllegalCurve( f"Illegal curve for OKP singing: {self.crv}") try: vk.verify(signature, to_be_verified) return True except InvalidSignature: return False
def from_jwk(jwk): try: if isinstance(jwk, str): obj = json.loads(jwk) elif isinstance(jwk, dict): obj = jwk else: raise ValueError except ValueError: raise InvalidKeyError("Key is not valid JSON") if obj.get("kty") != "OKP": raise InvalidKeyError("Not an Octet Key Pair") curve = obj.get("crv") if curve != "Ed25519" and curve != "Ed448": raise InvalidKeyError(f"Invalid curve: {curve}") if "x" not in obj: raise InvalidKeyError('OKP should have "x" parameter') x = base64url_decode(obj.get("x")) try: if "d" not in obj: if curve == "Ed25519": return Ed25519PublicKey.from_public_bytes(x) return Ed448PublicKey.from_public_bytes(x) d = base64url_decode(obj.get("d")) if curve == "Ed25519": return Ed25519PrivateKey.from_private_bytes(d) return Ed448PrivateKey.from_private_bytes(d) except ValueError as err: raise InvalidKeyError("Invalid key parameter") from err
def cryptography_okp_public_key( credential_public_key: OKPCredentialPublicKey) -> PublicKey: if credential_public_key.crv.name == 'ED25519': return Ed25519PublicKey.from_public_bytes(credential_public_key.x) elif credential_public_key.crv.name == 'ED448': return Ed448PublicKey.from_public_bytes(credential_public_key.x) else: raise UnimplementedError( 'Unsupported cryptography OKP curve {}'.format( credential_public_key.crv.name))
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
def test_ed448_signature(backend, wycheproof): key = Ed448PublicKey.from_public_bytes( binascii.unhexlify(wycheproof.testgroup["key"]["pk"])) if wycheproof.valid or wycheproof.acceptable: key.verify( binascii.unhexlify(wycheproof.testcase["sig"]), binascii.unhexlify(wycheproof.testcase["msg"]), ) else: with pytest.raises(InvalidSignature): key.verify( binascii.unhexlify(wycheproof.testcase["sig"]), binascii.unhexlify(wycheproof.testcase["msg"]), )
def test_malleability(self, backend): # This is a signature where r > the group order. It should be # rejected to prevent signature malleability issues. This test can # be removed when wycheproof grows ed448 vectors public_bytes = binascii.unhexlify( "fedb02a658d74990244d9d10cf338e977565cbbda6b24c716829ed6ee1e4f28cf" "2620c052db8d878f6243bffc22242816c1aaa67d2f3603600") signature = binascii.unhexlify( "0cc16ba24d69277f927c1554b0f08a2a711bbdd20b058ccc660d00ca13542a3ce" "f9e5c44c54ab23a2eb14f947e167b990b080863e28b399380f30db6e54d5d1406" "d23378ffde11b1fb81b2b438a3b8e8aa7f7f4e1befcc905023fab5a5465053844" "f04cf0c1b51d84760f869588687f57500") key = Ed448PublicKey.from_public_bytes(public_bytes) with pytest.raises(InvalidSignature): key.verify(signature, b"8")
def test_pub_priv_bytes_raw(self, vector, backend): sk = binascii.unhexlify(vector["secret"]) pk = binascii.unhexlify(vector["public"]) private_key = Ed448PrivateKey.from_private_bytes(sk) assert private_key.private_bytes( serialization.Encoding.Raw, serialization.PrivateFormat.Raw, serialization.NoEncryption() ) == sk assert private_key.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) == pk public_key = Ed448PublicKey.from_public_bytes(pk) assert public_key.public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) == pk
def test_malleability(self, backend): # This is a signature where r > the group order. It should be # rejected to prevent signature malleability issues. This test can # be removed when wycheproof grows ed448 vectors public_bytes = binascii.unhexlify( "fedb02a658d74990244d9d10cf338e977565cbbda6b24c716829ed6ee1e4f28cf" "2620c052db8d878f6243bffc22242816c1aaa67d2f3603600" ) signature = binascii.unhexlify( "0cc16ba24d69277f927c1554b0f08a2a711bbdd20b058ccc660d00ca13542a3ce" "f9e5c44c54ab23a2eb14f947e167b990b080863e28b399380f30db6e54d5d1406" "d23378ffde11b1fb81b2b438a3b8e8aa7f7f4e1befcc905023fab5a5465053844" "f04cf0c1b51d84760f869588687f57500" ) key = Ed448PublicKey.from_public_bytes(public_bytes) with pytest.raises(InvalidSignature): key.verify(signature, b"8")
def test_invalid_length_from_public_bytes(self, backend): with pytest.raises(ValueError): Ed448PublicKey.from_public_bytes(b"a" * 56) with pytest.raises(ValueError): Ed448PublicKey.from_public_bytes(b"a" * 58)
def test_invalid_type_public_bytes(self, backend): with pytest.raises(TypeError): Ed448PublicKey.from_public_bytes(object())
def __init__(self, params: Dict[int, Any]): super().__init__(params) self._public_key: Any = None self._private_key: Any = None self._hash_alg: Any = None self._x = None self._d = None # Validate kty. if params[1] != 1: raise ValueError("kty(1) should be OKP(1).") # Validate crv. if -1 not in params: raise ValueError("crv(-1) not found.") self._crv = params[-1] if not isinstance(self._crv, int): raise ValueError("crv(-1) should be int.") if self._crv not in [4, 5, 6, 7]: raise ValueError( f"Unsupported or unknown crv(-1) for OKP: {self._crv}.") if self._crv in [4, 5]: if not self._alg: raise ValueError("X25519/X448 needs alg explicitly.") if self._alg in [-25, -27]: self._hash_alg = hashes.SHA256 elif self._alg in [-26, -28]: self._hash_alg = hashes.SHA512 else: raise ValueError( f"Unsupported or unknown alg used with X25519/X448: {self._alg}." ) # Validate alg and key_ops. if self._key_ops: if set(self._key_ops) & set([3, 4, 5, 6, 9, 10]): raise ValueError( "Unknown or not permissible key_ops(4) for OKP.") else: if self._crv in [4, 5]: self._key_ops = [7, 8] if -4 in params else [] else: # self._crv in [6, 7] self._key_ops = [1, 2] if -4 in params else [2] if self._alg: if self._alg in COSE_ALGORITHMS_SIG_OKP.values(): if -4 in params: # private key for signing. if not (set(self._key_ops) & set([1, 2])): raise ValueError("Invalid key_ops for signing key.") if set(self._key_ops) & set([7, 8]): raise ValueError( "Signing key should not be used for key derivation." ) else: # public key for signing. if 2 not in self._key_ops or len(self._key_ops) != 1: raise ValueError("Invalid key_ops for public key.") elif self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values(): if -4 in params: # private key for key derivation. if not (set(self._key_ops) & set([7, 8])): raise ValueError("Invalid key_ops for key derivation.") if set(self._key_ops) & set([1, 2]): raise ValueError( "Private key for ECDHE should not be used for signing." ) else: # public key for key derivation. if self._key_ops: raise ValueError( "Public key for ECDHE should not have key_ops.") else: raise ValueError( f"Unsupported or unknown alg(3) for OKP: {self._alg}.") else: if -4 in params: # private key. if set(self._key_ops) & set([1, 2]): # private key for signing. if set(self._key_ops) & set([7, 8]): raise ValueError( "OKP private key should not be used for both signing and key derivation." ) self._alg = -8 # EdDSA else: # public key. if 2 in self._key_ops: if len(self._key_ops) > 1: raise ValueError("Invalid key_ops for public key.") else: raise ValueError("Invalid key_ops for public key.") if self._alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_ES.values(): if -2 not in params: return # Validate x. if -2 not in params: raise ValueError("x(-2) not found.") if not isinstance(params[-2], bytes): raise ValueError("x(-2) should be bytes(bstr).") self._x = params[-2] try: if -4 not in params: if self._crv == 4: # X25519 self._public_key = X25519PublicKey.from_public_bytes( self._x) elif self._crv == 5: # X448 self._public_key = X448PublicKey.from_public_bytes(self._x) elif self._crv == 6: # Ed25519 self._public_key = Ed25519PublicKey.from_public_bytes( self._x) else: # self._crv == 7 (Ed448) self._public_key = Ed448PublicKey.from_public_bytes( self._x) self._key = self._public_key return except ValueError as err: raise ValueError("Invalid key parameter.") from err if not isinstance(params[-4], bytes): raise ValueError("d(-4) should be bytes(bstr).") try: self._d = params[-4] if self._crv == 4: # X25519 self._private_key = X25519PrivateKey.from_private_bytes( self._d) elif self._crv == 5: # X448 self._private_key = X448PrivateKey.from_private_bytes(self._d) elif self._crv == 6: # Ed25519 self._private_key = Ed25519PrivateKey.from_private_bytes( self._d) else: # self._crv == 7 (Ed448) self._private_key = Ed448PrivateKey.from_private_bytes(self._d) self._key = self._private_key except ValueError as err: raise ValueError("Invalid key parameter.") from err return