diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 38030c6ab7..baa95e5324 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -346,7 +346,8 @@ export class AskarWallet implements Wallet { * Create a key with an optional seed and keyType. * The keypair is also automatically stored in the wallet afterwards * - * @param seed string The seed for creating a key + * @param privateKey Buffer Optional privateKey for creating a key + * @param seed string Optional seed for creating a key * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -354,13 +355,21 @@ export class AskarWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + if (keyTypeSupportedByAskar(keyType)) { const algorithm = keyAlgFromString(keyType) - // Create key from seed - const key = seed ? AskarKey.fromSeed({ seed: Buffer.from(seed), algorithm }) : AskarKey.generate(algorithm) + // Create key + const key = privateKey + ? AskarKey.fromSecretBytes({ secretKey: privateKey, algorithm }) + : seed + ? AskarKey.fromSeed({ seed, algorithm }) + : AskarKey.generate(algorithm) // Store key await this.session.insertKey({ key, name: TypedArrayEncoder.toBase58(key.publicBytes) }) @@ -370,7 +379,7 @@ export class AskarWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } @@ -398,7 +407,6 @@ export class AskarWallet implements Wallet { if (!TypedArrayEncoder.isTypedArray(data)) { throw new WalletError(`Currently not supporting signing of multiple messages`) } - const keyEntry = await this.session.fetchKey({ name: key.publicKeyBase58 }) if (!keyEntry) { diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 94732a15b0..40dea0cbc8 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -36,7 +36,8 @@ const walletConfig: WalletConfig = { describe('AskarWallet basic operations', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') + const privateKey = TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -62,12 +63,36 @@ describe('AskarWallet basic operations', () => { expect(nonce).toMatch(/[0-9]+/) }) - test('Create ed25519 keypair', async () => { - await expect( - askarWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from seed', async () => { + const key = await askarWallet.createKey({ + seed, + keyType: KeyType.Ed25519, + }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Create ed25519 keypair from private key', async () => { + const key = await askarWallet.createKey({ + privateKey, keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Attempt to create ed25519 keypair from both seed and private key', async () => { + await expect( + askarWallet.createKey({ + privateKey, + seed, + keyType: KeyType.Ed25519, + }) + ).rejects.toThrowError() }) test('Create x25519 keypair', async () => { @@ -109,7 +134,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { describe('AskarWallet with custom signing provider', () => { let askarWallet: AskarWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') class DummySigningProvider implements SigningProvider { @@ -117,7 +142,7 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { public async createKeyPair(options: CreateKeyPairOptions): Promise { return { - publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')), + publicKeyBase58: encodeToBase58(Buffer.from(options.seed || TypedArrayEncoder.fromString('publicKeyBase58'))), privateKeyBase58: 'privateKeyBase58', keyType: KeyType.Bls12381g1g2, } @@ -175,11 +200,11 @@ describe.skip('Currently, all KeyTypes are supported by Askar natively', () => { }) test('Attempt to create the same custom keypair twice', async () => { - await askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 }) + await askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) - await expect(askarWallet.createKey({ seed: 'keybase58', keyType: KeyType.Bls12381g1g2 })).rejects.toThrow( - WalletError - ) + await expect( + askarWallet.createKey({ seed: TypedArrayEncoder.fromString('keybase58'), keyType: KeyType.Bls12381g1g2 }) + ).rejects.toThrow(WalletError) }) }) }) diff --git a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts index fd7c1de9cf..b74730af47 100644 --- a/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts +++ b/packages/bbs-signatures/src/Bls12381g2SigningProvider.ts @@ -15,11 +15,12 @@ export class Bls12381g2SigningProvider implements SigningProvider { * * @throws {SigningProviderError} When a key could not be created */ - public async createKeyPair({ seed }: CreateKeyPairOptions): Promise { - // Generate bytes from the seed as required by the bbs-signatures libraries - const seedBytes = seed ? TypedArrayEncoder.fromString(seed) : undefined + public async createKeyPair({ seed, privateKey }: CreateKeyPairOptions): Promise { + if (privateKey) { + throw new SigningProviderError('Cannot create keypair from private key') + } - const blsKeyPair = await generateBls12381G2KeyPair(seedBytes) + const blsKeyPair = await generateBls12381G2KeyPair(seed) return { keyType: KeyType.Bls12381g2, diff --git a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts index 936dfbe22e..efbc08027a 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts @@ -19,6 +19,7 @@ import { W3cVerifiablePresentation, IndyWallet, Ed25519Signature2018, + TypedArrayEncoder, } from '@aries-framework/core' import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry' @@ -62,7 +63,8 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { let wallet: IndyWallet let agentContext: AgentContext let w3cCredentialService: W3cCredentialService - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -219,7 +221,10 @@ describeSkipNode17And18('BBS W3cCredentialService', () => { describe('signPresentation', () => { it('should sign the presentation successfully', async () => { - const signingKey = await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const signingKey = await wallet.createKey({ + privateKey, + keyType: KeyType.Ed25519, + }) const signingDidKey = new DidKey(signingKey) const verificationMethod = `${signingDidKey.did}#${signingDidKey.key.fingerprint}` const presentation = JsonTransformer.fromJSON(BbsBlsSignature2020Fixtures.TEST_VP_DOCUMENT, W3cPresentation) diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts index db67e0c5a1..d428dd65be 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.e2e.test.ts @@ -26,7 +26,7 @@ const walletConfig: WalletConfig = { describeSkipNode17And18('BBS Signing Provider', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const seed = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index a22408ce87..b445f0f24b 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -3,6 +3,8 @@ import type { JsonCredential, JsonLdCredentialDetailFormat } from '../../core/sr import type { Wallet } from '../../core/src/wallet' import type { CredentialTestsAgent } from '../../core/tests/helpers' +import { TypedArrayEncoder } from '@aries-framework/core' + import { InjectionSymbols } from '../../core/src/constants' import { KeyType } from '../../core/src/crypto' import { CredentialState } from '../../core/src/modules/credentials/models' @@ -29,14 +31,14 @@ describeSkipNode17And18('credentials, BBS+ signature', () => { let issuerDidKey: DidKey let didCommMessageRepository: DidCommMessageRepository let signCredentialOptions: JsonLdCredentialDetailFormat - const seed = 'testseed000000000000000000000001' + const seed = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { ;({ faberAgent, aliceAgent, aliceConnection } = await setupCredentialTests( 'Faber Agent Credentials LD BBS+', 'Alice Agent Credentials LD BBS+' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + await wallet.createKey({ keyType: KeyType.Ed25519, privateKey: seed }) const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, seed }) issuerDidKey = new DidKey(key) diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index 6b5d5fb258..4080ab2f24 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -3,7 +3,7 @@ import type { Key, Wallet } from '@aries-framework/core' import { getAgentConfig, getAgentContext } from '../../../tests/helpers' import { DidKey } from '../../modules/dids' -import { Buffer, JsonEncoder } from '../../utils' +import { Buffer, JsonEncoder, TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { JwsService } from '../JwsService' import { KeyType } from '../KeyType' @@ -28,8 +28,14 @@ describe('JwsService', () => { await wallet.createAndOpen(config.walletConfig!) jwsService = new JwsService() - didJwsz6MkfKey = await wallet.createKey({ seed: didJwsz6Mkf.SEED, keyType: KeyType.Ed25519 }) - didJwsz6MkvKey = await wallet.createKey({ seed: didJwsz6Mkv.SEED, keyType: KeyType.Ed25519 }) + didJwsz6MkfKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkf.SEED), + keyType: KeyType.Ed25519, + }) + didJwsz6MkvKey = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString(didJwsz6Mkv.SEED), + keyType: KeyType.Ed25519, + }) }) afterAll(async () => { diff --git a/packages/core/src/crypto/signing-provider/SigningProvider.ts b/packages/core/src/crypto/signing-provider/SigningProvider.ts index 2f2c31e701..3e70d67694 100644 --- a/packages/core/src/crypto/signing-provider/SigningProvider.ts +++ b/packages/core/src/crypto/signing-provider/SigningProvider.ts @@ -20,7 +20,8 @@ export interface VerifyOptions { } export interface CreateKeyPairOptions { - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface SigningProvider { diff --git a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts index 894520edaf..5336162c59 100644 --- a/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts +++ b/packages/core/src/decorators/signature/SignatureDecoratorUtils.test.ts @@ -1,6 +1,7 @@ import { getAgentConfig } from '../../../tests/helpers' import { KeyType } from '../../crypto' import { SigningProviderRegistry } from '../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../utils' import { IndyWallet } from '../../wallet/IndyWallet' import { SignatureDecorator } from './SignatureDecorator' @@ -53,8 +54,8 @@ describe('Decorators | Signature | SignatureDecoratorUtils', () => { }) test('signData signs json object and returns SignatureDecorator', async () => { - const seed1 = '00000000000000000000000000000My1' - const key = await wallet.createKey({ seed: seed1, keyType: KeyType.Ed25519 }) + const privateKey = TypedArrayEncoder.fromString('00000000000000000000000000000My1') + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const result = await signData(data, wallet, key.publicKeyBase58) diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts index 2e40d6234f..ea148db552 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.connectionless-credentials.test.ts @@ -13,6 +13,7 @@ import testLogger from '../../../../../../tests/logger' import { Agent } from '../../../../../agent/Agent' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonEncoder } from '../../../../../utils/JsonEncoder' import { W3cVcModule } from '../../../../vc' import { customDocumentLoader } from '../../../../vc/__tests__/documentLoader' @@ -62,7 +63,7 @@ describe('credentials', () => { let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']> let faberReplay: ReplaySubject let aliceReplay: ReplaySubject - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') const TEST_LD_DOCUMENT: JsonCredential = { '@context': [CREDENTIALS_CONTEXT_V1_URL, 'https://www.w3.org/2018/credentials/examples/v1'], type: ['VerifiableCredential', 'UniversityDegreeCredential'], @@ -106,7 +107,7 @@ describe('credentials', () => { .subscribe(aliceReplay) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts index ad08852a17..d8a3521a27 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials-auto-accept.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { AriesFrameworkError } from '../../../../../error/AriesFrameworkError' +import { TypedArrayEncoder } from '../../../../../utils' import { CREDENTIALS_CONTEXT_V1_URL } from '../../../../vc/constants' import { AutoAcceptCredential, CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -33,7 +34,7 @@ describe('credentials', () => { let aliceCredentialRecord: CredentialExchangeRecord let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') describe('Auto accept on `always`', () => { beforeAll(async () => { @@ -44,7 +45,7 @@ describe('credentials', () => { )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { @@ -143,7 +144,7 @@ describe('credentials', () => { AutoAcceptCredential.ContentApproved )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: TEST_LD_DOCUMENT, options: { diff --git a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts index c8f9a64d20..98621d7a40 100644 --- a/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts +++ b/packages/core/src/modules/credentials/protocol/v2/__tests__/v2.ldproof.credentials.propose-offerED25519.test.ts @@ -8,6 +8,7 @@ import testLogger from '../../../../../../tests/logger' import { InjectionSymbols } from '../../../../../constants' import { KeyType } from '../../../../../crypto' import { DidCommMessageRepository } from '../../../../../storage' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { CredentialState } from '../../../models' import { CredentialExchangeRecord } from '../../../repository/CredentialExchangeRecord' @@ -57,7 +58,7 @@ describe('credentials', () => { let signCredentialOptions: JsonLdCredentialDetailFormat let wallet - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') let credDefId: string beforeAll(async () => { @@ -66,7 +67,7 @@ describe('credentials', () => { 'Alice Agent Credentials LD' )) wallet = faberAgent.dependencyManager.resolve(InjectionSymbols.Wallet) - await wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) signCredentialOptions = { credential: inputDocAsJson, options: { diff --git a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts index 9599d06c92..ccd60edf71 100644 --- a/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts +++ b/packages/core/src/modules/dids/__tests__/dids-registrar.e2e.test.ts @@ -47,7 +47,7 @@ describe('dids', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) @@ -97,7 +97,7 @@ describe('dids', () => { ], id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', }, - secret: { seed: '96213c3d7fc8d4d6754c7a0fd969598e' }, + secret: { privateKey: '96213c3d7fc8d4d6754c7a0fd969598e' }, }, }) }) @@ -110,7 +110,7 @@ describe('dids', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'e008ef10b7c163114b3857542b3736eb', + privateKey: TypedArrayEncoder.fromString('e008ef10b7c163114b3857542b3736eb'), }, }) @@ -160,7 +160,7 @@ describe('dids', () => { ], id: 'did:peer:0z6Mkuo91yRhTWDrFkdNBcLXAbvtUiq2J9E4QQcfYZt4hevkh', }, - secret: { seed: 'e008ef10b7c163114b3857542b3736eb' }, + secret: { privateKey: 'e008ef10b7c163114b3857542b3736eb' }, }, }) }) @@ -168,11 +168,13 @@ describe('dids', () => { it('should create a did:sov did', async () => { // Generate a seed and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const publicKeyEd25519 = generateKeyPairFromSeed(TypedArrayEncoder.fromString(seed)).publicKey + const publicKeyEd25519 = generateKeyPairFromSeed(privateKey).publicKey const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(publicKeyEd25519)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyEd25519) const indyDid = indyDidFromPublicKeyBase58(ed25519PublicKeyBase58) @@ -193,7 +195,7 @@ describe('dids', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -261,7 +263,7 @@ describe('dids', () => { id: `did:sov:${indyDid}`, }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) diff --git a/packages/core/src/modules/dids/__tests__/peer-did.test.ts b/packages/core/src/modules/dids/__tests__/peer-did.test.ts index 5467b601c9..c352fc0383 100644 --- a/packages/core/src/modules/dids/__tests__/peer-did.test.ts +++ b/packages/core/src/modules/dids/__tests__/peer-did.test.ts @@ -8,7 +8,7 @@ import { InjectionSymbols } from '../../../constants' import { Key, KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' import { IndyStorageService } from '../../../storage/IndyStorageService' -import { JsonTransformer } from '../../../utils' +import { JsonTransformer, TypedArrayEncoder } from '../../../utils' import { IndyWallet } from '../../../wallet/IndyWallet' import { DidsModuleConfig } from '../DidsModuleConfig' import { DidCommV1Service, DidDocument, DidDocumentBuilder } from '../domain' @@ -62,9 +62,12 @@ describe('peer dids', () => { test('create a peer did method 1 document from ed25519 keys with a service', async () => { // The following scenario show how we could create a key and create a did document from it for DID Exchange - const ed25519Key = await wallet.createKey({ seed: 'astringoftotalin32characterslong', keyType: KeyType.Ed25519 }) + const ed25519Key = await wallet.createKey({ + privateKey: TypedArrayEncoder.fromString('astringoftotalin32characterslong'), + keyType: KeyType.Ed25519, + }) const mediatorEd25519Key = await wallet.createKey({ - seed: 'anotherstringof32characterslong1', + privateKey: TypedArrayEncoder.fromString('anotherstringof32characterslong1'), keyType: KeyType.Ed25519, }) diff --git a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts index 6a4def3cd2..645deee3a1 100644 --- a/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/key/KeyDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -16,6 +17,7 @@ export class KeyDidRegistrar implements DidRegistrar { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -28,7 +30,7 @@ export class KeyDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -39,10 +41,22 @@ export class KeyDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + try { const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) const didKey = new DidKey(key) @@ -67,7 +81,8 @@ export class KeyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -115,7 +130,8 @@ export interface KeyDidCreateOptions extends DidCreateOptions { keyType: KeyType } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts index e859dcf795..9f084a5b8d 100644 --- a/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/key/__tests__/KeyDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidDocumentRole } from '../../../domain/DidDocumentRole' import { DidRepository } from '../../../repository/DidRepository' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('KeyDidRegistrar', () => { it('should correctly create a did:key document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -40,7 +41,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) @@ -52,12 +53,12 @@ describe('DidRegistrar', () => { did: 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didKeyz6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) - expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, seed }) + expect(walletMock.createKey).toHaveBeenCalledWith({ keyType: KeyType.Ed25519, privateKey }) }) it('should return an error state if no key type is provided', async () => { @@ -77,7 +78,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await keyDidRegistrar.create(agentContext, { method: 'key', @@ -85,7 +86,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -94,13 +95,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:key:z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await keyDidRegistrar.create(agentContext, { @@ -110,7 +111,7 @@ describe('DidRegistrar', () => { keyType: KeyType.Ed25519, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts index 057ed96c9d..83b171e978 100644 --- a/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/peer/PeerDidRegistrar.ts @@ -1,5 +1,6 @@ import type { AgentContext } from '../../../../agent' import type { KeyType } from '../../../../crypto' +import type { Buffer } from '../../../../utils' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -27,6 +28,7 @@ export class PeerDidRegistrar implements DidRegistrar { if (isPeerDidNumAlgo0CreateOptions(options)) { const keyType = options.options.keyType const seed = options.secret?.seed + const privateKey = options.secret?.privateKey if (!keyType) { return { @@ -39,7 +41,7 @@ export class PeerDidRegistrar implements DidRegistrar { } } - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -50,9 +52,21 @@ export class PeerDidRegistrar implements DidRegistrar { } } + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid private key provided', + }, + } + } + const key = await agentContext.wallet.createKey({ keyType, seed, + privateKey, }) // TODO: validate did:peer document @@ -105,7 +119,8 @@ export class PeerDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -170,7 +185,8 @@ export interface PeerDidNumAlgo0CreateOptions extends DidCreateOptions { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts index f20079cd4f..b974cff303 100644 --- a/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/peer/__tests__/PeerDidRegistrar.test.ts @@ -3,6 +3,7 @@ import type { Wallet } from '../../../../../wallet' import { getAgentContext, mockFunction } from '../../../../../../tests/helpers' import { KeyType } from '../../../../../crypto' import { Key } from '../../../../../crypto/Key' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { DidCommV1Service, DidDocumentBuilder } from '../../../domain' import { DidDocumentRole } from '../../../domain/DidDocumentRole' @@ -32,7 +33,7 @@ describe('DidRegistrar', () => { describe('PeerDidRegistrar', () => { describe('did:peer:0', () => { it('should correctly create a did:peer:0 document using Ed25519 key type', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const result = await peerDidRegistrar.create(agentContext, { method: 'peer', @@ -41,7 +42,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) @@ -53,7 +54,7 @@ describe('DidRegistrar', () => { did: 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU', didDocument: didPeer0z6MksLeFixture, secret: { - seed: '96213c3d7fc8d4d6754c712fd969598e', + privateKey: '96213c3d7fc8d4d6754c712fd969598e', }, }, }) @@ -78,7 +79,7 @@ describe('DidRegistrar', () => { }) }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await peerDidRegistrar.create(agentContext, { method: 'peer', options: { @@ -86,7 +87,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -95,13 +96,13 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) it('should store the did without the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const did = 'did:peer:0z6MksLeew51QS6Ca6tVKM56LQNbxCNVcLHv4xXj4jMkAhPWU' await peerDidRegistrar.create(agentContext, { @@ -111,7 +112,7 @@ describe('DidRegistrar', () => { numAlgo: PeerDidNumAlgo.InceptionKeyWithoutDoc, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts index 763f97b622..21781642ab 100644 --- a/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts +++ b/packages/core/src/modules/dids/methods/sov/IndySdkSovDidRegistrar.ts @@ -1,4 +1,5 @@ import type { AgentContext } from '../../../../agent' +import type { Buffer } from '../../../../utils' import type { IndyEndpointAttrib, IndyPool } from '../../../ledger' import type { DidRegistrar } from '../../domain/DidRegistrar' import type { DidCreateOptions, DidCreateResult, DidDeactivateResult, DidUpdateResult } from '../../types' @@ -24,15 +25,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { const didRepository = agentContext.dependencyManager.resolve(DidRepository) const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -56,7 +57,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // FIXME: once askar/indy-vdr is supported we need to adjust this to work with both indy-sdk and askar assertIndyWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await indy.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -115,7 +116,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -237,7 +238,7 @@ export interface SovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts index 761a2956b6..7837772932 100644 --- a/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts +++ b/packages/core/src/modules/dids/methods/sov/__tests__/IndySdkSovDidRegistrar.test.ts @@ -5,6 +5,7 @@ import type * as Indy from 'indy-sdk' import { getAgentConfig, getAgentContext, mockFunction, mockProperty } from '../../../../../../tests/helpers' import { SigningProviderRegistry } from '../../../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../../../utils' import { JsonTransformer } from '../../../../../utils/JsonTransformer' import { IndyWallet } from '../../../../../wallet/IndyWallet' import { IndyPoolService } from '../../../../ledger/services/IndyPoolService' @@ -52,7 +53,7 @@ describe('DidRegistrar', () => { jest.clearAllMocks() }) - it('should return an error state if an invalid seed is provided', async () => { + it('should return an error state if an invalid private key is provided', async () => { const result = await indySdkSovDidRegistrar.create(agentContext, { method: 'sov', @@ -61,7 +62,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: 'invalid', + privateKey: TypedArrayEncoder.fromString('invalid'), }, }) @@ -70,7 +71,7 @@ describe('DidRegistrar', () => { didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, }) }) @@ -89,7 +90,7 @@ describe('DidRegistrar', () => { alias: 'Hello', }, secret: { - seed: '12345678901234567890123456789012', + privateKey: TypedArrayEncoder.fromString('12345678901234567890123456789012'), }, }) @@ -123,7 +124,7 @@ describe('DidRegistrar', () => { }) it('should correctly create a did:sov document without services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -136,7 +137,7 @@ describe('DidRegistrar', () => { role: 'STEWARD', }, secret: { - seed, + privateKey, }, }) @@ -191,14 +192,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should correctly create a did:sov document with services', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('R1xKJw17sUoXhejEpugMYJ')) @@ -219,7 +220,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) @@ -298,14 +299,14 @@ describe('DidRegistrar', () => { keyAgreement: ['did:sov:R1xKJw17sUoXhejEpugMYJ#key-agreement-1'], }, secret: { - seed, + privateKey: privateKey.toString(), }, }, }) }) it('should store the did document', async () => { - const seed = '96213c3d7fc8d4d6754c712fd969598e' + const privateKey = TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c712fd969598e') const registerPublicDidSpy = jest.spyOn(indySdkSovDidRegistrar, 'registerPublicDid') registerPublicDidSpy.mockImplementationOnce(() => Promise.resolve('did')) @@ -326,7 +327,7 @@ describe('DidRegistrar', () => { }, }, secret: { - seed, + privateKey, }, }) diff --git a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts index d6f3eb266f..06e0b42245 100644 --- a/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts +++ b/packages/core/src/modules/vc/__tests__/W3cCredentialService.test.ts @@ -3,6 +3,7 @@ import type { AgentContext } from '../../../agent' import { getAgentConfig, getAgentContext, mockFunction } from '../../../../tests/helpers' import { KeyType } from '../../../crypto' import { SigningProviderRegistry } from '../../../crypto/signing-provider' +import { TypedArrayEncoder } from '../../../utils' import { JsonTransformer } from '../../../utils/JsonTransformer' import { IndyWallet } from '../../../wallet/IndyWallet' import { WalletError } from '../../../wallet/error' @@ -67,7 +68,7 @@ describe('W3cCredentialService', () => { let agentContext: AgentContext let w3cCredentialService: W3cCredentialService let w3cCredentialRepository: W3cCredentialRepository - const seed = 'testseed000000000000000000000001' + const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001') beforeAll(async () => { wallet = new IndyWallet(agentConfig.agentDependencies, agentConfig.logger, signingProviderRegistry) @@ -116,7 +117,10 @@ describe('W3cCredentialService', () => { let verificationMethod: string beforeAll(async () => { // TODO: update to use did registrar - const issuerKey = await wallet.createKey({ keyType: KeyType.Ed25519, seed }) + const issuerKey = await wallet.createKey({ + keyType: KeyType.Ed25519, + privateKey, + }) issuerDidKey = new DidKey(issuerKey) verificationMethod = `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}` }) diff --git a/packages/core/src/wallet/IndyWallet.test.ts b/packages/core/src/wallet/IndyWallet.test.ts index 07c5e74978..8a600a2592 100644 --- a/packages/core/src/wallet/IndyWallet.test.ts +++ b/packages/core/src/wallet/IndyWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndyWallet', () => { let indyWallet: IndyWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndyWallet', () => { await expect(indyWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { - await expect( - indyWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) - ).resolves.toMatchObject({ + test('Create ed25519 keypair from private key', async () => { + const key = await indyWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), keyType: KeyType.Ed25519, }) + + expect(key).toMatchObject({ + keyType: KeyType.Ed25519, + }) + }) + + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) }) test('Fail to create x25519 keypair', async () => { - await expect(indyWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indyWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/core/src/wallet/IndyWallet.ts b/packages/core/src/wallet/IndyWallet.ts index 0bef6447d6..8c8e83d575 100644 --- a/packages/core/src/wallet/IndyWallet.ts +++ b/packages/core/src/wallet/IndyWallet.ts @@ -465,12 +465,12 @@ export class IndyWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -478,13 +478,26 @@ export class IndyWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indy.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndyWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + + const verkey = await this.indy.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -492,7 +505,7 @@ export class IndyWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index 6c5cff6388..4fcf70f5c2 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -55,7 +55,8 @@ export interface DidInfo { export interface WalletCreateKeyOptions { keyType: KeyType - seed?: string + seed?: Buffer + privateKey?: Buffer } export interface WalletSignOptions { diff --git a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts index 8553af9295..c043673cd1 100644 --- a/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts +++ b/packages/indy-sdk/src/dids/IndySdkSovDidRegistrar.ts @@ -40,15 +40,15 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndySdkSovDidCreateOptions): Promise { const { alias, role, submitterDid, indyNamespace } = options.options - const seed = options.secret?.seed + const privateKey = options.secret?.privateKey - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Invalid seed provided', + reason: 'Invalid private key provided', }, } } @@ -71,7 +71,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // to rely directly on the indy SDK, as we don't want to expose a createDid method just for. assertIndySdkWallet(agentContext.wallet) const [unqualifiedIndyDid, verkey] = await this.indySdk.createAndStoreMyDid(agentContext.wallet.handle, { - seed, + seed: privateKey?.toString(), }) const qualifiedSovDid = `did:sov:${unqualifiedIndyDid}` @@ -131,7 +131,7 @@ export class IndySdkSovDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -256,7 +256,7 @@ export interface IndySdkSovDidCreateOptions extends DidCreateOptions { submitterDid: string } secret?: { - seed?: string + privateKey?: Buffer } } diff --git a/packages/indy-sdk/src/wallet/IndySdkWallet.ts b/packages/indy-sdk/src/wallet/IndySdkWallet.ts index 9230ed5f28..66d75e8933 100644 --- a/packages/indy-sdk/src/wallet/IndySdkWallet.ts +++ b/packages/indy-sdk/src/wallet/IndySdkWallet.ts @@ -461,12 +461,12 @@ export class IndySdkWallet implements Wallet { } /** - * Create a key with an optional seed and keyType. + * Create a key with an optional private key and keyType. * The keypair is also automatically stored in the wallet afterwards * * Bls12381g1g2 and X25519 are not supported. * - * @param seed string The seed for creating a key + * @param privateKey Buffer Private key (formerly called 'seed') * @param keyType KeyType the type of key that should be created * * @returns a Key instance with a publicKeyBase58 @@ -474,13 +474,25 @@ export class IndySdkWallet implements Wallet { * @throws {WalletError} When an unsupported keytype is requested * @throws {WalletError} When the key could not be created */ - public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise { + public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise { try { + if (seed && privateKey) { + throw new AriesFrameworkError('Only one of seed and privateKey can be set') + } + // Ed25519 is supported natively in Indy wallet if (keyType === KeyType.Ed25519) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - const verkey = await this.indySdk.createKey(this.handle, { seed, crypto_type: 'ed25519' }) + if (seed) { + throw new AriesFrameworkError( + 'IndySdkWallet does not support seed. You may rather want to specify a private key for deterministic ed25519 key generation' + ) + } + const verkey = await this.indySdk.createKey(this.handle, { + seed: privateKey?.toString(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + crypto_type: 'ed25519', + }) return Key.fromPublicKeyBase58(verkey, keyType) } @@ -488,7 +500,7 @@ export class IndySdkWallet implements Wallet { if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { const signingKeyProvider = this.signingKeyProviderRegistry.getProviderForKeyType(keyType) - const keyPair = await signingKeyProvider.createKeyPair({ seed }) + const keyPair = await signingKeyProvider.createKeyPair({ seed, privateKey }) await this.storeKeyPair(keyPair) return Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyType) } diff --git a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts index 4b7f822f0e..1bb5447031 100644 --- a/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts +++ b/packages/indy-sdk/src/wallet/__tests__/IndySdkWallet.test.ts @@ -31,7 +31,7 @@ const walletConfigWithMasterSecretId: WalletConfig = { describe('IndySdkWallet', () => { let indySdkWallet: IndySdkWallet - const seed = 'sample-seed' + const privateKey = TypedArrayEncoder.fromString('sample-seed') const message = TypedArrayEncoder.fromString('sample-message') beforeEach(async () => { @@ -72,16 +72,23 @@ describe('IndySdkWallet', () => { await expect(indySdkWallet.generateNonce()).resolves.toEqual(expect.any(String)) }) - test('Create ed25519 keypair', async () => { + test('Create ed25519 keypair from private key', async () => { await expect( - indySdkWallet.createKey({ seed: '2103de41b4ae37e8e28586d84a342b67', keyType: KeyType.Ed25519 }) + indySdkWallet.createKey({ + privateKey: TypedArrayEncoder.fromString('2103de41b4ae37e8e28586d84a342b67'), + keyType: KeyType.Ed25519, + }) ).resolves.toMatchObject({ keyType: KeyType.Ed25519, }) }) + test('Fail to create ed25519 keypair from seed', async () => { + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.Ed25519 })).rejects.toThrowError(WalletError) + }) + test('Fail to create x25519 keypair', async () => { - await expect(indySdkWallet.createKey({ seed, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) + await expect(indySdkWallet.createKey({ privateKey, keyType: KeyType.X25519 })).rejects.toThrowError(WalletError) }) test('Create a signature with a ed25519 keypair', async () => { diff --git a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts index 7505c09280..cf41881cad 100644 --- a/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts +++ b/packages/indy-vdr/src/dids/IndyVdrIndyDidRegistrar.ts @@ -2,6 +2,7 @@ import type { IndyEndpointAttrib } from './didSovUtil' import type { IndyVdrPool } from '../pool' import type { AgentContext, + Buffer, DidRegistrar, DidCreateOptions, DidCreateResult, @@ -41,7 +42,20 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { public async create(agentContext: AgentContext, options: IndyVdrDidCreateOptions): Promise { const seed = options.secret?.seed - if (seed && (typeof seed !== 'string' || seed.length !== 32)) { + const privateKey = options.secret?.privateKey + + if (privateKey && (typeof privateKey !== 'object' || privateKey.length !== 32)) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Invalid privateKey provided', + }, + } + } + + if (seed && (typeof seed !== 'object' || seed.length !== 32)) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, @@ -57,13 +71,14 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { let did = options.did let id - if (seed && did) { + const allowOne = [privateKey, seed, did].filter((e) => e !== undefined) + if (allowOne.length > 1) { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: `Only one of 'seed' and 'did' must be provided`, + reason: `Only one of 'seed', 'privateKey' and 'did' must be provided`, }, } } @@ -88,7 +103,7 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { } } else { // Create a new key and calculate did according to the rules for indy did method - const key = await agentContext.wallet.createKey({ seed, keyType: KeyType.Ed25519 }) + const key = await agentContext.wallet.createKey({ privateKey, seed, keyType: KeyType.Ed25519 }) const buffer = Hasher.hash(key.publicKey, 'sha2-256') id = TypedArrayEncoder.toBase58(buffer.slice(0, 16)) @@ -187,7 +202,8 @@ export class IndyVdrIndyDidRegistrar implements DidRegistrar { // we can only return it if the seed was passed in by the user. Once // we have a secure method for generating seeds we should use the same // approach - seed: options.secret?.seed, + seed: options.secret?.seed?.toString(), + privateKey: options.secret?.privateKey?.toString(), }, }, } @@ -325,7 +341,8 @@ export interface IndyVdrDidCreateOptions extends DidCreateOptions { verkey?: string } secret?: { - seed?: string + seed?: Buffer + privateKey?: Buffer } } diff --git a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts index 6b24cc5c82..d148744502 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-registrar.e2e.test.ts @@ -60,7 +60,7 @@ describe('Indy VDR registrar E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) @@ -224,13 +224,15 @@ describe('Indy VDR registrar E2E', () => { }) test('can register a did:indy with services - did and verkey specified - using attrib endpoint', async () => { - // Generate a seed and the indy did. This allows us to create a new did every time + // Generate a private key and the indy did. This allows us to create a new did every time // but still check if the created output document is as expected. - const seed = Array(32 + 1) - .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) - .slice(0, 32) + const privateKey = TypedArrayEncoder.fromString( + Array(32 + 1) + .join((Math.random().toString(36) + '00000000000000000').slice(2, 18)) + .slice(0, 32) + ) - const key = await wallet.createKey({ seed: seed, keyType: KeyType.Ed25519 }) + const key = await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 }) const x25519PublicKeyBase58 = TypedArrayEncoder.toBase58(convertPublicKeyToX25519(key.publicKey)) const ed25519PublicKeyBase58 = TypedArrayEncoder.toBase58(key.publicKey) diff --git a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts index c3b0eaacbe..a40d4f72b4 100644 --- a/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-did-resolver.e2e.test.ts @@ -1,6 +1,7 @@ import type { Key } from '@aries-framework/core' import { + TypedArrayEncoder, IndyWallet, CacheModuleConfig, InMemoryLruCache, @@ -47,7 +48,7 @@ describe('indy-vdr DID Resolver E2E', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts index 2ea85b5e04..ee1faad9dc 100644 --- a/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts +++ b/packages/indy-vdr/tests/indy-vdr-pool.e2e.test.ts @@ -1,6 +1,6 @@ import type { Key } from '@aries-framework/core' -import { IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' +import { TypedArrayEncoder, IndyWallet, KeyType, SigningProviderRegistry } from '@aries-framework/core' import { GetNymRequest, NymRequest, SchemaRequest, CredentialDefinitionRequest } from '@hyperledger/indy-vdr-shared' import { agentDependencies, genesisTransactions, getAgentConfig, getAgentContext } from '../../core/tests/helpers' @@ -35,7 +35,7 @@ describe('IndyVdrPoolService', () => { } signerKey = await wallet.createKey({ - seed: '000000000000000000000000Trustee9', + privateKey: TypedArrayEncoder.fromString('000000000000000000000000Trustee9'), keyType: KeyType.Ed25519, }) }) diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts index 8b01c14c17..20b2898c84 100644 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts @@ -1,6 +1,6 @@ import type { KeyDidCreateOptions } from '@aries-framework/core' -import { Agent, KeyType, W3cCredentialRecord, W3cVcModule } from '@aries-framework/core' +import { Agent, KeyType, W3cCredentialRecord, W3cVcModule, TypedArrayEncoder } from '@aries-framework/core' import nock, { cleanAll, enableNetConnect } from 'nock' import { didKeyToInstanceOfKey } from '../../core/src/modules/dids/helpers' @@ -76,7 +76,7 @@ describe('OpenId4VcClient', () => { keyType: KeyType.Ed25519, }, secret: { - seed: '96213c3d7fc8d4d6754c7a0fd969598e', + privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), }, }) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion