@originator-profile/verify 0.4.0-beta.1

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1186 @@
1
+ 'use strict';
2
+
3
+ var securingMechanism = require('@originator-profile/securing-mechanism');
4
+ var sign = require('@originator-profile/sign');
5
+ var cryptography = require('@originator-profile/cryptography');
6
+ var model = require('@originator-profile/model');
7
+
8
+ class CaInvalid extends Error {
9
+ constructor(message, result) {
10
+ super(message);
11
+ this.result = result;
12
+ }
13
+ static get code() {
14
+ return "ERR_CONTENT_ATTESTATION_INVALID";
15
+ }
16
+ code = CaInvalid.code;
17
+ }
18
+ class CaVerifyFailed extends Error {
19
+ constructor(message, result) {
20
+ super(message);
21
+ this.result = result;
22
+ }
23
+ static get code() {
24
+ return "ERR_CONTENT_ATTESTATION_VERIFY_FAILED";
25
+ }
26
+ code = CaVerifyFailed.code;
27
+ }
28
+
29
+ function verifyAllowedOrigin(origin, allowedOrigins) {
30
+ if (origin === "null") {
31
+ return false;
32
+ }
33
+ return [allowedOrigins].flat().includes(origin);
34
+ }
35
+
36
+ async function importURLPatternPolyfill() {
37
+ if (typeof URLPattern === "undefined") {
38
+ await Promise.resolve().then(function () { return require('./index-D-j8gXz_.cjs'); });
39
+ }
40
+ }
41
+ function ReplaceEncode(url) {
42
+ return url.replace(/(%[0-9a-f]{2}?)+/g, function(match) {
43
+ return match.toUpperCase();
44
+ });
45
+ }
46
+ async function verifyAllowedUrl(url, allowedUrl) {
47
+ await importURLPatternPolyfill();
48
+ return [allowedUrl].flat().some((value) => {
49
+ if (!value) {
50
+ return false;
51
+ }
52
+ const pattern = new URLPattern(ReplaceEncode(value));
53
+ return pattern.test(ReplaceEncode(url));
54
+ });
55
+ }
56
+
57
+ const supportedHashAlgorithms = {
58
+ /** SHA-256 hash algorithm */
59
+ sha256: "SHA-256",
60
+ /** SHA-384 hash algorithm */
61
+ sha384: "SHA-384",
62
+ /** SHA-512 hash algorithm */
63
+ sha512: "SHA-512"
64
+ };
65
+ function getPrioritizedHashAlgorithm(a, b) {
66
+ if (a === b) return "";
67
+ if (!(a in supportedHashAlgorithms)) {
68
+ return b in supportedHashAlgorithms ? b : "";
69
+ }
70
+ if (!(b in supportedHashAlgorithms)) {
71
+ return a in supportedHashAlgorithms ? a : "";
72
+ }
73
+ return a < b ? b : a;
74
+ }
75
+ const IntegrityMetadataRegex = /^(?<alg>sha256|sha384|sha512)-(?<val>(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)(?:[?](?<opt>[\x21-\x7e]*))?$/;
76
+ const SeparatorRegex = /[^\x21-\x7e]+/;
77
+ class IntegrityMetadata {
78
+ /** Hash algorithm */
79
+ alg;
80
+ /** The base64-encoded hash value of the resource */
81
+ val;
82
+ /** Optional additional attributes */
83
+ opt;
84
+ /**
85
+ * Creates an instance of `IntegrityMetadata` from a given object or string.
86
+ * @param integrity The integrity metadata input, which can be a string or object.
87
+ * @example
88
+ * ```js
89
+ * new IntegrityMetadata("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
90
+ * ```
91
+ *
92
+ * or
93
+ *
94
+ * ```js
95
+ * new IntegrityMetadata({
96
+ * alg: "sha256",
97
+ * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
98
+ * })
99
+ * ```
100
+ */
101
+ constructor(integrity) {
102
+ const integrityString = typeof integrity === "object" && integrity !== null ? IntegrityMetadata.stringify(integrity) : String(integrity ?? "").trim();
103
+ const {
104
+ alg = "",
105
+ val = "",
106
+ opt
107
+ } = IntegrityMetadataRegex.exec(integrityString)?.groups ?? {};
108
+ Object.assign(this, {
109
+ alg,
110
+ val,
111
+ opt: opt?.split("?") ?? []
112
+ });
113
+ }
114
+ /**
115
+ * Compares the current integrity metadata with another object or string.
116
+ * @param integrity The integrity metadata to compare with.
117
+ * @returns `true` if the integrity metadata matches, `false` otherwise.
118
+ * @example
119
+ * ```js
120
+ * integrityMetadata.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
121
+ * ```
122
+ *
123
+ * or
124
+ *
125
+ * ```js
126
+ * integrityMetadata.match({
127
+ * alg: "sha256",
128
+ * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
129
+ * })
130
+ * ```
131
+ */
132
+ match(integrity) {
133
+ const { alg, val } = new IntegrityMetadata(integrity);
134
+ if (!alg) return false;
135
+ if (!val) return false;
136
+ if (!(alg in supportedHashAlgorithms)) return false;
137
+ return alg === this.alg && val === this.val;
138
+ }
139
+ /**
140
+ * Converts the integrity metadata into a string representation.
141
+ * @returns The string representation of the integrity metadata.
142
+ */
143
+ toString() {
144
+ return IntegrityMetadata.stringify(this);
145
+ }
146
+ /**
147
+ * Converts the integrity metadata into a JSON string.
148
+ * @returns The JSON string representation of the integrity metadata.
149
+ */
150
+ toJSON() {
151
+ return this.toString();
152
+ }
153
+ /**
154
+ * Static method to stringify an integrity metadata object.
155
+ * @param integrity The integrity metadata object to stringify.
156
+ * @returns The stringified integrity metadata.
157
+ * @example
158
+ * ```js
159
+ * IntegrityMetadata.stringify({
160
+ * alg: "sha256",
161
+ * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
162
+ * }) // "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM="
163
+ * ```
164
+ */
165
+ static stringify({ alg, val, opt = [] }) {
166
+ if (!alg) return "";
167
+ if (!val) return "";
168
+ if (!(alg in supportedHashAlgorithms)) return "";
169
+ return `${alg}-${[val, ...opt].join("?")}`;
170
+ }
171
+ }
172
+ async function createIntegrityMetadata(hashAlgorithm, data, opt = []) {
173
+ const alg = hashAlgorithm.toLowerCase();
174
+ if (!(alg in supportedHashAlgorithms)) {
175
+ return new IntegrityMetadata("");
176
+ }
177
+ const hashAlgorithmIdentifier = supportedHashAlgorithms[alg];
178
+ const arrayBuffer = await crypto.subtle.digest(hashAlgorithmIdentifier, data);
179
+ const val = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
180
+ const integrity = IntegrityMetadata.stringify({ alg, val, opt });
181
+ return new IntegrityMetadata(integrity);
182
+ }
183
+ class IntegrityMetadataSet {
184
+ #set;
185
+ #getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm;
186
+ /**
187
+ * Create an instance of `IntegrityMetadataSet` from integrity metadata or an array of integrity
188
+ * metadata.
189
+ * @param integrity The integrity metadata or an array of integrity metadata.
190
+ * @param options Optional configuration options for hash algorithm prioritization.
191
+ * @example
192
+ * ```js
193
+ * new IntegrityMetadataSet([
194
+ * "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
195
+ * "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
196
+ * "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
197
+ * ])
198
+ * ```
199
+ *
200
+ * or
201
+ *
202
+ * ```js
203
+ * new IntegrityMetadataSet(`
204
+ * sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
205
+ * sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
206
+ * sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
207
+ * `)
208
+ * ```
209
+ */
210
+ constructor(integrity, {
211
+ getPrioritizedHashAlgorithm: _getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm
212
+ } = {}) {
213
+ this.#set = [integrity].flat().flatMap(
214
+ (integrity2) => {
215
+ if (typeof integrity2 === "string") {
216
+ return integrity2.split(SeparatorRegex);
217
+ }
218
+ return [integrity2];
219
+ }
220
+ ).map((integrity2) => new IntegrityMetadata(integrity2)).filter((integrityMetadata) => integrityMetadata.toString() !== "");
221
+ this.#getPrioritizedHashAlgorithm = _getPrioritizedHashAlgorithm;
222
+ }
223
+ /**
224
+ * Enables iteration over the set of integrity metadata.
225
+ * @returns A generator that yields each IntegrityMetadata object.
226
+ * @example
227
+ * ```js
228
+ * [...integrityMetadataSet]
229
+ * ```
230
+ */
231
+ *[Symbol.iterator]() {
232
+ for (const integrityMetadata of this.#set) {
233
+ yield new IntegrityMetadata(integrityMetadata);
234
+ }
235
+ }
236
+ /**
237
+ * The number of integrity metadata entries in the set.
238
+ */
239
+ get size() {
240
+ return this.#set.length;
241
+ }
242
+ /**
243
+ * The strongest (most secure) integrity metadata from the set.
244
+ * @see {@link https://www.w3.org/TR/SRI/#get-the-strongest-metadata-from-set}
245
+ */
246
+ get strongest() {
247
+ let strongest = new IntegrityMetadataSet([]);
248
+ for (const integrityMetadata of this.#set) {
249
+ const [{ alg } = new IntegrityMetadata("")] = strongest;
250
+ const prioritizedHashAlgorithm = this.#getPrioritizedHashAlgorithm(
251
+ alg,
252
+ integrityMetadata.alg
253
+ );
254
+ switch (prioritizedHashAlgorithm) {
255
+ case "":
256
+ strongest = new IntegrityMetadataSet([
257
+ ...strongest,
258
+ integrityMetadata
259
+ ]);
260
+ break;
261
+ case integrityMetadata.alg:
262
+ strongest = new IntegrityMetadataSet(integrityMetadata);
263
+ break;
264
+ }
265
+ }
266
+ return strongest;
267
+ }
268
+ /**
269
+ * Returns an array of the strongest supported hash algorithms in the set.
270
+ */
271
+ get strongestHashAlgorithms() {
272
+ const strongestHashAlgorithms = [...this.strongest].map(({ alg }) => alg).filter(Boolean);
273
+ return [...new Set(strongestHashAlgorithms)];
274
+ }
275
+ /**
276
+ * Checks if a given integrity metadata object or string matches any in the set.
277
+ * @param integrity The integrity metadata to match.
278
+ * @returns `true` if the integrity metadata matches, `false` otherwise.
279
+ * @example
280
+ * ```js
281
+ * integrityMetadataSet.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
282
+ * ```
283
+ *
284
+ * or
285
+ *
286
+ * ```js
287
+ * integrityMetadataSet.match({
288
+ * alg: "sha256",
289
+ * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
290
+ * })
291
+ * ```
292
+ */
293
+ match(integrity) {
294
+ return this.#set.some(
295
+ (integrityMetadata) => integrityMetadata.match(integrity)
296
+ );
297
+ }
298
+ /**
299
+ * Joins the integrity metadata in the set into a single string, separated by the specified
300
+ * separator.
301
+ * @param separator The separator to use (default is a space).
302
+ * @returns The joined string representation of the set.
303
+ */
304
+ join(separator = " ") {
305
+ return this.#set.map(String).join(separator);
306
+ }
307
+ /**
308
+ * Converts the set of integrity metadata to a string representation.
309
+ * @returns The string representation of the set.
310
+ */
311
+ toString() {
312
+ return this.join();
313
+ }
314
+ /**
315
+ * Converts the set of integrity metadata to a JSON string.
316
+ * @returns The JSON string representation of the set.
317
+ */
318
+ toJSON() {
319
+ return this.toString();
320
+ }
321
+ }
322
+ async function createIntegrityMetadataSet(hashAlgorithms, data, options = {
323
+ getPrioritizedHashAlgorithm
324
+ }) {
325
+ const set = await Promise.all(
326
+ [hashAlgorithms].flat().map((alg) => createIntegrityMetadata(alg, data))
327
+ );
328
+ return new IntegrityMetadataSet(set, options);
329
+ }
330
+
331
+ async function verifyDigestSri(content, fetcher = fetch) {
332
+ const integrity = new IntegrityMetadataSet(content.digestSRI);
333
+ const alg = integrity.strongestHashAlgorithms.filter(Boolean);
334
+ if (alg.length === 0) return false;
335
+ const { digestSRI } = await sign.createDigestSri(alg[0], content, fetcher);
336
+ return integrity.match(digestSRI);
337
+ }
338
+
339
+ class IntegrityVerifier {
340
+ constructor(contentFetcher, elementSelector) {
341
+ this.contentFetcher = contentFetcher;
342
+ this.elementSelector = elementSelector;
343
+ }
344
+ async verify(content, document2) {
345
+ const integrity = new IntegrityMetadataSet(content.integrity);
346
+ const alg = integrity.strongestHashAlgorithms.filter(Boolean);
347
+ if (alg.length === 0) return { valid: false, failedIntegrities: [] };
348
+ const elements = this.elementSelector({ ...content, document: document2 });
349
+ if (elements.length === 0) return { valid: false, failedIntegrities: [] };
350
+ const responses = await this.contentFetcher(elements);
351
+ const meta = await Promise.all(
352
+ responses.map(async (res) => {
353
+ const data = await res.arrayBuffer();
354
+ return await createIntegrityMetadataSet(alg, data);
355
+ })
356
+ );
357
+ const failedIntegrities = meta.filter((m) => [...m].every((m2) => !integrity.match(m2))).map((m) => m.toString());
358
+ return { valid: failedIntegrities.length === 0, failedIntegrities };
359
+ }
360
+ }
361
+ const TargetIntegrityAlgorithm = {
362
+ HtmlTargetIntegrity: {
363
+ contentFetcher: sign.fetchHtmlContent,
364
+ elementSelector: sign.selectByCss
365
+ },
366
+ TextTargetIntegrity: {
367
+ contentFetcher: sign.fetchTextContent,
368
+ elementSelector: sign.selectByCss
369
+ },
370
+ VisibleTextTargetIntegrity: {
371
+ contentFetcher: sign.fetchVisibleTextContent,
372
+ elementSelector: sign.selectByCss
373
+ },
374
+ ExternalResourceTargetIntegrity: {
375
+ contentFetcher: sign.fetchExternalResource,
376
+ elementSelector: sign.selectByIntegrity
377
+ }
378
+ };
379
+ async function verifyIntegrity(content, doc = document, fetcher = fetch) {
380
+ const { contentFetcher, elementSelector } = TargetIntegrityAlgorithm[content.type];
381
+ const integrityVerifier = new IntegrityVerifier(
382
+ (content2) => contentFetcher(content2, fetcher),
383
+ elementSelector
384
+ );
385
+ return await integrityVerifier.verify(content, doc);
386
+ }
387
+
388
+ async function checkUrlAndOrigin(result, url) {
389
+ if (result.doc.allowedUrl && result.doc.allowedOrigin) {
390
+ return new CaInvalid("allowedUrl and allowedOrigin are exclusive", result);
391
+ }
392
+ if (result.doc.allowedUrl && !await verifyAllowedUrl(url.toString(), result.doc.allowedUrl)) {
393
+ return new CaVerifyFailed(
394
+ `URL not allowed. Expected:${Array.isArray(result.doc.allowedUrl) ? result.doc.allowedUrl.join(", ") : result.doc.allowedUrl} Actual:${url}`,
395
+ result
396
+ );
397
+ }
398
+ if (result.doc.allowedOrigin && !verifyAllowedOrigin(url.origin, result.doc.allowedOrigin)) {
399
+ return new CaVerifyFailed(
400
+ `Origin not allowed. Expected:${Array.isArray(result.doc.allowedOrigin) ? result.doc.allowedOrigin.join(", ") : result.doc.allowedOrigin} Actual:${url.origin}`,
401
+ result
402
+ );
403
+ }
404
+ return result;
405
+ }
406
+ function CaVerifier(ca, keys, issuer, url, verifyIntegrity$1 = verifyIntegrity, validator) {
407
+ const verifyCa = securingMechanism.JwtVcVerifier(keys, issuer, validator);
408
+ return async () => {
409
+ const result = await verifyCa(ca);
410
+ if (result instanceof securingMechanism.VcValidateFailed) {
411
+ return new CaInvalid("Content Attestation validate failed", result);
412
+ }
413
+ if (result instanceof securingMechanism.VcVerifyFailed) {
414
+ return new CaVerifyFailed("Content Attestation verify failed", result);
415
+ }
416
+ const urlResult = await checkUrlAndOrigin(result, url);
417
+ if (urlResult instanceof Error) {
418
+ return urlResult;
419
+ }
420
+ if (urlResult.doc.target) {
421
+ if (urlResult.doc.target.length === 0) {
422
+ return new CaInvalid("Target is empty", urlResult);
423
+ }
424
+ const integrityResults = await Promise.all(
425
+ urlResult.doc.target.map(async (t, index) => ({
426
+ index,
427
+ verifyResult: await verifyIntegrity$1(t),
428
+ expectedIntegrity: t.integrity
429
+ }))
430
+ );
431
+ const failedIndices = integrityResults.filter((result2) => !result2.verifyResult.valid).map((result2) => result2.index);
432
+ if (failedIndices.length > 0) {
433
+ const failedIntegritiesMessage = failedIndices.map((integrityResultIndex) => {
434
+ const integrityResult = integrityResults[integrityResultIndex];
435
+ if (integrityResult) {
436
+ const calculatedIntegrities = integrityResult.verifyResult.failedIntegrities.join();
437
+ return `target[${integrityResultIndex}] Expected: ${integrityResult.expectedIntegrity}, Calculated: ${calculatedIntegrities}`;
438
+ }
439
+ return void 0;
440
+ }).join(", ");
441
+ return new CaVerifyFailed(
442
+ `Content Attestation Target integrity verification failed for element(s): ${failedIntegritiesMessage}`,
443
+ urlResult
444
+ );
445
+ }
446
+ }
447
+ return urlResult;
448
+ };
449
+ }
450
+
451
+ class CasVerifyFailed extends Error {
452
+ constructor(message, result) {
453
+ super(message);
454
+ this.result = result;
455
+ }
456
+ static get code() {
457
+ return "ERR_CONTENT_ATTESTATION_SET_VERIFY_FAILED";
458
+ }
459
+ code = CasVerifyFailed.code;
460
+ }
461
+
462
+ function normalizeCasItem(ca) {
463
+ if (typeof ca === "object" && ca !== null && "attestation" in ca) {
464
+ return ca;
465
+ }
466
+ return { main: false, attestation: ca };
467
+ }
468
+
469
+ async function verifyCas(cas, verifiedOps, url, verifyIntegrity, validator) {
470
+ const decodeCa = securingMechanism.JwtVcDecoder();
471
+ const resultCas = await Promise.all(
472
+ cas.map(async (ca) => {
473
+ const { main, attestation: source } = normalizeCasItem(ca);
474
+ const decodedCa = decodeCa(source);
475
+ if (decodedCa instanceof Error) {
476
+ return { main, attestation: new CaInvalid("Invalid CA", decodedCa) };
477
+ }
478
+ const cp = verifiedOps.find(
479
+ (ops) => ops.core.doc.credentialSubject.id === decodedCa.doc.issuer
480
+ );
481
+ if (!cp) {
482
+ return {
483
+ main,
484
+ attestation: new CoreProfileNotFound(
485
+ "Appropriate Core Profile not found",
486
+ decodedCa
487
+ )
488
+ };
489
+ }
490
+ const verify = CaVerifier(
491
+ source,
492
+ cryptography.LocalKeys(cp.core.doc.credentialSubject.jwks),
493
+ decodedCa.doc.issuer,
494
+ new URL(url),
495
+ verifyIntegrity,
496
+ validator?.(model.ContentAttestation)
497
+ );
498
+ return { main, attestation: await verify() };
499
+ })
500
+ );
501
+ if (resultCas.some((ca) => {
502
+ return ca.attestation instanceof CaInvalid || ca.attestation instanceof CoreProfileNotFound || ca.attestation instanceof CaVerifyFailed;
503
+ })) {
504
+ return new CasVerifyFailed(
505
+ "Content Attestation Set verify failed",
506
+ resultCas
507
+ );
508
+ }
509
+ return resultCas;
510
+ }
511
+
512
+ class ProfileGenericError extends Error {
513
+ static get code() {
514
+ return "ERR_PROFILE_GENERIC";
515
+ }
516
+ code = ProfileGenericError.code;
517
+ }
518
+ class ProfileClaimsValidationFailed extends ProfileGenericError {
519
+ static get code() {
520
+ return "ERR_PROFILE_CLAIMS_VALIDATION_FAILED";
521
+ }
522
+ code = ProfileClaimsValidationFailed.code;
523
+ /** 復号結果 */
524
+ result;
525
+ constructor(message, result) {
526
+ super(message);
527
+ this.result = result;
528
+ }
529
+ }
530
+ class ProfileTokenVerifyFailed extends ProfileGenericError {
531
+ static get code() {
532
+ return "ERR_PROFILE_TOKEN_VERIFY_FAILED";
533
+ }
534
+ code = ProfileTokenVerifyFailed.code;
535
+ /** 検証結果 */
536
+ result;
537
+ constructor(message, result) {
538
+ super(message);
539
+ this.result = result;
540
+ }
541
+ }
542
+ class ProfileBodyExtractFailed extends ProfileGenericError {
543
+ static get code() {
544
+ return "ERR_PROFILE_BODY_EXTRACT_FAILED";
545
+ }
546
+ code = ProfileBodyExtractFailed.code;
547
+ }
548
+ class ProfileBodyVerifyFailed extends ProfileGenericError {
549
+ static get code() {
550
+ return "ERR_PROFILE_BODY_VERIFY_FAILED";
551
+ }
552
+ code = ProfileBodyVerifyFailed.code;
553
+ /** 検証結果 */
554
+ result;
555
+ constructor(message, result) {
556
+ super(message);
557
+ this.result = result;
558
+ }
559
+ }
560
+ class ProfilesResolveFailed extends ProfileGenericError {
561
+ static get code() {
562
+ return "ERR_PROFILES_RESOLVE_FAILED";
563
+ }
564
+ code = ProfilesResolveFailed.code;
565
+ /** 検証結果 */
566
+ result;
567
+ constructor(message, result) {
568
+ super(message);
569
+ this.result = result;
570
+ }
571
+ }
572
+ class ProfilesVerifyFailed extends ProfileGenericError {
573
+ static get code() {
574
+ return "ERR_PROFILES_VERIFY_FAILED";
575
+ }
576
+ code = ProfilesVerifyFailed.code;
577
+ /** 検証結果 */
578
+ result;
579
+ constructor(message, result) {
580
+ super(message);
581
+ this.result = result;
582
+ }
583
+ }
584
+ class CertificationSystemValidationFailed extends ProfileGenericError {
585
+ static get code() {
586
+ return "ERR_CERTIFICATION_SYSTEM_VALIDATION_FAILED";
587
+ }
588
+ code = CertificationSystemValidationFailed.code;
589
+ /** 検証結果 */
590
+ result;
591
+ constructor(message, result) {
592
+ super(message);
593
+ this.result = result;
594
+ }
595
+ }
596
+
597
+ var REMOVE = "remove";
598
+ var REPLACE = "replace";
599
+ var ADD = "add";
600
+ var MOVE = "move";
601
+ function diffApply(obj, diff, pathConverter) {
602
+ if (!obj || typeof obj != "object") {
603
+ throw new Error("base object must be an object or an array");
604
+ }
605
+ if (!Array.isArray(diff)) {
606
+ throw new Error("diff must be an array");
607
+ }
608
+ var diffLength = diff.length;
609
+ for (var i = 0; i < diffLength; i++) {
610
+ var thisDiff = diff[i];
611
+ var subObject = obj;
612
+ var thisOp = thisDiff.op;
613
+ var thisPath = transformPath(pathConverter, thisDiff.path);
614
+ var thisFromPath = thisDiff.from && transformPath(pathConverter, thisDiff.from);
615
+ var toPath, toPathCopy, lastToProp, subToObject, valueToMove;
616
+ if (thisFromPath) {
617
+ toPath = thisPath;
618
+ thisPath = thisFromPath;
619
+ toPathCopy = toPath.slice();
620
+ lastToProp = toPathCopy.pop();
621
+ prototypeCheck(lastToProp);
622
+ if (lastToProp == null) {
623
+ return false;
624
+ }
625
+ var thisToProp;
626
+ while ((thisToProp = toPathCopy.shift()) != null) {
627
+ prototypeCheck(thisToProp);
628
+ if (!(thisToProp in subToObject)) {
629
+ subToObject[thisToProp] = {};
630
+ }
631
+ subToObject = subToObject[thisToProp];
632
+ }
633
+ }
634
+ var pathCopy = thisPath.slice();
635
+ var lastProp = pathCopy.pop();
636
+ prototypeCheck(lastProp);
637
+ if (lastProp == null) {
638
+ return false;
639
+ }
640
+ var thisProp;
641
+ while ((thisProp = pathCopy.shift()) != null) {
642
+ prototypeCheck(thisProp);
643
+ if (!(thisProp in subObject)) {
644
+ subObject[thisProp] = {};
645
+ }
646
+ subObject = subObject[thisProp];
647
+ }
648
+ if (thisOp === REMOVE || thisOp === REPLACE || thisOp === MOVE) {
649
+ var path = thisOp === MOVE ? thisDiff.from : thisDiff.path;
650
+ if (!subObject.hasOwnProperty(lastProp)) {
651
+ throw new Error(["expected to find property", path, "in object", obj].join(" "));
652
+ }
653
+ }
654
+ if (thisOp === REMOVE || thisOp === MOVE) {
655
+ if (thisOp === MOVE) {
656
+ valueToMove = subObject[lastProp];
657
+ }
658
+ Array.isArray(subObject) ? subObject.splice(lastProp, 1) : delete subObject[lastProp];
659
+ }
660
+ if (thisOp === REPLACE || thisOp === ADD) {
661
+ subObject[lastProp] = thisDiff.value;
662
+ }
663
+ if (thisOp === MOVE) {
664
+ subObject[lastToProp] = valueToMove;
665
+ }
666
+ }
667
+ return subObject;
668
+ }
669
+ function transformPath(pathConverter, thisPath) {
670
+ {
671
+ if (!Array.isArray(thisPath)) {
672
+ throw new Error([
673
+ "diff path",
674
+ thisPath,
675
+ "must be an array, consider supplying a path converter"
676
+ ].join(" "));
677
+ }
678
+ }
679
+ return thisPath;
680
+ }
681
+ function prototypeCheck(prop) {
682
+ if (prop == "__proto__" || prop == "constructor" || prop == "prototype") {
683
+ throw new Error("setting of prototype values not supported");
684
+ }
685
+ }
686
+
687
+ const patch = (...args) => {
688
+ const [source, diff] = args;
689
+ const patched = structuredClone(source);
690
+ diffApply(patched, diff);
691
+ return patched;
692
+ };
693
+ const VerifyResultFactory = (issuedAt, expiredAt) => ({
694
+ create: (vc, jwt, verificationKey, validated = false) => {
695
+ const unverified = {
696
+ doc: vc,
697
+ issuedAt,
698
+ expiredAt,
699
+ algorithm: "ES256",
700
+ mediaType: "application/vc+jwt",
701
+ source: jwt
702
+ };
703
+ if (!verificationKey) return unverified;
704
+ return { ...unverified, verificationKey, validated };
705
+ }
706
+ });
707
+ const opId = {
708
+ /** CP 発行者 */
709
+ authority: "dns:cp-issuer.example.org",
710
+ /** PA 発行者 */
711
+ certifier: "dns:pa-issuer.example.org",
712
+ /** CA 発行者 */
713
+ originator: "dns:originator.example.org",
714
+ /** 無効な第三者 */
715
+ invalid: "dns:invalid.example.org"
716
+ };
717
+ const cp = {
718
+ "@context": [
719
+ "https://www.w3.org/ns/credentials/v2",
720
+ "https://originator-profile.org/ns/credentials/v1"
721
+ ],
722
+ type: ["VerifiableCredential", "CoreProfile"],
723
+ issuer: opId.authority,
724
+ credentialSubject: {
725
+ id: opId.originator,
726
+ type: "Core",
727
+ jwks: {
728
+ keys: []
729
+ }
730
+ }
731
+ };
732
+ const certificate = {
733
+ "@context": [
734
+ "https://www.w3.org/ns/credentials/v2",
735
+ "https://originator-profile.org/ns/credentials/v1",
736
+ "https://originator-profile.org/ns/cip/v1",
737
+ {
738
+ "@language": "ja"
739
+ }
740
+ ],
741
+ type: ["VerifiableCredential", "Certificate"],
742
+ issuer: opId.certifier,
743
+ credentialSubject: {
744
+ id: opId.originator,
745
+ type: "CertificateProperties",
746
+ description: "Example Certificate",
747
+ certificationSystem: {
748
+ id: "urn:uuid:de5d6e80-10a5-404f-b4d3-e9f0e6926a21",
749
+ type: "CertificationSystem",
750
+ name: "Example Certification System",
751
+ description: "Example Certification System Description"
752
+ }
753
+ }
754
+ };
755
+ const wmp = {
756
+ "@context": [
757
+ "https://www.w3.org/ns/credentials/v2",
758
+ "https://originator-profile.org/ns/credentials/v1",
759
+ "https://originator-profile.org/ns/cip/v1",
760
+ {
761
+ "@language": "ja"
762
+ }
763
+ ],
764
+ type: ["VerifiableCredential", "WebMediaProfile"],
765
+ issuer: opId.authority,
766
+ credentialSubject: {
767
+ id: opId.originator,
768
+ type: "OnlineBusiness",
769
+ name: "Example OP Holder",
770
+ url: "https://op-originator.example.org/"
771
+ }
772
+ };
773
+ const wsp = {
774
+ "@context": [
775
+ "https://www.w3.org/ns/credentials/v2",
776
+ "https://originator-profile.org/ns/credentials/v1",
777
+ "https://originator-profile.org/ns/cip/v1",
778
+ {
779
+ "@language": "ja"
780
+ }
781
+ ],
782
+ type: ["VerifiableCredential", "WebsiteProfile"],
783
+ issuer: opId.originator,
784
+ credentialSubject: {
785
+ id: "https://originator.example.org",
786
+ type: "WebSite",
787
+ name: "Example Website",
788
+ description: "Example Website Description",
789
+ allowedOrigin: ["https://originator.example.org"]
790
+ }
791
+ };
792
+ const caId = "urn:uuid:78550fa7-f846-4e0f-ad5c-8d34461cb95b";
793
+ const caUrl = new URL("https://www.example.org/articles/example");
794
+ const article = {
795
+ "@context": [
796
+ "https://www.w3.org/ns/credentials/v2",
797
+ "https://originator-profile.org/ns/credentials/v1",
798
+ "https://originator-profile.org/ns/cip/v1",
799
+ { "@language": "ja" }
800
+ ],
801
+ type: ["VerifiableCredential", "ContentAttestation"],
802
+ issuer: opId.originator,
803
+ target: [],
804
+ allowedUrl: ["https://www.example.org/articles*"],
805
+ credentialSubject: {
806
+ id: caId,
807
+ type: "Article",
808
+ headline: "\u30C6\u30B9\u30C8\u8A18\u4E8B",
809
+ description: "\u8A18\u4E8B\u306E\u8AAC\u660E"
810
+ }
811
+ };
812
+
813
+ class OpsInvalid extends Error {
814
+ constructor(message, result) {
815
+ super(message);
816
+ this.result = result;
817
+ }
818
+ static get code() {
819
+ return "ERR_ORIGINATOR_PROFILE_SET_INVALID";
820
+ }
821
+ code = OpsInvalid.code;
822
+ }
823
+ class OpInvalid extends Error {
824
+ constructor(message, result) {
825
+ super(message);
826
+ this.result = result;
827
+ }
828
+ static get code() {
829
+ return "ERR_ORIGINATOR_PROFILE_INVALID";
830
+ }
831
+ code = OpInvalid.code;
832
+ }
833
+ class CoreProfileNotFound extends Error {
834
+ constructor(message, result) {
835
+ super(message);
836
+ this.result = result;
837
+ }
838
+ static get code() {
839
+ return "ERR_CORE_PROFILE_NOT_FOUND";
840
+ }
841
+ code = CoreProfileNotFound.code;
842
+ }
843
+ class OpsVerifyFailed extends Error {
844
+ constructor(message, result) {
845
+ super(message);
846
+ this.result = result;
847
+ }
848
+ static get code() {
849
+ return "ERR_ORIGINATOR_PROFILE_SET_VERIFY_FAILED";
850
+ }
851
+ code = OpsVerifyFailed.code;
852
+ }
853
+ class OpVerifyFailed extends Error {
854
+ constructor(message, result) {
855
+ super(message);
856
+ this.result = result;
857
+ }
858
+ static get code() {
859
+ return "ERR_ORIGINATOR_PROFILE_VERIFY_FAILED";
860
+ }
861
+ code = OpVerifyFailed.code;
862
+ }
863
+
864
+ const isEveryDecodedPa = (annotations) => annotations.every((annotation) => "doc" in annotation);
865
+ const isDecodedOps = (ops) => ops.every((op) => !(op instanceof OpInvalid));
866
+ function decodeOps(ops) {
867
+ const decodeCp = securingMechanism.JwtVcDecoder();
868
+ const decodePa = securingMechanism.JwtVcDecoder();
869
+ const decodeWmp = securingMechanism.JwtVcDecoder();
870
+ const resultOps = ops.map((op) => {
871
+ const core = decodeCp(op.core);
872
+ const annotations = op.annotations ? op.annotations.map(decodePa) : void 0;
873
+ const media = op.media ? decodeWmp(op.media) : void 0;
874
+ const resultOp = { core, annotations, media };
875
+ if (core instanceof Error) {
876
+ return new OpInvalid("Core Profile decode failed", resultOp);
877
+ }
878
+ if (annotations && !isEveryDecodedPa(annotations)) {
879
+ return new OpInvalid("Profile Annotation decode failed", resultOp);
880
+ }
881
+ if (media instanceof Error) {
882
+ return new OpInvalid("Web Media Profile decode failed", resultOp);
883
+ }
884
+ if (media && core.doc.credentialSubject.id !== media.doc.credentialSubject.id) {
885
+ return new OpInvalid(
886
+ "Subject mismatch between Core Profile and Web Media Profile",
887
+ resultOp
888
+ );
889
+ }
890
+ if (annotations && annotations.some(
891
+ (annotation) => core.doc.credentialSubject.id !== annotation.doc.credentialSubject.id
892
+ )) {
893
+ return new OpInvalid(
894
+ "Subject mismatch between Core Profile and Profile Annotation",
895
+ resultOp
896
+ );
897
+ }
898
+ return resultOp;
899
+ });
900
+ if (!isDecodedOps(resultOps)) {
901
+ return new OpsInvalid("Invalid Originator Profile Set", resultOps);
902
+ }
903
+ return resultOps;
904
+ }
905
+
906
+ function OpVerifier(cps, vc, validator) {
907
+ const cpHolder = vc.doc.issuer;
908
+ const cp = cps.get(cpHolder);
909
+ if (!cp) {
910
+ return async () => new CoreProfileNotFound(`Missing Core Profile (${cpHolder})`, vc);
911
+ }
912
+ if (cp instanceof Error) {
913
+ return async () => new CoreProfileNotFound(`Invalid Core Profile (${cpHolder})`, vc);
914
+ }
915
+ const cpKeys = cryptography.LocalKeys(cp.doc.credentialSubject.jwks);
916
+ return securingMechanism.JwtVcVerifier(cpKeys, cpHolder, validator);
917
+ }
918
+ async function verifyAnnotations(cps, annotations, validator) {
919
+ if (!annotations) return;
920
+ return await Promise.all(
921
+ annotations.map((annotation) => {
922
+ const verify = OpVerifier(
923
+ cps,
924
+ annotation,
925
+ validator?.({
926
+ oneOf: [model.Certificate, model.JapaneseExistenceCertificate]
927
+ })
928
+ );
929
+ return verify(annotation.source);
930
+ })
931
+ );
932
+ }
933
+ async function verifyMedia(cps, media, validator) {
934
+ if (!media) return;
935
+ const verify = OpVerifier(
936
+ cps,
937
+ media,
938
+ validator?.(model.WebMediaProfile)
939
+ );
940
+ return await verify(media.source);
941
+ }
942
+ const isVerifiedOps = (ops) => ops.every((op) => !(op instanceof OpVerifyFailed));
943
+ function OpsVerifier(ops, keys, issuer, validator) {
944
+ const decoded = decodeOps(ops);
945
+ const verifyCp = securingMechanism.JwtVcVerifier(
946
+ keys,
947
+ issuer,
948
+ validator?.(model.CoreProfile)
949
+ );
950
+ async function verify() {
951
+ if (decoded instanceof OpsInvalid) {
952
+ return decoded;
953
+ }
954
+ const cps = new Map(
955
+ await Promise.all(
956
+ decoded.map(
957
+ ({ core }) => verifyCp(core.source).then(
958
+ (result) => [core.doc.credentialSubject.id, result]
959
+ )
960
+ )
961
+ )
962
+ );
963
+ const resultOps = await Promise.all(
964
+ decoded.map(async (op) => {
965
+ const core = cps.get(op.core.doc.credentialSubject.id) ?? new CoreProfileNotFound(
966
+ `Missing Core Profile (${op.core.doc.credentialSubject.id})`,
967
+ op.core
968
+ );
969
+ const annotations = await verifyAnnotations(
970
+ cps,
971
+ op.annotations,
972
+ validator
973
+ );
974
+ const media = await verifyMedia(cps, op.media, validator);
975
+ const resultOp = { core, annotations, media };
976
+ if (core instanceof Error) {
977
+ return new OpVerifyFailed("Core Profile verify failed", resultOp);
978
+ }
979
+ if (annotations && annotations.some((annotation) => annotation instanceof Error)) {
980
+ return new OpVerifyFailed(
981
+ "Profile Annotation verify failed",
982
+ resultOp
983
+ );
984
+ }
985
+ if (media instanceof Error) {
986
+ return new OpVerifyFailed(
987
+ "Web Media Profile verify failed",
988
+ resultOp
989
+ );
990
+ }
991
+ return resultOp;
992
+ })
993
+ );
994
+ if (!isVerifiedOps(resultOps)) {
995
+ return new OpsVerifyFailed(
996
+ "Originator Profile Set verify failed",
997
+ resultOps
998
+ );
999
+ }
1000
+ return resultOps;
1001
+ }
1002
+ return verify;
1003
+ }
1004
+
1005
+ class SiteProfileInvalid extends Error {
1006
+ constructor(message, result) {
1007
+ super(message);
1008
+ this.result = result;
1009
+ }
1010
+ static get code() {
1011
+ return "ERR_SITE_PROFILE_INVALID";
1012
+ }
1013
+ code = SiteProfileInvalid.code;
1014
+ }
1015
+ class SiteProfileVerifyFailed extends Error {
1016
+ constructor(message, result) {
1017
+ super(message);
1018
+ this.result = result;
1019
+ }
1020
+ static get code() {
1021
+ return "ERR_SITE_PROFILE_VERIFY_FAILED";
1022
+ }
1023
+ code = SiteProfileVerifyFailed.code;
1024
+ }
1025
+
1026
+ function SpVerifier(sp, keys, issuer, origin, verifyOrigin = true, validator) {
1027
+ async function verify() {
1028
+ const verifyOps = OpsVerifier(sp.originators, keys, issuer, validator);
1029
+ const opsVerified = await verifyOps();
1030
+ if (opsVerified instanceof OpsInvalid) {
1031
+ return new SiteProfileInvalid("Originator Profile Set invalid", {
1032
+ originators: opsVerified
1033
+ });
1034
+ }
1035
+ if (opsVerified instanceof OpsVerifyFailed) {
1036
+ return new SiteProfileVerifyFailed(
1037
+ "Originator Profile Set verify failed",
1038
+ { originators: opsVerified }
1039
+ );
1040
+ }
1041
+ const decodeWsp = securingMechanism.JwtVcDecoder();
1042
+ const decodedWsp = decodeWsp(sp.credential);
1043
+ if (decodedWsp instanceof Error) {
1044
+ return new SiteProfileInvalid("Website Profile invalid", {
1045
+ originators: opsVerified,
1046
+ credential: decodedWsp
1047
+ });
1048
+ }
1049
+ const wspIssuer = decodedWsp.doc.issuer;
1050
+ const cp = opsVerified.find(
1051
+ (op) => op.core.doc.credentialSubject.id === wspIssuer
1052
+ );
1053
+ if (!cp) {
1054
+ return new SiteProfileInvalid("Appropriate Core Profile not found", {
1055
+ originators: opsVerified,
1056
+ credential: new CoreProfileNotFound(
1057
+ `Missing Core Profile (${wspIssuer})`,
1058
+ decodedWsp
1059
+ )
1060
+ });
1061
+ }
1062
+ const verifyWsp = securingMechanism.JwtVcVerifier(
1063
+ cryptography.LocalKeys(cp.core.doc.credentialSubject.jwks),
1064
+ cp.core.doc.credentialSubject.id,
1065
+ validator?.(model.WebsiteProfile)
1066
+ );
1067
+ const credential = await verifyWsp(sp.credential);
1068
+ if (credential instanceof Error) {
1069
+ return new SiteProfileVerifyFailed("Website Profile verify failed", {
1070
+ originators: opsVerified,
1071
+ credential
1072
+ });
1073
+ }
1074
+ if (verifyOrigin) {
1075
+ const allowedOrigin = "allowedOrigin" in decodedWsp.doc.credentialSubject ? decodedWsp.doc.credentialSubject.allowedOrigin : decodedWsp.doc.credentialSubject.url;
1076
+ if (!verifyAllowedOrigin(origin, allowedOrigin)) {
1077
+ return new SiteProfileVerifyFailed("Origin not allowed", {
1078
+ originators: opsVerified,
1079
+ credential
1080
+ });
1081
+ }
1082
+ }
1083
+ return { originators: opsVerified, credential };
1084
+ }
1085
+ return verify;
1086
+ }
1087
+
1088
+ var objectTypeof = typeOf;
1089
+ function typeOf(obj) {
1090
+ if (obj === null) {
1091
+ return "null";
1092
+ }
1093
+ if (obj !== Object(obj)) {
1094
+ return typeof obj;
1095
+ }
1096
+ var result = {}.toString.call(obj).slice(8, -1).toLowerCase();
1097
+ return result.indexOf("function") > -1 ? "function" : result;
1098
+ }
1099
+
1100
+ function CertificationSystemValidator() {
1101
+ return function validate(payload) {
1102
+ if (objectTypeof(payload) !== "object") {
1103
+ return new CertificationSystemValidationFailed("should be an object", {
1104
+ payload
1105
+ });
1106
+ }
1107
+ const keys = Object.keys(payload);
1108
+ const entries = Object.entries(payload);
1109
+ if (!model.CertificationSystem.required.every((k) => keys.includes(k))) {
1110
+ return new CertificationSystemValidationFailed(
1111
+ "should be contain required properties",
1112
+ { payload }
1113
+ );
1114
+ }
1115
+ for (const entry of entries) {
1116
+ const [key, value] = entry;
1117
+ const propertySchema = model.CertificationSystem.properties[key];
1118
+ if (objectTypeof(propertySchema) !== "object") {
1119
+ return new CertificationSystemValidationFailed(
1120
+ "should not contain additional properties",
1121
+ { payload }
1122
+ );
1123
+ }
1124
+ if ("const" in propertySchema && value !== propertySchema.const)
1125
+ return new CertificationSystemValidationFailed(
1126
+ `should be contain value of '${value}' in '${key}' property`,
1127
+ { payload }
1128
+ );
1129
+ if ("type" in propertySchema && typeof value !== propertySchema.type)
1130
+ return new CertificationSystemValidationFailed(
1131
+ `should be contain ${propertySchema.type} value in '${key}' property`,
1132
+ { payload }
1133
+ );
1134
+ }
1135
+ return true;
1136
+ };
1137
+ }
1138
+ function validateCertificationSystem(payload) {
1139
+ const validator = CertificationSystemValidator();
1140
+ const result = validator(payload);
1141
+ if (result !== true) {
1142
+ return result;
1143
+ }
1144
+ return payload;
1145
+ }
1146
+
1147
+ exports.CaInvalid = CaInvalid;
1148
+ exports.CaVerifier = CaVerifier;
1149
+ exports.CaVerifyFailed = CaVerifyFailed;
1150
+ exports.CasVerifyFailed = CasVerifyFailed;
1151
+ exports.CertificationSystemValidationFailed = CertificationSystemValidationFailed;
1152
+ exports.CertificationSystemValidator = CertificationSystemValidator;
1153
+ exports.CoreProfileNotFound = CoreProfileNotFound;
1154
+ exports.OpInvalid = OpInvalid;
1155
+ exports.OpVerifyFailed = OpVerifyFailed;
1156
+ exports.OpsInvalid = OpsInvalid;
1157
+ exports.OpsVerifier = OpsVerifier;
1158
+ exports.OpsVerifyFailed = OpsVerifyFailed;
1159
+ exports.ProfileBodyExtractFailed = ProfileBodyExtractFailed;
1160
+ exports.ProfileBodyVerifyFailed = ProfileBodyVerifyFailed;
1161
+ exports.ProfileClaimsValidationFailed = ProfileClaimsValidationFailed;
1162
+ exports.ProfileGenericError = ProfileGenericError;
1163
+ exports.ProfileTokenVerifyFailed = ProfileTokenVerifyFailed;
1164
+ exports.ProfilesResolveFailed = ProfilesResolveFailed;
1165
+ exports.ProfilesVerifyFailed = ProfilesVerifyFailed;
1166
+ exports.SiteProfileInvalid = SiteProfileInvalid;
1167
+ exports.SiteProfileVerifyFailed = SiteProfileVerifyFailed;
1168
+ exports.SpVerifier = SpVerifier;
1169
+ exports.TargetIntegrityAlgorithm = TargetIntegrityAlgorithm;
1170
+ exports.VerifyResultFactory = VerifyResultFactory;
1171
+ exports.article = article;
1172
+ exports.caId = caId;
1173
+ exports.caUrl = caUrl;
1174
+ exports.certificate = certificate;
1175
+ exports.cp = cp;
1176
+ exports.decodeOps = decodeOps;
1177
+ exports.normalizeCasItem = normalizeCasItem;
1178
+ exports.opId = opId;
1179
+ exports.patch = patch;
1180
+ exports.validateCertificationSystem = validateCertificationSystem;
1181
+ exports.verifyAllowedOrigin = verifyAllowedOrigin;
1182
+ exports.verifyCas = verifyCas;
1183
+ exports.verifyDigestSri = verifyDigestSri;
1184
+ exports.verifyIntegrity = verifyIntegrity;
1185
+ exports.wmp = wmp;
1186
+ exports.wsp = wsp;