From 99df9614954ba8c8b18bf4dc29ce2f25e7b30bef Mon Sep 17 00:00:00 2001 From: Johannes Will Date: Tue, 6 Apr 2021 18:44:45 +0200 Subject: [PATCH 1/5] Add to_jwk to Ed25519Algorithm. (#642) --- jwt/algorithms.py | 45 ++++++++++++++++++++++++++++++++++++++++ tests/test_algorithms.py | 19 +++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/jwt/algorithms.py b/jwt/algorithms.py index 50719bea..4df49d9c 100644 --- a/jwt/algorithms.py +++ b/jwt/algorithms.py @@ -37,6 +37,10 @@ rsa_recover_prime_factors, ) from cryptography.hazmat.primitives.serialization import ( + Encoding, + NoEncryption, + PrivateFormat, + PublicFormat, load_pem_private_key, load_pem_public_key, load_ssh_public_key, @@ -587,6 +591,47 @@ def verify(self, msg, key, sig): except cryptography.exceptions.InvalidSignature: return False + @staticmethod + def to_jwk(key): + if isinstance(key, Ed25519PublicKey): + x = key.public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + + return json.dumps( + { + "x": base64url_encode(force_bytes(x)).decode(), + "kty": "OKP", + "alg": "EdDSA", + "crv": "Ed25519", + } + ) + + if isinstance(key, Ed25519PrivateKey): + d = key.private_bytes( + encoding=Encoding.Raw, + format=PrivateFormat.Raw, + encryption_algorithm=NoEncryption(), + ) + + x = key.public_key().public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + + return json.dumps( + { + "x": base64url_encode(force_bytes(x)).decode(), + "d": base64url_encode(force_bytes(d)).decode(), + "kty": "OKP", + "alg": "EdDSA", + "crv": "Ed25519", + } + ) + + raise InvalidKeyError("Not a public or private key") + @staticmethod def from_jwk(jwk): try: diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index 2144d484..23717a8d 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -800,3 +800,22 @@ def test_ed25519_jwk_fails_on_invalid_json(self): v["d"] = "123" with pytest.raises(InvalidKeyError): algo.from_jwk(v) + + def test_ed25519_jwk_public_key_to_jwk_works_with_from_jwk(self): + algo = Ed25519Algorithm() + + with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile: + priv_key_1 = algo.from_jwk(keyfile.read()) + + with open(key_path("jwk_okp_pub_Ed25519.json")) as keyfile: + pub_key_1 = algo.from_jwk(keyfile.read()) + + pub = algo.to_jwk(pub_key_1) + pub_key_2 = algo.from_jwk(pub) + pri = algo.to_jwk(priv_key_1) + priv_key_2 = algo.from_jwk(pri) + + signature_1 = algo.sign(b"Hello World!", priv_key_1) + signature_2 = algo.sign(b"Hello World!", priv_key_2) + assert algo.verify(b"Hello World!", pub_key_2, signature_1) + assert algo.verify(b"Hello World!", pub_key_2, signature_2) From 62396a564cd50eebaeb7fedb82d0b14f59f7342d Mon Sep 17 00:00:00 2001 From: Johannes Will Date: Tue, 6 Apr 2021 19:16:45 +0200 Subject: [PATCH 2/5] add test for invalid key --- tests/test_algorithms.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index 23717a8d..f2e39234 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -801,7 +801,7 @@ def test_ed25519_jwk_fails_on_invalid_json(self): with pytest.raises(InvalidKeyError): algo.from_jwk(v) - def test_ed25519_jwk_public_key_to_jwk_works_with_from_jwk(self): + def test_ed25519_to_jwk_works_with_from_jwk(self): algo = Ed25519Algorithm() with open(key_path("jwk_okp_key_Ed25519.json")) as keyfile: @@ -819,3 +819,9 @@ def test_ed25519_jwk_public_key_to_jwk_works_with_from_jwk(self): signature_2 = algo.sign(b"Hello World!", priv_key_2) assert algo.verify(b"Hello World!", pub_key_2, signature_1) assert algo.verify(b"Hello World!", pub_key_2, signature_2) + + def test_ed25519_to_jwk_raises_exception_on_invalid_key(self): + algo = Ed25519Algorithm() + + with pytest.raises(InvalidKeyError): + algo.to_jwk({"not": "a valid key"}) \ No newline at end of file From 7b62addaa87d51f146efa509c66ff9c7cf616940 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Apr 2021 17:17:19 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index f2e39234..0ece6d88 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -824,4 +824,4 @@ def test_ed25519_to_jwk_raises_exception_on_invalid_key(self): algo = Ed25519Algorithm() with pytest.raises(InvalidKeyError): - algo.to_jwk({"not": "a valid key"}) \ No newline at end of file + algo.to_jwk({"not": "a valid key"}) From 0be7e62865010dfe27b8258c5ed5a0d26065e068 Mon Sep 17 00:00:00 2001 From: Johannes Will Date: Tue, 6 Apr 2021 20:01:03 +0200 Subject: [PATCH 4/5] update CHANGELOG for #643 --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e67381fb..be10c2ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,6 +26,7 @@ Added - Add missing exceptions.InvalidKeyError to jwt module __init__ imports `#620 `__ - Add support for ES256K algorithm `#629 `__ - Add `from_jwk()` to Ed25519Algorithm `#621 `__ +- Add `to_jwk()` to Ed25519Algorithm `#643 `__ `v2.0.1 `__ -------------------------------------------------------------------- From ab95d252e0e1f39c7f27bb23dc1b1a7a508142f0 Mon Sep 17 00:00:00 2001 From: Johannes Will Date: Wed, 7 Apr 2021 08:09:48 +0200 Subject: [PATCH 5/5] remove alg from jwk --- jwt/algorithms.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jwt/algorithms.py b/jwt/algorithms.py index 4df49d9c..42648ce3 100644 --- a/jwt/algorithms.py +++ b/jwt/algorithms.py @@ -603,7 +603,6 @@ def to_jwk(key): { "x": base64url_encode(force_bytes(x)).decode(), "kty": "OKP", - "alg": "EdDSA", "crv": "Ed25519", } ) @@ -625,7 +624,6 @@ def to_jwk(key): "x": base64url_encode(force_bytes(x)).decode(), "d": base64url_encode(force_bytes(d)).decode(), "kty": "OKP", - "alg": "EdDSA", "crv": "Ed25519", } )