@sovrahq/waci 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +21 -0
  3. package/.prettierrc +4 -0
  4. package/LICENSE +201 -0
  5. package/dist/callbacks/index.d.ts +108 -0
  6. package/dist/callbacks/index.js +9 -0
  7. package/dist/callbacks/index.js.map +1 -0
  8. package/dist/constants/index.d.ts +1 -0
  9. package/dist/constants/index.js +5 -0
  10. package/dist/constants/index.js.map +1 -0
  11. package/dist/handlers/common/problem-report.handler.d.ts +4 -0
  12. package/dist/handlers/common/problem-report.handler.js +72 -0
  13. package/dist/handlers/common/problem-report.handler.js.map +1 -0
  14. package/dist/handlers/common/step-2-oob-invitation.handler.d.ts +4 -0
  15. package/dist/handlers/common/step-2-oob-invitation.handler.js +93 -0
  16. package/dist/handlers/common/step-2-oob-invitation.handler.js.map +1 -0
  17. package/dist/handlers/decorators/register-handler.decorator.d.ts +2 -0
  18. package/dist/handlers/decorators/register-handler.decorator.js +13 -0
  19. package/dist/handlers/decorators/register-handler.decorator.js.map +1 -0
  20. package/dist/handlers/index.d.ts +6 -0
  21. package/dist/handlers/index.js +11 -0
  22. package/dist/handlers/index.js.map +1 -0
  23. package/dist/handlers/issuance/step-3-propose-credential.handler.d.ts +32 -0
  24. package/dist/handlers/issuance/step-3-propose-credential.handler.js +180 -0
  25. package/dist/handlers/issuance/step-3-propose-credential.handler.js.map +1 -0
  26. package/dist/handlers/issuance/step-4-1-offer-credential-proceed.handler.d.ts +5 -0
  27. package/dist/handlers/issuance/step-4-1-offer-credential-proceed.handler.js +152 -0
  28. package/dist/handlers/issuance/step-4-1-offer-credential-proceed.handler.js.map +1 -0
  29. package/dist/handlers/issuance/step-4-offer-credential.handler.d.ts +5 -0
  30. package/dist/handlers/issuance/step-4-offer-credential.handler.js +169 -0
  31. package/dist/handlers/issuance/step-4-offer-credential.handler.js.map +1 -0
  32. package/dist/handlers/issuance/step-5-request-credential.handler.d.ts +4 -0
  33. package/dist/handlers/issuance/step-5-request-credential.handler.js +275 -0
  34. package/dist/handlers/issuance/step-5-request-credential.handler.js.map +1 -0
  35. package/dist/handlers/issuance/step-6-issue-credential.handler.d.ts +4 -0
  36. package/dist/handlers/issuance/step-6-issue-credential.handler.js +106 -0
  37. package/dist/handlers/issuance/step-6-issue-credential.handler.js.map +1 -0
  38. package/dist/handlers/issuance/step-7-ack-message.handler.d.ts +4 -0
  39. package/dist/handlers/issuance/step-7-ack-message.handler.js +78 -0
  40. package/dist/handlers/issuance/step-7-ack-message.handler.js.map +1 -0
  41. package/dist/handlers/presentation/step-3-propose-presentation.handler.d.ts +5 -0
  42. package/dist/handlers/presentation/step-3-propose-presentation.handler.js +107 -0
  43. package/dist/handlers/presentation/step-3-propose-presentation.handler.js.map +1 -0
  44. package/dist/handlers/presentation/step-4-1-presentation-proceed.handler.d.ts +5 -0
  45. package/dist/handlers/presentation/step-4-1-presentation-proceed.handler.js +130 -0
  46. package/dist/handlers/presentation/step-4-1-presentation-proceed.handler.js.map +1 -0
  47. package/dist/handlers/presentation/step-4-request-presentation.handler.d.ts +5 -0
  48. package/dist/handlers/presentation/step-4-request-presentation.handler.js +151 -0
  49. package/dist/handlers/presentation/step-4-request-presentation.handler.js.map +1 -0
  50. package/dist/handlers/presentation/step-5-present-proof.handler.d.ts +4 -0
  51. package/dist/handlers/presentation/step-5-present-proof.handler.js +211 -0
  52. package/dist/handlers/presentation/step-5-present-proof.handler.js.map +1 -0
  53. package/dist/handlers/presentation/step-6-ack-message.handler.d.ts +4 -0
  54. package/dist/handlers/presentation/step-6-ack-message.handler.js +72 -0
  55. package/dist/handlers/presentation/step-6-ack-message.handler.js.map +1 -0
  56. package/dist/index.d.ts +17 -0
  57. package/dist/index.js +37 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/services/waci-interpreter.d.ts +17 -0
  60. package/dist/services/waci-interpreter.js +222 -0
  61. package/dist/services/waci-interpreter.js.map +1 -0
  62. package/dist/types/actor.d.ts +5 -0
  63. package/dist/types/actor.js +10 -0
  64. package/dist/types/actor.js.map +1 -0
  65. package/dist/types/credential-application.d.ts +36 -0
  66. package/dist/types/credential-application.js +3 -0
  67. package/dist/types/credential-application.js.map +1 -0
  68. package/dist/types/credential-manifest.d.ts +173 -0
  69. package/dist/types/credential-manifest.js +3 -0
  70. package/dist/types/credential-manifest.js.map +1 -0
  71. package/dist/types/index.d.ts +4 -0
  72. package/dist/types/index.js +21 -0
  73. package/dist/types/index.js.map +1 -0
  74. package/dist/types/problem-report.d.ts +13 -0
  75. package/dist/types/problem-report.js +18 -0
  76. package/dist/types/problem-report.js.map +1 -0
  77. package/dist/types/waci-message.d.ts +106 -0
  78. package/dist/types/waci-message.js +106 -0
  79. package/dist/types/waci-message.js.map +1 -0
  80. package/dist/utils/erros.d.ts +15 -0
  81. package/dist/utils/erros.js +42 -0
  82. package/dist/utils/erros.js.map +1 -0
  83. package/dist/utils/index.d.ts +22 -0
  84. package/dist/utils/index.js +356 -0
  85. package/dist/utils/index.js.map +1 -0
  86. package/jest.config.json +17 -0
  87. package/package.json +39 -0
  88. package/readme.md +1 -0
  89. package/src/callbacks/index.ts +75 -0
  90. package/src/constants/index.ts +1 -0
  91. package/src/handlers/common/problem-report.handler.ts +15 -0
  92. package/src/handlers/common/step-2-oob-invitation.handler.ts +45 -0
  93. package/src/handlers/decorators/register-handler.decorator.ts +10 -0
  94. package/src/handlers/index.ts +7 -0
  95. package/src/handlers/issuance/step-3-propose-credential.handler.ts +186 -0
  96. package/src/handlers/issuance/step-4-1-offer-credential-proceed.handler.ts +129 -0
  97. package/src/handlers/issuance/step-4-offer-credential.handler.ts +137 -0
  98. package/src/handlers/issuance/step-5-request-credential.handler.ts +205 -0
  99. package/src/handlers/issuance/step-6-issue-credential.handler.ts +63 -0
  100. package/src/handlers/issuance/step-7-ack-message.handler.ts +21 -0
  101. package/src/handlers/presentation/step-3-propose-presentation.handler.ts +67 -0
  102. package/src/handlers/presentation/step-4-1-presentation-proceed.handler.ts +100 -0
  103. package/src/handlers/presentation/step-4-request-presentation.handler.ts +115 -0
  104. package/src/handlers/presentation/step-5-present-proof.handler.ts +159 -0
  105. package/src/handlers/presentation/step-6-ack-message.handler.ts +15 -0
  106. package/src/index.ts +18 -0
  107. package/src/services/waci-interpreter.ts +161 -0
  108. package/src/types/actor.ts +5 -0
  109. package/src/types/credential-application.ts +38 -0
  110. package/src/types/credential-manifest.ts +184 -0
  111. package/src/types/index.ts +4 -0
  112. package/src/types/problem-report.ts +29 -0
  113. package/src/types/waci-message.ts +148 -0
  114. package/src/utils/erros.ts +21 -0
  115. package/src/utils/index.ts +272 -0
  116. package/test/handlers/issuance/step-3-propose-credential.handler.spec.ts +43 -0
  117. package/test/handlers/issuance/step-4-offer-credential.handler.spec.ts +53 -0
  118. package/test/handlers/issuance/step-5-request-credential.handler.spec.ts +102 -0
  119. package/test/handlers/presentation/step-5-present-proof.handler.spec.ts +142 -0
  120. package/test/handlers/shared/step-2-oob-invitation.handler.spec.ts +55 -0
  121. package/test/stubs/index.ts +842 -0
  122. package/test/waci-interpreter.spec.ts +113 -0
  123. package/tsconfig.build.json +9 -0
  124. package/tsconfig.json +20 -0
