Skip to content

Commit

Permalink
feat(indy-vdr)!: extend did:indy support (openwallet-foundation#1362)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed Mar 8, 2023
1 parent 01669a7 commit 39c4ed0
Show file tree
Hide file tree
Showing 39 changed files with 2,210 additions and 985 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ describe('Legacy indy format services', () => {
]

const cd = parseCredentialDefinitionId(credentialDefinitionState.credentialDefinitionId)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag)

const s = parseSchemaId(schemaState.schemaId)
const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion)
const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion)

// Holder creates proposal
holderCredentialRecord.credentialAttributes = credentialAttributes
Expand Down
25 changes: 15 additions & 10 deletions packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry {

const parsed = parseSchemaId(schemaId)

const legacySchemaId = getLegacySchemaId(parsed.didIdentifier, parsed.schemaName, parsed.schemaVersion)
const legacySchemaId = getLegacySchemaId(parsed.namespaceIdentifier, parsed.schemaName, parsed.schemaVersion)
const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId)

if (!schema) {
Expand Down Expand Up @@ -94,16 +94,21 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry {
agentContext: AgentContext,
options: RegisterSchemaOptions
): Promise<RegisterSchemaReturn> {
const { id: didIdentifier, namespace } = parseIndyDid(options.schema.issuerId)
const didIndySchemaId = getDidIndySchemaId(namespace, didIdentifier, options.schema.name, options.schema.version)
const legacySchemaId = getLegacySchemaId(didIdentifier, options.schema.name, options.schema.version)
const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId)
const didIndySchemaId = getDidIndySchemaId(
namespace,
namespaceIdentifier,
options.schema.name,
options.schema.version
)
const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version)

const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId)

this.schemas[didIndySchemaId] = options.schema
this.schemas[legacySchemaId] = {
...options.schema,
issuerId: didIdentifier,
issuerId: namespaceIdentifier,
}

return {
Expand Down Expand Up @@ -152,30 +157,30 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry {
): Promise<RegisterCredentialDefinitionReturn> {
const parsedSchema = parseSchemaId(options.credentialDefinition.schemaId)
const legacySchemaId = getLegacySchemaId(
parsedSchema.didIdentifier,
parsedSchema.namespaceIdentifier,
parsedSchema.schemaName,
parsedSchema.schemaVersion
)
const indyLedgerSeqNo = getSeqNoFromSchemaId(legacySchemaId)

const { id: didIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId)
const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId)

const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(
namespace,
didIdentifier,
namespaceIdentifier,
indyLedgerSeqNo,
options.credentialDefinition.tag
)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(
didIdentifier,
namespaceIdentifier,
indyLedgerSeqNo,
options.credentialDefinition.tag
)

this.credentialDefinitions[didIndyCredentialDefinitionId] = options.credentialDefinition
this.credentialDefinitions[legacyCredentialDefinitionId] = {
...options.credentialDefinition,
issuerId: didIdentifier,
issuerId: namespaceIdentifier,
schemaId: legacySchemaId,
}

Expand Down
15 changes: 11 additions & 4 deletions packages/anoncreds/tests/legacyAnonCredsSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ import {
parseSchemaId,
} from '../../indy-sdk/src/anoncreds/utils/identifiers'
import { getIndySdkModuleConfig } from '../../indy-sdk/tests/setupIndySdkModule'
import { IndyVdrAnonCredsRegistry, IndyVdrSovDidResolver, IndyVdrModule } from '../../indy-vdr/src'
import {
IndyVdrAnonCredsRegistry,
IndyVdrSovDidResolver,
IndyVdrModule,
IndyVdrIndyDidResolver,
IndyVdrIndyDidRegistrar,
} from '../../indy-vdr/src'
import {
V1CredentialProtocol,
V1ProofProtocol,
Expand Down Expand Up @@ -163,7 +169,8 @@ export const getAskarAnonCredsIndyModules = ({
networks: [indyNetworkConfig],
}),
dids: new DidsModule({
resolvers: [new IndyVdrSovDidResolver()], // TODO: Support Registrar for tests
resolvers: [new IndyVdrSovDidResolver(), new IndyVdrIndyDidResolver()],
registrars: [new IndyVdrIndyDidRegistrar()],
}),
askar: new AskarModule(),
cache: new CacheModule({
Expand Down Expand Up @@ -474,8 +481,8 @@ export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames
const s = parseSchemaId(schema.schemaId)
const cd = parseCredentialDefinitionId(credentialDefinition.credentialDefinitionId)

const legacySchemaId = getLegacySchemaId(s.didIdentifier, s.schemaName, s.schemaVersion)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.didIdentifier, cd.schemaSeqNo, cd.tag)
const legacySchemaId = getLegacySchemaId(s.namespaceIdentifier, s.schemaName, s.schemaVersion)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(cd.namespaceIdentifier, cd.schemaSeqNo, cd.tag)

// Wait some time pass to let ledger settle the object
await sleep(1000)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tests/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export async function importExistingIndyDidFromPrivateKey(agent: Agent, privateK
const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16))

// import the did in the wallet so it can be used
await agent.dids.import({ did: `did:sov:${unqualifiedIndyDid}` })
await agent.dids.import({ did: `did:indy:pool:localtest:${unqualifiedIndyDid}` })

return unqualifiedIndyDid
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,9 @@ import {
} from '../utils/identifiers'
import { anonCredsRevocationStatusListFromIndySdk } from '../utils/transform'

/**
* TODO: validation of the identifiers. The Indy SDK classes only support the legacy (unqualified) identifiers.
*/
export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
/**
* This class only supports resolving and registering objects with legacy indy identifiers.
* This class supports resolving and registering objects with did:indy as well as legacy indy identifiers.
* It needs to include support for the schema, credential definition, revocation registry as well
* as the issuer id (which is needed when registering objects).
*/
Expand All @@ -48,12 +45,12 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)

// parse schema id (supports did:indy and legacy)
const { did, didIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId)
const { did, namespaceIdentifier, schemaName, schemaVersion } = parseSchemaId(schemaId)
const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did)
agentContext.config.logger.debug(`Getting schema '${schemaId}' from ledger '${pool.didIndyNamespace}'`)

