From 399c66309943ac1d417e38d46260e6267e73a0fb Mon Sep 17 00:00:00 2001 From: lenkan Date: Mon, 19 Feb 2024 10:15:08 +0100 Subject: [PATCH 1/4] fix: cannot set different salts for credential --- .../integration-scripts/credentials.test.ts | 66 +++++++------- .../multisig-holder.test.ts | 27 +++--- examples/integration-scripts/multisig.test.ts | 36 +++----- .../singlesig-vlei-issuance.test.ts | 25 +++--- src/keri/app/credentialing.ts | 88 ++++++------------- test/app/credentialing.test.ts | 13 +-- 6 files changed, 103 insertions(+), 152 deletions(-) diff --git a/examples/integration-scripts/credentials.test.ts b/examples/integration-scripts/credentials.test.ts index 1babbc26..e1e871d7 100644 --- a/examples/integration-scripts/credentials.test.ts +++ b/examples/integration-scripts/credentials.test.ts @@ -143,13 +143,16 @@ test('single signature credentials', async () => { LEI: '5493001KJTIIGC8Y1R17', }; - const issResult = await issuerClient.credentials().issue({ - issuerName: issuerAid.name, - registryId: registry.regk, - schemaId: QVI_SCHEMA_SAID, - recipient: holderAid.prefix, - data: vcdata, - }); + const issResult = await issuerClient + .credentials() + .issue(issuerAid.name, { + ri: registry.regk, + s: QVI_SCHEMA_SAID, + a: { + i: holderAid.prefix, + ...vcdata, + }, + }); await waitOperation(issuerClient, issResult.op); return issResult.acdc.ked.d as string; @@ -374,31 +377,32 @@ test('single signature credentials', async () => { .credentials() .get(qviCredentialId); - const result = await holderClient.credentials().issue({ - issuerName: holderAid.name, - recipient: legalEntityAid.prefix, - registryId: holderRegistry.regk, - schemaId: LE_SCHEMA_SAID, - data: { - LEI: '5493001KJTIIGC8Y1R17', - }, - rules: Saider.saidify({ - d: '', - usageDisclaimer: { - l: 'Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.', - }, - issuanceDisclaimer: { - l: 'All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.', - }, - })[1], - source: Saider.saidify({ - d: '', - qvi: { - n: qviCredential.sad.d, - s: qviCredential.sad.s, + const result = await holderClient + .credentials() + .issue(holderAid.name, { + a: { + i: legalEntityAid.prefix, + LEI: '5493001KJTIIGC8Y1R17', }, - })[1], - }); + ri: holderRegistry.regk, + s: LE_SCHEMA_SAID, + r: Saider.saidify({ + d: '', + usageDisclaimer: { + l: 'Usage of a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, does not assert that the Legal Entity is trustworthy, honest, reputable in its business dealings, safe to do business with, or compliant with any laws or that an implied or expressly intended purpose will be fulfilled.', + }, + issuanceDisclaimer: { + l: 'All information in a valid, unexpired, and non-revoked vLEI Credential, as defined in the associated Ecosystem Governance Framework, is accurate as of the date the validation process was complete. The vLEI Credential has been issued to the legal entity or person named in the vLEI Credential as the subject; and the qualified vLEI Issuer exercised reasonable care to perform the validation process set forth in the vLEI Ecosystem Governance Framework.', + }, + })[1], + e: Saider.saidify({ + d: '', + qvi: { + n: qviCredential.sad.d, + s: qviCredential.sad.s, + }, + })[1], + }); await waitOperation(holderClient, result.op); return result.acdc.ked.d; diff --git a/examples/integration-scripts/multisig-holder.test.ts b/examples/integration-scripts/multisig-holder.test.ts index 1af254f9..bb13c5b3 100644 --- a/examples/integration-scripts/multisig-holder.test.ts +++ b/examples/integration-scripts/multisig-holder.test.ts @@ -3,6 +3,7 @@ import signify, { SignifyClient, IssueCredentialArgs, Operation, + CredentialData, } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; import { @@ -354,12 +355,11 @@ test('multisig', async function run() { console.log(`Issuer starting credential issuance to holder...`); const registires = await client3.registries().list('issuer'); - await issueCredential(client3, { - issuerName: 'issuer', - registryId: registires[0].regk, - schemaId: SCHEMA_SAID, - recipient: holderAid['prefix'], - data: { + await issueCredential(client3, 'issuer', { + ri: registires[0].regk, + s: SCHEMA_SAID, + a: { + i: holderAid['prefix'], LEI: '5493001KJTIIGC8Y1R17', }, }); @@ -476,23 +476,24 @@ async function createRegistry( async function issueCredential( client: SignifyClient, - args: IssueCredentialArgs + name: string, + data: CredentialData ) { - const result = await client.credentials().issue(args); + const result = await client.credentials().issue(name, data); await waitOperation(client, result.op); const creds = await client.credentials().list(); assert.equal(creds.length, 1); - assert.equal(creds[0].sad.s, args.schemaId); + assert.equal(creds[0].sad.s, data.s); assert.equal(creds[0].status.s, '0'); const dt = createTimestamp(); - if (args.recipient) { + if (data.a.i) { const [grant, gsigs, end] = await client.ipex().grant({ - senderName: args.issuerName, - recipient: args.recipient, + senderName: name, + recipient: data.a.i, datetime: dt, acdc: result.acdc, anc: result.anc, @@ -501,7 +502,7 @@ async function issueCredential( let op = await client .ipex() - .submitGrant(args.issuerName, grant, gsigs, end, [args.recipient]); + .submitGrant(name, grant, gsigs, end, [data.a.i]); op = await waitOperation(client, op); } diff --git a/examples/integration-scripts/multisig.test.ts b/examples/integration-scripts/multisig.test.ts index ae03baaf..79f9d789 100644 --- a/examples/integration-scripts/multisig.test.ts +++ b/examples/integration-scripts/multisig.test.ts @@ -797,7 +797,6 @@ test('multisig', async function run() { }); op2 = await vcpRes2.op(); serder = vcpRes2.regser; - const regk2 = serder.pre; anc = vcpRes2.serder; sigs = vcpRes2.sigs; @@ -840,7 +839,6 @@ test('multisig', async function run() { }); op3 = await vcpRes3.op(); serder = vcpRes3.regser; - const regk3 = serder.pre; anc = vcpRes3.serder; sigs = vcpRes3.sigs; @@ -881,13 +879,14 @@ test('multisig', async function run() { const holder = aid4.prefix; const TIME = new Date().toISOString().replace('Z', '000+00:00'); - const credRes = await client1.credentials().issue({ - issuerName: 'multisig', - registryId: regk, - schemaId: SCHEMA_SAID, - data: vcdata, - recipient: holder, - datetime: TIME, + const credRes = await client1.credentials().issue('multisig', { + ri: regk, + s: SCHEMA_SAID, + a: { + i: holder, + dt: TIME, + ...vcdata, + }, }); op1 = credRes.op; await multisigIssue(client1, 'member1', 'multisig', credRes); @@ -905,15 +904,7 @@ test('multisig', async function run() { exn = res[0].exn; const credentialSaid = exn.e.acdc.d; - - const credRes2 = await client2.credentials().issue({ - issuerName: 'multisig', - registryId: regk2, - schemaId: SCHEMA_SAID, - data: vcdata, - datetime: exn.e.acdc.a.dt, - recipient: holder, - }); + const credRes2 = await client2.credentials().issue('multisig', exn.e.acdc); op2 = credRes2.op; await multisigIssue(client2, 'member2', 'multisig', credRes2); @@ -927,14 +918,7 @@ test('multisig', async function run() { res = await client3.groups().getRequest(msgSaid); exn = res[0].exn; - const credRes3 = await client3.credentials().issue({ - issuerName: 'multisig', - registryId: regk3, - schemaId: SCHEMA_SAID, - recipient: holder, - data: vcdata, - datetime: exn.e.acdc.a.dt, - }); + const credRes3 = await client3.credentials().issue('multisig', exn.e.acdc); op3 = credRes3.op; await multisigIssue(client3, 'member3', 'multisig', credRes3); diff --git a/examples/integration-scripts/singlesig-vlei-issuance.test.ts b/examples/integration-scripts/singlesig-vlei-issuance.test.ts index 7eee0d1b..59861090 100644 --- a/examples/integration-scripts/singlesig-vlei-issuance.test.ts +++ b/examples/integration-scripts/singlesig-vlei-issuance.test.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'assert'; -import { Saider, Serder, SignifyClient } from 'signify-ts'; +import { Saider, Salter, Serder, SignifyClient } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; import { assertOperations, @@ -490,8 +490,7 @@ async function getOrIssueCredential( credData: any, schema: string, rules?: any, - source?: any, - privacy: boolean = false + source?: any ): Promise { const credentialList = await issuerClient.credentials().list(); @@ -507,15 +506,17 @@ async function getOrIssueCredential( if (credential) return credential; } - const issResult = await issuerClient.credentials().issue({ - issuerName: issuerAid.name, - registryId: issuerRegistry.regk, - schemaId: schema, - recipient: recipientAid.prefix, - data: credData, - rules: rules, - source: source, - privacy: privacy, + const issResult = await issuerClient.credentials().issue(issuerAid.name, { + ri: issuerRegistry.regk, + s: schema, + u: new Salter({}).qb64, + a: { + i: recipientAid.prefix, + u: new Salter({}).qb64, + ...credData, + }, + r: rules, + e: source, }); await waitOperation(issuerClient, issResult.op); diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 28b11ab2..8a31a29c 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -36,51 +36,23 @@ export interface CredentialFilter { limit?: number; } -export interface IssueCredentialArgs { - /** - * Name of the issuer identifier - */ - issuerName: string; - - /** - * QB64 AID of credential registry - */ - registryId: string; - - /** - * SAID Of the schema - */ - schemaId: string; - - /** - * Prefix of recipient identifier - */ - recipient?: string; - - /** - * Credential data - */ - data?: Record; - - /** - * Credential rules - */ - rules?: string | Record; - - /** - * Credential sources - */ - source?: Record; - - /** - * Datetime to set for the credential - */ - datetime?: string; +export interface CredentialSubject { + i?: string; + dt?: string; + u?: string; + [key: string]: unknown; +} - /** - * Flag to issue a credential with privacy preserving features - */ - privacy?: boolean; +export interface CredentialData { + v?: string; + d?: string; + u?: string; + i?: string; + ri?: string; + s?: string; + a: CredentialSubject; + e?: { [key: string]: unknown }; + r?: { [key: string]: unknown }; } export interface IssueCredentialResult { @@ -184,8 +156,11 @@ export class Credentials { /** * Issue a credential */ - async issue(args: IssueCredentialArgs): Promise { - const hab = await this.client.identifiers().get(args.issuerName); + async issue( + name: string, + args: CredentialData + ): Promise { + const hab = await this.client.identifiers().get(name); const estOnly = hab.state.c !== undefined && hab.state.c.includes('EO'); if (estOnly) { // TODO implement rotation event @@ -197,27 +172,18 @@ export class Credentials { const keeper = this.client.manager.get(hab); - const dt = - args.datetime ?? new Date().toISOString().replace('Z', '000+00:00'); - const [, subject] = Saider.saidify({ d: '', - u: args.privacy ? new Salter({}).qb64 : undefined, - i: args.recipient, - dt: dt, - ...args.data, + ...args.a, + dt: args.a.dt ?? new Date().toISOString().replace('Z', '000+00:00'), }); const [, acdc] = Saider.saidify({ v: versify(Ident.ACDC, undefined, Serials.JSON, 0), d: '', - u: args.privacy ? new Salter({}).qb64 : undefined, - i: hab.prefix, - ri: args.registryId, - s: args.schemaId, + i: args.i ?? hab.prefix, + ...args, a: subject, - e: args.source, - r: args.rules, }); const [, iss] = Saider.saidify({ @@ -226,8 +192,8 @@ export class Credentials { d: '', i: acdc.d, s: '0', - ri: args.registryId, - dt: dt, + ri: args.ri, + dt: args.a.dt ?? new Date().toISOString().replace('Z', '000+00:00'), }); const sn = parseInt(hab.state.s, 16); diff --git a/test/app/credentialing.test.ts b/test/app/credentialing.test.ts index e426a041..3e822bc0 100644 --- a/test/app/credentialing.test.ts +++ b/test/app/credentialing.test.ts @@ -209,15 +209,10 @@ describe('Credentialing', () => { const registry = 'EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX'; const schema = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao'; const isuee = 'EG2XjQN-3jPN5rcR4spLjaJyM4zA6Lgg-Hd5vSMymu5p'; - await credentials.issue({ - issuerName: 'aid1', - registryId: registry, - schemaId: schema, - recipient: isuee, - data: { LEI: '1234' }, - source: {}, - rules: {}, - privacy: false, + await credentials.issue('aid1', { + ri: registry, + s: schema, + a: { i: isuee, LEI: '1234' }, }); lastCall = fetchMock.mock.calls[fetchMock.mock.calls.length - 1]!; lastBody = JSON.parse(lastCall[1]!.body!.toString()); From dab92513e9306338e0f3f8ce25ef016cf68f156b Mon Sep 17 00:00:00 2001 From: lenkan Date: Mon, 19 Feb 2024 10:21:44 +0100 Subject: [PATCH 2/4] fix types --- examples/integration-scripts/multisig-holder.test.ts | 7 +------ .../integration-scripts/singlesig-vlei-issuance.test.ts | 7 ++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/examples/integration-scripts/multisig-holder.test.ts b/examples/integration-scripts/multisig-holder.test.ts index bb13c5b3..b5e26879 100644 --- a/examples/integration-scripts/multisig-holder.test.ts +++ b/examples/integration-scripts/multisig-holder.test.ts @@ -1,10 +1,5 @@ import { strict as assert } from 'assert'; -import signify, { - SignifyClient, - IssueCredentialArgs, - Operation, - CredentialData, -} from 'signify-ts'; +import signify, { SignifyClient, Operation, CredentialData } from 'signify-ts'; import { resolveEnvironment } from './utils/resolve-env'; import { assertOperations, diff --git a/examples/integration-scripts/singlesig-vlei-issuance.test.ts b/examples/integration-scripts/singlesig-vlei-issuance.test.ts index 59861090..6b729663 100644 --- a/examples/integration-scripts/singlesig-vlei-issuance.test.ts +++ b/examples/integration-scripts/singlesig-vlei-issuance.test.ts @@ -490,7 +490,8 @@ async function getOrIssueCredential( credData: any, schema: string, rules?: any, - source?: any + source?: any, + privacy = false ): Promise { const credentialList = await issuerClient.credentials().list(); @@ -509,10 +510,10 @@ async function getOrIssueCredential( const issResult = await issuerClient.credentials().issue(issuerAid.name, { ri: issuerRegistry.regk, s: schema, - u: new Salter({}).qb64, + u: privacy ? new Salter({}).qb64 : undefined, a: { i: recipientAid.prefix, - u: new Salter({}).qb64, + u: privacy ? new Salter({}).qb64 : undefined, ...credData, }, r: rules, From ad375f01d2bf0c868b6509d19f203cc7e65cf231 Mon Sep 17 00:00:00 2001 From: lenkan Date: Mon, 19 Feb 2024 10:25:44 +0100 Subject: [PATCH 3/4] add comments --- src/keri/app/credentialing.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 8a31a29c..39876bb5 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -37,8 +37,17 @@ export interface CredentialFilter { } export interface CredentialSubject { + /** + * Issuee, or holder of the credential. + */ i?: string; + /** + * Timestamp of issuance. + */ dt?: string; + /** + * Privacy salt + */ u?: string; [key: string]: unknown; } @@ -46,12 +55,33 @@ export interface CredentialSubject { export interface CredentialData { v?: string; d?: string; + /** + * Privacy salt + */ u?: string; + /** + * Issuer of the credential. + */ i?: string; + /** + * Registry id. + */ ri?: string; + /** + * Schema id + */ s?: string; + /** + * Credential subject data + */ a: CredentialSubject; + /** + * Credential source section + */ e?: { [key: string]: unknown }; + /** + * Credential rules section + */ r?: { [key: string]: unknown }; } From 49cd8d5fa6bb6b9a3bac00d57409cc10315b3b03 Mon Sep 17 00:00:00 2001 From: lenkan Date: Mon, 19 Feb 2024 17:14:13 +0100 Subject: [PATCH 4/4] fix dt for issuance event --- src/keri/app/credentialing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keri/app/credentialing.ts b/src/keri/app/credentialing.ts index 39876bb5..4d231087 100644 --- a/src/keri/app/credentialing.ts +++ b/src/keri/app/credentialing.ts @@ -223,7 +223,7 @@ export class Credentials { i: acdc.d, s: '0', ri: args.ri, - dt: args.a.dt ?? new Date().toISOString().replace('Z', '000+00:00'), + dt: subject.dt, }); const sn = parseInt(hab.state.s, 16);