diff --git a/spec.md b/spec.md index 17b42ed8..ae55f6f7 100644 --- a/spec.md +++ b/spec.md @@ -3,7 +3,9 @@ # Introduction A [DID Method](https://www.w3.org/TR/did-core/) based on [Pkarr](https://github.com/nuhvi/pkarr), identified by `did:dht`. -Pkarr stands for **P**ublic **K**ey **A**ddressable **R**esource **R**ecords. Pkarr makes use of the [Mainline DHT](https://en.wikipedia.org/wiki/Mainline_DHT), specifically [BEP44](https://www.bittorrent.org/beps/bep_0044.html) to store records. This DID method uses Pkarr to store _DID Documents_. +Pkarr stands for **P**ublic **K**ey **A**ddressable **R**esource **R**ecords. Pkarr makes use of the +[Mainline DHT](https://en.wikipedia.org/wiki/Mainline_DHT), specifically [BEP44](https://www.bittorrent.org/beps/bep_0044.html) +to store records. This DID method uses Pkarr to store _DID Documents_. As Pkarr states, mainline is chosen for the following reasons (paraphased): @@ -14,7 +16,9 @@ As Pkarr states, mainline is chosen for the following reasons (paraphased): # Format -The format for `did:dht` conforms to the [DID Core](https://www.w3.org/TR/did-core/) specification. It consists of the `did:dht` prefix followed by the Pkarr identifier. The Pkarr identifier is a [z-base-32](https://en.wikipedia.org/wiki/Base32#z-base-32) encoded [Ed25519](https://ed25519.cr.yp.to/) public key. +The format for `did:dht` conforms to the [DID Core](https://www.w3.org/TR/did-core/) specification. It consists of the +`did:dht` prefix followed by the Pkarr identifier. The Pkarr identifier is a [z-base-32](https://en.wikipedia.org/wiki/Base32#z-base-32) +encoded [Ed25519](https://ed25519.cr.yp.to/) public key. ``` did-dht-format := did:dht: @@ -29,74 +33,87 @@ did-dht-format := did:dht:z-base-32(raw-public-key-bytes) # Operations -Entries to the DHT require a signed record. As such, the keypair that is genererated for the pkarr identifier is used to sign the DHT record. This keypair **MUST** always be present in a `did:pk` document and is referred to as the _identifier key_. +Entries to the DHT require a signed record. As such, the keypair that is generated for the pkarr identifier is used to +sign the DHT record. This keypair **MUST** always be present in a `did:dht` document and is referred to as the _identifier key_. ## Create To create a `did:dht`, the process is as follows: -1. Create an [Ed25519](https://ed25519.cr.yp.to/) keypair and encode the public key using the format provided in the [prior section](#Format). +1. Create an [Ed25519](https://ed25519.cr.yp.to/) keypair and encode the public key using the format provided in +the [prior section](#Format). 2. Construct a compliant JSON representation of a DID Document + a. The document **MUST** include a [verification method](https://www.w3.org/TR/did-core/#verification-methods) with + the _identifier key_ encoded as a `publicKeyJwk` with an `id` of ``#0`` and `type` of + [`JsonWebKey2020`](https://www.w3.org/community/reports/credentials/CG-FINAL-lds-jws2020-20220721/#json-web-key-2020). + b. The document can include any number of other [core properties](https://www.w3.org/TR/did-core/#core-properties); + always representing key material as a [JWK](https://datatracker.ietf.org/doc/html/rfc7517). - a. The document **MUST** include a [verification method](https://www.w3.org/TR/did-core/#verification-methods) with the _identifier key_ encoded as a `publicKeyJwk` with an `id` of ``#0`` and `type` of [`JsonWebKey2020`](https://www.w3.org/community/reports/credentials/CG-FINAL-lds-jws2020-20220721/#json-web-key-2020). - - b. The document can include any number of other [core properties](https://www.w3.org/TR/did-core/#core-properties); always representing key material as a [JWK](https://datatracker.ietf.org/doc/html/rfc7517). - 3. Map the output DID Document to a DNS packet as outlined in the [section below](#DNS-Packet-DID-document). -4. Construct a [BEP44 put message](https://www.bittorrent.org/beps/bep_0044.html) with the `v` value as a DNS packet from the previous step. +4. Construct a [BEP44 put message](https://www.bittorrent.org/beps/bep_0044.html) with the `v` value as a DNS packet +from the previous step. 5. Submit the result of (3) to the DHT, a Pkarr, or DID DHT service. - ### DIDs as a DNS Packet - | name | type | TTL | rdata | - |----------------|-----------|-----|-------------------------------------------| - | _did | TXT | - | id=o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy | - | _key1._did | TXT | - | t=0,k= | - | _key2._did | TXT | - | t=1,k= | - | _key3._did | TXT | - | t=1,k= | - | _srv1._did | TXT | - | t=LinkedDomains;uri=foo.com;... | - | _srv2._did | TXT | - | t=DWN;uri=https://dwn.tbddev.org/dwn5;... | + | name | type | TTL | rdata | + |------------------|-----------|-----|-------------------------------------------| + | `_did` | TXT | - | `id=o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy` | + | `_key1._did` | TXT | - | `t=0,k=` | + | `_key2._did` | TXT | - | `t=1,k=` | + | `_key3._did` | TXT | - | `t=1,k=` | + | `_srv1._did` | TXT | - | `t=LinkedDomains;uri=foo.com;...` | + | `_srv2._did` | TXT | - | `t=DWN;uri=https://dwn.tbddev.org/dwn5;...` | In this scheme, we encode the DID document as multiple TXT records. -A root `_did` includes the values of `id` (the DID). Other records such as `srv` (services), `vm` (verification methods), and `vr` (verification relationships) are surfaced as additional records of the format `._did`, which contains the value of each `key` or `service` as attributes. +A root `_did` includes the values of `id` (the DID). Other records such as `srv` (services), `vm` (verification methods), +and verification relationships are surfaced as additional records of the format `._did`, which contains the value of +each `key` or `service` as attributes. All records must end in `_did` or `_did.TLD` if a TLD is being used with the record. -It might look like repeating `_did` is an overhead, but these can be compressed away using normal DNS standard [packet compression](https://courses.cs.duke.edu/fall16/compsci356/DNS/DNS-primer.pdf) techniques. +It might look like repeating `_did` is an overhead, but these can be compressed away using normal DNS standard +[packet compression](https://courses.cs.duke.edu/fall16/compsci356/DNS/DNS-primer.pdf) techniques. The DNS packet must set the `Authoritative Answer` flag, since this is always an `Authoritative` packet. -The DID identifier z-base32 key should be appended as the Origin of all records, which won't cost much thanks to the name compression in DNS packets, so `_did` should be saved as `_did.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`, this should make caching and responding to DNS requests faster as they are already in the shape of a DNS packet, albeit for all types and subdomains. +The DID identifier z-base32 key should be appended as the Origin of all records, which won't cost much thanks to the +name compression in DNS packets, so `_did` should be saved as `_did.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`, +this should make caching and responding with to DNS requests faster as they are already in the shape of a DNS packet, +albeit for all types and subdomains. #### Property Mapping -The _root record_, `_did` or `_did.TLD` if a TLD is being utilizied contains a list of IDs of the keys and service endpoints used in different sections of the DID Document. +The _root record_, `_did` or `_did.TLD` if a TLD is being utilized contains a list of IDs of the keys and service +endpoints used in different sections of the DID Document. An example is as follows: - | name | rdata | - |-----------|--------------------------------------------------------| - | _did.TLD | vm=k1,k2,k3;auth=k1;asr=k2;cin=k3;cdel=k3;srv=s1,s2,s3 | + | name | rdata | + |-------------|----------------------------------------------------------| + | `_did.TLD` | `vm=k1,k2,k3;auth=k1;asr=k2;cin=k3;cdel=k3;srv=s1,s2,s3` | ------------------------------------------------------------------------- The following instructions serve as a reference of mapping DID Document properties to DNS `TXT` records: **1. Verification Methods** -Each Verification Method _name_ is represented as a `_kN._did` record where `N` is the positional index of the Verification Method (e.g. `_k0`, `_k1`). +Each Verification Method _name_ is represented as a `_kN._did` record where `N` is the positional index of the +Verification Method (e.g. `_k0`, `_k1`). -Each Verification Method _data_ is represented with the form `id=M,t=N,k=O` where `M` is the key's ID, `N` is the index of the key's type from the table below, and `O` is the base64URL representation of the public key. +Each Verification Method _data_ is represented with the form `id=M,t=N,k=O` where `M` is the key's ID, `N` is the index +of the key's type from the table below, and `O` is the base64URL representation of the public key. -| index | key type | -| ----- | --------- | -| 0 | ed25519 | -| 1 | secp256k1 | +| index | key type | +| ----- | ----------- | +| 0 | `ed25519` | +| 1 | `secp256k1` | +| 2 | `secp256r1` | An example is as follows: @@ -113,10 +130,10 @@ Each Verification Relationship, if utilized, is represented as a part of the roo The following table maps Verification Relationship types to their record name. -| Verification Relationship | Record Name | -| ------------------------- | ----------- | -| Authentication | `auth` | -| Assertion | `asm` | +| Verification Relationship | Record Name | +| ------------------------- | ---------- | +| Authentication | `auth` | +| Assertion | `asm` | | Key Agreement | `agm` | | Capability Invocation | `inv` | | Capability Delegation | `del` | @@ -124,20 +141,22 @@ The following table maps Verification Relationship types to their record name. The record data is uniform across verification relationships, a comma separated list of key references: An example is as follows: - | Verification Relationship | rdata in the root record | + | Verification Relationship | rdata in the root record | |---------------------------|----------------------------------------------| - | `"authentication": ["#0", "#HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ"]` | `auth=0,HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ` | - | `"assertionMethod": ["#0", "#HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ"]` | `asr=0,HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ` | - | `"keyAgreement": ["#1"]` | `keya=1` | - | `"capabilityInvocation": ["#0"]` | `cinv=0` | - | `"capabilityDelegation": ["#0"]` | `cdel=0` | + | `"authentication": ["#0", "#HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ"]` | `auth=0,HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ` | + | `"assertionMethod": ["#0", "#HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ"]`| `asm=0,HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ` | + | `"keyAgreement": ["#1"]` | `agm=1` | + | `"capabilityInvocation": ["#0"]` | `inv=0` | + | `"capabilityDelegation": ["#0"]` | `del=0` | ----------------------------------------------------- **3. Services** -Each Service, if utilized, is represented name is represented as a `_sN._did` record where N is the positional index of the Service (e.g. `_s0`, `_s1`). Each Service _data_ is represented with the form `id=M,t=N,uri=O` where `M` is the Service's ID, `N`n is the Service's Type and `O` is the Service's URI. +Each Service, if utilized, is represented name is represented as a `_sN._did` record where N is the positional index +of the Service (e.g. `_s0`, `_s1`). Each Service _data_ is represented with the form `id=M,t=N,uri=O` where `M` is the +Service's ID, `N` is the Service's Type and `O` is the Service's URI. An example is given as follows: @@ -146,7 +165,8 @@ An example is given as follows: | `_s0._did` | `id=dwn,t=DecentralizedWebNode,uri=https://example.com/dwn` | -Each Service is also represented as part of the root `_did.TLD` record as a list under the key `srv=` where `ids` is a comma separate list of all IDs for each Service. +Each Service is also represented as part of the root `_did.TLD` record as a list under the key `srv=` where `ids` +is a comma separate list of all IDs for each Service. ----------------------------------------------------- @@ -206,11 +226,11 @@ A sample transformation is provided of the following DID Document: All records are of type `TXT` with an expiry of `7200` to align with the DHT's standard 2-hour expiry window. - | name | rdata | - |-----------|-----------------------------------------------------------------------------| - | `_did.TLD` | `vm=k0,k1;auth=k0,k1;asr=k0,k1;cin=k0;cdel=k0;srv=s1` | - | `_k0._did` | `id=0,t=0,h=afdea69c63605863a68edea0ff7ff49dde0a96ce7e9249eb7780dd3d6f2ab5fc` | - | `_k1._did` | `id=HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ,t=1,k=BCiNAz7y-XBr853PBAzgAOU_c0Hyw0Gb69Hr9jTC3MQ80iSbXxZo0jIFLtW8vVnoWd8tEzUV2o22BVc_IjVTIt8` | + | name | rdata | + |------------|--------------------------------------------------------------------------------| + | `_did.TLD` | `vm=k0,k1;auth=k0,k1;asm=k0,k1;inv=k0;del=k0;srv=s1` | + | `_k0._did` | `id=0,t=0,h=afdea69c63605863a68edea0ff7ff49dde0a96ce7e9249eb7780dd3d6f2ab5fc` | + | `_k1._did` | `id=HTsY9aMkoDomPBhGcUxSOGP40F-W4Q9XCJV1ab8anTQ,t=1,k=BCiNAz7y-XBr853PBAzgAOU_c0Hyw0Gb69Hr9jTC3MQ80iSbXxZo0jIFLtW8vVnoWd8tEzUV2o22BVc_IjVTIt8` | | `_s0._did` | `id=dwn,t=DecentralizedWebNode,uri=https://example.com/dwn` | ## Read @@ -224,7 +244,8 @@ To read a `did:dht`, the process is as follows: ## Update -Each write to the DHT is considered an update. As long as control of the _identity key_ is retained, any update is possible with a unique sequence number with [mutable items](https://www.bittorrent.org/beps/bep_0044.html) using BEP44. +Each write to the DHT is considered an update. As long as control of the _identity key_ is retained any update is +possible with a unique sequence number with [mutable items](https://www.bittorrent.org/beps/bep_0044.html) using BEP44. ## Deactivate @@ -243,23 +264,30 @@ To deactivate a document there are two options: **(TODO): service endpoints / api** -To be recognized as a DID DHT retention gateway, the gateway operator must anchor a transaction on Bitcoin that timelocks Bitcoin value proportional to the number of DIDs they introduce into the _Retained DID Set_. +To be recognized as a DID DHT retention gateway, the gateway operator must anchor a transaction on Bitcoin that +timelocks Bitcoin value proportional to the number of DIDs they introduce into the _Retained DID Set_. -The amount of value locked must be no less than the mean value of the upper half of UTXOs for the block in which the timelock takes effect, and the lock must be a *relative timelock* set to 1000 blocks. +The amount of value locked must be no less than the mean value of the upper half of UTXOs for the block in which the +timelock takes effect, and the lock must be a *relative timelock* set to 1000 blocks. # Retained DID Set ### Generating a Retention Proof -Perform Proof of Work over the DID's identifier + the `retention` value of a given DID operation (composed of the selected bitcoin block hash and nonce). The resulting Retention Proof Hash determines the duration of retention based on the number of leading zeros of the hash, which must be no less than 26. +Perform Proof of Work over the DID's identifier + the `retention` value of a given DID operation (composed of the +selected bitcoin block hash and nonce). The resulting Retention Proof Hash determines the duration of retention based + on the number of leading zeros of the hash, which must be no less than 26. ### Managing the Retained DID Set -Nodes following the Retention Set rules SHOULD sort DIDs they are retaining by age of retention proof, followed by number of retention proof leading 0s. When a node needs to reduce its retained set of DID entries, it SHOULD remove entries from the bottom of the list in accordance with this sort. +Nodes following the Retention Set rules SHOULD sort DIDs they are retaining by age of retention proof, followed by +number of retention proof leading 0s. When a node needs to reduce its retained set of DID entries, it SHOULD remove +entries from the bottom of the list in accordance with this sort. ### Reporting on Retention Status -Nodes MUST include the approximate time until retention fall-off in the Method-specific metadata of a resolved DID Document, to aid in Identity Agents (wallets) being able to assess whether resubmission is required. +Nodes MUST include the approximate time until retention fall-off in the Method-specific metadata of a resolved DID +Document, to aid in Identity Agents (wallets) being able to assess whether resubmission is required. # Implementation Considerations