// even though we support did:indy and legacy identifiers we always need to fetch using the legacy identifier
const legacySchemaId = getLegacySchemaId(didIdentifier, schemaName, schemaVersion)
const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schemaName, schemaVersion)
const request = await indySdk.buildGetSchemaRequest(null, legacySchemaId)

agentContext.config.logger.trace(
Expand Down Expand Up @@ -110,7 +107,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
try {
// This will throw an error if trying to register a schema with a legacy indy identifier. We only support did:indy identifiers
// for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers.
const { id: unqualifiedDid, namespace } = parseIndyDid(options.schema.issuerId)
const { namespaceIdentifier, namespace } = parseIndyDid(options.schema.issuerId)

const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService)
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)
Expand All @@ -121,8 +118,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
options.schema
)

const didIndySchemaId = getDidIndySchemaId(namespace, unqualifiedDid, options.schema.name, options.schema.version)
const legacySchemaId = getLegacySchemaId(unqualifiedDid, options.schema.name, options.schema.version)
const didIndySchemaId = getDidIndySchemaId(
namespace,
namespaceIdentifier,
options.schema.name,
options.schema.version
)
const legacySchemaId = getLegacySchemaId(namespaceIdentifier, options.schema.name, options.schema.version)

const schema = {
attrNames: options.schema.attrNames,
Expand All @@ -134,7 +136,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
// buildSchemaRequest (seqNo is not yet known)
} as IndySdkSchema

const request = await indySdk.buildSchemaRequest(unqualifiedDid, schema)
const request = await indySdk.buildSchemaRequest(namespaceIdentifier, schema)
const submitterKey = await verificationKeyForIndyDid(agentContext, options.schema.issuerId)

const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey)
Expand Down Expand Up @@ -189,14 +191,14 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)

// we support did:indy and legacy identifiers
const { did, didIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId)
const { did, namespaceIdentifier, schemaSeqNo, tag } = parseCredentialDefinitionId(credentialDefinitionId)
const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did)

agentContext.config.logger.debug(
`Using ledger '${pool.didIndyNamespace}' to retrieve credential definition '${credentialDefinitionId}'`
)

const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, tag)
const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, tag)
const request = await indySdk.buildGetCredDefRequest(null, legacyCredentialDefinitionId)

agentContext.config.logger.trace(
Expand Down Expand Up @@ -224,7 +226,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {

// Format the schema id based on the type of the credential definition id
const schemaId = credentialDefinitionId.startsWith('did:indy')
? getDidIndySchemaId(pool.didIndyNamespace, didIdentifier, schema.name, schema.version)
? getDidIndySchemaId(pool.didIndyNamespace, namespaceIdentifier, schema.name, schema.version)
: schema.schemaId

return {
Expand Down Expand Up @@ -279,7 +281,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
try {
// This will throw an error if trying to register a credential defintion with a legacy indy identifier. We only support did:indy
// identifiers for registering, that will allow us to extract the namespace and means all stored records will use did:indy identifiers.
const { id: unqualifiedDid, namespace } = parseIndyDid(options.credentialDefinition.issuerId)
const { namespaceIdentifier, namespace } = parseIndyDid(options.credentialDefinition.issuerId)

const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService)
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)
Expand Down Expand Up @@ -312,18 +314,18 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
}

const legacyCredentialDefinitionId = getLegacyCredentialDefinitionId(
unqualifiedDid,
namespaceIdentifier,
schemaMetadata.indyLedgerSeqNo,
options.credentialDefinition.tag
)
const didIndyCredentialDefinitionId = getDidIndyCredentialDefinitionId(
namespace,
unqualifiedDid,
namespaceIdentifier,
schemaMetadata.indyLedgerSeqNo,
options.credentialDefinition.tag
)

const request = await indySdk.buildCredDefRequest(unqualifiedDid, {
const request = await indySdk.buildCredDefRequest(namespaceIdentifier, {
id: legacyCredentialDefinitionId,
// Indy ledger requires the credential schemaId to be a string of the schema seqNo.
schemaId: schemaMetadata.indyLedgerSeqNo.toString(),
Expand All @@ -334,7 +336,6 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
})

const submitterKey = await verificationKeyForIndyDid(agentContext, options.credentialDefinition.issuerId)

const response = await indySdkPoolService.submitWriteRequest(agentContext, pool, request, submitterKey)

agentContext.config.logger.debug(
Expand Down Expand Up @@ -376,7 +377,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService)
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)

const { did, didIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } =
const { did, namespaceIdentifier, credentialDefinitionTag, revocationRegistryTag, schemaSeqNo } =
parseRevocationRegistryId(revocationRegistryDefinitionId)
const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did)

Expand All @@ -385,7 +386,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
)

