Skip to content

Commit

Permalink
feat(key-manager): move private key storage to kms-local (#661)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `keyManagetGet` no longer returns private key data
BREAKING CHANGE: `KeyStore` no longer requires a `SecretBox`
BREAKING CHANGE: `KeyManagementSystem` needs a `PrivateKeyStore`
BREAKING CHANGE: @veramo/cli configuration version update to 3.0

If you're already working with Veramo and wish to upgrade existing agents to veramo 3.0, you'll have to make some changes to your configuration, depending on how you're using the framework.

It boils down to these 3 steps:

1. Update your database connection to use migrations
2. Remove the `SecretBox` parameter from `KeyManager`
3. Add a `PrivateKeyStore` parameter to `KeyManagementSystem` with a `SecretBox` that you were using before with `KeyManager` (and keep the same encryption key)

* feat(key-manager): move private key storage to kms-local

fixes #539
fixes #540
fixes #680

* feat(data-store): add migration of key stores

* fix(data-store): fix usage of where clause for queries

* refactor(kms-local): simplify constructor for KeyManagementSystem

* style: remove scar tissue and unused code
  • Loading branch information
mirceanis authored Sep 1, 2021
1 parent 32406d4 commit 6b1d135
Show file tree
Hide file tree
Showing 42 changed files with 992 additions and 280 deletions.
Binary file not shown.
34 changes: 25 additions & 9 deletions __tests__/initial.migration.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createAgent, TAgent, IDIDManager, IResolver, IKeyManager, IDataStore } from '@veramo/core/src'
import { createAgent, TAgent, IDIDManager, IResolver, IKeyManager, IDataStore } from '../packages/core/src'
import { DIDResolverPlugin } from '../packages/did-resolver/src'
import { EthrDIDProvider } from '../packages/did-provider-ethr/src'
import { WebDIDProvider } from '../packages/did-provider-web/src'
Expand All @@ -13,13 +13,14 @@ import {
KeyStore,
DIDStore,
migrations,
PrivateKeyStore,
} from '../packages/data-store/src'
import { createConnection, Connection } from 'typeorm'
import { getDidKeyResolver } from '../packages/did-provider-key/src'
import { KeyManager } from '../packages/key-manager/src'
import { DIDManager } from '../packages/did-manager/src'
import { FakeDidProvider, FakeDidResolver } from './utils/fake-did'

import { createConnection, Connection } from 'typeorm'
import { Resolver } from 'did-resolver'
import { getResolver as ethrDidResolver } from 'ethr-did-resolver'
import { getResolver as webDidResolver } from 'web-did-resolver'
Expand All @@ -32,7 +33,7 @@ const databaseFile = __dirname + '/migrated1.database.sqlite'
const infuraProjectId = '5ffc47f65c4042ce847ef66a3fa70d4c'
const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c'

