From 3fbb90ebc0cfcca17eca548555df6f53efe37d65 Mon Sep 17 00:00:00 2001 From: gabe Date: Sun, 12 May 2024 23:29:28 -0700 Subject: [PATCH 01/10] language on retained dids --- impl/internal/did/pow.go | 6 +- impl/internal/did/pow_test.go | 9 +- spec/spec.md | 234 ++++++++++++++++++++-------------- 3 files changed, 148 insertions(+), 101 deletions(-) diff --git a/impl/internal/did/pow.go b/impl/internal/did/pow.go index 3f5a7a47..6e6f38dd 100644 --- a/impl/internal/did/pow.go +++ b/impl/internal/did/pow.go @@ -29,10 +29,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) diff --git a/impl/internal/did/pow_test.go b/impl/internal/did/pow_test.go index 39e6dd26..2a072786 100644 --- a/impl/internal/did/pow_test.go +++ b/impl/internal/did/pow_test.go @@ -8,22 +8,23 @@ import ( ) func TestPOW(t *testing.T) { - // Example usage of computeRetentionProof + // Example usage of solveRetentionChallenge didIdentifier := "did:dht:test" - bitcoinBlockHash := "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" + inputHash := "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" // 26 leading zeros difficulty := 26 timer := time.Now() for nonce := 0; nonce < math.MaxInt; nonce++ { - hash, isValid := computeRetentionProof(didIdentifier, bitcoinBlockHash, difficulty, nonce) + hash, isValid := solveRetentionChallenge(didIdentifier, inputHash, difficulty, nonce) if isValid { fmt.Printf("Hash: %s\n", hash) - fmt.Printf("Valid Retention Proof: %v\n", isValid) + fmt.Printf("Valid Retention Challenge: %v\n", isValid) fmt.Printf("Nonce: %d\n", nonce) break } } + fmt.Printf("Time taken: %s\n", time.Since(timer)) } diff --git a/spec/spec.md b/spec/spec.md index ba5909ff..93532add 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 13, 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 | | ------------ | ---- | ------ | ------------------------------------------------------------ | @@ -311,8 +314,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,14 +489,14 @@ 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]]. @@ -564,7 +566,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 +617,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. @@ -702,64 +703,79 @@ One such registry is [provided by this specification](registry/index.html#gatewa 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). +[type index](#type-indexing) or supporting [historical resolution](#historical-resolution). 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 +subject to [a high volume of requests](#rate-limiting), we provie 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. - -#### Generating a Retention Proof - -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: +[[ref:Retention Challenge]], requires clients to generate a solution for the challenge — a [[ref:Retention Solution]] — for +write requests, which attest to an amount of work completed in exchange for a retention guarantee provided by a [[ref:Gateway]]. -1. Obtain a DID identifier and set it to `DID`. +This process 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. -2. Get the difficulty and recent hash from the server set to `DIFFICULTY` and `HASH`, respectively. +#### Managing the Retained DID Set -3. Generate a random 32-bit integer nonce value set to `NONCE`. +[[ref:Gateways]] supporting [[ref:Retention Set]] guidelines ****MUST**** provide an `expiry` value represented as a +[[ref:Unix Timestamp]]. 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. -4. Compute the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash over `ATTEMPT` where `ATTEMPT` = (`DID` + `HASH` + `NONCE`). +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]]. -5. Inspect the result of `ATTEMPT`, and ensure it has >= `DIFFICULTY` bits of leading zeroes. +[[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). - a. If so, `ATTEMPT` = `RETENTION_PROOF`. +[[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. - b. Otherwise, regenerate `NONCE` and go to step 3. +#### Generating a Retention Solution -6. Submit the `RETENTION_PROOF` to the [Gateway API](#register=or-update-a-did). +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 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-difficulty). The result of a given proof of work attempt is referred to as the +`retention` value. -#### Managing the Retained DID Set +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 ****MUST**** be no less than 26 bits of the 256-bit hash value. + +The algorithm, in detail, is as follows: + +1. Obtain a DID identifier and set it to `DID`. + +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`. -[[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. +4. Set `RETENTION` equal to the result of (`DID` + `HASH` + `NONCE`). -#### Reporting on Retention Status +5. Compute the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash over `ATTEMPT` where `ATTEMPT` = SHA256(`RETENTION_VALUE`). -[[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. +6. Inspect the result of `ATTEMPT`, and ensure it has >= `DIFFICULTY` bits of leading zeroes. -:::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. + a. If it does, set `RETENTION_SOLUTION` = `ATTEMPT`. + + b. Otherwise, return to step 3. + +7. 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]], compliant [[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 complete 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. ::: ### 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. +At a minimum, a [[ref:Gateway]] ****MUST**** support the [Relay API](#relay) inspired by [[ref:Pkarr]], which is specified +in the subsequent section. 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). @@ -807,7 +823,7 @@ On receiving a `GET` request the server submits a mutable get query to [[ref:Mai #### Get the Current Difficulty -Difficulty is exposed as an **OPTIONAL** endpoint based on support of [retention proofs](#retained-did-set). +Difficulty is exposed as an **OPTIONAL** endpoint based on support of the [Retained DID Set](#retained-did-set) feature. - **Method:** `GET` - **Path:** `/difficulty` @@ -815,12 +831,15 @@ Difficulty is exposed as an **OPTIONAL** endpoint based on support of [retention - `200` - Success. - `hash` - **string** - **REQUIRED** - The current hash. - `difficulty` - **integer** - **REQUIRED** - The current difficulty. - - `501` - Retention proofs not supported by this gateway. + - `expiry` - **integer** - An _approximate_ expiry date-time value which ****MUST**** be a [[ref:Unix Timestamp]] + in seconds. The _precise_ expiry date-time value is returned as a part of an [update operation](#register-or-update-a-did). + - `501` - [[ref:Retention Sets]] are not supported by this gateway. ```json { "hash": "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7", - "difficulty": 26 + "difficulty": 26, + "expiry": 1715578600 } ``` @@ -836,12 +855,14 @@ 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. + - `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. - - `401` - Invalid signature. + - `401` - Invalid signature or retention solution. - `409` - DID already exists with a higher [[ref:sequence number]]. DID may be accepted if the [[ref:Gateway]] supports [historical resolution](#historical-resolution). @@ -850,13 +871,18 @@ Difficulty is exposed as an **OPTIONAL** endpoint based on support of [retention "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` 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. #### Resolving a DID @@ -872,6 +898,8 @@ 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. @@ -902,28 +930,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: -This API is returns a `dht` property which matches the payload of a [Relay GET Request](#relay), +* 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. + +* 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 [Relay GET Request](#relay), 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 +981,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. @@ -1017,13 +1063,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,13 +1113,13 @@ 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**** to use [[ref:Gateways]] with support for a [Retained DID Set](#retained-did-set). ### 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 +spam, and other unwanted traffic, it is ****RECOMMENDED**** that [[ref:Gateways]] require [[ref:Retention Challenges]]. The +use of [[ref:Retention Challenges]] 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. From 6145410a5281572e6ef817286ae56733720ebe7d Mon Sep 17 00:00:00 2001 From: gabe Date: Mon, 13 May 2024 01:03:12 -0700 Subject: [PATCH 02/10] updates --- spec/api.yaml | 35 +++++++----- spec/spec.md | 146 +++++++++++++++++++++++++++++--------------------- 2 files changed, 108 insertions(+), 73 deletions(-) diff --git a/spec/api.yaml b/spec/api.yaml index f62e6b2b..65cb3f9a 100644 --- a/spec/api.yaml +++ b/spec/api.yaml @@ -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 @@ -112,9 +112,9 @@ paths: v: type: string description: A base64URL-encoded bencoded DNS packet containing the DID Document. - retention_proof: + 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": @@ -122,7 +122,10 @@ paths: content: application/json: schema: - type: string + type: object + properties: + expiry: + type: number "400": description: Invalid request. content: @@ -130,7 +133,7 @@ paths: schema: type: string "401": - description: Invalid signature. + description: Invalid signature or retention solution. content: application/json: schema: @@ -183,6 +186,9 @@ paths: items: type: integer 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. @@ -261,15 +267,15 @@ paths: 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 for responding to retention challenges. + 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 +283,17 @@ paths: properties: hash: type: string + description: "The current hash which is to be used as input for computing a retention solution." difficulty: type: integer - required: [ hash, difficulty ] + 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 ] "404": - description: Retention proofs not supported by this gateway. + description: Retention challenges are not supported by this gateway. content: application/json: schema: - type: string \ No newline at end of file + type: string \ No newline at end of file diff --git a/spec/spec.md b/spec/spec.md index 93532add..e7c7efd6 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -692,45 +692,29 @@ 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) or supporting [historical resolution](#historical-resolution). +As 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]]. -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 provie 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 Challenge]], requires clients to generate a solution for the challenge — a [[ref:Retention Solution]] — for -write requests, which attest to an amount of work completed in exchange for a retention guarantee provided by a [[ref:Gateway]]. +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). -This process 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. - -#### Managing the Retained DID Set - -[[ref:Gateways]] supporting [[ref:Retention Set]] guidelines ****MUST**** provide an `expiry` value represented as a -[[ref:Unix Timestamp]]. 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. - -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]]. - -[[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. +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 Solution @@ -738,11 +722,12 @@ A [[ref:Retention Solution]] is a form of [proof of work](https://en.bitcoin.it/ DID identifier, using input values supplied by 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-difficulty). The result of a given proof of work attempt is referred to as the +[supplied by the gateway](#get-the-current-challenge). 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 ****MUST**** be no less than 26 bits of the 256-bit hash value. +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, in detail, is as follows: @@ -754,7 +739,7 @@ The algorithm, in detail, is as follows: 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 `ATTEMPT` where `ATTEMPT` = SHA256(`RETENTION_VALUE`). +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. @@ -765,24 +750,47 @@ The algorithm, in detail, is as follows: 7. 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]], compliant [[ref:Gateways]] respond with an `expiry` timestamp. +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 complete 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. ::: +#### Managing the Retained DID Set + +[[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. + +Further, it is ****RECOMMENDED**** that [[ref:Gateway]]'s adhere to the following guidance: + +* 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]]. + +* [[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. + ### 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. + +As a convenicne, the API is made available via an [OpenAPI document](#open-api-definition). -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). +#### DHT -#### Relay +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**. -Public relays will need to set up [Cross-origin resource sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) headers as follows: +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` @@ -821,20 +829,23 @@ 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 the [Retained DID Set](#retained-did-set) feature. +Challenge is exposed as an endpoint to facilitate functionality pertaining to the [Retained DID Set](#retained-did-set). - **Method:** `GET` -- **Path:** `/difficulty` +- **Path:** `/challenge` - **Returns:** `application/json` - `200` - Success. - - `hash` - **string** - **REQUIRED** - The current hash. - - `difficulty` - **integer** - **REQUIRED** - The current difficulty. - - `expiry` - **integer** - An _approximate_ expiry date-time value which ****MUST**** be a [[ref:Unix Timestamp]] - in seconds. The _precise_ expiry date-time value is returned as a part of an [update operation](#register-or-update-a-did). + - `hash` - **string** - **REQUIRED** - The current hash which is to be used as input for computing a [[ref:Retention Solution]]. + - `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. +**Example Challenge Response** ```json { "hash": "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7", @@ -860,12 +871,13 @@ Difficulty is exposed as an **OPTIONAL** endpoint based on support of the [Retai - **Returns:** `application/json` - `202` - Accepted. The server has accepted the request as valid and will publish to the DHT. - `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]]. + from the [[ref:Gateway]]'s [[ref:Retained DID Set]]. - `400` - Invalid request. - `401` - Invalid signature or retention solution. - `409` - DID already exists with a higher [[ref:sequence number]]. DID may be accepted if the [[ref:Gateway]] supports [historical resolution](#historical-resolution). +**Example DID Registration Request** ```json { "did": "did:dht:example", @@ -884,6 +896,10 @@ Upon receiving a request to register a DID the [[ref:Gateway]] ****MUST**** perf * 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 - **Method:** `GET` @@ -899,10 +915,11 @@ Upon receiving a request to register a DID the [[ref:Gateway]] ****MUST**** perf - `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]]. + from the [[ref:Gateway]]'s [[ref:Retained DID Set]]. - `400` - Invalid request. - `404` - DID not found. +**Example DID Resolution Response** ```json { "did": { @@ -954,7 +971,7 @@ for the DID, the [[ref:Gateway]] ****MUST**** return the associated `sequence_nu associated `expiry` value. :::note -This API returns a `dht` property which matches the payload of a [Relay GET Request](#relay), +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. @@ -982,7 +999,7 @@ historical state: 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]]. + 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. @@ -1010,6 +1027,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 [ { @@ -1038,6 +1057,7 @@ type(s) for the DID. - `404` - Type not found. - `501` - Types not supported by this gateway. +**Example Type Response** ```json [ "did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y", @@ -1113,15 +1133,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]] with support for a [Retained DID Set](#retained-did-set). +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 Challenges]]. The -use of [[ref:Retention Challenges]] 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 @@ -1136,11 +1157,11 @@ However, we provide additional guidance for [DID Resolvers](https://www.w3.org/T [[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 @@ -1148,16 +1169,19 @@ for the DID. #### 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 `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). +* 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). + ::: 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). +Register `types`, `gateway`, and `expiry` types in the [DID Specification Registry](https://www.w3.org/TR/did-spec-registries/#did-document-metadata). ::: ## Security and Privacy Considerations From dff03603a988cbd9c8167fe79e6252ebb7d192e6 Mon Sep 17 00:00:00 2001 From: gabe Date: Mon, 13 May 2024 01:06:03 -0700 Subject: [PATCH 03/10] formatting --- spec/spec.md | 55 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/spec/spec.md b/spec/spec.md index e7c7efd6..0c102b85 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -650,9 +650,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: @@ -661,8 +661,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** @@ -709,8 +709,8 @@ A [[ref:Retained DID Set]] refers to the set of DIDs a [[ref:Gateway]] retains a 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). +— 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). 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 @@ -719,15 +719,15 @@ the overall reliability and effectiveness of [[ref:Gateways]] in managing DIDs. #### Generating a Retention Solution 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 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 result of a given proof of work attempt is referred to as the -`retention` value. +DID identifier, using input values supplied by 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 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 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, in detail, is as follows: @@ -750,11 +750,11 @@ The algorithm, in detail, is as follows: 7. 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 complete 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. +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. ::: #### Managing the Retained DID Set @@ -781,7 +781,7 @@ to aid [[ref:Clients]] in being able to assess whether further proof of work is A conformant [[ref:Gateway]] ****MUST**** support the API defined in the following sections. -As a convenicne, the API 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). #### DHT @@ -838,11 +838,11 @@ Challenge is exposed as an endpoint to facilitate functionality pertaining to th - **Returns:** `application/json` - `200` - Success. - `hash` - **string** - **REQUIRED** - The current hash which is to be used as input for computing a [[ref:Retention Solution]]. - - `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). + - `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. **Example Challenge Response** @@ -892,7 +892,8 @@ Upon receiving a request to register a DID the [[ref:Gateway]] ****MUST**** perf * 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` and, if valid, add the DID to the [[ref:Gateway]]'s [[ref:Retained DID Set]]. +* If one is provided, validate the `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. From a71458187e63bdab341af5a6c1381d38fb972eea Mon Sep 17 00:00:00 2001 From: gabe Date: Mon, 13 May 2024 21:09:07 -0700 Subject: [PATCH 04/10] updates --- impl/internal/did/pow.go | 24 ++++++++++ impl/internal/did/pow_test.go | 9 ++-- spec/api.yaml | 90 ++++++++++++++++++++--------------- spec/spec.md | 70 ++++++++++++++++++++++++--- 4 files changed, 145 insertions(+), 48 deletions(-) diff --git a/impl/internal/did/pow.go b/impl/internal/did/pow.go index 6e6f38dd..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" ) @@ -40,3 +41,26 @@ func solveRetentionChallenge(didIdentifier, inputHash string, difficulty, nonce // 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 2a072786..77950a48 100644 --- a/impl/internal/did/pow_test.go +++ b/impl/internal/did/pow_test.go @@ -17,11 +17,14 @@ func TestPOW(t *testing.T) { timer := time.Now() for nonce := 0; nonce < math.MaxInt; nonce++ { - hash, isValid := solveRetentionChallenge(didIdentifier, inputHash, difficulty, nonce) + solution, isValid := solveRetentionChallenge(didIdentifier, inputHash, difficulty, nonce) if isValid { - fmt.Printf("Hash: %s\n", hash) - fmt.Printf("Valid Retention Challenge: %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 } } diff --git a/spec/api.yaml b/spec/api.yaml index 65cb3f9a..cfdb064d 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: @@ -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,20 +105,20 @@ 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. + description: A base64URL-encoded bencoded DNS packet containing the DID Document retention_solution: type: string - description: A retention solution calculated according to the spec-defined retention solution 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: @@ -127,13 +127,13 @@ paths: expiry: type: number "400": - description: Invalid request. + description: Invalid request content: application/json: schema: type: string "401": - description: Invalid signature or retention solution. + description: Invalid signature or retention solution content: application/json: schema: @@ -144,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: @@ -172,32 +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." + 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: @@ -206,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: @@ -224,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: @@ -233,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 @@ -244,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: @@ -262,7 +268,7 @@ paths: items: type: string "404": - description: Type not found. + description: Type not found content: application/json: schema: @@ -271,11 +277,11 @@ paths: get: tags: - DID - summary: Get information necessary for responding to retention challenges. - description: Get information needed to calculate a retention solution for DID PUT operations. + summary: Get information necessary for responding to retention challenges + 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 solution against, along with an estimated retention guarantee represented by the expiry property. + 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: @@ -283,16 +289,22 @@ paths: properties: hash: type: string - description: "The current hash which is to be used as input for computing a retention solution." + description: The current hash which is to be used as input for computing a retention solution difficulty: type: integer - description: "The current difficulty of the challenge representing the number of bits of leading zeros the resulting hash must contain." + 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." + 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 ] - "404": - description: Retention challenges are not supported by this gateway. + "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: diff --git a/spec/spec.md b/spec/spec.md index 0c102b85..51a5a5fc 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -729,7 +729,7 @@ The resulting `retention` value is determined to be a valid [[ref:Retention Solu 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, in detail, is as follows: +The algorithm for generating a [[ref:Retention Solution]] is as follows: 1. Obtain a DID identifier and set it to `DID`. @@ -747,7 +747,9 @@ The algorithm, in detail, is as follows: b. Otherwise, return to step 3. -7. Submit the `RETENTION_SOLUTION` to the [Gateway API](#register=or-update-a-did) for write operations. +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` @@ -757,6 +759,46 @@ they solve a new [[ref:Retention Challenge]] before the expiration is reached. B maintain the continuity of their DID's retention and prevent unintended eviction of their identifier. ::: +#### Validating a Retention Solution + +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. + +The algorithm for validating a [[ref:Retention Solution]] is as follows: + +1. Extract the `RETENTION_SOLUTION` value from the write request. + +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]] supporting [[ref:Retention Set]] feature ****MUST**** provide an `expiry` value represented as a @@ -777,6 +819,16 @@ with a substantial history with the [[ref:Gateway]], such as DIDs exposed throug [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 A conformant [[ref:Gateway]] ****MUST**** support the API defined in the following sections. @@ -844,8 +896,10 @@ Challenge is exposed as an endpoint to facilitate functionality pertaining to th 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", @@ -872,12 +926,14 @@ Challenge is exposed as an endpoint to facilitate functionality pertaining to th - `202` - Accepted. The server has accepted the request as valid and will publish to the DHT. - `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. - - `401` - Invalid signature or retention solution. + - `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", @@ -892,8 +948,8 @@ Upon receiving a request to register a DID the [[ref:Gateway]] ****MUST**** perf * 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` and, if valid, add the DID to the [[ref:Gateway]]'s -[[ref:Retained DID Set]]. +* 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. @@ -921,6 +977,7 @@ Requests without a `retention_solution` have **no retention guarantees**. - `404` - DID not found. **Example DID Resolution Response** + ```json { "did": { @@ -1059,6 +1116,7 @@ type(s) for the DID. - `501` - Types not supported by this gateway. **Example Type Response** + ```json [ "did:dht:i9xkp8ddcbcg8jwq54ox699wuzxyifsqx4jru45zodqu453ksz6y", From 5f6f148d0b26bf98f39fbd4a40aa647335f9addf Mon Sep 17 00:00:00 2001 From: gabe Date: Mon, 13 May 2024 21:16:53 -0700 Subject: [PATCH 05/10] update --- spec/spec.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/spec.md b/spec/spec.md index 51a5a5fc..9f0857c8 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -1250,10 +1250,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 @@ -1264,8 +1264,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 @@ -1632,6 +1632,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]] From ddb608c4e209153e0c4f992879b7f52f0be1e326 Mon Sep 17 00:00:00 2001 From: gabe Date: Mon, 13 May 2024 21:18:00 -0700 Subject: [PATCH 06/10] one more --- spec/spec.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/spec.md b/spec/spec.md index 9f0857c8..daf00cef 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -883,7 +883,8 @@ On receiving a `GET` request the server submits a mutable get query to [[ref:Mai #### Get the Current Challenge -Challenge is exposed as an endpoint to facilitate functionality pertaining to the [Retained DID Set](#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:** `/challenge` From f236b7e0b9076e82ac205bfd980514eb8e157666 Mon Sep 17 00:00:00 2001 From: Gabe <7622243+decentralgabe@users.noreply.github.com> Date: Tue, 14 May 2024 11:02:26 -0700 Subject: [PATCH 07/10] Update spec/api.yaml Co-authored-by: Kendall Weihe --- spec/api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api.yaml b/spec/api.yaml index cfdb064d..f6bff714 100644 --- a/spec/api.yaml +++ b/spec/api.yaml @@ -277,7 +277,7 @@ paths: get: tags: - DID - summary: Get information necessary for responding to retention challenges + summary: Get information necessary to solve a retention challenge description: Get information needed to calculate a retention solution for DID PUT operations responses: "200": From d375e5e03d822d93bb60096fd1996251f300634b Mon Sep 17 00:00:00 2001 From: Gabe <7622243+decentralgabe@users.noreply.github.com> Date: Tue, 14 May 2024 11:49:10 -0700 Subject: [PATCH 08/10] Apply suggestions from code review Co-authored-by: Frank Hinek --- spec/spec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec.md b/spec/spec.md index daf00cef..0c2c0775 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -701,7 +701,7 @@ As a convenience, one such registry is [provided by this specification](registry ### Retained DID Set -As feature of the DID DHT Method, operators of a [[ref:Gateway]] ****MUST**** support retaining DIDs for extended periods +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]]. @@ -719,7 +719,7 @@ the overall reliability and effectiveness of [[ref:Gateways]] in managing DIDs. #### Generating a Retention Solution 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 given [gateway](registry/index.html#gateways). The proof of work is +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 result of a given proof of work attempt is From 497f60b46b243f35610f40f2e057623a4e681876 Mon Sep 17 00:00:00 2001 From: gabe Date: Tue, 14 May 2024 12:01:17 -0700 Subject: [PATCH 09/10] clarity on identity key --- impl/internal/did/pow_test.go | 2 +- spec/spec.md | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/impl/internal/did/pow_test.go b/impl/internal/did/pow_test.go index 77950a48..c8be236b 100644 --- a/impl/internal/did/pow_test.go +++ b/impl/internal/did/pow_test.go @@ -10,7 +10,7 @@ import ( func TestPOW(t *testing.T) { // Example usage of solveRetentionChallenge didIdentifier := "did:dht:test" - inputHash := "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" + inputHash := "123bcdefghobc23567822be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7" // 26 leading zeros difficulty := 26 diff --git a/spec/spec.md b/spec/spec.md index 0c2c0775..5645c239 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -501,10 +501,13 @@ To create a `did:dht` document, the process is as follows: 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 document can include any number of other [core properties](https://www.w3.org/TR/did-core/#core-properties); + b. The [[ref:Identity Key]] ****MUST**** have the [Verification Relationships](#verification-relationships) + _Authentication_, _Assertion_, _Capability Invocation_, and _Capability Delegation_. + + 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). From 6c5a8134a5a675944c6ae238ef0010c9e4dab502 Mon Sep 17 00:00:00 2001 From: gabe Date: Tue, 14 May 2024 12:23:52 -0700 Subject: [PATCH 10/10] hash source registry --- spec/api.yaml | 3 +++ spec/registry/spec.md | 24 ++++++++++++++++-------- spec/spec.md | 38 ++++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/spec/api.yaml b/spec/api.yaml index f6bff714..31107766 100644 --- a/spec/api.yaml +++ b/spec/api.yaml @@ -290,6 +290,9 @@ paths: 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 description: The current difficulty of the challenge representing the number of bits of leading zeros the resulting hash must contain 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 5645c239..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 13, 2024 +**Last Updated:** May 14, 2024 **Editors:** ~ [Gabe Cohen](https://github.com/decentralgabe) @@ -255,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** @@ -725,8 +724,9 @@ A [[ref:Retention Solution]] is a form of [proof of work](https://en.bitcoin.it/ 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 result of a given proof of work attempt is -referred to as the `retention` value. +`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 @@ -894,6 +894,7 @@ surfacing a [[ref:Retention Challenge]]. - **Returns:** `application/json` - `200` - Success. - `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 @@ -907,6 +908,7 @@ surfacing a [[ref:Retention Challenge]]. ```json { "hash": "000000000000000000022be0c55caae4152d023dd57e8d63dc1a55c1f6de46e7", + "hash_source": "bitcoin", "difficulty": 26, "expiry": 1715578600 } @@ -1214,6 +1216,11 @@ 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 @@ -1230,22 +1237,17 @@ 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 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 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). - * 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). -::: 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). -::: +#### 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). ## Security and Privacy Considerations