Skip to content

Commit

Permalink
feat(wallet)!: createKey from private key (openwallet-foundation#1301)
Browse files Browse the repository at this point in the history
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
  • Loading branch information
genaris committed Feb 19, 2023
1 parent 64a5da9 commit 616b908
Show file tree
Hide file tree
Showing 32 changed files with 300 additions and 143 deletions.
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

0 comments on commit 616b908

Please sign in to comment.