Skip to content

Commit

Permalink
refactor: remove master secret id from wallet (openwallet-foundation#…
Browse files Browse the repository at this point in the history
…1320)


Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed Feb 20, 2023
1 parent b6d66b1 commit edf392f
Show file tree
Hide file tree
Showing 14 changed files with 81 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {

if (!linkSecretRecord) {
// No default link secret
throw new AnonCredsRsError('No default link secret has been found')
throw new AnonCredsRsError(
'No link secret provided to createCredentialRequest and no default link secret has been found'
)
}

const { credentialRequest, credentialRequestMetadata } = CredentialRequest.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@ import {
CredentialPreviewAttribute,
ProofExchangeRecord,
ProofState,
EventEmitter,
} from '@aries-framework/core'
import * as indySdk from 'indy-sdk'
import { Subject } from 'rxjs'

import { getAgentConfig, getAgentContext } from '../../../../core/tests/helpers'
import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers'
import {
IndySdkHolderService,
IndySdkIssuerService,
IndySdkStorageService,
IndySdkVerifierService,
IndySdkWallet,
} from '../../../../indy-sdk/src'
import { IndySdkRevocationService } from '../../../../indy-sdk/src/anoncreds/services/IndySdkRevocationService'
import { indyDidFromPublicKeyBase58 } from '../../../../indy-sdk/src/utils/did'
import { InMemoryAnonCredsRegistry } from '../../../tests/InMemoryAnonCredsRegistry'
import { AnonCredsModuleConfig } from '../../AnonCredsModuleConfig'
import { AnonCredsLinkSecretRecord, AnonCredsLinkSecretRepository } from '../../repository'
import {
AnonCredsHolderServiceSymbol,
AnonCredsIssuerServiceSymbol,
Expand All @@ -40,13 +44,17 @@ const anonCredsVerifierService = new IndySdkVerifierService(indySdk)
const anonCredsHolderService = new IndySdkHolderService(anonCredsRevocationService, indySdk)
const anonCredsIssuerService = new IndySdkIssuerService(indySdk)
const wallet = new IndySdkWallet(indySdk, agentConfig.logger, new SigningProviderRegistry([]))
const storageService = new IndySdkStorageService<AnonCredsLinkSecretRecord>(indySdk)
const eventEmitter = new EventEmitter(agentDependencies, new Subject())
const anonCredsLinkSecretRepository = new AnonCredsLinkSecretRepository(storageService, eventEmitter)
const agentContext = getAgentContext({
registerInstances: [
[AnonCredsIssuerServiceSymbol, anonCredsIssuerService],
[AnonCredsHolderServiceSymbol, anonCredsHolderService],
[AnonCredsVerifierServiceSymbol, anonCredsVerifierService],
[AnonCredsRegistryService, new AnonCredsRegistryService()],
[AnonCredsModuleConfig, anonCredsModuleConfig],
[AnonCredsLinkSecretRepository, anonCredsLinkSecretRepository],
],
agentConfig,
wallet,
Expand All @@ -71,6 +79,16 @@ describe('Legacy indy format services', () => {
const key = await wallet.createKey({ keyType: KeyType.Ed25519 })
const indyDid = indyDidFromPublicKeyBase58(key.publicKeyBase58)

// Create link secret
await anonCredsHolderService.createLinkSecret(agentContext, {
linkSecretId: 'link-secret-id',
})
const anonCredsLinkSecret = new AnonCredsLinkSecretRecord({
linkSecretId: 'link-secret-id',
})
anonCredsLinkSecret.setTag('isDefault', true)
await anonCredsLinkSecretRepository.save(agentContext, anonCredsLinkSecret)

const schema = await anonCredsIssuerService.createSchema(agentContext, {
attrNames: ['name', 'age'],
issuerId: indyDid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ describe('V1 Proofs - Connectionless - Indy', () => {
expect(faberConnection.isReady).toBe(true)
expect(aliceConnection.isReady).toBe(true)

await aliceAgent.modules.anoncreds.createLinkSecret({
linkSecretId: 'default',
setAsDefault: true,
})

await issueLegacyAnonCredsCredential({
issuerAgent: faberAgent,
issuerReplay: faberReplay,
Expand Down
6 changes: 6 additions & 0 deletions packages/anoncreds/tests/legacyAnonCredsSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ export async function setupAnonCredsTests<
await holderAgent.initialize()
if (verifierAgent) await verifierAgent.initialize()

// Create default link secret for holder
await holderAgent.modules.anoncreds.createLinkSecret({
linkSecretId: 'default',
setAsDefault: true,
})

const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, {
attributeNames,
// TODO: replace with more dynamic / generic value We should create a did using the dids module
Expand Down
12 changes: 0 additions & 12 deletions packages/askar/src/wallet/AskarWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,6 @@ export class AskarWallet implements Wallet {
return this._session
}

public get masterSecretId() {
if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) {
throw new AriesFrameworkError(
'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.'
)
}

return this.walletConfig?.masterSecretId ?? this.walletConfig.id
}

/**
* Dispose method is called when an agent context is disposed.
*/
Expand Down Expand Up @@ -156,8 +146,6 @@ export class AskarWallet implements Wallet {
})
this.walletConfig = walletConfig
this._session = await this._store.openSession()

// TODO: Master Secret creation (now part of IndyCredx/AnonCreds)
} catch (error) {
// FIXME: Askar should throw a Duplicate error code, but is currently returning Encryption
// And if we provide the very same wallet key, it will open it without any error
Expand Down
8 changes: 0 additions & 8 deletions packages/askar/src/wallet/__tests__/AskarWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ describe('AskarWallet basic operations', () => {
await askarWallet.delete()
})

test('Get the Master Secret', () => {
expect(askarWallet.masterSecretId).toEqual('Wallet: AskarWalletTest')
})

test('Get the wallet store', () => {
expect(askarWallet.store).toEqual(expect.any(Store))
})
Expand Down Expand Up @@ -124,10 +120,6 @@ describe('AskarWallet basic operations', () => {
})
await expect(askarWallet.verify({ key: ed25519Key, data: message, signature })).resolves.toStrictEqual(true)
})

test('masterSecretId is equal to wallet ID by default', async () => {
expect(askarWallet.masterSecretId).toEqual(walletConfig.id)
})
})

describe.skip('Currently, all KeyTypes are supported by Askar natively', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ describe('V2 Connectionless Credentials', () => {
aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap))
await aliceAgent.initialize()

// Create link secret for alice
await aliceAgent.modules.anoncreds.createLinkSecret({
linkSecretId: 'default',
setAsDefault: true,
})

const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, {
issuerId: faberAgent.publicDid?.did as string,
attributeNames: ['name', 'age'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ describe('V2 Credentials - JSON-LD - Ed25519', () => {
await aliceAgent.initialize()
;[, { id: aliceConnectionId }] = await makeConnection(faberAgent, aliceAgent)

// Create link secret for alice
await aliceAgent.modules.anoncreds.createLinkSecret({
linkSecretId: 'default',
setAsDefault: true,
})

const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, {
attributeNames: ['name', 'age', 'profile_picture', 'x-ray'],
issuerId: faberAgent.publicDid?.did as string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ describe('V2 Connectionless Proofs - Indy', () => {
)
agents = [aliceAgent, faberAgent, mediatorAgent]

await aliceAgent.modules.anoncreds.createLinkSecret({
linkSecretId: 'default',
setAsDefault: true,
})

const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, {
attributeNames: ['name', 'age', 'image_0', 'image_1'],
issuerId: faberAgent.publicDid?.did as string,
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export interface WalletConfig {
key: string
keyDerivationMethod?: KeyDerivationMethod
storage?: WalletStorageConfig
masterSecretId?: string
}

export interface WalletConfigRekey {
Expand Down
33 changes: 28 additions & 5 deletions packages/indy-sdk/src/anoncreds/services/IndySdkHolderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import type {
IndyProofRequest,
} from 'indy-sdk'

import { injectable, inject, utils } from '@aries-framework/core'
import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds'
import { AriesFrameworkError, injectable, inject, utils } from '@aries-framework/core'

import { IndySdkError, isIndyError } from '../../error'
import { IndySdk, IndySdkSymbol } from '../../types'
Expand Down Expand Up @@ -80,6 +81,8 @@ export class IndySdkHolderService implements AnonCredsHolderService {

assertIndySdkWallet(agentContext.wallet)

const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository)

try {
agentContext.config.logger.debug('Creating Indy Proof')
const indyRevocationStates: RevStates = await this.indyRevocationService.createRevocationState(
Expand Down Expand Up @@ -114,11 +117,19 @@ export class IndySdkHolderService implements AnonCredsHolderService {
indySchemas[schemaId] = indySdkSchemaFromAnonCreds(schemaId, schema, seqNoMap[schemaId])
}

const linkSecretRecord = await linkSecretRepository.findDefault(agentContext)
if (!linkSecretRecord) {
// No default link secret
throw new AriesFrameworkError(
'No default link secret found. Indy SDK requires a default link secret to be created before creating a proof.'
)
}

const indyProof = await this.indySdk.proverCreateProof(
agentContext.wallet.handle,
proofRequest as IndyProofRequest,
this.parseSelectedCredentials(selectedCredentials),
agentContext.wallet.masterSecretId,
linkSecretRecord.linkSecretId,
indySchemas,
indyCredentialDefinitions,
indyRevocationStates
Expand Down Expand Up @@ -201,10 +212,24 @@ export class IndySdkHolderService implements AnonCredsHolderService {
): Promise<CreateCredentialRequestReturn> {
assertIndySdkWallet(agentContext.wallet)

const linkSecretRepository = agentContext.dependencyManager.resolve(AnonCredsLinkSecretRepository)

// We just generate a prover did like string, as it's not used for anything and we don't need
// to prove ownership of the did. It's deprecated in AnonCreds v1, but kept for backwards compatibility
const proverDid = generateLegacyProverDidLikeString()

// If a link secret is specified, use it. Otherwise, attempt to use default link secret
const linkSecretRecord = options.linkSecretId
? await linkSecretRepository.getByLinkSecretId(agentContext, options.linkSecretId)
: await linkSecretRepository.findDefault(agentContext)

if (!linkSecretRecord) {
// No default link secret
throw new AriesFrameworkError(
'No link secret provided to createCredentialRequest and no default link secret has been found'
)
}

try {
const result = await this.indySdk.proverCreateCredentialReq(
agentContext.wallet.handle,
Expand All @@ -213,9 +238,7 @@ export class IndySdkHolderService implements AnonCredsHolderService {
// NOTE: Is it safe to use the cred_def_id from the offer? I think so. You can't create a request
// for a cred def that is not in the offer
indySdkCredentialDefinitionFromAnonCreds(options.credentialOffer.cred_def_id, options.credentialDefinition),
// FIXME: we need to remove the masterSecret from the wallet, as it is AnonCreds specific
// Issue: https://github.com/hyperledger/aries-framework-javascript/issues/1198
agentContext.wallet.masterSecretId
linkSecretRecord.linkSecretId
)

return {
Expand Down
62 changes: 0 additions & 62 deletions packages/indy-sdk/src/wallet/IndySdkWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,6 @@ export class IndySdkWallet implements Wallet {
return this.walletHandle
}

public get masterSecretId() {
if (!this.isInitialized || !(this.walletConfig?.id || this.walletConfig?.masterSecretId)) {
throw new AriesFrameworkError(
'Wallet has not been initialized yet. Make sure to await agent.initialize() before using the agent.'
)
}

return this.walletConfig?.masterSecretId ?? this.walletConfig.id
}

/**
* Dispose method is called when an agent context is disposed.
*/
Expand Down Expand Up @@ -155,15 +145,8 @@ export class IndySdkWallet implements Wallet {
await this.indySdk.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig))
this.walletConfig = walletConfig

// We usually want to create master secret only once, therefore, we can to do so when creating a wallet.
await this.open(walletConfig)

// We need to open wallet before creating master secret because we need wallet handle here.
await this.createMasterSecret(this.handle, this.masterSecretId)
} catch (error) {
// If an error ocurred while creating the master secret, we should close the wallet
if (this.isInitialized) await this.close()

if (isIndyError(error, 'WalletAlreadyExistsError')) {
const errorMessage = `Wallet '${walletConfig.id}' already exists`
this.logger.debug(errorMessage)
Expand Down Expand Up @@ -394,51 +377,6 @@ export class IndySdkWallet implements Wallet {
}
}

/**
* Create master secret with specified id in currently opened wallet.
*
* If a master secret by this id already exists in the current wallet, the method
* will return without doing anything.
*
* @throws {WalletError} if an error occurs
*/
private async createMasterSecret(walletHandle: number, masterSecretId: string): Promise<string> {
this.logger.debug(`Creating master secret with id '${masterSecretId}' in wallet with handle '${walletHandle}'`)

try {
await this.indySdk.proverCreateMasterSecret(walletHandle, masterSecretId)

return masterSecretId
} catch (error) {
if (isIndyError(error, 'AnoncredsMasterSecretDuplicateNameError')) {
// master secret id is the same as the master secret id passed in the create function
// so if it already exists we can just assign it.
this.logger.debug(
`Master secret with id '${masterSecretId}' already exists in wallet with handle '${walletHandle}'`,
{
indyError: 'AnoncredsMasterSecretDuplicateNameError',
}
)

return masterSecretId
} else {
if (!isIndyError(error)) {
throw new AriesFrameworkError('Attempted to throw Indy error, but it was not an Indy error')
}

this.logger.error(`Error creating master secret with id ${masterSecretId}`, {
indyError: error.indyName,
error,
})

throw new WalletError(
`Error creating master secret with id ${masterSecretId} in wallet with handle '${walletHandle}'`,
{ cause: error }
)
}
}
}

public async initPublicDid(didConfig: DidConfig) {
const { did, verkey } = await this.createDid(didConfig)
this.publicDidInfo = {
Expand Down
Loading

0 comments on commit edf392f

Please sign in to comment.