diff --git a/aries_cloudagent/resolver/default/legacy_peer.py b/aries_cloudagent/resolver/default/legacy_peer.py index 238d92702d..bb94ed4468 100644 --- a/aries_cloudagent/resolver/default/legacy_peer.py +++ b/aries_cloudagent/resolver/default/legacy_peer.py @@ -6,7 +6,7 @@ from copy import deepcopy from dataclasses import asdict, dataclass import logging -from typing import Optional, Sequence, Text, Union +from typing import List, Optional, Sequence, Text, Union from pydid import DID @@ -118,7 +118,8 @@ def didcomm_services_use_updated_conventions(value: dict) -> dict: for service in value["service"]: if "type" in service and service["type"] == "IndyAgent": service["type"] = "did-communication" - service["id"] = service["id"].replace(";indy", "#didcomm") + if ";" in service["id"]: + service["id"] = value["id"] + "#didcomm" return value @staticmethod @@ -139,6 +140,74 @@ def didcomm_services_recip_keys_are_refs_routing_keys_are_did_key( ] return value + @staticmethod + def qualified(did_or_did_url: str) -> str: + """Make sure DID or DID URL is fully qualified.""" + if not did_or_did_url.startswith("did:"): + return f"did:sov:{did_or_did_url}" + return did_or_did_url + + @classmethod + def fully_qualified_ids_and_controllers(cls, value: dict) -> dict: + """Make sure IDs and controllers are fully qualified.""" + + def _make_qualified(value: dict) -> dict: + if "id" in value: + ident = value["id"] + value["id"] = cls.qualified(ident) + if "controller" in value: + controller = value["controller"] + value["controller"] = cls.qualified(controller) + return value + + value = _make_qualified(value) + vms = [] + for verification_method in value.get("verificationMethod", []): + vms.append(_make_qualified(verification_method)) + + services = [] + for service in value.get("service", []): + services.append(_make_qualified(service)) + + auths = [] + for authn in value.get("authentication", []): + if isinstance(authn, dict): + auths.append(_make_qualified(authn)) + elif isinstance(authn, str): + auths.append(cls.qualified(authn)) + else: + raise ValueError("Unexpected authentication value type") + + value["authentication"] = auths + value["verificationMethod"] = vms + value["service"] = services + return value + + @staticmethod + def remove_verification_method( + vms: List[dict], public_key_base58: str + ) -> List[dict]: + """Remove the verification method with the given key.""" + return [vm for vm in vms if vm["publicKeyBase58"] != public_key_base58] + + @classmethod + def remove_routing_keys_from_verification_method(cls, value: dict) -> dict: + """Remove routing keys from verification methods. + + This was an old convention; routing keys were added to the public keys + of the doc even though they're usually not owned by the doc sender. + + This correction should be applied before turning the routing keys into + did keys. + """ + vms = value.get("verificationMethod", []) + for service in value.get("service", []): + if "routingKeys" in service: + for routing_key in service["routingKeys"]: + vms = cls.remove_verification_method(vms, routing_key) + value["verificationMethod"] = vms + return value + @classmethod def apply(cls, value: dict) -> dict: """Apply all corrections to the given DID document.""" @@ -146,7 +215,9 @@ def apply(cls, value: dict) -> dict: for correction in ( cls.public_key_is_verification_method, cls.authentication_is_list_of_verification_methods_and_refs, + cls.fully_qualified_ids_and_controllers, cls.didcomm_services_use_updated_conventions, + cls.remove_routing_keys_from_verification_method, cls.didcomm_services_recip_keys_are_refs_routing_keys_are_did_key, ): value = correction(value) diff --git a/aries_cloudagent/resolver/default/tests/test_legacy_peer.py b/aries_cloudagent/resolver/default/tests/test_legacy_peer.py index 7e3a7c8ff7..71683f41fb 100644 --- a/aries_cloudagent/resolver/default/tests/test_legacy_peer.py +++ b/aries_cloudagent/resolver/default/tests/test_legacy_peer.py @@ -179,3 +179,125 @@ def test_corrections_examples(self): actual = test_module.LegacyDocCorrections.apply(input_doc) assert actual == expected assert expected == test_module.LegacyDocCorrections.apply(expected) + + def test_corrections_bifold(self): + input_doc = { + "@context": "https://w3id.org/did/v1", + "publicKey": [ + { + "id": "PkWfCgY4SSAYeSoaWx3RFP#1", + "controller": "PkWfCgY4SSAYeSoaWx3RFP", + "type": "Ed25519VerificationKey2018", + "publicKeyBase58": "DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K", + } + ], + "service": [ + { + "id": "PkWfCgY4SSAYeSoaWx3RFP#IndyAgentService", + "serviceEndpoint": "https://aries-mediator-agent.vonx.io", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K"], + "routingKeys": ["cK7fwfjpakMuv8QKVv2y6qouZddVw4TxZNQPUs2fFTd"], + } + ], + "authentication": [ + { + "publicKey": "PkWfCgY4SSAYeSoaWx3RFP#1", + "type": "Ed25519SignatureAuthentication2018", + } + ], + "id": "PkWfCgY4SSAYeSoaWx3RFP", + } + expected = { + "@context": "https://w3id.org/did/v1", + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "verificationMethod": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "publicKeyBase58": "DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K", + } + ], + "authentication": ["did:sov:PkWfCgY4SSAYeSoaWx3RFP#1"], + "service": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#IndyAgentService", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:sov:PkWfCgY4SSAYeSoaWx3RFP#1"], + "routingKeys": [ + "did:key:z6Mkf4aAGBvBA8Eq2Qy714sspCPoj8uUupJpeaHLDkq3aUF1#z6Mkf4aAGBvBA8Eq2Qy714sspCPoj8uUupJpeaHLDkq3aUF1" + ], + "serviceEndpoint": "https://aries-mediator-agent.vonx.io", + } + ], + } + actual = test_module.LegacyDocCorrections.apply(input_doc) + assert actual == expected + assert expected == test_module.LegacyDocCorrections.apply(expected) + + def test_corrections_stored(self): + input_doc = { + "@context": "https://w3id.org/did/v1", + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "publicKey": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "publicKeyBase58": "DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K", + }, + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#QPUs2fFT", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "publicKeyBase58": "cK7fwfjpakMuv8QKVv2y6qouZddVw4TxZNQPUs2fFTd", + }, + ], + "authentication": [ + { + "type": "Ed25519SignatureAuthentication2018", + "publicKey": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#1", + } + ], + "service": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP;PkWfCgY4SSAYeSoaWx3RFP#IndyAgentService", + "type": "IndyAgent", + "priority": 0, + "recipientKeys": ["DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K"], + "routingKeys": ["cK7fwfjpakMuv8QKVv2y6qouZddVw4TxZNQPUs2fFTd"], + "serviceEndpoint": "https://aries-mediator-agent.vonx.io", + } + ], + } + expected = { + "@context": "https://w3id.org/did/v1", + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "verificationMethod": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#1", + "type": "Ed25519VerificationKey2018", + "controller": "did:sov:PkWfCgY4SSAYeSoaWx3RFP", + "publicKeyBase58": "DQBMbzLmAK5iyiuPnqNvfkx3CQ2iTJ2sKz98utvdET4K", + } + ], + "authentication": ["did:sov:PkWfCgY4SSAYeSoaWx3RFP#1"], + "service": [ + { + "id": "did:sov:PkWfCgY4SSAYeSoaWx3RFP#didcomm", + "type": "did-communication", + "priority": 0, + "recipientKeys": ["did:sov:PkWfCgY4SSAYeSoaWx3RFP#1"], + "routingKeys": [ + "did:key:z6Mkf4aAGBvBA8Eq2Qy714sspCPoj8uUupJpeaHLDkq3aUF1#z6Mkf4aAGBvBA8Eq2Qy714sspCPoj8uUupJpeaHLDkq3aUF1" + ], + "serviceEndpoint": "https://aries-mediator-agent.vonx.io", + } + ], + } + actual = test_module.LegacyDocCorrections.apply(input_doc) + assert actual == expected + assert expected == test_module.LegacyDocCorrections.apply(expected)