Skip to content

Commit

Permalink
Merge pull request #1977 from dbluhm/fix/public-did-mediator-routing-…
Browse files Browse the repository at this point in the history
…keys

fix: public did mediator routing keys as did keys
  • Loading branch information
dbluhm authored Jan 11, 2023
2 parents 7b1c457 + 23a8172 commit 4c20dcd
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 37 deletions.
8 changes: 5 additions & 3 deletions aries_cloudagent/connections/base_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,18 @@ async def resolve_invitation(

endpoint = first_didcomm_service.service_endpoint
recipient_keys: List[VerificationMethod] = [
doc.dereference(url) for url in first_didcomm_service.recipient_keys
await resolver.dereference(self._profile, url, document=doc)
for url in first_didcomm_service.recipient_keys
]
routing_keys: List[VerificationMethod] = [
doc.dereference(url) for url in first_didcomm_service.routing_keys
await resolver.dereference(self._profile, url, document=doc)
for url in first_didcomm_service.routing_keys
]

for key in [*recipient_keys, *routing_keys]:
if not isinstance(key, self.SUPPORTED_KEY_TYPES):
raise BaseConnectionManagerError(
f"Key type {key.type} is not supported"
f"Key type {type(key).__name__} is not supported"
)

return (
Expand Down
2 changes: 0 additions & 2 deletions aries_cloudagent/messaging/jsonld/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from marshmallow import INCLUDE, Schema, fields
from pydid.verification_method import (
Ed25519VerificationKey2018,
KnownVerificationMethods,
)

from ...admin.request_context import AdminRequestContext
Expand Down Expand Up @@ -148,7 +147,6 @@ async def verify(request: web.BaseRequest):
vmethod = await resolver.dereference(
profile,
doc["proof"]["verificationMethod"],
cls=KnownVerificationMethods,
)

if not isinstance(vmethod, SUPPORTED_VERIFICATION_METHOD_TYPES):
Expand Down
26 changes: 13 additions & 13 deletions aries_cloudagent/messaging/jsonld/tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,22 +234,22 @@ async def test_verify_bad_ver_meth_deref_req_error(
assert "error" in mock_response.call_args[0][0]


@pytest.mark.asyncio
async def test_verify_bad_ver_meth_not_ver_meth(
mock_resolver, mock_verify_request, mock_response, request_body
):
request_body["doc"]["proof"][
"verificationMethod"
] = "did:example:1234abcd#did-communication"
await test_module.verify(mock_verify_request(request_body))
assert "error" in mock_response.call_args[0][0]


@pytest.mark.parametrize(
"vmethod",
[
"did:example:1234abcd#key-2",
"did:example:1234abcd#did-communication",
],
)
@pytest.mark.asyncio
async def test_verify_bad_vmethod_unsupported(
mock_resolver, mock_verify_request, mock_response, request_body
mock_resolver,
mock_verify_request,
mock_response,
request_body,
vmethod,
):
request_body["doc"]["proof"]["verificationMethod"] = "did:example:1234abcd#key-2"
request_body["doc"]["proof"]["verificationMethod"] = vmethod
with pytest.raises(web.HTTPBadRequest):
await test_module.verify(mock_verify_request(request_body))

Expand Down
12 changes: 12 additions & 0 deletions aries_cloudagent/protocols/connections/v1_0/tests/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,9 @@ async def test_fetch_connection_targets_conn_invitation_did_resolver(self):
return_value=self.test_endpoint
)
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
self.resolver.dereference = async_mock.CoroutineMock(
return_value=did_doc.verification_method[0]
)
self.context.injector.bind_instance(DIDResolver, self.resolver)

local_did = await session.wallet.create_local_did(
Expand Down Expand Up @@ -2137,6 +2140,9 @@ async def test_fetch_connection_targets_conn_invitation_btcr_resolver(self):
return_value=self.test_endpoint
)
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
self.resolver.dereference = async_mock.CoroutineMock(
return_value=did_doc.verification_method[0]
)
self.context.injector.bind_instance(DIDResolver, self.resolver)
local_did = await session.wallet.create_local_did(
method=SOV,
Expand Down Expand Up @@ -2288,6 +2294,9 @@ async def test_fetch_connection_targets_conn_invitation_unsupported_key_type(sel
return_value=self.test_endpoint
)
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
self.resolver.dereference = async_mock.CoroutineMock(
return_value=did_doc.verification_method[0]
)
self.context.injector.bind_instance(DIDResolver, self.resolver)
local_did = await session.wallet.create_local_did(
method=SOV,
Expand Down Expand Up @@ -2357,6 +2366,9 @@ async def test_fetch_connection_targets_oob_invitation_svc_did_resolver(self):

self.resolver = async_mock.MagicMock()
self.resolver.resolve = async_mock.CoroutineMock(return_value=did_doc)
self.resolver.dereference = async_mock.CoroutineMock(
return_value=did_doc.verification_method[0]
)
self.context.injector.bind_instance(DIDResolver, self.resolver)

local_did = await session.wallet.create_local_did(
Expand Down
1 change: 1 addition & 0 deletions aries_cloudagent/protocols/out_of_band/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ async def create_invitation(
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
public_did = await wallet.get_public_did()

if not public_did:
raise OutOfBandManagerError(
"Cannot create public invitation with no public DID"
Expand Down
27 changes: 24 additions & 3 deletions aries_cloudagent/resolver/default/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from ...config.injection_context import InjectionContext
from ...core.profile import Profile
from ...did.did_key import DIDKey
from ...ledger.endpoint_type import EndpointType
from ...ledger.error import LedgerError
from ...ledger.multiple_ledger.ledger_requests_executor import (
Expand All @@ -19,7 +20,7 @@
)
from ...messaging.valid import IndyDID
from ...multitenant.base import BaseMultitenantManager

from ...wallet.key_type import ED25519
from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType

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


def _routing_keys_as_did_key_urls(routing_keys: Sequence[str]) -> Sequence[str]:
"""Convert raw base58 keys to did:key values.
If a did:key is passed in, convert to a did:key URL.
"""

did_key_urls = []
for routing_key in routing_keys:
if not routing_key.startswith("did:key:"):
did_key_urls.append(DIDKey.from_public_key_b58(routing_key, ED25519).key_id)
else:
if "#" not in routing_key:
did_key_urls.append(
f"{routing_key}#{DIDKey.from_did(routing_key).fingerprint}"
)
else:
return routing_keys
return did_key_urls


class IndyDIDResolver(BaseDIDResolver):
"""Indy DID Resolver."""

Expand Down Expand Up @@ -101,7 +122,7 @@ def add_services(
type_=self.SERVICE_TYPE_DID_COMMUNICATION,
service_endpoint=endpoint,
priority=1,
routing_keys=routing_keys,
routing_keys=_routing_keys_as_did_key_urls(routing_keys),
recipient_keys=[recipient_key.id],
accept=(
service_accept if service_accept else ["didcomm/aip2;env=rfc19"]
Expand All @@ -114,7 +135,7 @@ def add_services(
type_=self.SERVICE_TYPE_DIDCOMM,
service_endpoint=endpoint,
recipient_keys=[recipient_key.id],
routing_keys=routing_keys,
routing_keys=_routing_keys_as_did_key_urls(routing_keys),
# CHECKME
# accept=(service_accept if service_accept else ["didcomm/v2"]),
accept=["didcomm/v2"],
Expand Down
19 changes: 17 additions & 2 deletions aries_cloudagent/resolver/default/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from ....multitenant.manager import MultitenantManager

from ...base import DIDNotFound, ResolverError
from ..indy import IndyDIDResolver
from ..indy import IndyDIDResolver, _routing_keys_as_did_key_urls

# pylint: disable=W0621
TEST_DID0 = "did:sov:WgWxqztrNooG92RXvxSTWv"
Expand Down Expand Up @@ -127,7 +127,7 @@ async def test_supports_updated_did_sov_rules(
"""Test that new attrib structure is supported."""
example = {
"endpoint": "https://example.com/endpoint",
"routingKeys": ["a-routing-key"],
"routingKeys": ["HQhjaj4mcaS3Xci27a9QhnBrNpS91VNFUU4TDrtMxa9j"],
"types": ["DIDComm", "did-communication", "endpoint"],
"profile": "https://example.com",
"linked_domains": "https://example.com",
Expand Down Expand Up @@ -177,3 +177,18 @@ async def test_supports_updated_did_sov_rules_no_endpoint_url(
)
def test_process_endpoint_types(self, resolver: IndyDIDResolver, types, result):
assert resolver.process_endpoint_types(types) == result

@pytest.mark.parametrize(
"keys",
[
["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
["did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA"],
[
"did:key:z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA#z6MkgzZFYHiH9RhyMmkoyvNvVwnvgLxkVrJbureLx9HXsuKA"
],
],
)
def test_routing_keys_as_did_key_urls(self, keys):
for key in _routing_keys_as_did_key_urls(keys):
assert key.startswith("did:key:")
assert "#" in key
31 changes: 18 additions & 13 deletions aries_cloudagent/resolver/did_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
from datetime import datetime
from itertools import chain
import logging
from typing import Optional, List, Sequence, Tuple, Text, Type, TypeVar, Union
from typing import List, Optional, Sequence, Text, Tuple, Union

from pydid import DID, DIDError, DIDUrl, Resource, NonconformantDocument
from pydid.doc.doc import IDNotFoundError
from pydid import DID, DIDError, DIDUrl, Resource
import pydid
from pydid.doc.doc import BaseDIDDocument, IDNotFoundError

from ..core.profile import Profile
from .base import (
Expand All @@ -26,9 +27,6 @@
LOGGER = logging.getLogger(__name__)


ResourceType = TypeVar("ResourceType", bound=Resource)


class DIDResolver:
"""did resolver singleton."""

Expand Down Expand Up @@ -115,8 +113,12 @@ async def _match_did_to_resolver(
return resolvers

async def dereference(
self, profile: Profile, did_url: str, *, cls: Type[ResourceType] = Resource
) -> ResourceType:
self,
profile: Profile,
did_url: str,
*,
document: Optional[BaseDIDDocument] = None,
) -> Resource:
"""Dereference a DID URL to its corresponding DID Doc object."""
# TODO Use cached DID Docs when possible
try:
Expand All @@ -128,12 +130,15 @@ async def dereference(
"Failed to parse DID URL from {}".format(did_url)
) from err

doc_dict = await self.resolve(profile, parsed.did)
# Use non-conformant doc as the "least common denominator"
if document and parsed.did != document.id:
document = None

if not document:
doc_dict = await self.resolve(profile, parsed.did)
document = pydid.deserialize_document(doc_dict)

try:
return NonconformantDocument.deserialize(doc_dict).dereference_as(
cls, parsed
)
return document.dereference(parsed)
except IDNotFoundError as error:
raise ResolverError(
"Failed to dereference DID URL: {}".format(error)
Expand Down
13 changes: 12 additions & 1 deletion aries_cloudagent/resolver/tests/test_did_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest

from asynctest import mock as async_mock
from pydid import DID, DIDDocument, VerificationMethod
from pydid import DID, DIDDocument, VerificationMethod, BasicDIDDocument

from ..base import (
BaseDIDResolver,
Expand Down Expand Up @@ -153,6 +153,17 @@ async def test_dereference(resolver, profile):
assert expected == actual.serialize()


@pytest.mark.asyncio
async def test_dereference_diddoc(resolver, profile):
url = "did:example:1234abcd#4"
doc = BasicDIDDocument(
id="did:example:z6Mkmpe2DyE4NsDiAb58d75hpi1BjqbH6wYMschUkjWDEEuR"
)
result = await resolver.dereference(profile, url, document=doc)
assert isinstance(result, VerificationMethod)
assert result.id == url


@pytest.mark.asyncio
async def test_dereference_x(resolver, profile):
url = "non-did"
Expand Down

0 comments on commit 4c20dcd

Please sign in to comment.