Skip to content

Commit

Permalink
feat: improve key creation/import options when creating identifiers (#…
Browse files Browse the repository at this point in the history
…1423)

`didManagerCreate()` accepts an `options` parameter where a `keyRef` string or a `key` object can be specified to bootstrap a DID from an existing managed key or predefined private key material. All the included DID providers except did:ion now accept these parameters for bootstrapping. The previous options have been marked as deprecated.
  • Loading branch information
0x62 authored Oct 31, 2024
1 parent daf94b5 commit 12aa854
Show file tree
Hide file tree
Showing 19 changed files with 595 additions and 169 deletions.
1 change: 1 addition & 0 deletions packages/did-provider-ethr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@veramo/core-types": "workspace:^",
"@veramo/did-manager": "workspace:^",
"@veramo/utils": "workspace:^",
"debug": "^4.3.3",
"ethers": "^6.11.1",
"ethr-did": "^3.0.21"
Expand Down
26 changes: 26 additions & 0 deletions packages/did-provider-ethr/src/__tests__/ethr-did-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ describe('EthrDIDProvider', () => {
})

describe('adding keys', () => {
it('supports implicit creation of a Secp256k1 key', async () => {
const newDid = await agent.didManagerCreate({
kms: KMS,
provider: PROVIDER
})

expect(newDid.controllerKeyId).toBeDefined()
})

it('supports creation with a key reference', async () => {
const newKey = await agent.keyManagerCreate({
kms: KMS,
type: 'Secp256k1'
})

const newDid = await agent.didManagerCreate({
kms: KMS,
provider: PROVIDER,
options: {
keyRef: newKey.kid,
}
})

expect(newDid.controllerKeyId).toBe(newKey.kid)
})

it('returns the signed addKey transaction parameters for an Ed25519 key type', async () => {
expect.assertions(11)
const ed25519Key: MinimalImportableKey = {
Expand Down
22 changes: 20 additions & 2 deletions packages/did-provider-ethr/src/ethr-did-provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IAgentContext, IIdentifier, IKey, IKeyManager, IService } from '@veramo/core-types'
import { AbstractIdentifierProvider } from '@veramo/did-manager'
import { importOrCreateKey, CreateIdentifierBaseOptions } from '@veramo/utils'
import { Provider, SigningKey, computeAddress, JsonRpcProvider, TransactionRequest, Signature } from 'ethers'
import { KmsEthereumSigner } from './kms-eth-signer.js'
import Debug from 'debug'
Expand Down Expand Up @@ -27,7 +28,7 @@ export function toEthereumAddress(hexPublicKey: string): string {
* Options for creating a did:ethr
* @beta
*/
export interface CreateDidEthrOptions {
export type CreateDidEthrOptions = CreateIdentifierBaseOptions<'Secp256k1'> & {
/**
* This can be a network name or hex encoded chain ID (string) or a chainId number
*
Expand Down Expand Up @@ -183,7 +184,24 @@ export class EthrDIDProvider extends AbstractIdentifierProvider {
{ kms, options }: { kms?: string; options?: CreateDidEthrOptions },
context: IRequiredContext,
): Promise<Omit<IIdentifier, 'provider'>> {
const key = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, type: 'Secp256k1' })
let key: IKey

if (options?.keyRef) {
key = await context.agent.keyManagerGet({ kid: options.keyRef })

if (key.type !== 'Secp256k1') {
throw new Error(`not_supported: Key type must be Secp256k1` );
}
} else {
key = await importOrCreateKey({
kms: kms || this.defaultKms,
options: {
...(options?.key ?? {}),
type: 'Secp256k1',
}
}, context)
}

const compressedPublicKey = SigningKey.computePublicKey(`0x${key.publicKeyHex}`, true)

let networkSpecifier
Expand Down
3 changes: 3 additions & 0 deletions packages/did-provider-ethr/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
},
{
"path": "../did-manager"
},
{
"path": "../utils"
}
]
}
128 changes: 128 additions & 0 deletions packages/did-provider-jwk/src/__tests__/jwk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,38 @@ const agent = createAgent<IKeyManager & IDIDManager & IResolver>({
})
describe('create did:jwk', () => {
it('Secp256k1', async () => {
const id = await agent.didManagerCreate({
options: {
key: {
type: 'Secp256k1',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
}
},
})
expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFUzI1NksiLCJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJVNV85NlJMQWxMeEl0a3llNXhzcnJzNGt4eEM4clN4N3JNN1dGZllLNVRrIiwieSI6IlNjM0pVM25yVUZWdEVjc0stckRscHNxTXRIWFVFN0x4SXdmTUxYOVVPTjQifQ',
)
})

it('Secp256k1 with keyRef', async () => {
const key = await agent.keyManagerImport({
kms: defaultKms,
type: 'Secp256k1',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
})

const id = await agent.didManagerCreate({
options: {
keyRef: key.kid
},
})

expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFUzI1NksiLCJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsInVzZSI6InNpZyIsIngiOiJVNV85NlJMQWxMeEl0a3llNXhzcnJzNGt4eEM4clN4N3JNN1dGZllLNVRrIiwieSI6IlNjM0pVM25yVUZWdEVjc0stckRscHNxTXRIWFVFN0x4SXdmTUxYOVVPTjQifQ',
)
})

it('Secp256k1 deprecated config', async () => {
const id = await agent.didManagerCreate({
options: {
keyType: 'Secp256k1',
Expand All @@ -44,6 +76,38 @@ describe('create did:jwk', () => {
})

it('Ed25519', async () => {
const id = await agent.didManagerCreate({
options: {
key: {
type: 'Ed25519',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
}
},
})
expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFZERTQSIsImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ1c2UiOiJzaWciLCJ4IjoiTTNodVJCZnJpU3lHemlJS3pUSE5nS1djSVhuX3IxUzYxRnZBcUQyVmhSUSJ9',
)
})

it('Ed25519 with keyRef', async () => {
const key = await agent.keyManagerImport({
kms: defaultKms,
type: 'Ed25519',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
})

const id = await agent.didManagerCreate({
options: {
keyRef: key.kid
},
})

expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFZERTQSIsImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ1c2UiOiJzaWciLCJ4IjoiTTNodVJCZnJpU3lHemlJS3pUSE5nS1djSVhuX3IxUzYxRnZBcUQyVmhSUSJ9',
)
})

it('Ed25519 deprecated config', async () => {
const id = await agent.didManagerCreate({
options: {
keyType: 'Ed25519',
Expand All @@ -56,6 +120,38 @@ describe('create did:jwk', () => {
})

it('X25519', async () => {
const id = await agent.didManagerCreate({
options: {
key: {
type: 'X25519',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
}
},
})
expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFQ0RILUVTIiwiY3J2IjoiWDI1NTE5Iiwia3R5IjoiT0tQIiwidXNlIjoiZW5jIiwieCI6IlVuNFNEWk12R2dReENiZkRBOWpwNjlyNDdvVWdsSF93eU1aRjU2THAwbU0ifQ',
)
})

it('X25519 with keyRef', async () => {
const key = await agent.keyManagerImport({
kms: defaultKms,
type: 'X25519',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
})

const id = await agent.didManagerCreate({
options: {
keyRef: key.kid
},
})

expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFQ0RILUVTIiwiY3J2IjoiWDI1NTE5Iiwia3R5IjoiT0tQIiwidXNlIjoiZW5jIiwieCI6IlVuNFNEWk12R2dReENiZkRBOWpwNjlyNDdvVWdsSF93eU1aRjU2THAwbU0ifQ',
)
})

it('X25519 deprecated config', async () => {
const id = await agent.didManagerCreate({
options: {
keyType: 'X25519',
Expand All @@ -68,6 +164,38 @@ describe('create did:jwk', () => {
})

it('Secp256r1', async () => {
const id = await agent.didManagerCreate({
options: {
key: {
type: 'Secp256r1',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
}
},
})
expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFUzI1NiIsImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ1c2UiOiJzaWciLCJ4IjoiejhTTlNYTVgxUjZlVEt6SkdtLUE3ZWpBZkZsdURsaUhKdW9nT2FQc0REUSIsInkiOiJLUUtBTWVwTU56dHJseTB6ODI3MTg0dDRQdkFuU0lULW1MMFFsaUg1enU0In0',
)
})

it('Secp256r1 with keyRef', async () => {
const key = await agent.keyManagerImport({
kms: defaultKms,
type: 'Secp256r1',
privateKeyHex: 'a5e81a8cd50cf5c31d5b87db3e153e2817f86de350a60edc2335f76d5c3b4e0d',
})

const id = await agent.didManagerCreate({
options: {
keyRef: key.kid
},
})

expect(id.did).toEqual(
'did:jwk:eyJhbGciOiJFUzI1NiIsImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ1c2UiOiJzaWciLCJ4IjoiejhTTlNYTVgxUjZlVEt6SkdtLUE3ZWpBZkZsdURsaUhKdW9nT2FQc0REUSIsInkiOiJLUUtBTWVwTU56dHJseTB6ODI3MTg0dDRQdkFuU0lULW1MMFFsaUg1enU0In0',
)
})

it('Secp256r1 deprecated config', async () => {
const id = await agent.didManagerCreate({
options: {
keyType: 'Secp256r1',
Expand Down
59 changes: 32 additions & 27 deletions packages/did-provider-jwk/src/jwk-did-provider.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IIdentifier, IKey, IService, IAgentContext, IKeyManager } from '@veramo/core-types'
import { AbstractIdentifierProvider } from '@veramo/did-manager'
import { JwkDidSupportedKeyTypes, encodeJoseBlob, generateJwkFromVerificationMethod } from '@veramo/utils'
import { SupportedKeyTypes, JwkDidSupportedKeyTypes, encodeJoseBlob, importOrCreateKey, generateJwkFromVerificationMethod } from '@veramo/utils'
import { VerificationMethod } from 'did-resolver'
import type { JwkCreateIdentifierOptions, JwkDidImportOrGenerateKeyArgs } from './types/jwk-provider-types.js'
import type { JwkCreateIdentifierOptions } from './types/jwk-provider-types.js'

import Debug from 'debug'
const debug = Debug('veramo:did-jwk:identifier-provider')
Expand All @@ -23,20 +23,39 @@ export class JwkDIDProvider extends AbstractIdentifierProvider {
}

async createIdentifier(
{ kms, options }: { kms?: string; options?: JwkCreateIdentifierOptions },
{
kms,
options
}: {
kms?: string;
options?: JwkCreateIdentifierOptions
},
context: IContext,
): Promise<Omit<IIdentifier, 'provider'>> {
const keyType: JwkDidSupportedKeyTypes = options?.keyType || 'Secp256k1'
const key = await this.importOrGenerateKey(
{
kms: kms || this.defaultKms,
options: {
keyType,
...(options?.privateKeyHex && { privateKeyHex: options.privateKeyHex }),
let keyType: JwkDidSupportedKeyTypes = options?.key?.type || options?.keyType || 'Secp256k1'
const privateKeyHex = options?.key?.privateKeyHex || options?.privateKeyHex

let key: IKey

if (options?.keyRef) {
key = await context.agent.keyManagerGet({ kid: options.keyRef })
if (!Object.keys(SupportedKeyTypes).includes(key.type)) {
throw new Error(`not_supported: Key type ${key.type} is not supported`)
}
keyType = key.type as JwkDidSupportedKeyTypes
} else {
key = await importOrCreateKey(
{
kms: kms || this.defaultKms,
options: {
...(options?.key ?? {}),
type: keyType,
privateKeyHex,
},
},
},
context,
)
context,
)
}

const jwk = generateJwkFromVerificationMethod(
keyType,
Expand Down Expand Up @@ -103,18 +122,4 @@ export class JwkDIDProvider extends AbstractIdentifierProvider {
): Promise<any> {
throw Error('not_supported: JwkDIDProvider removeService not possible')
}

private async importOrGenerateKey(args: JwkDidImportOrGenerateKeyArgs, context: IContext): Promise<IKey> {
if (args.options.privateKeyHex) {
return context.agent.keyManagerImport({
kms: args.kms || this.defaultKms,
type: args.options.keyType,
privateKeyHex: args.options.privateKeyHex,
})
}
return context.agent.keyManagerCreate({
kms: args.kms || this.defaultKms,
type: args.options.keyType,
})
}
}
25 changes: 12 additions & 13 deletions packages/did-provider-jwk/src/types/jwk-provider-types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { JwkDidSupportedKeyTypes, KeyUse } from '@veramo/utils'
import { RequireOnly, KeyMetadata } from '@veramo/core-types'
import { JwkDidSupportedKeyTypes, KeyUse, CreateIdentifierBaseOptions } from '@veramo/utils'

export type JwkCreateIdentifierOptions = {
export type JwkCreateIdentifierOptions = CreateIdentifierBaseOptions<JwkDidSupportedKeyTypes> & {
/**
* @deprecated use key.type instead
*/
keyType?: JwkDidSupportedKeyTypes
privateKeyHex?: string
keyUse?: KeyUse
}

export type JwkDidImportOrGenerateKeyArgs = {
kms: string
options: ImportOrGenerateKeyOpts
}

type ImportOrGenerateKeyOpts = {
keyType: JwkDidSupportedKeyTypes
/**
* @deprecated use key.privateKeyHex instead
*/
privateKeyHex?: string
}

keyUse?: KeyUse
};
Loading

0 comments on commit 12aa854

Please sign in to comment.