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(anoncreds): legacy indy proof format service #1283

Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c8472b4
refactor(proofs)!: generalize proofs api and improve consistency with…
TimoGlastra Feb 9, 2023
0c6d091
chore: fix type issues
TimoGlastra Feb 9, 2023
e941d5a
feat: initial version legacy indy proof format
TimoGlastra Feb 9, 2023
205acf5
feat: legacy indy proof format service
TimoGlastra Feb 10, 2023
246991c
Update packages/core/src/modules/credentials/CredentialsApiOptions.ts
TimoGlastra Feb 10, 2023
92209c9
Update packages/core/src/modules/credentials/protocol/CredentialProto…
TimoGlastra Feb 10, 2023
2d39aa9
Update packages/core/src/modules/proofs/ProofsModule.ts
TimoGlastra Feb 10, 2023
b23e1b1
Update packages/core/src/modules/proofs/ProofsApi.ts
TimoGlastra Feb 10, 2023
78bee76
Update packages/core/src/modules/proofs/protocol/ProofProtocolOptions.ts
TimoGlastra Feb 10, 2023
77b72af
chore: address feedback
TimoGlastra Feb 10, 2023
c2748a9
Merge branch 'main' into refactor/generic-proofs-api-for-merge
TimoGlastra Feb 10, 2023
d2b6927
Merge branch 'main' into refactor/generic-proofs-api-for-merge
genaris Feb 10, 2023
6608947
fix: check-types after merge with main
genaris Feb 10, 2023
8924f01
chore: address feedback
TimoGlastra Feb 13, 2023
adcf052
Merge branch 'refactor/generic-proofs-api-for-merge' into feat/legacy…
TimoGlastra Feb 13, 2023
3c6a35c
Merge remote-tracking branch 'upstream/main' into feat/legacy-indy-pr…
TimoGlastra Feb 13, 2023
80564a4
fix: merge fices
TimoGlastra Feb 13, 2023
8904104
Merge remote-tracking branch 'upstream/main' into feat/legacy-indy-pr…
TimoGlastra Feb 13, 2023
ba6f119
chore: address feedback
TimoGlastra Feb 13, 2023
a39c57e
Merge remote-tracking branch 'upstream/main' into feat/legacy-indy-pr…
TimoGlastra Feb 13, 2023
645876d
test: add legacy proof foramt service rs test
TimoGlastra Feb 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions packages/anoncreds-rs/src/services/AnonCredsRsHolderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import type {
CreateLinkSecretOptions,
CreateLinkSecretReturn,
AnonCredsProofRequestRestriction,
AnonCredsRequestedAttribute,
AnonCredsRequestedPredicate,
AnonCredsCredential,
AnonCredsRequestedAttributeMatch,
AnonCredsRequestedPredicateMatch,
} from '@aries-framework/anoncreds'
import type { AgentContext, Query, SimpleQuery } from '@aries-framework/core'
import type { CredentialEntry, CredentialProve } from '@hyperledger/anoncreds-shared'
Expand Down Expand Up @@ -63,7 +63,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {
}