const legacyRevocationRegistryId = getLegacyRevocationRegistryId(
didIdentifier,
namespaceIdentifier,
schemaSeqNo,
credentialDefinitionTag,
revocationRegistryTag
Expand Down Expand Up @@ -413,8 +414,13 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
)

const credentialDefinitionId = revocationRegistryDefinitionId.startsWith('did:indy:')
? getDidIndyCredentialDefinitionId(pool.didIndyNamespace, didIdentifier, schemaSeqNo, credentialDefinitionTag)
: getLegacyCredentialDefinitionId(didIdentifier, schemaSeqNo, credentialDefinitionTag)
? getDidIndyCredentialDefinitionId(
pool.didIndyNamespace,
namespaceIdentifier,
schemaSeqNo,
credentialDefinitionTag
)
: getLegacyCredentialDefinitionId(namespaceIdentifier, schemaSeqNo, credentialDefinitionTag)

return {
resolutionMetadata: {},
Expand Down Expand Up @@ -465,7 +471,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
const indySdkPoolService = agentContext.dependencyManager.resolve(IndySdkPoolService)
const indySdk = agentContext.dependencyManager.resolve<IndySdk>(IndySdkSymbol)

const { did, didIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } =
const { did, namespaceIdentifier, schemaSeqNo, credentialDefinitionTag, revocationRegistryTag } =
parseRevocationRegistryId(revocationRegistryId)
const { pool } = await indySdkPoolService.getPoolForDid(agentContext, did)

Expand All @@ -474,7 +480,7 @@ export class IndySdkAnonCredsRegistry implements AnonCredsRegistry {
)

const legacyRevocationRegistryId = getLegacyRevocationRegistryId(
didIdentifier,
namespaceIdentifier,
schemaSeqNo,
credentialDefinitionTag,
revocationRegistryTag
Expand Down
10 changes: 5 additions & 5 deletions packages/indy-sdk/src/anoncreds/services/IndySdkIssuerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export class IndySdkIssuerService implements AnonCredsIssuerService {

public async createSchema(agentContext: AgentContext, options: CreateSchemaOptions): Promise<AnonCredsSchema> {
// We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects
const { id: unqualifiedDid } = parseIndyDid(options.issuerId)
const { namespaceIdentifier } = parseIndyDid(options.issuerId)

const { name, version, attrNames, issuerId } = options
assertIndySdkWallet(agentContext.wallet)

try {
const [, schema] = await this.indySdk.issuerCreateSchema(unqualifiedDid, name, version, attrNames)
const [, schema] = await this.indySdk.issuerCreateSchema(namespaceIdentifier, name, version, attrNames)

return {
issuerId,
Expand All @@ -60,10 +60,10 @@ export class IndySdkIssuerService implements AnonCredsIssuerService {
const { tag, supportRevocation, schema, issuerId, schemaId } = options

// We only support passing qualified did:indy issuer ids in the indy issuer service when creating objects
const { id: unqualifiedDid } = parseIndyDid(options.issuerId)
const { namespaceIdentifier } = parseIndyDid(options.issuerId)

// parse schema in a way that supports both unqualified and qualified identifiers
const legacySchemaId = getLegacySchemaId(unqualifiedDid, schema.name, schema.version)
const legacySchemaId = getLegacySchemaId(namespaceIdentifier, schema.name, schema.version)

if (!metadata)
throw new AriesFrameworkError('The metadata parameter is required when using Indy, but received undefined.')
Expand All @@ -72,7 +72,7 @@ export class IndySdkIssuerService implements AnonCredsIssuerService {
assertIndySdkWallet(agentContext.wallet)
const [, credentialDefinition] = await this.indySdk.issuerCreateAndStoreCredentialDef(
agentContext.wallet.handle,
unqualifiedDid,
namespaceIdentifier,
indySdkSchemaFromAnonCreds(legacySchemaId, schema, metadata.indyLedgerSchemaSeqNo),
tag,
'CL',
Expand Down
Loading

0 comments on commit 39c4ed0

Please sign in to comment.