From 88f1da9a39f6d249fbed301e2d77ea3cee167e33 Mon Sep 17 00:00:00 2001 From: Lauritz Leifermann Date: Tue, 18 Oct 2022 15:37:07 +0200 Subject: [PATCH] feat: add support for did:ethr signed/meta transactions (#1031) --- __tests__/localAgent.test.ts | 2 + __tests__/shared/didManager.ts | 1 + __tests__/shared/ethrDidFlowSigned.ts | 150 +++++++++++++ __tests__/shared/keyManager.ts | 140 +++++++----- packages/did-provider-ethr/package.json | 2 +- .../src/ethr-did-provider.ts | 206 +++++++++++++++--- .../kms-local/src/key-management-system.ts | 11 +- yarn.lock | 33 ++- 8 files changed, 451 insertions(+), 94 deletions(-) create mode 100644 __tests__/shared/ethrDidFlowSigned.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 77d9638fd..f77772efc 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -84,6 +84,7 @@ import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow' import utils from './shared/utils' import web3 from './shared/web3' import credentialStatus from './shared/credentialStatus' +import ethrDidFlowSigned from "./shared/ethrDidFlowSigned"; jest.setTimeout(60000) @@ -278,4 +279,5 @@ describe('Local integration tests', () => { web3(testContext) didCommWithEthrDidFlow(testContext) credentialStatus(testContext) + ethrDidFlowSigned(testContext) }) diff --git a/__tests__/shared/didManager.ts b/__tests__/shared/didManager.ts index 1c3da7c4d..7398ab1b6 100644 --- a/__tests__/shared/didManager.ts +++ b/__tests__/shared/didManager.ts @@ -309,6 +309,7 @@ export default (testContext: { 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage', + 'eth_rawSign', ], }, publicKeyHex: diff --git a/__tests__/shared/ethrDidFlowSigned.ts b/__tests__/shared/ethrDidFlowSigned.ts new file mode 100644 index 000000000..6e5b87ddf --- /dev/null +++ b/__tests__/shared/ethrDidFlowSigned.ts @@ -0,0 +1,150 @@ +// noinspection ES6PreferShortImport + +import { + IAgentOptions, + IDIDManager, + IIdentifier, + IKeyManager, + IMessageHandler, + IResolver, + TAgent, +} from '../../packages/core/src' +import {IDIDComm} from '../../packages/did-comm/src' +// @ts-ignore +import express from 'express' +import {Server} from 'http' + +type ConfiguredAgent = TAgent + +export async function sleep(milliseconds: number): Promise { + return new Promise((resolve) => setTimeout(resolve, milliseconds)) +} + +export default (testContext: { + getAgent: () => ConfiguredAgent + setup: (options?: IAgentOptions) => Promise + tearDown: () => Promise +}) => { + describe('did ethr controller signed interactions', () => { + let agent: ConfiguredAgent + + let alice: IIdentifier + let bob: IIdentifier + + let didCommEndpointServer: Server + let listeningPort = Math.round(Math.random() * 32000 + 2048) + + beforeEach(async () => { + await testContext.setup() + agent = testContext.getAgent() + + alice = await agent.didManagerImport({ + controllerKeyId: 'alice-controller-key', + did: 'did:ethr:ganache:0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + provider: 'did:ethr:ganache', + alias: 'alice-did-ethr', + keys: [ + { + privateKeyHex: '0000000000000000000000000000000000000000000000000000000000000001', + kms: 'local', + type: 'Secp256k1', + kid: 'alice-controller-key', + }, + ], + }) + + bob = await agent.didManagerImport({ + controllerKeyId: 'bob-controller-key', + did: 'did:ethr:ganache:0x02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5', + provider: 'did:ethr:ganache', + alias: 'bob-did-ethr', + keys: [ + { + privateKeyHex: '0000000000000000000000000000000000000000000000000000000000000002', + kms: 'local', + type: 'Secp256k1', + kid: 'bob-controller-key', + }, + ], + }) + }) + + afterAll(async () => { + await testContext.tearDown() + }) + + it('should add dummy service to alice did with bob sending the tx', async () => { + const result = await agent.didManagerAddService({ + did: alice.did, + service: { + id: 'localhost-useless-endpoint', + type: 'DIDComm', + serviceEndpoint: `http://localhost:${listeningPort}/foobar`, + description: 'this endpoint will be removed', + }, options: { + metaIdentifierKeyId: bob.controllerKeyId + } + }) + + const resolution = await agent.resolveDid({ didUrl: alice.did }) + + expect(resolution.didDocument).toEqual(expect.objectContaining({ + service: expect.arrayContaining([ + expect.objectContaining({ + serviceEndpoint: `http://localhost:${listeningPort}/foobar` + }) + ]) + })) + }) + + it('should remove dummy service to alice did with bob sending the tx', async () => { + await agent.didManagerAddService({ + did: alice.did, + service: { + id: 'localhost-useless-endpoint', + type: 'DIDComm', + serviceEndpoint: `http://localhost:${listeningPort}/foobar`, + description: 'this endpoint will be removed', + }, options: { + metaIdentifierKeyId: bob.controllerKeyId + } + }) + + await agent.didManagerRemoveService({ + did: alice.did, + id: "localhost-useless-endpoint", + options: { + metaIdentifierKeyId: bob.controllerKeyId + } + }) + + const resolution = await agent.resolveDid({ didUrl: alice.did }) + expect(resolution?.didDocument?.service?.[0].serviceEndpoint).toBeFalsy() + }) + + it('should add verification key to alice did with bob sending the tx', async () => { + const keyToAdd = await agent.keyManagerCreate({type: 'Secp256k1', kms: 'local'}) + + await agent.didManagerAddKey({ + did: alice.did, + key: keyToAdd, + options: { + metaIdentifierKeyId: bob.controllerKeyId + } + }) + + // Give ganache some time to emit the event from the contract + await sleep(1000) + + const didAfterchange = await agent.resolveDid({ didUrl: alice.did }) + expect(didAfterchange).toBeTruthy() + expect(didAfterchange.didDocument).toEqual(expect.objectContaining({ + verificationMethod: expect.arrayContaining([ + expect.objectContaining({ + publicKeyHex: keyToAdd.publicKeyHex + }) + ]) + })) + }) + }) +} diff --git a/__tests__/shared/keyManager.ts b/__tests__/shared/keyManager.ts index 1be85b60c..42d9ae6ad 100644 --- a/__tests__/shared/keyManager.ts +++ b/__tests__/shared/keyManager.ts @@ -107,7 +107,14 @@ export default (testContext: { expect(key.meta).toEqual({ foo: 'bar', bar: 'baz', - algorithms: ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage'], + algorithms: [ + 'ES256K', + 'ES256K-R', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signMessage', + 'eth_rawSign', + ], }) }) @@ -170,7 +177,14 @@ export default (testContext: { publicKeyHex: '04dd467afb12bdb797303e7f3f0c8cd0ba80d518dc4e339e0e2eb8f2d99a9415cac537854a30d31a854b7af0b4fcb54c3954047390fa9500d3cc2e15a3e09017bb', meta: { - algorithms: ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage'], + algorithms: [ + 'ES256K', + 'ES256K-R', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signMessage', + 'eth_rawSign', + ], foo: 'bar', }, } @@ -450,7 +464,6 @@ export default (testContext: { ], }, types: { - Mail: [ { name: 'from', type: 'Person' }, { name: 'to', type: 'Person[]' }, @@ -461,7 +474,7 @@ export default (testContext: { { name: 'wallets', type: 'address[]' }, ], }, - }; + } const identifier = await agent.didManagerCreate({ kms: 'local' }) @@ -471,7 +484,7 @@ export default (testContext: { const signature = await agent.keyManagerSign({ data: JSON.stringify(msgParams), keyRef: extendedKey.kid, - algorithm: 'eth_signTypedData' + algorithm: 'eth_signTypedData', }) const address = extendedKey.meta.ethereumAddress @@ -493,75 +506,73 @@ export default (testContext: { } //@ts-ignore - const recovered = recoverTypedSignature({data, signature: signature, version: SignTypedDataVersion.V4}) + const recovered = recoverTypedSignature({ + data: data as any, + signature: signature, + version: SignTypedDataVersion.V4, + }) expect(address.toLowerCase()).toEqual(recovered) }) it('should sign credential with eth_signTypedData', async () => { const msgParams = { - "domain": { - "chainId": 4, - "name": "VerifiableCredential", - "version": "1" + domain: { + chainId: 4, + name: 'VerifiableCredential', + version: '1', }, - "types": { - "CredentialSubject": [ + types: { + CredentialSubject: [ { - "name": "id", - "type": "string" + name: 'id', + type: 'string', }, { - "name": "you", - "type": "string" - } + name: 'you', + type: 'string', + }, ], - "Issuer": [ + Issuer: [ { - "name": "id", - "type": "string" - } + name: 'id', + type: 'string', + }, ], - "VerifiableCredential": [ + VerifiableCredential: [ { - "name": "@context", - "type": "string[]" + name: '@context', + type: 'string[]', }, { - "name": "credentialSubject", - "type": "CredentialSubject" + name: 'credentialSubject', + type: 'CredentialSubject', }, { - "name": "issuanceDate", - "type": "string" + name: 'issuanceDate', + type: 'string', }, { - "name": "issuer", - "type": "Issuer" + name: 'issuer', + type: 'Issuer', }, { - "name": "type", - "type": "string[]" - } - ] + name: 'type', + type: 'string[]', + }, + ], }, - "message": { - "issuer": { - "id": "did:fake:123" + message: { + issuer: { + id: 'did:fake:123', }, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://example.com/1/2/3" - ], - "type": [ - "VerifiableCredential", - "Custom" - ], - "issuanceDate": "2022-05-31T14:02:06.109Z", - "credentialSubject": { - "id": "did:web:example.com", - "you": "Rock" - } - } + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], + type: ['VerifiableCredential', 'Custom'], + issuanceDate: '2022-05-31T14:02:06.109Z', + credentialSubject: { + id: 'did:web:example.com', + you: 'Rock', + }, + }, } const identifier = await agent.didManagerCreate({ kms: 'local' }) @@ -572,7 +583,7 @@ export default (testContext: { const signature = await agent.keyManagerSign({ data: JSON.stringify(msgParams), keyRef: extendedKey.kid, - algorithm: 'eth_signTypedData' + algorithm: 'eth_signTypedData', }) const address = extendedKey.meta.ethereumAddress @@ -592,13 +603,34 @@ export default (testContext: { }, } - const args = {data, signature: signature, version: SignTypedDataVersion.V4} + const args = { data, signature: signature, version: SignTypedDataVersion.V4 } //@ts-ignore const recovered = recoverTypedSignature(args) expect(address.toLowerCase()).toEqual(recovered) }) - }) + it('should sign keccak hash with eth_rawSign', async () => { + const importedKey = { + kid: 'imported_raw', + kms: 'local', + type: 'Secp256k1', + publicKeyHex: + '04155ee0cbefeecd80de63a62b4ed8f0f97ac22a58f76a265903b9acab79bf018c7037e2bd897812170c92a4c978d6a10481491a37299d74c4bd412a111a4ac875', + privateKeyHex: '31d1ec15ff8110442012fef0d1af918c0e09b2e2ab821bba52ecc85f8655ec63', + } + + const key = await agent.keyManagerImport(importedKey) + const signature = await agent.keyManagerSign({ + algorithm: 'eth_rawSign', + data: '9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658', + encoding: 'hex', + keyRef: key.kid, + }) + expect(signature).toEqual( + '0x9d9e6c705cfa5f348de0c5174e70234c3b372d6bfca05fd11ffc5fe46eab996a3e4780d403b611395fd8548f2b101c3542c5e43848e486ea238da18fe0ffe6d0', + ) + }) + }) } diff --git a/packages/did-provider-ethr/package.json b/packages/did-provider-ethr/package.json index 2c29618ba..0d17a71d0 100644 --- a/packages/did-provider-ethr/package.json +++ b/packages/did-provider-ethr/package.json @@ -19,7 +19,7 @@ "@veramo/core": "^4.0.0", "@veramo/did-manager": "^4.0.0", "debug": "^4.3.3", - "ethr-did": "^2.2.4" + "ethr-did": "^2.3.3" }, "devDependencies": { "@types/debug": "4.1.7", diff --git a/packages/did-provider-ethr/src/ethr-did-provider.ts b/packages/did-provider-ethr/src/ethr-did-provider.ts index af9ba751f..0ea781e94 100644 --- a/packages/did-provider-ethr/src/ethr-did-provider.ts +++ b/packages/did-provider-ethr/src/ethr-did-provider.ts @@ -8,6 +8,7 @@ import { computeAddress } from '@ethersproject/transactions' import { KmsEthereumSigner } from './kms-eth-signer' import Debug from 'debug' import { EthrDID } from 'ethr-did' +import { splitSignature } from '@ethersproject/bytes' const debug = Debug('veramo:did-provider-ethr') @@ -49,6 +50,7 @@ export interface CreateDidEthrOptions { export interface TransactionOptions extends TransactionRequest { ttl?: number encoding?: string + metaIdentifierKeyId?: string } /** @@ -210,7 +212,10 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { return identifier } - async updateIdentifier(args: { did: string; kms?: string | undefined; alias?: string | undefined; options?: any }, context: IAgentContext): Promise { + async updateIdentifier( + args: { did: string; kms?: string | undefined; alias?: string | undefined; options?: any }, + context: IAgentContext, + ): Promise { throw new Error('EthrDIDProvider updateIdentifier not supported yet.') } @@ -239,7 +244,11 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { return network } - private async getEthrDidController(identifier: IIdentifier, context: IRequiredContext): Promise { + private async getEthrDidController( + identifier: IIdentifier, + context: IRequiredContext, + metaIdentifierKeyId?: string, + ): Promise { if (identifier.controllerKeyId == null) { throw new Error('invalid_argument: identifier does not list a `controllerKeyId`') } @@ -256,6 +265,23 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { throw new Error(`invalid_argument: cannot find network for ${identifier.did}`) } + if (metaIdentifierKeyId) { + const metaControllerKey = await context.agent.keyManagerGet({ kid: metaIdentifierKeyId }) + if (typeof metaControllerKey === 'undefined') { + throw new Error('invalid_argument: identifier.controllerKeyId is not managed by this agent') + } + + // Identity owner signs payload but metaIdentifier send the tx (meta transaction; signed methods) + return new EthrDID({ + identifier: identifier.did, + provider: network.provider, + chainNameOrId: network.name || network.chainId, + rpcUrl: network.rpcUrl, + registry: network.registry, + txSigner: new KmsEthereumSigner(metaControllerKey, context, network?.provider), + }) + } + if (controllerKey.meta?.algorithms?.includes('eth_signTransaction')) { return new EthrDID({ identifier: identifier.did, @@ -286,15 +312,36 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { const encoding = key.type === 'X25519' ? 'base58' : options?.encoding || 'hex' const attrName = `did/pub/${key.type}/${usg}/${encoding}` const attrValue = '0x' + key.publicKeyHex - const ttl = options?.ttl || this.ttl + const ttl = options?.ttl || this.ttl || 86400 const gasLimit = options?.gasLimit || this.gas || DEFAULT_GAS_LIMIT - debug('ethrDid.setAttribute %o', { attrName, attrValue, ttl, gasLimit }) - const txHash = await ethrDid.setAttribute(attrName, attrValue, ttl, undefined, { - ...options, - gasLimit, - }) - debug(`ethrDid.addKey tx = ${txHash}`) - return txHash + if (options?.metaIdentifierKeyId) { + const metaHash = await ethrDid.createSetAttributeHash(attrName, attrValue, ttl) + const canonicalSignature = await EthrDIDProvider.createMetaSignature(context, identifier, metaHash) + + const metaEthrDid = await this.getEthrDidController(identifier, context, options.metaIdentifierKeyId!) + debug('ethrDid.addKeySigned %o', { attrName, attrValue, ttl, gasLimit }) + delete options.metaIdentifierKeyId + const txHash = await metaEthrDid.setAttributeSigned( + attrName, + attrValue, + ttl, + { sigV: canonicalSignature.v, sigR: canonicalSignature.r, sigS: canonicalSignature.s }, + { + ...options, + gasLimit, + }, + ) + debug(`ethrDid.addKeySigned tx = ${txHash}`) + return txHash + } else { + debug('ethrDid.setAttribute %o', { attrName, attrValue, ttl, gasLimit }) + const txHash = await ethrDid.setAttribute(attrName, attrValue, ttl, undefined, { + ...options, + gasLimit, + }) + debug(`ethrDid.addKey tx = ${txHash}`) + return txHash + } } async addService( @@ -308,18 +355,42 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { const ethrDid = await this.getEthrDidController(identifier, context) const attrName = 'did/svc/' + service.type - const attrValue = (typeof service.serviceEndpoint === 'string') ? service.serviceEndpoint : JSON.stringify(service.serviceEndpoint) - const ttl = options?.ttl || this.ttl + const attrValue = + typeof service.serviceEndpoint === 'string' + ? service.serviceEndpoint + : JSON.stringify(service.serviceEndpoint) + const ttl = options?.ttl || this.ttl || 86400 const gasLimit = options?.gasLimit || this.gas || DEFAULT_GAS_LIMIT debug('ethrDid.setAttribute %o', { attrName, attrValue, ttl, gasLimit }) - const txHash = await ethrDid.setAttribute(attrName, attrValue, ttl, undefined, { - ...options, - gasLimit, - }) - debug(`ethrDid.addService tx = ${txHash}`) - return txHash + if (options?.metaIdentifierKeyId) { + const metaHash = await ethrDid.createSetAttributeHash(attrName, attrValue, ttl) + const canonicalSignature = await EthrDIDProvider.createMetaSignature(context, identifier, metaHash) + + const metaEthrDid = await this.getEthrDidController(identifier, context, options.metaIdentifierKeyId!) + debug('ethrDid.addServiceSigned %o', { attrName, attrValue, ttl, gasLimit }) + delete options.metaIdentifierKeyId + const txHash = await metaEthrDid.setAttributeSigned( + attrName, + attrValue, + ttl, + { sigV: canonicalSignature.v, sigR: canonicalSignature.r, sigS: canonicalSignature.s }, + { + ...options, + gasLimit, + }, + ) + debug(`ethrDid.addServiceSigned tx = ${txHash}`) + return txHash + } else { + const txHash = await ethrDid.setAttribute(attrName, attrValue, ttl, undefined, { + ...options, + gasLimit, + }) + debug(`ethrDid.addService tx = ${txHash}`) + return txHash + } } async removeKey( @@ -337,13 +408,37 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { const attrValue = '0x' + key.publicKeyHex const gasLimit = args.options?.gasLimit || this.gas || DEFAULT_GAS_LIMIT - debug('ethrDid.revokeAttribute', { attrName, attrValue, gasLimit }) - const txHash = await ethrDid.revokeAttribute(attrName, attrValue, undefined, { - ...args.options, - gasLimit, - }) - debug(`ethrDid.removeKey tx = ${txHash}`) - return txHash + if (args.options?.metaIdentifierKeyId) { + const metaHash = await ethrDid.createRevokeAttributeHash(attrName, attrValue) + const canonicalSignature = await EthrDIDProvider.createMetaSignature(context, args.identifier, metaHash) + + const metaEthrDid = await this.getEthrDidController( + args.identifier, + context, + args.options.metaIdentifierKeyId!, + ) + debug('ethrDid.revokeAttributeSigned %o', { attrName, attrValue, gasLimit }) + delete args.options.metaIdentifierKeyId + const txHash = await metaEthrDid.revokeAttributeSigned( + attrName, + attrValue, + { sigV: canonicalSignature.v, sigR: canonicalSignature.r, sigS: canonicalSignature.s }, + { + ...args.options, + gasLimit, + }, + ) + debug(`ethrDid.removeKeySigned tx = ${txHash}`) + return txHash + } else { + debug('ethrDid.revokeAttribute', { attrName, attrValue, gasLimit }) + const txHash = await ethrDid.revokeAttribute(attrName, attrValue, undefined, { + ...args.options, + gasLimit, + }) + debug(`ethrDid.removeKey tx = ${txHash}`) + return txHash + } } async removeService( @@ -356,16 +451,43 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { if (!service) throw Error('Service not found') const attrName = 'did/svc/' + service.type - const attrValue = (typeof service.serviceEndpoint === 'string') ? service.serviceEndpoint : JSON.stringify(service.serviceEndpoint) + const attrValue = + typeof service.serviceEndpoint === 'string' + ? service.serviceEndpoint + : JSON.stringify(service.serviceEndpoint) const gasLimit = args.options?.gasLimit || this.gas || DEFAULT_GAS_LIMIT - debug('ethrDid.revokeAttribute', { attrName, attrValue, gasLimit }) - const txHash = await ethrDid.revokeAttribute(attrName, attrValue, undefined, { - ...args.options, - gasLimit, - }) - debug(`ethrDid.removeService tx = ${txHash}`) - return txHash + if (args.options?.metaIdentifierKeyId) { + const metaHash = await ethrDid.createRevokeAttributeHash(attrName, attrValue) + const canonicalSignature = await EthrDIDProvider.createMetaSignature(context, args.identifier, metaHash) + + const metaEthrDid = await this.getEthrDidController( + args.identifier, + context, + args.options.metaIdentifierKeyId!, + ) + debug('ethrDid.revokeAttributeSigned %o', { attrName, attrValue, gasLimit }) + delete args.options.metaIdentifierKeyId + const txHash = await metaEthrDid.revokeAttributeSigned( + attrName, + attrValue, + { sigV: canonicalSignature.v, sigR: canonicalSignature.r, sigS: canonicalSignature.s }, + { + ...args.options, + gasLimit, + }, + ) + debug(`ethrDid.removeServiceSigned tx = ${txHash}`) + return txHash + } else { + debug('ethrDid.revokeAttribute', { attrName, attrValue, gasLimit }) + const txHash = await ethrDid.revokeAttribute(attrName, attrValue, undefined, { + ...args.options, + gasLimit, + }) + debug(`ethrDid.removeService tx = ${txHash}`) + return txHash + } } /** @@ -384,4 +506,22 @@ export class EthrDIDProvider extends AbstractIdentifierProvider { } return true } + + private static async createMetaSignature( + context: IRequiredContext, + identifier: IIdentifier, + metaHash: string, + ) { + const controllerKey = await context.agent.keyManagerGet({ kid: identifier.controllerKeyId! }) + if (typeof controllerKey === 'undefined') { + throw new Error('invalid_argument: identifier.controllerKeyId is not managed by this agent') + } + const signature = await context.agent.keyManagerSign({ + keyRef: controllerKey.kid, + data: metaHash, + algorithm: 'eth_rawSign', + encoding: 'hex', + }) + return splitSignature(signature) + } } diff --git a/packages/kms-local/src/key-management-system.ts b/packages/kms-local/src/key-management-system.ts index 511031a5b..c2dc9d1b2 100644 --- a/packages/kms-local/src/key-management-system.ts +++ b/packages/kms-local/src/key-management-system.ts @@ -130,6 +130,8 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { return await this.eth_signMessage(managedKey.privateKeyHex, data) } else if (['eth_signTypedData', 'EthereumEip712Signature2021'].includes(algorithm)) { return await this.eth_signTypedData(managedKey.privateKeyHex, data) + } else if (['eth_rawSign'].includes(algorithm)) { + return this.eth_rawSign(managedKey.privateKeyHex, data); } } throw Error(`not_supported: Cannot sign ${algorithm} using key of type ${managedKey.type}`) @@ -235,6 +237,13 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { return signedRawTransaction } + /** + * @returns a `0x` prefixed hex string representing the signed digest in compact format + */ + private eth_rawSign(managedKey: string, data: Uint8Array) { + return new SigningKey("0x" + managedKey).signDigest(data).compact + } + /** * @returns a base64url encoded signature for the `EdDSA` alg */ @@ -287,7 +296,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { kid: args.alias || publicKeyHex, publicKeyHex, meta: { - algorithms: ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage'], + algorithms: ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage', 'eth_rawSign'], }, } break diff --git a/yarn.lock b/yarn.lock index 96a1d311a..4189480f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8147,6 +8147,11 @@ did-resolver@4.0.0, did-resolver@^4.0.0: resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.0.tgz#fc8f657b4cd7f44c2921051fb046599fbe7d4b31" integrity sha512-/roxrDr9EnAmLs+s9T+8+gcpilMo+IkeytcsGO7dcxvTmVJ+0Rt60HtV8o0UXHhGBo0Q+paMH/0ffXz1rqGFYg== +did-resolver@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/did-resolver/-/did-resolver-4.0.1.tgz#11bb3f19ed1c8f53f4af4702912fa9f7852fc305" + integrity sha512-eHs2VLKhcANmh08S87PKvOauIAmSOd7nb7AlhNxcvOyDAIGQY1UfbiqI1VOW5IDKvOO6aEWY+5edOt1qrCp1Eg== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -8893,10 +8898,28 @@ ethr-did-resolver@^6.1.0: did-resolver "^4.0.0" ethr-did-registry "^1.0.0" -ethr-did@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/ethr-did/-/ethr-did-2.2.4.tgz#ed9b7b7ad6221d122dda66ef792378c4c26b48ed" - integrity sha512-Vh0TSMccvscN/FQim8XS+U9gHhjVLmj8sdki+mlpWygr2REriADuso0mhQrIzNlcDpdsuUOJEcvXJGAJekVSDw== +ethr-did-resolver@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ethr-did-resolver/-/ethr-did-resolver-7.0.1.tgz#3df8fb9b81886776afda5b54eeb7c62038edd393" + integrity sha512-7/ifFxchMIyLRb+wOqS56Mo/UxHfHCd43T2ZTUAJNYZKMnUD25U2Sdpva/HsslfwFvmkDEK4+6GJjFRNvk2mzQ== + dependencies: + "@ethersproject/abi" "^5.6.3" + "@ethersproject/abstract-signer" "^5.6.2" + "@ethersproject/address" "^5.6.1" + "@ethersproject/basex" "^5.6.1" + "@ethersproject/bignumber" "^5.6.2" + "@ethersproject/bytes" "^5.6.1" + "@ethersproject/contracts" "^5.6.2" + "@ethersproject/keccak256" "^5.6.1" + "@ethersproject/providers" "^5.6.8" + "@ethersproject/signing-key" "^5.6.2" + "@ethersproject/transactions" "^5.6.2" + did-resolver "^4.0.1" + +ethr-did@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/ethr-did/-/ethr-did-2.3.3.tgz#5f1eacbb401d904a3761450188f06e7f81f2d6cd" + integrity sha512-Kw3aqqay4t4O8eohDzNZi+V08B/LSQ/0faUWIGGSVa7O6pvOp+wSQ2vDkPvLgoI3HRpHXVrtpvXs8yyf29wkLw== dependencies: "@ethersproject/abstract-signer" "^5.6.2" "@ethersproject/base64" "^5.6.1" @@ -8909,7 +8932,7 @@ ethr-did@^2.2.4: "@ethersproject/wallet" "^5.6.2" did-jwt "^6.3.0" did-resolver "^4.0.0" - ethr-did-resolver "^6.1.0" + ethr-did-resolver "^7.0.0" event-target-shim@^5.0.0: version "5.0.1"