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..f9b1c1f --- /dev/null +++ b/didcomm_messaging/resolver/jwk.py @@ -0,0 +1,69 @@ +"""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[A-Za-z0-9\-_]+)$") + + 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}") + + 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", + "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))