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

cleanupo & testing

dholms 0d0f59ba 9311aa1e

Changed files
+380 -283
packages
+30 -19
packages/lib/src/data.ts
···
import * as crypto from '@atproto/crypto'
import * as t from './types'
import { didForCreateOp, normalizeOp } from './operations'
-
import { ServerError } from './error'
+
import {
+
GenesisHashError,
+
ImproperOperationError,
+
InvalidSignatureError,
+
LateRecoveryError,
+
MisorderedOperationError,
+
UnsupportedKeyError,
+
} from './error'
export const assureValidNextOp = async (
did: string,
···
): Promise<{ nullified: CID[]; prev: CID | null }> => {
// ensure we support the proposed keys
const keys = [proposed.signingKey, ...proposed.rotationKeys]
-
await Promise.all(keys.map((k) => crypto.parseDidKey(k)))
+
await Promise.all(
+
keys.map(async (k) => {
+
try {
+
crypto.parseDidKey(k)
+
} catch (err) {
+
throw new UnsupportedKeyError(k, err)
+
}
+
}),
+
)
// special case if account creation
if (ops.length === 0) {
···
const proposedPrev = proposed.prev ? CID.parse(proposed.prev) : undefined
if (!proposedPrev) {
-
throw new ServerError(400, `Invalid prev on operation: ${proposed.prev}`)
+
throw new ImproperOperationError('could not parse prev', proposed)
}
const indexOfPrev = ops.findIndex((op) => proposedPrev.equals(op.cid))
if (indexOfPrev < 0) {
-
throw new ServerError(409, 'Operations not correctly ordered')
+
throw new MisorderedOperationError()
}
// if we are forking history, these are the ops still in the proposed canonical history
···
const nullified = ops.slice(indexOfPrev + 1)
const lastOp = opsInHistory.at(-1)
if (!lastOp) {
-
throw new ServerError(400, 'Could not locate last op in history')
+
throw new MisorderedOperationError()
}
const lastOpNormalized = normalizeOp(lastOp.operation)
const firstNullified = nullified[0]
···
const RECOVERY_WINDOW = 72 * HOUR
const timeLapsed = Date.now() - firstNullified.createdAt.getTime()
if (timeLapsed > RECOVERY_WINDOW) {
-
throw new ServerError(
-
400,
-
'Recovery operation occured outside of the allowed 72 hr recovery window',
-
)
+
throw new LateRecoveryError(timeLapsed)
}
}
···
// make sure they're all validly formatted operations
const [first, ...rest] = ops
if (!check.is(first, t.def.compatibleOp)) {
-
throw new ServerError(400, `Improperly formatted operation: ${first}`)
+
throw new ImproperOperationError('incorrect structure', first)
}
for (const op of rest) {
if (!check.is(op, t.def.operation)) {
-
throw new ServerError(400, `Improperly formatted operation: ${op}`)
+
throw new ImproperOperationError('incorrect structure', op)
}
}
···
for (const op of rest) {
if (!op.prev || !CID.parse(op.prev).equals(prev)) {
-
throw new ServerError(400, 'Operations not correctly ordered')
+
throw new MisorderedOperationError()
}
await assureValidSig(doc.rotationKeys, op)
···
await assureValidSig(normalized.rotationKeys, op)
const expectedDid = await didForCreateOp(op, 64)
if (!expectedDid.startsWith(did)) {
-
throw new ServerError(
-
400,
-
`Hash of genesis operation does not match DID identifier: ${expectedDid}`,
-
)
+
throw new GenesisHashError(expectedDid)
}
if (op.prev !== null) {
-
throw new ServerError(400, `Prev of create op is not null: ${op}`)
+
throw new ImproperOperationError('expected null prev on create', op)
}
const { signingKey, rotationKeys, handles, services } = normalized
return { did, signingKey, rotationKeys, handles, services }
···
let isValid = true
for (const did of allowedDids) {
isValid = await crypto.verifySignature(did, dataBytes, sigBytes)
-
if (isValid) return did
+
if (isValid) {
+
return did
+
}
}
-
throw new ServerError(400, `Invalid signature on op: ${JSON.stringify(op)}`)
+
throw new InvalidSignatureError(op)
}
+5 -4
packages/lib/src/document.ts
···
import * as uint8arrays from 'uint8arrays'
import * as crypto from '@atproto/crypto'
import * as t from './types'
-
import { ServerError } from './error'
+
import { UnsupportedKeyError } from './error'
+
import { ParsedDidKey } from '@atproto/crypto'
export const formatDidDoc = (data: t.DocumentData): t.DidDocument => {
const context = ['https://www.w3.org/ns/did/v1']
···
}
const formatKeyAndContext = (key: string): KeyAndContext => {
-
let keyInfo
+
let keyInfo: ParsedDidKey
try {
keyInfo = crypto.parseDidKey(key)
} catch (err) {
-
throw new ServerError(400, `Could not parse did:key: ${err}`)
+
throw new UnsupportedKeyError(key, err)
}
const { jwtAlg, keyBytes } = keyInfo
···
publicKeyMultibase: `z${uint8arrays.toString(keyBytes, 'base58btc')}`,
}
}
-
throw new ServerError(400, `Unsupported key type: ${jwtAlg}`)
+
throw new UnsupportedKeyError(key, `Unsupported key type: ${jwtAlg}`)
}
export const ensureHttpPrefix = (str: string): string => {
+37 -4
packages/lib/src/error.ts
···
-
// @TODO fix
-
export class ServerError extends Error {
-
constructor(public code: number, msg: string) {
-
super(msg)
+
export class ImproperOperationError extends Error {
+
constructor(public reason: string, public op: unknown) {
+
super(`Improperly formatted operation, ${reason}: ${op}`)
+
}
+
}
+
+
export class MisorderedOperationError extends Error {
+
constructor() {
+
super('Operations not correctly ordered')
+
}
+
}
+
+
export class LateRecoveryError extends Error {
+
constructor(public timeLapsed: number) {
+
super(
+
`Recovery operation occured outside of the allowed 72 hr recovery window. Time lapsed: ${timeLapsed}`,
+
)
+
}
+
}
+
+
export class GenesisHashError extends Error {
+
constructor(public expected: string) {
+
super(
+
`Hash of genesis operation does not match DID identifier: ${expected}`,
+
)
+
}
+
}
+
+
export class InvalidSignatureError extends Error {
+
constructor(public op: unknown) {
+
super(`Invalid signature on op: ${JSON.stringify(op)}`)
+
}
+
}
+
+
export class UnsupportedKeyError extends Error {
+
constructor(public key: string, public err: unknown) {
+
super(`Unsupported key type ${key}: ${err}`)
}
}
+2 -1
packages/lib/src/index.ts
···
+
export * from './client'
export * from './data'
export * from './document'
+
export * from './error'
export * from './operations'
export * from './types'
-
export * from './client'
+12
packages/lib/src/operations.ts
···
}
}
+
export const deprecatedSignCreate = async (
+
op: t.UnsignedCreateOpV1,
+
signingKey: Keypair,
+
): Promise<t.CreateOpV1> => {
+
const data = new Uint8Array(cbor.encode(op))
+
const sig = await signingKey.sign(data)
+
return {
+
...op,
+
sig: uint8arrays.toString(sig, 'base64url'),
+
}
+
}
+
export const normalizeOp = (op: t.CompatibleOp): t.Operation => {
if (check.is(op, t.def.operation)) {
return op
+224
packages/lib/tests/data.test.ts
···
+
import { check, cidForCbor } from '@atproto/common'
+
import {
+
EcdsaKeypair,
+
Keypair,
+
parseDidKey,
+
Secp256k1Keypair,
+
} from '@atproto/crypto'
+
import * as uint8arrays from 'uint8arrays'
+
import { InvalidSignatureError } from '../src'
+
import * as data from '../src/data'
+
import * as document from '../src/document'
+
import * as operations from '../src/operations'
+
import * as t from '../src/types'
+
+
describe('plc did data', () => {
+
const ops: t.Operation[] = []
+
+
let signingKey: Secp256k1Keypair
+
let rotationKey1: Secp256k1Keypair
+
let rotationKey2: EcdsaKeypair
+
let did: string
+
let handle = 'alice.example.com'
+
let atpPds = 'https://example.com'
+
+
let oldSigningKey: Secp256k1Keypair
+
let oldRotationKey1: Secp256k1Keypair
+
+
beforeAll(async () => {
+
signingKey = await Secp256k1Keypair.create()
+
rotationKey1 = await Secp256k1Keypair.create()
+
rotationKey2 = await EcdsaKeypair.create()
+
})
+
+
const makeNextOp = async (
+
changes: Partial<t.Operation>,
+
key: Keypair,
+
): Promise<t.Operation> => {
+
const lastOp = ops.at(-1)
+
if (!lastOp) {
+
throw new Error('expected an op on log')
+
}
+
const prev = await cidForCbor(lastOp)
+
return operations.signOperation(
+
{
+
signingKey: lastOp.signingKey,
+
rotationKeys: lastOp.rotationKeys,
+
handles: lastOp.handles,
+
services: lastOp.services,
+
prev: prev.toString(),
+
...changes,
+
},
+
key,
+
)
+
}
+
+
it('creates a valid create op', async () => {
+
const createOp = await operations.signOperation(
+
{
+
signingKey: signingKey.did(),
+
rotationKeys: [rotationKey1.did(), rotationKey2.did()],
+
handles: [handle],
+
services: {
+
atpPds,
+
},
+
prev: null,
+
},
+
rotationKey1,
+
)
+
const isValid = check.is(createOp, t.def.operation)
+
expect(isValid).toBeTruthy()
+
ops.push(createOp)
+
did = await operations.didForCreateOp(createOp)
+
})
+
+
it('parses an operation log with no updates', async () => {
+
const doc = await data.validateOperationLog(did, ops)
+
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
it('updates handle', async () => {
+
handle = 'ali.example2.com'
+
const op = await makeNextOp({ handles: [handle] }, rotationKey1)
+
ops.push(op)
+
+
const doc = await data.validateOperationLog(did, ops)
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
it('updates atpPds', async () => {
+
atpPds = 'https://example2.com'
+
const op = await makeNextOp(
+
{
+
services: {
+
atpPds,
+
},
+
},
+
rotationKey1,
+
)
+
ops.push(op)
+
+
const doc = await data.validateOperationLog(did, ops)
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
it('rotates signingKey', async () => {
+
const newSigningKey = await Secp256k1Keypair.create()
+
const op = await makeNextOp(
+
{
+
signingKey: newSigningKey.did(),
+
},
+
rotationKey1,
+
)
+
ops.push(op)
+
+
oldSigningKey = signingKey
+
signingKey = newSigningKey
+
+
const doc = await data.validateOperationLog(did, ops)
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
it('rotates rotation keys', async () => {
+
const newRotationKey = await Secp256k1Keypair.create()
+
const op = await makeNextOp(
+
{
+
rotationKeys: [newRotationKey.did(), rotationKey2.did()],
+
},
+
rotationKey1,
+
)
+
ops.push(op)
+
+
oldRotationKey1 = rotationKey1
+
rotationKey1 = newRotationKey
+
+
const doc = await data.validateOperationLog(did, ops)
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
it('no longer allows operations from old rotation key', async () => {
+
const op = await makeNextOp(
+
{
+
handles: ['bob'],
+
},
+
oldRotationKey1,
+
)
+
expect(data.validateOperationLog(did, [...ops, op])).rejects.toThrow(
+
InvalidSignatureError,
+
)
+
})
+
+
it('does not allow operations from the signingKey', async () => {
+
const op = await makeNextOp(
+
{
+
handles: ['bob'],
+
},
+
signingKey,
+
)
+
expect(data.validateOperationLog(did, [...ops, op])).rejects.toThrow(
+
InvalidSignatureError,
+
)
+
})
+
+
it('allows for operations from either rotation key', async () => {
+
const newHandle = 'ali.example.com'
+
const op = await makeNextOp(
+
{
+
handles: [newHandle],
+
},
+
rotationKey2,
+
)
+
const doc = await data.validateOperationLog(did, ops)
+
expect(doc.did).toEqual(did)
+
expect(doc.signingKey).toEqual(signingKey.did())
+
expect(doc.rotationKeys).toEqual([rotationKey1.did(), rotationKey2.did()])
+
expect(doc.handles).toEqual([handle])
+
expect(doc.services).toEqual({ atpPds })
+
})
+
+
// it('requires operations to be in order', async () => {
+
// const prev = await cidForCbor(ops[ops.length - 2])
+
// const op = await operations.updateAtpPds(
+
// 'foobar.com',
+
// prev.toString(),
+
// signingKey,
+
// )
+
// expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
+
// })
+
+
// it('does not allow a create operation in the middle of the log', async () => {
+
// const op = await operations.create(
+
// signingKey,
+
// recoveryKey.did(),
+
// handle,
+
// atpPds,
+
// )
+
// expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
+
// })
+
+
// it('requires that the log start with a create operation', async () => {
+
// const rest = ops.slice(1)
+
// expect(document.validateOperationLog(did, rest)).rejects.toThrow()
+
// })
+
})
+70 -255
packages/lib/tests/document.test.ts
···
-
import { check, cidForCbor } from '@atproto/common'
+
import * as uint8arrays from 'uint8arrays'
import { EcdsaKeypair, parseDidKey, Secp256k1Keypair } from '@atproto/crypto'
-
import * as uint8arrays from 'uint8arrays'
import * as document from '../src/document'
-
import * as operations from '../src/operations'
import * as t from '../src/types'
-
describe('plc DID document', () => {
-
const ops: t.Operation[] = []
-
-
let signingKey: EcdsaKeypair
-
let recoveryKey: Secp256k1Keypair
-
let did: string
-
let handle = 'alice.example.com'
-
let atpPds = 'https://example.com'
-
-
let oldSigningKey: EcdsaKeypair
-
let oldRecoveryKey: Secp256k1Keypair
-
-
beforeAll(async () => {
-
signingKey = await EcdsaKeypair.create()
-
recoveryKey = await Secp256k1Keypair.create()
-
})
-
-
it('creates a valid create op', async () => {
-
const createOp = await operations.create(
-
signingKey,
-
recoveryKey.did(),
-
handle,
-
atpPds,
-
)
-
const isValid = check.is(createOp, t.def.createOp)
-
expect(isValid).toBeTruthy()
-
ops.push(createOp)
-
did = await operations.didForCreateOp(createOp)
-
})
-
-
it('parses an operation log with no updates', async () => {
-
const doc = await document.validateOperationLog(did, ops)
-
-
expect(doc.did).toEqual(did)
-
expect(doc.signingKey).toEqual(signingKey.did())
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
-
expect(doc.handle).toEqual(handle)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('allows for updating handle', async () => {
-
handle = 'ali.example2.com'
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateHandle(
-
handle,
-
prev.toString(),
-
signingKey,
-
)
-
ops.push(op)
-
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.did).toEqual(did)
-
expect(doc.signingKey).toEqual(signingKey.did())
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
-
expect(doc.handle).toEqual(handle)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('allows for updating atpPds', async () => {
-
atpPds = 'https://example2.com'
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateAtpPds(
-
atpPds,
-
prev.toString(),
-
signingKey,
-
)
-
ops.push(op)
-
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.did).toEqual(did)
-
expect(doc.signingKey).toEqual(signingKey.did())
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
-
expect(doc.handle).toEqual(handle)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('allows for rotating signingKey', async () => {
-
const newSigningKey = await EcdsaKeypair.create()
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.rotateSigningKey(
-
newSigningKey.did(),
-
prev.toString(),
-
signingKey,
-
)
-
ops.push(op)
-
oldSigningKey = signingKey
-
signingKey = newSigningKey
-
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.did).toEqual(did)
-
expect(doc.signingKey).toEqual(signingKey.did())
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
-
expect(doc.handle).toEqual(handle)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('no longer allows operations from old signing key', async () => {
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateHandle(
-
'bob',
-
prev.toString(),
-
oldSigningKey,
-
)
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
-
})
-
-
it('allows for rotating recoveryKey', async () => {
-
const newRecoveryKey = await Secp256k1Keypair.create()
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.rotateRecoveryKey(
-
newRecoveryKey.did(),
-
prev.toString(),
-
signingKey,
-
)
-
ops.push(op)
-
oldRecoveryKey = recoveryKey
-
recoveryKey = newRecoveryKey
-
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.did).toEqual(did)
-
expect(doc.signingKey).toEqual(signingKey.did())
-
expect(doc.recoveryKey).toEqual(recoveryKey.did())
-
expect(doc.handle).toEqual(handle)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('no longer allows operations from old recovery key', async () => {
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateHandle(
-
'bob',
-
prev.toString(),
-
oldRecoveryKey,
-
)
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
-
})
-
-
it('it allows recovery key to rotate signing key', async () => {
-
const newKey = await EcdsaKeypair.create()
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.rotateSigningKey(
-
newKey.did(),
-
prev.toString(),
-
recoveryKey,
-
)
-
ops.push(op)
-
signingKey = newKey
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.signingKey).toEqual(newKey.did())
-
})
-
-
it('it allows recovery key to rotate recovery key', async () => {
-
const newKey = await Secp256k1Keypair.create()
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.rotateRecoveryKey(
-
newKey.did(),
-
prev.toString(),
-
recoveryKey,
-
)
-
ops.push(op)
-
recoveryKey = newKey
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.recoveryKey).toEqual(newKey.did())
-
})
-
-
it('it allows recovery key to update handle', async () => {
-
handle = 'ally.example3.com'
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateHandle(
-
handle,
-
prev.toString(),
-
recoveryKey,
-
)
-
ops.push(op)
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.handle).toEqual(handle)
-
})
-
-
it('it allows recovery key to update atpPds', async () => {
-
atpPds = 'https://example3.com'
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op = await operations.updateAtpPds(
-
atpPds,
-
prev.toString(),
-
recoveryKey,
-
)
-
ops.push(op)
-
const doc = await document.validateOperationLog(did, ops)
-
expect(doc.atpPds).toEqual(atpPds)
-
})
-
-
it('requires operations to be in order', async () => {
-
const prev = await cidForCbor(ops[ops.length - 2])
-
const op = await operations.updateAtpPds(
-
'foobar.com',
-
prev.toString(),
-
signingKey,
-
)
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
-
})
-
-
it('does not allow a create operation in the middle of the log', async () => {
-
const op = await operations.create(
-
signingKey,
-
recoveryKey.did(),
-
handle,
-
atpPds,
-
)
-
expect(document.validateOperationLog(did, [...ops, op])).rejects.toThrow()
-
})
-
-
it('requires that the log start with a create operation', async () => {
-
const rest = ops.slice(1)
-
expect(document.validateOperationLog(did, rest)).rejects.toThrow()
-
})
-
+
describe('document', () => {
it('formats a valid DID document', async () => {
-
const data = await document.validateOperationLog(did, ops)
+
const signingKey = await Secp256k1Keypair.create()
+
const rotate1 = await Secp256k1Keypair.create()
+
const rotate2 = await EcdsaKeypair.create()
+
const handles = ['alice.test', 'bob.test']
+
const atpPds = 'https://example.com'
+
const data: t.DocumentData = {
+
did: 'did:example:alice',
+
signingKey: signingKey.did(),
+
rotationKeys: [rotate1.did(), rotate2.did()],
+
handles,
+
services: {
+
atpPds,
+
},
+
}
const doc = await document.formatDidDoc(data)
expect(doc['@context']).toEqual([
'https://www.w3.org/ns/did/v1',
-
'https://w3id.org/security/suites/ecdsa-2019/v1',
'https://w3id.org/security/suites/secp256k1-2019/v1',
])
-
expect(doc.id).toEqual(did)
-
expect(doc.alsoKnownAs).toEqual([`https://${handle}`])
-
-
expect(doc.verificationMethod.length).toBe(2)
+
expect(doc.id).toEqual(data.did)
+
const formattedHandles = handles.map((h) => `https://${h}`)
+
expect(doc.alsoKnownAs).toEqual(formattedHandles)
+
expect(doc.verificationMethod.length).toBe(1)
expect(doc.verificationMethod[0].id).toEqual('#signingKey')
expect(doc.verificationMethod[0].type).toEqual(
-
'EcdsaSecp256r1VerificationKey2019',
+
'EcdsaSecp256k1VerificationKey2019',
)
-
expect(doc.verificationMethod[0].controller).toEqual(did)
+
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(
signingKeyMultibase,
)
-
expect(doc.verificationMethod[1].id).toEqual('#recoveryKey')
-
expect(doc.verificationMethod[1].type).toEqual(
-
'EcdsaSecp256k1VerificationKey2019',
-
)
-
expect(doc.verificationMethod[1].controller).toEqual(did)
-
const parsedRecoveryKey = parseDidKey(recoveryKey.did())
-
const recoveryKeyMultibase =
-
'z' + uint8arrays.toString(parsedRecoveryKey.keyBytes, 'base58btc')
-
expect(doc.verificationMethod[1].publicKeyMultibase).toEqual(
-
recoveryKeyMultibase,
-
)
-
expect(doc.assertionMethod).toEqual(['#signingKey'])
expect(doc.capabilityInvocation).toEqual(['#signingKey'])
expect(doc.capabilityDelegation).toEqual(['#signingKey'])
···
expect(doc.service[0].serviceEndpoint).toEqual(atpPds)
})
-
it('formats a valid DID document regardless of leading https://', async () => {
-
handle = 'https://alice.example.com'
-
const prev = await cidForCbor(ops[ops.length - 1])
-
const op1 = await operations.updateHandle(
-
handle,
-
prev.toString(),
-
signingKey,
+
it('handles P-256 keys', async () => {
+
const signingKey = await EcdsaKeypair.create()
+
const rotate1 = await Secp256k1Keypair.create()
+
const rotate2 = await EcdsaKeypair.create()
+
const handles = ['alice.test', 'bob.test']
+
const atpPds = 'https://example.com'
+
const data: t.DocumentData = {
+
did: 'did:example:alice',
+
signingKey: signingKey.did(),
+
rotationKeys: [rotate1.did(), rotate2.did()],
+
handles,
+
services: {
+
atpPds,
+
},
+
}
+
const doc = await document.formatDidDoc(data)
+
expect(doc.verificationMethod.length).toBe(1)
+
expect(doc['@context']).toEqual([
+
'https://www.w3.org/ns/did/v1',
+
'https://w3id.org/security/suites/ecdsa-2019/v1',
+
])
+
expect(doc.verificationMethod[0].id).toEqual('#signingKey')
+
expect(doc.verificationMethod[0].type).toEqual(
+
'EcdsaSecp256r1VerificationKey2019',
)
-
atpPds = 'example.com'
-
const prev2 = await cidForCbor(op1)
-
const op2 = await operations.updateAtpPds(
-
atpPds,
-
prev2.toString(),
-
signingKey,
+
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(
+
signingKeyMultibase,
)
-
ops.push(op1)
-
ops.push(op2)
-
const data = await document.validateOperationLog(did, ops)
+
})
+
+
it('formats a valid DID document regardless of leading https://', async () => {
+
const signingKey = await Secp256k1Keypair.create()
+
const rotate1 = await Secp256k1Keypair.create()
+
const rotate2 = await EcdsaKeypair.create()
+
const handles = ['https://alice.test', 'bob.test']
+
const atpPds = 'example.com'
+
const data: t.DocumentData = {
+
did: 'did:example:alice',
+
signingKey: signingKey.did(),
+
rotationKeys: [rotate1.did(), rotate2.did()],
+
handles,
+
services: {
+
atpPds,
+
},
+
}
const doc = await document.formatDidDoc(data)
-
expect(doc.alsoKnownAs).toEqual([handle])
+
expect(doc.alsoKnownAs).toEqual(['https://alice.test', 'https://bob.test'])
expect(doc.service[0].serviceEndpoint).toEqual(`https://${atpPds}`)
})
})