public async createProof(agentContext: AgentContext, options: CreateProofOptions): Promise<AnonCredsProof> {
const { credentialDefinitions, proofRequest, requestedCredentials, schemas } = options
const { credentialDefinitions, proofRequest, selectedCredentials, schemas } = options

try {
const rsCredentialDefinitions: Record<string, CredentialDefinition> = {}
Expand All @@ -82,7 +82,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {
const retrievedCredentials = new Map<string, AnonCredsCredentialRecord>()

const credentialEntryFromAttribute = async (
attribute: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate
attribute: AnonCredsRequestedAttributeMatch | AnonCredsRequestedPredicateMatch
): Promise<{ linkSecretId: string; credentialEntry: CredentialEntry }> => {
let credentialRecord = retrievedCredentials.get(attribute.credentialId)
if (!credentialRecord) {
Expand Down Expand Up @@ -136,15 +136,15 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {
const credentials: { linkSecretId: string; credentialEntry: CredentialEntry }[] = []

let entryIndex = 0
for (const referent in requestedCredentials.requestedAttributes) {
const attribute = requestedCredentials.requestedAttributes[referent]
for (const referent in selectedCredentials.attributes) {
const attribute = selectedCredentials.attributes[referent]
credentials.push(await credentialEntryFromAttribute(attribute))
credentialsProve.push({ entryIndex, isPredicate: false, referent, reveal: attribute.revealed })
entryIndex = entryIndex + 1
}

for (const referent in requestedCredentials.requestedPredicates) {
const predicate = requestedCredentials.requestedPredicates[referent]
for (const referent in selectedCredentials.predicates) {
const predicate = selectedCredentials.predicates[referent]
credentials.push(await credentialEntryFromAttribute(predicate))
credentialsProve.push({ entryIndex, isPredicate: true, referent, reveal: true })
entryIndex = entryIndex + 1
Expand All @@ -170,7 +170,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {
presentationRequest: PresentationRequest.load(JSON.stringify(proofRequest)),
credentials: credentials.map((entry) => entry.credentialEntry),
credentialsProve,
selfAttest: requestedCredentials.selfAttestedAttributes,
selfAttest: selectedCredentials.selfAttestedAttributes,
masterSecret: MasterSecret.load(JSON.stringify({ value: { ms: linkSecretRecord.value } })),
})

Expand All @@ -179,7 +179,7 @@ export class AnonCredsRsHolderService implements AnonCredsHolderService {
agentContext.config.logger.error(`Error creating AnonCreds Proof`, {
error,
proofRequest,
requestedCredentials,
selectedCredentials,
})
throw new AnonCredsRsError(`Error creating proof: ${error}`, { cause: error })
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AnonCredsVerifierService, VerifyProofOptions } from '@aries-framework/anoncreds'
import type { AgentContext } from '@aries-framework/core'

import { injectable } from '@aries-framework/core'
import {
Expand All @@ -14,8 +15,8 @@ import { AnonCredsRsError } from '../errors/AnonCredsRsError'

@injectable()
export class AnonCredsRsVerifierService implements AnonCredsVerifierService {
public async verifyProof(options: VerifyProofOptions): Promise<boolean> {
const { credentialDefinitions, proof, proofRequest, revocationStates, schemas } = options
public async verifyProof(agentContext: AgentContext, options: VerifyProofOptions): Promise<boolean> {
const { credentialDefinitions, proof, proofRequest, revocationRegistries, schemas } = options

try {
const presentation = Presentation.load(JSON.stringify(proof))
Expand All @@ -33,8 +34,8 @@ export class AnonCredsRsVerifierService implements AnonCredsVerifierService {
const revocationRegistryDefinitions: Record<string, RevocationRegistryDefinition> = {}
const lists = []

for (const revocationRegistryDefinitionId in revocationStates) {
const { definition, revocationStatusLists } = options.revocationStates[revocationRegistryDefinitionId]
for (const revocationRegistryDefinitionId in revocationRegistries) {
const { definition, revocationStatusLists } = options.revocationRegistries[revocationRegistryDefinitionId]

revocationRegistryDefinitions[revocationRegistryDefinitionId] = RevocationRegistryDefinition.load(
JSON.stringify(definition)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {
AnonCredsCredentialDefinition,
AnonCredsProofRequest,
AnonCredsRequestedCredentials,
AnonCredsRevocationStatusList,
AnonCredsCredential,
AnonCredsSchema,
AnonCredsSelectedCredentials,
} from '@aries-framework/anoncreds'

import {
Expand Down Expand Up @@ -191,15 +191,15 @@ describe('AnonCredsRsHolderService', () => {
revocationRegistryDefinitionId: 'phonerevregid:uri',
})

const requestedCredentials: AnonCredsRequestedCredentials = {
const selectedCredentials: AnonCredsSelectedCredentials = {
selfAttestedAttributes: { attr5_referent: 'football' },
requestedAttributes: {
attributes: {
attr1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true },
attr2_referent: { credentialId: 'phoneCredId', credentialInfo: phoneCredentialInfo, revealed: true },
attr3_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true },
attr4_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo, revealed: true },
},
requestedPredicates: {
predicates: {
predicate1_referent: { credentialId: 'personCredId', credentialInfo: personCredentialInfo },
},
}
Expand Down Expand Up @@ -246,7 +246,7 @@ describe('AnonCredsRsHolderService', () => {
'phonecreddef:uri': phoneCredentialDefinition as AnonCredsCredentialDefinition,
},
proofRequest,
requestedCredentials,
selectedCredentials,
schemas: {
'phoneschema:uri': { attrNames: ['phoneNumber'], issuerId: 'issuer:uri', name: 'phoneschema', version: '1' },
'personschema:uri': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { anoncreds } from '@hyperledger/anoncreds-nodejs'
import { Subject } from 'rxjs'

import { InMemoryStorageService } from '../../../../../tests/InMemoryStorageService'
import { encode } from '../../../../anoncreds/src/utils/credential'
import { encodeCredentialValue } from '../../../../anoncreds/src/utils/credential'
import { InMemoryAnonCredsRegistry } from '../../../../anoncreds/tests/InMemoryAnonCredsRegistry'
import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers'
import { AnonCredsRsHolderService } from '../AnonCredsRsHolderService'
Expand Down Expand Up @@ -145,7 +145,10 @@ describe('AnonCredsRsServices', () => {
const { credential } = await anonCredsIssuerService.createCredential(agentContext, {
credentialOffer,
credentialRequest: credentialRequestState.credentialRequest,
credentialValues: { name: { raw: 'John', encoded: encode('John') }, age: { raw: '25', encoded: encode('25') } },
credentialValues: {
name: { raw: 'John', encoded: encodeCredentialValue('John') },
age: { raw: '25', encoded: encodeCredentialValue('25') },
},
})

const credentialId = 'holderCredentialId'
Expand Down Expand Up @@ -197,12 +200,12 @@ describe('AnonCredsRsServices', () => {
const proof = await anonCredsHolderService.createProof(agentContext, {
credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition },
proofRequest,
requestedCredentials: {
requestedAttributes: {
selectedCredentials: {
attributes: {
attr1_referent: { credentialId, credentialInfo, revealed: true },
attr2_referent: { credentialId, credentialInfo, revealed: true },
},
requestedPredicates: {
predicates: {
predicate1_referent: { credentialId, credentialInfo },
},
selfAttestedAttributes: {},
Expand All @@ -211,12 +214,12 @@ describe('AnonCredsRsServices', () => {
revocationRegistries: {},
})

const verifiedProof = await anonCredsVerifierService.verifyProof({
const verifiedProof = await anonCredsVerifierService.verifyProof(agentContext, {
credentialDefinitions: { [credentialDefinitionState.credentialDefinitionId]: credentialDefinition },
proof,
proofRequest,
schemas: { [schemaState.schemaId]: schema },
revocationStates: {},
revocationRegistries: {},
})

expect(verifiedProof).toBeTruthy()
Expand Down
4 changes: 3 additions & 1 deletion packages/anoncreds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"dependencies": {
"@aries-framework/core": "0.3.3",
"@aries-framework/node": "0.3.3",
"bn.js": "^5.2.1"
"bn.js": "^5.2.1",
"class-transformer": "0.5.1",
"class-validator": "0.13.1"
},
"devDependencies": {
"indy-sdk": "^1.16.0-dev-1636",
Expand Down
30 changes: 16 additions & 14 deletions packages/anoncreds/src/formats/AnonCredsCredentialFormat.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models'
import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core'

export interface AnonCredsCredentialProposalFormat {
schema_issuer_id?: string
schema_name?: string
schema_version?: string
schema_id?: string

cred_def_id?: string
issuer_id?: string

// TODO: we don't necessarily need to include these in the AnonCreds Format RFC
// as it's a new one and we can just forbid the use of legacy properties
schema_issuer_did?: string
issuer_did?: string
}

/**
* This defines the module payload for calling CredentialsApi.createProposal
* or CredentialsApi.negotiateOffer
Expand Down Expand Up @@ -70,20 +85,7 @@ export interface AnonCredsCredentialFormat extends CredentialFormat {
// Format data is based on RFC 0592
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments
formatData: {
proposal: {
schema_issuer_id?: string
schema_name?: string
schema_version?: string
schema_id?: string

cred_def_id?: string
issuer_id?: string

// TODO: we don't necessarily need to include these in the AnonCreds Format RFC
// as it's a new one and we can just forbid the use of legacy properties
schema_issuer_did?: string
issuer_did?: string
}
proposal: AnonCredsCredentialProposalFormat
offer: AnonCredsCredentialOffer
request: AnonCredsCredentialRequest
credential: AnonCredsCredential
Expand Down
89 changes: 89 additions & 0 deletions packages/anoncreds/src/formats/AnonCredsProofFormat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type {
AnonCredsNonRevokedInterval,
AnonCredsPredicateType,
AnonCredsProof,
AnonCredsProofRequest,
AnonCredsRequestedAttribute,
AnonCredsRequestedAttributeMatch,
AnonCredsRequestedPredicate,
AnonCredsRequestedPredicateMatch,
AnonCredsSelectedCredentials,
} from '../models'
import type { ProofFormat } from '@aries-framework/core'

export interface AnonCredsPresentationPreviewAttribute {
name: string
credentialDefinitionId?: string
mimeType?: string
value?: string
referent?: string
}

export interface AnonCredsPresentationPreviewPredicate {
name: string
credentialDefinitionId: string
predicate: AnonCredsPredicateType
threshold: number
}

/**
* Interface for creating an anoncreds proof proposal.
*/
export interface AnonCredsProposeProofFormat {
name?: string
version?: string
attributes?: AnonCredsPresentationPreviewAttribute[]
predicates?: AnonCredsPresentationPreviewPredicate[]
}

/**
* Interface for creating an anoncreds proof request.
*/
export interface AnonCredsRequestProofFormat {
name: string
version: string
nonRevoked?: AnonCredsNonRevokedInterval
requestedAttributes?: Record<string, AnonCredsRequestedAttribute>
requestedPredicates?: Record<string, AnonCredsRequestedPredicate>
}

/**
* Interface for getting credentials for an indy proof request.
*/
export interface AnonCredsCredentialsForProofRequest {
attributes: Record<string, AnonCredsRequestedAttributeMatch[]>
predicates: Record<string, AnonCredsRequestedPredicateMatch[]>
}

export interface AnonCredsGetCredentialsForProofRequestOptions {
filterByNonRevocationRequirements?: boolean
}

export interface AnonCredsProofFormat extends ProofFormat {
formatKey: 'anoncreds'

proofFormats: {
createProposal: AnonCredsProposeProofFormat
acceptProposal: {
name?: string
version?: string
}
createRequest: AnonCredsRequestProofFormat
acceptRequest: AnonCredsSelectedCredentials

getCredentialsForRequest: {
input: AnonCredsGetCredentialsForProofRequestOptions
output: AnonCredsCredentialsForProofRequest
}
selectCredentialsForRequest: {
input: AnonCredsGetCredentialsForProofRequestOptions
output: AnonCredsSelectedCredentials
}
}

formatData: {
proposal: AnonCredsProofRequest
request: AnonCredsProofRequest
presentation: AnonCredsProof
}
}
33 changes: 11 additions & 22 deletions packages/anoncreds/src/formats/LegacyIndyCredentialFormat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,26 @@ import type {
AnonCredsAcceptOfferFormat,
AnonCredsAcceptProposalFormat,
AnonCredsAcceptRequestFormat,
AnonCredsCredentialProposalFormat,
AnonCredsOfferCredentialFormat,
AnonCredsProposeCredentialFormat,
} from './AnonCredsCredentialFormat'
import type { AnonCredsCredential, AnonCredsCredentialOffer, AnonCredsCredentialRequest } from '../models'
import type { CredentialPreviewAttributeOptions, CredentialFormat, LinkedAttachment } from '@aries-framework/core'
import type { CredentialFormat } from '@aries-framework/core'

// Legacy indy credential proposal doesn't support _id properties
export type LegacyIndyCredentialProposalFormat = Omit<
AnonCredsCredentialProposalFormat,
'schema_issuer_id' | 'issuer_id'
>

/**
* This defines the module payload for calling CredentialsApi.createProposal
* or CredentialsApi.negotiateOffer
*
* NOTE: This doesn't include the `issuerId` and `schemaIssuerId` properties that are present in the newer format.
*/
export interface LegacyIndyProposeCredentialFormat {
schemaIssuerDid?: string
schemaId?: string
schemaName?: string
schemaVersion?: string

credentialDefinitionId?: string
issuerDid?: string

attributes?: CredentialPreviewAttributeOptions[]
linkedAttachments?: LinkedAttachment[]
}
type LegacyIndyProposeCredentialFormat = Omit<AnonCredsProposeCredentialFormat, 'schemaIssuerId' | 'issuerId'>

export interface LegacyIndyCredentialRequest extends AnonCredsCredentialRequest {
// prover_did is optional in AnonCreds credential request, but required in legacy format
Expand All @@ -51,15 +48,7 @@ export interface LegacyIndyCredentialFormat extends CredentialFormat {
// Format data is based on RFC 0592
// https://github.com/hyperledger/aries-rfcs/tree/main/features/0592-indy-attachments
formatData: {
proposal: {
schema_name?: string
schema_issuer_did?: string
schema_version?: string
schema_id?: string

cred_def_id?: string
issuer_did?: string
}
proposal: LegacyIndyCredentialProposalFormat
offer: AnonCredsCredentialOffer
request: LegacyIndyCredentialRequest
credential: AnonCredsCredential
Expand Down
Loading