Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: kms-web3 #924

Merged
merged 14 commits into from
Jun 13, 2022
Merged
9 changes: 9 additions & 0 deletions __tests__/localAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
SelectiveDisclosure,
} from '../packages/selective-disclosure/src'
import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src'
import { Web3KeyManagementSystem } from '../packages/kms-web3/src'
import { DIDDiscovery, IDIDDiscovery } from '../packages/did-discovery/src'

import {
Expand Down Expand Up @@ -77,6 +78,9 @@ import messageHandler from './shared/messageHandler'
import didDiscovery from './shared/didDiscovery'
import dbInitOptions from './shared/dbInitOptions'
import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow'
import utils from './shared/utils'
import web3 from './shared/web3'


jest.setTimeout(60000)

Expand Down Expand Up @@ -141,6 +145,9 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
web3: new Web3KeyManagementSystem({
'ganache': provider
})
},
}),
new DIDManager({
Expand Down Expand Up @@ -261,5 +268,7 @@ describe('Local integration tests', () => {
didCommPacking(testContext)
didDiscovery(testContext)
dbInitOptions(testContext)
utils(testContext)
web3(testContext)
didCommWithEthrDidFlow(testContext)
})
5 changes: 5 additions & 0 deletions __tests__/localJsonStoreAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
SelectiveDisclosure,
} from '../packages/selective-disclosure/src'
import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src'
import { Web3KeyManagementSystem } from '../packages/kms-web3/src'
import {
DataStoreJson,
DIDStoreJson,
Expand Down Expand Up @@ -67,8 +68,10 @@ import keyManager from './shared/keyManager'
import didManager from './shared/didManager'
import didCommPacking from './shared/didCommPacking'
import messageHandler from './shared/messageHandler'
import utils from './shared/utils'
import { JsonFileStore } from './utils/json-file-store'


jest.setTimeout(60000)

const infuraProjectId = '3586660d179141e3801c3895de1c2eba'
Expand Down Expand Up @@ -120,6 +123,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
store: new KeyStoreJson(jsonFileStore),
kms: {
local: new KeyManagementSystem(new PrivateKeyStoreJson(jsonFileStore, new SecretBox(secretKey))),
web3: new Web3KeyManagementSystem({}),
},
}),
new DIDManager({
Expand Down Expand Up @@ -218,4 +222,5 @@ describe('Local json-data-store integration tests', () => {
didManager(testContext)
messageHandler(testContext)
didCommPacking(testContext)
utils(testContext)
})
4 changes: 4 additions & 0 deletions __tests__/localMemoryStoreAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
SelectiveDisclosure,
} from '../packages/selective-disclosure/src'
import { KeyManagementSystem } from '../packages/kms-local/src'
import { Web3KeyManagementSystem } from '../packages/kms-web3/src'
import { DataStore, DataStoreORM, Entities, migrations } from '../packages/data-store/src'
import { FakeDidProvider, FakeDidResolver } from '../packages/test-utils/src'

Expand All @@ -60,6 +61,7 @@ import keyManager from './shared/keyManager'
import didManager from './shared/didManager'
import didCommPacking from './shared/didCommPacking'
import messageHandler from './shared/messageHandler'
import utils from './shared/utils'

jest.setTimeout(60000)

Expand Down Expand Up @@ -115,6 +117,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
store: new MemoryKeyStore(),
kms: {
local: new KeyManagementSystem(new MemoryPrivateKeyStore()),
web3: new Web3KeyManagementSystem({})
},
}),
new DIDManager({
Expand Down Expand Up @@ -214,4 +217,5 @@ describe('Local in-memory integration tests', () => {
didManager(testContext)
messageHandler(testContext)
didCommPacking(testContext)
utils(testContext)
})
4 changes: 4 additions & 0 deletions __tests__/restAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
SelectiveDisclosure,
} from '../packages/selective-disclosure/src'
import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src'
import { Web3KeyManagementSystem } from '../packages/kms-web3/src'
import {
DataStore,
DataStoreORM,
Expand Down Expand Up @@ -81,6 +82,7 @@ import didCommPacking from './shared/didCommPacking'
import didWithFakeDidFlow from './shared/didCommWithFakeDidFlow'
import messageHandler from './shared/messageHandler'
import didDiscovery from './shared/didDiscovery'
import utils from './shared/utils'

jest.setTimeout(60000)

Expand Down Expand Up @@ -138,6 +140,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
store: new KeyStore(dbConnection),
kms: {
local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))),
web3: new Web3KeyManagementSystem({})
},
}),
new DIDManager({
Expand Down Expand Up @@ -263,4 +266,5 @@ describe('REST integration tests', () => {
didCommPacking(testContext)
didWithFakeDidFlow(testContext)
didDiscovery(testContext)
utils(testContext)
})
2 changes: 1 addition & 1 deletion __tests__/shared/keyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default (testContext: {

it('should get a list of available key management systems', async () => {
const keyManagementSystems = await agent.keyManagerGetKeyManagementSystems()
expect(keyManagementSystems).toEqual(['local'])
expect(keyManagementSystems).toEqual(['local', 'web3'])
})

it('should create Secp256k1 key', async () => {
Expand Down
61 changes: 61 additions & 0 deletions __tests__/shared/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { IAgentOptions, IDIDManager, IResolver, MinimalImportableKey, TAgent } from '../../packages/core/src'
import { getChainIdForDidEthr, resolveDidOrThrow, mapIdentifierKeysToDoc } from '../../packages/utils/src'

type ConfiguredAgent = TAgent<IResolver & IDIDManager>

export default (testContext: {
getAgent: (options?: IAgentOptions) => ConfiguredAgent
setup: (options?: IAgentOptions) => Promise<boolean>
tearDown: () => Promise<boolean>
}) => {
describe('utilities', () => {
let agent: ConfiguredAgent

beforeAll(async () => {
await testContext.setup()
agent = testContext.getAgent()
return true
})
afterAll(testContext.tearDown)

it('should get chainId for ethr did', async () => {
const didUrl = 'did:ethr:rinkeby:0xb09b66026ba5909a7cfe99b76875431d2b8d5190'
const didDoc = await resolveDidOrThrow(didUrl, {agent})
if (didDoc.verificationMethod) {
const chainId = getChainIdForDidEthr(didDoc.verificationMethod[0])
expect(chainId).toEqual(4)
}
})

it('should map identifier keys to did doc', async () => {
const account = `0xb09b66026ba5909a7cfe99b76875431d2b8d5190`
const did = `did:ethr:0x4:${account}`
const controllerKeyId = `metamask-${account}`
await agent.didManagerImport({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not to be fixed in this PR, but definitely before the next release, so I'm writing this as a reference.

This import seems crucial to the way the web3-kms will behave.
The key metadata, the controllerKeyId and kid of the key must all align properly for this to work.

Also, if web3 providers change, there should also be some update to the managed DIDs and keys from @veramo/did-manager / @veramo/key-manager

did,
provider: 'did:ethr:rinkeby',
controllerKeyId,
keys: [{
kid: controllerKeyId,
type: 'Secp256k1',
kms: 'web3',
privateKeyHex: '',
publicKeyHex: '',
meta: {
account,
provider: 'metamask',
algorithms: [
'eth_signMessage',
'eth_signTypedData',
]
},
} as MinimalImportableKey],
})

const identifier = await agent.didManagerGet({ did })
const extendedKeys = await mapIdentifierKeysToDoc(identifier, 'verificationMethod', { agent })
expect(extendedKeys[0].meta.verificationMethod?.blockchainAccountId?.toLocaleLowerCase()).toEqual(`eip155:4:${account}`)

})
})
}
91 changes: 91 additions & 0 deletions __tests__/shared/web3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { IAgentOptions, IDIDManager, IIdentifier, IKeyManager, IResolver, MinimalImportableKey, TAgent, VerifiableCredential } from '../../packages/core/src'

type ConfiguredAgent = TAgent<IResolver & IDIDManager & IKeyManager>

export default (testContext: {
getAgent: (options?: IAgentOptions) => ConfiguredAgent
setup: (options?: IAgentOptions) => Promise<boolean>
tearDown: () => Promise<boolean>
}) => {
describe('web3', () => {
let agent: ConfiguredAgent
let identifier: IIdentifier
let verifiableCredential: VerifiableCredential

beforeAll(async () => {
await testContext.setup()
agent = testContext.getAgent()
return true
})
afterAll(testContext.tearDown)

it('should import ganache did', async () => {
const account = `0x7e5f4552091a69125d5dfcb7b8c2659029395bdf`
const did = `did:ethr:ganache:${account}`
const controllerKeyId = `ganache-${account}`
identifier = await agent.didManagerImport({
did,
provider: 'did:ethr:ganache',
controllerKeyId,
keys: [{
kid: controllerKeyId,
type: 'Secp256k1',
kms: 'web3',
privateKeyHex: '',
publicKeyHex: '',
meta: {
account,
provider: 'ganache',
algorithms: [
'eth_signMessage',
'eth_signTypedData',
]
},
} as MinimalImportableKey],
})
})

// getting error: The method personal_sign does not exist/is not available
// https://github.com/trufflesuite/ganache/issues/995
it.skip('should sign a message', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these 2 tests should not be skipped since they represent the base functionality of this package.

if (identifier.controllerKeyId) {
const signature = await agent.keyManagerSign({
data: 'Hello world',
keyRef: identifier.controllerKeyId,
algorithm: 'eth_signMessage'
})
expect(signature).toBeTruthy()
}
})

it('should create verifiable credential with EthereumEip712Signature2021 proof type', async () => {
verifiableCredential = await agent.createVerifiableCredential({
credential: {
issuer: { id: identifier.did },
'@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'],
type: ['VerifiableCredential', 'Custom'],
issuanceDate: new Date().toISOString(),
credentialSubject: {
id: 'did:web:example.com',
you: 'Rock',
}
},
proofFormat: 'EthereumEip712Signature2021',
})

expect(verifiableCredential).toHaveProperty('proof.proofValue')
expect(verifiableCredential['@context']).toEqual([
'https://www.w3.org/2018/credentials/v1',
'https://example.com/1/2/3',
])
expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom'])

const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential })
expect(typeof hash).toEqual('string')

const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash })
expect(verifiableCredential).toEqual(verifiableCredential2)

})
})
}
2 changes: 1 addition & 1 deletion __tests__/utils/ganache-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ganache from 'ganache'
*
* This provider can only be used in a single test suite, because of some concurrency issues with ganache.
*/
export async function createGanacheProvider(): Promise<{ provider: JsonRpcProvider; registry: string }> {
export async function createGanacheProvider(): Promise<{ provider: Web3Provider; registry: string }> {
const provider = new Web3Provider(
ganache.provider({
logging: { quiet: true },
Expand Down
14 changes: 7 additions & 7 deletions packages/credential-eip712/src/agent/CredentialEIP712.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,19 @@ export class CredentialIssuerEIP712 implements IAgentPlugin {
version: "1",
};

const allTypes = getEthTypesFromInputDoc(credential, "VerifiableCredential");
const primaryType = "VerifiableCredential"
const allTypes = getEthTypesFromInputDoc(credential, primaryType);
const types = {...allTypes}
delete(types.EIP712Domain)

const data = JSON.stringify({domain, types, message})
const data = JSON.stringify({ domain, types, message, primaryType })

const signature = await context.agent.keyManagerSign({ keyRef, data, algorithm: 'eth_signTypedData' })

credential['proof']['proofValue'] = signature;
credential['proof']['eip712'] = {
domain,
messageSchema: allTypes,
primaryType: "VerifiableCredential",
primaryType,
}

return credential as VerifiableCredential;
Expand Down Expand Up @@ -246,9 +246,9 @@ export class CredentialIssuerEIP712 implements IAgentPlugin {
version: "1",
};

const allTypes = getEthTypesFromInputDoc(presentation, "VerifiablePresentation");
const primaryType = 'VerifiablePresentation'
const allTypes = getEthTypesFromInputDoc(presentation, primaryType);
const types = {...allTypes}
delete(types.EIP712Domain)

const data = JSON.stringify({domain, types, message})

Expand All @@ -260,7 +260,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin {
presentation.proof.eip712 = {
domain,
messageSchema: allTypes,
primaryType: "VerifiablePresentation",
primaryType,
};

return presentation as VerifiablePresentation
Expand Down
27 changes: 19 additions & 8 deletions packages/did-provider-ethr/src/ethr-did-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,25 @@ export class EthrDIDProvider extends AbstractIdentifierProvider {
if (typeof controllerKey === 'undefined') {
throw new Error('invalid_argument: identifier.controllerKeyId is not managed by this agent')
}
return new EthrDID({
identifier: identifier.did,
provider: this.web3Provider,
chainNameOrId: this.network,
rpcUrl: this.rpcUrl,
registry: this.registry,
txSigner: new KmsEthereumSigner(controllerKey, context, this.web3Provider),
})
if (controllerKey.meta?.algorithms?.includes('eth_signTransaction')) {
return new EthrDID({
identifier: identifier.did,
provider: this.web3Provider,
chainNameOrId: this.network,
rpcUrl: this.rpcUrl,
registry: this.registry,
txSigner: new KmsEthereumSigner(controllerKey, context, this.web3Provider),
})
} else {
// Web3Provider should perform signing and sending transaction
return new EthrDID({
identifier: identifier.did,
provider: this.web3Provider,
chainNameOrId: this.network,
rpcUrl: this.rpcUrl,
registry: this.registry,
})
}
}

async addKey(
Expand Down
1 change: 1 addition & 0 deletions packages/key-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@ethersproject/abstract-signer": "^5.6.2",
"typescript": "4.7.3"
},
"files": [
Expand Down
1 change: 1 addition & 0 deletions packages/key-manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { AbstractKeyStore } from './abstract-key-store'
export { AbstractPrivateKeyStore, ImportablePrivateKey, ManagedPrivateKey } from './abstract-private-key-store'
export { AbstractSecretBox } from './abstract-secret-box'
export { MemoryKeyStore, MemoryPrivateKeyStore } from './memory-key-store'
export * from './types'
Loading