Fork of github.com/did-method-plc/did-method-plc
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

updated readme

dholms c1d0afd9 79cda64f

+160 -85
+160 -85
README.md
··· 2 2 3 3 DID Placeholder is a cryptographic, strongly-consistent, and recoverable [DID](https://www.w3.org/TR/did-core/) method. 4 4 5 - ### ⚠ README️ out of date ⚠️ 6 - 7 5 ## Motivation 8 6 9 7 We introduced DID Placeholder because we weren't totally satisfied with any of the existing DID methods. 10 8 We wanted a strongly consistent, highly available, recoverable, and cryptographically secure method with cheap and fast propagation of updates. 11 9 12 - We cheekily titled the method "Placeholder", because we _don't_ want it to stick around. We're actively hoping to replace it with something less centralized. 13 - We expect a method to emerge that fits the bill within the next few years, likely a permissioned DID consortium. 10 + We titled the method "Placeholder", because we _don't_ want it to stick around in its current form. We're actively hoping to replace it with or evolve it into something less centralized - likely a permissioned DID consortium. 14 11 15 12 ## How it works 16 - This is not a fully-expressive DID format. 17 - Though it adheres to the DID spec, it is domain-specific and only allows for representing specific data types in a specific manner. 18 - There is the possibility that it could be extended to be more general in the future. 13 + Each DID document can be described by a JSON object of the following format: 14 + ```ts 15 + type DocumentData = { 16 + did: string 17 + rotationKeys: string[] 18 + verificationMethods: Record<string, string> 19 + alsoKnownAs: string[] 20 + services: Record<string, Service> 21 + } 19 22 20 - Each DID document is made up of just four pieces of data (for now): 21 - - `signingKey` 22 - - `recoveryKey` 23 - - `handle` 24 - - `atpPds` (Personal Data Server for the related AT Protocol repository) 23 + type Service = { 24 + type: string 25 + endpoint: string 26 + } 27 + ``` 25 28 26 - DID documents are derived from a log of signed operations, ordered by the PLC server. 29 + Keys are notated using [did:key](https://w3c-ccg.github.io/did-method-key/) and only secp256k1 and NIST P-256 are currently supported. 27 30 28 - There are 5 operations that can be found in each log: `create`, `rotate_signing_key`, `rotate_recovery_key`, `update_handle`, and `update_atp_pds`. 31 + This data is a succinct format that can be directly translated to a valid DID document. 29 32 30 - Each operation is of the shape: 33 + Each operation fully attests the current state of the document data. It also includes a reference to the previous operation in the log using a sha256 [CID](https://github.com/multiformats/cid). Each operation also includes a `base64url` encoded signature of the cbor-encoded operation from a valid rotation key. 34 + 35 + An operation is of the shape: 31 36 ```ts 32 37 type Operation = { 33 - type: string // operation type 34 - prev: CID | null // pointer to the CID of the previous operation in the log 35 - sig: string // base64url encoded signature of the operation 36 - ... // other operation-specific data 38 + type: 'plc_operation', 39 + rotationKeys: string[] 40 + verificationMethods: Record<string, string> 41 + alsoKnownAs: string[] 42 + services: Record<string, Service> 43 + prev: CID | null // null if genesis operation 44 + sig: string 37 45 } 38 46 ``` 39 47 40 - Each operation contains a reference the the immediately preceding operation in the log and is signed by either the `signingKey` or the `recoveryKey`. 41 - 42 - The DID itself is derived from the sha256 hash of the first operation in the log. 43 - It is then base32 encoded and truncated to 24 chars. 48 + The DID itself is derived from the sha256 hash of the first operation in the log. It is then base32 encoded and truncated to 24 chars. 44 49 45 50 To illustrate: 46 51 `did:plc:${base32Encode(sha256(createOp)).slice(0,24)}` 47 52 48 53 Operations are verified, ordered and made available by the PLC server. 49 54 50 - The PLC server is constrained in it's capabilities. 51 55 The operation logs are fully self-certifying, with the exception of their ordering. 52 56 53 57 Therefore, the PLC server's attacks are limited to: 54 58 - Denial of service: rejecting valid operations, or refusing to serve some information about the DID 55 59 - Misordering: In the event of a fork in DID document history, the server could choose to serve the "wrong" fork 56 60 57 - ### Signing and Recovery Keys 61 + ### DID Rotation & Account Recovery 62 + 63 + Any key specified in `rotationKeys` has the ability to sign operations for the DID document. 64 + 65 + These keys are solely a PLC concept and are _not_ included in the DID document. 66 + 67 + Keys are listed in the document data in order of descending authority. 68 + 69 + The PLC server provides a 72hr window during which a higher authority key can "rewrite" history. 70 + 71 + To do so, that key must sign a new operation that points to the CID of the last "valid" operation - ie the fork point. This operation will be accepted as long as it was within 72hrs of the pointed to operation & the key that signed it is at a lower index in the `rotationKeys` array than the key that signed the to-be-invalidated operation 58 72 59 - Both the `signingKey` and the `recoveryKey` are permissioned to make changes to the DID document. 60 - However, these keys are not equal. 61 73 62 - As can be seen in the example document (below), only the `signingKey` is granted the ability to make assertions and invoke/delegate capabilities. 74 + ### Resolution 63 75 64 - The recovery key on the other hand is capable of performing a "recovery operation" 76 + Dids are resolved by making a GET request to `https://plc.directory/:did` 65 77 66 - ### Account Recovery 78 + In addition, you may resolve the constituent data by making a request to `https://plc.directory/:did/data` 67 79 68 - The PLC server provides a 72hr window during which the `recoveryKey` can "rewrite" history. 80 + ### Auditability 69 81 70 - This is to be used in adversarial situations in which a user's `signingKey` leaks or is being held by some custodian who turns out to be a bad actor. 82 + As an additional check against the PLC server and to promote resiliency, the entire database of PLC is auditable. 71 83 72 - In a situation such as this, the `recoveryKey` may be used to rotate both the `signingKey` and `recoveryKey`. 84 + The audit history of a given DID (complete with timestamps & invalidated forked histories) can be found at: `https://plc.directory/:did/log/audit` 73 85 74 - If a user wishes to recover from this situation, they sign a new operation rotating the `signingKey` to a key that they hold and set the `prev` of that operation to point to the most recent pre-attack operation. 86 + The entire history of PLC operations may be downloaded as a paginated series of jsonlines at `https://plc.directory/export` 75 87 76 88 ## Example 77 89 78 - Consider the following operation log: 79 90 ```ts 80 - [ 81 - { 82 - type: 'create', 83 - signingKey: 'did:key:zDnaejYFhgFiVF89LhJ4UipACLKuqo6PteZf8eKDVKeExXUPk', 84 - recoveryKey: 'did:key:zDnaeSezF2TgCD71b5DiiFyhHQwKAfsBVqTTHRMvP597Z5Ztn', 85 - handle: 'alice.example.com', 86 - service: 'https://example.com', 87 - prev: null, 88 - sig: 'vi6JAl5W4FfyViD5_BKL9p0rbI3MxTWuh0g_egTFAjtf7gwoSfSe1O3qMOEUPX6QH3H0Q9M4y7gOLGblWkEwfQ' 91 + // note: we use shorthand for keys for ease of reference, but consider them valid did:keys 92 + 93 + // Genesis operation 94 + const genesisOp = { 95 + type: 'plc_operation', 96 + verificationMethods: { 97 + atproto:"did:key:zSigningKey" 98 + }, 99 + rotationKeys: [ 100 + "did:key:zRecoveryKey", 101 + "did:key:zRotationKey" 102 + ], 103 + alsoKnownAs: [ 104 + "at://alice.test" 105 + ], 106 + services: { 107 + atproto_pds: { 108 + type: "AtprotoPersonalDataServer", 109 + endpoint: "https://example.test" 110 + } 111 + }, 112 + prev: null, 113 + sig: 'sig_from_did:key:zRotationKey' 114 + } 115 + 116 + // Operation to update recovery key 117 + const updateKeys = { 118 + type: 'plc_operation', 119 + verificationMethods: { 120 + atproto:"did:key:zSigningKey" 121 + }, 122 + rotationKeys: [ 123 + "did:key:zNewRecoveryKey", 124 + "did:key:zRotationKey" 125 + ], 126 + alsoKnownAs: [ 127 + "at://alice.test" 128 + ], 129 + services: { 130 + atproto_pds: { 131 + type: "AtprotoPersonalDataServer", 132 + endpoint: "https://example.test" 133 + } 89 134 }, 90 - { 91 - type: 'update_handle', 92 - handle: 'ali.example2.com', 93 - prev: 'bafyreih2gihqzgq5qd6uqktyfpyxqxvpdnrpu2qunnkaxugbyquxumisuq', 94 - sig: 'KL98ORpGmAJTqDsC9mWAYbhoDIv_-eZ3Nv0YqiPkbgx0ra96gYa3fQhIpZVxXFyNbu_4Y3JhPCvyJb8yDMe9Sg' 135 + prev: CID(genesisOp), 136 + sig: 'sig_from_did:key:zRotationKey' 137 + } 138 + 139 + // Invalid operation that will be rejected 140 + // because did:key:zAttackerKey is not listed in rotationKeys 141 + const invalidUpdate = { 142 + type: 'plc_operation', 143 + verificationMethods: { 144 + atproto:"did:key:zAttackerKey" 95 145 }, 96 - { 97 - type: 'update_atp_pds', 98 - service: 'https://example2.com', 99 - prev: 'bafyreickw7v7mwncrganw645agsmwjciolknt4f6f5an5wt3nrjepqaoiu', 100 - sig: 'AS-APea3xxR5-sq2i5v9IOsgbM5G5qAnB92tExZ8Z4vEy_GQbV8jmfY7zTx76P88AVXInZsO6yWX4UO7_xAIfg' 146 + rotationKeys: [ 147 + "did:key:zAttackerKey" 148 + ], 149 + alsoKnownAs: [ 150 + "at://bob.test" 151 + ], 152 + services: { 153 + atproto_pds: { 154 + type: "AtprotoPersonalDataServer", 155 + endpoint: "https://example.test" 156 + } 101 157 }, 102 - { 103 - type: 'rotate_signing_key', 104 - key: 'did:key:zDnaeh9v2RmcMo13Du2d6pjUf5bZwtauYxj3n9dYjw4EZUAR7', 105 - prev: 'bafyreictfsrkdt5azni355vapqka5a7erqjsa3vv7iaf52yjlqqbzkwgga', 106 - sig: 'VvcCoYVDluLZghv3i6ARyk1r7m1M32BPryJlTma1HTOx2CdbmIOUkVUbFa2LWi571fe-2yjTWY0IEAKfRiPAZg' 158 + prev: CID(updateKeys), 159 + sig: 'sig_from_did:key:zAttackerKey' 160 + } 161 + 162 + // Valid recovery operation that "undoes" updateKeys 163 + const recoveryOp = { 164 + type: 'plc_operation', 165 + verificationMethods: { 166 + atproto:"did:key:zSigningKey" 107 167 }, 108 - { 109 - type: 'rotate_recovery_key', 110 - key: 'did:key:zDnaedvvAsDE6H3BDdBejpx9ve2Tz95cymyCAKF66JbyMh1Lt', 111 - prev: 'bafyreiazzldal6642usrcowrpztb5gjb73qla343ifnt5dfbxz4swmf5vi', 112 - sig: 'Um1GVZZT9JgB2SKEbwoF4_Sip05QjH7r_g-Hcx7lIY-OhIg88ZKcN_N4TgzljgBGwe6qZb0u_0Vaq0c-S2WSDg' 113 - } 114 - ] 168 + rotationKeys: [ 169 + "did:key:zRecoveryKey" 170 + ], 171 + alsoKnownAs: [ 172 + "at://alice.test" 173 + ], 174 + services: { 175 + atproto_pds: { 176 + type: "AtprotoPersonalDataServer", 177 + endpoint: "https://example.test" 178 + } 179 + }, 180 + prev: CID(genesisOp), 181 + sig: 'sig_from_did:key:zRecoveryKey' 182 + } 115 183 ``` 116 184 117 - The log produces the following document data: 185 + ## Presentation as DID Document 186 + 187 + The following data: 188 + 118 189 ```ts 119 190 { 120 191 did: 'did:plc:7iza6de2dwap2sbkpav7c6c6', 121 - signingKey: 'did:key:zDnaeh9v2RmcMo13Du2d6pjUf5bZwtauYxj3n9dYjw4EZUAR7', 122 - recoveryKey: 'did:key:zDnaedvvAsDE6H3BDdBejpx9ve2Tz95cymyCAKF66JbyMh1Lt', 123 - handle: 'ali.example2.com', 124 - atpPds: 'https://example2.com' 192 + verificationMethods: { 193 + atproto: 'did:key:zDnaeh9v2RmcMo13Du2d6pjUf5bZwtauYxj3n9dYjw4EZUAR7' 194 + }, 195 + rotationKeys: [ 196 + 'did:key:zDnaedvvAsDE6H3BDdBejpx9ve2Tz95cymyCAKF66JbyMh1Lt', 197 + 'did:key:zDnaeh9v2RmcMo13Du2d6pjUf5bZwtauYxj3n9dYjw4EZUAR7' 198 + ], 199 + alsoKnownAs: [ 200 + 'at://alice.test' 201 + ], 202 + services: { 203 + atproto_pds: { 204 + type: "AtprotoPersonalDataServer", 205 + endpoint: "https://example.test" 206 + } 207 + } 125 208 } 126 209 ``` 127 210 128 - And the following DID document: 211 + Will be presented as the following DID document: 212 + 129 213 ```ts 130 214 { 131 215 '@context': [ ··· 133 217 'https://w3id.org/security/suites/ecdsa-2019/v1' 134 218 ], 135 219 id: 'did:plc:7iza6de2dwap2sbkpav7c6c6', 136 - alsoKnownAs: [ 'https://ali.example2.com' ], 220 + alsoKnownAs: [ 'at://alice.test' ], 137 221 verificationMethod: [ 138 222 { 139 - id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey', 223 + id: '#atproto', 140 224 type: 'EcdsaSecp256r1VerificationKey2019', 141 225 controller: 'did:plc:7iza6de2dwap2sbkpav7c6c6', 142 226 publicKeyMultibase: 'zSSa7w8s5aApu6td45gWTAAFkqCnaWY6ZsJ8DpyzDdYmVy4fARKqbn5F1UYBUMeVvYTBsoSoLvZnPdjd3pVHbmAHP' 143 - }, 144 - { 145 - id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#recoveryKey', 146 - type: 'EcdsaSecp256r1VerificationKey2019', 147 - controller: 'did:plc:7iza6de2dwap2sbkpav7c6c6', 148 - publicKeyMultibase: 'zRV2EDDvop2r2aKWTcCtei3NvuNEnR5ucTVd9U4CSCnJEiha2QFyTjdxoFZ6629iHxhmTModThGQzX1495ZS6iD4V' 149 227 } 150 228 ], 151 - assertionMethod: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ], 152 - capabilityInvocation: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ], 153 - capabilityDelegation: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ], 154 229 service: [ 155 230 { 156 - id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#atpPds', 157 - type: 'AtpPersonalDataServer', 231 + id: '#atproto_pds', 232 + type: 'AtprotoPersonalDataServer', 158 233 serviceEndpoint: 'https://example2.com' 159 234 } 160 235 ]