From 41f2872284c0332702fa99b10e3d8ea2ce13206e Mon Sep 17 00:00:00 2001 From: dchagastelles Date: Wed, 1 Feb 2023 15:27:57 -0500 Subject: [PATCH 1/6] - minor code correction - use of namespace parameter - allow passing privateKey to import key directly on identifier creation --- .../did-provider-pkh/src/pkh-did-provider.ts | 181 ++++++++++-------- packages/did-provider-pkh/src/resolver.ts | 82 ++++---- 2 files changed, 142 insertions(+), 121 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index 69697ca78..d342fe4a5 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -1,37 +1,49 @@ -import { IIdentifier, IKey, IService, IAgentContext, IKeyManager } from '@veramo/core' -import { computeAddress } from '@ethersproject/transactions' +import { computeAddress } from '@ethersproject/transactions'; +import { + IAgentContext, + IIdentifier, + IKey, + IKeyManager, + IService, + ManagedKeyInfo, +} from '@veramo/core'; -import { AbstractIdentifierProvider } from '@veramo/did-manager' -import { computePublicKey } from '@ethersproject/signing-key' -import { BigNumber } from '@ethersproject/bignumber' +import { AbstractIdentifierProvider } from '@veramo/did-manager'; -import Debug from 'debug' -const debug = Debug('veramo:did-pkh:identifier-provider') +type IContext = IAgentContext; -type IContext = IAgentContext +const isIn = (values: readonly T[], value: any): value is T => { + return values.includes(value); +}; + +export const SECPK1_NAMESPACES = ['eip155'] as const; +export const isValidNamespace = (x: string) => isIn(SECPK1_NAMESPACES, x); /** * Options for creating a did:ethr * @beta */ - export interface CreateDidPkhEthrOptions { +export interface CreateDidPkhOptions { + namespace: string; + privateKey: string; /** * This can be hex encoded chain ID (string) or a chainId number * * If this is not specified, `1` is assumed. */ - chainId?: string | number + chainId?: string; } - /** +/** * Helper method that can computes the ethereumAddress corresponding to a Secp256k1 public key. * @param hexPublicKey A hex encoded public key, optionally prefixed with `0x` */ - export function toEthereumAddress(hexPublicKey: string): string { - const publicKey = hexPublicKey.startsWith('0x') ? hexPublicKey : '0x' + hexPublicKey - return computeAddress(publicKey) - } - +export function toEthereumAddress(hexPublicKey: string): string { + const publicKey = hexPublicKey.startsWith('0x') + ? hexPublicKey + : '0x' + hexPublicKey; + return computeAddress(publicKey); +} /** * {@link @veramo/did-manager#DIDManager} identifier provider for `did:pkh` identifiers @@ -39,97 +51,114 @@ type IContext = IAgentContext * @beta This API may change without a BREAKING CHANGE notice. */ export class PkhDIDProvider extends AbstractIdentifierProvider { - private defaultKms: string - - constructor(options: { - defaultKms: string - }) - { - super() - this.defaultKms = options.defaultKms - } - + private defaultKms: string; + private chainId: string; + constructor(options: { defaultKms: string; chainId?: string }) { + super(); + this.defaultKms = options.defaultKms; + this.chainId = options?.chainId ? options.chainId : '1'; + } async createIdentifier( - { kms, options }: { kms?: string; options?: CreateDidPkhEthrOptions }, - context: IContext, + { kms, options }: { kms?: string; options?: CreateDidPkhOptions }, + context: IContext ): Promise> { + const namespace = options?.namespace ? options.namespace : 'eip155'; - const key = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, type: 'Secp256k1' }) - const publicAddress = toEthereumAddress(key.publicKeyHex); - - const network = options?.chainId; - if (!network) { + if (!isValidNamespace(namespace)) { + console.error( + `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + ); throw new Error( - `invalid_setup: Cannot create did:pkh. There is no known configuration for network=${network}'`, - ) + `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + ); } - const identifier: Omit = { - did: 'did:pkh:eip155:' + network + ':' + publicAddress, - controllerKeyId: key.kid, - keys: [key], - services: [], + let key: ManagedKeyInfo | null; + if (options?.privateKey !== undefined){ + key = await context.agent.keyManagerImport({ + kms: kms || this.defaultKms, + type: 'Secp256k1', + privateKeyHex: options?.privateKey as string, + }); + } else { + key = await context.agent.keyManagerCreate({ + kms: kms || this.defaultKms, + type: 'Secp256k1' + }); + } + const evmAddress: string = toEthereumAddress(key.publicKeyHex); + + if (key !== null) { + const identifier: Omit = { + did: 'did:pkh:' + namespace + ':' + this.chainId + ':' + evmAddress, + controllerKeyId: key.kid, + keys: [key], + services: [], + }; + return identifier; + } else { + console.error('Could not create identifier due to some errors'); + throw new Error('Could not create identifier due to some errors'); } - debug('Created', identifier.did) - return identifier } - async updateIdentifier(args: { did: string; kms?: string | undefined; alias?: string | undefined; options?: any }, context: IAgentContext): Promise { - throw new Error('PkhDIDProvider updateIdentifier not supported yet.') + + async updateIdentifier( + args: { + did: string; + kms?: string | undefined; + alias?: string | undefined; + options?: any; + }, + context: IAgentContext + ): Promise { + throw new Error('PkhDIDProvider updateIdentifier not supported yet.'); } - async deleteIdentifier(identifier: IIdentifier, context: IContext): Promise { + async deleteIdentifier( + identifier: IIdentifier, + context: IContext + ): Promise { for (const { kid } of identifier.keys) { - await context.agent.keyManagerDelete({ kid }) + await context.agent.keyManagerDelete({ kid }); } - return true + return true; } async addKey( - { identifier, key, options }: { identifier: IIdentifier; key: IKey; options?: any }, - context: IContext, + { + identifier, + key, + options, + }: { identifier: IIdentifier; key: IKey; options?: any }, + context: IContext ): Promise { - throw Error('PkhDIDProvider addKey not supported') + throw Error('PkhDIDProvider addKey not supported'); } async addService( - { identifier, service, options }: { identifier: IIdentifier; service: IService; options?: any }, - context: IContext, + { + identifier, + service, + options, + }: { identifier: IIdentifier; service: IService; options?: any }, + context: IContext ): Promise { - throw Error('PkhDIDProvider addService not supported') + throw Error('PkhDIDProvider addService not supported'); } async removeKey( args: { identifier: IIdentifier; kid: string; options?: any }, - context: IContext, + context: IContext ): Promise { - throw Error('PkhDIDProvider removeKey not supported') + throw Error('PkhDIDProvider removeKey not supported'); } async removeService( args: { identifier: IIdentifier; id: string; options?: any }, - context: IContext, + context: IContext ): Promise { - throw Error('PkhDIDProvider removeService not supported') + throw Error('PkhDIDProvider removeService not supported'); } - - // private getNetworkFor(networkSpecifier: string | number | undefined): EthrNetworkConfiguration | undefined { - // let networkNameOrId: string | number = networkSpecifier || 'mainnet' - // if ( - // typeof networkNameOrId === 'string' && - // (networkNameOrId.startsWith('0x') || parseInt(networkNameOrId) > 0) - // ) { - // networkNameOrId = BigNumber.from(networkNameOrId).toNumber() - // } - // let network = this.networks?.find( - // (n) => n.chainId === networkNameOrId || n.name === networkNameOrId || n.description === networkNameOrId, - // ) - // if (!network && !networkSpecifier && this.networks?.length === 1) { - // network = this.networks[0] - // } - // return network - // } - - } diff --git a/packages/did-provider-pkh/src/resolver.ts b/packages/did-provider-pkh/src/resolver.ts index 9ae477a17..9dca66eca 100644 --- a/packages/did-provider-pkh/src/resolver.ts +++ b/packages/did-provider-pkh/src/resolver.ts @@ -1,21 +1,20 @@ -import { AccountId, ChainIdParams } from 'caip' +import { AccountId, ChainIdParams } from 'caip'; import type { - DIDResolutionResult, DIDResolutionOptions, - ResolverRegistry, + DIDResolutionResult, ParsedDID, - Resolvable -} from 'did-resolver' - -const DID_LD_JSON = 'application/did+ld+json' -const DID_JSON = 'application/did+json' -const SECPK1_NAMESPACES = ['eip155', 'bip122'] -const TZ_NAMESPACE = 'tezos' + Resolvable, + ResolverRegistry, +} from 'did-resolver'; +import { isValidNamespace, SECPK1_NAMESPACES } from './pkh-did-provider'; +const DID_LD_JSON = 'application/did+ld+json'; +const DID_JSON = 'application/did+json'; -function toDidDoc(did: string, accountId: string): any { - const { namespace } = AccountId.parse(accountId).chainId as ChainIdParams - const vmId = did + '#blockchainAccountId' +function toDidDoc(did: string, blockchainAccountId: string): any { + const { namespace } = AccountId.parse(blockchainAccountId) + .chainId as ChainIdParams; + const vmId = did + '#blockchainAccountId'; const doc = { '@context': [ 'https://www.w3.org/ns/did/v1', @@ -23,7 +22,8 @@ function toDidDoc(did: string, accountId: string): any { blockchainAccountId: 'https://w3id.org/security#blockchainAccountId', EcdsaSecp256k1RecoveryMethod2020: 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020', - Ed25519VerificationKey2018: 'https://w3id.org/security#Ed25519VerificationKey2018', + Ed25519VerificationKey2018: + 'https://w3id.org/security#Ed25519VerificationKey2018', }, ], id: did, @@ -32,29 +32,21 @@ function toDidDoc(did: string, accountId: string): any { id: vmId, type: 'EcdsaSecp256k1RecoveryMethod2020', controller: did, - blockchainAccountId: accountId, + blockchainAccountId, }, ], authentication: [vmId], assertionMethod: [vmId], + }; + if (!isValidNamespace(namespace)) { + console.error( + `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + ); + throw new Error( + `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + ); } - if (SECPK1_NAMESPACES.includes(namespace)) { - // nothing to do here - } else if (namespace === TZ_NAMESPACE) { - (doc['@context'][1] as any).TezosMethod2021 = 'https://w3id.org/security#TezosMethod2021' - const tzId = did + '#TezosMethod2021' - doc.verificationMethod.push({ - id: tzId, - type: 'TezosMethod2021', - controller: did, - blockchainAccountId: accountId, - }) - doc.authentication.push(tzId) - doc.assertionMethod.push(tzId) - } else { - throw new Error(`chain namespace not supported ${namespace}`) - } - return doc + return doc; } export function getResolver(): ResolverRegistry { @@ -65,28 +57,28 @@ export function getResolver(): ResolverRegistry { r: Resolvable, options: DIDResolutionOptions ): Promise => { - const contentType = options.accept || DID_JSON + const contentType = options.accept || DID_JSON; const response: DIDResolutionResult = { didResolutionMetadata: { contentType }, didDocument: null, didDocumentMetadata: {}, - } + }; try { - const doc = toDidDoc(did, parsed.id) + const doc = toDidDoc(did, parsed.id); if (contentType === DID_LD_JSON) { - response.didDocument = doc + response.didDocument = doc; } else if (contentType === DID_JSON) { - delete doc['@context'] - response.didDocument = doc + delete doc['@context']; + response.didDocument = doc; } else { - delete response.didResolutionMetadata.contentType - response.didResolutionMetadata.error = 'representationNotSupported' + delete response.didResolutionMetadata.contentType; + response.didResolutionMetadata.error = 'representationNotSupported'; } } catch (e) { - response.didResolutionMetadata.error = 'invalidDid' - response.didResolutionMetadata.message = e.toString() + response.didResolutionMetadata.error = 'invalidDid'; + response.didResolutionMetadata.message = (e as Error).message; } - return response + return response; }, - } -} \ No newline at end of file + }; +} From c2dab8ffc49304a372e33653a85b9add87583ae2 Mon Sep 17 00:00:00 2001 From: dchagastelles Date: Fri, 3 Feb 2023 08:46:23 -0500 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Mircea Nistor --- .../did-provider-pkh/src/pkh-did-provider.ts | 16 +++++++++------- packages/did-provider-pkh/src/resolver.ts | 4 +--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index d342fe4a5..0be33afb0 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -20,7 +20,7 @@ export const SECPK1_NAMESPACES = ['eip155'] as const; export const isValidNamespace = (x: string) => isIn(SECPK1_NAMESPACES, x); /** - * Options for creating a did:ethr + * Options for creating a did:pkh * @beta */ export interface CreateDidPkhOptions { @@ -31,7 +31,7 @@ export interface CreateDidPkhOptions { * * If this is not specified, `1` is assumed. */ - chainId?: string; + chainId?: string | number; } /** @@ -113,7 +113,7 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { }, context: IAgentContext ): Promise { - throw new Error('PkhDIDProvider updateIdentifier not supported yet.'); + throw new Error('illegal_operation: did:pkh update is not possible.'); } async deleteIdentifier( @@ -134,7 +134,7 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { }: { identifier: IIdentifier; key: IKey; options?: any }, context: IContext ): Promise { - throw Error('PkhDIDProvider addKey not supported'); + throw Error('illegal_operation: did:pkh addKey is not possible.'); } async addService( @@ -145,20 +145,22 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { }: { identifier: IIdentifier; service: IService; options?: any }, context: IContext ): Promise { - throw Error('PkhDIDProvider addService not supported'); + throw Error('illegal_operation: did:pkh addService is not possible.'); } async removeKey( args: { identifier: IIdentifier; kid: string; options?: any }, context: IContext ): Promise { - throw Error('PkhDIDProvider removeKey not supported'); + throw Error('illegal_operation: did:pkh removeKey is not possible.'); + } async removeService( args: { identifier: IIdentifier; id: string; options?: any }, context: IContext ): Promise { - throw Error('PkhDIDProvider removeService not supported'); + throw Error('illegal_operation: did:pkh removeService is not possible.'); + } } diff --git a/packages/did-provider-pkh/src/resolver.ts b/packages/did-provider-pkh/src/resolver.ts index 9dca66eca..551aa3a32 100644 --- a/packages/did-provider-pkh/src/resolver.ts +++ b/packages/did-provider-pkh/src/resolver.ts @@ -22,8 +22,6 @@ function toDidDoc(did: string, blockchainAccountId: string): any { blockchainAccountId: 'https://w3id.org/security#blockchainAccountId', EcdsaSecp256k1RecoveryMethod2020: 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020', - Ed25519VerificationKey2018: - 'https://w3id.org/security#Ed25519VerificationKey2018', }, ], id: did, @@ -43,7 +41,7 @@ function toDidDoc(did: string, blockchainAccountId: string): any { `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` ); throw new Error( - `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + `illegal_argument: namespace '${namespace}' not supported. Valid namespaces are: ${SECPK1_NAMESPACES}` ); } return doc; From f90a8ef39d6a1d79978e4d5090ea2fcd5cd5cdf2 Mon Sep 17 00:00:00 2001 From: dchagastelles Date: Fri, 3 Feb 2023 08:56:43 -0500 Subject: [PATCH 3/6] - use debug instead of console.error --- packages/did-provider-pkh/src/pkh-did-provider.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index d342fe4a5..bea4eab62 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -9,6 +9,9 @@ import { } from '@veramo/core'; import { AbstractIdentifierProvider } from '@veramo/did-manager'; +import Debug from 'debug' + +const debug = Debug('veramo:data-store:migrate-presentation-issuance-date') type IContext = IAgentContext; @@ -67,7 +70,7 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { const namespace = options?.namespace ? options.namespace : 'eip155'; if (!isValidNamespace(namespace)) { - console.error( + debug( `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` ); throw new Error( @@ -99,7 +102,7 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { }; return identifier; } else { - console.error('Could not create identifier due to some errors'); + debug('Could not create identifier due to some errors'); throw new Error('Could not create identifier due to some errors'); } } From 6fa77f0fd2765661a628acd1defa1c1f278ee2fe Mon Sep 17 00:00:00 2001 From: dchagastelles Date: Fri, 3 Feb 2023 09:03:41 -0500 Subject: [PATCH 4/6] Use lowercase underscore string prefix --- packages/did-provider-pkh/src/pkh-did-provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index 9017ae981..0d70ee7e6 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -71,10 +71,10 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { if (!isValidNamespace(namespace)) { debug( - `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + `invalid_namespace: '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` ); throw new Error( - `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + `invalid_namespace: '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` ); } From 6a08e73b29c233c3f0f267c385e96ec296ca600a Mon Sep 17 00:00:00 2001 From: dchagastelles Date: Fri, 3 Feb 2023 09:05:51 -0500 Subject: [PATCH 5/6] remove uppercase --- packages/did-provider-pkh/src/pkh-did-provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index 0d70ee7e6..b208dd647 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -71,10 +71,10 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { if (!isValidNamespace(namespace)) { debug( - `invalid_namespace: '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + `invalid_namespace: '${namespace}'. valid namespaces are: ${SECPK1_NAMESPACES}` ); throw new Error( - `invalid_namespace: '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` + `invalid_namespace: '${namespace}'. valid namespaces are: ${SECPK1_NAMESPACES}` ); } From 4dd14b98f63627266be95b27af728738bcd62d4b Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 7 Feb 2023 19:28:08 +0100 Subject: [PATCH 6/6] chore(did-provider-pkh): apply suggestions from code review --- packages/did-provider-pkh/src/pkh-did-provider.ts | 4 ++-- packages/did-provider-pkh/src/resolver.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/did-provider-pkh/src/pkh-did-provider.ts b/packages/did-provider-pkh/src/pkh-did-provider.ts index b208dd647..ef5485330 100644 --- a/packages/did-provider-pkh/src/pkh-did-provider.ts +++ b/packages/did-provider-pkh/src/pkh-did-provider.ts @@ -11,7 +11,7 @@ import { import { AbstractIdentifierProvider } from '@veramo/did-manager'; import Debug from 'debug' -const debug = Debug('veramo:data-store:migrate-presentation-issuance-date') +const debug = Debug('veramo:pkh-did-provider') type IContext = IAgentContext; @@ -103,7 +103,7 @@ export class PkhDIDProvider extends AbstractIdentifierProvider { return identifier; } else { debug('Could not create identifier due to some errors'); - throw new Error('Could not create identifier due to some errors'); + throw new Error('unknown_error: could not create identifier due to errors creating or importing keys'); } } diff --git a/packages/did-provider-pkh/src/resolver.ts b/packages/did-provider-pkh/src/resolver.ts index 551aa3a32..2ae41f336 100644 --- a/packages/did-provider-pkh/src/resolver.ts +++ b/packages/did-provider-pkh/src/resolver.ts @@ -7,7 +7,9 @@ import type { ResolverRegistry, } from 'did-resolver'; import { isValidNamespace, SECPK1_NAMESPACES } from './pkh-did-provider'; +import Debug from 'debug' +const debug = Debug('veramo:pkh-did-resolver') const DID_LD_JSON = 'application/did+ld+json'; const DID_JSON = 'application/did+json'; @@ -37,7 +39,7 @@ function toDidDoc(did: string, blockchainAccountId: string): any { assertionMethod: [vmId], }; if (!isValidNamespace(namespace)) { - console.error( + debug( `Invalid namespace '${namespace}'. Valid namespaces are: ${SECPK1_NAMESPACES}` ); throw new Error(