From e12c225748568fa4c3ff096d4a065dc1246ffd86 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 18:08:32 -0400 Subject: [PATCH 1/7] feat: add did-peer provider --- __tests__/localAgent.test.ts | 4 + packages/cli/tsconfig.json | 1 + packages/core-types/src/plugin.schema.json | 6 +- packages/did-comm/src/plugin.schema.json | 2 +- packages/did-provider-key/README.md | 2 +- packages/did-provider-peer/LICENSE | 201 ++++++++++++++++++ packages/did-provider-peer/README.md | 4 + packages/did-provider-peer/api-extractor.json | 18 ++ packages/did-provider-peer/package.json | 52 +++++ packages/did-provider-peer/src/index.ts | 8 + .../src/peer-did-provider.ts | 143 +++++++++++++ packages/did-provider-peer/src/resolver.ts | 53 +++++ packages/did-provider-peer/tsconfig.json | 13 ++ packages/tsconfig.json | 1 + pnpm-lock.yaml | 29 +++ 15 files changed, 532 insertions(+), 5 deletions(-) create mode 100644 packages/did-provider-peer/LICENSE create mode 100644 packages/did-provider-peer/README.md create mode 100644 packages/did-provider-peer/api-extractor.json create mode 100644 packages/did-provider-peer/package.json create mode 100644 packages/did-provider-peer/src/index.ts create mode 100644 packages/did-provider-peer/src/peer-did-provider.ts create mode 100644 packages/did-provider-peer/src/resolver.ts create mode 100644 packages/did-provider-peer/tsconfig.json diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 5ab1109d8..4fa0f88cf 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -37,6 +37,7 @@ import { } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' +import { PeerDIDProvider } from '../packages/did-provider-peer/src' import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { getDidPkhResolver, PkhDIDProvider } from '../packages/did-provider-pkh/src' import { getDidJwkResolver, JwkDIDProvider } from '../packages/did-provider-jwk/src' @@ -199,6 +200,9 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), + 'did:peer': new PeerDIDProvider({ + defaultKms: 'local' + }), 'did:pkh': new PkhDIDProvider({ defaultKms: 'local', }), diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 2a15ec786..1e75d3174 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -16,6 +16,7 @@ { "path": "../did-manager" }, { "path": "../did-provider-ethr" }, { "path": "../did-provider-key" }, + { "path": "../did-provider-peer" }, { "path": "../did-provider-pkh" }, { "path": "../did-provider-web" }, { "path": "../did-resolver" }, diff --git a/packages/core-types/src/plugin.schema.json b/packages/core-types/src/plugin.schema.json index 0c600c42d..617f9a613 100644 --- a/packages/core-types/src/plugin.schema.json +++ b/packages/core-types/src/plugin.schema.json @@ -3665,7 +3665,7 @@ "save": { "type": "boolean", "description": "Optional. If set to `true`, the message will be saved using\n {@link @veramo/core-types#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage } \n

