From 431b29d4432c9b7fd74e999290550e350b99d79f Mon Sep 17 00:00:00 2001 From: Gabe <7622243+decentralgabe@users.noreply.github.com> Date: Tue, 14 May 2024 15:59:07 -0700 Subject: [PATCH] Support for retention challenges (#219) * language on retained dids * updates * formatting * updates * update * one more * Update spec/api.yaml Co-authored-by: Kendall Weihe * Apply suggestions from code review Co-authored-by: Frank Hinek * clarity on identity key * hash source registry --------- Co-authored-by: Kendall Weihe Co-authored-by: Frank Hinek --- impl/internal/did/pow.go | 30 ++- impl/internal/did/pow_test.go | 14 +- spec/api.yaml | 108 +++++---- spec/registry/spec.md | 24 +- spec/spec.md | 405 ++++++++++++++++++++++------------ 5 files changed, 389 insertions(+), 192 deletions(-) diff --git a/impl/internal/did/pow.go b/impl/internal/did/pow.go index 3f5a7a47..56f4207b 100644 --- a/impl/internal/did/pow.go +++ b/impl/internal/did/pow.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math/big" + "strconv" "strings" ) @@ -29,10 +30,10 @@ func hasLeadingZeros(hash string, difficulty int) bool { return strings.HasPrefix(binaryHash, target) } -// computeRetentionProof generates the Retention Proof Hash and checks if it meets the criteria. -func computeRetentionProof(didIdentifier, bitcoinBlockHash string, difficulty, nonce int) (string, bool) { +// solveRetentionChallenge generates the Retention Challenge Hash and checks if it meets the criteria. +func solveRetentionChallenge(didIdentifier, inputHash string, difficulty, nonce int) (string, bool) { // Concatenating the DID identifier with the retention value - retentionValue := didIdentifier + (bitcoinBlockHash + fmt.Sprintf("%d", nonce)) + retentionValue := didIdentifier + (inputHash + fmt.Sprintf("%d", nonce)) // Computing the SHA-256 hash hash := computeSHA256Hash(retentionValue) @@ -40,3 +41,26 @@ func computeRetentionProof(didIdentifier, bitcoinBlockHash string, difficulty, n // Checking for the required number of leading zeros according to the difficulty return hash, hasLeadingZeros(hash, difficulty) } + +// validateRetentionSolution validates the Retention Solution. +func validateRetentionSolution(did, hash, retentionSolution string, difficulty int) bool { + parts := strings.Split(retentionSolution, ":") + if len(parts) != 2 { + return false + } + + nonce, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return false + } + + retentionValue := did + hash + strconv.FormatUint(nonce, 10) + computedHash := computeSHA256Hash(retentionValue) + + if !hasLeadingZeros(computedHash, difficulty) { + return false + } + + solutionHash := parts[0] + return solutionHash == computedHash +} diff --git a/impl/internal/did/pow_test.go b/impl/internal/did/pow_test.go index 39e6dd26..c8be236b 100644 --- a/impl/internal/did/pow_test.go +++ b/impl/internal/did/pow_test.go @@ -8,22 +8,26 @@ import ( ) func TestPOW(t *testing.T) { - // Example usage of computeRetentionProof + // Example usage of solveRetentionChallenge didIdentifier := "did:dht:test" - bitcoinBlockHash := "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" + inputHash := "123bcdefghobc23567822be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" // 26 leading zeros difficulty := 26 timer := time.Now() for nonce := 0; nonce < math.MaxInt; nonce++ { - hash, isValid := computeRetentionProof(didIdentifier, bitcoinBlockHash, difficulty, nonce) + solution, isValid := solveRetentionChallenge(didIdentifier, inputHash, difficulty, nonce) if isValid { - fmt.Printf("Hash: %s\n", hash) - fmt.Printf("Valid Retention Proof: %v\n", isValid) + fmt.Printf("Solution: %s\n", solution) + fmt.Printf("Valid Retention Solution: %v\n", isValid) fmt.Printf("Nonce: %d\n", nonce) + + isValidRetentionSolution := validateRetentionSolution(didIdentifier, inputHash, fmt.Sprintf("%s:%d", solution, nonce), difficulty) + fmt.Printf("Validated Solution: %v\n", isValidRetentionSolution) break } } + fmt.Printf("Time taken: %s\n", time.Since(timer)) } diff --git a/spec/api.yaml b/spec/api.yaml index f62e6b2b..31107766 100644 --- a/spec/api.yaml +++ b/spec/api.yaml @@ -5,7 +5,7 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - version: v0.1 + version: v1.0 paths: /{id}: get: @@ -22,7 +22,7 @@ paths: type: string responses: "200": - description: "64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v." + description: 64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v content: application/octet-stream: schema: @@ -50,7 +50,7 @@ paths: put: tags: - DHT - summary: Put DNS-encoded BEP44 records into the DHT + summary: Put a DNS-encoded BEP44 record set into the DHT description: Put a DNS-encoded BEP44 record set into the DHT parameters: - name: id @@ -60,7 +60,7 @@ paths: schema: type: string requestBody: - description: "64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v." + description: 64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v content: application/octet-stream: schema: @@ -105,32 +105,35 @@ paths: properties: sig: type: string - description: A base64URL-encoded signature of the BEP44 payload. + description: A base64URL-encoded signature of the BEP44 payload seq: type: integer - description: A sequence number for the request, recommended to be a unix timestamp in seconds. + description: A sequence number for the request, recommended to be a unix timestamp in seconds v: type: string - description: A base64URL-encoded bencoded DNS packet containing the DID Document. - retention_proof: + description: A base64URL-encoded bencoded DNS packet containing the DID Document + retention_solution: type: string - description: A retention proof calculated according to the spec-defined retention proof algorithm. + description: A retention solution calculated according to the spec-defined retention solution algorithm required: [ did, sig, seq, v ] responses: "202": - description: Accepted. The server has accepted the request as valid and will publish it to the + description: The server has accepted the request as valid and will publish it to the DHT content: application/json: schema: - type: string + type: object + properties: + expiry: + type: number "400": - description: Invalid request. + description: Invalid request content: application/json: schema: type: string "401": - description: Invalid signature. + description: Invalid signature or retention solution content: application/json: schema: @@ -141,27 +144,33 @@ paths: application/json: schema: type: string + "503": + description: Retention sets have been temporarily disabled. + content: + application/json: + schema: + type: string get: tags: - DID summary: Resolve a DID - description: Resolve a DID from the DHT first, with a fallback to local storage. + description: Resolve a DID from the DHT first, with a fallback to local storage parameters: - name: id in: path - description: DID to resolve. + description: The DID to resolve required: true schema: type: string - name: seq in: query - description: Sequence number of the DID to resolve. + description: The sequence number of the DID to resolve required: false schema: type: integer responses: "200": - description: The resolved DID Document. + description: The resolved DID Document content: application/json: schema: @@ -169,29 +178,32 @@ paths: properties: did: type: object - description: "The DID Document." + description: The DID Document dht: type: string - description: "An unpadded base64URL-encoded representation of a full DNS-encoded BEP44 payload, represented as 64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v concatenated; enabling independent verification." + description: An unpadded base64URL-encoded representation of a full DNS-encoded BEP44 payload, represented as 64 bytes sig, 8 bytes u64 big-endian seq, 0-1000 bytes of v concatenated; enabling independent verification types: type: array items: type: integer - description: "The types associated with the DID." + description: The types associated with the DID sequence_numbers: type: array items: type: integer - description: "The sequence numbers available for querying for this node." + description: The sequence numbers available for querying for this node + expiry: + type: number + description: The Unix Timestamp in seconds indicating when the DID will be evicted from the Gateway's Retained DID Set required: [ did, dht ] "400": - description: Invalid request. + description: Invalid request content: application/json: schema: type: string "404": - description: DID could not be resolved. + description: The requested DID could not be resolved content: application/json: schema: @@ -200,11 +212,11 @@ paths: get: tags: - DID - summary: Retrieve a list of supported types for indexing. - description: Retrieve a list of supported indexing types, according to the spec-defined type list. + summary: Retrieve a list of supported types for indexing + description: Retrieve a list of supported indexing types, according to the spec-defined type list responses: "200": - description: A list of types support, alongside their human-readable description. + description: A list of types support, alongside their human-readable description content: application/json: schema: @@ -218,7 +230,7 @@ paths: type: string required: [ type, description ] "404": - description: Type indexing not supported by this gateway. + description: Type indexing not supported by this gateway content: application/json: schema: @@ -227,8 +239,8 @@ paths: get: tags: - DID - summary: Retrieve a list of DIDs indexed under a given type. - description: Retrieve a list of DIDs indexed under a given type, according to the spec-defined type index. + summary: Retrieve a list of DIDs indexed under a given type + description: Retrieve a list of DIDs indexed under a given type, according to the spec-defined type index parameters: - name: id in: path @@ -238,17 +250,17 @@ paths: type: integer - name: offset in: query - description: Specifies the starting position from where the type records should be retrieved. Default is 0. + description: Specifies the starting position from where the type records should be retrieved. Default is 0 schema: type: integer - name: limit in: query - description: Specifies the maximum number of type records to retrieve. Default is 100. + description: Specifies the maximum number of type records to retrieve. The default is 100. schema: type: integer responses: "200": - description: A list of DIDs indexed under the given type. + description: A list of DIDs indexed under the given type content: application/json: schema: @@ -256,20 +268,20 @@ paths: items: type: string "404": - description: Type not found. + description: Type not found content: application/json: schema: type: string - /difficulty: + /challenge: get: tags: - DID - summary: Get information about the current difficulty. - description: Get information needed to calculate a retention proof for DID PUT operations. + summary: Get information necessary to solve a retention challenge + description: Get information needed to calculate a retention solution for DID PUT operations responses: "200": - description: The current hash and difficulty to calculate a retention proof against. + description: The current hash and difficulty to calculate a retention solution against, along with an estimated retention guarantee represented by the expiry property content: application/json: schema: @@ -277,12 +289,26 @@ paths: properties: hash: type: string + description: The current hash which is to be used as input for computing a retention solution + hash_source: + type: string + description: The source of the hash as defined by the Hash Source Registry. difficulty: type: integer - required: [ hash, difficulty ] - "404": - description: Retention proofs not supported by this gateway. + description: The current difficulty of the challenge representing the number of bits of leading zeros the resulting hash must contain + expiry: + type: integer + description: An approximate expiry date-time value, if a valid Retention Solution is submitted against this challenge, represented as a Unix Timestamp in seconds. The precise expiry date-time value is returned as a part of a PUT operation + required: [ hash, difficult, expiry ] + "501": + description: Retention challenges are not supported + content: + application/json: + schema: + type: string + "503": + description: Retention sets have been temporarily disabled content: application/json: schema: - type: string \ No newline at end of file + type: string \ No newline at end of file diff --git a/spec/registry/spec.md b/spec/registry/spec.md index cd382496..7ff13ef6 100644 --- a/spec/registry/spec.md +++ b/spec/registry/spec.md @@ -9,7 +9,7 @@ The DID DHT Method Specification Registry 1.0 **Draft Created:** November 20, 2023 -**Latest Update:** May 2, 2024 +**Latest Update:** May 14, 2024 **Editors:** ~ [Gabe Cohen](https://github.com/decentralgabe) @@ -92,7 +92,6 @@ These properties are for use on a service object, in the value of [service](http | `sig` | String or array of strings | `enc=E` where `E` is a string or array of strings | id=s1;t=TestService;se=https://test-service.com/1;enc=1 | | `enc` | String or array of strings | `sig=S` where `S` is a string or array of strings | id=s1;t=TestService;se=https://test-service.com/1;sig=2 | - ### Interoperable DID Methods As an **OPTIONAL** extension, some existing DID methods can leverage `did:dht` to broaden their feature set. This registry serves @@ -168,25 +167,34 @@ ensure the maintenance of an active timelock, which serves as a proof-of-legitim 1. Generate a relative [[ref:timelock]] transaction for the Bitcoin blockchain with the following attributes: - Set the lock duration to 1000 - - Add locked value locked must be no less than the mean value of the upper quintile of [UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output) as of a block that is no more than 10 blocks earlier from the block the locking transaction is included in (this effectively provides a 10 block grace period for the transaction to make it into the chain). + - Add locked value locked must be no less than the mean value of the upper quintile of + [UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output) as of a block that is no more than 10 blocks earlier from the block + the locking transaction is included in (this effectively provides a 10 block grace period for the transaction to make it into the chain). - Add an `OP_RETURN` string composed of the following comma-separated values: - The block number used to compute the mean value of the upper quintile of [UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output). - The `URI` where your node can be addressed 2. Include the [[ref:timelock]] transaction within 10 blocks of the block number specified for the average UTXO value calculation. 3. If this is a relocking transaction that refreshes an existing registration of a node: - The relocking transaction ****MUST**** spend the outputs of the lock it replaces. - - If the operator wants to prevent other nodes and clients using the decentralized registry from dropping the Registered Gateway from their Registered Gateway list, the relocking transaction ****MUST**** be included in the blockchain within ten blocks of the previous lock's expiration. + - If the operator wants to prevent other nodes and clients using the decentralized registry from dropping the Registered Gateway from their + Registered Gateway list, the relocking transaction ****MUST**** be included in the blockchain within ten blocks of the previous lock's expiration. + +##### Hash Source -##### Hash +The hash source used for providing [Retention Challenges](../index.html#retained-did-set) is configurable. The registry table +below serves as a way to define new hash sources and communicate them as a part of the [Gateway API](../index.html#gateway-api). -The hash source to be used is [Bitcoin block hashes](https://csrc.nist.gov/glossary/term/block_header#:~:text=Definitions%3A,cryptographic%20nonce%20(if%20needed).). It is ****RECOMMENDED**** to use the most recent block hash value. +| Hash Source Name | Hash Source | +|------------------|-------------------------------------------| +| `bitcoin` | [Bitcoin block hashes](https://csrc.nist.gov/glossary/term/block_header#:~:text=Definitions%3A,cryptographic%20nonce%20(if%20needed) | ##### Discovery To discover Bitcoin Anchored Gateways one must follow the following steps: -1. Starting at block height of `817714` traverse the chain, checking the value of the `OP_RETURN` field of transactions with _at least_ **6 confirmations**. -2. Find transactions that match the form `block number + uri`, as per the [steps outlined in the section above](#bitcoin-anchored-gateways). +1. Starting at block height of `817714` traverse the chain, checking the value of the `OP_RETURN` field of +transactions with _at least_ **6 confirmations**. +2. Find transactions that match the form `block number + URI`, as per the [steps outlined in the section above](#bitcoin-anchored-gateways). ## References diff --git a/spec/spec.md b/spec/spec.md index ba5909ff..7d1fd915 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -9,7 +9,7 @@ The DID DHT Method Specification 1.0 **Draft Created:** October 20, 2023 -**Last Updated:** May 9, 2024 +**Last Updated:** May 14, 2024 **Editors:** ~ [Gabe Cohen](https://github.com/decentralgabe) @@ -91,10 +91,10 @@ services, and other properties outlined in the specification. ~ The unique identifier string within a DID URI (e.g. the part after `did:dht:`). For DID DHT the suffix is the [[ref:z-base-32]] encoded public key portion of the [[ref:Identity Key]]. -[[def:Identity Key]] -~ An [Identity Key](#identity-key) is an [[ref:Ed25519]] key-pair required to authenticate all records in +[[def:Identity Key Pair, Identity Key]] +~ An [Identity Key Pair](#identity-key-pair) is an [[ref:Ed25519]] key pair required to authenticate all records in [[ref:Mainline DHT]]. The public key portion is encoded using [[ref:z-base-32]] and represented in the [[ref:DID Suffix]]. -This key is guaranteed to be present in each `did:dht` document. +The public key, also known as the [[ref:Identity Key]] is guaranteed to be present in each `did:dht` document. [[def:DNS Resource Records, DNS Resource Record]] ~ An efficient format for representing [[ref:DID Documents]] and providing semantics pertinent to DID DHT, @@ -125,15 +125,17 @@ for retaining records in the DHT. See [Republishing Data](#republishing-data). [[def:Client, Clients]] ~ A client is a piece of software that is responsible for generating a `did:dht` and submitting it to a [[ref:Mainline Server]] or [[ref:Gateway]]. Notably, a client has the ability to sign messages with the -private key associated with an [[ref:Identity Key]]. +private-key portion of an [[ref:Identity Key Pair]]. -[[def:Retained DID Set, Retained Set, Retention Set]] +[[def:Retained DID Set, Retained Set, Retention Set, Retention Sets]] ~ The set of DIDs that a [[ref:Gateway]] is retaining and thus is responsible for [[ref:republishing]]. +See [Retained DID Set](#retained-did-set). -[[def:Retention Proof, Retention Proofs]] -~ A proof provided by the [[ref:DID]] controller to a [[ref:Gateway]] which attests that (1) they control the -DID and (2) have done sufficient work to have a [[ref:Gateway]] retain their DID for a set period of time. See -[Retained DID Set](#retained-did-set). +[[def:Retention Challenge, Retention Challenges, Retention Solution, Retention Solutions]] +~ A _Retention Challenge_ refers to a computational cost associated with a set of guarantees to be provided to a +DID controller by a [[ref:Gateway]], who submits a corresponding _Retention Solution_. A solution provided by the [[ref:DID]] +controller to a [[ref:Gateway]] which attests both that (1) they control the DID and (2) have done sufficient work to +have a [[ref:Gateway]] retain their DID for a set period of time as a part of its [[ref:Retention Set]]. [[def:Sequence Number, Sequence Numbers, Sequence]] ~ A sequence number, or `seq`, is a property of a mutable item as defined in [[ref:BEP44]]. It is a 64-bit integer that @@ -163,10 +165,11 @@ Alternatively, one can interpret the encoding rules as a series of transformatio did-dht-format := did:dht:Z-BASE-32(raw-public-key-bytes) ``` -### Identity Key +### Identity Key Pair -A unique property of DID DHT is its dependence on an a single non-rotatable key which we refer to as an _Identity Key_. -This requirement stems from [[ref:BEP44]], particularly within the _Mutable Items_ section: +A unique property of DID DHT is its dependence on an a single non-rotatable key which we refer to as an _Identity Key Pair_, +whose public key is referred to as the _Identity Key_. This requirement stems from [[ref:BEP44]], particularly within the +section on _Mutable Items_: > Mutable items can be updated, without changing their DHT keys. To authenticate that only the original publisher can update an item, it is signed by a private key generated by the original publisher. The target ID mutable items are @@ -175,19 +178,19 @@ stored under is the SHA-1 hash of the public key (as it appears in the put messa This mechanism, as detailed in [[ref:BEP44]], ensures that all entries in the DHT are authenticated through a private key unique to the initial publisher. Consequently, DHT records, including DID DHT Documents, are _independently verifiable_. This independence implies that trust in a specific [[ref:Mainline]] or [[ref:Gateway]] server -for providing unaltered messages is unnecessary. Instead, all clients can, and should, verify messages themselves. This +for providing unaltered messages is unnecessary. Instead, all clients have the ability to verify messages themselves. This approach significantly mitigates risks associated with other DID methods, where a compromised server or -[DID resolver](https://www.w3.org/TR/did-core/#choosing-did-resolvers) might tamper with a [[ref:DID Document]] -which would be undetectable by a client. +[DID resolver](https://www.w3.org/TR/did-core/#choosing-did-resolvers) has the ability tamper with a [[ref:DID Document]], +in a manner which could be undetectable by a client. -Currently, [[ref:Mainline]] exclusively supports the [[ref:Ed25519]] signature system. In turn, [[ref:Ed25519]]-based +At present, [[ref:Mainline]] exclusively supports the [[ref:Ed25519]] signature system. In turn, [[ref:Ed25519]]-based keys are required by DID DHT and used to uniquely identify DID DHT Documents. DID DHT identifiers are formed by -concatenating the `did:dht:` prefix with a [[ref:z-base-32]] encoded public key, which acts as its [[ref:suffix]]. -Identity Keys ****MUST**** have the identifier `0` as both its Verification Method `id` and JWK `kid` [[spec:RFC7517]]. -Identity Keys ****MUST**** have the [Verification Relationships](#verification-relationships) _Authentication_, _Assertion_, -_Capability Invocation_, and _Capability Delegation_. +concatenating the `did:dht:` prefix with a [[ref:z-base-32]] encoded public key (the [[ref:Identity Key]], which acts as +its [[ref:suffix]]. Identity Keys ****MUST**** have the identifier `0` as both its Verification Method `id` and JWK `kid` +[[spec:RFC7517]]. Identity Keys ****MUST**** have the [Verification Relationships](#verification-relationships) +_Authentication_, _Assertion_, _Capability Invocation_, and _Capability Delegation_. -While the system requires at least one [[ref:Ed25519]], a DID DHT Document can include any number of additional keys. +While DID DHT requires at least one [[ref:Ed25519]] key pair, a DID DHT Document can include any number of additional keys. Additional key types ****MUST**** be registered in the [Key Type Index](registry/index.html##key-type-index). As a unique consequence of the requirement of the Identity Key, DID DHT Documents are able to be partially-resolved @@ -199,7 +202,7 @@ resolution is only used as a fallback mechanism. Similarly, the requirement of a In this scheme, we encode the [[ref:DID Document]] as [DNS Resource Records](https://en.wikipedia.org/wiki/Domain_Name_System#Resource_records). These resource records make -up a DNS packet [[spec:RFC1034]] [[spec:RFC1035]], which is then stored in the [[ref:DHT]]. +up a DNS packet [[spec:RFC1034]] [[spec:RFC1035]], which is then stored in the [[ref:DHT]] encoded as a [[ref:BEP44]] payload. | Name | Type | TTL | Rdata | | ------------ | ---- | ------ | ------------------------------------------------------------ | @@ -252,15 +255,14 @@ the set of Service resource aliases (e.g. `s0`) for each [Service](#services). Additionally: - - A version number ****MUST**** be present. The version number for this specification version +- A version number ****MUST**** be present. The version number for this specification version is **0** (e.g. `v=0`). The version number is not present in the corresponding DID Document. - - The `vm` property ****MUST**** always contain _at least_ the [[ref:Identity Key]] represented by `k0`. +- The `vm` property ****MUST**** always contain _at least_ the [[ref:Identity Key]] represented by `k0`. - - Verification Relationships (`auth`, `asm`, `agm`, `inv`, `del`) without any members ****MUST**** be omitted. - - - If there are no [Services](#services) the `svc` property ****MUST**** be omitted. +- Verification Relationships (`auth`, `asm`, `agm`, `inv`, `del`) without any members ****MUST**** be omitted. +- If there are no [Services](#services) the `svc` property ****MUST**** be omitted. **Example Root Record** @@ -311,8 +313,7 @@ To ensure that the DID controller is authorized to make changes to the DID Docum ##### Also Known As -A `did:dht` document ****MAY**** have multiple identifiers using the -[alsoKnownAs](https://www.w3.org/TR/did-core/#also-known-as) property. +A `did:dht` document ****MAY**** have multiple identifiers using the [alsoKnownAs](https://www.w3.org/TR/did-core/#also-known-as) property. - The [Also Known As](https://www.w3.org/TR/did-core/#also-known-as) record's **name** is represented as a `_aka._did.`. @@ -487,22 +488,25 @@ A sample transformation of a fully-featured DID Document to a DNS packet is exem ### Operations -Entries to the [[ref:DHT]] require a signed record as per [[ref:BEP44]]. As such, the keypair used for the [[ref:Mainline]] +Entries to the [[ref:DHT]] require a signed record as per [[ref:BEP44]]. As such, the key pair used for the [[ref:Mainline]] identifier is also used to sign the [[ref:DHT]] record. #### Create To create a `did:dht` document, the process is as follows: -1. Generate an [[ref:Ed25519]] keypair and encode the public key using the format provided in the [format section](#format). +1. Generate an [[ref:Ed25519]] key pair and encode the public key using the format provided in the [format section](#format). 2. Construct a conformant JSON representation of a [[ref:DID Document]]. a. The document ****MUST**** include a [Verification Method](https://www.w3.org/TR/did-core/#verification-methods) with - the [[ref:Identity Key]] encoded as a `publicKeyJwk` as per [[spec:RFC7517]] with an `id` of `0` and `type` of - `JsonWebKey` defined by [[ref:VC-JOSE-COSE]]. + the [[ref:Identity Key]]. The `id` property of this Verification Method ****MUST**** be `0` and type of `JsonWebKey` as defined + by [[ref:VC-JOSE-COSE]]. The key ****MUST**** be represented as a `publicKeyJwk` as per [[spec:RFC7517]] with a `kid` of `0`. + + b. The [[ref:Identity Key]] ****MUST**** have the [Verification Relationships](#verification-relationships) + _Authentication_, _Assertion_, _Capability Invocation_, and _Capability Delegation_. - b. The document can include any number of other [core properties](https://www.w3.org/TR/did-core/#core-properties); + c. 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` as per [[spec:RFC7517]]. In addition to the properties required by the `JWK` specification, the `alg` property ****MUST**** always be present. Default algorithms are defined per key type in the [indexed types registry](registry/index.html#indexed-types). @@ -564,7 +568,7 @@ Any valid [[ref:BEP44]] record written to the DHT is an update. As long as contr [[ref:Identity Key]] is retained any update is made possibly by signing and writing records with a unique incremental [[ref:sequence number]] with [mutable items](https://www.bittorrent.org/beps/bep_0044.html). -It is ****RECOMMENDED**** that updates are infrequent, as caching of the DHT is highly encouraged. +It is ****RECOMMENDED**** that updates are infrequent — at least every 2 hours — as DHT caching is highly encouraged. #### Deactivate @@ -615,7 +619,6 @@ and `N` is the the unpadded base64URL signature from step (3) above. | ---------- | ---- | ----- | -------------------------------------------------------------------------------------- | | _prv._did. | TXT | 7200 | id=did:dht:pxoem5sfzxxxrnrwfgiu5i5wc7epouy1jk9zb7ad159dsxbxy8io;s=ol5LbUydL3_PdChE8tVYH-z_NhyFDQlop0agYtjyYbKz_-CYrj_3JGLiFne1e7PruOwf-b91uEFq9R_PgBn-Bg | - The DID controller ****MAY**** include a statement in the old [[ref:DID Document]] indicating the rotation to the new identifier, by setting the [controller property](#controller) to the new DID. Without the previous record present in the new DID's record set, the linkage ****MUST NOT**** be considered legitimate. @@ -649,9 +652,9 @@ records is outlined as follows: ### Type Indexing -Type indexing is an **OPTIONAL** feature that enables DIDs to become **discoverable**, by flagging themselves as being of -a particular type. Types are not included as a part of the DID Document, but rather as part of the DNS packet. This allows -for DIDs to be indexed by type by [[ref:Gateways]], and for DIDs to be resolved by type. +Type indexing is an **OPTIONAL** feature that enables DIDs to become **discoverable**, by flagging themselves as being +of a particular type. Types are not included as a part of the DID Document, but rather as part of the DNS packet. This +allows for DIDs to be indexed by type by [[ref:Gateways]], and for DIDs to be resolved by type. DIDs can be indexed by type by adding a `_typ._did.` record to the DNS packet. A DID ****MAY**** have **AT MOST** one type index record. This record is of the following format: @@ -660,8 +663,8 @@ type index record. This record is of the following format: - The Type Index record's **type** is `TXT`, indicating a Text record. -- The Type Index record's **data** is represented with the form `id=H,I,J,...N` where the value is a comma-separated list of -integer types from the [indexed types registry](registry/index.html#indexed-types). +- The Type Index record's **data** is represented with the form `id=H,I,J,...N` where the value is a comma-separated +list of integer types from the [indexed types registry](registry/index.html#indexed-types). **Example Type Index Record** @@ -691,82 +694,158 @@ optional integration of these gateways into a registry system. ### Discovering Gateways -As an **OPTIONAL** feature of the DID DHT Method, operators of a [[ref:Gateway]] may choose to make their server +As an **OPTIONAL** feature of the DID DHT Method, operators of a [[ref:Gateway]] ****MAY**** choose to make their server discoverable through a [[ref:Gateway Registry]]. This feature allows for easy location through various internet-based discovery mechanisms. [[ref:Gateway Registries]] can vary in nature, encompassing a spectrum from centrally managed directories to diverse decentralized systems including databases, ledgers, or other structures. -One such registry is [provided by this specification](registry/index.html#gateways). +As a convenience, one such registry is [provided by this specification](registry/index.html#gateways). ### Retained DID Set -A [[ref:Retained DID Set]] refers to the set of DIDs a [[ref:Gateway]] retains and [[ref:republishes]] to the DHT. A -[[ref:Gateway]] may choose to surface additional [APIs](#gateway-api) based on this set, such as providing a -[type index](#type-indexing). +As a feature of the DID DHT Method, operators of a [[ref:Gateway]] ****MUST**** support retaining DIDs for extended periods +of time to reduce the burden on DID controllers and [[ref:Clients]] in needing to [[ref:republish]] their records to +[[ref:Mainline]]. + +A [[ref:Retained DID Set]] refers to the set of DIDs a [[ref:Gateway]] retains and [[ref:republishes]] to the DHT. +This feature aims to safeguard equitable access to the resources of [[ref:Gateways]], which are publicly accessible +and potentially subject to [a high volume of requests](#rate-limiting). The [[ref:Retained DID Set]] is facilitated +by a [[ref:Retention Challenge]], which requires clients to generate a solution for the challenge — a [[ref:Retention Solution]] +— in order to have their write requests accepted. [[ref:Retention Solutions]] act as proof of an amount of work +completed in exchange for a retention guarantee provided by a [[ref:Gateway]] (via the `expiry` property). -To safeguard equitable access to the resources of [[ref:Gateways]], which are publicly accessible and potentially -subject to [a high volume of requests](#rate-limiting), we suggest an ****OPTIONAL**** mechanism aimed at upholding -fairness in the retention and [[ref:republishing]] of record sets by [[ref:Gateways]]. This mechanism, referred to as a -[[ref:Retention Proof]], requires clients to generate a proof value for write requests that attests to to an amount of work -completed in exchange for a retention guarantee provided by a [[ref:Gateway]]. This process aims to fairly guarnatee that -the amount of work done by a client is proportional to the duration of data retention and [[ref:republishing]] a [[ref:Gateway]] -performs. This mechanism enhances the overall reliability and effectiveness of [[ref:Gateways]] in managing requests. +The [[ref:Retained DID Set]] aims to provide a fair mechanism which provides numerous benefits for clients while giving +[[ref:Gateway]] operators anti-spam prevention, and control over the rate at which they accept new DIDs, thus enhancing +the overall reliability and effectiveness of [[ref:Gateways]] in managing DIDs. -#### Generating a Retention Proof +#### Generating a Retention Solution -A [[ref:Retention Proof]] is a form of [Proof of Work](https://en.bitcoin.it/wiki/Proof_of_work) performed over a DID -identifier concatenated with the `retention` value of a given DID operation. The `retention` value is composed of a -hash value specified [by the gateway](registry/index.html#gateways), and a random -[nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) using the [SHA-256 hashing algorithm](https://en.wikipedia.org/wiki/SHA-2). -The resulting _Retention Proof Hash_ is used to determine the retention duration based on the number of leading zeros -of the hash, referred to as the _difficulty_, which ****MUST**** be no less than 26 bits of the 256-bit hash value. -The algorithm, in detail, is as follows: +A [[ref:Retention Solution]] is a form of [proof of work](https://en.bitcoin.it/wiki/Proof_of_work) bound to a specific +DID identifier, using input values supplied by a given [gateway](registry/index.html#gateways). The proof of work is +performed using the [SHA-256 hashing algorithm](https://en.wikipedia.org/wiki/SHA-2) over the concatenation of a the +`did` identifier and random [`nonce`](https://en.wikipedia.org/wiki/Cryptographic_nonce) supplied by the user, and a +`hash` value [supplied by the gateway](#get-the-current-challenge). The source of a given `hash` referred to as a +`hash_source` ****MUST**** be one of the values specified in the [Hash Source Registry](registry/index.html#hash-source). +The result of a given proof of work attempt is referred to as the `retention` value. + +The resulting `retention` value is determined to be a valid [[ref:Retention Solution]] based on whether it has the +requisite number of leading zeros defined by the `difficulty`. Difficulty values are +[supplied by the gateway](#get-the-current-challenge) and ****MUST**** be no less than 26 bits of the 256-bit hash value. + +The algorithm for generating a [[ref:Retention Solution]] is as follows: 1. Obtain a DID identifier and set it to `DID`. -2. Get the difficulty and recent hash from the server set to `DIFFICULTY` and `HASH`, respectively. +2. Get the `difficulty` and `hash` value from the server set to `DIFFICULTY` and `HASH`, respectively. 3. Generate a random 32-bit integer nonce value set to `NONCE`. -4. Compute the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash over `ATTEMPT` where `ATTEMPT` = (`DID` + `HASH` + `NONCE`). +4. Set `RETENTION` equal to the result of (`DID` + `HASH` + `NONCE`). + +5. Compute the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash over `RETENTION` where `ATTEMPT` = SHA256(`RETENTION`). + +6. Inspect the result of `ATTEMPT`, and ensure it has >= `DIFFICULTY` bits of leading zeroes. + + a. If it does, set `RETENTION_SOLUTION` = `ATTEMPT`. + + b. Otherwise, return to step 3. + +7. Set `RETENTION_SOLUTION` equal to the concatenation of `ATTEMPT` and `NONCE` separated by a colon (e.g., `ATTEMPT`:`NONCE`). + +8. Submit the `RETENTION_SOLUTION` to the [Gateway API](#register=or-update-a-did) for write operations. + +:::note +When a [[ref:Client]] submits a valid [[ref:Retention Solution]], conformant [[ref:Gateways]] respond with an `expiry` +timestamp. This timestamp indicates when DID will be evicted from the [[ref:Gateway]]'s +[Retained DID Set](#retained-did-set). [[ref:Clients]] are advised to take note of this `expiry` timestamp and ensure +they solve a new [[ref:Retention Challenge]] before the expiration is reached. By doing so, [[ref:Clients]] can +maintain the continuity of their DID's retention and prevent unintended eviction of their identifier. +::: + +#### Validating a Retention Solution -5. Inspect the result of `ATTEMPT`, and ensure it has >= `DIFFICULTY` bits of leading zeroes. +When a [[ref:Gateway]] receives a [[ref:Retention Solution]] as part of a write operation, it ****MUST**** validate +the solution to ensure it meets the required criteria before accepting the write request and providing a retention guarantee. - a. If so, `ATTEMPT` = `RETENTION_PROOF`. +The algorithm for validating a [[ref:Retention Solution]] is as follows: - b. Otherwise, regenerate `NONCE` and go to step 3. +1. Extract the `RETENTION_SOLUTION` value from the write request. -6. Submit the `RETENTION_PROOF` to the [Gateway API](#register=or-update-a-did). +2. Retrieve the `DID` identifier associated with the write request. + +3. Obtain the current `HASH` and `DIFFICULTY` values used by the [[ref:Gateway]]. + +4. Construct the `RETENTION_VALUE` by concatenating the `DID`, `HASH`, and the `NONCE` (extracted from the `RETENTION_SOLUTION`). + +5. Compute the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash of the `RETENTION_VALUE` and set it to `COMPUTED_HASH`. + +6. Compare the `COMPUTED_HASH` with the `RETENTION_SOLUTION`: + + a. If the `COMPUTED_HASH` matches the `RETENTION_SOLUTION`, proceed to step 7. + + b. If the `COMPUTED_HASH` does not match the `RETENTION_SOLUTION`, the validation fails, and the write request is rejected. + +7. Check if the `COMPUTED_HASH` has the required number of leading zeros specified by the difficulty value: + + a. If the `COMPUTED_HASH` has the required number of leading zeros, the [[ref:Retention Solution]] is considered valid. + + b. If the `COMPUTED_HASH` does not have the required number of leading zeros, the validation fails, and the write request is rejected. + +If the [[ref:Retention Solution]] is deemed valid, the [[ref:Gateway]] accepts the write request and proceeds to store the DID document +in its [[ref:Retained DID Set]]. The [[ref:Gateway]] generates an `expiry` timestamp based on its retention policy and includes it in the +response to the [[ref:Client]]. + +By validating the [[ref:Retention Solution]], the [[ref:Gateway]] ensures that the [[ref:Client]] has performed the necessary proof of +work and is eligible for the retention guarantee. This validation process helps maintain the integrity and fairness of the [[ref:Retained DID Set]] +system. It is important for [[ref:Gateways]] to consistently apply the validation logic and reject any write requests that do not include a valid +[[ref:Retention Solution]]. This helps prevent abuse and ensures that only [[ref:Clients]] who have invested the required computational effort can +benefit from the retention feature. [[ref:Gateways]] ****SHOULD**** regularly update their hash value to prevent pre-computation attacks and ensure +the uniqueness and freshness of the [[ref:Retention Solutions]]. The recommended hash refresh window is **10 minutes**. #### Managing the Retained DID Set -[[ref:Gateways]] adhering to the [[ref:Retention Set]] rules ****SHOULD**** sort DIDs they are retaining by the number of -_leading 0s_ in their [[ref:Retention Proofs]] in descending order, followed by the block hash's index number in -descending order. When a [[ref:Gateway]] needs to reduce its [[ref:retained set]] of DID entries, it ****SHOULD**** -remove entries from the bottom of the list following this sort. +[[ref:Gateways]] supporting [[ref:Retention Set]] feature ****MUST**** provide an `expiry` value represented as a +[[ref:Unix Timestamp]] as a part of the [Gateway API](#gateway-api). This timestamp precisely indicates when the +[[ref:Gateway]] will cease republishing a particular DID, thus evicting the DID from the [[ref:Gateway]]'s Retained DID Set. +This timestamp establishes a binding agreement between the [[ref:Client]] and the [[ref:Gateway]], and it ****MUST NOT**** +be modified once set. -#### Reporting on Retention Status +Further, it is ****RECOMMENDED**** that [[ref:Gateway]]'s adhere to the following guidance: -[[ref:Gateways]] ****MUST**** include the approximate time until retention fall-off in the -[DID Resolution Metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata) of a resolved -[[ref:DID Document]], to aid [[ref:clients]] in being able to assess whether resubmission is required. +* To guarantee a sensible minimum retention period, it is ****RECOMMENDED**** that [[ref:Gateways]] retain DIDs for at +least **1 week** starting from the acceptance of [[ref:Retention Solution]]. -:::todo -[](https://github.com/TBD54566975/did-dht-method/issues/74) -Specify how gateways can report on retention guarantees and provide guidance for clients to work with these guarantees. -::: +* [[ref:Gateways]] ****MAY**** choose to offer an extended grace period before evicting DIDs, particularly for those +with a substantial history with the [[ref:Gateway]], such as DIDs exposed through the [Historical Resolution API](#historical-resolution). + +* [[ref:Gateways]] ****MAY**** choose to include the `expiry` value as a part of the +[DID Resolution Metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata), during [DID Resolution](#did-resolution), +to aid [[ref:Clients]] in being able to assess whether further proof of work is required. + +* [[ref:Gateways]] ****SHOULD**** treat each new valid [[ref:Retention Solution]] as extending a DID's retention period. +When a [[ref:Client]] submits a fresh [[ref:Retention Solution]] for a DID already in the [[ref:Retained DID Set]], the +[[ref:Gateway]] ****SHOULD**** update the DID's expiry timestamp, effectively resetting the expiry window and granting a +renewed retention period. + +* When a [[ref:Gateway]] reaches its storage capacity or experiences high load, it is ****RECOMMENDED**** that it significantly +increases the `difficulty` parameter for [[ref:Retention Challenges]]. By raising the `difficulty`, the [[ref:Gateway]] can +effectively limit the acceptance of new DIDs into its [[ref:Retained DID Set]], ensuring the stability and performance of the +server under resource constraints. ### Gateway API -At a minimum, a [[ref:Gateway]] ****MUST**** support the [Relay API](#relay) inspired by [[ref:Pkarr]], which is specified in the subsequent -section. +A conformant [[ref:Gateway]] ****MUST**** support the API defined in the following sections. -Expanding on this API, a fully conformant [[ref:Gateway]] ****MUST**** support the following API, which is made -available via an [OpenAPI document](#open-api-definition). +As a convenience this API is made available via an [OpenAPI document](#open-api-definition). -#### Relay +#### DHT -Public relays will need to set up [Cross-origin resource sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) headers as follows: +The DHT API, drawing inspiration from [[ref:Pkarr]], is built as an abstraction layer over [[ref:Mainline DHT]] +which enables the storage of [[ref:DNS Resource Records]] within [[ref:BEP44]] payloads. It is important to exercise +caution when utilizing the DHT API, as it **does not offer any assurances regarding data retention**. + +For this API, [[ref:Gateways]] will need to set up [Cross-origin resource sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) +headers as follows: - `Access-Control-Allow-Origin`: `*` - `Access-Control-Allow-Methods`: `GET`, `PUT`, `OPTIONS` @@ -805,22 +884,33 @@ On receiving a `GET` request the server submits a mutable get query to [[ref:Mai - `v` - represents between 0-1000 bytes of a [[ref:bencoded]] compressed DNS packet. - `404` - Record not found. -#### Get the Current Difficulty +#### Get the Current Challenge -Difficulty is exposed as an **OPTIONAL** endpoint based on support of [retention proofs](#retained-did-set). +Challenge is exposed as an endpoint to facilitate functionality pertaining to the [Retained DID Set](#retained-did-set), +surfacing a [[ref:Retention Challenge]]. - **Method:** `GET` -- **Path:** `/difficulty` +- **Path:** `/challenge` - **Returns:** `application/json` - `200` - Success. - - `hash` - **string** - **REQUIRED** - The current hash. - - `difficulty` - **integer** - **REQUIRED** - The current difficulty. - - `501` - Retention proofs not supported by this gateway. + - `hash` - **string** - **REQUIRED** - The current hash which is to be used as input for computing a [[ref:Retention Solution]]. + - `hash_source` - **string** - **REQUIRED** - The source of the hash as defined by the [Hash Source Registry](registry/index.html#hash-source). + - `difficulty` - **integer** - **REQUIRED** - The current difficulty of the challenge, representing the number of + bits of leading zeros the resulting hash must contain. + - `expiry` - **integer** - An _approximate_ expiry date-time value, if a valid [[ref:Retention Solution]] is + submitted against this challenge, which ****MUST**** be a [[ref:Unix Timestamp]] in seconds. The _precise_ expiry + date-time value is returned as a part of a [PUT operation](#register-or-update-a-did). + - `501` - [[ref:Retention Sets]] are not supported by this gateway. + - `503` - [[ref:Retention Sets]] have been temporarily disabled. + +**Example Challenge Response** ```json { "hash": "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7", - "difficulty": 26 + "hash_source": "bitcoin", + "difficulty": 26, + "expiry": 1715578600 } ``` @@ -836,27 +926,42 @@ Difficulty is exposed as an **OPTIONAL** endpoint based on support of [retention for each DID operation, which ****MUST**** be a [[ref:Unix Timestamp]] in seconds. - `v` - **string** - **REQUIRED** - An unpadded base64URL-encoded [[ref:bencoded]] compressed DNS packet containing the DID Document. - - `retention_proof` - **string** - **OPTIONAL** - A retention proof calculated according to the - [retention proof algorithm](#generating-a-retention-proof). + - `retention_solution` - **string** - **OPTIONAL** - A retention solution calculated according to the + [retention solution algorithm](#generating-a-retention-solution). - **Returns:** `application/json` - `202` - Accepted. The server has accepted the request as valid and will publish to the DHT. - - `400` - Invalid request. + - `expiry` - **string** - **OPTIONAL** – The [[ref:Unix Timestamp]] in seconds indicating when the DID will be evicted + from the [[ref:Gateway]]'s [[ref:Retained DID Set]]. + - `400` - Invalid request, including an invalid retention solution. - `401` - Invalid signature. - `409` - DID already exists with a higher [[ref:sequence number]]. DID may be accepted if the [[ref:Gateway]] supports [historical resolution](#historical-resolution). + - `503` - [[ref:Retention Sets]] have been temporarily disabled. + +**Example DID Registration Request** ```json { "did": "did:dht:example", "sig": "", "seq": 1234, - "v": "" + "v": "", + "retention_solution": "000000270b7c547aff552f302aad08200b3db815b69bc11ec3f263b7ef755a52" } ``` -Upon receiving a request to register a DID, the Gateway ****MUST**** verify the signature of the request and if valid -publish the DID Document to the DHT. If the DNS Packets contain a `_typ._did.` record, the [[ref:Gateway]] ****MUST**** -index the DID by its type. +Upon receiving a request to register a DID the [[ref:Gateway]] ****MUST**** perform the following steps: + +* Verify the signature of the request and, if valid, publish the [[ref:BEP44]] payload to the DHT. + +* If one is provided, [validate the `retention_solution`](#validating-a-retention-solution) and, if valid, +add the DID to the [[ref:Gateway]]'s [[ref:Retained DID Set]]. + +* If the DNS packet contain a `_typ._did.` record, update the specified indexes with the DID. + +:::note +Requests without a `retention_solution` have **no retention guarantees**. +::: #### Resolving a DID @@ -872,9 +977,13 @@ index the DID by its type. - `types` - **array** - **OPTIONAL** - An array of [type integers](#type-indexing) for the DID. - `sequence_numbers` - **array** - **OPTIONAL** - An sorted array of integers representing seen [[ref:sequence numbers]], used with [historical resolution](#historical-resolution). + - `expiry` - **integer** - **OPTIONAL** - The [[ref:Unix Timestamp]] in seconds indicating when the DID will be evicted + from the [[ref:Gateway]]'s [[ref:Retained DID Set]]. - `400` - Invalid request. - `404` - DID not found. +**Example DID Resolution Response** + ```json { "did": { @@ -902,28 +1011,44 @@ index the DID by its type. }, "dht": "", "types": [1, 4], - "sequence_numbers": [1700356854, 1700461736] + "sequence_numbers": [1700356854, 1700461736], + "expiry": 1715579444 } ``` -Upon receiving a request to resolve a DID, the [[ref:Gateway]] ****MUST**** query the DHT for the [[ref:DID Document]], -and if found, return the document. If the records are not found in the DHT, the [[ref:Gateway]] ****MAY**** fall back -to its local storage. If the DNS Packets contain a `_typ._did.` record, the [[ref:Gateway]] ****MUST**** return the -type index. +Upon receiving a request to resolve a DID, the [[ref:Gateway]] ****MUST**** perform the following steps: + +* Query local storage for the [[ref:DID]] record set, and if the [[ref:Gateway]] [is authoritative](#designating-authoritative-gateways) +for the DID, return the stored DIDs. + +* If the [[ref:Gateway]] is not authoritative for the DID, the [[ref:Gateway]] ****MUST**** query the DHT for the +[[ref:DID Document]], and if found, return the record set. If the records are not found in the DHT, the [[ref:Gateway]] +****MAY**** fall back to its local storage. + +* If the DNS Packet contains a `_typ._did.` record, the [[ref:Gateway]] ****MUST**** return the +associated `types` value. -This API is returns a `dht` property which matches the payload of a [Relay GET Request](#relay), +* If the [[ref:Gateway]] supports [historical resolution](#historical-resolution) and has multiple stored [[ref:Sequence Numbers]] +for the DID, the [[ref:Gateway]] ****MUST**** return the associated `sequence_number` value(s). + +* If the DID is in the [[ref:Gateway]]'s [[ref:Retained DID Set]], the [[ref:Gateway]] ****MUST**** return the +associated `expiry` value. + +:::note +This API returns a `dht` property which matches the payload of a [DHT GET Request](#DHT), when encoded as an unpadded base64URL string. Implementers are ****RECOMMENDED**** to verify the integrity of the response using the `dht` data and reconstruct the DID Document themselves. The `did` property is provided as a utility which, without independent verification, ****MUST NOT**** be trusted. +::: ##### Historical Resolution -[[ref:Gateways]] ****MAY**** choose to support historical resolution, which is to surface different versions of the -same [[ref:DID Document]], sorted by [[ref:sequence number]], according to the rules set out in the section on +[[ref:Gateways]] ****MAY**** choose to support _historical resolution_, which is to surface different versions of the +same [[ref:DID Document]], sorted by [[ref:Sequence Number]], according to the rules set out in the section on [conflict resolution](#conflict-resolution). -Upon [resolving a DID](#resolving-a-did), the Gateway will return the parameter `sequence_numbers` if there exists -historical state for a given [[ref:DID]]. The following API can be used with specific [[ref:sequence numbers]] to fetch +Upon [resolving a DID](#resolving-a-did), the [[ref:Gateway]] will return the value `sequence_numbers` if there exists +historical state for a given [[ref:DID]]. The following API can be used with specific [[ref:Sequence Numbers]] to fetch historical state: - **Method:** `GET` @@ -937,6 +1062,8 @@ historical state: payload, represented as 64 bytes sig, 8 bytes u64 big-endian seq, and 0-1000 bytes of v concatenated, enabling independent verification. - `types` - **array** - **OPTIONAL** - An array of [type integers](#type-indexing) for the DID. + - `expiry` - **integer** - **OPTIONAL** - The [[ref:Unix Timestamp]] in seconds indicating when the DID will be evicted + from the [[ref:Gateway]]'s [[ref:Retained DID Set]]. - `400` - Invalid request. - `404` - DID not found for the given [[ref:sequence number]]. - `501` - Historical resolution not supported by this gateway. @@ -964,6 +1091,8 @@ type(s) for the DID. - `description` - **string** - **REQUIRED** - A string describing the [type](#type-indexing). - `404` - Type indexing not supported. + +**Example Type Index Response** ```json [ { @@ -992,6 +1121,8 @@ type(s) for the DID. - `404` - Type not found. - `501` - Types not supported by this gateway. +**Example Type Response** + ```json [ "did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y", @@ -1017,13 +1148,13 @@ Interoperable DID methods ****MUST**** be registered in ### Conflict Resolution -Per [[ref:BEP44]], [[ref:Gateway]] servers can leverage the `seq` [[ref:sequence number]] to handle conflicts: +Per [[ref:BEP44]], [[ref:Gateway]] servers can leverage the `seq` [[ref:Sequence Number]] to handle conflicts: -> [[ref:Gateways]] receiving a put request where `seq` is lower than or equal to what's already stored on the server, -****MUST**** reject the request. If the [[ref:sequence number]] is equal, and the value is also the same, the server +> [[ref:Gateways]] receiving a put request where `seq` is lower than or equal to what's already stored on the [[ref:Gateway]], +****MUST**** reject the request. If the [[ref:Sequence Number]] is equal, and the value is also the same, the [[ref:Gateway]] ****SHOULD**** reset its timeout counter. -When the [[ref:sequence number]] is equal, but the value is different, servers need to decide which value to accept +When the [[ref:Sequence Number]] is equal, but the value is different, [[ref:Gateways]] need to decide which value to accept and which to reject. To make this determination [[ref:Gateways]] ****MUST**** compare the payloads lexicographically to determine a [lexicographical order](https://en.wikipedia.org/wiki/Lexicographic_order), and reject the payload with a **lower** lexicographical order. @@ -1067,15 +1198,16 @@ the [[ref:DID Document]] more concise. [[ref:Mainline]] offers a limited duration (approximately 2 hours) for retaining records in the DHT. To ensure the verifiability of data signed by a [[ref:DID]], consistent republishing of [[ref:DID Document]] records is crucial. To -address this, it is ****RECOMMENDED**** to use [[ref:Gateways]] equipped with [[ref:Retention Proofs]] support. +address this, it is ****RECOMMENDED**** for [[ref:Clients]] to use [[ref:Retention Challenges]] when interfacing +with [[ref:Gateways]]. ### Rate Limiting To reduce the risk of [Denial of Service Attacks](https://www.cisa.gov/news-events/news/understanding-denial-service-attacks), -spam, and other unwanted traffic, it is ****RECOMMENDED**** that [[ref:Gateways]] require [[ref:Retention Proofs]]. The -use of [[ref:Retention Proofs]] can act as an attack prevention measure, as it would be costly to scale retention proof -calculations. [[ref:Gateways]] ****MAY**** choose to explore other rate limiting techniques, such as IP-limiting, or an -access-token-based approach. +spam, and other unwanted traffic, it is ****RECOMMENDED**** that [[ref:Gateways]] require [[ref:Retention Challenges]] for +all requests. The use of [[ref:Retention Challenges]] can act as an attack prevention measure, as it would be costly to scale +retention challenge calculations. [[ref:Gateways]] ****MAY**** choose to explore other rate limiting techniques, such as +IP-limiting, or an access-token-based approaches. ### DID Resolution @@ -1084,36 +1216,39 @@ However, we provide additional guidance for [DID Resolvers](https://www.w3.org/T [DID Document Metadata](https://www.w3.org/TR/did-core/#did-document-metadata) and [DID Resolution Metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata) as follows: +::: todo +[](https://github.com/TBD54566975/did-dht-method/issues/136) +Register `types`, `gateway`, and `expiry` types in the [DID Specification Registry](https://www.w3.org/TR/did-spec-registries). +::: + #### DID Document Metadata * The metadata [`versionId` property](https://www.w3.org/TR/did-core/#dfn-versionid) ****MUST**** be set to the [[ref:DID Document]] packet's current [[ref:sequence number]]. * The metadata [`created` property](https://www.w3.org/TR/did-core/#dfn-created) ****MUST**** be set to -[XML Datetime](https://www.w3.org/TR/xmlschema11-2/#dateTime) representation of the earliest known sequence number +[XML Datetime](https://www.w3.org/TR/xmlschema11-2/#dateTime) representation of the earliest known [[ref:Sequence Number]] for the DID. * The metadata [`updated` property](https://www.w3.org/TR/did-core/#dfn-updated) ****MUST**** be set to the -[XML Datetime](https://www.w3.org/TR/xmlschema11-2/#dateTime) representation of the last known sequence number +[XML Datetime](https://www.w3.org/TR/xmlschema11-2/#dateTime) representation of the last known [[ref:Sequence Number]] for the DID. * If the [[ref:DID Document]] has [been deactivated](#deactivate) the [`deactivated` property](https://www.w3.org/TR/did-core/#dfn-deactivated) ****MUST**** be set to `true`. -#### DID Resolution Metadata - -* The metadata `types` property ****MUST**** be set to an array of strings representing type values if +* The metadata `types` property ****MUST**** be set to the array of strings representing type values, if [type data](#type-indexing) is present in the [[ref:DID Document]]'s packet. -* The metadata `gateway` property ****MUST**** be set to a string representing the [[ref:Gateway]]'s URI +* The metadata `expiry` property ****MUST**** be set to the integer representing the expiry date-time value +for the DID in the [[ref:Gateway]]'s [Retained DID Set](#retained-did-set). + +#### DID Resolution Metadata + +* The metaata `gateway` property ****MUST**** be set to the string representing the [[ref:Gateway]]'s URI from which the DID was resolved. This is useful in cases where a [DID Resolvers](https://www.w3.org/TR/did-core/#dfn-did-resolvers) performs resolution against an [Authoritative Gateway](#designating-authoritative-gateways). -::: todo -[](https://github.com/TBD54566975/did-dht-method/issues/136) -Register `types` and `gateway` in the [DID Specification Registry](https://www.w3.org/TR/did-spec-registries/#did-document-metadata). -::: - ## Security and Privacy Considerations When implementing and using the `did:dht` method, there are several security and privacy considerations to be aware of @@ -1121,10 +1256,10 @@ to ensure expected and legitimate behavior. ### Data Conflicts -Malicious actors may try to force [[ref:Gateways]] into uncertain states by manipulating the [[ref:sequence number]] +Malicious actors may try to force [[ref:Gateways]] into uncertain states by manipulating the [[ref:Sequence Number]] associated with a record set. There are three such cases to be aware of: -- **Low Sequence Number** - If a [[ref:Gateway]] has yet to see [[ref:sequence numbers]] for a given record it +- **Low Sequence Number** - If a [[ref:Gateway]] has yet to see [[ref:Sequence Numbers]] for a given record it ****MUST**** query its peers to see if they have encountered the record. If a peer is found who has encountered the record, the record with the latest sequence number must be selected. If the server has encountered greater [[ref:sequence numbers]] before, the server ****MAY**** reject the record set. If the server supports @@ -1135,8 +1270,8 @@ its historical ordered state. [[ref:Mainline Servers]] or [[ref:Gateways]]. Implementers are encouraged to follow the guidance outlined in [conflict resolution](#conflict-resolution). -- **High Sequence Number** - Since [[ref:sequence numbers]] ****MUST**** be second representations of a [[ref:Unix Timestamp]], -it is ****RECOMMENDED**** that [[ref:Gateways]] reject [[ref:sequence numbers]] that represent timestamps greater than +- **High Sequence Number** - Since [[ref:Sequence Numbers]] ****MUST**** be second representations of a [[ref:Unix Timestamp]], +it is ****RECOMMENDED**** that [[ref:Gateways]] reject [[ref:Sequence Numbers]] that represent timestamps greater than **2 hours** into the future to mitigate [timing attack](#data-conflicts) risks. ### Data Availability @@ -1503,6 +1638,6 @@ Z. O'Whielacronx; November 2002. [[def:VC-JOSE-COSE]] ~ [Securing Verifiable Credentials using JOSE and COSE](https://www.w3.org/TR/vc-jose-cose/). O. Steele, M. Jones, -M. Prorock, G. Cohen; 26 February 2024. [W3C](https://www.w3.org/). +M. Prorock, G. Cohen; 25 April 2024. [W3C](https://www.w3.org/). [[spec]]