Skip to content

Commit 4c20dcd

Browse files
authored
Merge pull request openwallet-foundation#1977 from dbluhm/fix/public-did-mediator-routing-keys
fix: public did mediator routing keys as did keys
2 parents 7b1c457 + 23a8172 commit 4c20dcd

9 files changed

Lines changed: 102 additions & 37 deletions

File tree

aries_cloudagent/connections/base_manager.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,16 +269,18 @@ async def resolve_invitation(
269269

270270
endpoint = first_didcomm_service.service_endpoint
271271
recipient_keys: List[VerificationMethod] = [
272-
doc.dereference(url) for url in first_didcomm_service.recipient_keys
272+
await resolver.dereference(self._profile, url, document=doc)
273+
for url in first_didcomm_service.recipient_keys
273274
]
274275
routing_keys: List[VerificationMethod] = [
275-
doc.dereference(url) for url in first_didcomm_service.routing_keys
276+
await resolver.dereference(self._profile, url, document=doc)
277+
for url in first_didcomm_service.routing_keys
276278
]
277279

278280
for key in [*recipient_keys, *routing_keys]:
279281
if not isinstance(key, self.SUPPORTED_KEY_TYPES):
280282
raise BaseConnectionManagerError(
281-
f"Key type {key.type} is not supported"
283+
f"Key type {type(key).__name__} is not supported"
282284
)
283285

284286
return (

aries_cloudagent/messaging/jsonld/routes.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from marshmallow import INCLUDE, Schema, fields
66
from pydid.verification_method import (
77
Ed25519VerificationKey2018,
8-
KnownVerificationMethods,
98
)
109

1110
from ...admin.request_context import AdminRequestContext
@@ -148,7 +147,6 @@ async def verify(request: web.BaseRequest):
148147
vmethod = await resolver.dereference(
149148
profile,
150149
doc["proof"]["verificationMethod"],
151-
cls=KnownVerificationMethods,
152150
)
153151

154152
if not isinstance(vmethod, SUPPORTED_VERIFICATION_METHOD_TYPES):

aries_cloudagent/messaging/jsonld/tests/test_routes.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -234,22 +234,22 @@ async def test_verify_bad_ver_meth_deref_req_error(
234234
assert "error" in mock_response.call_args[0][0]
235235

236236

237-
@pytest.mark.asyncio
238-
async def test_verify_bad_ver_meth_not_ver_meth(
239-
mock_resolver, mock_verify_request, mock_response, request_body
240-
):
241-
request_body["doc"]["proof"][
242-
"verificationMethod"
243-
] = "did:example:1234abcd#did-communication"
244-
await test_module.verify(mock_verify_request(request_body))
245-
assert "error" in mock_response.call_args[0][0]
246-
247-
237+
@pytest.mark.parametrize(
238+
"vmethod",
239+
[
240+
"did:example:1234abcd#key-2",
241+
"did:example:1234abcd#did-communication",
242+
],
243+
)
248244
@pytest.mark.asyncio
249245
async def test_verify_bad_vmethod_unsupported(
250-
mock_resolver, mock_verify_request, mock_response, request_body
246+
mock_resolver,
247+
mock_verify_request,
248+
mock_response,
249+
request_body,
250+
vmethod,
251251
):
252-
request_body["doc"]["proof"]["verificationMethod"] = "did:example:1234abcd#key-2"
252+
request_body["doc"]["proof"]["verificationMethod"] = vmethod
253253
with pytest.raises(web.HTTPBadRequest):
254254
await test_module.verify(mock_verify_request(request_body))
255255

aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,9 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self):
20682068
return_value=self.test_endpoint
20692069
)
20702070
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
2071+
self.resolver.dereference = async_mock.CoroutineMock(
2072+
return_value=did_doc.verification_method[0]
2073+
)
20712074
self.context.injector.bind_instance(DIDResolver, self.resolver)
20722075

20732076
local_did = await session.wallet.create_local_did(
@@ -2137,6 +2140,9 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self):
21372140
return_value=self.test_endpoint
21382141
)
21392142
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
2143+
self.resolver.dereference = async_mock.CoroutineMock(
2144+
return_value=did_doc.verification_method[0]
2145+
)
21402146
self.context.injector.bind_instance(DIDResolver, self.resolver)
21412147
local_did = await session.wallet.create_local_did(
21422148
method=SOV,
@@ -2288,6 +2294,9 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel
22882294
return_value=self.test_endpoint
22892295
)
22902296
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
2297+
self.resolver.dereference = async_mock.CoroutineMock(
2298+
return_value=did_doc.verification_method[0]
2299+
)
22912300
self.context.injector.bind_instance(DIDResolver, self.resolver)
22922301
local_did = await session.wallet.create_local_did(
22932302
method=SOV,
@@ -2357,6 +2366,9 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self):
23572366

23582367
self.resolver = async_mock.MagicMock()
23592368
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
2369+
self.resolver.dereference = async_mock.CoroutineMock(
2370+
return_value=did_doc.verification_method[0]
2371+
)
23602372
self.context.injector.bind_instance(DIDResolver, self.resolver)
23612373

23622374
local_did = await session.wallet.create_local_did(

aries_cloudagent/protocols/out_of_band/v1_0/manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ async def create_invitation(
223223
async with self.profile.session() as session:
224224
wallet = session.inject(BaseWallet)
225225
public_did = await wallet.get_public_did()
226+
226227
if not public_did:
227228
raise OutOfBandManagerError(
228229
"Cannot create public invitation with no public DID"

aries_cloudagent/resolver/default/indy.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from ...config.injection_context import InjectionContext
1313
from ...core.profile import Profile
14+
from ...did.did_key import DIDKey
1415
from ...ledger.endpoint_type import EndpointType
1516
from ...ledger.error import LedgerError
1617
from ...ledger.multiple_ledger.ledger_requests_executor import (
@@ -19,7 +20,7 @@
1920
)
2021
from ...messaging.valid import IndyDID
2122
from ...multitenant.base import BaseMultitenantManager
22-
23+
from ...wallet.key_type import ED25519
2324
from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType
2425

2526
LOGGER = logging.getLogger(__name__)
@@ -29,6 +30,26 @@ class NoIndyLedger(ResolverError):
2930
"""Raised when there is no Indy ledger instance configured."""
3031

3132

33+
def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]:
34+
"""Convert raw base58 keys to did:key values.
35+
36+
If a did:key is passed in, convert to a did:key URL.
37+
"""
38+
39+
did_key_urls = []
40+
for routing_key in routing_keys:
41+
if not routing_key.startswith("did:key:"):
42+
did_key_urls.append(DIDKey.from_public_key_b58(routing_key, ED25519).key_id)
43+
else:
44+
if "#" not in routing_key:
45+
did_key_urls.append(
46+
f"{routing_key}#{DIDKey.from_did(routing_key).fingerprint}"
47+
)
48+
else:
49+
return routing_keys
50+
return did_key_urls
51+
52+
3253
class IndyDIDResolver(BaseDIDResolver):
3354
"""Indy DID Resolver."""
3455

@@ -101,7 +122,7 @@ def add_services(
101122
type_=self.SERVICE_TYPE_DID_COMMUNICATION,
102123
service_endpoint=endpoint,
103124
priority=1,
104-
routing_keys=routing_keys,
125+
routing_keys=_routing_keys_as_did_key_urls(routing_keys),
105126
recipient_keys=[recipient_key.id],
106127
accept=(
107128
service_accept if service_accept else ["didcomm/aip2;env=rfc19"]
@@ -114,7 +135,7 @@ def add_services(
114135
type_=self.SERVICE_TYPE_DIDCOMM,
115136
service_endpoint=endpoint,
116137
recipient_keys=[recipient_key.id],
117-
routing_keys=routing_keys,
138+
routing_keys=_routing_keys_as_did_key_urls(routing_keys),
118139
# CHECKME
119140
# accept=(service_accept if service_accept else ["didcomm/v2"]),
120141
accept=["didcomm/v2"],

aries_cloudagent/resolver/default/tests/test_indy.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from ....multitenant.manager import MultitenantManager
1818

1919
from ...base import DIDNotFound, ResolverError
20-
from ..indy import IndyDIDResolver
20+
from ..indy import IndyDIDResolver, _routing_keys_as_did_key_urls
2121

2222
# pylint: disable=W0621
2323
TEST_DID0 = "did:sov:WgWxqztrNooG92RXvxSTWv"
@@ -127,7 +127,7 @@ async def test_supports_updated_did_sov_rules(
127127
"""Test that new attrib structure is supported."""
128128
example = {
129129
"endpoint": "https://example.com/endpoint",
130-
"routingKeys": ["a-routing-key"],
130+
"routingKeys": ["HQhjaj4mcaS3Xci27a9QhnBrNpS91VNFUU4TDrtMxa9j"],
131131
"types": ["DIDComm", "did-communication", "endpoint"],
132132
"profile": "https://example.com",
133133
"linked_domains": "https://example.com",
@@ -177,3 +177,18 @@ async def test_supports_updated_did_sov_rules_no_endpoint_url(
177177
)
178178
def test_process_endpoint_types(self, resolver: IndyDIDResolver, types, result):
179179
assert resolver.process_endpoint_types(types) == result
180+
181+
@pytest.mark.parametrize(
182+
"keys",
183+
[
184+
["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
185+
["did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA"],
186+
[
187+
"did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA#z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA"
188+
],
189+
],
190+
)
191+
def test_routing_keys_as_did_key_urls(self, keys):
192+
for key in _routing_keys_as_did_key_urls(keys):
193+
assert key.startswith("did:key:")
194+
assert "#" in key

aries_cloudagent/resolver/did_resolver.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
from datetime import datetime
99
from itertools import chain
1010
import logging
11-
from typing import Optional, List, Sequence, Tuple, Text, Type, TypeVar, Union
11+
from typing import List, Optional, Sequence, Text, Tuple, Union
1212

13-
from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument
14-
from pydid.doc.doc import IDNotFoundError
13+
from pydid import DID, DIDError, DIDUrl, Resource
14+
import pydid
15+
from pydid.doc.doc import BaseDIDDocument, IDNotFoundError
1516

1617
from ..core.profile import Profile
1718
from .base import (
@@ -26,9 +27,6 @@
2627
LOGGER = logging.getLogger(__name__)
2728

2829

29-
ResourceType = TypeVar("ResourceType", bound=Resource)
30-
31-
3230
class DIDResolver:
3331
"""did resolver singleton."""
3432

@@ -115,8 +113,12 @@ async def _match_did_to_resolver(
115113
return resolvers
116114

117115
async def dereference(
118-
self, profile: Profile, did_url: str, *, cls: Type[ResourceType] = Resource
119-
) -> ResourceType:
116+
self,
117+
profile: Profile,
118+
did_url: str,
119+
*,
120+
document: Optional[BaseDIDDocument] = None,
121+
) -> Resource:
120122
"""Dereference a DID URL to its corresponding DID Doc object."""
121123
# TODO Use cached DID Docs when possible
122124
try:
@@ -128,12 +130,15 @@ async def dereference(
128130
"Failed to parse DID URL from {}".format(did_url)
129131
) from err
130132

131-
doc_dict = await self.resolve(profile, parsed.did)
132-
# Use non-conformant doc as the "least common denominator"
133+
if document and parsed.did != document.id:
134+
document = None
135+
136+
if not document:
137+
doc_dict = await self.resolve(profile, parsed.did)
138+
document = pydid.deserialize_document(doc_dict)
139+
133140
try:
134-
return NonconformantDocument.deserialize(doc_dict).dereference_as(
135-
cls, parsed
136-
)
141+
return document.dereference(parsed)
137142
except IDNotFoundError as error:
138143
raise ResolverError(
139144
"Failed to dereference DID URL: {}".format(error)

aries_cloudagent/resolver/tests/test_did_resolver.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pytest
88

99
from asynctest import mock as async_mock
10-
from pydid import DID, DIDDocument, VerificationMethod
10+
from pydid import DID, DIDDocument, VerificationMethod, BasicDIDDocument
1111

1212
from ..base import (
1313
BaseDIDResolver,
@@ -153,6 +153,17 @@ async def test_dereference(resolver, profile):
153153
assert expected == actual.serialize()
154154

155155

156+
@pytest.mark.asyncio
157+
async def test_dereference_diddoc(resolver, profile):
158+
url = "did:example:1234abcd#4"
159+
doc = BasicDIDDocument(
160+
id="did:example:z6Mkmpe2DyE4NsDiAb58d75hpi1BjqbH6wYMschUkjWDEEuR"
161+
)
162+
result = await resolver.dereference(profile, url, document=doc)
163+
assert isinstance(result, VerificationMethod)
164+
assert result.id == url
165+
166+
156167
@pytest.mark.asyncio
157168
async def test_dereference_x(resolver, profile):
158169
url = "non-did"

0 commit comments

Comments
 (0)