···
1
-
import { check, cidForCbor } from '@atproto/common'
1
+
import * as uint8arrays from 'uint8arrays'
import { EcdsaKeypair, parseDidKey, Secp256k1Keypair } from '@atproto/crypto'
3
-
import * as uint8arrays from 'uint8arrays'
import * as document from '../src/document'
5
-
import * as operations from '../src/operations'
import * as t from '../src/types'
8
-
describe('plc DID document', () => {
9
-
const ops: t.Operation[] = []
11
-
let signingKey: EcdsaKeypair
12
-
let recoveryKey: Secp256k1Keypair
14
-
let handle = 'alice.example.com'
15
-
let atpPds = 'https://example.com'
17
-
let oldSigningKey: EcdsaKeypair
18
-
let oldRecoveryKey: Secp256k1Keypair
20
-
beforeAll(async () => {
21
-
signingKey = await EcdsaKeypair.create()
22
-
recoveryKey = await Secp256k1Keypair.create()
25
-
it('creates a valid create op', async () => {
26
-
const createOp = await operations.create(
32
-
const isValid = check.is(createOp, t.def.createOp)
33
-
expect(isValid).toBeTruthy()
35
-
did = await operations.didForCreateOp(createOp)
38
-
it('parses an operation log with no updates', async () => {
39
-
const doc = await document.validateOperationLog(did, ops)
41
-
expect(doc.did).toEqual(did)
42
-
expect(doc.signingKey).toEqual(signingKey.did())
43
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
44
-
expect(doc.handle).toEqual(handle)
45
-
expect(doc.atpPds).toEqual(atpPds)
48
-
it('allows for updating handle', async () => {
49
-
handle = 'ali.example2.com'
50
-
const prev = await cidForCbor(ops[ops.length - 1])
51
-
const op = await operations.updateHandle(
58
-
const doc = await document.validateOperationLog(did, ops)
59
-
expect(doc.did).toEqual(did)
60
-
expect(doc.signingKey).toEqual(signingKey.did())
61
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
62
-
expect(doc.handle).toEqual(handle)
63
-
expect(doc.atpPds).toEqual(atpPds)
66
-
it('allows for updating atpPds', async () => {
67
-
atpPds = 'https://example2.com'
68
-
const prev = await cidForCbor(ops[ops.length - 1])
69
-
const op = await operations.updateAtpPds(
76
-
const doc = await document.validateOperationLog(did, ops)
77
-
expect(doc.did).toEqual(did)
78
-
expect(doc.signingKey).toEqual(signingKey.did())
79
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
80
-
expect(doc.handle).toEqual(handle)
81
-
expect(doc.atpPds).toEqual(atpPds)
84
-
it('allows for rotating signingKey', async () => {
85
-
const newSigningKey = await EcdsaKeypair.create()
86
-
const prev = await cidForCbor(ops[ops.length - 1])
87
-
const op = await operations.rotateSigningKey(
88
-
newSigningKey.did(),
93
-
oldSigningKey = signingKey
94
-
signingKey = newSigningKey
96
-
const doc = await document.validateOperationLog(did, ops)
97
-
expect(doc.did).toEqual(did)
98
-
expect(doc.signingKey).toEqual(signingKey.did())
99
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
100
-
expect(doc.handle).toEqual(handle)
101
-
expect(doc.atpPds).toEqual(atpPds)
104
-
it('no longer allows operations from old signing key', async () => {
105
-
const prev = await cidForCbor(ops[ops.length - 1])
106
-
const op = await operations.updateHandle(
111
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
114
-
it('allows for rotating recoveryKey', async () => {
115
-
const newRecoveryKey = await Secp256k1Keypair.create()
116
-
const prev = await cidForCbor(ops[ops.length - 1])
117
-
const op = await operations.rotateRecoveryKey(
118
-
newRecoveryKey.did(),
123
-
oldRecoveryKey = recoveryKey
124
-
recoveryKey = newRecoveryKey
126
-
const doc = await document.validateOperationLog(did, ops)
127
-
expect(doc.did).toEqual(did)
128
-
expect(doc.signingKey).toEqual(signingKey.did())
129
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
130
-
expect(doc.handle).toEqual(handle)
131
-
expect(doc.atpPds).toEqual(atpPds)
134
-
it('no longer allows operations from old recovery key', async () => {
135
-
const prev = await cidForCbor(ops[ops.length - 1])
136
-
const op = await operations.updateHandle(
141
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
144
-
it('it allows recovery key to rotate signing key', async () => {
145
-
const newKey = await EcdsaKeypair.create()
146
-
const prev = await cidForCbor(ops[ops.length - 1])
147
-
const op = await operations.rotateSigningKey(
153
-
signingKey = newKey
154
-
const doc = await document.validateOperationLog(did, ops)
155
-
expect(doc.signingKey).toEqual(newKey.did())
158
-
it('it allows recovery key to rotate recovery key', async () => {
159
-
const newKey = await Secp256k1Keypair.create()
160
-
const prev = await cidForCbor(ops[ops.length - 1])
161
-
const op = await operations.rotateRecoveryKey(
167
-
recoveryKey = newKey
168
-
const doc = await document.validateOperationLog(did, ops)
169
-
expect(doc.recoveryKey).toEqual(newKey.did())
172
-
it('it allows recovery key to update handle', async () => {
173
-
handle = 'ally.example3.com'
174
-
const prev = await cidForCbor(ops[ops.length - 1])
175
-
const op = await operations.updateHandle(
181
-
const doc = await document.validateOperationLog(did, ops)
182
-
expect(doc.handle).toEqual(handle)
185
-
it('it allows recovery key to update atpPds', async () => {
186
-
atpPds = 'https://example3.com'
187
-
const prev = await cidForCbor(ops[ops.length - 1])
188
-
const op = await operations.updateAtpPds(
194
-
const doc = await document.validateOperationLog(did, ops)
195
-
expect(doc.atpPds).toEqual(atpPds)
198
-
it('requires operations to be in order', async () => {
199
-
const prev = await cidForCbor(ops[ops.length - 2])
200
-
const op = await operations.updateAtpPds(
205
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
208
-
it('does not allow a create operation in the middle of the log', async () => {
209
-
const op = await operations.create(
215
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
218
-
it('requires that the log start with a create operation', async () => {
219
-
const rest = ops.slice(1)
220
-
expect(document.validateOperationLog(did, rest)).rejects.toThrow()
6
+
describe('document', () => {
it('formats a valid DID document', async () => {
224
-
const data = await document.validateOperationLog(did, ops)
8
+
const signingKey = await Secp256k1Keypair.create()
9
+
const rotate1 = await Secp256k1Keypair.create()
10
+
const rotate2 = await EcdsaKeypair.create()
11
+
const handles = ['alice.test', 'bob.test']
12
+
const atpPds = 'https://example.com'
13
+
const data: t.DocumentData = {
14
+
did: 'did:example:alice',
15
+
signingKey: signingKey.did(),
16
+
rotationKeys: [rotate1.did(), rotate2.did()],
const doc = await document.formatDidDoc(data)
expect(doc['@context']).toEqual([
'https://www.w3.org/ns/did/v1',
228
-
'https://w3id.org/security/suites/ecdsa-2019/v1',
'https://w3id.org/security/suites/secp256k1-2019/v1',
231
-
expect(doc.id).toEqual(did)
232
-
expect(doc.alsoKnownAs).toEqual([`https://${handle}`])
234
-
expect(doc.verificationMethod.length).toBe(2)
27
+
expect(doc.id).toEqual(data.did)
28
+
const formattedHandles = handles.map((h) => `https://${h}`)
29
+
expect(doc.alsoKnownAs).toEqual(formattedHandles)
30
+
expect(doc.verificationMethod.length).toBe(1)
expect(doc.verificationMethod[0].id).toEqual('#signingKey')
expect(doc.verificationMethod[0].type).toEqual(
237
-
'EcdsaSecp256r1VerificationKey2019',
33
+
'EcdsaSecp256k1VerificationKey2019',
239
-
expect(doc.verificationMethod[0].controller).toEqual(did)
35
+
expect(doc.verificationMethod[0].controller).toEqual(data.did)
const parsedSigningKey = parseDidKey(signingKey.did())
const signingKeyMultibase =
'z' + uint8arrays.toString(parsedSigningKey.keyBytes, 'base58btc')
expect(doc.verificationMethod[0].publicKeyMultibase).toEqual(
246
-
expect(doc.verificationMethod[1].id).toEqual('#recoveryKey')
247
-
expect(doc.verificationMethod[1].type).toEqual(
248
-
'EcdsaSecp256k1VerificationKey2019',
250
-
expect(doc.verificationMethod[1].controller).toEqual(did)
251
-
const parsedRecoveryKey = parseDidKey(recoveryKey.did())
252
-
const recoveryKeyMultibase =
253
-
'z' + uint8arrays.toString(parsedRecoveryKey.keyBytes, 'base58btc')
254
-
expect(doc.verificationMethod[1].publicKeyMultibase).toEqual(
255
-
recoveryKeyMultibase,
expect(doc.assertionMethod).toEqual(['#signingKey'])
expect(doc.capabilityInvocation).toEqual(['#signingKey'])
expect(doc.capabilityDelegation).toEqual(['#signingKey'])
···
expect(doc.service[0].serviceEndpoint).toEqual(atpPds)
267
-
it('formats a valid DID document regardless of leading https://', async () => {
268
-
handle = 'https://alice.example.com'
269
-
const prev = await cidForCbor(ops[ops.length - 1])
270
-
const op1 = await operations.updateHandle(
51
+
it('handles P-256 keys', async () => {
52
+
const signingKey = await EcdsaKeypair.create()
53
+
const rotate1 = await Secp256k1Keypair.create()
54
+
const rotate2 = await EcdsaKeypair.create()
55
+
const handles = ['alice.test', 'bob.test']
56
+
const atpPds = 'https://example.com'
57
+
const data: t.DocumentData = {
58
+
did: 'did:example:alice',
59
+
signingKey: signingKey.did(),
60
+
rotationKeys: [rotate1.did(), rotate2.did()],
66
+
const doc = await document.formatDidDoc(data)
67
+
expect(doc.verificationMethod.length).toBe(1)
68
+
expect(doc['@context']).toEqual([
69
+
'https://www.w3.org/ns/did/v1',
70
+
'https://w3id.org/security/suites/ecdsa-2019/v1',
72
+
expect(doc.verificationMethod[0].id).toEqual('#signingKey')
73
+
expect(doc.verificationMethod[0].type).toEqual(
74
+
'EcdsaSecp256r1VerificationKey2019',
275
-
atpPds = 'example.com'
276
-
const prev2 = await cidForCbor(op1)
277
-
const op2 = await operations.updateAtpPds(
76
+
expect(doc.verificationMethod[0].controller).toEqual(data.did)
77
+
const parsedSigningKey = parseDidKey(signingKey.did())
78
+
const signingKeyMultibase =
79
+
'z' + uint8arrays.toString(parsedSigningKey.keyBytes, 'base58btc')
80
+
expect(doc.verificationMethod[0].publicKeyMultibase).toEqual(
81
+
signingKeyMultibase,
284
-
const data = await document.validateOperationLog(did, ops)
85
+
it('formats a valid DID document regardless of leading https://', async () => {
86
+
const signingKey = await Secp256k1Keypair.create()
87
+
const rotate1 = await Secp256k1Keypair.create()
88
+
const rotate2 = await EcdsaKeypair.create()
89
+
const handles = ['https://alice.test', 'bob.test']
90
+
const atpPds = 'example.com'
91
+
const data: t.DocumentData = {
92
+
did: 'did:example:alice',
93
+
signingKey: signingKey.did(),
94
+
rotationKeys: [rotate1.did(), rotate2.did()],
const doc = await document.formatDidDoc(data)
286
-
expect(doc.alsoKnownAs).toEqual([handle])
101
+
expect(doc.alsoKnownAs).toEqual(['https://alice.test', 'https://bob.test'])
expect(doc.service[0].serviceEndpoint).toEqual(`https://${atpPds}`)