diff --git a/src/cryptojwt/jwk/__init__.py b/src/cryptojwt/jwk/__init__.py index d8fd480a..fd4e9cb1 100644 --- a/src/cryptojwt/jwk/__init__.py +++ b/src/cryptojwt/jwk/__init__.py @@ -75,6 +75,9 @@ def __init__( "PS256", "PS384", "PS512", + "EdDSA", + "Ed25519", + "Ed448", "none", ]: raise UnsupportedAlgorithm("Unknown algorithm: {}".format(alg)) @@ -93,6 +96,9 @@ def __init__( "PS256", "PS384", "PS512", + "EdDSA", + "Ed25519", + "Ed448", "none", "RSA1_5", "RSA-OAEP", diff --git a/src/cryptojwt/jws/eddsa.py b/src/cryptojwt/jws/eddsa.py index 983d93d2..6a88f8e0 100644 --- a/src/cryptojwt/jws/eddsa.py +++ b/src/cryptojwt/jws/eddsa.py @@ -10,6 +10,9 @@ class EDDSASigner(Signer): + def __init__(self, algorithm=None): + self.algorithm = algorithm + def sign(self, msg, key): """ Create a signature over a message as defined in RFC7515 using an @@ -20,6 +23,17 @@ def sign(self, msg, key): :return: """ + if self.algorithm: + if self.algorithm == "Ed25519" and not isinstance(key, ed25519.Ed25519PrivateKey): + raise TypeError("The private key must be an instance of Ed25519PrivateKey") + if self.algorithm == "Ed448" and not isinstance(key, ed448.Ed448PrivateKey): + raise TypeError("The private key must be an instance of Ed448PrivateKey") + + if not isinstance(key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)): + raise TypeError( + "The private key must be an instance of Ed25519PrivateKey or Ed448PrivateKey" + ) + if not isinstance(key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)): raise TypeError( "The private key must be an instance of Ed25519PrivateKey or Ed448PrivateKey" @@ -37,6 +51,13 @@ def verify(self, msg, sig, key): :raises: BadSignature if the signature can't be verified. :return: True """ + + if self.algorithm: + if self.algorithm == "Ed25519" and not isinstance(key, ed25519.Ed25519PublicKey): + raise TypeError("The public key must be an instance of Ed25519PublicKey") + if self.algorithm == "Ed448" and not isinstance(key, ed448.Ed448PublicKey): + raise TypeError("The public key must be an instance of Ed448PublicKey") + if not isinstance(key, (ed25519.Ed25519PublicKey, ed448.Ed448PublicKey)): raise TypeError( "The public key must be an instance of Ed25519PublicKey or Ed448PublicKey" diff --git a/src/cryptojwt/jws/jws.py b/src/cryptojwt/jws/jws.py index 7da26fa3..7a93a843 100644 --- a/src/cryptojwt/jws/jws.py +++ b/src/cryptojwt/jws/jws.py @@ -48,6 +48,8 @@ "PS384": PSSSigner("SHA384"), "PS512": PSSSigner("SHA512"), "EdDSA": EDDSASigner(), + "Ed25519": EDDSASigner("Ed25519"), + "Ed448": EDDSASigner("Ed448"), "none": None, } diff --git a/src/cryptojwt/jws/utils.py b/src/cryptojwt/jws/utils.py index 9ccf16ff..40ac46f2 100644 --- a/src/cryptojwt/jws/utils.py +++ b/src/cryptojwt/jws/utils.py @@ -47,6 +47,10 @@ def alg2keytype(alg): return "RSA" elif alg.startswith("HS") or alg.startswith("A"): return "oct" + elif alg == "Ed25519": + return "OKP" + elif alg == "Ed448": + return "OKP" elif alg.startswith("ES") or alg.startswith("ECDH-ES"): return "EC" elif alg == "EdDSA": diff --git a/tests/test_03_key_bundle.py b/tests/test_03_key_bundle.py index bd7353ba..e4841924 100755 --- a/tests/test_03_key_bundle.py +++ b/tests/test_03_key_bundle.py @@ -225,7 +225,7 @@ def test_ignore_unknown_types(): "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", "use": "sig", "alg": "EdDSA", - "kty": "OKP", + "kty": "XXX", "crv": "Ed25519", "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ", } diff --git a/tests/test_04_key_issuer.py b/tests/test_04_key_issuer.py index e2d6f36d..6ff041f1 100755 --- a/tests/test_04_key_issuer.py +++ b/tests/test_04_key_issuer.py @@ -446,24 +446,6 @@ def test_load_missing_key_parameter(): "n": "68be-nJp46VLj4Ci1V36IrVGYqkuBfYNyjQTZD_7yRYcERZebowOnwr3w0DoIQpl8iL2X8OXUo7rUW_LMzLxKx2hEmdJfUn4LL2QqA3KPgjYz8hZJQPG92O14w9IZ-8bdDUgXrg9216H09yq6ZvJrn5Nwvap3MXgECEzsZ6zQLRKdb_R96KFFgCiI3bEiZKvZJRA7hM2ePyTm15D9En_Wzzfn_JLMYgE_DlVpoKR1MsTinfACOlwwdO9U5Dm-5elapovILTyVTgjN75i-wsPU2TqzdHFKA-4hJNiWGrYPiihlAFbA2eUSXuEYFkX43ahoQNpeaf0mc17Jt5kp7pM2w", "e": "AQAB", }, - { - "kid": "q-H9y8iuh3BIKZBbK6S0mH_isBlJsk" - "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", - "use": "sig", - "alg": "EdDSA", - "kty": "OKP", - "crv": "Ed25519", - "x": "FnbcUAXZ4ySvrmdXK1MrDuiqlqTXvGdAaE4RWZjmFIQ", - }, - { - "kid": "bL33HthM3fWaYkY2_pDzUd7a65FV2R2LHAKCOsye8eNmAPDgRgpHWPYpWFVmeaujUUEXRyDLHN" - "-Up4QH_sFcmw", - "use": "sig", - "alg": "EdDSA", - "kty": "OKP", - "crv": "Ed25519", - "x": "CS01DGXDBPV9cFmd8tgFu3E7eHn1UcP7N1UCgd_JgZo", - }, ] } diff --git a/tests/test_04_key_jar.py b/tests/test_04_key_jar.py index 9e73e9e1..5f002430 100755 --- a/tests/test_04_key_jar.py +++ b/tests/test_04_key_jar.py @@ -538,6 +538,11 @@ def test_load_missing_key_parameter(): "n": "68be-nJp46VLj4Ci1V36IrVGYqkuBfYNyjQTZD_7yRYcERZebowOnwr3w0DoIQpl8iL2X8OXUo7rUW_LMzLxKx2hEmdJfUn4LL2QqA3KPgjYz8hZJQPG92O14w9IZ-8bdDUgXrg9216H09yq6ZvJrn5Nwvap3MXgECEzsZ6zQLRKdb_R96KFFgCiI3bEiZKvZJRA7hM2ePyTm15D9En_Wzzfn_JLMYgE_DlVpoKR1MsTinfACOlwwdO9U5Dm-5elapovILTyVTgjN75i-wsPU2TqzdHFKA-4hJNiWGrYPiihlAFbA2eUSXuEYFkX43ahoQNpeaf0mc17Jt5kp7pM2w", "e": "AQAB", }, + ] +} + +JWKS_EDDSA = { + "keys": [ { "kid": "q-H9y8iuh3BIKZBbK6S0mH_isBlJsk" "-u6VtZ5rAdBo5fCjjy3LnkrsoK_QWrlKB08j_PcvwpAMfTEDHw5spepw", @@ -556,6 +561,22 @@ def test_load_missing_key_parameter(): "crv": "Ed25519", "x": "CS01DGXDBPV9cFmd8tgFu3E7eHn1UcP7N1UCgd_JgZo", }, + { + "kid": "OF9xVk9NWE5iQ2N6OGhILTVGcXg4RE1FRk5NWVVsaXZLcFNRNUxCYk9vQQ", + "use": "sig", + "alg": "Ed25519", + "kty": "OKP", + "crv": "Ed25519", + "x": "M_D8nslNSecjPwiP6DwuNhWRdrgqp02U7f5xo4GhdlY", + }, + { + "kid": "RUpoaXktM1JwT0hON3lzNWNfN0RUbVpiWExwbnJnNDRfYWhZY3htaTZ1Zw", + "use": "sig", + "alg": "Ed448", + "kty": "OKP", + "crv": "Ed448", + "x": "C3y5YN00IxyadHXm4NApPGAzv5w8s9e-fbGu2svYrrCuJDYDDZe-uEOPSobII6psCZCEvo2howmA", + }, ] } @@ -580,6 +601,16 @@ def test_get_ec_wrong_alg(): assert k == [] +def test_get_eddsa(): + kj = KeyJar() + kj.import_jwks(JWKS_EDDSA, "") + assert len(kj.get_issuer_keys("")) == 4 + k = kj.get("sig", "OKP", alg="Ed25519") + assert k + k = kj.get("sig", "OKP", alg="Ed448") + assert k + + def test_keyjar_eq(): kj1 = KeyJar() kj1.import_jwks(JWKS_SPO, "") diff --git a/tests/test_06_jws.py b/tests/test_06_jws.py index 8ce77c2d..286e3b3a 100644 --- a/tests/test_06_jws.py +++ b/tests/test_06_jws.py @@ -609,6 +609,20 @@ def test_signer_ps512(): def test_signer_eddsa(): + payload = "Please take a moment to register today" + okp = ed25519.Ed25519PrivateKey.generate() + _key = OKPKey().load_key(okp) + keys = [_key] + _jws = JWS(payload, alg="Ed25519") + _jwt = _jws.sign_compact(keys) + + _pubkey = OKPKey().load_key(okp.public_key()) + _rj = JWS(alg="Ed25519") + info = _rj.verify_compact(_jwt, [_pubkey]) + assert info == payload + + +def test_signer_eddsa_polymorphic(): payload = "Please take a moment to register today" okp = ed25519.Ed25519PrivateKey.generate() _key = OKPKey().load_key(okp) @@ -627,12 +641,12 @@ def test_signer_eddsa_fail(): okp = ed25519.Ed25519PrivateKey.generate() _key = OKPKey().load_key(okp) keys = [_key] - _jws = JWS(payload, alg="EdDSA") + _jws = JWS(payload, alg="Ed25519") _jwt = _jws.sign_compact(keys) okp2 = ed25519.Ed25519PrivateKey.generate() _pubkey = OKPKey().load_key(okp2.public_key()) - _rj = JWS(alg="EdDSA") + _rj = JWS(alg="Ed25519") try: info = _rj.verify_compact(_jwt, [_pubkey]) except BadSignature: