From 00371a2f5cb58583e40bea533b936fa9d0163f9c Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Fri, 19 Jul 2024 11:55:30 -0400 Subject: [PATCH 1/2] feat: add did jwk resolver and support for p256 keys Signed-off-by: Daniel Bluhm --- didcomm_messaging/crypto/backend/askar.py | 1 + didcomm_messaging/multiformats/multicodec.py | 2 + didcomm_messaging/resolver/jwk.py | 59 ++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 didcomm_messaging/resolver/jwk.py diff --git a/didcomm_messaging/crypto/backend/askar.py b/didcomm_messaging/crypto/backend/askar.py index df5b24e..333d2ae 100644 --- a/didcomm_messaging/crypto/backend/askar.py +++ b/didcomm_messaging/crypto/backend/askar.py @@ -30,6 +30,7 @@ class AskarKey(PublicKey): "ed25519-pub": KeyAlg.ED25519, "x25519-pub": KeyAlg.X25519, "secp256k1-pub": KeyAlg.K256, + "p256-pub": KeyAlg.P256, } alg_to_codec = {v: k for k, v in codec_to_alg.items()} diff --git a/didcomm_messaging/multiformats/multicodec.py b/didcomm_messaging/multiformats/multicodec.py index 2ce785e..48e214c 100644 --- a/didcomm_messaging/multiformats/multicodec.py +++ b/didcomm_messaging/multiformats/multicodec.py @@ -22,6 +22,7 @@ class SupportedCodecs(Enum): bls12381g2 = Multicodec("bls12_381-g2-pub", b"\xeb\x01") bls12381g1g2 = Multicodec("bls12_381-g1g2-pub", b"\xee\x01") secp256k1_pub = Multicodec("secp256k1-pub", b"\xe7\x01") + p256_pub = Multicodec("p256-pub", b"\x12\x00") @classmethod def by_name(cls, name: str) -> Multicodec: @@ -49,6 +50,7 @@ def for_data(cls, data: bytes) -> Multicodec: "bls12_381-g2-pub", "bls12_381-g1g2-pub", "secp256k1-pub", + "p256-pub", ] diff --git a/didcomm_messaging/resolver/jwk.py b/didcomm_messaging/resolver/jwk.py new file mode 100644 index 0000000..1ec5600 --- /dev/null +++ b/didcomm_messaging/resolver/jwk.py @@ -0,0 +1,59 @@ +"""did:jwk Resolver.""" + +import re +import json + +from didcomm_messaging import DIDResolver +from didcomm_messaging.resolver import DIDResolutionError +from didcomm_messaging.multiformats.multibase import Base64UrlEncoder + +b64 = Base64UrlEncoder() + + +class JWKResolver(DIDResolver): + """Resolve did:jwk.""" + + PATTERN = re.compile(r"^did:jwk:(?P.*)$") + + async def resolve(self, did: str) -> dict: + """Resolve a did:jwk.""" + if match := self.PATTERN.match(did): + encoded = match.group("did") + else: + raise DIDResolutionError(f"Invalid DID: {did}") + + jwk = json.loads(b64.decode(encoded)) + doc = { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/jws-2020/v1", + ], + "id": f"did:jwk:{encoded}", + "verificationMethod": [ + { + "id": f"did:jwk:{encoded}#0", + "type": "JsonWebKey2020", + "controller": f"did:jwk:{encoded}", + "publicKeyJwk": jwk, + } + ], + } + + use = jwk.get("use") + if use == "sig": + doc.update( + { + "assertionMethod": [f"did:jwk:{encoded}#0"], + "authentication": [f"did:jwk:{encoded}#0"], + "capabilityInvocation": [f"did:jwk:{encoded}#0"], + "capabilityDelegation": [f"did:jwk:{encoded}#0"], + } + ) + elif use == "enc": + doc.update({"keyAgreement": [f"did:jwk:{encoded}#0"]}) + + return doc + + async def is_resolvable(self, did: str) -> bool: + """Return if did is resolvable by this resolver.""" + return bool(self.PATTERN.match(did)) From b28b7fd23265156f1eaf5710f600da9cc247fa81 Mon Sep 17 00:00:00 2001 From: Daniel Bluhm Date: Mon, 12 Aug 2024 12:42:19 -0400 Subject: [PATCH 2/2] feat: jwk validation Signed-off-by: Daniel Bluhm --- didcomm_messaging/resolver/jwk.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/didcomm_messaging/resolver/jwk.py b/didcomm_messaging/resolver/jwk.py index 1ec5600..f9b1c1f 100644 --- a/didcomm_messaging/resolver/jwk.py +++ b/didcomm_messaging/resolver/jwk.py @@ -13,7 +13,7 @@ class JWKResolver(DIDResolver): """Resolve did:jwk.""" - PATTERN = re.compile(r"^did:jwk:(?P.*)$") + PATTERN = re.compile(r"^did:jwk:(?P[A-Za-z0-9\-_]+)$") async def resolve(self, did: str) -> dict: """Resolve a did:jwk.""" @@ -22,7 +22,17 @@ async def resolve(self, did: str) -> dict: else: raise DIDResolutionError(f"Invalid DID: {did}") - jwk = json.loads(b64.decode(encoded)) + try: + jwk = json.loads(b64.decode(encoded)) + except json.JSONDecodeError: + raise DIDResolutionError("Invalid JWK") + + if not isinstance(jwk, dict): + raise DIDResolutionError("Invalid JWK") + + if "kty" not in jwk: + raise DIDResolutionError("Invalid JWK") + doc = { "@context": [ "https://www.w3.org/ns/did/v1",