@@ -0,0 +1,272 @@
1
+ import * as UUID from 'uuid';
2
+ import * as jsonpath from 'jsonpath';
3
+ import * as jsonschema from 'jsonschema';
4
+ import {
5
+ CredentialApplication,
6
+ CredentialPresentation,
7
+ PresentationDefinition,
8
+ WACIMessage,
9
+ } from '../types';
10
+ import { Callback } from '../callbacks';
11
+ import { InputDescriptorError } from './erros';
12
+
13
+ /**
14
+ * Extract the claim key from a JSONPath expression.
15
+ * E.g. "$.credentialSubject.name" → "name", "$.type" → "type"
16
+ */
17
+ export const extractClaimKeyFromPath = (path: string): string => {
18
+ const segments = path.replace(/^\$\.?/, '').split('.');
19
+ return segments[segments.length - 1];
20
+ };
21
+
22
+ /**
23
+ * Parse an SD-JWT string and extract the disclosed claim names as a Set.
24
+ * SD-JWT format: <JWT>~<disclosure1>~<disclosure2>~...~[<KB-JWT>]
25
+ * Each disclosure is base64url-encoded JSON array: [salt, claimName, claimValue]
26
+ */
27
+ export const extractSDJWTClaims = (sdJwtString: string): Set<string> => {
28
+ const claims = new Set<string>();
29
+ try {
30
+ const parts = sdJwtString.split('~');
31
+ // Skip first part (JWT) and filter out empty strings and potential KB-JWT (has dots)
32
+ const disclosures = parts.slice(1).filter(p => p.length > 0);
33
+ for (const disclosure of disclosures) {
34
+ // Skip if it looks like a JWT (KB-JWT)
35
+ if (disclosure.split('.').length === 3) continue;
36
+ try {
37
+ // Base64url decode
38
+ let base64 = disclosure.replace(/-/g, '+').replace(/_/g, '/');
39
+ while (base64.length % 4 !== 0) base64 += '=';
40
+ const decoded = Buffer.from(base64, 'base64').toString('utf-8');
41
+ const array = JSON.parse(decoded);
42
+ if (Array.isArray(array) && array.length >= 2) {
43
+ claims.add(array[1]); // claimName is second element
44
+ }
45
+ } catch {
46
+ // Skip malformed disclosures
47
+ }
48
+ }
49
+ } catch {
50
+ // Return empty set on any error
51
+ }
52
+ return claims;
53
+ };
54
+
55
+ export const getObjectValues = (object: any): string[] =>
56
+ Object.values<string>(object);
57
+
58
+ export const createUUID = UUID.v4;
59
+
60
+ export const verifyPresentation = async (
61
+ presentationDefinition: PresentationDefinition,
62
+ credentialApplication: CredentialApplication | CredentialPresentation,
63
+ verificationCallback: Callback<any, { result: boolean, error?: string[] }>,
64
+ ): Promise<any> => {
65
+ try {
66
+ const vcs: any[] = [];
67
+ for await (const inputDescriptor of presentationDefinition.input_descriptors) {
68
+
69
+ const vcInput =
70
+ credentialApplication.data.json.presentation_submission.descriptor_map.find(
71
+ (descriptor) => inputDescriptor.id === descriptor.id,
72
+ );
73
+
74
+ if (!vcInput) return new InputDescriptorError();
75
+ const vc = jsonpath.query(
76
+ credentialApplication.data.json,
77
+ vcInput.path,
78
+ )[0];
79
+
80
+ vcs.push(vc);
81
+
82
+ // SD-JWT: if VC is a string (e.g. SD-JWT compact), verify signature then validate disclosed fields
83
+ if (typeof vc === 'string') {
84
+ const verificationResult = await verificationCallback(vc);
85
+ console.log('---- Verification Result (SD-JWT string) -----', verificationResult);
86
+ if (!verificationResult.result) {
87
+ return {
88
+ result: false,
89
+ error: verificationResult.error || ['Credential verification failed'],
90
+ vcs
91
+ };
92
+ }
93
+ // Validate that required fields from constraints are present in SD-JWT disclosures
94
+ if (inputDescriptor.constraints?.fields) {
95
+ const disclosedClaims = extractSDJWTClaims(vc);
96
+ for (const field of inputDescriptor.constraints.fields) {
97
+ const pathKey = extractClaimKeyFromPath(field.path[0]);
98
+ if (!disclosedClaims.has(pathKey)) {
99
+ return {
100
+ result: false,
101
+ error: [{ name: 'missing-field', description: `SD-JWT missing required disclosure: ${pathKey}` }],
102
+ vcs,
103
+ };
104
+ }
105
+ }
106
+ }
107
+ continue;
108
+ }
109
+
110
+ // Verify fields
111
+ for (const field of inputDescriptor.constraints.fields) {
112
+ const fieldValue = jsonpath.query(vc, field.path[0])?.[0];
113
+ if (!fieldValue) {
114
+ return { result: false, error: [{ name: 'missing-field', description: `Missing required field: ${field.path[0]}` }], vcs };
115
+ }
116
+ if (field.filter) {
117
+ const { errors } = jsonschema.validate(fieldValue, field.filter);
118
+ if (errors.length) {
119
+ return { result: false, error: [{ name: 'invalid-field', description: `Field ${field.path[0]} does not match filter` }], vcs };
120
+ }
121
+ }
122
+ }
123
+
124
+ // Verify proof
125
+ const verificationResult = await verificationCallback(vc);
126
+ console.log('---- Verification Result -----', verificationResult);
127
+
128
+ if (!verificationResult.result) {
129
+ const error = verificationResult.error as any;
130
+ switch (error.name) {
131
+ case 'did-document-resolution-error':
132
+ // DIDDocumentResolutionError
133
+ console.log(`Cannot resolve DID document: ${error.did}`);
134
+ // Handle DID resolution failure
135
+ break;
136
+
137
+ case 'vc-invalid-signature':
138
+ // InvalidSignatureError
139
+ console.log('Invalid signature detected');
140
+ console.log('Description:', error.description);
141
+ // Handle signature validation failure
142
+ break;
143
+
144
+ case 'verification-method-not-found':
145
+ // VerificationMethodNotFound
146
+ console.log(`Verification method ${error.verificationMethod} not found in DID Document: ${error.did}`);
147
+ // Handle missing verification method
148
+ break;
149
+
150
+ case 'verification-relationship-invalid':
151
+ // VerificationRelationshipError
152
+ console.log(`Verification method ${error.verificationMethod} is not configured as ${error.expectedVerificationRelationship}`);
153
+ // Handle incorrect verification relationship
154
+ break;
155
+
156
+ case 'unexpected-challenge':
157
+ // UnexpectedChallengeError
158
+ console.log('Unexpected challenge error:', error.errorMessage);
159
+ // Handle challenge validation failure
160
+ break;
161
+
162
+ case 'authentication-purpose-challenge-required':
163
+ // AuthenticationPurposeChallengeRequired
164
+ console.log('Authentication purpose requires a challenge');
165
+ // Handle missing challenge for authentication
166
+ break;
167
+
168
+ case 'verifiable-credential-revoked':
169
+ // VerifiableCredentialRevoked
170
+ console.log('Credential has been revoked');
171
+ console.log('Revocation details:', error.errors);
172
+ return {
173
+ result: false,
174
+ error: verificationResult.error || ['Credential verification failed'],
175
+ vcs:vcs
176
+ };
177
+ // Handle revoked credential
178
+ break;
179
+
180
+ case 'verifiable-credential-suspended':
181
+ // VerifiableCredentialSuspended
182
+ console.log('Credential has been suspended');
183
+ console.log('Suspension details:', error.errors);
184
+ return {
185
+ result: false,
186
+ error: verificationResult.error || ['Credential verification failed'],
187
+ vcs:vcs
188
+ };
189
+ // Handle suspended credential
190
+ break;
191
+
192
+ case 'credential-status-service-error':
193
+ // CredentialStatusServiceError
194
+ console.log(`Error retrieving credential status from: ${error.endpoint}`);
195
+ console.log(`HTTP Status: ${error.httpStatusResult}`);
196
+ console.log(`Response Data: ${error.dataResult}`);
197
+ // Handle credential status service failure
198
+ break;
199
+
200
+ case 'verifiable-credential-expired':
201
+ // VerifiableCredentialExpired
202
+ console.log('Credential has expired');
203
+ return {
204
+ result: false,
205
+ error: verificationResult.error || ['Credential verification failed'],
206
+ vcs:vcs
207
+ };
208
+ // Handle expired credential
209
+ break;
210
+
211
+ case 'unhandled-vc-suite-error':
212
+ // UnhandledVCSuiteError
213
+ console.log('Unhandled VC suite error:', error.messageError);
214
+ // Handle unexpected verification errors
215
+ break;
216
+
217
+ default:
218
+ console.log('Unknown error type:', error.name);
219
+ console.log('Error description:', error.description);
220
+ console.log('Error code:', error.code);
221
+ // Handle unknown errors
222
+ }
223
+ }
224
+ }
225
+ return {
226
+ result: true,
227
+ vcs
228
+ };
229
+ } catch (error) {
230
+ console.error(error);
231
+ return {
232
+ result: false, error, vcs: []
233
+ };
234
+ }
235
+ };
236
+
237
+ export const extractExpectedChallenge = (
238
+ presentationDefinitionMessage: WACIMessage,
239
+ ): string => {
240
+ return presentationDefinitionMessage.attachments.find(
241
+ (attachment) => attachment?.data?.json?.options?.challenge,
242
+ ).data.json.options.challenge;
243
+ };
244
+
245
+ export const validateVcByInputDescriptor = (vc, inputDescriptor): boolean => {
246
+ // SD-JWT: validate disclosed claims against required fields
247
+ if (typeof vc === 'string') {
248
+ if (!inputDescriptor.constraints?.fields) return true;
249
+ const disclosedClaims = extractSDJWTClaims(vc);
250
+ for (const field of inputDescriptor.constraints.fields) {
251
+ const pathKey = extractClaimKeyFromPath(field.path[0]);
252
+ if (!disclosedClaims.has(pathKey)) return false;
253
+ }
254
+ return true;
255
+ }
256
+ for (const field of inputDescriptor.constraints.fields) {
257
+ const fieldValues = field.path?.map((path) => {
258
+ return jsonpath.value(vc, path);
259
+ });
260
+
261
+ for (const value of fieldValues) {
262
+ if (!value) return false;
263
+ if (field.filter) {
264
+ const { errors } = jsonschema.validate(value, field.filter);
265
+ if (errors.length) {
266
+ return false;
267
+ }
268
+ }
269
+ }
270
+ }
271
+ return true;
272
+ };
@@ -0,0 +1,43 @@
1
+ import {
2
+ WACIMessage,
3
+ WACIMessageHandlerResponse,
4
+ WACIMessageResponseType,
5
+ WACIMessageType,
6
+ } from '../../../src';
7
+ import {
8
+ credentialManifestParamsStub,
9
+ offerCredentialMessageStub1,
10
+ } from '../../stubs';
11
+ import { ProposeCredentialHandler } from '../../../src/handlers/issuance/step-3-propose-credential.handler';
12
+
13
+ jest.mock('../../../src/utils', () => ({
14
+ extractSuffixFromUUID:
15
+ jest.requireActual('../../../src/utils').extractSuffixFromUUID,
16
+ verifyPresentation:
17
+ jest.requireActual('../../../src/utils').verifyPresentation,
18
+ createUUID: (): string => 'f137e0db-db7b-4776-9530-83c808a34a42',
19
+ }));
20
+
21
+ describe('ProposeCredentialHandler', () => {
22
+ const message: WACIMessage = {
23
+ type: WACIMessageType.ProposeCredential,
24
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
25
+ from: 'did:example:holder',
26
+ pthid: 'f137e0db-db7b-4776-9530-83c808a34a42',
27
+ to: ['did:example:issuer'],
28
+ };
29
+
30
+ const callbacks = {
31
+ issuer: { getCredentialManifest: () => credentialManifestParamsStub },
32
+ };
33
+
34
+ const handler = new ProposeCredentialHandler();
35
+ it('should return an offer credential message', async () => {
36
+ const response = await handler.handle([message], callbacks);
37
+ const expectedResponse: WACIMessageHandlerResponse = {
38
+ responseType: WACIMessageResponseType.ReplyThread,
39
+ message: offerCredentialMessageStub1,
40
+ };
41
+ expect(response).toEqual(expectedResponse);
42
+ });
43
+ });
@@ -0,0 +1,53 @@
1
+ import {
2
+ WACIMessageHandlerResponse,
3
+ WACIMessageResponseType,
4
+ } from '../../../src';
5
+ import {
6
+ credentialsToPresentStub,
7
+ offerCredentialMessageStub2,
8
+ requestCredentialMessageStub,
9
+ } from '../../stubs';
10
+ import { OfferCredentialHandler } from '../../../src/handlers/issuance/step-4-offer-credential.handler';
11
+
12
+ jest.mock('../../../src/utils', () => ({
13
+ extractSuffixFromUUID:
14
+ jest.requireActual('../../../src/utils').extractSuffixFromUUID,
15
+ verifyPresentation:
16
+ jest.requireActual('../../../src/utils').verifyPresentation,
17
+ createUUID: (): string => 'f137e0db-db7b-4776-9530-83c808a34a42',
18
+ }));
19
+
20
+ describe('OfferCredentialHandler', () => {
21
+ const callbacks: any = {
22
+ holder: {
23
+ getCredentialApplication: () => ({
24
+ credentialsToPresent: credentialsToPresentStub,
25
+ presentationProofTypes: [
26
+ 'JsonWebSignature2020',
27
+ 'EcdsaSecp256k1Signature2019',
28
+ ],
29
+ }),
30
+ signPresentation: ({ contentToSign }) => ({
31
+ ...contentToSign,
32
+ proof: {
33
+ type: 'Ed25519Signature2018',
34
+ verificationMethod: 'did:example:123#key-0',
35
+ created: '2021-05-14T20:16:29.565377',
36
+ proofPurpose: 'authentication',
37
+ challenge: 'f137e0db-db7b-4776-9530-83c808a34a42',
38
+ jws: 'eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0IjogWyJiNjQiXX0..7M9LwdJR1_SQayHIWVHF5eSSRhbVsrjQHKUrfRhRRrlbuKlggm8mm_4EI_kTPeBpalQWiGiyCb_0OWFPtn2wAQ',
39
+ },
40
+ }),
41
+ },
42
+ };
43
+
44
+ const handler = new OfferCredentialHandler();
45
+ it('should return a request credential message', async () => {
46
+ const response = await handler.handle([offerCredentialMessageStub2], callbacks);
47
+ const expectedResponse: WACIMessageHandlerResponse = {
48
+ responseType: WACIMessageResponseType.ReplyThread,
49
+ message: requestCredentialMessageStub,
50
+ };
51
+ expect(response).toEqual(expectedResponse);
52
+ });
53
+ });
@@ -0,0 +1,102 @@
1
+ import {
2
+ WACIMessageHandlerResponse,
3
+ WACIMessageResponseType,
4
+ WACIMessageType,
5
+ } from '../../../src';
6
+ import {
7
+ badCredentialApplicationStub,
8
+ credentialFulfillmentStub,
9
+ offerCredentialMessageStub1,
10
+ requestCredentialMessageStub,
11
+ credentialSignatureStub,
12
+ offerCredentialMessageStub2,
13
+ } from '../../stubs';
14
+ import { RequestCredentialHandler } from '../../../src/handlers/issuance/step-5-request-credential.handler';
15
+
16
+ jest.mock('../../../src/utils', () => ({
17
+ extractSuffixFromUUID:
18
+ jest.requireActual('../../../src/utils').extractSuffixFromUUID,
19
+ verifyPresentation:
20
+ jest.requireActual('../../../src/utils').verifyPresentation,
21
+ extractExpectedChallenge:
22
+ jest.requireActual('../../../src/utils').extractExpectedChallenge,
23
+ createUUID: (): string => 'f137e0db-db7b-4776-9530-83c808a34a42',
24
+ }));
25
+
26
+ describe('RequestCredentialHandler', () => {
27
+ const callbacks: any = {
28
+ issuer: {
29
+ signCredential: (vc) => ({ ...vc, proof: credentialSignatureStub }),
30
+ verifyCredential: async () => true,
31
+ verifyPresentation: async () => true,
32
+ },
33
+ };
34
+
35
+ const handler = new RequestCredentialHandler();
36
+
37
+ it('should return a issue credential message when credential application is successful', async () => {
38
+ const response = await handler.handle([
39
+ offerCredentialMessageStub1,
40
+ requestCredentialMessageStub,
41
+ ], callbacks);
42
+ const expectedResponse: WACIMessageHandlerResponse = {
43
+ responseType: WACIMessageResponseType.ReplyThread,
44
+ message: {
45
+ type: WACIMessageType.IssueCredential,
46
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
47
+ thid: 'f137e0db-db7b-4776-9530-83c808a34a42',
48
+ from: 'did:example:issuer',
49
+ to: ['did:example:holder'],
50
+ body: {},
51
+ attachments: [credentialFulfillmentStub],
52
+ },
53
+ };
54
+ expect(response).toEqual(expectedResponse);
55
+ });
56
+
57
+ it('should return a issue credential message when credential application is successful', async () => {
58
+ const response = await handler.handle([
59
+ offerCredentialMessageStub1,
60
+ {
61
+ type: WACIMessageType.RequestCredential,
62
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
63
+ thid: 'f137e0db-db7b-4776-9530-83c808a34a42',
64
+ from: 'did:example:holder',
65
+ to: ['did:example:issuer'],
66
+ body: {},
67
+ attachments: [],
68
+ },
69
+ ], callbacks);
70
+ const expectedResponse: WACIMessageHandlerResponse = {
71
+ responseType: WACIMessageResponseType.ReplyThread,
72
+ message: {
73
+ type: WACIMessageType.IssueCredential,
74
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
75
+ thid: 'f137e0db-db7b-4776-9530-83c808a34a42',
76
+ from: 'did:example:issuer',
77
+ to: ['did:example:holder'],
78
+ body: {},
79
+ attachments: [credentialFulfillmentStub],
80
+ },
81
+ };
82
+ expect(response).toEqual(expectedResponse);
83
+ });
84
+
85
+ it('should return undefined/void when credential application fails', async () => {
86
+ const failCallbacks: any = {
87
+ issuer: {
88
+ signCredential: (vc) => ({ ...vc, proof: credentialSignatureStub }),
89
+ verifyCredential: async () => false,
90
+ verifyPresentation: async () => false,
91
+ },
92
+ };
93
+ const response = await handler.handle([
94
+ offerCredentialMessageStub2,
95
+ {
96
+ ...requestCredentialMessageStub,
97
+ attachments: [badCredentialApplicationStub],
98
+ },
99
+ ], failCallbacks);
100
+ expect(response).toBe(undefined);
101
+ });
102
+ });
@@ -0,0 +1,142 @@
1
+ import {
2
+ badPresentProofMessageStub,
3
+ presentProofMessageStub,
4
+ requestPresentationMessageStub,
5
+ } from '../../stubs';
6
+ import {
7
+ AckStatus,
8
+ WACIMessageHandlerResponse,
9
+ WACIMessageResponseType,
10
+ WACIMessageType,
11
+ } from '../../../src';
12
+ import { PresentProofHandler } from '../../../src/handlers/presentation/step-5-present-proof.handler';
13
+
14
+ jest.mock('../../../src/utils', () => ({
15
+ extractSuffixFromUUID:
16
+ jest.requireActual('../../../src/utils').extractSuffixFromUUID,
17
+ verifyPresentation:
18
+ jest.requireActual('../../../src/utils').verifyPresentation,
19
+ extractExpectedChallenge:
20
+ jest.requireActual('../../../src/utils').extractExpectedChallenge,
21
+ createUUID: (): string => 'f137e0db-db7b-4776-9530-83c808a34a42',
22
+ }));
23
+
24
+ describe('PresentProofHandler', () => {
25
+ const callbacks: any = {
26
+ verifier: {
27
+ getPresentationDefinition: async () => [],
28
+ verifyCredential: () => true,
29
+ verifyPresentation: () => true,
30
+ },
31
+ };
32
+
33
+ const handler = new PresentProofHandler();
34
+ it('should return status OK', async () => {
35
+ const response = await handler.handle([
36
+ requestPresentationMessageStub,
37
+ presentProofMessageStub,
38
+ ], callbacks);
39
+ const expectedResponse: WACIMessageHandlerResponse = {
40
+ responseType: WACIMessageResponseType.ReplyThread,
41
+ message: {
42
+ type: WACIMessageType.PresentationAck,
43
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
44
+ thid: 'f137e0db-db7b-4776-9530-83c808a34a42',
45
+ from: 'did:example:issuer',
46
+ to: ['did:example:holder'],
47
+ body: {
48
+ status: AckStatus.Ok,
49
+ },
50
+ },
51
+ };
52
+
53
+ expect(response).toEqual(expectedResponse);
54
+ });
55
+
56
+ it('should return status FAIL when wrong data is presented', async () => {
57
+ const failCallbacks: any = {
58
+ verifier: {
59
+ getPresentationDefinition: async () => [
60
+ {
61
+ id: 'ed7d9b1f-9eed-4bde-b81c-3aa7485cf947',
62
+ media_type: 'application/json',
63
+ format: 'dif/presentation-exchange/definitions@v1.0',
64
+ data: {
65
+ json: {
66
+ options: {
67
+ challenge: '3fa85f64-5717-4562-b3fc-2c963f66afa7',
68
+ domain: '4jt78h47fh47',
69
+ },
70
+ presentation_definition: {
71
+ id: '32f54163-7166-48f1-93d8-ff217bdb0654',
72
+ frame: {
73
+ '@context': [
74
+ 'https://www.w3.org/2018/credentials/v1',
75
+ 'https://w3id.org/vaccination/v1',
76
+ 'https://w3id.org/security/suites/bls12381-2020/v1',
77
+ ],
78
+ type: ['VerifiableCredential', 'VaccinationCertificate'],
79
+ credentialSubject: {
80
+ '@explicit': true,
81
+ type: ['VaccinationEvent'],
82
+ batchNumber: {},
83
+ countryOfVaccination: {},
84
+ },
85
+ },
86
+ input_descriptors: [
87
+ {
88
+ id: 'vaccination_input',
89
+ name: 'Vaccination Certificate',
90
+ constraints: {
91
+ fields: [
92
+ {
93
+ path: ['$.credentialSubject.batchNumber'],
94
+ filter: {
95
+ type: 'string',
96
+ },
97
+ },
98
+ {
99
+ path: ['$.credentialSubject.countryOfVaccination'],
100
+ filter: {
101
+ type: 'string',
102
+ },
103
+ },
104
+ ],
105
+ },
106
+ },
107
+ ],
108
+ },
109
+ },
110
+ },
111
+ },
112
+ ],
113
+ verifyCredential: () => true,
114
+ verifyPresentation: () => true,
115
+ },
116
+ };
117
+
118
+ const response2 = await handler.handle([
119
+ requestPresentationMessageStub,
120
+ {
121
+ ...badPresentProofMessageStub,
122
+ attachments: [],
123
+ },
124
+ ], failCallbacks);
125
+
126
+ const expectedResponse = {
127
+ message: {
128
+ body: {
129
+ status: 'FAIL',
130
+ },
131
+ from: 'did:example:issuer',
132
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
133
+ thid: 'f137e0db-db7b-4776-9530-83c808a34a42',
134
+ to: ['did:example:holder'],
135
+ type: 'https://didcomm.org/present-proof/3.0/ack',
136
+ },
137
+ responseType: 1,
138
+ };
139
+
140
+ expect(response2).toEqual(expectedResponse);
141
+ });
142
+ });
@@ -0,0 +1,55 @@
1
+ import { GoalCode, WACIMessage, WACIMessageType } from '../../../src';
2
+ import { callbacks } from '../../../src/callbacks';
3
+ import { OOBInvitationHandler } from '../../../src/handlers/common/step-2-oob-invitation.handler';
4
+
5
+ describe('OobInvitationHandler', () => {
6
+ const message : WACIMessage = {
7
+ type: WACIMessageType.OutOfBandInvitation,
8
+ id: 'f137e0db-db7b-4776-9530-83c808a34a42',
9
+ from: 'did:example:issuer',
10
+ body: {
11
+ goal_code: GoalCode.Issuance,
12
+ accept: [ 'didcomm/v2' ],
13
+ },
14
+ };
15
+
16
+ Object.assign(callbacks, {
17
+ holder: {
18
+ getHolderDID: () => 'did:modena:1323232',
19
+ },
20
+ });
21
+
22
+ it('should return a propose credential message when handling an issuance invitation', async () => {
23
+ const response = await new OOBInvitationHandler().handle([ message ]);
24
+
25
+ expect(response.message.type).toEqual(WACIMessageType.ProposeCredential);
26
+ });
27
+
28
+ it('should return a propose presentation message when handling a presentation invitation', async () => {
29
+ const presentationInvitation = {
30
+ ...message,
31
+ body: {
32
+ ...message.body,
33
+ goal_code: GoalCode.Presentation,
34
+ },
35
+ };
36
+
37
+ const response = await new OOBInvitationHandler().handle([
38
+ presentationInvitation,
39
+ ]);
40
+
41
+ expect(response.message.type).toEqual(WACIMessageType.ProposePresentation);
42
+ });
43
+
44
+ it('should return a message whose parent thread id matches the invitation message id', async () => {
45
+ const response = await new OOBInvitationHandler().handle([ message ]);
46
+
47
+ expect(response.message.pthid).toEqual(message.id);
48
+ });
49
+
50
+ it('should return a message whose receiver matches the sender from the handled message', async () => {
51
+ const response = await new OOBInvitationHandler().handle([ message ]);
52
+
53
+ expect(response.message.to[0]).toEqual(message.from);
54
+ });
55
+ });