···
DID Placeholder is a cryptographic, strongly-consistent, and recoverable [DID](https://www.w3.org/TR/did-core/) method.
5
-
### ⚠ README️ out of date ⚠️
We introduced DID Placeholder because we weren't totally satisfied with any of the existing DID methods.
We wanted a strongly consistent, highly available, recoverable, and cryptographically secure method with cheap and fast propagation of updates.
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.
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:
15
+
type DocumentData = {
17
+
rotationKeys: string[]
18
+
verificationMethods: Record<string, string>
19
+
alsoKnownAs: string[]
20
+
services: Record<string, Service>
20
-
Each DID document is made up of just four pieces of data (for now):
24
-
- `atpPds` (Personal Data Server for the related AT Protocol repository)
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.
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.
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.
35
+
An operation is of the shape:
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
40
-
Each operation contains a reference the the immediately preceding operation in the log and is signed by either the `signingKey` or the `recoveryKey`.
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.
`did:plc:${base32Encode(sha256(createOp)).slice(0,24)}`
Operations are verified, ordered and made available by the PLC server.
50
-
The PLC server is constrained in it's capabilities.
The operation logs are fully self-certifying, with the exception of their ordering.
Therefore, the PLC server's attacks are limited to:
- Denial of service: rejecting valid operations, or refusing to serve some information about the DID
- Misordering: In the event of a fork in DID document history, the server could choose to serve the "wrong" fork
57
-
### Signing and Recovery Keys
61
+
### DID Rotation & Account Recovery
63
+
Any key specified in `rotationKeys` has the ability to sign operations for the DID document.
65
+
These keys are solely a PLC concept and are _not_ included in the DID document.
67
+
Keys are listed in the document data in order of descending authority.
69
+
The PLC server provides a 72hr window during which a higher authority key can "rewrite" history.
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
59
-
Both the `signingKey` and the `recoveryKey` are permissioned to make changes to the DID document.
60
-
However, these keys are not equal.
62
-
As can be seen in the example document (below), only the `signingKey` is granted the ability to make assertions and invoke/delegate capabilities.
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`
66
-
### Account Recovery
78
+
In addition, you may resolve the constituent data by making a request to `https://plc.directory/:did/data`
68
-
The PLC server provides a 72hr window during which the `recoveryKey` can "rewrite" history.
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.
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`
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`
78
-
Consider the following operation log:
83
-
signingKey: 'did:key:zDnaejYFhgFiVF89LhJ4UipACLKuqo6PteZf8eKDVKeExXUPk',
84
-
recoveryKey: 'did:key:zDnaeSezF2TgCD71b5DiiFyhHQwKAfsBVqTTHRMvP597Z5Ztn',
85
-
handle: 'alice.example.com',
86
-
service: 'https://example.com',
88
-
sig: 'vi6JAl5W4FfyViD5_BKL9p0rbI3MxTWuh0g_egTFAjtf7gwoSfSe1O3qMOEUPX6QH3H0Q9M4y7gOLGblWkEwfQ'
91
+
// note: we use shorthand for keys for ease of reference, but consider them valid did:keys
93
+
// Genesis operation
95
+
type: 'plc_operation',
96
+
verificationMethods: {
97
+
atproto:"did:key:zSigningKey"
100
+
"did:key:zRecoveryKey",
101
+
"did:key:zRotationKey"
108
+
type: "AtprotoPersonalDataServer",
109
+
endpoint: "https://example.test"
113
+
sig: 'sig_from_did:key:zRotationKey'
116
+
// Operation to update recovery key
117
+
const updateKeys = {
118
+
type: 'plc_operation',
119
+
verificationMethods: {
120
+
atproto:"did:key:zSigningKey"
123
+
"did:key:zNewRecoveryKey",
124
+
"did:key:zRotationKey"
131
+
type: "AtprotoPersonalDataServer",
132
+
endpoint: "https://example.test"
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'
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"
97
-
type: 'update_atp_pds',
98
-
service: 'https://example2.com',
99
-
prev: 'bafyreickw7v7mwncrganw645agsmwjciolknt4f6f5an5wt3nrjepqaoiu',
100
-
sig: 'AS-APea3xxR5-sq2i5v9IOsgbM5G5qAnB92tExZ8Z4vEy_GQbV8jmfY7zTx76P88AVXInZsO6yWX4UO7_xAIfg'
147
+
"did:key:zAttackerKey"
154
+
type: "AtprotoPersonalDataServer",
155
+
endpoint: "https://example.test"
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'
162
+
// Valid recovery operation that "undoes" updateKeys
163
+
const recoveryOp = {
164
+
type: 'plc_operation',
165
+
verificationMethods: {
166
+
atproto:"did:key:zSigningKey"
109
-
type: 'rotate_recovery_key',
110
-
key: 'did:key:zDnaedvvAsDE6H3BDdBejpx9ve2Tz95cymyCAKF66JbyMh1Lt',
111
-
prev: 'bafyreiazzldal6642usrcowrpztb5gjb73qla343ifnt5dfbxz4swmf5vi',
112
-
sig: 'Um1GVZZT9JgB2SKEbwoF4_Sip05QjH7r_g-Hcx7lIY-OhIg88ZKcN_N4TgzljgBGwe6qZb0u_0Vaq0c-S2WSDg'
169
+
"did:key:zRecoveryKey"
176
+
type: "AtprotoPersonalDataServer",
177
+
endpoint: "https://example.test"
180
+
prev: CID(genesisOp),
181
+
sig: 'sig_from_did:key:zRecoveryKey'
117
-
The log produces the following document data:
185
+
## Presentation as DID Document
187
+
The following data:
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'
196
+
'did:key:zDnaedvvAsDE6H3BDdBejpx9ve2Tz95cymyCAKF66JbyMh1Lt',
197
+
'did:key:zDnaeh9v2RmcMo13Du2d6pjUf5bZwtauYxj3n9dYjw4EZUAR7'
204
+
type: "AtprotoPersonalDataServer",
205
+
endpoint: "https://example.test"
128
-
And the following DID document:
211
+
Will be presented as the following DID document:
···
'https://w3id.org/security/suites/ecdsa-2019/v1'
id: 'did:plc:7iza6de2dwap2sbkpav7c6c6',
136
-
alsoKnownAs: [ 'https://ali.example2.com' ],
220
+
alsoKnownAs: [ 'at://alice.test' ],
139
-
id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey',
type: 'EcdsaSecp256r1VerificationKey2019',
controller: 'did:plc:7iza6de2dwap2sbkpav7c6c6',
publicKeyMultibase: 'zSSa7w8s5aApu6td45gWTAAFkqCnaWY6ZsJ8DpyzDdYmVy4fARKqbn5F1UYBUMeVvYTBsoSoLvZnPdjd3pVHbmAHP'
145
-
id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#recoveryKey',
146
-
type: 'EcdsaSecp256r1VerificationKey2019',
147
-
controller: 'did:plc:7iza6de2dwap2sbkpav7c6c6',
148
-
publicKeyMultibase: 'zRV2EDDvop2r2aKWTcCtei3NvuNEnR5ucTVd9U4CSCnJEiha2QFyTjdxoFZ6629iHxhmTModThGQzX1495ZS6iD4V'
151
-
assertionMethod: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ],
152
-
capabilityInvocation: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ],
153
-
capabilityDelegation: [ 'did:plc:7iza6de2dwap2sbkpav7c6c6#signingKey' ],
156
-
id: 'did:plc:7iza6de2dwap2sbkpav7c6c6#atpPds',
157
-
type: 'AtpPersonalDataServer',
231
+
id: '#atproto_pds',
232
+
type: 'AtprotoPersonalDataServer',
serviceEndpoint: 'https://example2.com'