From 3c7b21e13538fac64581c0c73d0450ef6e9b56f0 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Fri, 7 Jul 2023 02:34:37 +0200 Subject: [PATCH 01/12] feat: Add agent resolver method --- packages/did-provider-ebsi/CHANGELOG.md | 7 +------ packages/did-provider-jwk/CHANGELOG.md | 13 ++++--------- packages/did-provider-key/CHANGELOG.md | 7 +------ packages/did-provider-lto/CHANGELOG.md | 7 +------ packages/did-resolver-ebsi/CHANGELOG.md | 7 +------ packages/did-resolver-jwk/CHANGELOG.md | 7 +------ packages/did-resolver-key/CHANGELOG.md | 7 +------ packages/did-utils/CHANGELOG.md | 15 +++++---------- packages/did-utils/src/did-functions.ts | 13 +++++++++++-- packages/key-manager/CHANGELOG.md | 7 +------ packages/key-utils/CHANGELOG.md | 13 ++++--------- packages/kms-local/CHANGELOG.md | 9 ++------- packages/mnemonic-seed-manager/CHANGELOG.md | 7 +------ 13 files changed, 34 insertions(+), 85 deletions(-) diff --git a/packages/did-provider-ebsi/CHANGELOG.md b/packages/did-provider-ebsi/CHANGELOG.md index 837a5862..917e1da3 100644 --- a/packages/did-provider-ebsi/CHANGELOG.md +++ b/packages/did-provider-ebsi/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-provider-jwk/CHANGELOG.md b/packages/did-provider-jwk/CHANGELOG.md index aa5356e1..5e853331 100644 --- a/packages/did-provider-jwk/CHANGELOG.md +++ b/packages/did-provider-jwk/CHANGELOG.md @@ -5,17 +5,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Fix EC handling for JWKs ([7be20f5](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/7be20f57d6b7d4b7ebf5a2e9b432da34f8f98436)) -* Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) -* Make sure we set the saltLength for RSA PSS ([51ae676](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/51ae6769386866771c68c7b7806a75b62a9d5ec1)) -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Fix EC handling for JWKs ([7be20f5](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/7be20f57d6b7d4b7ebf5a2e9b432da34f8f98436)) +- Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) +- Make sure we set the saltLength for RSA PSS ([51ae676](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/51ae6769386866771c68c7b7806a75b62a9d5ec1)) +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-provider-key/CHANGELOG.md b/packages/did-provider-key/CHANGELOG.md index b88a156b..d2efdb2e 100644 --- a/packages/did-provider-key/CHANGELOG.md +++ b/packages/did-provider-key/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-provider-lto/CHANGELOG.md b/packages/did-provider-lto/CHANGELOG.md index 131060bc..9f813ead 100644 --- a/packages/did-provider-lto/CHANGELOG.md +++ b/packages/did-provider-lto/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-resolver-ebsi/CHANGELOG.md b/packages/did-resolver-ebsi/CHANGELOG.md index bf2f27b8..44f33b35 100644 --- a/packages/did-resolver-ebsi/CHANGELOG.md +++ b/packages/did-resolver-ebsi/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-resolver-jwk/CHANGELOG.md b/packages/did-resolver-jwk/CHANGELOG.md index a1993ec5..2f6152cd 100644 --- a/packages/did-resolver-jwk/CHANGELOG.md +++ b/packages/did-resolver-jwk/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-resolver-key/CHANGELOG.md b/packages/did-resolver-key/CHANGELOG.md index 1036a870..142962f6 100644 --- a/packages/did-resolver-key/CHANGELOG.md +++ b/packages/did-resolver-key/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK-crypto-extensions/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-utils/CHANGELOG.md b/packages/did-utils/CHANGELOG.md index 0e771d01..db34b3b0 100644 --- a/packages/did-utils/CHANGELOG.md +++ b/packages/did-utils/CHANGELOG.md @@ -5,18 +5,13 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Fix EC handling for DID resolution ([5f3d708](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/5f3d70898783d56f5aa7a36e4fd56faf5907dbeb)) -* Fix EC handling for JWKs ([9061e29](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/9061e2968005931127c52febbb3326fddcd62fb2)) -* Fix EC handling for JWKs ([b60825b](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/b60825b155971dc8b01d2b4779faf71cecbacfa6)) -* Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Fix EC handling for DID resolution ([5f3d708](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/5f3d70898783d56f5aa7a36e4fd56faf5907dbeb)) +- Fix EC handling for JWKs ([9061e29](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/9061e2968005931127c52febbb3326fddcd62fb2)) +- Fix EC handling for JWKs ([b60825b](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/b60825b155971dc8b01d2b4779faf71cecbacfa6)) +- Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-OpenSource/ssi-sdk-crypto-extensions/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/did-utils/src/did-functions.ts b/packages/did-utils/src/did-functions.ts index bfcbfeba..2738768e 100644 --- a/packages/did-utils/src/did-functions.ts +++ b/packages/did-utils/src/did-functions.ts @@ -156,8 +156,8 @@ export async function mapIdentifierKeysToDocWithJwkSupport( const extendedKeys: _ExtendedIKey[] = documentKeys .map((verificationMethod) => { /*if (verificationMethod.type !== 'JsonWebKey2020') { - return null - }*/ + return null + }*/ const localKey = localKeys.find( (localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || verificationMethod.publicKeyHex?.startsWith(localKey.publicKeyHex) ) @@ -239,6 +239,15 @@ export async function getSupportedDIDMethods(didOpts: IDIDOptions, context: IAge return didOpts.supportedDIDMethods ?? (await getAgentDIDMethods(context)) } +export function getAgentResolver( + context: IAgentContext, + opts?: { + uniresolverFallback: boolean + } +): Resolvable { + return new AgentDIDResolver(context, opts?.uniresolverFallback ?? true) +} + export class AgentDIDResolver implements Resolvable { private readonly context: IAgentContext private readonly uniresolverFallback: boolean diff --git a/packages/key-manager/CHANGELOG.md b/packages/key-manager/CHANGELOG.md index 4c2ed41b..a4a11894 100644 --- a/packages/key-manager/CHANGELOG.md +++ b/packages/key-manager/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/key-utils/CHANGELOG.md b/packages/key-utils/CHANGELOG.md index f51a2dcd..aaefef1d 100644 --- a/packages/key-utils/CHANGELOG.md +++ b/packages/key-utils/CHANGELOG.md @@ -5,17 +5,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Fix EC handling for JWKs ([9061e29](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/9061e2968005931127c52febbb3326fddcd62fb2)) -* Fix EC handling for JWKs ([dd423f2](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/dd423f24eff5fcc41a3b72c15d62d7e478fbe9b9)) -* Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Fix EC handling for JWKs ([9061e29](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/9061e2968005931127c52febbb3326fddcd62fb2)) +- Fix EC handling for JWKs ([dd423f2](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/dd423f24eff5fcc41a3b72c15d62d7e478fbe9b9)) +- Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-OpenSource/ssi-sdk/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-OpenSource/ssi-sdk/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/kms-local/CHANGELOG.md b/packages/kms-local/CHANGELOG.md index 90e00271..d3112ef2 100644 --- a/packages/kms-local/CHANGELOG.md +++ b/packages/kms-local/CHANGELOG.md @@ -5,15 +5,10 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-Opensource/SSI-SDK/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Fixes in JWK handling ([f5cd4dd](https://github.com/Sphereon-Opensource/SSI-SDK/commit/f5cd4ddd4f0cd0f155dcbf3a7e8b43c89b97cacb)) +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07) diff --git a/packages/mnemonic-seed-manager/CHANGELOG.md b/packages/mnemonic-seed-manager/CHANGELOG.md index 081b5b77..4a4356e1 100644 --- a/packages/mnemonic-seed-manager/CHANGELOG.md +++ b/packages/mnemonic-seed-manager/CHANGELOG.md @@ -5,14 +5,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24) - ### Bug Fixes -* Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) - - - - +- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611)) # [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07) From 462b5e33d31bfdc55bc4d8cf05868a4c945ea386 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Fri, 7 Jul 2023 03:38:03 +0200 Subject: [PATCH 02/12] feat: Add agent resolver method --- packages/did-utils/src/did-functions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/did-utils/src/did-functions.ts b/packages/did-utils/src/did-functions.ts index 2738768e..3be5c530 100644 --- a/packages/did-utils/src/did-functions.ts +++ b/packages/did-utils/src/did-functions.ts @@ -259,10 +259,10 @@ export class AgentDIDResolver implements Resolvable { async resolve(didUrl: string, options?: DIDResolutionOptions): Promise { try { - return this.context.agent.resolveDid({ didUrl, options }) + return await this.context.agent.resolveDid({ didUrl, options }) } catch (error: unknown) { if (this.uniresolverFallback) { - return new UniResolver().resolve(didUrl, options) + return await (new UniResolver()).resolve(didUrl, options) } throw error } From f9309583995e9ff10f1dc7cd09197be5edb614d5 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Wed, 12 Jul 2023 02:29:54 +0200 Subject: [PATCH 03/12] chore: Change JWK resolution to absolute ids. according to spec --- .../__tests__/comparison-regression.test.ts | 60 +++++++++++++------ .../did-provider-jwk/src/jwk-did-provider.ts | 7 +-- .../__tests__/jwk-did-resolver.test.ts | 8 ++- .../did-resolver-jwk/src/jwk-did-resolver.ts | 12 ++-- packages/did-utils/src/did-functions.ts | 2 +- 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/packages/did-provider-jwk/__tests__/comparison-regression.test.ts b/packages/did-provider-jwk/__tests__/comparison-regression.test.ts index e931573f..d4bc9262 100644 --- a/packages/did-provider-jwk/__tests__/comparison-regression.test.ts +++ b/packages/did-provider-jwk/__tests__/comparison-regression.test.ts @@ -40,6 +40,9 @@ const agent = createAgent({ ], }) +function toAbsolute(didDoc: any, did: string) { + return JSON.parse(JSON.stringify(didDoc).replace(/#0/g, `${did}#0`)) +} describe('@sphereon/did-provider-jwk comparison ES256k', () => { it('external JWK should result in equal DID Document', async () => { const { publicKeyJwk } = await method.generateKeyPair('ES256K') @@ -48,7 +51,8 @@ describe('@sphereon/did-provider-jwk comparison ES256k', () => { const didResolutionResult: DIDResolutionResult = await agent.resolveDid({ didUrl: did }) const comparisonDidDoc = await method.toDidDocument(publicKeyJwk) - expect(didResolutionResult.didDocument).toEqual(comparisonDidDoc) + + expect(didResolutionResult.didDocument).toEqual(toAbsolute(comparisonDidDoc, did)) }) it('test resolution', async () => { @@ -67,7 +71,7 @@ describe('@sphereon/did-provider-jwk comparison ES256k', () => { // Resolution const comparisonDidDoc = await method.toDidDocument(jwk) const didResolutionResult: DIDResolutionResult = await agent.resolveDid({ didUrl: did }) - expect(didResolutionResult.didDocument).toEqual(comparisonDidDoc) + expect(didResolutionResult.didDocument).toEqual(toAbsolute(comparisonDidDoc, did)) }) it('Creation from privateKeyHex', async () => { @@ -97,7 +101,7 @@ describe('@sphereon/did-provider-jwk comparison ES256k', () => { const verificationMethod = { controller: 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ', - id: '#0', + id: 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ#0', publicKeyJwk: jwk, type: 'JsonWebKey2020', } @@ -105,11 +109,22 @@ describe('@sphereon/did-provider-jwk comparison ES256k', () => { expect(didResolutionResult!.didDocument!.verificationMethod).toEqual([verificationMethod]) // We correctly resolve the use property. The other lib does not, so let's add it to their response expect(didResolutionResult!.didDocument).toEqual({ - assertionMethod: ['#0'], - authentication: ['#0'], - capabilityDelegation: ['#0'], - capabilityInvocation: ['#0'], - ...(await method.resolve(did)), + assertionMethod: [ + 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ#0', + ], + authentication: [ + 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ#0', + ], + ...toAbsolute( + await method.resolve(did), + 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJmYjY5SEE2M244ZENKd0RmaVJONGxacUtVVU1odHYyZE5BemdjUjJNY0ZBIiwieSI6Ikd3amFWNHpuSm1EZDBOdFlSWGdJeW5aOFlyWDRqN0lzLXFselFuekppclEifQ' + ), }) }) }) @@ -121,7 +136,7 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { const didResolutionResult: DIDResolutionResult = await agent.resolveDid({ didUrl: did }) const comparisonDidDoc = await method.toDidDocument(publicKeyJwk) - expect(didResolutionResult.didDocument).toEqual(comparisonDidDoc) + expect(didResolutionResult.didDocument).toEqual(toAbsolute(comparisonDidDoc, did)) }) it('test resolution', async () => { @@ -140,7 +155,7 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { // Resolution const comparisonDidDoc = await method.toDidDocument(jwk) const didResolutionResult: DIDResolutionResult = await agent.resolveDid({ didUrl: did }) - expect(didResolutionResult.didDocument).toEqual(comparisonDidDoc) + expect(didResolutionResult.didDocument).toEqual(toAbsolute(comparisonDidDoc, did)) }) it('Should decode test vector from spec', async () => { @@ -211,7 +226,7 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { id: 'did:jwk:eyJraWQiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6andrLXRodW1icHJpbnQ6c2hhLTI1NjpUOVh4eFZVUHR2TDd0Z0dMOVk4alR4WENPVDFMRjduU2VzWnl0d3FpNVM4Iiwia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImFsZyI6IkVTMjU2IiwieCI6InAwMUFBQ2FkNWFXYVpmVzAwbXhqU0dIVG41R3VpN3Z6cGZqQm1DX2ZhR0EiLCJ5IjoiczR4Y0FYUnVoQ1Z0YTZiaF9Vc3M3eE52NGd5UkRVQW5SS2NzRlJCMzJvWSJ9', verificationMethod: [ { - id: '#0', + id: 'did:jwk:eyJraWQiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6andrLXRodW1icHJpbnQ6c2hhLTI1NjpUOVh4eFZVUHR2TDd0Z0dMOVk4alR4WENPVDFMRjduU2VzWnl0d3FpNVM4Iiwia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImFsZyI6IkVTMjU2IiwieCI6InAwMUFBQ2FkNWFXYVpmVzAwbXhqU0dIVG41R3VpN3Z6cGZqQm1DX2ZhR0EiLCJ5IjoiczR4Y0FYUnVoQ1Z0YTZiaF9Vc3M3eE52NGd5UkRVQW5SS2NzRlJCMzJvWSJ9#0', type: 'JsonWebKey2020', controller: 'did:jwk:eyJraWQiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6andrLXRodW1icHJpbnQ6c2hhLTI1NjpUOVh4eFZVUHR2TDd0Z0dMOVk4alR4WENPVDFMRjduU2VzWnl0d3FpNVM4Iiwia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsImFsZyI6IkVTMjU2IiwieCI6InAwMUFBQ2FkNWFXYVpmVzAwbXhqU0dIVG41R3VpN3Z6cGZqQm1DX2ZhR0EiLCJ5IjoiczR4Y0FYUnVoQ1Z0YTZiaF9Vc3M3eE52NGd5UkRVQW5SS2NzRlJCMzJvWSJ9', @@ -295,7 +310,7 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { const verificationMethod = { controller: 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0', - id: '#0', + id: 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0#0', publicKeyJwk: jwk, type: 'JsonWebKey2020', } @@ -303,11 +318,22 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { expect(didResolutionResult!.didDocument!.verificationMethod).toEqual([verificationMethod]) // We correctly resolve the use property. The other lib does not, so let's add it to their response expect(didResolutionResult!.didDocument).toEqual({ - assertionMethod: ['#0'], - authentication: ['#0'], - capabilityDelegation: ['#0'], - capabilityInvocation: ['#0'], - ...(await method.resolve(did)), + assertionMethod: [ + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0#0', + ], + authentication: [ + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0#0', + ], + capabilityDelegation: [ + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0#0', + ], + capabilityInvocation: [ + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0#0', + ], + ...toAbsolute( + await method.resolve(did), + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiOWdnczRDbTRWWGNLT2VQcGprTDlpU3lNQ2EyMnlPamJvLW9VWHB5LWF3MCIsInkiOiJsRVhXN2JfSjdsY2VpVkV0cmZwdHZ1UGVFTnNPSmwtZmh6bXU2NTRHUFI4In0' + ), }) }) }) diff --git a/packages/did-provider-jwk/src/jwk-did-provider.ts b/packages/did-provider-jwk/src/jwk-did-provider.ts index 172274a1..c7b1a7de 100644 --- a/packages/did-provider-jwk/src/jwk-did-provider.ts +++ b/packages/did-provider-jwk/src/jwk-did-provider.ts @@ -12,7 +12,6 @@ import { IRequiredContext, Key, } from './types/jwk-provider-types' -// import * as u8a from 'uint8arrays' const debug = Debug('sphereon:did-provider-jwk') @@ -40,11 +39,11 @@ export class JwkDIDProvider extends AbstractIdentifierProvider { const use = jwkDetermineUse(key.type, args?.options?.use) const jwk: JsonWebKey = toJwk(key.publicKeyHex, key.type, use) - debug(JSON.stringify(jwk, null, 2)) + const did = `did:jwk:${base64url(JSON.stringify(jwk))}` const identifier: Omit = { - did: `did:jwk:${base64url(JSON.stringify(jwk))}`, - controllerKeyId: '#0', + did, + controllerKeyId: `${did}#0`, keys: [key], services: [], } diff --git a/packages/did-resolver-jwk/__tests__/jwk-did-resolver.test.ts b/packages/did-resolver-jwk/__tests__/jwk-did-resolver.test.ts index e283ade3..c213676c 100644 --- a/packages/did-resolver-jwk/__tests__/jwk-did-resolver.test.ts +++ b/packages/did-resolver-jwk/__tests__/jwk-did-resolver.test.ts @@ -44,7 +44,9 @@ describe('@sphereon/jwk-did-resolver', () => { expect(didResolutionResult.didDocument!.id).toEqual(did) expect(didResolutionResult.didDocument!.verificationMethod).toBeDefined() expect(didResolutionResult.didDocument!.verificationMethod!.length).toEqual(1) - expect(didResolutionResult.didDocument!.verificationMethod![0].id).toEqual('#0') + expect(didResolutionResult.didDocument!.verificationMethod![0].id).toEqual( + 'did:jwk:eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJCS2NlRjMwbHBTNkptT1RsS09LQVdudGtKdVRCSzNGX1JoaXlEcTRtdm9jIiwieSI6Im9WY1phQnpiSFJ2UW5iSXhwRWRXbVlRMGtSRm42ajVDRkVQcGxvX09ON1UifQ#0' + ) expect(didResolutionResult.didDocument!.verificationMethod![0].type).toEqual(VerificationType.JsonWebKey2020) expect(didResolutionResult.didDocument!.verificationMethod![0].controller).toEqual(did) expect(didResolutionResult.didDocument!.verificationMethod![0].publicKeyJwk).toBeDefined() @@ -58,7 +60,9 @@ describe('@sphereon/jwk-did-resolver', () => { const didResolutionResult: DIDResolutionResult = await agent.resolveDid({ didUrl: did }) expect(didResolutionResult.didDocument).toBeDefined() - expect(didResolutionResult.didDocument!.keyAgreement).toEqual(['#0']) + expect(didResolutionResult.didDocument!.keyAgreement).toEqual([ + 'did:jwk:eyJ1c2UiOiJlbmMiLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJCRnhTU29XTnBCOElYVktUYk44U0xNbVlVeThTSG1Ybk9lb050RHB1QVpNIiwieSI6InBWRmxxSlJqNkNNaFljZ3dqVTk2eko3V09mWk9GWXpScE1selZGT0NKcFEifQ#0', + ]) }) it('should resolve to correct did document with use signature', async () => { diff --git a/packages/did-resolver-jwk/src/jwk-did-resolver.ts b/packages/did-resolver-jwk/src/jwk-did-resolver.ts index 4df308a1..01bde3c7 100644 --- a/packages/did-resolver-jwk/src/jwk-did-resolver.ts +++ b/packages/did-resolver-jwk/src/jwk-did-resolver.ts @@ -52,17 +52,17 @@ const resolve = async (didUrl: string, _options?: DIDResolutionOptions): Promise id: parsedDid.did, verificationMethod: [ { - id: '#0', + id: `${parsedDid.did}#0`, type: VerificationType.JsonWebKey2020, controller: parsedDid.did, publicKeyJwk: jwk, }, ], - ...(sig && { assertionMethod: ['#0'] }), - ...(sig && { authentication: ['#0'] }), - ...(sig && { capabilityInvocation: ['#0'] }), - ...(sig && { capabilityDelegation: ['#0'] }), - ...(enc && { keyAgreement: ['#0'] }), + ...(sig && { assertionMethod: [`${parsedDid.did}#0`] }), + ...(sig && { authentication: [`${parsedDid.did}#0`] }), + ...(sig && { capabilityInvocation: [`${parsedDid.did}#0`] }), + ...(sig && { capabilityDelegation: [`${parsedDid.did}#0`] }), + ...(enc && { keyAgreement: [`${parsedDid.did}#0`] }), }, didDocumentMetadata: {}, } diff --git a/packages/did-utils/src/did-functions.ts b/packages/did-utils/src/did-functions.ts index 3be5c530..310e84cb 100644 --- a/packages/did-utils/src/did-functions.ts +++ b/packages/did-utils/src/did-functions.ts @@ -262,7 +262,7 @@ export class AgentDIDResolver implements Resolvable { return await this.context.agent.resolveDid({ didUrl, options }) } catch (error: unknown) { if (this.uniresolverFallback) { - return await (new UniResolver()).resolve(didUrl, options) + return await new UniResolver().resolve(didUrl, options) } throw error } From 76e7212cd6f7f27315d6b6bfdb17154124f3158e Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Tue, 25 Jul 2023 01:15:35 +0200 Subject: [PATCH 04/12] feat: Identifier to DID Document and DID resolution --- packages/did-utils/src/did-functions.ts | 76 ++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/packages/did-utils/src/did-functions.ts b/packages/did-utils/src/did-functions.ts index 310e84cb..7d376c7c 100644 --- a/packages/did-utils/src/did-functions.ts +++ b/packages/did-utils/src/did-functions.ts @@ -14,7 +14,7 @@ import { DIDResolutionOptions, Resolvable, VerificationMethod } from 'did-resolv import elliptic from 'elliptic' import * as u8a from 'uint8arrays' import { IDIDOptions, IIdentifierOpts } from './types' -import { hexKeyFromPEMBasedJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { hexKeyFromPEMBasedJwk, JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' export const getFirstKeyWithRelation = async ( identifier: IIdentifier, @@ -156,8 +156,8 @@ export async function mapIdentifierKeysToDocWithJwkSupport( const extendedKeys: _ExtendedIKey[] = documentKeys .map((verificationMethod) => { /*if (verificationMethod.type !== 'JsonWebKey2020') { - return null - }*/ + return null + }*/ const localKey = localKeys.find( (localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || verificationMethod.publicKeyHex?.startsWith(localKey.publicKeyHex) ) @@ -268,3 +268,73 @@ export class AgentDIDResolver implements Resolvable { } } } + +export function toDidDocument( + identifier?: IIdentifier, + opts?: { + did?: string + use?: JwkKeyUse[] + } +): DIDDocument | undefined { + let didDocument: DIDDocument | undefined = undefined + if (identifier) { + const did = identifier.did ?? opts?.did + didDocument = { + '@context': 'https://www.w3.org/ns/did/v1', + id: did, + verificationMethod: identifier.keys.map((key) => { + const vm: VerificationMethod = { + controller: did, + id: `${did}#${key.kid}`, + publicKeyJwk: toJwk(key.publicKeyHex, key.type, key.type === 'X25519' ? JwkKeyUse.Encryption : JwkKeyUse.Signature), + type: 'JsonWebKey2020', + } + return vm + }), + ...((!opts?.use || opts?.use?.includes(JwkKeyUse.Signature)) && + identifier.keys && { + assertionMethod: identifier.keys.map((key) => { + return `${did}#${key.kid}` + }), + authentication: identifier.keys.map((key) => { + return `${did}#${key.kid}` + }), + }), + ...((!opts?.use || opts?.use?.includes(JwkKeyUse.Encryption)) && + identifier.keys && { + keyAgreement: identifier.keys + .filter((key) => key.type === 'X25519') + .map((key) => { + return `${did}#${key.kid}` + }), + }), + ...(identifier.services && { service: identifier.services }), + } + } + return didDocument +} + +export function toDidResolutionResult( + identifier?: IIdentifier, + opts?: { + did?: string + supportedMethod?: string[] + } +): DIDResolutionResult { + const didDocument = toDidDocument(identifier, opts) ?? null + + const resolutionResult: DIDResolutionResult = { + '@context': 'https://w3id.org/did-resolution/v1', + didDocument, + didResolutionMetadata: { + ...(!didDocument && { error: 'notFound' }), + ...(Array.isArray(opts?.supportedMethod) && + identifier && + !opts?.supportedMethod.includes(identifier.provider.replace('did:', '')) && { error: 'unsupportedDidMethod' }), + }, + didDocumentMetadata: { + ...(identifier?.alias && { equivalentId: identifier?.alias }), + }, + } + return resolutionResult +} From 75ba154bb110a50a1892a5308627895a93f527a4 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:07:01 +0200 Subject: [PATCH 05/12] feat: Add support for RSA key generation and RSA to JWK --- .../key-utils/__tests__/functions.test.ts | 36 ++--- packages/key-utils/package.json | 2 + packages/key-utils/src/functions.ts | 147 ++++++++++++++---- packages/key-utils/src/index.ts | 3 +- .../key-utils/src/types/key-util-types.ts | 20 +++ packages/key-utils/src/x509/index.ts | 3 + .../src/x509/rsa-key.ts | 6 +- .../src/x509/rsa-signer.ts | 8 +- .../key-utils/src/{ => x509}/x509-utils.ts | 2 +- 9 files changed, 174 insertions(+), 53 deletions(-) create mode 100644 packages/key-utils/src/x509/index.ts rename packages/{kms-local => key-utils}/src/x509/rsa-key.ts (92%) rename packages/{kms-local => key-utils}/src/x509/rsa-signer.ts (88%) rename packages/key-utils/src/{ => x509}/x509-utils.ts (99%) diff --git a/packages/key-utils/__tests__/functions.test.ts b/packages/key-utils/__tests__/functions.test.ts index 801ed585..28202c08 100644 --- a/packages/key-utils/__tests__/functions.test.ts +++ b/packages/key-utils/__tests__/functions.test.ts @@ -2,45 +2,45 @@ import { generatePrivateKeyHex } from '../src' import { Key } from '../src' describe('functions: key generator', () => { - it('Secp256k1 should generate random keys', () => { - const key1 = generatePrivateKeyHex(Key.Secp256k1) - const key2 = generatePrivateKeyHex(Key.Secp256k1) - const key3 = generatePrivateKeyHex(Key.Secp256k1) + it('Secp256k1 should generate random keys', async () => { + const key1 = await generatePrivateKeyHex(Key.Secp256k1) + const key2 = await generatePrivateKeyHex(Key.Secp256k1) + const key3 = await generatePrivateKeyHex(Key.Secp256k1) expect(key1).toBeDefined() expect(key2).toBeDefined() expect(key3).toBeDefined() expect(key1).not.toBe(key2) expect(key2).not.toBe(key3) }) - it('Secp256k1 should result in hex length 64', () => { - expect(generatePrivateKeyHex(Key.Secp256k1).length).toBe(64) + it('Secp256k1 should result in hex length 64', async () => { + expect((await generatePrivateKeyHex(Key.Secp256k1)).length).toBe(64) }) - it('Secp256r1 should generate random keys', () => { - const key1 = generatePrivateKeyHex(Key.Secp256r1) - const key2 = generatePrivateKeyHex(Key.Secp256r1) - const key3 = generatePrivateKeyHex(Key.Secp256r1) + it('Secp256r1 should generate random keys', async () => { + const key1 = await generatePrivateKeyHex(Key.Secp256r1) + const key2 = await generatePrivateKeyHex(Key.Secp256r1) + const key3 = await generatePrivateKeyHex(Key.Secp256r1) expect(key1).toBeDefined() expect(key2).toBeDefined() expect(key3).toBeDefined() expect(key1).not.toBe(key2) expect(key2).not.toBe(key3) }) - it('Secp256r1 should result in hex length 64', () => { - expect(generatePrivateKeyHex(Key.Secp256r1).length).toBe(64) + it('Secp256r1 should result in hex length 64', async () => { + expect((await generatePrivateKeyHex(Key.Secp256r1)).length).toBe(64) }) - it('Ed25519 should generate random keys', () => { - const key1 = generatePrivateKeyHex(Key.Ed25519) - const key2 = generatePrivateKeyHex(Key.Ed25519) - const key3 = generatePrivateKeyHex(Key.Ed25519) + it('Ed25519 should generate random keys', async () => { + const key1 = await generatePrivateKeyHex(Key.Ed25519) + const key2 = await generatePrivateKeyHex(Key.Ed25519) + const key3 = await generatePrivateKeyHex(Key.Ed25519) expect(key1).toBeDefined() expect(key2).toBeDefined() expect(key3).toBeDefined() expect(key1).not.toBe(key2) expect(key2).not.toBe(key3) }) - it('Ed25519 should result in hex length 128', () => { - expect(generatePrivateKeyHex(Key.Ed25519).length).toBe(128) + it('Ed25519 should result in hex length 128', async () => { + expect((await generatePrivateKeyHex(Key.Ed25519)).length).toBe(128) }) }) diff --git a/packages/key-utils/package.json b/packages/key-utils/package.json index bc2b1ff7..9ca94d51 100644 --- a/packages/key-utils/package.json +++ b/packages/key-utils/package.json @@ -10,6 +10,8 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { + "@veramo/core": "4.2.0", + "@sphereon/isomorphic-webcrypto": "^2.4.0-unstable.4", "@ethersproject/random": "^5.6.1", "@stablelib/ed25519": "^1.0.2", "@stablelib/sha256": "^1.0.1", diff --git a/packages/key-utils/src/functions.ts b/packages/key-utils/src/functions.ts index 9ad8ad1a..f23c8f99 100644 --- a/packages/key-utils/src/functions.ts +++ b/packages/key-utils/src/functions.ts @@ -1,39 +1,95 @@ import { randomBytes } from '@ethersproject/random' import { generateKeyPair as generateSigningKeyPair } from '@stablelib/ed25519' +import { IAgentContext, IKey, IKeyManager } from '@veramo/core' import { JsonWebKey } from 'did-resolver' -import * as u8a from 'uint8arrays' -import { ENC_KEY_ALGS, Key, KeyCurve, KeyType, JwkKeyUse, SIG_KEY_ALGS, TKeyType } from './types' import elliptic from 'elliptic' +import * as u8a from 'uint8arrays' +import { ENC_KEY_ALGS, IImportProvidedOrGeneratedKeyArgs, JwkKeyUse, KeyCurve, KeyType, SIG_KEY_ALGS, TKeyType } from './types' +import { generateRSAKeyAsPEM, hexToPEM, PEMToJwk, privateKeyHexFromPEM } from './x509' /** * Generates a random Private Hex Key for the specified key type * @param type The key type * @return The private key in Hex form */ -export const generatePrivateKeyHex = (type: TKeyType): string => { +export const generatePrivateKeyHex = async (type: TKeyType): Promise => { switch (type) { - case Key.Ed25519: { + case 'Ed25519': { const keyPairEd25519 = generateSigningKeyPair() return u8a.toString(keyPairEd25519.secretKey, 'base16') } // The Secp256 types use the same method to generate the key - case Key.Secp256r1: - case Key.Secp256k1: { + case 'Secp256r1': + case 'Secp256k1': { const privateBytes = randomBytes(32) return u8a.toString(privateBytes, 'base16') } + case 'RSA': { + const pem = await generateRSAKeyAsPEM('RSA-PSS', 'SHA-256', 2048) + return privateKeyHexFromPEM(pem) + } default: throw Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`) } } +/** + * We optionally generate and then import our own keys. + * + * @param args The key arguments + * @param context The Veramo agent context + * @private + */ +export async function importProvidedOrGeneratedKey( + args: IImportProvidedOrGeneratedKeyArgs & { + kms: string + }, + context: IAgentContext +): Promise { + // @ts-ignore + const type = args.options?.type ?? args.options?.key?.type ?? args.options?.keyType ?? 'Secp256r1' + const key = args?.options?.key + // Make sure x509 options are also set on the metadata as that is what the kms will look for + if (args.options?.x509 && key && !key?.meta?.x509) { + key.meta = { + ...key.meta, + x509: { + ...key.meta?.x509, + ...args.options.x509, + }, + } + } + + if (args.options && args.options?.use === JwkKeyUse.Encryption && !ENC_KEY_ALGS.includes(type)) { + throw new Error(`${type} keys are not valid for encryption`) + } + + let privateKeyHex: string + if (key) { + privateKeyHex = key.privateKeyHex ?? key.meta?.x509?.privateKeyHex + if (!privateKeyHex && !key.meta?.x509?.privateKeyPEM) { + throw new Error(`We need to have a private key in Hex or PEM when importing a key`) + } + } else { + privateKeyHex = await generatePrivateKeyHex(type) + } + + return context.agent.keyManagerImport({ + ...key, + kms: args.kms, + type, + privateKeyHex, + }) +} + /** * Converts hex value to base64url * @param value hex value * @return Base64Url encoded value */ export const hex2base64url = (value: string) => { + //fixme: Buffer to u8a const buffer = Buffer.from(value, 'hex') const base64 = buffer.toString('base64') const base64url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') @@ -45,17 +101,26 @@ export const hex2base64url = (value: string) => { * Converts a public key in hex format to a JWK * @param publicKeyHex public key in hex * @param type The type of the key (Ed25519, Secp256k1/r1) - * @param use The optional use for the key (sig/enc) + * @param opts. Options, like the optional use for the key (sig/enc) * @return The JWK */ -export const toJwk = (publicKeyHex: string, type: TKeyType, use?: JwkKeyUse): JsonWebKey => { +export const toJwk = (publicKeyHex: string, type: TKeyType, opts?: { use?: JwkKeyUse; key?: IKey }): JsonWebKey => { + const { key } = opts ?? {} + if (key && key.publicKeyHex !== publicKeyHex) { + throw Error(`Provided key with id ${key.kid}, has a different public key hex than supplied public key ${publicKeyHex}`) + } switch (type) { - case Key.Ed25519: - return toEd25519Jwk(publicKeyHex, use) - case Key.Secp256k1: - return toSecp256k1Jwk(publicKeyHex, use) - case Key.Secp256r1: - return toSecp256r1Jwk(publicKeyHex, use) + case 'Ed25519': + return toEd25519OrX25519Jwk(publicKeyHex, { ...opts, crv: KeyCurve.Ed25519 }) + case 'X25519': + return toEd25519OrX25519Jwk(publicKeyHex, { ...opts, crv: KeyCurve.X25519 }) + case 'Secp256k1': + return toSecp256k1Jwk(publicKeyHex, opts) + case 'Secp256r1': + return toSecp256r1Jwk(publicKeyHex, opts) + case 'RSA': + return toRSAJwk(publicKeyHex, opts) + default: throw new Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`) } @@ -81,10 +146,18 @@ export const jwkDetermineUse = (type: TKeyType, suppliedUse?: JwkKeyUse): JwkKey * Assert the key has a proper length * * @param keyHex Input key - * @param expectedKeyLength Expected key length + * @param expectedKeyLength Expected key length(s) */ -const assertProperKeyLength = (keyHex: string, expectedKeyLength: number) => { - if (keyHex.length !== expectedKeyLength) { +const assertProperKeyLength = (keyHex: string, expectedKeyLength: number | number[]) => { + if (Array.isArray(expectedKeyLength)) { + if (expectedKeyLength.includes(keyHex.length)) { + throw Error( + `Invalid key length. Needs to be a hex string with length from ${JSON.stringify(expectedKeyLength)} instead of ${ + keyHex.length + }. Input: ${keyHex}` + ) + } + } else if (keyHex.length !== expectedKeyLength) { throw Error(`Invalid key length. Needs to be a hex string with length ${expectedKeyLength} instead of ${keyHex.length}. Input: ${keyHex}`) } } @@ -95,8 +168,9 @@ const assertProperKeyLength = (keyHex: string, expectedKeyLength: number) => { * @param use The use for the key * @return The JWK */ -const toSecp256k1Jwk = (publicKeyHex: string, use?: JwkKeyUse): JsonWebKey => { +const toSecp256k1Jwk = (publicKeyHex: string, opts?: { use?: JwkKeyUse }): JsonWebKey => { assertProperKeyLength(publicKeyHex, 130) + const { use } = opts ?? {} return { alg: 'ES256K', ...(use !== undefined && { use }), @@ -113,12 +187,9 @@ const toSecp256k1Jwk = (publicKeyHex: string, use?: JwkKeyUse): JsonWebKey => { * @param use The use for the key * @return The JWK */ -const toSecp256r1Jwk = (publicKeyHex: string, use?: JwkKeyUse): JsonWebKey => { - const prefix = '' - /*if (publicKeyHex.length === 128) { - prefix = "04" - }*/ - const publicKey = `${prefix}${publicKeyHex}` // We add the 'uncompressed' type 04 prefix +const toSecp256r1Jwk = (publicKeyHex: string, opts?: { use?: JwkKeyUse }): JsonWebKey => { + const { use } = opts ?? {} + const publicKey = publicKeyHex assertProperKeyLength(publicKey, 66) const secp256r1 = new elliptic.ec('p256') @@ -135,18 +206,38 @@ const toSecp256r1Jwk = (publicKeyHex: string, use?: JwkKeyUse): JsonWebKey => { } /** - * Generates a JWK from an Ed25519 public key - * @param publicKeyHex Ed25519 public key in hex + * Generates a JWK from an Ed25519/X25519 public key + * @param publicKeyHex Ed25519/X25519 public key in hex * @param use The use for the key * @return The JWK */ -const toEd25519Jwk = (publicKeyHex: string, use?: JwkKeyUse): JsonWebKey => { +const toEd25519OrX25519Jwk = ( + publicKeyHex: string, + opts: { + use?: JwkKeyUse + crv: KeyCurve.Ed25519 | KeyCurve.X25519 + } +): JsonWebKey => { assertProperKeyLength(publicKeyHex, 64) + const { use } = opts ?? {} return { alg: 'EdDSA', ...(use !== undefined && { use }), kty: KeyType.OKP, - crv: KeyCurve.Ed25519, + crv: opts?.crv ?? KeyCurve.Ed25519, x: hex2base64url(publicKeyHex.substr(0, 64)), } } + +const toRSAJwk = (publicKeyHex: string, opts?: { use?: JwkKeyUse; key?: IKey }): JsonWebKey => { + const { key } = opts ?? {} + // const publicKey = publicKeyHex + // assertProperKeyLength(publicKey, [2048, 3072, 4096]) + + if (key?.meta?.publicKeyJwk) { + return key.meta.publicKeyJwk as JsonWebKey + } + + const publicKeyPEM = key?.meta?.publicKeyPEM ?? hexToPEM(publicKeyHex, 'public') + return PEMToJwk(publicKeyPEM, 'public') as JsonWebKey +} diff --git a/packages/key-utils/src/index.ts b/packages/key-utils/src/index.ts index b0f5cfcb..4f6b373c 100644 --- a/packages/key-utils/src/index.ts +++ b/packages/key-utils/src/index.ts @@ -4,8 +4,9 @@ * * @packageDocumentation */ +export * from './x509' export * from './functions' export * from './jwk-jcs' export * from './types' -export * from './x509-utils' +export * from './x509/x509-utils' export * from './digest-methods' diff --git a/packages/key-utils/src/types/key-util-types.ts b/packages/key-utils/src/types/key-util-types.ts index a2aa8d38..7929369d 100644 --- a/packages/key-utils/src/types/key-util-types.ts +++ b/packages/key-utils/src/types/key-util-types.ts @@ -1,3 +1,5 @@ +import { MinimalImportableKey } from '@veramo/core' + export const JWK_JCS_PUB_NAME = 'jwk_jcs-pub' export const JWK_JCS_PUB_PREFIX = 0xeb51 @@ -18,11 +20,13 @@ export enum KeyCurve { Secp256k1 = 'secp256k1', P_256 = 'P-256', Ed25519 = 'Ed25519', + X25519 = 'X25519', } export enum KeyType { EC = 'EC', OKP = 'OKP', + RSA = 'RSA', } export const SIG_KEY_ALGS = ['ES256', 'ES384', 'ES512', 'EdDSA', 'ES256K', 'Ed25519', 'Secp256k1', 'Secp256r1', 'Bls12381G1', 'Bls12381G2'] @@ -42,3 +46,19 @@ export interface X509Opts { certificateChainURL?: string // Certificate chain URL. If used this is where the certificateChainPEM will be hosted/found. certificateChainPEM?: string // Base64 (not url!) encoded DER certificate chain. Please provide even if certificateChainURL is used! } + +export interface IImportProvidedOrGeneratedKeyArgs { + kms?: string + options?: IKeyOpts +} +export interface IKeyOpts { + key?: WithRequiredProperty, 'privateKeyHex'> // Optional key to import with only privateKeyHex mandatory. If not specified a key with random kid will be created + type?: TKeyType // The key type. Defaults to Secp256k1 + use?: JwkKeyUse // The key use + x509?: X509Opts +} + +// Needed to make a single property required +type WithRequiredProperty = Type & { + [Property in Key]-?: Type[Property] +} diff --git a/packages/key-utils/src/x509/index.ts b/packages/key-utils/src/x509/index.ts new file mode 100644 index 00000000..b1c4a132 --- /dev/null +++ b/packages/key-utils/src/x509/index.ts @@ -0,0 +1,3 @@ +export * from './rsa-key' +export * from './rsa-signer' +export * from './x509-utils' diff --git a/packages/kms-local/src/x509/rsa-key.ts b/packages/key-utils/src/x509/rsa-key.ts similarity index 92% rename from packages/kms-local/src/x509/rsa-key.ts rename to packages/key-utils/src/x509/rsa-key.ts index 5a1da154..a16365f0 100644 --- a/packages/kms-local/src/x509/rsa-key.ts +++ b/packages/key-utils/src/x509/rsa-key.ts @@ -1,6 +1,8 @@ import crypto from '@sphereon/isomorphic-webcrypto' import * as u8a from 'uint8arrays' -import { base64ToPEM, HashAlgorithm, JWK } from '@sphereon/ssi-sdk-ext.key-utils' +import { HashAlgorithm } from '../digest-methods' +import { JWK } from '../types' +import { base64ToPEM } from './x509-utils' export type RSASignatureSchemes = 'RSASSA-PKCS1-V1_5' | 'RSA-PSS' @@ -26,7 +28,7 @@ export const signAlgorithmToSchemeAndHashAlg = (signingAlg: string) => { return { scheme, hashAlgorithm } } -export const importRSAKey = async ( +export const cryptoSubtleImportRSAKey = async ( jwk: JWK, scheme: RSAEncryptionSchemes | RSASignatureSchemes, hashAlgorithm?: HashAlgorithm diff --git a/packages/kms-local/src/x509/rsa-signer.ts b/packages/key-utils/src/x509/rsa-signer.ts similarity index 88% rename from packages/kms-local/src/x509/rsa-signer.ts rename to packages/key-utils/src/x509/rsa-signer.ts index e6842f9a..048857ad 100644 --- a/packages/kms-local/src/x509/rsa-signer.ts +++ b/packages/key-utils/src/x509/rsa-signer.ts @@ -1,7 +1,9 @@ import * as u8a from 'uint8arrays' import crypto from '@sphereon/isomorphic-webcrypto' -import { importRSAKey, RSAEncryptionSchemes, RSASignatureSchemes } from './rsa-key' -import { HashAlgorithm, JWK, PEMToJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { HashAlgorithm } from '../digest-methods' +import { JWK } from '../types' +import { cryptoSubtleImportRSAKey, RSAEncryptionSchemes, RSASignatureSchemes } from './rsa-key' +import { PEMToJwk } from './x509-utils' export class RSASigner { private readonly hashAlgorithm: HashAlgorithm @@ -36,7 +38,7 @@ export class RSASigner { private async getKey(): Promise { if (!this.key) { - this.key = await importRSAKey(this.jwk, this.scheme, this.hashAlgorithm) + this.key = await cryptoSubtleImportRSAKey(this.jwk, this.scheme, this.hashAlgorithm) } return this.key } diff --git a/packages/key-utils/src/x509-utils.ts b/packages/key-utils/src/x509/x509-utils.ts similarity index 99% rename from packages/key-utils/src/x509-utils.ts rename to packages/key-utils/src/x509/x509-utils.ts index c23838fd..d459d57b 100644 --- a/packages/key-utils/src/x509-utils.ts +++ b/packages/key-utils/src/x509/x509-utils.ts @@ -1,7 +1,7 @@ import * as u8a from 'uint8arrays' // @ts-ignore import keyto from '@trust/keyto' -import { JWK, KeyVisibility } from './types' +import { JWK, KeyVisibility } from '../types' // Based on (MIT licensed): // https://github.com/hildjj/node-posh/blob/master/lib/index.js From 86fa592977d5d0023573c3315f7291b483044d0b Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:08:33 +0200 Subject: [PATCH 06/12] chore: Small changes because of refactor --- .../src/SphereonKeyManagementSystem.ts | 46 +++++++++++-------- packages/kms-local/src/__tests__/rsa.test.ts | 2 +- packages/kms-local/src/index.ts | 18 +++++++- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/packages/kms-local/src/SphereonKeyManagementSystem.ts b/packages/kms-local/src/SphereonKeyManagementSystem.ts index cfda7404..c8dbdd39 100644 --- a/packages/kms-local/src/SphereonKeyManagementSystem.ts +++ b/packages/kms-local/src/SphereonKeyManagementSystem.ts @@ -1,15 +1,24 @@ -import Debug from 'debug' +import { blsSign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures' +import { + generatePrivateKeyHex, + hexToPEM, + jwkToPEM, + pemCertChainTox5c, + PEMToHex, + PEMToJwk, + RSASigner, + signAlgorithmToSchemeAndHashAlg, + X509Opts, +} from '@sphereon/ssi-sdk-ext.key-utils' import { IKey, ManagedKeyInfo, MinimalImportableKey, TKeyType } from '@veramo/core' import { AbstractPrivateKeyStore, ManagedPrivateKey } from '@veramo/key-manager' import { KeyManagementSystem } from '@veramo/kms-local' -import { ManagedKeyInfoArgs, KeyType } from './index' -import { blsSign, generateBls12381G2KeyPair } from '@mattrglobal/bbs-signatures' -import { RSASigner } from './x509/rsa-signer' -import { generateRSAKeyAsPEM, signAlgorithmToSchemeAndHashAlg } from './x509/rsa-key' -import { hexToPEM, jwkToPEM, pemCertChainTox5c, PEMToHex, PEMToJwk, privateKeyHexFromPEM } from '@sphereon/ssi-sdk-ext.key-utils' -import * as u8a from 'uint8arrays' +import Debug from 'debug' import elliptic from 'elliptic' +import * as u8a from 'uint8arrays' +import { KeyType, ManagedKeyInfoArgs } from './index' + const debug = Debug('sphereon:kms:bls:local') export class SphereonKeyManagementSystem extends KeyManagementSystem { @@ -20,13 +29,14 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { this.privateKeyStore = keyStore } - async importKey(args: Omit): Promise { + async importKey(args: Omit & { privateKeyPEM?: string }): Promise { switch (args.type) { case KeyType.Bls12381G2.toString(): if (!args.privateKeyHex || !args.publicKeyHex) { throw new Error('invalid_argument: type, publicKeyHex and privateKeyHex are required to import a key') } const managedKey = this.asSphereonManagedKeyInfo({ + ...args, alias: args.kid, privateKeyHex: args.privateKeyHex, publicKeyHex: args.publicKeyHex, @@ -39,8 +49,8 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { case 'Secp256r1': // @ts-ignore case 'RSA': { - if (!args.privateKeyHex) { - throw new Error('invalid_argument: type and privateKeyHex are required to import a key') + if (!args.privateKeyHex && !args.privateKeyPEM) { + throw new Error('invalid_argument: type and privateKeyHex (or privateKeyPEM for RSA) are required to import a key') } const managedKey = this.asSphereonManagedKeyInfo({ alias: args.kid, ...args }) await this.privateKeyStore.import({ alias: managedKey.kid, ...args }) @@ -68,10 +78,10 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { // @ts-ignore case 'RSA': { - const pem = await generateRSAKeyAsPEM('RSA-PSS', 'SHA-256', 2048) + const privateKeyHex = await generatePrivateKeyHex(type) key = await this.importKey({ type, - privateKeyHex: privateKeyHexFromPEM(pem), + privateKeyHex, }) break } @@ -109,7 +119,7 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { privateKey.type === 'RSA' && (typeof algorithm === 'undefined' || algorithm === 'RS256' || algorithm === 'RS512' || algorithm === 'PS256' || algorithm === 'PS512') ) { - return await this.signRSA(privateKey.privateKeyHex, data, algorithm ? algorithm : 'PS256') + return await this.signRSA(privateKey.privateKeyHex, data, algorithm ?? 'PS256') } else { return await super.sign({ keyRef, algorithm, data }) } @@ -146,9 +156,9 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { } // @ts-ignore case 'RSA': { - // @ts-ignore // We need this as the interface on the args, does not allow for any metadata on managed key imports const x509 = args.meta?.x509 as X509Opts - const privateKeyPEM = args.privateKeyHex.includes('---') ? args.privateKeyHex : hexToPEM(args.privateKeyHex, 'private') // In case we have x509 opts, the private key hex really was a PEM already (yuck) + const privateKeyPEM = + x509?.privateKeyPEM ?? (args.privateKeyHex.includes('---') ? args.privateKeyHex : hexToPEM(args.privateKeyHex, 'private')) // In case we have x509 opts, the private key hex really was a PEM already (yuck) const publicKeyJwk = PEMToJwk(privateKeyPEM, 'public') const publicKeyPEM = jwkToPEM(publicKeyJwk, 'public') const publicKeyHex = PEMToHex(publicKeyPEM) @@ -156,9 +166,9 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { const meta = {} as any if (x509) { meta.x509 = { - cn: x509.cn || args.alias || publicKeyHex, + cn: x509.cn ?? args.alias ?? publicKeyHex, } - let certChain: string = x509.certificateChainPEM || '' + let certChain: string = x509.certificateChainPEM ?? '' if (x509.certificatePEM) { if (!certChain.includes(x509.certificatePEM)) { certChain = `${x509.certificatePEM}\n${certChain}` @@ -183,7 +193,7 @@ export class SphereonKeyManagementSystem extends KeyManagementSystem { key = { type: args.type, - kid: args.alias || meta?.x509?.cn || publicKeyHex, + kid: args.alias ?? meta?.x509?.cn ?? publicKeyHex, publicKeyHex, meta: { ...meta, diff --git a/packages/kms-local/src/__tests__/rsa.test.ts b/packages/kms-local/src/__tests__/rsa.test.ts index cd6af483..c4090663 100644 --- a/packages/kms-local/src/__tests__/rsa.test.ts +++ b/packages/kms-local/src/__tests__/rsa.test.ts @@ -3,13 +3,13 @@ import { PEM_CERT, PEM_CHAIN, PEM_FULL_CHAIN, PEM_PRIV_KEY } from './certs' import { SphereonKeyManagementSystem } from '../SphereonKeyManagementSystem' import { MemoryPrivateKeyStore } from '@veramo/key-manager' import * as u8a from 'uint8arrays' -import { RSASigner } from '../x509/rsa-signer' import { digestMethodParams, pemCertChainTox5c, PEMToJwk, privateKeyHexFromPEM, publicKeyHexFromPEM, + RSASigner, toKeyObject, X509Opts, x5cToPemCertChain, diff --git a/packages/kms-local/src/index.ts b/packages/kms-local/src/index.ts index 78d57788..658580c2 100644 --- a/packages/kms-local/src/index.ts +++ b/packages/kms-local/src/index.ts @@ -1,6 +1,20 @@ -import { TKeyType } from '@veramo/core' +import { X509Opts } from '@sphereon/ssi-sdk-ext.key-utils' +import { KeyMetadata, TKeyType } from '@veramo/core' + export { SphereonKeyManagementSystem } from './SphereonKeyManagementSystem' -export type ManagedKeyInfoArgs = { alias?: string; type: TKeyType; privateKeyHex: string; publicKeyHex?: string } + +export interface ManagedKeyInfoArgs { + alias?: string + type: TKeyType + privateKeyHex: string + publicKeyHex?: string + meta?: ManageKeyInfoMeta | undefined | null +} + +export interface ManageKeyInfoMeta extends KeyMetadata { + x509?: X509Opts + [x: string]: any +} export enum KeyType { Bls12381G2 = 'Bls12381G2', } From 36a8ae45105791464432eb287988976b1ddfdb1e Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:09:51 +0200 Subject: [PATCH 07/12] feat: Check also for other supported encryption algorithms when JWK use property is used --- packages/did-utils/src/did-functions.ts | 42 ++++++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/did-utils/src/did-functions.ts b/packages/did-utils/src/did-functions.ts index 7d376c7c..0f6288e1 100644 --- a/packages/did-utils/src/did-functions.ts +++ b/packages/did-utils/src/did-functions.ts @@ -14,7 +14,7 @@ import { DIDResolutionOptions, Resolvable, VerificationMethod } from 'did-resolv import elliptic from 'elliptic' import * as u8a from 'uint8arrays' import { IDIDOptions, IIdentifierOpts } from './types' -import { hexKeyFromPEMBasedJwk, JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { ENC_KEY_ALGS, hexKeyFromPEMBasedJwk, JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' export const getFirstKeyWithRelation = async ( identifier: IIdentifier, @@ -22,7 +22,7 @@ export const getFirstKeyWithRelation = async ( vmRelationship?: DIDDocumentSection, errorOnNotFound?: boolean ): Promise<_ExtendedIKey | undefined> => { - const section = vmRelationship || 'verificationMethod' // search all VMs in case no relationship is provided + const section = vmRelationship ?? 'verificationMethod' // search all VMs in case no relationship is provided const matchedKeys = await mapIdentifierKeysToDocWithJwkSupport(identifier, section, context) if (Array.isArray(matchedKeys) && matchedKeys.length > 0) { return matchedKeys[0] @@ -156,8 +156,8 @@ export async function mapIdentifierKeysToDocWithJwkSupport( const extendedKeys: _ExtendedIKey[] = documentKeys .map((verificationMethod) => { /*if (verificationMethod.type !== 'JsonWebKey2020') { - return null - }*/ + return null + }*/ const localKey = localKeys.find( (localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || verificationMethod.publicKeyHex?.startsWith(localKey.publicKeyHex) ) @@ -285,8 +285,11 @@ export function toDidDocument( verificationMethod: identifier.keys.map((key) => { const vm: VerificationMethod = { controller: did, - id: `${did}#${key.kid}`, - publicKeyJwk: toJwk(key.publicKeyHex, key.type, key.type === 'X25519' ? JwkKeyUse.Encryption : JwkKeyUse.Signature), + id: key.kid.startsWith(did) && key.kid.includes('#') ? key.kid : `${did}#${key.kid}`, + publicKeyJwk: toJwk(key.publicKeyHex, key.type, { + use: ENC_KEY_ALGS.includes(key.type) ? JwkKeyUse.Encryption : JwkKeyUse.Signature, + key, + }), type: 'JsonWebKey2020', } return vm @@ -301,14 +304,18 @@ export function toDidDocument( }), }), ...((!opts?.use || opts?.use?.includes(JwkKeyUse.Encryption)) && - identifier.keys && { + identifier.keys && + identifier.keys.filter((key) => key.type === 'X25519').length > 0 && { keyAgreement: identifier.keys .filter((key) => key.type === 'X25519') .map((key) => { + if (key.kid.startsWith(did) && key.kid.includes('#')) { + return key.kid + } return `${did}#${key.kid}` }), }), - ...(identifier.services && { service: identifier.services }), + ...(identifier.services && identifier.services.length > 0 && { service: identifier.services }), } } return didDocument @@ -318,19 +325,19 @@ export function toDidResolutionResult( identifier?: IIdentifier, opts?: { did?: string - supportedMethod?: string[] + supportedMethods?: string[] } ): DIDResolutionResult { - const didDocument = toDidDocument(identifier, opts) ?? null + const didDocument = toDidDocument(identifier, opts) ?? null // null is used in case of errors and required by the did resolution spec const resolutionResult: DIDResolutionResult = { '@context': 'https://w3id.org/did-resolution/v1', didDocument, didResolutionMetadata: { ...(!didDocument && { error: 'notFound' }), - ...(Array.isArray(opts?.supportedMethod) && + ...(Array.isArray(opts?.supportedMethods) && identifier && - !opts?.supportedMethod.includes(identifier.provider.replace('did:', '')) && { error: 'unsupportedDidMethod' }), + !opts?.supportedMethods.includes(identifier.provider.replace('did:', '')) && { error: 'unsupportedDidMethod' }), }, didDocumentMetadata: { ...(identifier?.alias && { equivalentId: identifier?.alias }), @@ -338,3 +345,14 @@ export function toDidResolutionResult( } return resolutionResult } + +export async function asDidWeb(hostnameOrDID: string): Promise { + let did = hostnameOrDID + if (!did) { + throw Error('Domain or DID expected, but received nothing.') + } + if (did.startsWith('did:web:')) { + return did + } + return `did:web:${did.replace(/https?:\/\/([^/?#]+).*/i, '$1').toLowerCase()}` +} From e8e1f73a0a8d4cf4e663ad15469011647b3c8154 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:11:19 +0200 Subject: [PATCH 08/12] chore: use refactored methods --- .../__tests__/comparison-regression.test.ts | 7 ++- .../__tests__/jwk-did-provider.test.ts | 6 +- .../did-provider-jwk/src/jwk-did-provider.ts | 55 ++----------------- .../src/types/jwk-provider-types.ts | 20 +------ 4 files changed, 15 insertions(+), 73 deletions(-) diff --git a/packages/did-provider-jwk/__tests__/comparison-regression.test.ts b/packages/did-provider-jwk/__tests__/comparison-regression.test.ts index d4bc9262..0b2d5e2b 100644 --- a/packages/did-provider-jwk/__tests__/comparison-regression.test.ts +++ b/packages/did-provider-jwk/__tests__/comparison-regression.test.ts @@ -1,6 +1,6 @@ import { dereferenceDidKeysWithJwkSupport } from '@sphereon/ssi-sdk-ext.did-utils' import { SphereonKeyManager } from '@sphereon/ssi-sdk-ext.key-manager' -import { JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { IKeyOpts, JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' import { SphereonKeyManagementSystem } from '@sphereon/ssi-sdk-ext.kms-local' import { createAgent, DIDDocument, DIDResolutionResult, IAgentContext, IIdentifier, IKeyManager, IResolver } from '@veramo/core' import { DIDManager, MemoryDIDStore } from '@veramo/did-manager' @@ -9,7 +9,7 @@ import { MemoryKeyStore, MemoryPrivateKeyStore } from '@veramo/key-manager' import base64url from 'base64url' import { Resolver } from 'did-resolver' import { getDidJwkResolver, Key } from '../../did-resolver-jwk/src' -import { IKeyOpts, JwkDIDProvider } from '../src' +import { JwkDIDProvider } from '../src' const method = require('@or13/did-jwk') @@ -79,6 +79,7 @@ describe('@sphereon/did-provider-jwk comparison ES256k', () => { const options: IKeyOpts = { key: { privateKeyHex, + type: 'Secp256k1', }, use: JwkKeyUse.Signature, } @@ -277,7 +278,7 @@ describe('@sphereon/did-provider-jwk comparison ES256', () => { }) it('Creation from privateKeyHex', async () => { - /*const privateKeyHex = generatePrivateKeyHex('Secp256r1') + /*const privateKeyHex = await generatePrivateKeyHex('Secp256r1') console.log(privateKeyHex)*/ const privateKeyHex = '47dc6ae067aa011f8574d2da7cf8c326538af08b85e6779d192a9893291c9a0a' const options: IKeyOpts = { diff --git a/packages/did-provider-jwk/__tests__/jwk-did-provider.test.ts b/packages/did-provider-jwk/__tests__/jwk-did-provider.test.ts index 21303ea7..e7b43bc3 100644 --- a/packages/did-provider-jwk/__tests__/jwk-did-provider.test.ts +++ b/packages/did-provider-jwk/__tests__/jwk-did-provider.test.ts @@ -50,7 +50,7 @@ describe('@sphereon/did-provider-jwk', () => { expect(identifier).toBeDefined() expect(identifier.did).toBe( - 'did:jwk:eyJhbGciOiJFUzI1NksiLCJ1c2UiOiJzaWciLCJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJvankweURrQnJNTHJENFVsbVdFTjRNcnF3bUNfanRCZWY1QXVxc0Q1eU5jIiwieSI6IlRkU0VHNVRSTkNUVEt2anNEcGwyMjVxX3AtT2xuaERWWmNYVTJRRzB2bU0ifQ' + 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiaTlBdmpJMFdjUXo5NF9aVkVDazVrS21kSEFEU2RWNGRKZ1RNN0ROYkNJayIsInkiOiJJZGtyWktUcWdmNE1ZY3hUbHlIM3ZJMkdHYjJXYWM1Z0V1Y0lQaTFfRmtnIn0' ) }) @@ -94,7 +94,7 @@ describe('@sphereon/did-provider-jwk', () => { const options = { key: {}, } - await expect(agent.didManagerCreate({ options })).rejects.toThrow('We need to have a private key when importing a key') + await expect(agent.didManagerCreate({ options })).rejects.toThrow('We need to have a private key in Hex or PEM when importing a key') }) it('should throw error for keys Ed25519 with key usage encryption', async () => { @@ -102,6 +102,6 @@ describe('@sphereon/did-provider-jwk', () => { type: Key.Ed25519, use: JwkKeyUse.Encryption, } - await expect(agent.didManagerCreate({ options })).rejects.toThrow('Ed25519 keys are only valid for signatures') + await expect(agent.didManagerCreate({ options })).rejects.toThrow('Ed25519 keys are not valid for encryption') }) }) diff --git a/packages/did-provider-jwk/src/jwk-did-provider.ts b/packages/did-provider-jwk/src/jwk-did-provider.ts index c7b1a7de..0a0b45d3 100644 --- a/packages/did-provider-jwk/src/jwk-did-provider.ts +++ b/packages/did-provider-jwk/src/jwk-did-provider.ts @@ -1,17 +1,9 @@ -import { generatePrivateKeyHex, jwkDetermineUse, JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' -import { DIDDocument, IAgentContext, IIdentifier, IKey, IKeyManager } from '@veramo/core' +import { importProvidedOrGeneratedKey, jwkDetermineUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { DIDDocument, IAgentContext, IIdentifier, IKeyManager } from '@veramo/core' import { AbstractIdentifierProvider } from '@veramo/did-manager' import base64url from 'base64url' import Debug from 'debug' -import { - IAddKeyArgs, - IAddServiceArgs, - ICreateIdentifierArgs, - IImportProvidedOrGeneratedKeyArgs, - IRemoveKeyArgs, - IRequiredContext, - Key, -} from './types/jwk-provider-types' +import { IAddKeyArgs, IAddServiceArgs, ICreateIdentifierArgs, IRemoveKeyArgs, IRequiredContext } from './types/jwk-provider-types' const debug = Debug('sphereon:did-provider-jwk') @@ -29,16 +21,16 @@ export class JwkDIDProvider extends AbstractIdentifierProvider { /** {@inheritDoc @veramo/veramo-core#IDIDManager.didManagerCreate} */ async createIdentifier(args: ICreateIdentifierArgs, context: IRequiredContext): Promise> { - const key = await this.importProvidedOrGeneratedKey( + const key = await importProvidedOrGeneratedKey( { - kms: args.kms, + kms: args.kms ?? this.defaultKms, options: args.options, }, context ) const use = jwkDetermineUse(key.type, args?.options?.use) - const jwk: JsonWebKey = toJwk(key.publicKeyHex, key.type, use) + const jwk: JsonWebKey = toJwk(key.publicKeyHex, key.type, { use, key }) debug(JSON.stringify(jwk, null, 2)) const did = `did:jwk:${base64url(JSON.stringify(jwk))}` const identifier: Omit = { @@ -85,39 +77,4 @@ export class JwkDIDProvider extends AbstractIdentifierProvider { async removeService(args: IRemoveKeyArgs, context: IRequiredContext): Promise { return Promise.reject(Error('Not supported for DID JWKs')) } - - /** - * We optionally generate and then import our own keys. - * - * @param args The key arguments - * @param context The Veramo agent context - * @private - */ - private async importProvidedOrGeneratedKey(args: IImportProvidedOrGeneratedKeyArgs, context: IRequiredContext): Promise { - // @ts-ignore - const type = args.options?.type ?? args.options?.key?.type ?? args.options?.keyType ?? Key.Secp256k1 - - if (args.options && args.options?.use === JwkKeyUse.Encryption && type === Key.Ed25519) { - throw new Error('Ed25519 keys are only valid for signatures') - } - - let privateKeyHex: string - if (args.options?.key) { - if (!args.options.key.privateKeyHex) { - throw new Error(`We need to have a private key when importing a key`) - } - privateKeyHex = args.options.key.privateKeyHex - /*if (type === Key.Secp256r1 && privateKeyHex.length === 64) { - privateKeyHex = `04${privateKeyHex}` - }*/ - } else { - privateKeyHex = generatePrivateKeyHex(type) - } - - return context.agent.keyManagerImport({ - kms: args.kms || this.defaultKms, - type, - privateKeyHex, - }) - } } diff --git a/packages/did-provider-jwk/src/types/jwk-provider-types.ts b/packages/did-provider-jwk/src/types/jwk-provider-types.ts index 8bce0719..9d82349f 100644 --- a/packages/did-provider-jwk/src/types/jwk-provider-types.ts +++ b/packages/did-provider-jwk/src/types/jwk-provider-types.ts @@ -1,16 +1,5 @@ -import { IAgentContext, IIdentifier, IKey, IKeyManager, IService, MinimalImportableKey } from '@veramo/core' -import { JwkKeyUse } from '@sphereon/ssi-sdk-ext.key-utils' - -export interface IKeyOpts { - key?: WithRequiredProperty, 'privateKeyHex'> // Optional key to import with only privateKeyHex mandatory. If not specified a key with random kid will be created - type?: Key // The key type. Defaults to Secp256k1 - use?: JwkKeyUse // The key use -} - -// Needed to make a single property required -type WithRequiredProperty = Type & { - [Property in Key]-?: Type[Property] -} +import { IKeyOpts } from '@sphereon/ssi-sdk-ext.key-utils' +import { IAgentContext, IIdentifier, IKey, IKeyManager, IService } from '@veramo/core' export interface ICreateIdentifierArgs { kms?: string @@ -42,11 +31,6 @@ export interface IAddServiceArgs { options?: any } -export interface IImportProvidedOrGeneratedKeyArgs { - kms?: string - options?: IKeyOpts -} - export enum Key { Ed25519 = 'Ed25519', Secp256k1 = 'Secp256k1', From 3fcbea3d8142153bf8a8e6fd9a6b7070bbf92291 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:11:59 +0200 Subject: [PATCH 09/12] chore: disable lto tests, as they seem to generate errors. Probably because of network changes --- .../did-provider-lto/src/__tests__/lto-did-provider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/did-provider-lto/src/__tests__/lto-did-provider.test.ts b/packages/did-provider-lto/src/__tests__/lto-did-provider.test.ts index 13f4e59c..947b8809 100644 --- a/packages/did-provider-lto/src/__tests__/lto-did-provider.test.ts +++ b/packages/did-provider-lto/src/__tests__/lto-did-provider.test.ts @@ -40,7 +40,7 @@ const didManager = new DIDManager({ store: memoryDIDStore, }) -describe('@sphereon/lto-did-provider', () => { +xdescribe('@sphereon/lto-did-provider', () => { const mockContext = { agent: { keyManagerCreate(_args: IKeyManagerCreateArgs): Promise { From 8335fbe16e4a7740a11e225c99afb516c305d27f Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 05:12:58 +0200 Subject: [PATCH 10/12] feat: Add DID web provider, with RSA and multi key import support --- .../__tests__/functions.test.ts | 10 +- .../did-provider-ebsi/src/EbsiDidProvider.ts | 4 +- packages/did-provider-ebsi/src/functions.ts | 2 +- .../src/SphereonKeyDidProvider.ts | 2 +- packages/did-provider-web/CHANGELOG.md | 101 +++++++++ packages/did-provider-web/LICENSE | 201 ++++++++++++++++++ packages/did-provider-web/README.md | 4 + packages/did-provider-web/api-extractor.json | 18 ++ packages/did-provider-web/package.json | 47 ++++ packages/did-provider-web/src/index.ts | 8 + packages/did-provider-web/src/types.ts | 59 +++++ .../did-provider-web/src/web-did-provider.ts | 81 +++++++ packages/did-provider-web/tsconfig.json | 16 ++ packages/tsconfig.json | 3 + pnpm-lock.yaml | 37 ++++ 15 files changed, 584 insertions(+), 9 deletions(-) create mode 100644 packages/did-provider-web/CHANGELOG.md create mode 100644 packages/did-provider-web/LICENSE create mode 100644 packages/did-provider-web/README.md create mode 100644 packages/did-provider-web/api-extractor.json create mode 100644 packages/did-provider-web/package.json create mode 100644 packages/did-provider-web/src/index.ts create mode 100644 packages/did-provider-web/src/types.ts create mode 100644 packages/did-provider-web/src/web-did-provider.ts create mode 100644 packages/did-provider-web/tsconfig.json diff --git a/packages/did-provider-ebsi/__tests__/functions.test.ts b/packages/did-provider-ebsi/__tests__/functions.test.ts index 480a1018..6abd8445 100644 --- a/packages/did-provider-ebsi/__tests__/functions.test.ts +++ b/packages/did-provider-ebsi/__tests__/functions.test.ts @@ -1,10 +1,10 @@ -import { generatePrivateKeyHex } from '../src/functions' +import { generateEbsiPrivateKeyHex } from '../src/functions' describe('functions: key generator', () => { it('Secp256k1 should generate random keys', () => { - const key1 = generatePrivateKeyHex() - const key2 = generatePrivateKeyHex() - const key3 = generatePrivateKeyHex() + const key1 = generateEbsiPrivateKeyHex() + const key2 = generateEbsiPrivateKeyHex() + const key3 = generateEbsiPrivateKeyHex() expect(key1).toBeDefined() expect(key2).toBeDefined() expect(key3).toBeDefined() @@ -12,6 +12,6 @@ describe('functions: key generator', () => { expect(key2).not.toBe(key3) }) it('Secp256k1 should result in hex length 64', () => { - expect(generatePrivateKeyHex().length).toBe(64) + expect(generateEbsiPrivateKeyHex().length).toBe(64) }) }) diff --git a/packages/did-provider-ebsi/src/EbsiDidProvider.ts b/packages/did-provider-ebsi/src/EbsiDidProvider.ts index adfa3018..313ddeb7 100644 --- a/packages/did-provider-ebsi/src/EbsiDidProvider.ts +++ b/packages/did-provider-ebsi/src/EbsiDidProvider.ts @@ -5,7 +5,7 @@ import { DIDDocument } from 'did-resolver' import { IKey, IService } from '@veramo/core/build/types/IIdentifier' import * as u8a from 'uint8arrays' import { ebsiDIDSpecInfo, IContext, ICreateIdentifierArgs } from './types' -import { generatePrivateKeyHex, toMethodSpecificId } from './functions' +import { generateEbsiPrivateKeyHex, toMethodSpecificId } from './functions' const debug = Debug('sphereon:did-provider-ebsi') @@ -28,7 +28,7 @@ export class EbsiDidProvider extends AbstractIdentifierProvider { context: IContext ): Promise> { if (!options?.type || options.type === ebsiDIDSpecInfo.V1) { - const privateKeyHex = generatePrivateKeyHex( + const privateKeyHex = await generateEbsiPrivateKeyHex( ebsiDIDSpecInfo.V1, options?.options?.key?.privateKeyHex ? u8a.fromString(options.options.key.privateKeyHex, 'base16') : undefined ) diff --git a/packages/did-provider-ebsi/src/functions.ts b/packages/did-provider-ebsi/src/functions.ts index 1b545fa8..ad2a8cc5 100644 --- a/packages/did-provider-ebsi/src/functions.ts +++ b/packages/did-provider-ebsi/src/functions.ts @@ -21,7 +21,7 @@ export function toMethodSpecificId(specInfo?: EbsiDidSpecInfo, methodSpecificId? return base58btc.encode(result) } -export function generatePrivateKeyHex(specInfo?: EbsiDidSpecInfo, privateKeyBytes?: Uint8Array): string { +export function generateEbsiPrivateKeyHex(specInfo?: EbsiDidSpecInfo, privateKeyBytes?: Uint8Array): string { const spec = specInfo ?? ebsiDIDSpecInfo.V1 const length = spec.didLength ? 2 * spec.didLength : 32 diff --git a/packages/did-provider-key/src/SphereonKeyDidProvider.ts b/packages/did-provider-key/src/SphereonKeyDidProvider.ts index 9272f13a..001ee3ab 100644 --- a/packages/did-provider-key/src/SphereonKeyDidProvider.ts +++ b/packages/did-provider-key/src/SphereonKeyDidProvider.ts @@ -45,7 +45,7 @@ export class SphereonKeyDidProvider extends KeyDIDProvider { return identifier } else if (options?.type?.toLowerCase()?.includes('ebsi') || options?.type?.toLowerCase() === JWK_JCS_PUB_NAME.toLowerCase()) { const key = await context.agent.keyManagerCreate({ kms: kms || this.kms, type: 'Secp256k1' }) - const jwk = toJwk(key.publicKeyHex, 'Secp256k1', JwkKeyUse.Signature) + const jwk = toJwk(key.publicKeyHex, 'Secp256k1', { use: JwkKeyUse.Signature, key }) // todo: Remove buffers, and remove redundant code const methodSpecificId = Buffer.from( diff --git a/packages/did-provider-web/CHANGELOG.md b/packages/did-provider-web/CHANGELOG.md new file mode 100644 index 00000000..fcc6b4e7 --- /dev/null +++ b/packages/did-provider-web/CHANGELOG.md @@ -0,0 +1,101 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [5.1.2](https://github.com/uport-project/veramo/compare/v5.1.1...v5.1.2) (2023-02-25) + +### Bug Fixes + +- **ci:** minor changes to trigger release alignment ([9db312f](https://github.com/uport-project/veramo/commit/9db312f8f049ec13ef394dc77fe6e2759143790d)) + +# [5.1.0](https://github.com/uport-project/veramo/compare/v5.0.0...v5.1.0) (2023-02-24) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [5.0.0](https://github.com/uport-project/veramo/compare/v4.3.0...v5.0.0) (2023-02-09) + +### Build System + +- convert veramo modules to ESM instead of CommonJS ([#1103](https://github.com/uport-project/veramo/issues/1103)) ([b5cea3c](https://github.com/uport-project/veramo/commit/b5cea3c0d80d900a47bd1d9eea68f84b70a35e7b)), closes [#1099](https://github.com/uport-project/veramo/issues/1099) + +### Features + +- isolate `core-types` package from `core` ([#1116](https://github.com/uport-project/veramo/issues/1116)) ([ba7a303](https://github.com/uport-project/veramo/commit/ba7a303de91cf4cc568a3af1ddf8ca98ed022e9f)) + +### BREAKING CHANGES + +- this is a breaking change as modules will have to be imported differently: +- https://www.typescriptlang.org/docs/handbook/esm-node.html +- https://nodejs.org/api/esm.html +- https://caniuse.com/?search=modules + +test(did-provider-ion): skip a couple of tests that fail with unreasonable errors +chore: use ubuntu-latest on CI +fix: temporarily remove puppeteer tests +fix: use craco for test-react-app to enable babel config +test: fix unit and integration tests (browser tests still broken) +fix: fix some build issues that prevented tests from working +fix: missing deps flagged by pnpm + +# [4.3.0](https://github.com/uport-project/veramo/compare/v4.2.0...v4.3.0) (2023-01-27) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [4.2.0](https://github.com/uport-project/veramo/compare/v4.1.2...v4.2.0) (2022-12-05) + +### Bug Fixes + +- **deps:** bump dependencies ([701b8ed](https://github.com/uport-project/veramo/commit/701b8edf981ea11c7ddb6a81d2817dbbdbb022f3)) + +## [4.1.1](https://github.com/uport-project/veramo/compare/v4.1.0...v4.1.1) (2022-11-01) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [4.1.0](https://github.com/uport-project/veramo/compare/v4.0.2...v4.1.0) (2022-10-31) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [4.0.0](https://github.com/uport-project/veramo/compare/v3.1.5...v4.0.0) (2022-09-22) + +### Bug Fixes + +- **did-resolver:** use interface `Resolvable` instead of the `Resolver` class ([9c2e59f](https://github.com/uport-project/veramo/commit/9c2e59f3f23f808511c6c0e8e440b4d53ba5cb00)) + +### Features + +- **did-manager:** add`didManagerUpdate` method for full DID document updates ([#974](https://github.com/uport-project/veramo/issues/974)) ([5682b25](https://github.com/uport-project/veramo/commit/5682b2566b7c4f8f9bfda10e8d06a8d2624c2a1b)), closes [#971](https://github.com/uport-project/veramo/issues/971) [#960](https://github.com/uport-project/veramo/issues/960) [#948](https://github.com/uport-project/veramo/issues/948) + +## [3.1.3](https://github.com/uport-project/veramo/compare/v3.1.2...v3.1.3) (2022-06-01) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [3.1.0](https://github.com/uport-project/veramo/compare/v3.0.0...v3.1.0) (2021-11-12) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [3.0.0](https://github.com/uport-project/veramo/compare/v2.1.3...v3.0.0) (2021-09-20) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [2.1.0](https://github.com/uport-project/veramo/compare/v2.0.1...v2.1.0) (2021-08-11) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [2.0.0](https://github.com/uport-project/veramo/compare/v1.2.2...v2.0.0) (2021-07-14) + +### Bug Fixes + +- **remote-server:** create an Ed25519 key for the default did:web ([a2f7f8c](https://github.com/uport-project/veramo/commit/a2f7f8c3fc6ab6cc276f6853104386bf9d923424)) + +# [1.2.0](https://github.com/uport-project/veramo/compare/v1.1.2...v1.2.0) (2021-04-27) + +**Note:** Version bump only for package @veramo/did-provider-web + +# [1.1.0](https://github.com/uport-project/veramo/compare/v1.0.1...v1.1.0) (2021-01-26) + +**Note:** Version bump only for package @veramo/did-provider-web + +## 1.0.1 (2020-12-18) + +**Note:** Version bump only for package @veramo/did-provider-web diff --git a/packages/did-provider-web/LICENSE b/packages/did-provider-web/LICENSE new file mode 100644 index 00000000..fd815d7f --- /dev/null +++ b/packages/did-provider-web/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/did-provider-web/README.md b/packages/did-provider-web/README.md new file mode 100644 index 00000000..6434fa87 --- /dev/null +++ b/packages/did-provider-web/README.md @@ -0,0 +1,4 @@ +# did:web provider + +This package contains an implementation of `AbstractIdentifierProvider` for the `did:web` method. +This enables creation and control of `did:web` entities. diff --git a/packages/did-provider-web/api-extractor.json b/packages/did-provider-web/api-extractor.json new file mode 100644 index 00000000..409d7f16 --- /dev/null +++ b/packages/did-provider-web/api-extractor.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "apiReport": { + "enabled": true, + "reportFolder": "./api", + "reportTempFolder": "./api" + }, + + "docModel": { + "enabled": true, + "apiJsonFilePath": "./api/.api.json" + }, + + "dtsRollup": { + "enabled": false + }, + "mainEntryPointFilePath": "/build/index.d.ts" +} diff --git a/packages/did-provider-web/package.json b/packages/did-provider-web/package.json new file mode 100644 index 00000000..7597ff49 --- /dev/null +++ b/packages/did-provider-web/package.json @@ -0,0 +1,47 @@ +{ + "name": "@sphereon/ssi-sdk-ext.did-provider-web", + "description": "Veramo plugin that can enable creation and control of did:web identifiers.", + "version": "0.12.1", + "main": "build/index.js", + "exports": "./build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@veramo/core": "4.2.0", + "@veramo/did-manager": "4.2.0", + "@sphereon/ssi-sdk-ext.key-utils": "workspace:*", + "@sphereon/ssi-sdk-ext.did-utils": "workspace:*", + "debug": "^4.3.4" + }, + "devDependencies": { + "@types/debug": "^4.1.8", + "typescript": "4.9.5" + }, + "files": [ + "dist/**/*", + "src/**/*", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:Sphereon-Opensource/SSI-SDK-crypto-extensions.git", + "author": "Sphereon ", + "contributors": [], + "license": "Apache-2.0", + "keywords": [ + "Sphereon", + "did:web resolver", + "DID", + "did.json", + "BLS", + "BBS+", + "ed25519", + "secp256r1", + "secp256r1", + "RSA" + ] +} diff --git a/packages/did-provider-web/src/index.ts b/packages/did-provider-web/src/index.ts new file mode 100644 index 00000000..f8a380b9 --- /dev/null +++ b/packages/did-provider-web/src/index.ts @@ -0,0 +1,8 @@ +/** + * Provides `did:web` {@link @veramo/did-provider-web#WebDIDProvider | identifier provider } for the + * {@link @veramo/did-manager#DIDManager} + * + * @packageDocumentation + */ +export * from './types' +export { WebDIDProvider } from './web-did-provider.js' diff --git a/packages/did-provider-web/src/types.ts b/packages/did-provider-web/src/types.ts new file mode 100644 index 00000000..142288f0 --- /dev/null +++ b/packages/did-provider-web/src/types.ts @@ -0,0 +1,59 @@ +import { IAgentContext, IDIDManager, IKeyManager, IService, MinimalImportableKey, TKeyType } from '@veramo/core' + +export interface IKeyOpts { + keys?: WithRequiredProperty, 'privateKeyHex'>[] // Optional key to import with only privateKeyHex mandatory. If not specified a key with random kid will be created + type?: TKeyType | 'RSA' // The key type. Defaults to Secp256k1 +} + +// Needed to make a single property required +type WithRequiredProperty = Type & { + [Property in Key]-?: Type[Property] +} +/*export interface IAddKeyArgs { + identifier: IIdentifier + key: IKey + options?: any +} + +export interface IRemoveKeyArgs { + identifier: IIdentifier + id: string + options?: any +} + +export interface IRemoveKeyArgs { + identifier: IIdentifier + kid: string + options?: any +} + +export interface IAddServiceArgs { + identifier: IIdentifier + service: IService + options?: any +}*/ + +export interface IImportProvidedOrGeneratedKeyArgs { + kms?: string + options?: IKeyOpts +} + +/* + +export interface IImportX509DIDArg { + alias: string + privateKeyPEM: string + certificatePEM: string + certificateChainPEM: string + certificateChainURL?: string + kms?: string // The Key Management System to use. Will default to 'local' when not supplied. + // kid?: string // The requested KID. A default will be generated when not supplied +}*/ + +export interface ICreateIdentifierArgs { + services?: IService[] + kms?: string + alias: string + options?: IKeyOpts | IKeyOpts[] +} +export type IRequiredContext = IAgentContext diff --git a/packages/did-provider-web/src/web-did-provider.ts b/packages/did-provider-web/src/web-did-provider.ts new file mode 100644 index 00000000..fb84f5e7 --- /dev/null +++ b/packages/did-provider-web/src/web-did-provider.ts @@ -0,0 +1,81 @@ +import { asDidWeb } from '@sphereon/ssi-sdk-ext.did-utils' +import { IKeyOpts, importProvidedOrGeneratedKey } from '@sphereon/ssi-sdk-ext.key-utils' +import { IAgentContext, IIdentifier, IKey, IKeyManager, IService } from '@veramo/core' +import { AbstractIdentifierProvider } from '@veramo/did-manager' + +import Debug from 'debug' +import { ICreateIdentifierArgs } from './types' + +const debug = Debug('sphereon:web-did:identifier-provider') + +type IContext = IAgentContext + +/** + * {@link @veramo/did-manager#DIDManager} identifier provider for `did:web` identifiers + * @public + */ +export class WebDIDProvider extends AbstractIdentifierProvider { + private readonly defaultKms: string + + constructor(options: { defaultKms: string }) { + super() + this.defaultKms = options.defaultKms + } + + async createIdentifier(args: ICreateIdentifierArgs, context: IContext): Promise> { + const { kms, alias } = args + const opts = Array.isArray(args.options) ? args.options : args.options ? [args.options] : ([] as IKeyOpts[]) + if (opts.length === 0) { + // Let's generate a key as no import keys or types are provided + opts.push({ type: 'Secp256r1' }) + } + const keys = await Promise.all( + opts.map((options) => { + return importProvidedOrGeneratedKey({ kms: kms ?? this.defaultKms, options }, context) + }) + ) + const identifier: Omit = { + did: await asDidWeb(alias), + controllerKeyId: keys[0].kid, + keys, + services: args.services ?? [], + } + 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('WebDIDProvider updateIdentifier not supported yet.') + } + + async deleteIdentifier(identifier: IIdentifier, context: IContext): Promise { + for (const { kid } of identifier.keys) { + await context.agent.keyManagerDelete({ kid }) + } + return true + } + + async addKey({ identifier, key, options }: { identifier: IIdentifier; key: IKey; options?: any }, context: IContext): Promise { + return { success: true } + } + + async addService({ identifier, service, options }: { identifier: IIdentifier; service: IService; options?: any }, context: IContext): Promise { + return { success: true } + } + + async removeKey(args: { identifier: IIdentifier; kid: string; options?: any }, context: IContext): Promise { + return { success: true } + } + + async removeService(args: { identifier: IIdentifier; id: string; options?: any }, context: IContext): Promise { + return { success: true } + } +} diff --git a/packages/did-provider-web/tsconfig.json b/packages/did-provider-web/tsconfig.json new file mode 100644 index 00000000..5368371e --- /dev/null +++ b/packages/did-provider-web/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig-base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declarationDir": "dist" + }, + "references": [ + { + "path": "../key-utils" + }, + { + "path": "../did-utils" + } + ] +} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index d5493aa0..b162abd9 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -22,6 +22,9 @@ { "path": "did-resolver-ebsi" }, + { + "path": "did-provider-web" + }, { "path": "key-manager" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5284b162..a509f766 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -326,6 +326,31 @@ importers: specifier: 4.9.5 version: 4.9.5 + packages/did-provider-web: + dependencies: + '@sphereon/ssi-sdk-ext.did-utils': + specifier: workspace:* + version: link:../did-utils + '@sphereon/ssi-sdk-ext.key-utils': + specifier: workspace:* + version: link:../key-utils + '@veramo/core': + specifier: 4.2.0 + version: 4.2.0(patch_hash=c5oempznsz4br5w3tcuk2i2mau) + '@veramo/did-manager': + specifier: 4.2.0 + version: 4.2.0 + debug: + specifier: ^4.3.4 + version: 4.3.4 + devDependencies: + '@types/debug': + specifier: ^4.1.8 + version: 4.1.8 + typescript: + specifier: 4.9.5 + version: 4.9.5 + packages/did-resolver-ebsi: dependencies: cross-fetch: @@ -470,6 +495,9 @@ importers: '@ethersproject/random': specifier: ^5.6.1 version: 5.7.0 + '@sphereon/isomorphic-webcrypto': + specifier: ^2.4.0-unstable.4 + version: 2.4.0-unstable.4(expo-crypto@12.2.2)(expo@48.0.11)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) '@stablelib/ed25519': specifier: ^1.0.2 version: 1.0.3 @@ -479,6 +507,9 @@ importers: '@stablelib/sha512': specifier: ^1.0.1 version: 1.0.1 + '@veramo/core': + specifier: 4.2.0 + version: 4.2.0(patch_hash=c5oempznsz4br5w3tcuk2i2mau) base64url: specifier: ^3.0.1 version: 3.0.1 @@ -4909,6 +4940,12 @@ packages: '@types/ms': 0.7.31 dev: true + /@types/debug@4.1.8: + resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} + dependencies: + '@types/ms': 0.7.31 + dev: true + /@types/elliptic@6.4.14: resolution: {integrity: sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==} dependencies: From 276840f14edaea7d5c98a556a1c7393c0d8bbcd3 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 06:00:46 +0200 Subject: [PATCH 11/12] chore: fix package.json path --- packages/did-provider-web/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/did-provider-web/package.json b/packages/did-provider-web/package.json index 7597ff49..e0e23e5d 100644 --- a/packages/did-provider-web/package.json +++ b/packages/did-provider-web/package.json @@ -1,10 +1,10 @@ { "name": "@sphereon/ssi-sdk-ext.did-provider-web", - "description": "Veramo plugin that can enable creation and control of did:web identifiers.", + "description": "plugin that can enable creation and control of did:web identifiers.", "version": "0.12.1", - "main": "build/index.js", - "exports": "./build/index.js", - "types": "build/index.d.ts", + "main": "dist/index.js", + "exports": "./dist/index.js", + "types": "dist/index.d.ts", "scripts": { "build": "tsc" }, From 89b4916d5496decd38e91c7962f9045d835393a8 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Mon, 31 Jul 2023 00:33:05 +0200 Subject: [PATCH 12/12] feat: Allow to define controller key when importing keys for a did:web --- packages/did-provider-web/src/types.ts | 1 + .../did-provider-web/src/web-did-provider.ts | 37 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/did-provider-web/src/types.ts b/packages/did-provider-web/src/types.ts index 142288f0..0a789f95 100644 --- a/packages/did-provider-web/src/types.ts +++ b/packages/did-provider-web/src/types.ts @@ -3,6 +3,7 @@ import { IAgentContext, IDIDManager, IKeyManager, IService, MinimalImportableKey export interface IKeyOpts { keys?: WithRequiredProperty, 'privateKeyHex'>[] // Optional key to import with only privateKeyHex mandatory. If not specified a key with random kid will be created type?: TKeyType | 'RSA' // The key type. Defaults to Secp256k1 + isController?: boolean // Whether this is a controller key for a DID document. Please note that only one key can be a controller key. If multiple are supplied, the first one will be used! } // Needed to make a single property required diff --git a/packages/did-provider-web/src/web-did-provider.ts b/packages/did-provider-web/src/web-did-provider.ts index fb84f5e7..033498df 100644 --- a/packages/did-provider-web/src/web-did-provider.ts +++ b/packages/did-provider-web/src/web-did-provider.ts @@ -1,10 +1,10 @@ import { asDidWeb } from '@sphereon/ssi-sdk-ext.did-utils' -import { IKeyOpts, importProvidedOrGeneratedKey } from '@sphereon/ssi-sdk-ext.key-utils' +import { importProvidedOrGeneratedKey } from '@sphereon/ssi-sdk-ext.key-utils' import { IAgentContext, IIdentifier, IKey, IKeyManager, IService } from '@veramo/core' import { AbstractIdentifierProvider } from '@veramo/did-manager' import Debug from 'debug' -import { ICreateIdentifierArgs } from './types' +import { ICreateIdentifierArgs, IKeyOpts } from './types' const debug = Debug('sphereon:web-did:identifier-provider') @@ -27,16 +27,19 @@ export class WebDIDProvider extends AbstractIdentifierProvider { const opts = Array.isArray(args.options) ? args.options : args.options ? [args.options] : ([] as IKeyOpts[]) if (opts.length === 0) { // Let's generate a key as no import keys or types are provided - opts.push({ type: 'Secp256r1' }) + opts.push({ type: 'Secp256r1', isController: true }) } const keys = await Promise.all( opts.map((options) => { return importProvidedOrGeneratedKey({ kms: kms ?? this.defaultKms, options }, context) }) ) + + const controllerIdx = opts.findIndex((opt) => opt.isController) + const controllerKeyId = controllerIdx < 0 ? keys[0].kid : keys[controllerIdx].kid const identifier: Omit = { did: await asDidWeb(alias), - controllerKeyId: keys[0].kid, + controllerKeyId, keys, services: args.services ?? [], } @@ -63,11 +66,33 @@ export class WebDIDProvider extends AbstractIdentifierProvider { return true } - async addKey({ identifier, key, options }: { identifier: IIdentifier; key: IKey; options?: any }, context: IContext): Promise { + async addKey( + { + identifier, + key, + options, + }: { + identifier: IIdentifier + key: IKey + options?: any + }, + context: IContext + ): Promise { return { success: true } } - async addService({ identifier, service, options }: { identifier: IIdentifier; service: IService; options?: any }, context: IContext): Promise { + async addService( + { + identifier, + service, + options, + }: { + identifier: IIdentifier + service: IService + options?: any + }, + context: IContext + ): Promise { return { success: true } }