Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet)!: createKey from private key #1301

Merged
20 changes: 14 additions & 6 deletions packages/askar/src/wallet/AskarWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,21 +346,30 @@ 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
*
* @throws {WalletError} When an unsupported keytype is requested
* @throws {WalletError} When the key could not be created
*/
public async createKey({ seed, keyType }: WalletCreateKeyOptions): Promise<Key> {
public async createKey({ seed, privateKey, keyType }: WalletCreateKeyOptions): Promise<Key> {
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) })
Expand All @@ -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)
}
Expand Down Expand Up @@ -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) {
Expand Down
47 changes: 36 additions & 11 deletions packages/askar/src/wallet/__tests__/AskarWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand Down Expand Up @@ -109,15 +134,15 @@ 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 {
public keyType: KeyType = KeyType.Bls12381g1g2

public async createKeyPair(options: CreateKeyPairOptions): Promise<KeyPair> {
return {
publicKeyBase58: encodeToBase58(Buffer.from(options.seed || 'publicKeyBase58')),
publicKeyBase58: encodeToBase58(Buffer.from(options.seed || TypedArrayEncoder.fromString('publicKeyBase58'))),
privateKeyBase58: 'privateKeyBase58',
keyType: KeyType.Bls12381g1g2,
}
Expand Down Expand Up @@ -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)
})
})
})
Expand Down
9 changes: 5 additions & 4 deletions packages/bbs-signatures/src/Bls12381g2SigningProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ export class Bls12381g2SigningProvider implements SigningProvider {
*
* @throws {SigningProviderError} When a key could not be created
*/
public async createKeyPair({ seed }: CreateKeyPairOptions): Promise<KeyPair> {
// 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<KeyPair> {
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,
Expand Down
9 changes: 7 additions & 2 deletions packages/bbs-signatures/tests/bbs-signatures.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
W3cVerifiablePresentation,
IndyWallet,
Ed25519Signature2018,
TypedArrayEncoder,
} from '@aries-framework/core'

import { SignatureSuiteRegistry } from '../../core/src/modules/vc/SignatureSuiteRegistry'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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<Wallet>(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)
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/crypto/__tests__/JwsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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 () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/crypto/signing-provider/SigningProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export interface VerifyOptions {
}

export interface CreateKeyPairOptions {
seed?: string
seed?: Buffer
privateKey?: Buffer
}

export interface SigningProvider {
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -62,7 +63,7 @@ describe('credentials', () => {
let aliceAgent: Agent<(typeof aliceAgentOptions)['modules']>
let faberReplay: ReplaySubject<CredentialStateChangedEvent>
let aliceReplay: ReplaySubject<CredentialStateChangedEvent>
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'],
Expand Down Expand Up @@ -106,7 +107,7 @@ describe('credentials', () => {
.subscribe(aliceReplay)
wallet = faberAgent.dependencyManager.resolve<Wallet>(InjectionSymbols.Wallet)

await wallet.createKey({ seed, keyType: KeyType.Ed25519 })
await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 })

signCredentialOptions = {
credential: TEST_LD_DOCUMENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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 () => {
Expand All @@ -44,7 +45,7 @@ describe('credentials', () => {
))

wallet = faberAgent.dependencyManager.resolve<Wallet>(InjectionSymbols.Wallet)
await wallet.createKey({ seed, keyType: KeyType.Ed25519 })
await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 })
signCredentialOptions = {
credential: TEST_LD_DOCUMENT,
options: {
Expand Down Expand Up @@ -143,7 +144,7 @@ describe('credentials', () => {
AutoAcceptCredential.ContentApproved
))
wallet = faberAgent.dependencyManager.resolve<Wallet>(InjectionSymbols.Wallet)
await wallet.createKey({ seed, keyType: KeyType.Ed25519 })
await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 })
signCredentialOptions = {
credential: TEST_LD_DOCUMENT,
options: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -57,7 +58,7 @@ describe('credentials', () => {
let signCredentialOptions: JsonLdCredentialDetailFormat

let wallet
const seed = 'testseed000000000000000000000001'
const privateKey = TypedArrayEncoder.fromString('testseed000000000000000000000001')
let credDefId: string

beforeAll(async () => {
Expand All @@ -66,7 +67,7 @@ describe('credentials', () => {
'Alice Agent Credentials LD'
))
wallet = faberAgent.dependencyManager.resolve<Wallet>(InjectionSymbols.Wallet)
await wallet.createKey({ seed, keyType: KeyType.Ed25519 })
await wallet.createKey({ privateKey, keyType: KeyType.Ed25519 })
signCredentialOptions = {
credential: inputDocAsJson,
options: {
Expand Down
Loading