", - "deprecated": "Please call {@link @veramo/core-types#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage()} after\nhandling the message and determining that it must be saved." + "deprecated": "Please call {@link @veramo/core-types#IDataStore.dataStoreSaveMessage | dataStoreSaveMessage()} after\r\nhandling the message and determining that it must be saved." } }, "required": [ @@ -4069,7 +4069,7 @@ "save": { "type": "boolean", "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core-types#IDataStore | storage plugin } to be saved.", - "deprecated": "Please call\n{@link @veramo/core-types#IDataStore.dataStoreSaveVerifiableCredential | dataStoreSaveVerifiableCredential()} to save\nthe credential after creating it." + "deprecated": "Please call\r\n{@link @veramo/core-types#IDataStore.dataStoreSaveVerifiableCredential | dataStoreSaveVerifiableCredential()} to save\r\nthe credential after creating it." }, "proofFormat": { "$ref": "#/components/schemas/ProofFormat", @@ -4283,7 +4283,7 @@ "save": { "type": "boolean", "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core-types#IDataStore | storage plugin } to be saved.

", - "deprecated": "Please call\n{@link @veramo/core-types#IDataStore.dataStoreSaveVerifiablePresentation | dataStoreSaveVerifiablePresentation()} to\nsave the credential after creating it." + "deprecated": "Please call\r\n{@link @veramo/core-types#IDataStore.dataStoreSaveVerifiablePresentation | dataStoreSaveVerifiablePresentation()} to\r\nsave the credential after creating it." }, "challenge": { "type": "string", diff --git a/packages/did-comm/src/plugin.schema.json b/packages/did-comm/src/plugin.schema.json index 6cee64c18..986f20a25 100644 --- a/packages/did-comm/src/plugin.schema.json +++ b/packages/did-comm/src/plugin.schema.json @@ -257,7 +257,7 @@ "required": [ "data" ], - "deprecated": "Please use {@link IDIDComm.sendDIDCommMessage } instead. This will be removed in Veramo 4.0.\nInput arguments for {@link IDIDComm.sendMessageDIDCommAlpha1 }" + "deprecated": "Please use {@link IDIDComm.sendDIDCommMessage } instead. This will be removed in Veramo 4.0.\r\nInput arguments for {@link IDIDComm.sendMessageDIDCommAlpha1 }" }, "IMessage": { "type": "object", diff --git a/packages/did-provider-key/README.md b/packages/did-provider-key/README.md index 56731bd4b..aa34d413f 100644 --- a/packages/did-provider-key/README.md +++ b/packages/did-provider-key/README.md @@ -1,4 +1,4 @@ -# Veramo did:web provider +# Veramo did:key provider This package contains an implementation of `AbstractIdentifierProvider` for the `did:key` method. This enables creation and control of `did:key` entities. diff --git a/packages/did-provider-peer/LICENSE b/packages/did-provider-peer/LICENSE new file mode 100644 index 000000000..fd815d7f8 --- /dev/null +++ b/packages/did-provider-peer/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-peer/README.md b/packages/did-provider-peer/README.md new file mode 100644 index 000000000..4c74f6658 --- /dev/null +++ b/packages/did-provider-peer/README.md @@ -0,0 +1,4 @@ +# Veramo did:peer provider + +This package contains an implementation of `AbstractIdentifierProvider` for the `did:key` method. +This enables creation and control of `did:key` entities. diff --git a/packages/did-provider-peer/api-extractor.json b/packages/did-provider-peer/api-extractor.json new file mode 100644 index 000000000..409d7f16c --- /dev/null +++ b/packages/did-provider-peer/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-peer/package.json b/packages/did-provider-peer/package.json new file mode 100644 index 000000000..797d7b004 --- /dev/null +++ b/packages/did-provider-peer/package.json @@ -0,0 +1,52 @@ +{ + "name": "@veramo/did-provider-peer", + "description": "Veramo plugin that can enable creation and control of did:peer identifiers.", + "version": "5.1.2", + "main": "build/index.js", + "exports": "./build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc", + "extract-api": "node ../cli/bin/veramo.js dev extract-api" + }, + "dependencies": { + "@transmute/did-key-ed25519": "^0.3.0-unstable.10", + "@transmute/did-key-secp256k1": "^0.3.0-unstable.10", + "@transmute/did-key-x25519": "^0.3.0-unstable.10", + "@veramo/core-types": "^5.1.2", + "@veramo/did-manager": "^5.1.2", + "debug": "^4.3.3", + "did-resolver": "^4.0.1", + "multibase": "^4.0.6", + "multicodec": "^3.2.1", + "uint8arrays": "^3.0.0" + }, + "devDependencies": { + "@types/debug": "4.1.7", + "typescript": "4.9.4" + }, + "resolutions": { + "jsonld": "npm:@digitalcredentials/jsonld@^5.2.1" + }, + "files": [ + "build/**/*", + "src/**/*", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:uport-project/veramo.git", + "author": "Alex Andrei - roots.id", + "contributors": [ + "Nick Reynolds " + ], + "license": "Apache-2.0", + "keywords": [], + "type": "module", + "moduleDirectories": [ + "node_modules", + "src" + ] +} diff --git a/packages/did-provider-peer/src/index.ts b/packages/did-provider-peer/src/index.ts new file mode 100644 index 000000000..ce8c3479d --- /dev/null +++ b/packages/did-provider-peer/src/index.ts @@ -0,0 +1,8 @@ +/** + * Provides `did:key` {@link @veramo/did-provider-peer#PeerDIDProvider | identifier provider } for the + * {@link @veramo/did-manager#DIDManager} + * + * @packageDocumentation + */ +export { PeerDIDProvider } from './peer-did-provider.js' +export { getDidPeerResolver } from './resolver.js' diff --git a/packages/did-provider-peer/src/peer-did-provider.ts b/packages/did-provider-peer/src/peer-did-provider.ts new file mode 100644 index 000000000..47b681748 --- /dev/null +++ b/packages/did-provider-peer/src/peer-did-provider.ts @@ -0,0 +1,143 @@ +import { IIdentifier, IKey, IService, IAgentContext, IKeyManager, DIDDocument } from '@veramo/core-types' +import { AbstractIdentifierProvider } from '@veramo/did-manager' +import Multibase from 'multibase' +import Multicodec from 'multicodec' +import * as u8a from 'uint8arrays' + +import Debug from 'debug' +const debug = Debug('veramo:did-peer:identifier-provider') + +type IContext = IAgentContext + +const ServiceReplacements = { + 'type': 't', + 'DIDCommMessaging': 'dm', + 'serviceEndpoint': 's', + 'routingKeys': 'r', + 'accept': 'a' +} + +const encodeService = (service: IService): string => { + let encoded = JSON.stringify(service) + Object.values(ServiceReplacements).forEach((v: string, idx: number) => { + encoded = encoded.replace(Object.keys(ServiceReplacements)[idx], v) + }) + return u8a.toString(u8a.fromString(encoded, 'utf-8'), 'base64url') +} + +/** + * {@link @veramo/did-manager#DIDManager} identifier provider for `did:key` identifiers + * + * @beta This API may change without a BREAKING CHANGE notice. + */ +export class PeerDIDProvider extends AbstractIdentifierProvider { + private defaultKms: string + + constructor(options: { defaultKms: string }) { + super() + this.defaultKms = options.defaultKms + } + + async createIdentifier( + { kms, options }: { kms?: string; options?: any }, + context: IContext, + ): Promise> { + if (options.num_algo == 0) { + const key = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, type: 'Ed25519' }) + const methodSpecificId = Buffer.from( + Multibase.encode( + 'base58btc', + Multicodec.addPrefix('ed25519-pub', Buffer.from(key.publicKeyHex, 'hex')), + ), + ).toString() + + const identifier: Omit = { + did: 'did:peer:0' + methodSpecificId, + controllerKeyId: key.kid, + keys: [key], + services: [], + } + debug('Created', identifier.did) + return identifier + } + + else if (options.num_algo == 1) { + throw new Error( `'PeerDIDProvider num algo ${options.num_algo} not supported yet.'`) + + } + else if (options.num_algo == 2) { + const authKey = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, type: 'Ed25519' }) + + const agreementKey = await context.agent.keyManagerCreate({ kms: kms || this.defaultKms, type: 'X25519' }) + + const authKeyText = Buffer.from( + Multibase.encode( + 'base58btc', + Multicodec.addPrefix('ed25519-pub', Buffer.from(authKey.publicKeyHex, 'hex')), + ), + ).toString() + + const agreementKeyText = Buffer.from( + Multibase.encode( + 'base58btc', + Multicodec.addPrefix('x25519-pub', Buffer.from(agreementKey.publicKeyHex, 'hex')), + ), + ).toString() + + const ServiceEncoded = encodeService(options.service) + + + const identifier: Omit = { + did: `did:peer:2.E${agreementKeyText}.V${authKeyText}.S${ServiceEncoded}`, + controllerKeyId: authKey.kid, + keys: [authKey,agreementKey], + services: [options.service], + } + debug('Created', identifier.did) + return identifier + } + else { + throw new Error( `'PeerDIDProvider num algo ${options.num_algo} not supported yet.'`) + + } + } + + async updateIdentifier(args: { did: string; kms?: string | undefined; alias?: string | undefined; options?: any }, context: IAgentContext): Promise { + throw new Error('PeerDIDProvider 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 { + throw Error('PeerDIDProvider addKey not supported') + } + + async addService( + { identifier, service, options }: { identifier: IIdentifier; service: IService; options?: any }, + context: IContext, + ): Promise { + throw Error('PeerDIDProvider addService not supported') + } + + async removeKey( + args: { identifier: IIdentifier; kid: string; options?: any }, + context: IContext, + ): Promise { + throw Error('PeerDIDProvider removeKey not supported') + } + + async removeService( + args: { identifier: IIdentifier; id: string; options?: any }, + context: IContext, + ): Promise { + throw Error('PeerDIDProvider removeService not supported') + } +} diff --git a/packages/did-provider-peer/src/resolver.ts b/packages/did-provider-peer/src/resolver.ts new file mode 100644 index 000000000..cf4b6a39f --- /dev/null +++ b/packages/did-provider-peer/src/resolver.ts @@ -0,0 +1,53 @@ +import { resolve as resolveED25519 } from '@transmute/did-key-ed25519' +import { resolve as resolveX25519 } from '@transmute/did-key-x25519' +import { resolve as resolveSecp256k1 } from '@transmute/did-key-secp256k1' +import { DIDResolutionOptions, DIDResolutionResult, DIDResolver, ParsedDID, Resolvable } from 'did-resolver' + +export const startsWithMap: Record = { + 'did:peer:z6Mk': resolveED25519, + 'did:peer:z6LS': resolveX25519, + 'did:peer:zQ3s': resolveSecp256k1, +} + +const resolveDidPeer: DIDResolver = async ( + didUrl: string, + _parsed: ParsedDID, + _resolver: Resolvable, + options: DIDResolutionOptions, +): Promise => { + try { + const startsWith = _parsed.did.substring(0, 12) + if (startsWithMap[startsWith] !== undefined) { + const didResolution = await startsWithMap[startsWith](didUrl, { + ...options, + enableEncryptionKeyDerivation: true, + }) + return { + didDocumentMetadata: {}, + didResolutionMetadata: {}, + ...didResolution, + } + } else { + return { + didDocumentMetadata: {}, + didResolutionMetadata: { error: 'invalidDid', message: 'unsupported key type for did:peer' }, + didDocument: null, + } + } + } catch (err: any) { + return { + didDocumentMetadata: {}, + didResolutionMetadata: { error: 'invalidDid', message: err.toString() }, + didDocument: null, + } + } +} + +/** + * Provides a mapping to a did:peer resolver, usable by {@link did-resolver#Resolver}. + * + * @public + */ +export function getDidPeerResolver() { + return { peer: resolveDidPeer } +} diff --git a/packages/did-provider-peer/tsconfig.json b/packages/did-provider-peer/tsconfig.json new file mode 100644 index 000000000..b63702b6f --- /dev/null +++ b/packages/did-provider-peer/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.settings.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "declarationDir": "build", + "skipLibCheck": true + }, + "references": [ + {"path": "../core-types"}, + {"path": "../did-manager"} + ] +} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 950b777f1..c982dece0 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -17,6 +17,7 @@ { "path": "did-provider-ion" }, { "path": "did-provider-pkh" }, { "path": "did-provider-key" }, + { "path": "did-provider-peer" }, { "path": "did-provider-web" }, { "path": "did-resolver" }, { "path": "key-manager" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3d2f3081..6ae57f6f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -658,6 +658,35 @@ importers: '@types/debug': 4.1.7 typescript: 4.9.4 + packages/did-provider-peer: + specifiers: + '@transmute/did-key-ed25519': ^0.3.0-unstable.10 + '@transmute/did-key-secp256k1': ^0.3.0-unstable.10 + '@transmute/did-key-x25519': ^0.3.0-unstable.10 + '@types/debug': 4.1.7 + '@veramo/core-types': ^5.1.2 + '@veramo/did-manager': ^5.1.2 + debug: ^4.3.3 + did-resolver: ^4.0.1 + multibase: ^4.0.6 + multicodec: ^3.2.1 + typescript: 4.9.4 + uint8arrays: ^3.0.0 + dependencies: + '@transmute/did-key-ed25519': 0.3.0-unstable.10 + '@transmute/did-key-secp256k1': 0.3.0-unstable.10 + '@transmute/did-key-x25519': 0.3.0-unstable.10 + '@veramo/core-types': link:../core-types + '@veramo/did-manager': link:../did-manager + debug: 4.3.4 + did-resolver: 4.0.1 + multibase: 4.0.6 + multicodec: 3.2.1 + uint8arrays: 3.1.1 + devDependencies: + '@types/debug': 4.1.7 + typescript: 4.9.4 + packages/did-provider-pkh: specifiers: '@ethersproject/abstract-provider': ^5.7.0 From 574ba769a66612a6208b259a43dd074aee5623fd Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 18:36:59 -0400 Subject: [PATCH 2/7] chore: add peer-did didComm test --- __tests__/localAgent.test.ts | 43 +++++----- __tests__/localJsonStoreAgent.test.ts | 26 +++--- __tests__/localMemoryStoreAgent.test.ts | 28 +++---- __tests__/restAgent.test.ts | 30 +++---- __tests__/shared/didCommWithPeerDidFlow.ts | 74 +++++++++++++++++ packages/did-comm/src/didcomm.ts | 4 + packages/did-provider-peer/package.json | 1 + packages/did-provider-peer/src/index.ts | 2 +- packages/did-provider-peer/src/resolver.ts | 97 ++++++++++++---------- packages/utils/src/did-utils.ts | 3 + pnpm-lock.yaml | 8 ++ 11 files changed, 211 insertions(+), 105 deletions(-) create mode 100644 __tests__/shared/didCommWithPeerDidFlow.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 4fa0f88cf..287401409 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -37,7 +37,7 @@ import { } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { PeerDIDProvider } from '../packages/did-provider-peer/src' +import { getResolver as getDidPeerResolver, PeerDIDProvider } from '../packages/did-provider-peer/src' import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { getDidPkhResolver, PkhDIDProvider } from '../packages/did-provider-pkh/src' import { getDidJwkResolver, JwkDIDProvider } from '../packages/did-provider-jwk/src' @@ -92,6 +92,7 @@ import utils from './shared/utils' import web3 from './shared/web3' import credentialStatus from './shared/credentialStatus' import ethrDidFlowSigned from "./shared/ethrDidFlowSigned"; +import didCommWithPeerDidFlow from './shared/didCommWithPeerDidFlow.js' jest.setTimeout(60000) @@ -228,6 +229,7 @@ const setup = async (options?: IAgentOptions): Promise => { ...getDidKeyResolver(), ...getDidPkhResolver(), ...getDidJwkResolver(), + ...getDidPeerResolver(), ...new FakeDidResolver(() => agent).getDidFakeResolver(), }), new DataStore(dbConnection), @@ -281,23 +283,24 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - saveClaims(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) - didDiscovery(testContext) - dbInitOptions(testContext) - utils(testContext) - web3(testContext) - didCommWithEthrDidFlow(testContext) - credentialStatus(testContext) - ethrDidFlowSigned(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // saveClaims(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) + // didDiscovery(testContext) + // dbInitOptions(testContext) + // utils(testContext) + // web3(testContext) + // didCommWithEthrDidFlow(testContext) + didCommWithPeerDidFlow(testContext) + // credentialStatus(testContext) + // ethrDidFlowSigned(testContext) }) diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index d3c7cff35..217cde936 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -224,18 +224,18 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local json-data-store integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - saveClaims(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // saveClaims(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) utils(testContext) - credentialStatus(testContext) + // credentialStatus(testContext) }) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index f184d3c2c..e5f17376d 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -220,19 +220,19 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local in-memory integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - saveClaims(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // saveClaims(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) utils(testContext) - credentialStatus(testContext) - credentialInterop(testContext) + // credentialStatus(testContext) + // credentialInterop(testContext) }) diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 048306e01..473d1262f 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -282,20 +282,20 @@ const tearDown = async (): Promise => { const testContext = { getAgent, setup, tearDown } describe('REST integration tests', () => { - verifiableDataJWT(testContext) - verifiableDataLD(testContext) - verifiableDataEIP712(testContext) - handleSdrMessage(testContext) - resolveDid(testContext) - webDidFlow(testContext) - documentationExamples(testContext) - keyManager(testContext) - didManager(testContext) - messageHandler(testContext) - didCommPacking(testContext) - didWithFakeDidFlow(testContext) - didCommAndDataStoreWithCredentials(testContext) - didDiscovery(testContext) + // verifiableDataJWT(testContext) + // verifiableDataLD(testContext) + // verifiableDataEIP712(testContext) + // handleSdrMessage(testContext) + // resolveDid(testContext) + // webDidFlow(testContext) + // documentationExamples(testContext) + // keyManager(testContext) + // didManager(testContext) + // messageHandler(testContext) + // didCommPacking(testContext) + // didWithFakeDidFlow(testContext) + // didCommAndDataStoreWithCredentials(testContext) + // didDiscovery(testContext) utils(testContext) - credentialStatus(testContext) + // credentialStatus(testContext) }) diff --git a/__tests__/shared/didCommWithPeerDidFlow.ts b/__tests__/shared/didCommWithPeerDidFlow.ts new file mode 100644 index 000000000..8e4a66dbb --- /dev/null +++ b/__tests__/shared/didCommWithPeerDidFlow.ts @@ -0,0 +1,74 @@ +// noinspection ES6PreferShortImport + +import { + IAgentOptions, + IDIDManager, + IEventListener, + IIdentifier, + IKeyManager, + IResolver, + TAgent, +} from '../../packages/core-types/src' +import { IDIDComm } from '../../packages/did-comm/src' +import { jest } from '@jest/globals' + +type ConfiguredAgent = TAgent + +const DIDCommEventSniffer: IEventListener = { + eventTypes: ['DIDCommV2Message-sent', 'DIDCommV2Message-received', 'DIDCommV2Message-forwarded'], + onEvent: jest.fn(() => Promise.resolve()), +} + +export default (testContext: { + getAgent: () => ConfiguredAgent + setup: (options?: IAgentOptions) => Promise + tearDown: () => Promise +}) => { + describe('DID comm using did:peer flow', () => { + let agent: ConfiguredAgent + let sender: IIdentifier + let receiver: IIdentifier + + beforeAll(async () => { + await testContext.setup({ plugins: [DIDCommEventSniffer] }) + agent = testContext.getAgent() + + sender = await agent.didManagerCreate({ + "alias": "sender", + "provider": "did:peer", + "kms": "local", + "options": {"num_algo":2 , "service" : {"id":"12344","type":"didcommv2","serviceEndpoint":"alexexxxxx","description":"an endpoint"} + } + }) + + receiver = await agent.didManagerCreate({ + "alias": "receiver", + "provider": "did:peer", + "kms": "local", + "options": {"num_algo":2 , "service" : {"id":"12345","type":"didcommv2","serviceEndpoint":"alexexxxxx","description":"an endpoint"} + } + }) + + return true + }) + afterAll(testContext.tearDown) + + it('should pack and unpack a message', async () => { + expect.assertions(1) + + const message = { + type: 'test', + to: receiver.did, + from: sender.did, + id: 'test', + body: { hello: 'world' }, + } + const packedMessage = await agent.packDIDCommMessage({ + packing: 'authcrypt', + message, + }) + console.log("packedMessage: ", packedMessage) + expect(packedMessage).toBeDefined() + }) + }) +} diff --git a/packages/did-comm/src/didcomm.ts b/packages/did-comm/src/didcomm.ts index d260da4d5..8b343148a 100644 --- a/packages/did-comm/src/didcomm.ts +++ b/packages/did-comm/src/didcomm.ts @@ -263,8 +263,12 @@ export class DIDComm implements IAgentPlugin { } // 1.1 check that args.message.from is a managed DID const sender: IIdentifier = await context.agent.didManagerGet({ did: args?.message?.from }) + + console.log("sender: ", sender) + // 1.2 match key agreement keys from DID to managed keys const senderKeys: _ExtendedIKey[] = await mapIdentifierKeysToDoc(sender, 'keyAgreement', context) + console.log("senderKeys: ", senderKeys) // try to find a sender key by keyRef, otherwise pick the first one let senderKey if (isDefined(keyRef)) { diff --git a/packages/did-provider-peer/package.json b/packages/did-provider-peer/package.json index 797d7b004..8a7e1c982 100644 --- a/packages/did-provider-peer/package.json +++ b/packages/did-provider-peer/package.json @@ -10,6 +10,7 @@ "extract-api": "node ../cli/bin/veramo.js dev extract-api" }, "dependencies": { + "@aviarytech/did-peer": "^0.0.19", "@transmute/did-key-ed25519": "^0.3.0-unstable.10", "@transmute/did-key-secp256k1": "^0.3.0-unstable.10", "@transmute/did-key-x25519": "^0.3.0-unstable.10", diff --git a/packages/did-provider-peer/src/index.ts b/packages/did-provider-peer/src/index.ts index ce8c3479d..63875fc59 100644 --- a/packages/did-provider-peer/src/index.ts +++ b/packages/did-provider-peer/src/index.ts @@ -5,4 +5,4 @@ * @packageDocumentation */ export { PeerDIDProvider } from './peer-did-provider.js' -export { getDidPeerResolver } from './resolver.js' +export { getResolver } from './resolver.js' diff --git a/packages/did-provider-peer/src/resolver.ts b/packages/did-provider-peer/src/resolver.ts index cf4b6a39f..ec9c66860 100644 --- a/packages/did-provider-peer/src/resolver.ts +++ b/packages/did-provider-peer/src/resolver.ts @@ -1,53 +1,66 @@ -import { resolve as resolveED25519 } from '@transmute/did-key-ed25519' -import { resolve as resolveX25519 } from '@transmute/did-key-x25519' -import { resolve as resolveSecp256k1 } from '@transmute/did-key-secp256k1' -import { DIDResolutionOptions, DIDResolutionResult, DIDResolver, ParsedDID, Resolvable } from 'did-resolver' +import { DIDDocument, DIDResolutionResult, DIDResolver, ParsedDID } from 'did-resolver' +import { resolve } from '@aviarytech/did-peer' +import { IDIDDocumentServiceDescriptor } from '@aviarytech/did-peer/interfaces.js' -export const startsWithMap: Record = { - 'did:peer:z6Mk': resolveED25519, - 'did:peer:z6LS': resolveX25519, - 'did:peer:zQ3s': resolveSecp256k1, -} +export function getResolver(): Record { + async function resolveInner(did: string, parsed: ParsedDID): Promise { + const didDocumentMetadata = {} + let didDocument: DIDDocument | null = null + let err = '' + do { + try { + const doc = await resolve(did) + didDocument = { + '@context': doc['@context'], + id: doc.id, + verificationMethod: doc.verificationMethod, + keyAgreement: doc.keyAgreement, + authentication: doc.authentication, + assertionMethod: doc.assertionMethod, + capabilityInvocation: doc.capabilityInvocation, + capabilityDelegation: doc.capabilityDelegation, + service: doc.service as IDIDDocumentServiceDescriptor[], + } + if (doc.alsoKnownAs) { + didDocument.alsoKnownAs = [doc.alsoKnownAs] + } + if (doc.controller) { + didDocument.controller = doc.controller + } + } catch (error) { + err = `resolver_error: DID must resolve to a valid https URL containing a JSON document: ${error}` + break + } + + // TODO: this excludes the use of query params + const docIdMatchesDid = didDocument?.id === did + if (!docIdMatchesDid) { + err = 'resolver_error: DID document id does not match requested did' + // break // uncomment this when adding more checks + } + // eslint-disable-next-line no-constant-condition + } while (false) -const resolveDidPeer: DIDResolver = async ( - didUrl: string, - _parsed: ParsedDID, - _resolver: Resolvable, - options: DIDResolutionOptions, -): Promise => { - try { - const startsWith = _parsed.did.substring(0, 12) - if (startsWithMap[startsWith] !== undefined) { - const didResolution = await startsWithMap[startsWith](didUrl, { - ...options, - enableEncryptionKeyDerivation: true, - }) + const contentType = + typeof didDocument?.['@context'] !== 'undefined' ? 'application/did+ld+json' : 'application/did+json' + + if (err) { return { - didDocumentMetadata: {}, - didResolutionMetadata: {}, - ...didResolution, + didDocument, + didDocumentMetadata, + didResolutionMetadata: { + error: 'notFound', + message: err, + }, } } else { return { - didDocumentMetadata: {}, - didResolutionMetadata: { error: 'invalidDid', message: 'unsupported key type for did:peer' }, - didDocument: null, + didDocument, + didDocumentMetadata, + didResolutionMetadata: { contentType }, } } - } catch (err: any) { - return { - didDocumentMetadata: {}, - didResolutionMetadata: { error: 'invalidDid', message: err.toString() }, - didDocument: null, - } } -} -/** - * Provides a mapping to a did:peer resolver, usable by {@link did-resolver#Resolver}. - * - * @public - */ -export function getDidPeerResolver() { - return { peer: resolveDidPeer } + return { peer: resolveInner } } diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index 123be81bf..16fcd9b3b 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -208,7 +208,9 @@ export async function mapIdentifierKeysToDoc( section: DIDDocumentSection = 'keyAgreement', context: IAgentContext, ): Promise<_ExtendedIKey[]> { + console.log("go map id keys to doc") const didDocument = await resolveDidOrThrow(identifier.did, context) + console.log("yes got didDoc: ", didDocument) // dereference all key agreement keys from DID document and normalize const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( didDocument, @@ -216,6 +218,7 @@ export async function mapIdentifierKeysToDoc( context, ) + console.log("documentKeys: ", documentKeys) let localKeys = identifier.keys.filter(isDefined) if (section === 'keyAgreement') { localKeys = convertIdentifierEncryptionKeys(identifier) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ae57f6f8..8b204b070 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -660,6 +660,7 @@ importers: packages/did-provider-peer: specifiers: + '@aviarytech/did-peer': ^0.0.19 '@transmute/did-key-ed25519': ^0.3.0-unstable.10 '@transmute/did-key-secp256k1': ^0.3.0-unstable.10 '@transmute/did-key-x25519': ^0.3.0-unstable.10 @@ -673,6 +674,7 @@ importers: typescript: 4.9.4 uint8arrays: ^3.0.0 dependencies: + '@aviarytech/did-peer': 0.0.19 '@transmute/did-key-ed25519': 0.3.0-unstable.10 '@transmute/did-key-secp256k1': 0.3.0-unstable.10 '@transmute/did-key-x25519': 0.3.0-unstable.10 @@ -1163,6 +1165,12 @@ packages: leven: 3.1.0 dev: true + /@aviarytech/did-peer/0.0.19: + resolution: {integrity: sha512-koSwVi++RIVWgYoNHHFZ95ouVqCNlLvqDfJfThOwExkZ2YSoRW/EFXeXFe33Gm7wR9gwTgoZXgi71hHO9d5RVQ==} + dependencies: + buffer: 6.0.3 + dev: false + /@babel/code-frame/7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} From 97235375c35bf455d9d5a6479f375ac9f553a898 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 18:46:24 -0400 Subject: [PATCH 3/7] fix: correct multibase conversion --- .../did-comm/src/__tests__/packing.test.ts | 2 +- packages/utils/src/did-utils.ts | 18 +++--- packages/utils/src/encodings.ts | 60 +++++++++++++++++++ packages/utils/src/types/utility-types.ts | 2 +- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/packages/did-comm/src/__tests__/packing.test.ts b/packages/did-comm/src/__tests__/packing.test.ts index a5d3bad2b..d3551a9ef 100644 --- a/packages/did-comm/src/__tests__/packing.test.ts +++ b/packages/did-comm/src/__tests__/packing.test.ts @@ -47,7 +47,7 @@ const multiBaseDoc = { { "controller": "did:web:portcullis.1keep.com", "id": "did:web:portcullis.1keep.com#1", - "publicKeyMultibase": "z7bNjXnt3EXhpy7UdzZUANa2CsBFpzKGFXsgvkBd9iHGo", + "publicKeyMultibase": "z6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud", "type": "X25519KeyAgreementKey2020" } ] diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index 16fcd9b3b..9a38bc21b 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -13,12 +13,13 @@ import * as u8a from 'uint8arrays' import elliptic from 'elliptic' import { bases } from 'multiformats/basics' import Debug from 'debug' -import { hexToBytes, bytesToHex, base64ToBytes, base58ToBytes } from './encodings.js' +import { hexToBytes, bytesToHex, base64ToBytes, base58ToBytes, multibaseKeyToBytes } from './encodings.js' const debug = Debug('veramo:utils') /** - * Converts any Ed25519 keys of an {@link @veramo/core-types#IIdentifier | IIdentifier} to X25519 to be usable for encryption. + * Converts any Ed25519 keys of an {@link @veramo/core-types#IIdentifier | IIdentifier} to X25519 to be usable for + * encryption. * * @param identifier - the identifier with keys * @@ -47,7 +48,8 @@ export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] } /** - * Converts any Secp256k1 public keys of an {@link @veramo/core-types#IIdentifier | IIdentifier} to their compressed form. + * Converts any Secp256k1 public keys of an {@link @veramo/core-types#IIdentifier | IIdentifier} to their compressed + * form. * * @param identifier - the identifier with keys * @@ -86,7 +88,7 @@ export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] function compareBlockchainAccountId(localKey: IKey, verificationMethod: VerificationMethod): boolean { if ( (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' && - verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2019') || + verificationMethod.type !== 'EcdsaSecp256k1VerificationKey2019') || localKey.type !== 'Secp256k1' ) { return false @@ -130,6 +132,7 @@ export function getEthereumAddress(verificationMethod: VerificationMethod): stri } return vmEthAddr } + interface LegacyVerificationMethod extends VerificationMethod { publicKeyBase64: string } @@ -138,7 +141,7 @@ function extractPublicKeyBytes(pk: VerificationMethod): Uint8Array { if (pk.publicKeyBase58) { return base58ToBytes(pk.publicKeyBase58) } else if (pk.publicKeyMultibase) { - return bases['base58btc'].decode(pk.publicKeyMultibase) + return multibaseKeyToBytes(pk.publicKeyMultibase) } else if ((pk).publicKeyBase64) { return base64ToBytes((pk).publicKeyBase64) } else if (pk.publicKeyHex) { @@ -208,9 +211,7 @@ export async function mapIdentifierKeysToDoc( section: DIDDocumentSection = 'keyAgreement', context: IAgentContext, ): Promise<_ExtendedIKey[]> { - console.log("go map id keys to doc") const didDocument = await resolveDidOrThrow(identifier.did, context) - console.log("yes got didDoc: ", didDocument) // dereference all key agreement keys from DID document and normalize const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( didDocument, @@ -218,7 +219,6 @@ export async function mapIdentifierKeysToDoc( context, ) - console.log("documentKeys: ", documentKeys) let localKeys = identifier.keys.filter(isDefined) if (section === 'keyAgreement') { localKeys = convertIdentifierEncryptionKeys(identifier) @@ -327,7 +327,7 @@ export async function dereferenceDidKeys( // Should type of `newKey` change? if (convert && 'Ed25519VerificationKey2018' === newKey.type) { newKey.type = 'X25519KeyAgreementKey2019' - } else if (convert && 'Ed25519VerificationKey2020' === newKey.type) { + } else if (convert && ['Ed25519VerificationKey2020', 'JsonWebKey2020'].includes(newKey.type)) { newKey.type = 'X25519KeyAgreementKey2020' } diff --git a/packages/utils/src/encodings.ts b/packages/utils/src/encodings.ts index d11c964b7..13a7c174b 100644 --- a/packages/utils/src/encodings.ts +++ b/packages/utils/src/encodings.ts @@ -1,4 +1,5 @@ import * as u8a from 'uint8arrays' +import { bases } from 'multiformats/basics' /** * Converts a Uint8Array to a base64url string @@ -143,3 +144,62 @@ export function base58ToBytes(s: string): Uint8Array { export function bytesToBase58(byteArray: Uint8Array): string { return u8a.toString(byteArray, 'base58btc') } + + +/** + * Converts a multibase string to the Uint8Array it represents. + * + * @param s - the string to be converted + * + * @throws if the string is not formatted correctly. + * + * @public +*/ +export function multibaseKeyToBytes(s: string): Uint8Array { + if (s.charAt(0) !== 'z') { + throw new Error('invalid multibase string: string is not base58 encoded (does not start with "z")') + } + const bytes = bases['base58btc'].decode(s) + + if (bytes.length !== 34) { + throw new Error('invalid multibase string: length is not 34 bytes') + } + + // only ed25519-pub and x25519-pub multicodecs supported now + if (bytes[0] !== 0xed && bytes[0] !== 0xec) { + throw new Error('invalid multibase string: first byte is not 0xed') + } + + if (bytes[1] !== 0x01) { + throw new Error('invalid multibase string: second byte is not 0x01') + } + + return bytes.slice(2) +} + +/** + * Converts a Uint8Array to a multibase string. + * + * @param b - the array to be converted + * @param type - the type of the key to be represented + * + * @throws if the array is not formatted correctly. + * + * @public +*/ +export function bytesToMultibase(byteArray: Uint8Array, type: string): string { + if (byteArray.length !== 32) { + throw new Error('invalid byte array: length is not 32 bytes') + } + + const bytes = new Uint8Array(34) + if (type === 'Ed25519') { + bytes[0] = 0xed + } else if (type === 'X25519') { + bytes[0] = 0xec + } + bytes[1] = 0x01 + bytes.set(byteArray, 2) + + return bases['base58btc'].encode(bytes) +} \ No newline at end of file diff --git a/packages/utils/src/types/utility-types.ts b/packages/utils/src/types/utility-types.ts index 2eee951fb..4919bedd3 100644 --- a/packages/utils/src/types/utility-types.ts +++ b/packages/utils/src/types/utility-types.ts @@ -28,7 +28,7 @@ export interface _ExtendedIKey extends IKey { */ export type _NormalizedVerificationMethod = Omit< VerificationMethod, - 'publicKeyBase58' | 'publicKeyBase64' | 'publicKeyJwk' + 'publicKeyBase58' | 'publicKeyBase64' | 'publicKeyJwk' | 'publicKeyMultibase' > /** From 8585df1fe5ef1dd489aecfbef3c966c8ddaec6f4 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 18:52:25 -0400 Subject: [PATCH 4/7] chore: uncomment tests --- __tests__/localAgent.test.ts | 38 ++++++++++++------------- __tests__/localJsonStoreAgent.test.ts | 26 ++++++++--------- __tests__/localMemoryStoreAgent.test.ts | 28 +++++++++--------- __tests__/restAgent.test.ts | 30 +++++++++---------- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 287401409..95907e8db 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -283,24 +283,24 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // saveClaims(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) - // didDiscovery(testContext) - // dbInitOptions(testContext) - // utils(testContext) - // web3(testContext) - // didCommWithEthrDidFlow(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + saveClaims(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) + didDiscovery(testContext) + dbInitOptions(testContext) + utils(testContext) + web3(testContext) + didCommWithEthrDidFlow(testContext) didCommWithPeerDidFlow(testContext) - // credentialStatus(testContext) - // ethrDidFlowSigned(testContext) + credentialStatus(testContext) + ethrDidFlowSigned(testContext) }) diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index 217cde936..d3c7cff35 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -224,18 +224,18 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local json-data-store integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // saveClaims(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + saveClaims(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) utils(testContext) - // credentialStatus(testContext) + credentialStatus(testContext) }) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index e5f17376d..f184d3c2c 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -220,19 +220,19 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local in-memory integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // saveClaims(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + saveClaims(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) utils(testContext) - // credentialStatus(testContext) - // credentialInterop(testContext) + credentialStatus(testContext) + credentialInterop(testContext) }) diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 473d1262f..048306e01 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -282,20 +282,20 @@ const tearDown = async (): Promise => { const testContext = { getAgent, setup, tearDown } describe('REST integration tests', () => { - // verifiableDataJWT(testContext) - // verifiableDataLD(testContext) - // verifiableDataEIP712(testContext) - // handleSdrMessage(testContext) - // resolveDid(testContext) - // webDidFlow(testContext) - // documentationExamples(testContext) - // keyManager(testContext) - // didManager(testContext) - // messageHandler(testContext) - // didCommPacking(testContext) - // didWithFakeDidFlow(testContext) - // didCommAndDataStoreWithCredentials(testContext) - // didDiscovery(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) + verifiableDataEIP712(testContext) + handleSdrMessage(testContext) + resolveDid(testContext) + webDidFlow(testContext) + documentationExamples(testContext) + keyManager(testContext) + didManager(testContext) + messageHandler(testContext) + didCommPacking(testContext) + didWithFakeDidFlow(testContext) + didCommAndDataStoreWithCredentials(testContext) + didDiscovery(testContext) utils(testContext) - // credentialStatus(testContext) + credentialStatus(testContext) }) From 425b1e114e4c4ddde16ce37625c356d91273a772 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 18:57:01 -0400 Subject: [PATCH 5/7] chore: cleanup --- packages/did-provider-peer/README.md | 4 ++-- packages/did-provider-peer/package.json | 4 ++-- packages/did-provider-peer/src/peer-did-provider.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/did-provider-peer/README.md b/packages/did-provider-peer/README.md index 4c74f6658..b54e4d98b 100644 --- a/packages/did-provider-peer/README.md +++ b/packages/did-provider-peer/README.md @@ -1,4 +1,4 @@ # Veramo did:peer provider -This package contains an implementation of `AbstractIdentifierProvider` for the `did:key` method. -This enables creation and control of `did:key` entities. +This package contains an implementation of `AbstractIdentifierProvider` for the `did:peer` method. +This enables creation and control of `did:peer` entities. diff --git a/packages/did-provider-peer/package.json b/packages/did-provider-peer/package.json index 8a7e1c982..42d492481 100644 --- a/packages/did-provider-peer/package.json +++ b/packages/did-provider-peer/package.json @@ -39,9 +39,9 @@ "access": "public" }, "repository": "git@github.com:uport-project/veramo.git", - "author": "Alex Andrei - roots.id", + "author": "Nick Reynolds ", "contributors": [ - "Nick Reynolds " + "Alex Andrei " ], "license": "Apache-2.0", "keywords": [], diff --git a/packages/did-provider-peer/src/peer-did-provider.ts b/packages/did-provider-peer/src/peer-did-provider.ts index 47b681748..d711629f0 100644 --- a/packages/did-provider-peer/src/peer-did-provider.ts +++ b/packages/did-provider-peer/src/peer-did-provider.ts @@ -1,4 +1,4 @@ -import { IIdentifier, IKey, IService, IAgentContext, IKeyManager, DIDDocument } from '@veramo/core-types' +import { IIdentifier, IKey, IService, IAgentContext, IKeyManager } from '@veramo/core-types' import { AbstractIdentifierProvider } from '@veramo/did-manager' import Multibase from 'multibase' import Multicodec from 'multicodec' From cad66b4afb819dea8bd5d7f75eb9328431965427 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 19:15:20 -0400 Subject: [PATCH 6/7] chore: cleanup --- __tests__/shared/didCommWithPeerDidFlow.ts | 5 +++- packages/did-comm/src/didcomm.ts | 4 --- .../utils/src/__tests__/did-utils.test.ts | 27 ++++++++++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/__tests__/shared/didCommWithPeerDidFlow.ts b/__tests__/shared/didCommWithPeerDidFlow.ts index 8e4a66dbb..188a765c7 100644 --- a/__tests__/shared/didCommWithPeerDidFlow.ts +++ b/__tests__/shared/didCommWithPeerDidFlow.ts @@ -54,7 +54,7 @@ export default (testContext: { afterAll(testContext.tearDown) it('should pack and unpack a message', async () => { - expect.assertions(1) + expect.assertions(2) const message = { type: 'test', @@ -69,6 +69,9 @@ export default (testContext: { }) console.log("packedMessage: ", packedMessage) expect(packedMessage).toBeDefined() + + const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage) + expect(unpackedMessage.message.id).toEqual('test') }) }) } diff --git a/packages/did-comm/src/didcomm.ts b/packages/did-comm/src/didcomm.ts index 8b343148a..d260da4d5 100644 --- a/packages/did-comm/src/didcomm.ts +++ b/packages/did-comm/src/didcomm.ts @@ -263,12 +263,8 @@ export class DIDComm implements IAgentPlugin { } // 1.1 check that args.message.from is a managed DID const sender: IIdentifier = await context.agent.didManagerGet({ did: args?.message?.from }) - - console.log("sender: ", sender) - // 1.2 match key agreement keys from DID to managed keys const senderKeys: _ExtendedIKey[] = await mapIdentifierKeysToDoc(sender, 'keyAgreement', context) - console.log("senderKeys: ", senderKeys) // try to find a sender key by keyRef, otherwise pick the first one let senderKey if (isDefined(keyRef)) { diff --git a/packages/utils/src/__tests__/did-utils.test.ts b/packages/utils/src/__tests__/did-utils.test.ts index b92a51e83..f014c5447 100644 --- a/packages/utils/src/__tests__/did-utils.test.ts +++ b/packages/utils/src/__tests__/did-utils.test.ts @@ -1,4 +1,5 @@ -import { getChainIdForDidEthr, getEthereumAddress } from '../did-utils.js' +import { extractPublicKeyHex, getChainIdForDidEthr, getEthereumAddress } from '../did-utils.js' +import { bytesToMultibase, hexToBytes } from '../encodings.js' describe('@veramo/utils did utils', () => { it(`should return correct chainId for did:ethr`, () => { @@ -54,4 +55,28 @@ describe('@veramo/utils did utils', () => { expect(getEthereumAddress(verificationMethod)).toEqual("0x923f7158062db4761a8917ad1628d11536c5f07b".toLowerCase()) }) + + it('should convert to multibase and back', async () => { + const publicKeyHex = '6bb3f30242ac89bb6baa169fd5d1fea5adb61ce5b3cfee9e157e699a51983869' + const computedMultibase = bytesToMultibase(hexToBytes(publicKeyHex), 'Ed25519') + + // // multibase + multicodec + let expectedMultibase = `z6MkmhgpMDfiVVfLShamhYVCsWX6jQLVmwxXLtUykKFw3LwJ`; + + expect(computedMultibase).toEqual(expectedMultibase) + + const computedXMultibase = bytesToMultibase(hexToBytes(publicKeyHex), 'X25519') + let expectedXMultibase = `z6LSivbwHHE9FQtcRb7qYd3KM1Bakybm4ftKXrHjQVwSqVvg`; + + expect(computedXMultibase).toEqual(expectedXMultibase) + + const computedHex = extractPublicKeyHex({ + publicKeyMultibase: expectedMultibase, + type: 'Ed25519VerificationKey2020', + id: 'dummy key', + controller: 'dummy controller', + }) + + expect(computedHex).toEqual(publicKeyHex) + }) }) From dfb84c1beae7bc378b7c26c8b23ea7259e1a6411 Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Wed, 5 Apr 2023 19:46:27 -0400 Subject: [PATCH 7/7] chore: cleanup --- __tests__/shared/didCommWithPeerDidFlow.ts | 1 - packages/did-comm/src/utils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/__tests__/shared/didCommWithPeerDidFlow.ts b/__tests__/shared/didCommWithPeerDidFlow.ts index 188a765c7..ce52c8144 100644 --- a/__tests__/shared/didCommWithPeerDidFlow.ts +++ b/__tests__/shared/didCommWithPeerDidFlow.ts @@ -67,7 +67,6 @@ export default (testContext: { packing: 'authcrypt', message, }) - console.log("packedMessage: ", packedMessage) expect(packedMessage).toBeDefined() const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage) diff --git a/packages/did-comm/src/utils.ts b/packages/did-comm/src/utils.ts index a571c3691..f62aa6505 100644 --- a/packages/did-comm/src/utils.ts +++ b/packages/did-comm/src/utils.ts @@ -39,7 +39,7 @@ export async function extractSenderEncryptionKey( didUrl: protectedHeader.skid, section: 'keyAgreement', })) as _ExtendedVerificationMethod - if (!['Ed25519VerificationKey2018', 'X25519KeyAgreementKey2019', 'JsonWebKey2020'].includes(sKey.type)) { + if (!['Ed25519VerificationKey2018', 'X25519KeyAgreementKey2019', 'JsonWebKey2020', 'Ed25519VerificationKey2020', 'X25519KeyAgreementKey2020'].includes(sKey.type)) { throw new Error(`not_supported: sender key of type ${sKey.type} is not supported`) } let publicKeyHex = extractPublicKeyHex(sKey, true)