From 57b6c583138b0f8f283f4f00b27573529f394a7a Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 15 Aug 2023 15:57:27 +0200 Subject: [PATCH] feat(core-types): export a basic mapping between key types and algorithms --- packages/core-types/src/plugin.schema.json | 24 +++++++++++---- packages/core-types/src/types/IIdentifier.ts | 30 +++++++++++++++++-- .../kms-local/src/key-management-system.ts | 20 ++++++++----- .../src/action-handler.ts | 20 +++++-------- packages/utils/src/__tests__/utils.test.ts | 21 ++++++++++++- packages/utils/src/type-utils.ts | 14 +++++++++ 6 files changed, 101 insertions(+), 28 deletions(-) diff --git a/packages/core-types/src/plugin.schema.json b/packages/core-types/src/plugin.schema.json index e42167d4c..e8576f669 100644 --- a/packages/core-types/src/plugin.schema.json +++ b/packages/core-types/src/plugin.schema.json @@ -549,12 +549,16 @@ "algorithms": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/TAlg" } } }, "description": "This encapsulates data about a key.\n\nImplementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | AbstractKeyManagementSystem } should populate this object, for each key, with the algorithms that can be performed using it.\n\nThis can also be used to add various tags to the keys under management." }, + "TAlg": { + "type": "string", + "description": "Known algorithms supported by some of the above key types defined by {@link TKeyType } .\n\nActual implementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | Key Management Systems } can support more. One should check the {@link IKey.meta.algorithms } property to see what is possible for a particular managed key." + }, "ManagedKeyInfo": { "type": "object", "properties": { @@ -736,7 +740,7 @@ "type", "publicKeyHex" ], - "description": "Cryptographic key" + "description": "Cryptographic key, usually managed by the current Veramo instance." }, "MinimalImportableKey": { "$ref": "#/components/schemas/RequireOnly", @@ -1099,7 +1103,7 @@ "type", "publicKeyHex" ], - "description": "Cryptographic key" + "description": "Cryptographic key, usually managed by the current Veramo instance." }, "TKeyType": { "type": "string", @@ -1119,12 +1123,16 @@ "algorithms": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/TAlg" } } }, "description": "This encapsulates data about a key.\n\nImplementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | AbstractKeyManagementSystem } should populate this object, for each key, with the algorithms that can be performed using it.\n\nThis can also be used to add various tags to the keys under management." }, + "TAlg": { + "type": "string", + "description": "Known algorithms supported by some of the above key types defined by {@link TKeyType } .\n\nActual implementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | Key Management Systems } can support more. One should check the {@link IKey.meta.algorithms } property to see what is possible for a particular managed key." + }, "IDIDManagerAddServiceArgs": { "type": "object", "properties": { @@ -2776,7 +2784,7 @@ "type", "publicKeyHex" ], - "description": "Cryptographic key" + "description": "Cryptographic key, usually managed by the current Veramo instance." }, "TKeyType": { "type": "string", @@ -2796,12 +2804,16 @@ "algorithms": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/TAlg" } } }, "description": "This encapsulates data about a key.\n\nImplementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | AbstractKeyManagementSystem } should populate this object, for each key, with the algorithms that can be performed using it.\n\nThis can also be used to add various tags to the keys under management." }, + "TAlg": { + "type": "string", + "description": "Known algorithms supported by some of the above key types defined by {@link TKeyType } .\n\nActual implementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | Key Management Systems } can support more. One should check the {@link IKey.meta.algorithms } property to see what is possible for a particular managed key." + }, "IService": { "type": "object", "properties": { diff --git a/packages/core-types/src/types/IIdentifier.ts b/packages/core-types/src/types/IIdentifier.ts index c8ae990cf..e73606de4 100644 --- a/packages/core-types/src/types/IIdentifier.ts +++ b/packages/core-types/src/types/IIdentifier.ts @@ -54,7 +54,33 @@ export type MinimalImportableIdentifier = { export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' /** - * Cryptographic key + * Known algorithms supported by some of the above key types defined by {@link TKeyType}. + * + * Actual implementations of {@link @veramo/key-manager#AbstractKeyManagementSystem | Key Management Systems} can + * support more. One should check the {@link IKey.meta.algorithms} property to see what is possible + * for a particular managed key. + * + * @public + */ +export type TAlg = 'ES256K' | 'ES256K-R' | 'ES256' | 'EdDSA' | 'ECDH' | 'ECDH-ES' | 'ECDH-1PU' | string + +/** + * Mapping of known key types({@link TKeyType}) to the known algorithms({@link TAlg}) they should support. + * + * @public + */ +export const KEY_ALG_MAPPING: Record> = { + Secp256k1: ['ES256K', 'ES256K-R'], + Secp256r1: ['ES256', 'ECDH', 'ECDH-ES', 'ECDH-1PU'], + Ed25519: ['EdDSA'], + X25519: ['ECDH', 'ECDH-ES', 'ECDH-1PU'], + Bls12381G1: [], + Bls12381G2: [], +} as const + +/** + * Cryptographic key, usually managed by the current Veramo instance. + * * @public */ export interface IKey { @@ -100,7 +126,7 @@ export interface IKey { * @public */ export interface KeyMetadata { - algorithms?: string[] + algorithms?: TAlg[] [x: string]: any } diff --git a/packages/kms-local/src/key-management-system.ts b/packages/kms-local/src/key-management-system.ts index 5ac4143bb..74564d0fc 100644 --- a/packages/kms-local/src/key-management-system.ts +++ b/packages/kms-local/src/key-management-system.ts @@ -1,4 +1,11 @@ -import { IKey, ManagedKeyInfo, MinimalImportableKey, RequireOnly, TKeyType } from '@veramo/core-types' +import { + IKey, + KEY_ALG_MAPPING, + ManagedKeyInfo, + MinimalImportableKey, + RequireOnly, + TKeyType, +} from '@veramo/core-types' import { AbstractKeyManagementSystem, AbstractPrivateKeyStore, @@ -8,6 +15,7 @@ import { import { EdDSASigner, ES256KSigner, ES256Signer } from 'did-jwt' import { ed25519, x25519 } from '@noble/curves/ed25519' +import { p256 } from '@noble/curves/p256' import { TransactionRequest } from '@ethersproject/abstract-provider' import { toUtf8String } from '@ethersproject/strings' import { parse } from '@ethersproject/transactions' @@ -16,7 +24,6 @@ import { SigningKey } from '@ethersproject/signing-key' import { randomBytes } from '@ethersproject/random' import { arrayify, hexlify } from '@ethersproject/bytes' import Debug from 'debug' -import { p256 } from '@noble/curves/p256' import { bytesToHex, concat, @@ -302,7 +309,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { kid: args.alias || publicKeyHex, publicKeyHex, meta: { - algorithms: ['Ed25519', 'EdDSA'], + algorithms: [...KEY_ALG_MAPPING[args.type], 'Ed25519'], }, } break @@ -317,8 +324,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { publicKeyHex, meta: { algorithms: [ - 'ES256K', - 'ES256K-R', + ...KEY_ALG_MAPPING[args.type], 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage', @@ -336,7 +342,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { kid: args.alias || publicKeyHex, publicKeyHex, meta: { - algorithms: ['ES256'], + algorithms: ['ES256'], // ECDH not supported yet by this KMS }, } break @@ -349,7 +355,7 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { kid: args.alias || publicKeyHex, publicKeyHex: publicKeyHex, meta: { - algorithms: ['ECDH', 'ECDH-ES', 'ECDH-1PU'], + algorithms: [...KEY_ALG_MAPPING[args.type]], }, } break diff --git a/packages/selective-disclosure/src/action-handler.ts b/packages/selective-disclosure/src/action-handler.ts index 6552e3bdb..c9c978753 100644 --- a/packages/selective-disclosure/src/action-handler.ts +++ b/packages/selective-disclosure/src/action-handler.ts @@ -7,8 +7,9 @@ import { IDIDManager, IKey, IKeyManager, + KEY_ALG_MAPPING, + TAlg, TClaimsColumns, - TKeyType, VerifiableCredential, VerifiablePresentation, } from '@veramo/core-types' @@ -32,17 +33,9 @@ import { computeEntryHash, decodeCredentialToObject, extractIssuer, + intersect, } from '@veramo/utils' -const KEY_ALG_MAPPING: Record = { - Secp256k1: 'ES256K', - Secp256r1: 'ES256', - Ed25519: 'EdDSA', - X25519: null, - Bls12381G1: null, - Bls12381G2: null, -} as const - /** * This class adds support for creating * {@link https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md | Selective Disclosure} @@ -89,17 +82,20 @@ export class SelectiveDisclosure implements IAgentPlugin { delete data.issuer Debug('veramo:selective-disclosure:create-sdr')('Signing SDR with', identifier.did) + // only these signature algorithms are supported + const algs: TAlg[] = ['ES256K', 'ES256K-R', 'EdDSA', 'ES256'] + const key = identifier.keys.find((k: IKey) => { return ( Object.keys(KEY_ALG_MAPPING).includes(k.type) && KEY_ALG_MAPPING[k.type] && - k.meta?.algorithms?.includes(KEY_ALG_MAPPING[k.type] ?? 'unsupported') + intersect(intersect(k.meta?.algorithms, KEY_ALG_MAPPING[k.type]), algs).length > 0 ) }) if (!key) throw Error('Signing key not found') - const algorithm = KEY_ALG_MAPPING[key?.type ?? ''] + const algorithm = KEY_ALG_MAPPING[key.type]?.[0] if (!algorithm) throw Error('Unsupported key type') diff --git a/packages/utils/src/__tests__/utils.test.ts b/packages/utils/src/__tests__/utils.test.ts index c70901a6b..ec9bdb8c3 100644 --- a/packages/utils/src/__tests__/utils.test.ts +++ b/packages/utils/src/__tests__/utils.test.ts @@ -1,4 +1,4 @@ -import { asArray, isDefined } from '../type-utils.js' +import { asArray, intersect, isDefined } from '../type-utils.js' describe('@veramo/utils type utils', () => { it('isDefined should return correct results', () => { @@ -20,4 +20,23 @@ describe('@veramo/utils type utils', () => { expect(asArray(undefined)).toEqual([]) expect(asArray(null)).toEqual([]) }) + + describe('intersect', () => { + it('should work with primitive types', () => { + expect(intersect(['a'], ['b'])).toStrictEqual([]) + expect(intersect(['a', 'a'], ['b'])).toStrictEqual([]) + expect(intersect(['a', 'a', 'b'], ['b'])).toStrictEqual(['b']) + expect(intersect(['a', 'a', 'b'], ['b', 'a'])).toStrictEqual(['a', 'b']) + expect(intersect(['a', 'a', 'b', 1], ['b', 'a', 2])).toStrictEqual(['a', 'b']) + expect(intersect(['a', 'a', 'b', 1], ['b', 1, 2])).toStrictEqual(['b', 1]) + expect(intersect([1, false], [true])).toStrictEqual([]) + expect(intersect([1, false, null], [true, null])).toStrictEqual([null]) + expect(intersect([1, false, undefined], [true, undefined])).toStrictEqual([undefined]) + expect(intersect([1], [true])).toStrictEqual([]) + }) + + it('does not work with objects since references are different', () => { + expect(intersect([{}], [{}])).toStrictEqual([]) + }) + }) }) diff --git a/packages/utils/src/type-utils.ts b/packages/utils/src/type-utils.ts index 5b565d9da..231e409f5 100644 --- a/packages/utils/src/type-utils.ts +++ b/packages/utils/src/type-utils.ts @@ -33,3 +33,17 @@ export function asArray(arg?: T | T[] | any): (T | any)[] { export function isIterable(obj: any): obj is Iterable { return obj != null && typeof obj[Symbol.iterator] === 'function' } + +/** + * Compute the intersection of two arrays + * Elements are compared by reference so object types will appear as unique even if they contain the same data. + * + * @param a - first array + * @param b - second array + * @returns The intersection of the two arrays. + * + */ +export function intersect(a: T[] | any, b: any[] | any): T[] { + const setB = new Set(asArray(b)); + return [...new Set(asArray(a))].filter(x => setB.has(x)); +}