diff --git a/README.md b/README.md index 7157a90..6278418 100644 --- a/README.md +++ b/README.md @@ -49,19 +49,18 @@ did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrG { "@context": [ "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/suites/ed25519-2020/v1", - "https://w3id.org/security/suites/x25519-2020/v1" + "https://w3id.org/security/multikey/v1" ], "id": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ", "verificationMethod": [ { - "type": "Ed25519VerificationKey2020", + "type": "Multikey", "id": "#key-1", "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ", "publicKeyMultibase": "z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc" }, { - "type": "X25519KeyAgreementKey2020", + "type": "Multikey", "id": "#key-2", "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ", "publicKeyMultibase": "z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR" @@ -87,6 +86,9 @@ did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrG }, "id": "#service" } + ], + "alsoKnownAs": [ + "did:peer:3zQmbRvRJgKBuubq8T9VBjrDPTmjb2Ed91f89ekW4gr6aZxa" ] } >>> # Let's derive the did:peer:3 @@ -117,7 +119,7 @@ service = 1*B64URL When generating a `did:peer:2`, take as inputs a set of keys and their purpose. Each key's purpose corresponds to the [Verification Relationship](https://www.w3.org/TR/did-core/#verification-relationships) it will hold in the DID Document generated from the DID. -Abstractly, these inputs may look like the following: +Abstractly, these inputs may look like the following (in this example, the keys are already multibase, multicodec encoded values): ```json [ @@ -134,7 +136,7 @@ Abstractly, these inputs may look like the following: To encode these keys: -* Construct a multibase, multicodec form of each public key to be included. +* Construct a [multibase](https://github.com/multiformats/multibase), [multicodec](https://github.com/multiformats/multicodec) form of each public key to be included. * Prefix each encoded key with a period character (.) and single character from the purpose codes table below. * Concatenate the prefixed encoded keys. The inputs above will result in: ``` @@ -236,13 +238,6 @@ did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrG | **S** | Service | -#### Multicodec Prefix Name to Verification Method Type (clarified) - -| Multicodec Prefix Name | Verification Method Type | -|------------------------------------|----------------------------| -| ed25519-pub | Ed25519VerificationKey2020 | -| x25519-pub | X25519KeyAgreementKey2020 | - ### Resolving a `did:peer:2` > [!NOTE] @@ -252,27 +247,19 @@ did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrG When Resolving the peer DID into a DID Document, the process is reversed: -* Start with an empty document with the DID Core context (clarified): +* Start with an empty document with the DID Core context and [Multikey context](https://www.w3.org/TR/vc-data-integrity/#multikey) (clarified): ```json { - "context": ["https://www.w3.org/ns/did/v1"] + "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/multikey/v1"] } ``` - * If any of the keys in the DID are ed25519, add the following context: - ``` - https://w3id.org/security/suites/ed25519-2020/v1 - ``` - * If any of the keys in the DID are x25519, add the following context: - ``` - https://w3id.org/security/suites/x25519-2020/v1 - ``` * Set the `id` of the document to the DID being resolved. * Optionally, set the `alsoKnownAs` to the `did:peer:3` value corresponding to the DID being resolved. * Split the DID string into elements. * For each element with a purpose corresponding to a key, transform keys into verification methods in the DID Document: * Remove the period (.) and the Purpose prefix. Consider the remaining string the "encoded key." * Create an empty object. Consider this value the "verification method." - * Set the `type` of the verification method according to the multicodec prefix using the lookup table above (clarified). + * Set the `type` of the verification method to [`Multikey`](https://www.w3.org/TR/vc-data-integrity/#multikey) (clarified). * Set the `id` of the verification method to `#key-N` where `N` is an incrementing number starting at `1` (clarified). The keys MUST be processed in the order they appear in the DID string. For example, if you have the DID (whitespace added for example only): ``` did:peer:2 @@ -308,56 +295,48 @@ When Resolving the peer DID into a DID Document, the process is reversed: ```json { - "@context": "https://www.w3.org/ns/did/v1", + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/multikey/v1", + ], "id": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", "verificationMethod": [ - { - "id": "#key-1", - "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", - "type": "Ed25519VerificationKey2020", - "publicKeyMultibase": "z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc" - }, - { - "id": "#key-2", - "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", - "type": "X25519KeyAgreementKey2020", - "publicKeyMultibase": "z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR" - } - ], - "authentication": [ - "#key-1" - ], - "keyAgreement": [ - "#key-2" + { + "id": "#key-1", + "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", + "type": "Multikey", + "publicKeyMultibase": "z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc", + }, + { + "id": "#key-2", + "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", + "type": "Multikey", + "publicKeyMultibase": "z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR", + }, ], + "authentication": ["#key-1"], + "keyAgreement": ["#key-2"], "service": [ { "type": "DIDCommMessaging", "serviceEndpoint": { "uri": "http://example.com/didcomm", - "accept": [ - "didcomm/v2" - ], - "routingKeys": [ - "did:example:123456789abcdefghi#key-1" - ] + "accept": ["didcomm/v2"], + "routingKeys": ["did:example:123456789abcdefghi#key-1"], }, - "id": "#service" + "id": "#service", }, { "type": "DIDCommMessaging", "serviceEndpoint": { "uri": "http://example.com/another", - "accept": [ - "didcomm/v2" - ], - "routingKeys": [ - "did:example:123456789abcdefghi#key-2" - ] + "accept": ["didcomm/v2"], + "routingKeys": ["did:example:123456789abcdefghi#key-2"], }, - "id": "#service-1" - } - ] + "id": "#service-1", + }, + ], + "alsoKnownAs": ["did:peer:3zQmd6RdU6e2nDrLn1rjwdA5Buzq7wJwsv3WJ1AgrwKYJoLE"], } ``` diff --git a/did_peer_2.py b/did_peer_2.py index 39fd3cb..10e0425 100644 --- a/did_peer_2.py +++ b/did_peer_2.py @@ -140,7 +140,7 @@ def decode_service(self, data: str) -> Dict[str, Any]: ) -MultibaseEncodedKey = str +MultikeyEncodedKey = str @dataclass @@ -148,7 +148,7 @@ class KeySpec: """Key specification for did:peer:2.""" purpose: PurposeCode - material: MultibaseEncodedKey + material: MultikeyEncodedKey @classmethod def assertion(cls, material: str) -> "KeySpec": @@ -185,24 +185,6 @@ def capability_delegation(cls, material: str) -> "KeySpec": """Create a key spec for capability delegation purposes.""" return cls(PurposeCode.capability_delegation, material) - @property - def vm_type(self) -> str: - """Determine the vm type from the multibase encoded key.""" - if self.material.startswith("z6Mk"): - return "Ed25519VerificationKey2020" - if self.material.startswith("z6LS"): - return "X25519KeyAgreementKey2020" - raise ValueError(f"Unsupported key type: {self.material}") - - @property - def required_contexts(self) -> List[str]: - """Determine the required context from the multibase encoded key.""" - if self.material.startswith("z6Mk"): - return ["https://w3id.org/security/suites/ed25519-2020/v1"] - if self.material.startswith("z6LS"): - return ["https://w3id.org/security/suites/x25519-2020/v1"] - raise ValueError(f"Unsupported key type: {self.material}") - def generate(keys: Sequence[KeySpec], services: Sequence[Dict[str, Any]]): """Generate a did:peer:2 DID from keys and services. @@ -228,7 +210,10 @@ def resolve(did: str) -> Dict[str, Any]: raise ValueError(f"Invalid did:peer:2: {did}") document = {} - document["@context"] = ["https://www.w3.org/ns/did/v1"] + document["@context"] = [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/multikey/v1", + ] document["id"] = did elements = did.split(".")[1:] @@ -243,10 +228,9 @@ def resolve(did: str) -> Dict[str, Any]: assert purpose == PurposeCode.service services.append(service_encoder.decode_service(element[1:])) - additional_contexts = set() for index, key in enumerate(keys, start=1): verification_method = { - "type": key.vm_type, + "type": "Multikey", "id": f"#key-{index}", "controller": did, "publicKeyMultibase": key.material, @@ -255,9 +239,6 @@ def resolve(did: str) -> Dict[str, Any]: document.setdefault(key.purpose.verification_relationship, []).append( f"#key-{index}" ) - additional_contexts.update(key.required_contexts) - - document["@context"].extend(sorted(additional_contexts)) unidentified_index = 0 for service in services: @@ -269,6 +250,8 @@ def resolve(did: str) -> Dict[str, Any]: unidentified_index += 1 document.setdefault("service", []).append(service) + document["alsoKnownAs"] = [peer2to3(did)] + return document diff --git a/tests/test_did_peer_2.py b/tests/test_did_peer_2.py index a48c072..cd77929 100644 --- a/tests/test_did_peer_2.py +++ b/tests/test_did_peer_2.py @@ -37,21 +37,20 @@ def test_generate_resolve(): assert resolved == { "@context": [ "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/suites/ed25519-2020/v1", - "https://w3id.org/security/suites/x25519-2020/v1", + "https://w3id.org/security/multikey/v1", ], "id": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", "verificationMethod": [ { "id": "#key-1", "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", - "type": "Ed25519VerificationKey2020", + "type": "Multikey", "publicKeyMultibase": "z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc", }, { "id": "#key-2", "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0xIl19fQ.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly9leGFtcGxlLmNvbS9hbm90aGVyIiwiYSI6WyJkaWRjb21tL3YyIl0sInIiOlsiZGlkOmV4YW1wbGU6MTIzNDU2Nzg5YWJjZGVmZ2hpI2tleS0yIl19fQ", - "type": "X25519KeyAgreementKey2020", + "type": "Multikey", "publicKeyMultibase": "z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR", }, ], @@ -77,6 +76,7 @@ def test_generate_resolve(): "id": "#service-1", }, ], + "alsoKnownAs": ["did:peer:3zQmd6RdU6e2nDrLn1rjwdA5Buzq7wJwsv3WJ1AgrwKYJoLE"], }