Skip to content

Commit 181a32a

Browse files
authored
Merge pull request #192 from jschlyter/jwk_hash
Define the hash of a JWK based thumbprint+kid
2 parents ea9776c + d11e0d1 commit 181a32a

File tree

6 files changed

+87
-9
lines changed

6 files changed

+87
-9
lines changed

src/cryptojwt/jwk/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,16 @@ class JWK:
2626
required = ["kty"]
2727

2828
def __init__(
29-
self, kty="", alg="", use="", kid="", x5c=None, x5t="", x5u="", key_ops=None, **kwargs
29+
self,
30+
kty="",
31+
alg="",
32+
use="",
33+
kid="",
34+
x5c=None,
35+
x5t="",
36+
x5u="",
37+
key_ops=None,
38+
**kwargs,
3039
):
3140
self.extra_args = kwargs
3241

@@ -220,6 +229,9 @@ def verify(self):
220229
raise ValueError("kid of wrong value type")
221230
return True
222231

232+
def __hash__(self) -> int:
233+
return hash((self.thumbprint("SHA-256"), self.kid))
234+
223235
def __eq__(self, other):
224236
"""
225237
Compare 2 Key instances to find out if they represent the same key

src/cryptojwt/jwk/ec.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ def encryption_key(self):
232232
"""
233233
return self.pub_key
234234

235+
def __hash__(self) -> int:
236+
return super().__hash__()
237+
235238
def __eq__(self, other):
236239
"""
237240
Verify that the other key has the same properties as myself.

src/cryptojwt/jwk/hmac.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,17 @@ class SYMKey(JWK):
4040
required = ["k", "kty"]
4141

4242
def __init__(
43-
self, kty="oct", alg="", use="", kid="", x5c=None, x5t="", x5u="", k="", key="", **kwargs
43+
self,
44+
kty="oct",
45+
alg="",
46+
use="",
47+
kid="",
48+
x5c=None,
49+
x5t="",
50+
x5u="",
51+
k="",
52+
key="",
53+
**kwargs,
4454
):
4555
JWK.__init__(self, kty, alg, use, kid, x5c, x5t, x5u, **kwargs)
4656
self.k = k
@@ -117,6 +127,9 @@ def encryption_key(self, alg, **kwargs):
117127

118128
return _enc_key
119129

130+
def __hash__(self) -> int:
131+
return super().__hash__()
132+
120133
def __eq__(self, other):
121134
"""
122135
Compare 2 JWK instances to find out if they represent the same key

src/cryptojwt/jwk/okp.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
)
1616

1717
OKPPublicKey = Union[
18-
ed25519.Ed25519PublicKey, ed448.Ed448PublicKey, x25519.X25519PublicKey, x448.X448PublicKey
18+
ed25519.Ed25519PublicKey,
19+
ed448.Ed448PublicKey,
20+
x25519.X25519PublicKey,
21+
x448.X448PublicKey,
1922
]
2023
OKPPrivateKey = Union[
21-
ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey, x25519.X25519PrivateKey, x448.X448PrivateKey
24+
ed25519.Ed25519PrivateKey,
25+
ed448.Ed448PrivateKey,
26+
x25519.X25519PrivateKey,
27+
x448.X448PrivateKey,
2228
]
2329

2430
OKP_CRV2PUBLIC = {
@@ -155,7 +161,8 @@ def deserialize(self):
155161
def _serialize_public(self, key):
156162
self.x = b64e(
157163
key.public_bytes(
158-
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
164+
encoding=serialization.Encoding.Raw,
165+
format=serialization.PublicFormat.Raw,
159166
)
160167
).decode("ascii")
161168

@@ -257,6 +264,9 @@ def encryption_key(self):
257264
"""
258265
return self.pub_key
259266

267+
def __hash__(self) -> int:
268+
return super().__hash__()
269+
260270
def __eq__(self, other):
261271
"""
262272
Verify that the other key has the same properties as myself.
@@ -304,9 +314,11 @@ def cmp_keys(a, b, key_type):
304314
return False
305315
else:
306316
if a.public_bytes(
307-
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
317+
encoding=serialization.Encoding.Raw,
318+
format=serialization.PublicFormat.Raw,
308319
) != b.public_bytes(
309-
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
320+
encoding=serialization.Encoding.Raw,
321+
format=serialization.PublicFormat.Raw,
310322
):
311323
return False
312324
return True

src/cryptojwt/jwk/rsa.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ def load(self, filename):
414414
"""
415415
return self.load_key(import_private_rsa_key_from_file(filename))
416416

417+
def __hash__(self) -> int:
418+
return super().__hash__()
419+
417420
def __eq__(self, other):
418421
"""
419422
Verify that this other key is the same as myself.

tests/test_02_jwk.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
import pytest
1010
from cryptography.hazmat.primitives.asymmetric import ec, ed25519, rsa
1111

12-
from cryptojwt.exception import DeSerializationNotPossible, UnsupportedAlgorithm, WrongUsage
12+
from cryptojwt.exception import (
13+
DeSerializationNotPossible,
14+
UnsupportedAlgorithm,
15+
WrongUsage,
16+
)
1317
from cryptojwt.jwk import JWK, certificate_fingerprint, pem_hash, pems_to_x5c
1418
from cryptojwt.jwk.ec import ECKey, new_ec_key
1519
from cryptojwt.jwk.hmac import SYMKey, new_sym_key, sha256_digest
@@ -735,7 +739,11 @@ def test_import_public_key_from_pem_file(filename, key_type):
735739
assert isinstance(pub_key, key_type)
736740

737741

738-
OKPKEY = {"crv": "Ed25519", "kty": "OKP", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}
742+
OKPKEY = {
743+
"crv": "Ed25519",
744+
"kty": "OKP",
745+
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
746+
}
739747
OKPKEY_SHA256 = "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k"
740748

741749

@@ -809,3 +817,30 @@ def test_key_from_jwk_dict_okp_ed448():
809817
_key = key_from_jwk_dict(jwk)
810818
assert isinstance(_key, OKPKey)
811819
assert _key.has_private_key()
820+
821+
822+
def test_jwk_set():
823+
keyset: set[JWK] = set()
824+
825+
key_a = new_ec_key("P-256", kid="key_a")
826+
key_b = new_ec_key("P-256", kid="key_b")
827+
key_c = new_ec_key("P-256", kid="key_b")
828+
829+
key1 = ECKey(**key_a.serialize())
830+
key2 = ECKey(**key_b.serialize())
831+
key3 = ECKey(**key_a.serialize())
832+
key4 = ECKey(**key_c.serialize())
833+
834+
keyset.add(key1)
835+
keyset.add(key1)
836+
keyset.add(key2)
837+
keyset.add(key2)
838+
assert len(keyset) == 2
839+
840+
keyset.add(key3) # should not add a new item since key1 == key3
841+
assert len(keyset) == 2
842+
843+
assert key1 in keyset
844+
assert key2 in keyset
845+
assert key3 in keyset
846+
assert key4 not in keyset

0 commit comments

Comments
 (0)