describe('database migration tests', () => {
describe('database initial migration tests', () => {
describe('using pre-migration database fixture', () => {
type TestingAgentPlugins = IDIDManager & IKeyManager & IDataStore & IDataStoreORM & IResolver & IDIDComm
let agent: TAgent<TestingAgentPlugins>
Expand All @@ -58,9 +59,9 @@ describe('database migration tests', () => {
},
plugins: [
new KeyManager({
store: new KeyStore(dbConnection, new SecretBox(secretKey)),
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(),
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
},
}),
new DIDManager({
Expand Down Expand Up @@ -102,6 +103,7 @@ describe('database migration tests', () => {
})

it('signs using a migrated key', async () => {
expect.assertions(2)
// output of agent.keyManagerGet() before migration
const key = {
kid: '048bb0844ebbcf434048862008991b01cdebb564207f0cea08e5c8d925cec3542bb4c8c1630f38a6b05528ec7460139fe0978bf34a1e4ff32ec210bbaed98dddda',
Expand All @@ -128,12 +130,16 @@ describe('database migration tests', () => {
})

it('reads a credential by hash', async () => {
const cred = await agent.dataStoreGetVerifiableCredential({hash: '133b9636e2fe2b7a77b88ca5d81998773b8bc3ebe0b1f3f80dc419dfa0bb797bea779ba0946d603c3ea8611fee5148395894f327661531929294a61589d4d0e7'})
const cred = await agent.dataStoreGetVerifiableCredential({
hash: '133b9636e2fe2b7a77b88ca5d81998773b8bc3ebe0b1f3f80dc419dfa0bb797bea779ba0946d603c3ea8611fee5148395894f327661531929294a61589d4d0e7',
})
expect(cred.credentialSubject.name).toEqual('Alice')
})

it('reads a presentation by hash', async () => {
const cred = await agent.dataStoreGetVerifiablePresentation({hash: '4cfe965596a0d343ff2cc02afd32068bced34caa2b1e7e3f253b23e420de106b58a613f06f55d9d9cbbdbe0b0f051a45d44404020b123c58f0ee48bdaeafdc90'})
const cred = await agent.dataStoreGetVerifiablePresentation({
hash: '4cfe965596a0d343ff2cc02afd32068bced34caa2b1e7e3f253b23e420de106b58a613f06f55d9d9cbbdbe0b0f051a45d44404020b123c58f0ee48bdaeafdc90',
})
expect(cred?.verifiableCredential?.[0]?.credentialSubject?.name).toEqual('Alice')
})

Expand All @@ -144,15 +150,25 @@ describe('database migration tests', () => {

it('reads existing message with attachments', async () => {
const msgs = await agent.dataStoreORMGetMessages({
where: [{column: 'id', value: ['13065b8bb97cd37410f4f43cfa878f396aa906701e70c7e2bb86c5de1fe1351a41fb05f445cb68b1ba2805858db619ddd26c71e30a0079c200843d52276213d8']}]
where: [
{
column: 'id',
value: [
'13065b8bb97cd37410f4f43cfa878f396aa906701e70c7e2bb86c5de1fe1351a41fb05f445cb68b1ba2805858db619ddd26c71e30a0079c200843d52276213d8',
],
},
],
})
expect(msgs[0]?.presentations?.length).toEqual(1)
expect(msgs[0]?.credentials?.length).toEqual(1)
})

it('reads a credential by claim', async () => {
const creds = await agent.dataStoreORMGetVerifiableCredentialsByClaims({
where: [{ column: 'type', value: ['name'] }, { column: 'value', value: ['Alice']}]
where: [
{ column: 'type', value: ['name'] },
{ column: 'value', value: ['Alice'] },
],
})
expect(creds.length).toEqual(1)
})
Expand Down
141 changes: 141 additions & 0 deletions __tests__/keyMigration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { createAgent, TAgent, IDIDManager, IResolver, IKeyManager, IDataStore } from '../packages/core/src'
import { DIDResolverPlugin } from '../packages/did-resolver/src'
import { EthrDIDProvider } from '../packages/did-provider-ethr/src'
import { WebDIDProvider } from '../packages/did-provider-web/src'
import { KeyDIDProvider } from '../packages/did-provider-key/src'
import { DIDComm, IDIDComm } from '../packages/did-comm/src'
import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src'
import {
Entities,
IDataStoreORM,
DataStore,
DataStoreORM,
KeyStore,
DIDStore,
PrivateKeyStore,
migrations,
} from '../packages/data-store/src'
import { getDidKeyResolver } from '../packages/did-provider-key/src'
import { KeyManager } from '../packages/key-manager/src'
import { DIDManager } from '../packages/did-manager/src'
import { FakeDidProvider, FakeDidResolver } from './utils/fake-did'

import { createConnection, Connection } from 'typeorm'
import { Resolver } from 'did-resolver'
import { getResolver as ethrDidResolver } from 'ethr-did-resolver'
import { getResolver as webDidResolver } from 'web-did-resolver'
import fs from 'fs'

jest.setTimeout(30000)

const databaseBeforeFile = __dirname + '/fixtures/local-database-before-3.0.sqlite'
const databaseFile = __dirname + '/migrated.keys.database.sqlite'
const infuraProjectId = '5ffc47f65c4042ce847ef66a3fa70d4c'
const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c'

describe('database private-key migration tests', () => {
describe('using pre-migration database fixture', () => {
type TestingAgentPlugins = IDIDManager & IKeyManager & IDataStore & IDataStoreORM & IResolver & IDIDComm
let agent: TAgent<TestingAgentPlugins>
let dbConnection: Promise<Connection>

beforeAll(async () => {
fs.copyFileSync(databaseBeforeFile, databaseFile)

dbConnection = createConnection({
name: 'key-migration-test',
type: 'sqlite',
database: databaseFile,
synchronize: false,
migrations: migrations,
migrationsRun: true,
logging: false,
entities: Entities,
})

agent = createAgent<TestingAgentPlugins>({
context: {
// authenticatedDid: 'did:example:3456'
},
plugins: [
new KeyManager({
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
},
}),
new DIDManager({
store: new DIDStore(dbConnection),
defaultProvider: 'did:ethr:goerli',
providers: {
'did:ethr:goerli': new EthrDIDProvider({
defaultKms: 'local',
network: 'goerli',
rpcUrl: 'https://goerli.infura.io/v3/' + infuraProjectId,
}),
'did:web': new WebDIDProvider({
defaultKms: 'local',
}),
'did:key': new KeyDIDProvider({
defaultKms: 'local',
}),
'did:fake': new FakeDidProvider(),
},
}),
new DIDResolverPlugin({
resolver: new Resolver({
...ethrDidResolver({ infuraProjectId }),
...webDidResolver(),
...getDidKeyResolver(),
...new FakeDidResolver(() => agent).getDidFakeResolver(),
}),
}),
new DataStore(dbConnection),
new DataStoreORM(dbConnection),
new DIDComm(),
],
})
return true
})
afterAll(async () => {
await (await dbConnection).close()
fs.unlinkSync(databaseFile)
})

it('loads a migrated key', async () => {
// output of agent.keyManagerGet() before migration
const key = {
kid: '04539ffde912c094bc48b64c9ee71b2baece24c710bcad2c7bacced615f60ae53949cdc95379eb50556d11cb0afab0e5a6ca8cb475d413b2f12307cc2d7f5438de',
kms: 'local',
type: 'Secp256k1',
publicKeyHex:
'04539ffde912c094bc48b64c9ee71b2baece24c710bcad2c7bacced615f60ae53949cdc95379eb50556d11cb0afab0e5a6ca8cb475d413b2f12307cc2d7f5438de',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
meta: {
algorithms: ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage'],
},
}
const migratedKey = await agent.keyManagerGet({ kid: key.kid })
expect(migratedKey.kid).toEqual(key.kid)
expect(migratedKey).not.toHaveProperty('privateKeyHex')
const signedMessage = await agent.keyManagerSign({
data: 'hello world',
keyRef: migratedKey.kid,
algorithm: 'ES256K',
encoding: 'utf-8',
})
expect(signedMessage).toEqual(
'vzDocUViJh7ooOCZ-jBHKZddEsTa4yClHwhIL9SHJwjAv3bC6TZIcUnX36ZqNBWvLbnNAQvdtzqrVf3l0pv3QQ',
)
})

it('unpacks DIDComm message intended for migrated managed key', async () => {
const packed = {
message:
'{"protected":"eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiZW5jIjoiWEMyMFAifQ","iv":"mBAgYLce2JpmKtmlNQLG6w9lm6kqf4Ne","ciphertext":"D9_7Xxj51xn3T9yBU-rZmxSTrR82Pi4G7hWCDSxSpRUlmUh2uJoqeCHixSTFeZvFAfw2ryROjrxbpCh5Arg-wqrW3WwKGpVFHXO_r0jHso5lNMO-vGjxOULN","tag":"9Qs-esw1tcnM0jE_Q3LxIQ","recipients":[{"encrypted_key":"kGNaBfhPS2VETu-_iYaUwy13sC1ZVm3i_qYiYkuEleA","header":{"alg":"ECDH-ES+XC20PKW","iv":"1sK1pyOwy_hNY_WsJPGdoFqE8ken51IA","tag":"MplY66h-bHnuSdP1ZGLYyw","epk":{"kty":"OKP","crv":"X25519","x":"UZx8Uf3BJ-m3wm7sBjvCp1UXuHA9v0Qu5KvWfWyBNio"},"kid":"did:key:z6MkiPXoC2uAWPdQpotWxzNMJpaDbfPxaQWcbux5avNwEMfD#z6LSqL9zfeZa53RwpAkjxN7Gizvzv4rjAT7GwhLVYLPXK5dC"}}]}',
}
const msg = await agent.unpackDIDCommMessage(packed)
expect(msg.message.body).toEqual({ hello: 'world' })
})
})
})
7 changes: 4 additions & 3 deletions __tests__/localAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
DataStore,
DataStoreORM,
ProfileDiscoveryProvider,
PrivateKeyStore,
migrations,
} from '../packages/data-store/src'
import { createConnection, Connection } from 'typeorm'
Expand Down Expand Up @@ -91,7 +92,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
logging: false,
entities: Entities,
// allow shared tests to override connection options
...options?.context?.dbConnectionOptions
...options?.context?.dbConnectionOptions,
})

agent = createAgent<
Expand All @@ -112,9 +113,9 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
},
plugins: [
new KeyManager({
store: new KeyStore(dbConnection, new SecretBox(secretKey)),
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(),
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
},
}),
new DIDManager({
Expand Down
4 changes: 2 additions & 2 deletions __tests__/localMemoryStoreAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
IAgentOptions,
} from '../packages/core/src'
import { MessageHandler } from '../packages/message-handler/src'
import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src'
import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../packages/key-manager/src'
import { DIDManager, MemoryDIDStore } from '../packages/did-manager/src'
import { createConnection, Connection } from 'typeorm'
import { DIDResolverPlugin } from '../packages/did-resolver/src'
Expand Down Expand Up @@ -95,7 +95,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
new KeyManager({
store: new MemoryKeyStore(),
kms: {
local: new KeyManagementSystem(),
local: new KeyManagementSystem(new MemoryPrivateKeyStore()),
},
}),
new DIDManager({
Expand Down
5 changes: 3 additions & 2 deletions __tests__/restAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
DataStore,
DataStoreORM,
ProfileDiscoveryProvider,
PrivateKeyStore,
migrations,
} from '../packages/data-store/src'
import { createConnection, Connection } from 'typeorm'
Expand Down Expand Up @@ -115,9 +116,9 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
...options,
plugins: [
new KeyManager({
store: new KeyStore(dbConnection, new SecretBox(secretKey)),
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(),
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
},
}),
new DIDManager({
Expand Down
50 changes: 25 additions & 25 deletions __tests__/shared/didManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,26 +268,33 @@ export default (testContext: {
})

it('should import identifier', async () => {
const imported: IIdentifier = {
did: 'did:web:example.org',
alias: 'example.org',
expect.assertions(1)
const did = 'did:web:imported.example'
const imported = await agent.didManagerImport({
did,
provider: 'did:web',
services: [
{
id: 'did:web:example.org#msg',
id: `${did}#msg`,
type: 'Messaging',
serviceEndpoint: 'https://example.org/messaging',
description: 'Handles incoming messages',
},
],
keys: [
{
kid: '0405debbb55a873648ca545f810e44edd1997688eb8e46b1fdd6842bd83e300b16fd8258a3ca19f5d858a5ab0c9ba8381b0bf00727be1154e5bbd3a4da5f186af6',
kms: 'local',
privateKeyHex: 'e63886b5ba367dc2aff9acea6d955ee7c39115f12eaf2aa6b1a2eaa852036668',
type: 'Secp256k1',
publicKeyHex:
'0405debbb55a873648ca545f810e44edd1997688eb8e46b1fdd6842bd83e300b16fd8258a3ca19f5d858a5ab0c9ba8381b0bf00727be1154e5bbd3a4da5f186af6',
privateKeyHex: '55e67c7f7b4c25657d3144fa7ca3cf842790906ce24176b02b927b3aebffab50',
},
],
})
expect(imported).toEqual({
did,
keys: [
{
kid: '04dd467afb12bdb797303e7f3f0c8cd0ba80d518dc4e339e0e2eb8f2d99a9415cac537854a30d31a854b7af0b4fcb54c3954047390fa9500d3cc2e15a3e09017bb',
kms: 'local',
meta: {
algorithms: [
'ES256K',
Expand All @@ -297,28 +304,21 @@ export default (testContext: {
'eth_signMessage',
],
},
publicKeyHex:
'04dd467afb12bdb797303e7f3f0c8cd0ba80d518dc4e339e0e2eb8f2d99a9415cac537854a30d31a854b7af0b4fcb54c3954047390fa9500d3cc2e15a3e09017bb',
type: 'Secp256k1',
},
],
provider: 'did:web',
services: [
{
kid: 'b4fdd9cb90730776f2934fd5bfd43d187fff667ddbe81ec40445250fcc6b60df',
kms: 'local',
type: 'Ed25519',
publicKeyHex: 'b4fdd9cb90730776f2934fd5bfd43d187fff667ddbe81ec40445250fcc6b60df',
privateKeyHex:
'eb1722c5767e9e938e6bef6361baf16d389051b6c1dcaf58adedf5ef9652be67b4fdd9cb90730776f2934fd5bfd43d187fff667ddbe81ec40445250fcc6b60df',
meta: { algorithms: ['Ed25519', 'EdDSA'] },
description: 'Handles incoming messages',
id: `${did}#msg`,
serviceEndpoint: 'https://example.org/messaging',
type: 'Messaging',
},
],
}

await agent.didManagerImport(imported)

const importedIdentifier = await agent.didManagerGet({
did: imported.did,
})

expect(importedIdentifier.did).toEqual(imported.did)
expect(importedIdentifier.keys.length).toEqual(imported.keys.length)
expect(importedIdentifier.services).toEqual(imported.services)
})

it('should set alias for identifier', async () => {
Expand Down
Loading

0 comments on commit 6b1d135

Please sign in to comment.