From 8b59fa322497edf2747b9ba2c92b7b1cf19be23d Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 9 Jun 2022 16:12:03 +0300 Subject: [PATCH 01/13] feat: kms-web3 --- __tests__/localAgent.test.ts | 37 ++-- __tests__/localJsonStoreAgent.test.ts | 31 +-- __tests__/localMemoryStoreAgent.test.ts | 4 + __tests__/restAgent.test.ts | 6 +- __tests__/shared/keyManager.ts | 2 +- __tests__/shared/utils.ts | 60 ++++++ .../src/ethr-did-provider.ts | 27 ++- packages/kms-web3/LICENSE | 201 ++++++++++++++++++ packages/kms-web3/README.md | 8 + packages/kms-web3/package.json | 42 ++++ packages/kms-web3/src/index.ts | 9 + .../src/web3-key-management-system.ts | 133 ++++++++++++ packages/kms-web3/tsconfig.json | 9 + packages/tsconfig.json | 1 + packages/utils/src/did-utils.ts | 13 +- yarn.lock | 28 ++- 16 files changed, 567 insertions(+), 44 deletions(-) create mode 100644 __tests__/shared/utils.ts create mode 100644 packages/kms-web3/LICENSE create mode 100644 packages/kms-web3/README.md create mode 100644 packages/kms-web3/package.json create mode 100644 packages/kms-web3/src/index.ts create mode 100644 packages/kms-web3/src/web3-key-management-system.ts create mode 100644 packages/kms-web3/tsconfig.json diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index ac7018356..b1c2526b0 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -17,7 +17,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager } from '../packages/key-manager/src' +import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' @@ -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 { @@ -77,6 +78,8 @@ 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' + jest.setTimeout(60000) @@ -141,6 +144,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStore(dbConnection), kms: { local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))), + web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) }, }), new DIDManager({ @@ -247,19 +251,20 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - saveClaims(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) - didDiscovery(testContext) - dbInitOptions(testContext) - didCommWithEthrDidFlow(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // saveClaims(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) + // didDiscovery(testContext) + // dbInitOptions(testContext) + // didCommWithEthrDidFlow(testContext) + utils(testContext) }) diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index ee4f1fdd3..167830a05 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -17,7 +17,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager } from '../packages/key-manager/src' +import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' import { DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' @@ -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, @@ -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' @@ -120,6 +123,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStoreJson(jsonFileStore), kms: { local: new KeyManagementSystem(new PrivateKeyStoreJson(jsonFileStore, new SecretBox(secretKey))), + web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()), }, }), new DIDManager({ @@ -206,16 +210,17 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local json-data-store integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - saveClaims(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // saveClaims(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) + utils(testContext) }) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 4310b1bdd..2992769b2 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -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' @@ -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) @@ -115,6 +117,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new MemoryKeyStore(), kms: { local: new KeyManagementSystem(new MemoryPrivateKeyStore()), + web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) }, }), new DIDManager({ @@ -214,4 +217,5 @@ describe('Local in-memory integration tests', () => { didManager(testContext) messageHandler(testContext) didCommPacking(testContext) + utils(testContext) }) diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 6a852ebfc..0578b0cde 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -20,7 +20,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager } from '../packages/key-manager/src' +import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' @@ -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, @@ -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) @@ -138,6 +140,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStore(dbConnection), kms: { local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))), + web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) }, }), new DIDManager({ @@ -263,4 +266,5 @@ describe('REST integration tests', () => { didCommPacking(testContext) didWithFakeDidFlow(testContext) didDiscovery(testContext) + utils(testContext) }) diff --git a/__tests__/shared/keyManager.ts b/__tests__/shared/keyManager.ts index 1470a513b..899fede4b 100644 --- a/__tests__/shared/keyManager.ts +++ b/__tests__/shared/keyManager.ts @@ -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 () => { diff --git a/__tests__/shared/utils.ts b/__tests__/shared/utils.ts new file mode 100644 index 000000000..afa6c0d8d --- /dev/null +++ b/__tests__/shared/utils.ts @@ -0,0 +1,60 @@ +import { IAgentOptions, IDIDManager, IResolver, MinimalImportableKey, TAgent } from '../../packages/core/src' +import { getChainIdForDidEthr, resolveDidOrThrow, mapIdentifierKeysToDoc } from '../../packages/utils/src' + +type ConfiguredAgent = TAgent + +export default (testContext: { + getAgent: (options?: IAgentOptions) => ConfiguredAgent + setup: (options?: IAgentOptions) => Promise + tearDown: () => Promise +}) => { + 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 ethereumAddress = `0xb09b66026ba5909a7cfe99b76875431d2b8d5190` + const did = `did:ethr:0x4:${ethereumAddress}` + const controllerKeyId = `${did}#controller` + await agent.didManagerImport({ + did, + provider: 'did:ethr:rinkeby', + controllerKeyId, + keys: [{ + kid: controllerKeyId, + type: 'Secp256k1', + kms: 'web3', + privateKeyHex: '', + meta: { + ethereumAddress, + 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(`${ethereumAddress}@eip155:4`) + + }) + }) +} \ No newline at end of file diff --git a/packages/did-provider-ethr/src/ethr-did-provider.ts b/packages/did-provider-ethr/src/ethr-did-provider.ts index 32dd833b5..152eafcbe 100644 --- a/packages/did-provider-ethr/src/ethr-did-provider.ts +++ b/packages/did-provider-ethr/src/ethr-did-provider.ts @@ -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( diff --git a/packages/kms-web3/LICENSE b/packages/kms-web3/LICENSE new file mode 100644 index 000000000..fd815d7f8 --- /dev/null +++ b/packages/kms-web3/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Consensys AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/kms-web3/README.md b/packages/kms-web3/README.md new file mode 100644 index 000000000..4287037d7 --- /dev/null +++ b/packages/kms-web3/README.md @@ -0,0 +1,8 @@ +# Veramo Web3 KMS + +A Veramo KMS implementation that provides secp256k1 crypto backed by web3 wallets. + +This module provides an implementation +of [`AbstractKeyManagementSystem`](../key-manager/src/abstract-key-management-system.ts#L6) that can be used by the +[`@veramo/key-manager`](../key-manager) plugin to provide Secp256k1 crypto functionality to a +Veramo agent. diff --git a/packages/kms-web3/package.json b/packages/kms-web3/package.json new file mode 100644 index 000000000..40e1d9b06 --- /dev/null +++ b/packages/kms-web3/package.json @@ -0,0 +1,42 @@ +{ + "name": "@veramo/kms-web3", + "description": "Veramo KMS implementation backed by web3 wallets", + "version": "3.1.4", + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@ethersproject/providers": "5.6.8", + "@ethersproject/abstract-signer": "5.6.2", + "@ethersproject/strings": "5.6.1", + "@ethersproject/transactions": "5.6.2", + "@veramo/core": "^3.1.4", + "@veramo/key-manager": "^3.1.4", + "debug": "^4.3.3" + }, + "devDependencies": { + "@types/debug": "4.1.7", + "typescript": "4.7.3" + }, + "files": [ + "build/**/*", + "src/**/*", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:uport-project/veramo.git", + "author": "Simonas Karuzas ", + "contributors": [ + { + "name": "Mircea Nistor", + "email": "mircea.nistor@mesh.xyz" + } + ], + "license": "Apache-2.0", + "keywords": [] +} diff --git a/packages/kms-web3/src/index.ts b/packages/kms-web3/src/index.ts new file mode 100644 index 000000000..edffafb01 --- /dev/null +++ b/packages/kms-web3/src/index.ts @@ -0,0 +1,9 @@ +/** + * Provides a web3 wallet backed + * {@link @veramo/kms-web3#Web3KeyManagementSystem | key management system } + * for the {@link @veramo/key-manager#KeyManager} + * + * @packageDocumentation + */ +export { Web3KeyManagementSystem } from './web3-key-management-system' + diff --git a/packages/kms-web3/src/web3-key-management-system.ts b/packages/kms-web3/src/web3-key-management-system.ts new file mode 100644 index 000000000..8e9e756c2 --- /dev/null +++ b/packages/kms-web3/src/web3-key-management-system.ts @@ -0,0 +1,133 @@ +import { TransactionRequest, Web3Provider } from '@ethersproject/providers' +import { + TKeyType, + IKey, + ManagedKeyInfo, + MinimalImportableKey, +} from '@veramo/core' +import { AbstractKeyManagementSystem, AbstractKeyStore } from '@veramo/key-manager' +import { toUtf8String } from '@ethersproject/strings' +import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' +import { parse } from '@ethersproject/transactions' +// import Debug from 'debug' +// const debug = Debug('veramo:kms:web3') + +type Eip712Payload = { + domain: TypedDataDomain + types: Record + primaryType: string + message: Record +} + +export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { + constructor(private providers: Record, private keyStore: AbstractKeyStore) { + super() + } + + createKey({ type }: { type: TKeyType }): Promise { + throw Error('not_supported: Web3KeyManagementSystem cannot create new keys') + } + + async importKey( + args: Omit, + ): Promise { + // throw Error('Not implemented') + return args as any as ManagedKeyInfo + } + + async listKeys(): Promise { + throw Error('not_implemented: Web3KeyManagementSystem listKeys') + } + + async sharedSecret(args: { + myKeyRef: Pick + theirKey: Pick + }): Promise { + throw Error('not_implemented: Web3KeyManagementSystem sharedSecret') + } + + async deleteKey(args: { kid: string }) { + // this kms doesn't need to delete keys + return true + } + + async sign({ + keyRef, + algorithm, + data, + }: { + keyRef: Pick + algorithm?: string + data: Uint8Array + }): Promise { + + let key: IKey + try { + key = await this.keyStore.get({ kid: keyRef.kid }) + } catch (e) { + throw new Error(`key_not_found: No key entry found for kid=${keyRef.kid}`) + } + + if (algorithm) { + if (algorithm === 'eth_signMessage') { + return await this.eth_signMessage(key, data) + } else if ( + ['eth_signTypedData', 'EthereumEip712Signature2021'].includes(algorithm) + ) { + return await this.eth_signTypedData(key, data) + } + } + + throw Error(`not_supported: Cannot sign ${algorithm} `) + } + + /** + * @returns a `0x` prefixed hex string representing the signed EIP712 data + */ + private async eth_signTypedData(key: IKey, data: Uint8Array) { + let msg, msgDomain, msgTypes + const serializedData = toUtf8String(data) + try { + const jsonData = JSON.parse(serializedData) as Eip712Payload + if ( + typeof jsonData.domain === 'object' && + typeof jsonData.types === 'object' + ) { + const { domain, types, message } = jsonData + msg = message + msgDomain = domain + msgTypes = types + } else { + // next check will throw since the data couldn't be parsed + } + } catch (e) { + // next check will throw since the data couldn't be parsed + } + if ( + typeof msgDomain !== 'object' || + typeof msgTypes !== 'object' || + typeof msg !== 'object' + ) { + throw Error( + `invalid_arguments: Cannot sign typed data. 'domain', 'types', and 'message' must be provided`, + ) + } + + const signature = await this.providers[key.meta?.provider] + .getSigner() + ._signTypedData(msgDomain, msgTypes, msg) + return signature + } + + /** + * @returns a `0x` prefixed hex string representing the signed message + */ + private async eth_signMessage(key: IKey, rawMessageBytes: Uint8Array) { + const signature = await this.providers[key.meta?.provider] + .getSigner() + .signMessage(rawMessageBytes) + // HEX encoded string, 0x prefixed + return signature + } + +} diff --git a/packages/kms-web3/tsconfig.json b/packages/kms-web3/tsconfig.json new file mode 100644 index 000000000..daae1afc0 --- /dev/null +++ b/packages/kms-web3/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.settings.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "declarationDir": "build" + }, + "references": [{ "path": "../core" }, { "path": "../key-manager" }] +} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 2d5fca6fc..82b34e0b1 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -18,6 +18,7 @@ { "path": "did-resolver" }, { "path": "key-manager" }, { "path": "kms-local" }, + { "path": "kms-web3" }, { "path": "message-handler" }, { "path": "remote-client" }, { "path": "remote-server" }, diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index b7d3d8661..97ff4bc4c 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -37,10 +37,12 @@ export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] return identifier.keys .map((key) => { if (key.type === 'Secp256k1') { - const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - key.publicKeyHex = computePublicKey(publicBytes, true).substring(2) - key.meta = { ...key.meta } - key.meta.ethereumAddress = computeAddress('0x' + key.publicKeyHex) + if (key.publicKeyHex) { + const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') + key.publicKeyHex = computePublicKey(publicBytes, true).substring(2) + key.meta = { ...key.meta } + key.meta.ethereumAddress = computeAddress('0x' + key.publicKeyHex) + } } return key }) @@ -52,6 +54,9 @@ function compareBlockchainAccountId(localKey: IKey, verificationMethod: _Normali return false } let vmEthAddr = getEthereumAddress(verificationMethod) + if (localKey.meta?.ethereumAddress) { + return vmEthAddr === localKey.meta?.ethereumAddress + } const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() return computedAddr === vmEthAddr } diff --git a/yarn.lock b/yarn.lock index 201ed87eb..435b2363b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1852,7 +1852,7 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/providers@^5.6.8": +"@ethersproject/providers@5.6.8": version "5.6.8" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== @@ -1878,6 +1878,32 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/providers@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" + integrity sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw== + dependencies: + "@ethersproject/abstract-provider" "^5.6.1" + "@ethersproject/abstract-signer" "^5.6.2" + "@ethersproject/address" "^5.6.1" + "@ethersproject/base64" "^5.6.1" + "@ethersproject/basex" "^5.6.1" + "@ethersproject/bignumber" "^5.6.2" + "@ethersproject/bytes" "^5.6.1" + "@ethersproject/constants" "^5.6.1" + "@ethersproject/hash" "^5.6.1" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks" "^5.6.3" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.1" + "@ethersproject/rlp" "^5.6.1" + "@ethersproject/sha2" "^5.6.1" + "@ethersproject/strings" "^5.6.1" + "@ethersproject/transactions" "^5.6.2" + "@ethersproject/web" "^5.6.1" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/random@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.1.tgz#66915943981bcd3e11bbd43733f5c3ba5a790255" From 4d29d2299bc1bf043c2b34a7f38cb199be9ad1c6 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 9 Jun 2022 18:23:51 +0300 Subject: [PATCH 02/13] fix(utils): test --- __tests__/shared/utils.ts | 9 +++++---- packages/utils/src/did-utils.ts | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/__tests__/shared/utils.ts b/__tests__/shared/utils.ts index afa6c0d8d..6fcf72f8f 100644 --- a/__tests__/shared/utils.ts +++ b/__tests__/shared/utils.ts @@ -28,8 +28,8 @@ export default (testContext: { }) it('should map identifier keys to did doc', async () => { - const ethereumAddress = `0xb09b66026ba5909a7cfe99b76875431d2b8d5190` - const did = `did:ethr:0x4:${ethereumAddress}` + const account = `0xb09b66026ba5909a7cfe99b76875431d2b8d5190` + const did = `did:ethr:0x4:${account}` const controllerKeyId = `${did}#controller` await agent.didManagerImport({ did, @@ -40,8 +40,9 @@ export default (testContext: { type: 'Secp256k1', kms: 'web3', privateKeyHex: '', + publicKeyHex: '', meta: { - ethereumAddress, + account, provider: 'metamask', algorithms: [ 'eth_signMessage', @@ -53,7 +54,7 @@ export default (testContext: { const identifier = await agent.didManagerGet({ did }) const extendedKeys = await mapIdentifierKeysToDoc(identifier, 'verificationMethod', { agent }) - expect(extendedKeys[0].meta.verificationMethod?.blockchainAccountId?.toLocaleLowerCase()).toEqual(`${ethereumAddress}@eip155:4`) + expect(extendedKeys[0].meta.verificationMethod?.blockchainAccountId?.toLocaleLowerCase()).toEqual(`${account}@eip155:4`) }) }) diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index 97ff4bc4c..e2dd2e27c 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -54,8 +54,8 @@ function compareBlockchainAccountId(localKey: IKey, verificationMethod: _Normali return false } let vmEthAddr = getEthereumAddress(verificationMethod) - if (localKey.meta?.ethereumAddress) { - return vmEthAddr === localKey.meta?.ethereumAddress + if (localKey.meta?.account) { + return vmEthAddr === localKey.meta?.account } const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() return computedAddr === vmEthAddr @@ -88,7 +88,6 @@ export async function mapIdentifierKeysToDoc( context: IAgentContext, ): Promise<_ExtendedIKey[]> { const didDocument = await resolveDidOrThrow(identifier.did, context) - // dereference all key agreement keys from DID document and normalize const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( didDocument, From 37a2b2e73228f10a72ed9a3a9eaf8d47af0d2568 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 9 Jun 2022 18:31:15 +0300 Subject: [PATCH 03/13] test: enabling all tests --- __tests__/localAgent.test.ts | 30 +++++++++++++-------------- __tests__/localJsonStoreAgent.test.ts | 24 ++++++++++----------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index b1c2526b0..35898349b 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -251,20 +251,20 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // saveClaims(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) - // didDiscovery(testContext) - // dbInitOptions(testContext) - // didCommWithEthrDidFlow(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + saveClaims(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) + didDiscovery(testContext) + dbInitOptions(testContext) + didCommWithEthrDidFlow(testContext) utils(testContext) }) diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index 167830a05..964b3b04f 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -210,17 +210,17 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local json-data-store integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // saveClaims(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + saveClaims(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) utils(testContext) }) From 9de1db5a04e618899ef8a4b402abd368db5de56f Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 9 Jun 2022 18:46:01 +0300 Subject: [PATCH 04/13] fix: tests --- __tests__/localAgent.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 35898349b..c2ecf8fbc 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -265,6 +265,6 @@ describe('Local integration tests', () => { didCommPacking(testContext) didDiscovery(testContext) dbInitOptions(testContext) - didCommWithEthrDidFlow(testContext) utils(testContext) + didCommWithEthrDidFlow(testContext) }) From ce9f8495bfd7b3bac55a25b86037cdd11d4bfe89 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Fri, 10 Jun 2022 13:10:34 +0300 Subject: [PATCH 05/13] fix(test-react-app): setup --- .../headless-tests/browserAgent.browser-test.ts | 2 ++ packages/test-react-app/src/veramo/setup.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/test-react-app/headless-tests/browserAgent.browser-test.ts b/packages/test-react-app/headless-tests/browserAgent.browser-test.ts index 1e2d4a9d7..d61e0b614 100644 --- a/packages/test-react-app/headless-tests/browserAgent.browser-test.ts +++ b/packages/test-react-app/headless-tests/browserAgent.browser-test.ts @@ -11,6 +11,7 @@ import saveClaims from '../../../__tests__/shared/saveClaims' import documentationExamples from '../../../__tests__/shared/documentationExamples' import didCommPacking from '../../../__tests__/shared/didCommPacking' import messageHandler from '../../../__tests__/shared/messageHandler' +import utils from '../../../__tests__/shared/utils' jest.setTimeout(3 * 60 * 1000) @@ -27,6 +28,7 @@ describe('Browser integration tests', () => { keyManager(testContext) didManager(testContext) messageHandler(testContext) + utils(testContext) didCommPacking(testContext) }) diff --git a/packages/test-react-app/src/veramo/setup.ts b/packages/test-react-app/src/veramo/setup.ts index 80b627b53..9fe935933 100644 --- a/packages/test-react-app/src/veramo/setup.ts +++ b/packages/test-react-app/src/veramo/setup.ts @@ -15,7 +15,7 @@ import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { MessageHandler } from '@veramo/message-handler' -import { KeyManager } from '@veramo/key-manager' +import { KeyManager, MemoryKeyStore } from '@veramo/key-manager' import { DIDManager } from '@veramo/did-manager' import { JwtMessageHandler } from '@veramo/did-jwt' import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '@veramo/credential-w3c' @@ -30,6 +30,7 @@ import { getDidKeyResolver, KeyDIDProvider } from '@veramo/did-provider-key' import { DIDComm, DIDCommMessageHandler, IDIDComm } from '@veramo/did-comm' import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure } from '@veramo/selective-disclosure' import { KeyManagementSystem, SecretBox } from '@veramo/kms-local' +import { Web3KeyManagementSystem } from '@veramo/kms-web3' import { EthrDIDProvider } from '@veramo/did-provider-ethr' import { WebDIDProvider } from '@veramo/did-provider-web' import { DataStoreJson, DIDStoreJson, KeyStoreJson, PrivateKeyStoreJson } from "@veramo/data-store-json"; @@ -71,6 +72,7 @@ export function getAgent(options?: IAgentOptions): TAgent { store: new KeyStoreJson(memoryJsonStore), kms: { local: new KeyManagementSystem(new PrivateKeyStoreJson(memoryJsonStore, new SecretBox(DB_SECRET_KEY))), + web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) }, }), new DIDManager({ From 41cb81f4751fbda1f948ec85b4530f10423ed43a Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Fri, 10 Jun 2022 16:14:46 +0300 Subject: [PATCH 06/13] fix(kms-web3): remove keyStore dependency --- __tests__/localAgent.test.ts | 6 +- __tests__/localJsonStoreAgent.test.ts | 2 +- __tests__/localMemoryStoreAgent.test.ts | 2 +- __tests__/restAgent.test.ts | 2 +- __tests__/shared/utils.ts | 2 +- __tests__/shared/web3.ts | 81 +++++++++++++++++++ __tests__/utils/ganache-provider.ts | 2 +- packages/kms-web3/package.json | 2 +- .../src/web3-key-management-system.ts | 40 +++++---- yarn.lock | 28 +------ 10 files changed, 112 insertions(+), 55 deletions(-) create mode 100644 __tests__/shared/web3.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index c2ecf8fbc..644136825 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -79,6 +79,7 @@ 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) @@ -144,7 +145,9 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStore(dbConnection), kms: { local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))), - web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) + web3: new Web3KeyManagementSystem({ + 'ganache': provider + }) }, }), new DIDManager({ @@ -266,5 +269,6 @@ describe('Local integration tests', () => { didDiscovery(testContext) dbInitOptions(testContext) utils(testContext) + // web3(testContext) didCommWithEthrDidFlow(testContext) }) diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index 964b3b04f..84fab96e6 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -123,7 +123,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStoreJson(jsonFileStore), kms: { local: new KeyManagementSystem(new PrivateKeyStoreJson(jsonFileStore, new SecretBox(secretKey))), - web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()), + web3: new Web3KeyManagementSystem({}), }, }), new DIDManager({ diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 2992769b2..347a1b83c 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -117,7 +117,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new MemoryKeyStore(), kms: { local: new KeyManagementSystem(new MemoryPrivateKeyStore()), - web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) + web3: new Web3KeyManagementSystem({}) }, }), new DIDManager({ diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 0578b0cde..81dbfc1f9 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -140,7 +140,7 @@ const setup = async (options?: IAgentOptions): Promise => { store: new KeyStore(dbConnection), kms: { local: new KeyManagementSystem(new PrivateKeyStore(dbConnection, new SecretBox(secretKey))), - web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) + web3: new Web3KeyManagementSystem({}) }, }), new DIDManager({ diff --git a/__tests__/shared/utils.ts b/__tests__/shared/utils.ts index 6fcf72f8f..8e9707d83 100644 --- a/__tests__/shared/utils.ts +++ b/__tests__/shared/utils.ts @@ -30,7 +30,7 @@ export default (testContext: { it('should map identifier keys to did doc', async () => { const account = `0xb09b66026ba5909a7cfe99b76875431d2b8d5190` const did = `did:ethr:0x4:${account}` - const controllerKeyId = `${did}#controller` + const controllerKeyId = `metamask-${account}` await agent.didManagerImport({ did, provider: 'did:ethr:rinkeby', diff --git a/__tests__/shared/web3.ts b/__tests__/shared/web3.ts new file mode 100644 index 000000000..22bd9b10a --- /dev/null +++ b/__tests__/shared/web3.ts @@ -0,0 +1,81 @@ +import { IAgentOptions, IDIDManager, IIdentifier, IResolver, MinimalImportableKey, TAgent, VerifiableCredential } from '../../packages/core/src' + +type ConfiguredAgent = TAgent + +export default (testContext: { + getAgent: (options?: IAgentOptions) => ConfiguredAgent + setup: (options?: IAgentOptions) => Promise + tearDown: () => Promise +}) => { + 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], + }) + + + + }) + + 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) + + }) + }) +} \ No newline at end of file diff --git a/__tests__/utils/ganache-provider.ts b/__tests__/utils/ganache-provider.ts index d340e47c6..4534d5935 100644 --- a/__tests__/utils/ganache-provider.ts +++ b/__tests__/utils/ganache-provider.ts @@ -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 }, diff --git a/packages/kms-web3/package.json b/packages/kms-web3/package.json index 40e1d9b06..3626738fc 100644 --- a/packages/kms-web3/package.json +++ b/packages/kms-web3/package.json @@ -8,8 +8,8 @@ "build": "tsc" }, "dependencies": { - "@ethersproject/providers": "5.6.8", "@ethersproject/abstract-signer": "5.6.2", + "@ethersproject/providers": "5.5.0", "@ethersproject/strings": "5.6.1", "@ethersproject/transactions": "5.6.2", "@veramo/core": "^3.1.4", diff --git a/packages/kms-web3/src/web3-key-management-system.ts b/packages/kms-web3/src/web3-key-management-system.ts index 8e9e756c2..2eac28853 100644 --- a/packages/kms-web3/src/web3-key-management-system.ts +++ b/packages/kms-web3/src/web3-key-management-system.ts @@ -1,16 +1,13 @@ -import { TransactionRequest, Web3Provider } from '@ethersproject/providers' +import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers' import { TKeyType, IKey, ManagedKeyInfo, MinimalImportableKey, } from '@veramo/core' -import { AbstractKeyManagementSystem, AbstractKeyStore } from '@veramo/key-manager' +import { AbstractKeyManagementSystem } from '@veramo/key-manager' import { toUtf8String } from '@ethersproject/strings' import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' -import { parse } from '@ethersproject/transactions' -// import Debug from 'debug' -// const debug = Debug('veramo:kms:web3') type Eip712Payload = { domain: TypedDataDomain @@ -20,7 +17,7 @@ type Eip712Payload = { } export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { - constructor(private providers: Record, private keyStore: AbstractKeyStore) { + constructor(private providers: Record) { super() } @@ -51,6 +48,16 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { return true } + // keyRef should be in this format '{providerName-account} + // example: 'metamask-0xf3beac30c498d9e26865f34fcaa57dbb935b0d74' + private getSignerByKeyRef(keyRef: Pick): JsonRpcSigner { + const [ providerName, account ] = keyRef.kid.split('-') + if (!this.providers[providerName]) { + throw Error(`not_available: provider ${providerName}`) + } + return this.providers[providerName].getSigner(account) + } + async sign({ keyRef, algorithm, @@ -60,21 +67,14 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { algorithm?: string data: Uint8Array }): Promise { - - let key: IKey - try { - key = await this.keyStore.get({ kid: keyRef.kid }) - } catch (e) { - throw new Error(`key_not_found: No key entry found for kid=${keyRef.kid}`) - } if (algorithm) { if (algorithm === 'eth_signMessage') { - return await this.eth_signMessage(key, data) + return await this.eth_signMessage(keyRef, data) } else if ( ['eth_signTypedData', 'EthereumEip712Signature2021'].includes(algorithm) ) { - return await this.eth_signTypedData(key, data) + return await this.eth_signTypedData(keyRef, data) } } @@ -84,7 +84,7 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { /** * @returns a `0x` prefixed hex string representing the signed EIP712 data */ - private async eth_signTypedData(key: IKey, data: Uint8Array) { + private async eth_signTypedData(keyRef: Pick, data: Uint8Array) { let msg, msgDomain, msgTypes const serializedData = toUtf8String(data) try { @@ -113,8 +113,7 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { ) } - const signature = await this.providers[key.meta?.provider] - .getSigner() + const signature = await this.getSignerByKeyRef(keyRef) ._signTypedData(msgDomain, msgTypes, msg) return signature } @@ -122,9 +121,8 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { /** * @returns a `0x` prefixed hex string representing the signed message */ - private async eth_signMessage(key: IKey, rawMessageBytes: Uint8Array) { - const signature = await this.providers[key.meta?.provider] - .getSigner() + private async eth_signMessage(keyRef: Pick, rawMessageBytes: Uint8Array) { + const signature = await this.getSignerByKeyRef(keyRef) .signMessage(rawMessageBytes) // HEX encoded string, 0x prefixed return signature diff --git a/yarn.lock b/yarn.lock index 435b2363b..7b8725ebf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1852,33 +1852,7 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/providers@5.6.8": - version "5.6.8" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" - integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/base64" "^5.6.1" - "@ethersproject/basex" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/providers@^5.5.0": +"@ethersproject/providers@5.5.0", "@ethersproject/providers@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" integrity sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw== From 057fcbf2c8c843aa3051d4350c6626c0b5b89a4c Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Fri, 10 Jun 2022 16:40:08 +0300 Subject: [PATCH 07/13] test: kms-web3 --- __tests__/localAgent.test.ts | 4 ++-- __tests__/shared/utils.ts | 2 +- __tests__/shared/web3.ts | 20 +++++++++++++++----- packages/kms-web3/package.json | 2 +- yarn.lock | 10 +++++----- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 644136825..28632c914 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -79,7 +79,7 @@ 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' +import web3 from './shared/web3' jest.setTimeout(60000) @@ -269,6 +269,6 @@ describe('Local integration tests', () => { didDiscovery(testContext) dbInitOptions(testContext) utils(testContext) - // web3(testContext) + web3(testContext) didCommWithEthrDidFlow(testContext) }) diff --git a/__tests__/shared/utils.ts b/__tests__/shared/utils.ts index 8e9707d83..56da45e7e 100644 --- a/__tests__/shared/utils.ts +++ b/__tests__/shared/utils.ts @@ -54,7 +54,7 @@ export default (testContext: { const identifier = await agent.didManagerGet({ did }) const extendedKeys = await mapIdentifierKeysToDoc(identifier, 'verificationMethod', { agent }) - expect(extendedKeys[0].meta.verificationMethod?.blockchainAccountId?.toLocaleLowerCase()).toEqual(`${account}@eip155:4`) + expect(extendedKeys[0].meta.verificationMethod?.blockchainAccountId?.toLocaleLowerCase()).toEqual(`eip155:4:${account}`) }) }) diff --git a/__tests__/shared/web3.ts b/__tests__/shared/web3.ts index 22bd9b10a..a397a3bf8 100644 --- a/__tests__/shared/web3.ts +++ b/__tests__/shared/web3.ts @@ -1,6 +1,6 @@ -import { IAgentOptions, IDIDManager, IIdentifier, IResolver, MinimalImportableKey, TAgent, VerifiableCredential } from '../../packages/core/src' +import { IAgentOptions, IDIDManager, IIdentifier, IKeyManager, IResolver, MinimalImportableKey, TAgent, VerifiableCredential } from '../../packages/core/src' -type ConfiguredAgent = TAgent +type ConfiguredAgent = TAgent export default (testContext: { getAgent: (options?: IAgentOptions) => ConfiguredAgent @@ -43,12 +43,22 @@ export default (testContext: { }, } as MinimalImportableKey], }) + }) - - + // getting error: The method personal_sign does not exist/is not available + it.skip('should sign a message', async () => { + 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 () => { + // getting error: cannot sign data; string sent, expected object + it.skip('should create verifiable credential with EthereumEip712Signature2021 proof type', async () => { verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, diff --git a/packages/kms-web3/package.json b/packages/kms-web3/package.json index 3626738fc..97a70a852 100644 --- a/packages/kms-web3/package.json +++ b/packages/kms-web3/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@ethersproject/abstract-signer": "5.6.2", - "@ethersproject/providers": "5.5.0", + "@ethersproject/providers": "^5.6.8", "@ethersproject/strings": "5.6.1", "@ethersproject/transactions": "5.6.2", "@veramo/core": "^3.1.4", diff --git a/yarn.lock b/yarn.lock index 7b8725ebf..aa9a6c8ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1852,10 +1852,10 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/providers@5.5.0", "@ethersproject/providers@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" - integrity sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw== +"@ethersproject/providers@^5.6.8": + version "5.6.8" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" + integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== dependencies: "@ethersproject/abstract-provider" "^5.6.1" "@ethersproject/abstract-signer" "^5.6.2" @@ -1935,7 +1935,7 @@ elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/strings@^5.6.1": +"@ethersproject/strings@5.6.1", "@ethersproject/strings@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.1.tgz#dbc1b7f901db822b5cafd4ebf01ca93c373f8952" integrity sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw== From 1163f1fa3d377b230e10fa0cdf8c5b441740038c Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 16:39:21 +0300 Subject: [PATCH 08/13] test: update ganache --- package.json | 2 +- yarn.lock | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b6d807dee..8bc139a91 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "codecov": "3.8.3", "cross-fetch": "3.1.5", "ethr-did-registry": "0.0.3", - "ganache": "7.2.0", + "ganache": "^7.3.0", "jest": "27.5.1", "jest-fetch-mock": "3.0.3", "json-schema": "0.4.0", diff --git a/yarn.lock b/yarn.lock index aa9a6c8ab..e707257b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4299,6 +4299,13 @@ dependencies: "@types/node" "*" +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + "@types/body-parser@*": version "1.19.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" @@ -4543,6 +4550,11 @@ "@types/koa-compose" "*" "@types/node" "*" +"@types/lru-cache@5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -4695,6 +4707,11 @@ dependencies: "@types/node" "*" +"@types/seedrandom@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" + integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -9069,12 +9086,15 @@ fuzzy@^0.1.3: resolved "https://registry.yarnpkg.com/fuzzy/-/fuzzy-0.1.3.tgz#4c76ec2ff0ac1a36a9dccf9a00df8623078d4ed8" integrity sha1-THbsL/CsGjap3M+aAN+GIweNTtg= -ganache@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.2.0.tgz#2da7b0c609c5b0aff36d4e5216568a34fa1724f4" - integrity sha512-KsKysVeVN6CALALOkIPSIxNZbl5s2/DE6Z0lFpj05gH1XsvYMit3djP4LxpxdjUfSSyb9gIPEOzqMw7v56ihJg== +ganache@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.3.0.tgz#45c856dbd3f602a02c1fa4dcf959586d620e7f7d" + integrity sha512-FPfMsmYUiE4E0fxoqu9owdsfcA7xGz4dUrRvaucXvFgJL3Bh/JBqcGDRQajpqmu8v4hGs94NlJOVlD0Zm69uCA== dependencies: "@trufflesuite/bigint-buffer" "1.1.9" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "5.1.1" + "@types/seedrandom" "3.0.1" emittery "0.10.0" keccak "3.0.1" leveldown "6.1.0" From f5dfa89bf0185b3b46c212b0080207b1688bfdab Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 16:41:13 +0300 Subject: [PATCH 09/13] fix(credential-eip712): remove EIP712Domain at kms --- __tests__/shared/web3.ts | 4 +-- .../src/agent/CredentialEIP712.ts | 14 +++++----- .../kms-local/src/key-management-system.ts | 1 + .../src/web3-key-management-system.ts | 28 +++++++++++++------ 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/__tests__/shared/web3.ts b/__tests__/shared/web3.ts index a397a3bf8..88aa10dce 100644 --- a/__tests__/shared/web3.ts +++ b/__tests__/shared/web3.ts @@ -46,6 +46,7 @@ export default (testContext: { }) // 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 () => { if (identifier.controllerKeyId) { const signature = await agent.keyManagerSign({ @@ -57,8 +58,7 @@ export default (testContext: { } }) - // getting error: cannot sign data; string sent, expected object - it.skip('should create verifiable credential with EthereumEip712Signature2021 proof type', async () => { + it('should create verifiable credential with EthereumEip712Signature2021 proof type', async () => { verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, diff --git a/packages/credential-eip712/src/agent/CredentialEIP712.ts b/packages/credential-eip712/src/agent/CredentialEIP712.ts index da5ae6e29..a43c9343b 100644 --- a/packages/credential-eip712/src/agent/CredentialEIP712.ts +++ b/packages/credential-eip712/src/agent/CredentialEIP712.ts @@ -104,11 +104,11 @@ 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' }) @@ -116,7 +116,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { credential['proof']['eip712'] = { domain, messageSchema: allTypes, - primaryType: "VerifiableCredential", + primaryType, } return credential as VerifiableCredential; @@ -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}) @@ -260,7 +260,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { presentation.proof.eip712 = { domain, messageSchema: allTypes, - primaryType: "VerifiablePresentation", + primaryType, }; return presentation as VerifiablePresentation diff --git a/packages/kms-local/src/key-management-system.ts b/packages/kms-local/src/key-management-system.ts index df4445650..6e0e60cf1 100644 --- a/packages/kms-local/src/key-management-system.ts +++ b/packages/kms-local/src/key-management-system.ts @@ -198,6 +198,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { `invalid_arguments: Cannot sign typed data. 'domain', 'types', and 'message' must be provided`, ) } + delete(msgTypes.EIP712Domain) const wallet = new Wallet(privateKeyHex) const signature = await wallet._signTypedData(msgDomain, msgTypes, msg) diff --git a/packages/kms-web3/src/web3-key-management-system.ts b/packages/kms-web3/src/web3-key-management-system.ts index 2eac28853..52c35a8b3 100644 --- a/packages/kms-web3/src/web3-key-management-system.ts +++ b/packages/kms-web3/src/web3-key-management-system.ts @@ -50,12 +50,13 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { // keyRef should be in this format '{providerName-account} // example: 'metamask-0xf3beac30c498d9e26865f34fcaa57dbb935b0d74' - private getSignerByKeyRef(keyRef: Pick): JsonRpcSigner { + private getAccountAndSignerByKeyRef(keyRef: Pick): {account: string, signer: JsonRpcSigner } { const [ providerName, account ] = keyRef.kid.split('-') if (!this.providers[providerName]) { throw Error(`not_available: provider ${providerName}`) } - return this.providers[providerName].getSigner(account) + const signer = this.providers[providerName].getSigner(account) + return { account, signer } } async sign({ @@ -85,7 +86,7 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { * @returns a `0x` prefixed hex string representing the signed EIP712 data */ private async eth_signTypedData(keyRef: Pick, data: Uint8Array) { - let msg, msgDomain, msgTypes + let msg, msgDomain, msgTypes, msgPrimaryType const serializedData = toUtf8String(data) try { const jsonData = JSON.parse(serializedData) as Eip712Payload @@ -93,10 +94,11 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { typeof jsonData.domain === 'object' && typeof jsonData.types === 'object' ) { - const { domain, types, message } = jsonData + const { domain, types, message, primaryType } = jsonData msg = message msgDomain = domain msgTypes = types + msgPrimaryType = primaryType } else { // next check will throw since the data couldn't be parsed } @@ -112,9 +114,19 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { `invalid_arguments: Cannot sign typed data. 'domain', 'types', and 'message' must be provided`, ) } + const { signer, account } = this.getAccountAndSignerByKeyRef(keyRef) - const signature = await this.getSignerByKeyRef(keyRef) - ._signTypedData(msgDomain, msgTypes, msg) + const signature = await signer.provider.send('eth_signTypedData_v4', [ + account, + { + domain: msgDomain, + types: msgTypes, + primaryType: msgPrimaryType, + message: msg + } + ]) + // ._signTypedData(msgDomain, msgTypes, msg) + return signature } @@ -122,8 +134,8 @@ export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { * @returns a `0x` prefixed hex string representing the signed message */ private async eth_signMessage(keyRef: Pick, rawMessageBytes: Uint8Array) { - const signature = await this.getSignerByKeyRef(keyRef) - .signMessage(rawMessageBytes) + const { signer } = this.getAccountAndSignerByKeyRef(keyRef) + const signature = await signer.signMessage(rawMessageBytes) // HEX encoded string, 0x prefixed return signature } From 7d47d07a39c700745dac69f9e10db1a419281636 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 16:41:41 +0300 Subject: [PATCH 10/13] test(test-react-app): fixed setup --- packages/test-react-app/src/veramo/setup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/test-react-app/src/veramo/setup.ts b/packages/test-react-app/src/veramo/setup.ts index 9fe935933..c80657977 100644 --- a/packages/test-react-app/src/veramo/setup.ts +++ b/packages/test-react-app/src/veramo/setup.ts @@ -15,7 +15,7 @@ import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { MessageHandler } from '@veramo/message-handler' -import { KeyManager, MemoryKeyStore } from '@veramo/key-manager' +import { KeyManager } from '@veramo/key-manager' import { DIDManager } from '@veramo/did-manager' import { JwtMessageHandler } from '@veramo/did-jwt' import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '@veramo/credential-w3c' @@ -72,7 +72,7 @@ export function getAgent(options?: IAgentOptions): TAgent { store: new KeyStoreJson(memoryJsonStore), kms: { local: new KeyManagementSystem(new PrivateKeyStoreJson(memoryJsonStore, new SecretBox(DB_SECRET_KEY))), - web3: new Web3KeyManagementSystem({}, new MemoryKeyStore()) + web3: new Web3KeyManagementSystem({}) }, }), new DIDManager({ From 0d5e987638ce5041a9de93618546854e7a580ed0 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 16:44:20 +0300 Subject: [PATCH 11/13] test: remove unused imports --- __tests__/localAgent.test.ts | 2 +- __tests__/localJsonStoreAgent.test.ts | 2 +- __tests__/restAgent.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 28632c914..853534dd4 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -17,7 +17,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' +import { KeyManager } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index 84fab96e6..a56081898 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -17,7 +17,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' +import { KeyManager } from '../packages/key-manager/src' import { DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 81dbfc1f9..cca6107f1 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -20,7 +20,7 @@ import { TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' -import { KeyManager, MemoryKeyStore } from '../packages/key-manager/src' +import { KeyManager } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' From fa89dce874139f8147755f4c70c2b4339bdcba5e Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 16:52:19 +0300 Subject: [PATCH 12/13] fix: move Eip712Payload to key-manager --- packages/key-manager/package.json | 1 + packages/key-manager/src/index.ts | 1 + packages/key-manager/src/types.ts | 8 ++++++++ packages/kms-local/package.json | 1 - packages/kms-local/src/key-management-system.ts | 10 +--------- packages/kms-web3/package.json | 1 - packages/kms-web3/src/web3-key-management-system.ts | 10 +--------- 7 files changed, 12 insertions(+), 20 deletions(-) create mode 100644 packages/key-manager/src/types.ts diff --git a/packages/key-manager/package.json b/packages/key-manager/package.json index eaade7ec9..909889804 100644 --- a/packages/key-manager/package.json +++ b/packages/key-manager/package.json @@ -19,6 +19,7 @@ "uuid": "^8.3.2" }, "devDependencies": { + "@ethersproject/abstract-signer": "^5.6.2", "typescript": "4.7.3" }, "files": [ diff --git a/packages/key-manager/src/index.ts b/packages/key-manager/src/index.ts index 451db007f..7da73e3ba 100644 --- a/packages/key-manager/src/index.ts +++ b/packages/key-manager/src/index.ts @@ -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' \ No newline at end of file diff --git a/packages/key-manager/src/types.ts b/packages/key-manager/src/types.ts new file mode 100644 index 000000000..c28037c04 --- /dev/null +++ b/packages/key-manager/src/types.ts @@ -0,0 +1,8 @@ +import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' + +export type Eip712Payload = { + domain: TypedDataDomain + types: Record + primaryType: string + message: Record +} \ No newline at end of file diff --git a/packages/kms-local/package.json b/packages/kms-local/package.json index 483c27d12..f37d73a27 100644 --- a/packages/kms-local/package.json +++ b/packages/kms-local/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/abstract-signer": "^5.6.2", "@ethersproject/bytes": "^5.6.1", "@ethersproject/random": "^5.6.1", "@ethersproject/signing-key": "^5.6.2", diff --git a/packages/kms-local/src/key-management-system.ts b/packages/kms-local/src/key-management-system.ts index 6e0e60cf1..511031a5b 100644 --- a/packages/kms-local/src/key-management-system.ts +++ b/packages/kms-local/src/key-management-system.ts @@ -1,5 +1,5 @@ import { TKeyType, IKey, ManagedKeyInfo, MinimalImportableKey, RequireOnly } from '@veramo/core' -import { AbstractKeyManagementSystem, AbstractPrivateKeyStore } from '@veramo/key-manager' +import { AbstractKeyManagementSystem, AbstractPrivateKeyStore, Eip712Payload } from '@veramo/key-manager' import { ManagedPrivateKey } from '@veramo/key-manager' import { EdDSASigner, ES256KSigner } from 'did-jwt' @@ -14,7 +14,6 @@ import { generateKeyPairFromSeed as generateEncryptionKeyPairFromSeed, sharedKey, } from '@stablelib/x25519' -import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' import { TransactionRequest } from '@ethersproject/abstract-provider' import { toUtf8String } from '@ethersproject/strings' import { parse } from '@ethersproject/transactions' @@ -313,10 +312,3 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { return key as ManagedKeyInfo } } - -type Eip712Payload = { - domain: TypedDataDomain - types: Record - primaryType: string - message: Record -} diff --git a/packages/kms-web3/package.json b/packages/kms-web3/package.json index 97a70a852..9aee45527 100644 --- a/packages/kms-web3/package.json +++ b/packages/kms-web3/package.json @@ -8,7 +8,6 @@ "build": "tsc" }, "dependencies": { - "@ethersproject/abstract-signer": "5.6.2", "@ethersproject/providers": "^5.6.8", "@ethersproject/strings": "5.6.1", "@ethersproject/transactions": "5.6.2", diff --git a/packages/kms-web3/src/web3-key-management-system.ts b/packages/kms-web3/src/web3-key-management-system.ts index 52c35a8b3..4f43c6ee2 100644 --- a/packages/kms-web3/src/web3-key-management-system.ts +++ b/packages/kms-web3/src/web3-key-management-system.ts @@ -5,16 +5,8 @@ import { ManagedKeyInfo, MinimalImportableKey, } from '@veramo/core' -import { AbstractKeyManagementSystem } from '@veramo/key-manager' +import { AbstractKeyManagementSystem, Eip712Payload } from '@veramo/key-manager' import { toUtf8String } from '@ethersproject/strings' -import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' - -type Eip712Payload = { - domain: TypedDataDomain - types: Record - primaryType: string - message: Record -} export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { constructor(private providers: Record) { From 0d6e7017b6b4c83f4ec2a286dec41284450081d7 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Mon, 13 Jun 2022 17:04:34 +0300 Subject: [PATCH 13/13] docs(kms-web3): constructor params --- packages/kms-web3/src/web3-key-management-system.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/kms-web3/src/web3-key-management-system.ts b/packages/kms-web3/src/web3-key-management-system.ts index 4f43c6ee2..1524f4219 100644 --- a/packages/kms-web3/src/web3-key-management-system.ts +++ b/packages/kms-web3/src/web3-key-management-system.ts @@ -9,6 +9,10 @@ import { AbstractKeyManagementSystem, Eip712Payload } from '@veramo/key-manager' import { toUtf8String } from '@ethersproject/strings' export class Web3KeyManagementSystem extends AbstractKeyManagementSystem { + /** + * + * @param providers - the key can be any unique name. Example { metamask: metamaskProvider, walletConnect: walletConnectProvider } + */ constructor(private providers: Record) { super() }