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

working on recovery tests

dholms dc8ae9d9 0d0f59ba

Changed files
+172 -38
packages
+1 -3
packages/lib/src/data.ts
···
)
const indexOfSigner = lastOpNormalized.rotationKeys.indexOf(disputedSigner)
-
const morePowerfulKeys = lastOpNormalized.rotationKeys.slice(
-
indexOfSigner + 1,
-
)
await assureValidSig(morePowerfulKeys, proposed)
···
)
const indexOfSigner = lastOpNormalized.rotationKeys.indexOf(disputedSigner)
+
const morePowerfulKeys = lastOpNormalized.rotationKeys.slice(0, indexOfSigner)
await assureValidSig(morePowerfulKeys, proposed)
+47 -32
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'
···
let handle = 'alice.example.com'
let atpPds = 'https://example.com'
-
let oldSigningKey: Secp256k1Keypair
let oldRotationKey1: Secp256k1Keypair
beforeAll(async () => {
···
)
ops.push(op)
-
oldSigningKey = signingKey
signingKey = newSigningKey
const doc = await data.validateOperationLog(did, ops)
···
},
rotationKey2,
)
const doc = await data.validateOperationLog(did, ops)
expect(doc.did).toEqual(did)
expect(doc.signingKey).toEqual(signingKey.did())
···
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()
-
// })
})
···
import { check, cidForCbor } from '@atproto/common'
+
import { EcdsaKeypair, Keypair, Secp256k1Keypair } from '@atproto/crypto'
import {
+
GenesisHashError,
+
ImproperOperationError,
+
InvalidSignatureError,
+
MisorderedOperationError,
+
} from '../src'
import * as data from '../src/data'
import * as operations from '../src/operations'
import * as t from '../src/types'
···
let handle = 'alice.example.com'
let atpPds = 'https://example.com'
let oldRotationKey1: Secp256k1Keypair
beforeAll(async () => {
···
)
ops.push(op)
signingKey = newSigningKey
const doc = await data.validateOperationLog(did, ops)
···
},
rotationKey2,
)
+
ops.push(op)
+
handle = newHandle
const doc = await data.validateOperationLog(did, ops)
expect(doc.did).toEqual(did)
expect(doc.signingKey).toEqual(signingKey.did())
···
expect(doc.services).toEqual({ atpPds })
})
+
it('requires operations to be in order', async () => {
+
const prev = await cidForCbor(ops[ops.length - 2])
+
const op = await makeNextOp(
+
{
+
handles: ['bob.test'],
+
prev: prev.toString(),
+
},
+
rotationKey1,
+
)
+
expect(data.validateOperationLog(did, [...ops, op])).rejects.toThrow(
+
MisorderedOperationError,
+
)
+
})
+
+
it('does not allow a create operation in the middle of the log', async () => {
+
const op = await makeNextOp(
+
{
+
handles: ['bob.test'],
+
prev: null,
+
},
+
rotationKey1,
+
)
+
expect(data.validateOperationLog(did, [...ops, op])).rejects.toThrow(
+
MisorderedOperationError,
+
)
+
})
+
it('requires that the did is the hash of the genesis op', async () => {
+
const rest = ops.slice(1)
+
expect(data.validateOperationLog(did, rest)).rejects.toThrow(
+
GenesisHashError,
+
)
+
})
+
it('requires that the log starts with a create op (no prev)', async () => {
+
const rest = ops.slice(1)
+
const expectedDid = await operations.didForCreateOp(rest[0])
+
expect(data.validateOperationLog(expectedDid, rest)).rejects.toThrow(
+
ImproperOperationError,
+
)
+
})
})
+123
packages/lib/tests/recovery.test.ts
···
···
+
import { check, cidForCbor, DAY, HOUR } from '@atproto/common'
+
import { EcdsaKeypair, Keypair, Secp256k1Keypair } from '@atproto/crypto'
+
import { create } from 'domain'
+
import { CID } from 'multiformats/cid'
+
import {
+
GenesisHashError,
+
ImproperOperationError,
+
InvalidSignatureError,
+
MisorderedOperationError,
+
} from '../src'
+
import * as data from '../src/data'
+
import * as operations from '../src/operations'
+
import * as t from '../src/types'
+
+
describe('plc recovery', () => {
+
let signingKey: Secp256k1Keypair
+
let rotationKey1: Secp256k1Keypair
+
let rotationKey2: EcdsaKeypair
+
let rotationKey3: EcdsaKeypair
+
let did: string
+
let handle = 'alice.example.com'
+
let atpPds = 'https://example.com'
+
+
let log: t.IndexedOperation[] = []
+
+
let createCid: CID
+
const key3AttackCids: CID[] = []
+
const key2AttackCid: CID[] = []
+
+
beforeAll(async () => {
+
signingKey = await Secp256k1Keypair.create()
+
rotationKey1 = await Secp256k1Keypair.create()
+
rotationKey2 = await EcdsaKeypair.create()
+
rotationKey3 = await EcdsaKeypair.create()
+
})
+
+
const signOpForKeys = async (
+
keys: Keypair[],
+
prev: CID | null,
+
signer: Keypair,
+
otherChanges: Partial<t.Operation> = {},
+
) => {
+
const op = await operations.signOperation(
+
{
+
signingKey: signingKey.did(),
+
rotationKeys: keys.map((k) => k.did()),
+
handles: [handle],
+
services: {
+
atpPds,
+
},
+
prev: prev ? prev.toString() : null,
+
...otherChanges,
+
},
+
signer,
+
)
+
+
const cid = await cidForCbor(op)
+
+
const indexed = {
+
did,
+
operation: op,
+
cid,
+
nullified: false,
+
createdAt: new Date(),
+
}
+
return { op, indexed }
+
}
+
+
it('creates an op log with rotation', async () => {
+
const create = await signOpForKeys(
+
[rotationKey1, rotationKey2, rotationKey3],
+
null,
+
rotationKey1,
+
)
+
createCid = create.indexed.cid
+
+
log.push({
+
...create.indexed,
+
createdAt: new Date(Date.now() - 7 * DAY),
+
})
+
+
// key 3 tries to usurp control
+
const rotate = await signOpForKeys([rotationKey3], createCid, rotationKey3)
+
+
log.push({
+
...rotate.indexed,
+
createdAt: new Date(Date.now() - DAY),
+
})
+
+
// and does some additional ops
+
const another = await signOpForKeys(
+
[rotationKey3],
+
rotate.indexed.cid,
+
rotationKey3,
+
{ handles: ['newhandle.test'] },
+
)
+
+
log.push({
+
...another.indexed,
+
createdAt: new Date(Date.now() - HOUR),
+
})
+
})
+
+
it('allows a rotation key with higher authority to rewrite history', async () => {
+
// key 2 asserts control over key 3
+
const rotate = await signOpForKeys([rotationKey2], createCid, rotationKey2)
+
+
const res = await data.assureValidNextOp(did, log, rotate.op)
+
expect(res.nullified.length).toBe(2)
+
expect(res.nullified[0].equals(log[1].cid))
+
expect(res.nullified[1].equals(log[2].cid))
+
expect(res.prev?.equals(createCid))
+
+
log = [log[0], rotate.indexed]
+
})
+
+
it('does not allow the lower authority key to take control back', async () => {
+
const rotate = await signOpForKeys([rotationKey3], createCid, rotationKey3)
+
await expect(data.assureValidNextOp(did, log, rotate.op)).rejects.toThrow(
+
InvalidSignatureError,
+
)
+
})
+
})
+1 -3
packages/server/tests/server.test.ts
···
import { EcdsaKeypair } from '@atproto/crypto'
-
import { Client, document } from '@did-plc/lib'
import { CloseFn, runTestServer } from './_util'
import { cidForCbor } from '@atproto/common'
import { AxiosError } from 'axios'
···
expect(doc.handle).toEqual(handle)
expect(doc.atpPds).toEqual(atpPds)
})
-
-
return
it('can perform some updates', async () => {
const newSigningKey = await EcdsaKeypair.create()
···
import { EcdsaKeypair } from '@atproto/crypto'
+
import { Client } from '@did-plc/lib'
import { CloseFn, runTestServer } from './_util'
import { cidForCbor } from '@atproto/common'
import { AxiosError } from 'axios'
···
expect(doc.handle).toEqual(handle)
expect(doc.atpPds).toEqual(atpPds)
})
it('can perform some updates', async () => {
const newSigningKey = await EcdsaKeypair.create()