Skip to content

Commit c26fc20

Browse files
authored
DCKA 3509 use the presentation generation method in the wallet sdk (#460)
* Use credential-sdk to generation presentation for pex request * Use credential-sdk to generation presentation for pex request * Use credential-sdk to generation presentation for pex request * Fix prettier formatting in generatePresentationFromPex tests
1 parent 50a3e95 commit c26fc20

6 files changed

Lines changed: 456 additions & 78 deletions

File tree

integration-tests/verification-flow/range-proofs.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ describe('Range proofs verification', () => {
3535
template: proofRequest.qr,
3636
});
3737

38-
// We can keep the attributes to reveal empty
39-
// Range proof attributes will be handled automatically during presentation creation
40-
// and will not be revealed in the presentation
41-
// even if we include them in the attributesToReveal, they will be ignored
4238
const attributesToReveal = [];
4339

4440
controller.selectedCredentials.set(credential.id, {
@@ -149,7 +145,7 @@ describe('Range proofs verification', () => {
149145
const presentation = await controller.createPresentation();
150146

151147
// Presentation issuanceDate should not be equal to the credential issuanceDate
152-
// The credential SDK will genreate a presentation timestamp instead
148+
// The credential SDK will generate a presentation timestamp instead
153149
expect(presentation.verifiableCredential[0].issuanceDate).not.toBe(
154150
credential.issuanceDate,
155151
);

packages/core/src/verification-controller.ts

Lines changed: 89 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ export enum VerificationStatus {
2121
SelectingCredentials = 'SelectingCredentials',
2222
}
2323

24-
function isRangeProofTemplate(templateJSON) {
25-
return templateJSON.proving_key;
26-
}
27-
2824
type CredentialId = string;
2925
type CredentialSelection = {
3026
credential: any;
@@ -52,7 +48,6 @@ export function createVerificationController({
5248
let filteredCredentials = [];
5349
let selectedCredentials: CredentialSelectionMap = new Map();
5450
let selectedDID = null;
55-
let provingKey = null;
5651

5752
if (!credentialProvider) {
5853
credentialProvider = createCredentialProvider({wallet});
@@ -62,23 +57,6 @@ export function createVerificationController({
6257
didProvider = createDIDProvider({wallet});
6358
}
6459

65-
async function fetchProvingKey(templateJSON: any) {
66-
if (templateJSON.proving_key) {
67-
setState(VerificationStatus.FetchingProvingKey);
68-
try {
69-
provingKey = await axios
70-
.get(templateJSON.proving_key)
71-
.then(res => res.data);
72-
} catch (err) {
73-
setState(VerificationStatus.Error, {
74-
message: 'failed_to_fetch_proving_key',
75-
});
76-
77-
throw err;
78-
}
79-
}
80-
}
81-
8260
async function start({template}: {template: string | any}) {
8361
setState(VerificationStatus.LoadingTemplate);
8462

@@ -96,7 +74,6 @@ export function createVerificationController({
9674
selectedDID = dids[0].didDocument.id;
9775
templateJSON = await getJSON(template);
9876

99-
await fetchProvingKey(templateJSON);
10077
await loadCredentials();
10178

10279
setState(VerificationStatus.SelectingCredentials);
@@ -154,71 +131,110 @@ export function createVerificationController({
154131
return credentialServiceRPC.isKvacCredential({credential});
155132
}
156133

134+
async function deriveNonBbsCredentials(sdJwtSelections, regularSelections) {
135+
const credentials = [];
136+
137+
for (const sel of sdJwtSelections) {
138+
const derived = await credentialServiceRPC.createSDJWTPresentation({
139+
attributesToReveal: sel.attributesToReveal,
140+
credential: sel.credential._sd_jwt.encoded,
141+
});
142+
credentials.push(derived);
143+
}
144+
145+
for (const sel of regularSelections) {
146+
credentials.push(sel.credential);
147+
}
148+
149+
return credentials;
150+
}
151+
152+
function getKeyId(keyDoc) {
153+
return keyDoc.controller.startsWith('did:key:')
154+
? keyDoc.id
155+
: `${keyDoc.controller}#keys-1`;
156+
}
157+
158+
async function assembleSignedPresentation(credentials, keyDoc) {
159+
return credentialServiceRPC.createPresentation({
160+
credentials,
161+
challenge: templateJSON.nonce,
162+
keyDoc,
163+
id: getKeyId(keyDoc),
164+
domain: 'dock.io',
165+
});
166+
}
167+
157168
async function createPresentation() {
158169
assert(!!selectedDID, 'No DID selected');
159170
assert(!!selectedCredentials.size, 'No credentials selected');
160171

161-
if (isRangeProofTemplate(templateJSON)) {
162-
// TODO: Implement proving key usage for range-proofs
163-
assert(!!provingKey, 'No proving key found');
164-
}
172+
const didKeyPairList = await didProvider.getDIDKeyPairs();
173+
const keyDoc = didKeyPairList.find(doc => doc.controller === selectedDID);
174+
assert(keyDoc, `No key pair found for the selected DID ${selectedDID}`);
165175

166-
const credentials = [];
176+
const sdJwtSelections = [];
177+
const bbsKvacSelections = [];
178+
const regularSelections = [];
167179

168180
for (const credentialSelection of selectedCredentials.values()) {
169-
const isBBS = await isBBSPlusCredential(credentialSelection.credential);
170-
const isKVAC = await isKvacCredential(credentialSelection.credential);
171-
172181
if (credentialSelection.credential._sd_jwt) {
173-
const derivedCredential =
174-
await credentialServiceRPC.createSDJWTPresentation({
175-
attributesToReveal: credentialSelection.attributesToReveal,
176-
credential: credentialSelection.credential._sd_jwt.encoded,
177-
});
178-
179-
credentials.push(derivedCredential);
180-
} else if (isBBS || isKVAC) {
181-
// derive credential
182-
const derivedCredentials =
183-
await credentialServiceRPC.deriveVCFromPresentation({
184-
proofRequest: templateJSON,
185-
186-
credentials: [
187-
{
188-
credential: credentialSelection.credential,
189-
witness: await credentialProvider.getMembershipWitness(credentialSelection.credential.id),
190-
attributesToReveal: [
191-
...(credentialSelection.attributesToReveal || []),
192-
'id',
193-
],
194-
},
195-
],
196-
});
197-
198-
console.log('Credential derived');
199-
200-
credentials.push(derivedCredentials[0]);
182+
sdJwtSelections.push(credentialSelection);
201183
} else {
202-
credentials.push(credentialSelection.credential);
184+
const isBBS = await isBBSPlusCredential(credentialSelection.credential);
185+
const isKVAC = await isKvacCredential(credentialSelection.credential);
186+
if (isBBS || isKVAC) {
187+
bbsKvacSelections.push(credentialSelection);
188+
} else {
189+
regularSelections.push(credentialSelection);
190+
}
203191
}
204192
}
205193

206-
const didKeyPairList = await didProvider.getDIDKeyPairs();
207-
const keyDoc = didKeyPairList.find(doc => doc.controller === selectedDID);
194+
if (bbsKvacSelections.length > 0) {
195+
const credentialsWithWitness = await Promise.all(
196+
bbsKvacSelections.map(async sel => ({
197+
credential: sel.credential,
198+
witness: await credentialProvider.getMembershipWitness(sel.credential.id),
199+
attributesToReveal: sel.attributesToReveal || [],
200+
})),
201+
);
208202

209-
assert(keyDoc, `No key pair found for the selected DID ${selectedDID}`);
203+
// Pure BBS+/KVAC: use generatePresentationFromPex end-to-end
204+
if (sdJwtSelections.length === 0 && regularSelections.length === 0) {
205+
return credentialServiceRPC.generatePresentationFromPex({
206+
credentials: credentialsWithWitness,
207+
pexRequest: templateJSON.request,
208+
holderKeyDoc: keyDoc,
209+
holderDid: selectedDID,
210+
challenge: templateJSON.nonce,
211+
domain: 'dock.io',
212+
boundCheckSnarkKey: templateJSON.boundCheckSnarkKey,
213+
skipSigning: true,
214+
});
215+
}
210216

211-
const presentation = await credentialServiceRPC.createPresentation({
212-
credentials,
213-
challenge: templateJSON.nonce,
214-
keyDoc,
215-
id: keyDoc.controller.startsWith('did:key:')
216-
? keyDoc.id
217-
: `${keyDoc.controller}#keys-1`,
218-
domain: 'dock.io',
219-
});
217+
// Mixed: derive BBS+/KVAC, then combine with SD-JWT and regular
218+
const derivedCredentials =
219+
await credentialServiceRPC.deriveVCFromPresentation({
220+
proofRequest: templateJSON,
221+
credentials: credentialsWithWitness.map(c => ({
222+
credential: c.credential,
223+
witness: c.witness,
224+
attributesToReveal: [...(c.attributesToReveal || []), 'id'],
225+
})),
226+
});
227+
228+
const nonBbsCredentials = await deriveNonBbsCredentials(sdJwtSelections, regularSelections);
229+
return assembleSignedPresentation(
230+
[...derivedCredentials, ...nonBbsCredentials],
231+
keyDoc,
232+
);
233+
}
220234

221-
return presentation;
235+
// No BBS+/KVAC: handle SD-JWT and regular only
236+
const credentials = await deriveNonBbsCredentials(sdJwtSelections, regularSelections);
237+
return assembleSignedPresentation(credentials, keyDoc);
222238
}
223239

224240
/**

packages/wasm/src/services/credential/config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ export const validation = {
4040
'publicKeyBase58 is not present',
4141
);
4242
},
43+
generatePresentationFromPex: params => {
44+
const {credentials, pexRequest, challenge} = params;
45+
assert(Array.isArray(credentials), 'invalid credentials');
46+
assert(credentials.length > 0, 'no credential found');
47+
assert(pexRequest, 'pexRequest is required');
48+
assert(challenge, 'challenge is required');
49+
},
4350
createPresentation: params => {
4451
const {credentials, keyDoc, challenge, id} = params;
4552
assert(typeof id === 'string', 'invalid id');

0 commit comments

Comments
 (0)