From bfed44e7c3e80a65a693767e09462d99c585e9cc Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Thu, 14 Mar 2024 21:20:20 +0800 Subject: [PATCH 001/107] chore: npm install jsonld & joi - upgrade @types/jsonld and ts-node --- package-lock.json | 323 ++++++++++++++++++++++++++++++++++++---------- package.json | 6 +- 2 files changed, 262 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc421fa0..e18a0a93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,8 +17,10 @@ "debug": "^4.3.2", "ethers": "^5.7.2", "flatley": "^5.2.0", + "joi": "^17.12.2", "js-base64": "^3.6.1", "js-sha3": "^0.8.0", + "jsonld": "^8.3.2", "lodash": "^4.17.21", "runtypes": "^6.3.2", "uuid": "^8.3.2", @@ -32,7 +34,7 @@ "@types/debug": "^4.1.7", "@types/jest": "^29.5.3", "@types/js-base64": "^3.3.1", - "@types/jsonld": "^1.5.6", + "@types/jsonld": "^1.5.13", "@types/lodash": "^4.14.171", "@types/qrcode": "^1.4.1", "@types/uuid": "^8.3.1", @@ -57,7 +59,7 @@ "rollup-plugin-commonjs": "^10.1.0", "semantic-release": "^21.1.1", "ts-jest": "^29.1.1", - "ts-node": "^9.1.1", + "ts-node": "^10.9.2", "typescript": "^5.1.6" } }, @@ -908,49 +910,6 @@ "node": ">=v14" } }, - "node_modules/@commitlint/load/node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/@commitlint/message": { "version": "17.4.2", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", @@ -1154,6 +1113,19 @@ "node": ">=4.9.1" } }, + "node_modules/@digitalbazaar/http-client": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-3.4.1.tgz", + "integrity": "sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==", + "dependencies": { + "ky": "^0.33.3", + "ky-universal": "^0.11.0", + "undici": "^5.21.2" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -1898,6 +1870,14 @@ "@ethersproject/strings": "^5.7.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, "node_modules/@govtechsg/jsonld": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@govtechsg/jsonld/-/jsonld-0.1.1.tgz", @@ -1912,6 +1892,19 @@ "node": ">=12" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -3633,6 +3626,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3803,9 +3814,9 @@ "dev": true }, "node_modules/@types/jsonld": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@types/jsonld/-/jsonld-1.5.9.tgz", - "integrity": "sha512-K76ImkErPYL2wGPZpNFSKp6wE+h/APecZLJrU7UfDaGqt/f+D9Rrg1aR7VdRrQ6k5DUNRZ2vn9yACwmpOr9QcA==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@types/jsonld/-/jsonld-1.5.13.tgz", + "integrity": "sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==", "dev": true }, "node_modules/@types/lodash": { @@ -4037,6 +4048,17 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -5667,6 +5689,14 @@ "node": ">=8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -6878,6 +6908,14 @@ "@ethersproject/wordlists": "5.7.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -7035,6 +7073,28 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -7191,6 +7251,17 @@ "is-callable": "^1.1.3" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -9460,6 +9531,18 @@ } } }, + "node_modules/joi": { + "version": "17.12.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", + "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-base64": { "version": "3.7.5", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", @@ -9574,6 +9657,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/jsonld": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.2.tgz", + "integrity": "sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -9617,6 +9714,58 @@ "node": ">=6" } }, + "node_modules/ky": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/ky-universal": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.11.0.tgz", + "integrity": "sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==", + "dependencies": { + "abort-controller": "^3.0.0", + "node-fetch": "^3.2.10" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" + }, + "peerDependencies": { + "ky": ">=0.31.4", + "web-streams-polyfill": ">=3.2.1" + }, + "peerDependenciesMeta": { + "web-streams-polyfill": { + "optional": true + } + } + }, + "node_modules/ky-universal/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -10220,6 +10369,24 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -16609,39 +16776,46 @@ } }, "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, - "engines": { - "node": ">=10.0.0" - }, "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", "typescript": ">=2.7" - } - }, - "node_modules/ts-node/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, "node_modules/tsconfig-paths": { @@ -16837,6 +17011,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/unicode-trie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz", @@ -17034,6 +17219,14 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index ea12195c..62a290cd 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@types/debug": "^4.1.7", "@types/jest": "^29.5.3", "@types/js-base64": "^3.3.1", - "@types/jsonld": "^1.5.6", + "@types/jsonld": "^1.5.13", "@types/lodash": "^4.14.171", "@types/qrcode": "^1.4.1", "@types/uuid": "^8.3.1", @@ -67,7 +67,7 @@ "rollup-plugin-commonjs": "^10.1.0", "semantic-release": "^21.1.1", "ts-jest": "^29.1.1", - "ts-node": "^9.1.1", + "ts-node": "^10.9.2", "typescript": "^5.1.6" }, "dependencies": { @@ -78,8 +78,10 @@ "debug": "^4.3.2", "ethers": "^5.7.2", "flatley": "^5.2.0", + "joi": "^17.12.2", "js-base64": "^3.6.1", "js-sha3": "^0.8.0", + "jsonld": "^8.3.2", "lodash": "^4.17.21", "runtypes": "^6.3.2", "uuid": "^8.3.2", From 36c9bcab16b1dc8c71dbd7e6628475c40f2801e1 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Thu, 14 Mar 2024 22:00:07 +0800 Subject: [PATCH 002/107] chore: add vc 2.0 context url and store as cache - instead of keeping a local copy, may consider to use node-cache in the future --- src/4.0/contexts/index.ts | 292 ++++++++++++++++++++++++++++++++++ src/4.0/validate/validate.ts | 2 +- src/shared/@types/document.ts | 1 + 3 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 src/4.0/contexts/index.ts diff --git a/src/4.0/contexts/index.ts b/src/4.0/contexts/index.ts new file mode 100644 index 00000000..5dbff4e1 --- /dev/null +++ b/src/4.0/contexts/index.ts @@ -0,0 +1,292 @@ +export const cachedContexts: Record = { + "https://www.w3.org/ns/credentials/v2": { + "@context": { + "@protected": true, + "@vocab": "https://www.w3.org/ns/credentials/issuer-dependent#", + id: "@id", + type: "@type", + kid: { + "@id": "https://www.iana.org/assignments/jose#kid", + "@type": "@id", + }, + iss: { + "@id": "https://www.iana.org/assignments/jose#iss", + "@type": "@id", + }, + sub: { + "@id": "https://www.iana.org/assignments/jose#sub", + "@type": "@id", + }, + jku: { + "@id": "https://www.iana.org/assignments/jose#jku", + "@type": "@id", + }, + x5u: { + "@id": "https://www.iana.org/assignments/jose#x5u", + "@type": "@id", + }, + aud: { + "@id": "https://www.iana.org/assignments/jwt#aud", + "@type": "@id", + }, + exp: { + "@id": "https://www.iana.org/assignments/jwt#exp", + "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", + }, + nbf: { + "@id": "https://www.iana.org/assignments/jwt#nbf", + "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", + }, + iat: { + "@id": "https://www.iana.org/assignments/jwt#iat", + "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", + }, + cnf: { + "@id": "https://www.iana.org/assignments/jwt#cnf", + "@context": { + "@protected": true, + kid: { + "@id": "https://www.iana.org/assignments/jwt#kid", + "@type": "@id", + }, + jwk: { + "@id": "https://www.iana.org/assignments/jwt#jwk", + "@type": "@json", + }, + }, + }, + _sd_alg: { + "@id": "https://www.iana.org/assignments/jwt#_sd_alg", + }, + _sd: { + "@id": "https://www.iana.org/assignments/jwt#_sd", + }, + "...": { + "@id": "https://www.iana.org/assignments/jwt#...", + }, + digestSRI: { + "@id": "https://www.w3.org/2018/credentials#digestSRI", + "@type": "https://www.w3.org/2018/credentials#sriString", + }, + digestMultibase: { + "@id": "https://w3id.org/security#digestMultibase", + "@type": "https://w3id.org/security#multibase", + }, + mediaType: { + "@id": "https://schema.org/encodingFormat", + }, + description: "https://schema.org/description", + name: "https://schema.org/name", + EnvelopedVerifiableCredential: "https://www.w3.org/2018/credentials#EnvelopedVerifiableCredential", + VerifiableCredential: { + "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + credentialSchema: { + "@id": "https://www.w3.org/2018/credentials#credentialSchema", + "@type": "@id", + }, + credentialStatus: { + "@id": "https://www.w3.org/2018/credentials#credentialStatus", + "@type": "@id", + }, + credentialSubject: { + "@id": "https://www.w3.org/2018/credentials#credentialSubject", + "@type": "@id", + }, + description: "https://schema.org/description", + evidence: { + "@id": "https://www.w3.org/2018/credentials#evidence", + "@type": "@id", + }, + validFrom: { + "@id": "https://www.w3.org/2018/credentials#validFrom", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + validUntil: { + "@id": "https://www.w3.org/2018/credentials#validUntil", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + issuer: { + "@id": "https://www.w3.org/2018/credentials#issuer", + "@type": "@id", + }, + name: "https://schema.org/name", + proof: { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph", + }, + refreshService: { + "@id": "https://www.w3.org/2018/credentials#refreshService", + "@type": "@id", + }, + termsOfUse: { + "@id": "https://www.w3.org/2018/credentials#termsOfUse", + "@type": "@id", + }, + confidenceMethod: { + "@id": "https://www.w3.org/2018/credentials#confidenceMethod", + "@type": "@id", + }, + relatedResource: { + "@id": "https://www.w3.org/2018/credentials#relatedResource", + "@type": "@id", + }, + }, + }, + VerifiablePresentation: { + "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + holder: { + "@id": "https://www.w3.org/2018/credentials#holder", + "@type": "@id", + }, + proof: { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph", + }, + verifiableCredential: { + "@id": "https://www.w3.org/2018/credentials#verifiableCredential", + "@type": "@id", + "@container": "@graph", + "@context": null, + }, + termsOfUse: { + "@id": "https://www.w3.org/2018/credentials#termsOfUse", + "@type": "@id", + }, + }, + }, + JsonSchemaCredential: "https://www.w3.org/2018/credentials#JsonSchemaCredential", + JsonSchema: { + "@id": "https://www.w3.org/2018/credentials#JsonSchema", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + jsonSchema: { + "@id": "https://w3.org/2018/credentials#jsonSchema", + "@type": "@json", + }, + }, + }, + BitstringStatusListCredential: "https://www.w3.org/ns/credentials/status#BitstringStatusListCredential", + BitstringStatusList: { + "@id": "https://www.w3.org/ns/credentials/status#BitstringStatusList", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + statusPurpose: "https://www.w3.org/ns/credentials/status#statusPurpose", + encodedList: { + "@id": "https://www.w3.org/ns/credentials/status#encodedList", + "@type": "https://w3id.org/security#multibase", + }, + ttl: "https://www.w3.org/ns/credentials/status#ttl", + statusReference: "https://www.w3.org/ns/credentials/status#statusReference", + statusSize: "https://www.w3.org/ns/credentials/status#statusSize", + statusMessage: { + "@id": "https://www.w3.org/ns/credentials/status#statusMessage", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + status: "https://www.w3.org/ns/credentials/status#status", + message: "https://www.w3.org/ns/credentials/status#message", + }, + }, + }, + }, + BitstringStatusListEntry: { + "@id": "https://www.w3.org/ns/credentials/status#BitstringStatusListEntry", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + statusPurpose: "https://www.w3.org/ns/credentials/status#statusPurpose", + statusListIndex: "https://www.w3.org/ns/credentials/status#statusListIndex", + statusListCredential: { + "@id": "https://www.w3.org/ns/credentials/status#statusListCredential", + "@type": "@id", + }, + }, + }, + DataIntegrityProof: { + "@id": "https://w3id.org/security#DataIntegrityProof", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + challenge: "https://w3id.org/security#challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + domain: "https://w3id.org/security#domain", + expires: { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + nonce: "https://w3id.org/security#nonce", + previousProof: { + "@id": "https://w3id.org/security#previousProof", + "@type": "@id", + }, + proofPurpose: { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + assertionMethod: { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set", + }, + authentication: { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityInvocation: { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityDelegation: { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set", + }, + keyAgreement: { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set", + }, + }, + }, + cryptosuite: { + "@id": "https://w3id.org/security#cryptosuite", + "@type": "https://w3id.org/security#cryptosuiteString", + }, + proofValue: { + "@id": "https://w3id.org/security#proofValue", + "@type": "https://w3id.org/security#multibase", + }, + verificationMethod: { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id", + }, + }, + }, + }, + }, +}; diff --git a/src/4.0/validate/validate.ts b/src/4.0/validate/validate.ts index db2a53e8..389d0184 100644 --- a/src/4.0/validate/validate.ts +++ b/src/4.0/validate/validate.ts @@ -40,7 +40,7 @@ const isValidRFC3986 = (str: any) => { return rfc3986.test(str); }; -const preloadedContextList = ["https://www.w3.org/2018/credentials/v1", ContextUrl.v4_alpha]; +const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; const contexts: Map> = new Map(); const nodeDocumentLoader = documentLoaders.xhr ? documentLoaders.xhr() : documentLoaders.node(); let preload = true; diff --git a/src/shared/@types/document.ts b/src/shared/@types/document.ts index b9002cf6..0a62aa56 100644 --- a/src/shared/@types/document.ts +++ b/src/shared/@types/document.ts @@ -40,6 +40,7 @@ export enum SchemaId { } export enum ContextUrl { + v2_vc = "https://www.w3.org/ns/credentials/v2", v4_alpha = "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", } From ef74d88584ffe93c1ca9fbed080f33ec7a53fc97 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Thu, 14 Mar 2024 23:17:23 +0800 Subject: [PATCH 003/107] fix: align validation with vc 2.0 test suite - fix: utilise "expand" from jsonld to ensure provided @context are interpretable - fix: improve readability and allow flexibility in oa v4 schema (wrap vs unwrapped, etc.) --- src/4.0/contexts/index.ts | 292 -------------------------------- src/4.0/schema/schema.json | 185 ++++++++++++-------- src/4.0/validate/context.ts | 43 +++++ src/4.0/validate/dataModel.ts | 70 ++++++++ src/4.0/validate/index.ts | 3 +- src/4.0/validate/validate.ts | 119 ------------- src/4.0/wrap.ts | 43 +++-- src/shared/utils/diagnose.ts | 10 +- src/shared/utils/utils.ts | 3 +- src/shared/validate/validate.ts | 3 +- 10 files changed, 263 insertions(+), 508 deletions(-) delete mode 100644 src/4.0/contexts/index.ts create mode 100644 src/4.0/validate/context.ts create mode 100644 src/4.0/validate/dataModel.ts delete mode 100644 src/4.0/validate/validate.ts diff --git a/src/4.0/contexts/index.ts b/src/4.0/contexts/index.ts deleted file mode 100644 index 5dbff4e1..00000000 --- a/src/4.0/contexts/index.ts +++ /dev/null @@ -1,292 +0,0 @@ -export const cachedContexts: Record = { - "https://www.w3.org/ns/credentials/v2": { - "@context": { - "@protected": true, - "@vocab": "https://www.w3.org/ns/credentials/issuer-dependent#", - id: "@id", - type: "@type", - kid: { - "@id": "https://www.iana.org/assignments/jose#kid", - "@type": "@id", - }, - iss: { - "@id": "https://www.iana.org/assignments/jose#iss", - "@type": "@id", - }, - sub: { - "@id": "https://www.iana.org/assignments/jose#sub", - "@type": "@id", - }, - jku: { - "@id": "https://www.iana.org/assignments/jose#jku", - "@type": "@id", - }, - x5u: { - "@id": "https://www.iana.org/assignments/jose#x5u", - "@type": "@id", - }, - aud: { - "@id": "https://www.iana.org/assignments/jwt#aud", - "@type": "@id", - }, - exp: { - "@id": "https://www.iana.org/assignments/jwt#exp", - "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", - }, - nbf: { - "@id": "https://www.iana.org/assignments/jwt#nbf", - "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", - }, - iat: { - "@id": "https://www.iana.org/assignments/jwt#iat", - "@type": "https://www.w3.org/2001/XMLSchema#nonNegativeInteger", - }, - cnf: { - "@id": "https://www.iana.org/assignments/jwt#cnf", - "@context": { - "@protected": true, - kid: { - "@id": "https://www.iana.org/assignments/jwt#kid", - "@type": "@id", - }, - jwk: { - "@id": "https://www.iana.org/assignments/jwt#jwk", - "@type": "@json", - }, - }, - }, - _sd_alg: { - "@id": "https://www.iana.org/assignments/jwt#_sd_alg", - }, - _sd: { - "@id": "https://www.iana.org/assignments/jwt#_sd", - }, - "...": { - "@id": "https://www.iana.org/assignments/jwt#...", - }, - digestSRI: { - "@id": "https://www.w3.org/2018/credentials#digestSRI", - "@type": "https://www.w3.org/2018/credentials#sriString", - }, - digestMultibase: { - "@id": "https://w3id.org/security#digestMultibase", - "@type": "https://w3id.org/security#multibase", - }, - mediaType: { - "@id": "https://schema.org/encodingFormat", - }, - description: "https://schema.org/description", - name: "https://schema.org/name", - EnvelopedVerifiableCredential: "https://www.w3.org/2018/credentials#EnvelopedVerifiableCredential", - VerifiableCredential: { - "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - credentialSchema: { - "@id": "https://www.w3.org/2018/credentials#credentialSchema", - "@type": "@id", - }, - credentialStatus: { - "@id": "https://www.w3.org/2018/credentials#credentialStatus", - "@type": "@id", - }, - credentialSubject: { - "@id": "https://www.w3.org/2018/credentials#credentialSubject", - "@type": "@id", - }, - description: "https://schema.org/description", - evidence: { - "@id": "https://www.w3.org/2018/credentials#evidence", - "@type": "@id", - }, - validFrom: { - "@id": "https://www.w3.org/2018/credentials#validFrom", - "@type": "http://www.w3.org/2001/XMLSchema#dateTime", - }, - validUntil: { - "@id": "https://www.w3.org/2018/credentials#validUntil", - "@type": "http://www.w3.org/2001/XMLSchema#dateTime", - }, - issuer: { - "@id": "https://www.w3.org/2018/credentials#issuer", - "@type": "@id", - }, - name: "https://schema.org/name", - proof: { - "@id": "https://w3id.org/security#proof", - "@type": "@id", - "@container": "@graph", - }, - refreshService: { - "@id": "https://www.w3.org/2018/credentials#refreshService", - "@type": "@id", - }, - termsOfUse: { - "@id": "https://www.w3.org/2018/credentials#termsOfUse", - "@type": "@id", - }, - confidenceMethod: { - "@id": "https://www.w3.org/2018/credentials#confidenceMethod", - "@type": "@id", - }, - relatedResource: { - "@id": "https://www.w3.org/2018/credentials#relatedResource", - "@type": "@id", - }, - }, - }, - VerifiablePresentation: { - "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - holder: { - "@id": "https://www.w3.org/2018/credentials#holder", - "@type": "@id", - }, - proof: { - "@id": "https://w3id.org/security#proof", - "@type": "@id", - "@container": "@graph", - }, - verifiableCredential: { - "@id": "https://www.w3.org/2018/credentials#verifiableCredential", - "@type": "@id", - "@container": "@graph", - "@context": null, - }, - termsOfUse: { - "@id": "https://www.w3.org/2018/credentials#termsOfUse", - "@type": "@id", - }, - }, - }, - JsonSchemaCredential: "https://www.w3.org/2018/credentials#JsonSchemaCredential", - JsonSchema: { - "@id": "https://www.w3.org/2018/credentials#JsonSchema", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - jsonSchema: { - "@id": "https://w3.org/2018/credentials#jsonSchema", - "@type": "@json", - }, - }, - }, - BitstringStatusListCredential: "https://www.w3.org/ns/credentials/status#BitstringStatusListCredential", - BitstringStatusList: { - "@id": "https://www.w3.org/ns/credentials/status#BitstringStatusList", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - statusPurpose: "https://www.w3.org/ns/credentials/status#statusPurpose", - encodedList: { - "@id": "https://www.w3.org/ns/credentials/status#encodedList", - "@type": "https://w3id.org/security#multibase", - }, - ttl: "https://www.w3.org/ns/credentials/status#ttl", - statusReference: "https://www.w3.org/ns/credentials/status#statusReference", - statusSize: "https://www.w3.org/ns/credentials/status#statusSize", - statusMessage: { - "@id": "https://www.w3.org/ns/credentials/status#statusMessage", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - status: "https://www.w3.org/ns/credentials/status#status", - message: "https://www.w3.org/ns/credentials/status#message", - }, - }, - }, - }, - BitstringStatusListEntry: { - "@id": "https://www.w3.org/ns/credentials/status#BitstringStatusListEntry", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - statusPurpose: "https://www.w3.org/ns/credentials/status#statusPurpose", - statusListIndex: "https://www.w3.org/ns/credentials/status#statusListIndex", - statusListCredential: { - "@id": "https://www.w3.org/ns/credentials/status#statusListCredential", - "@type": "@id", - }, - }, - }, - DataIntegrityProof: { - "@id": "https://w3id.org/security#DataIntegrityProof", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - challenge: "https://w3id.org/security#challenge", - created: { - "@id": "http://purl.org/dc/terms/created", - "@type": "http://www.w3.org/2001/XMLSchema#dateTime", - }, - domain: "https://w3id.org/security#domain", - expires: { - "@id": "https://w3id.org/security#expiration", - "@type": "http://www.w3.org/2001/XMLSchema#dateTime", - }, - nonce: "https://w3id.org/security#nonce", - previousProof: { - "@id": "https://w3id.org/security#previousProof", - "@type": "@id", - }, - proofPurpose: { - "@id": "https://w3id.org/security#proofPurpose", - "@type": "@vocab", - "@context": { - "@protected": true, - id: "@id", - type: "@type", - assertionMethod: { - "@id": "https://w3id.org/security#assertionMethod", - "@type": "@id", - "@container": "@set", - }, - authentication: { - "@id": "https://w3id.org/security#authenticationMethod", - "@type": "@id", - "@container": "@set", - }, - capabilityInvocation: { - "@id": "https://w3id.org/security#capabilityInvocationMethod", - "@type": "@id", - "@container": "@set", - }, - capabilityDelegation: { - "@id": "https://w3id.org/security#capabilityDelegationMethod", - "@type": "@id", - "@container": "@set", - }, - keyAgreement: { - "@id": "https://w3id.org/security#keyAgreementMethod", - "@type": "@id", - "@container": "@set", - }, - }, - }, - cryptosuite: { - "@id": "https://w3id.org/security#cryptosuite", - "@type": "https://w3id.org/security#cryptosuiteString", - }, - proofValue: { - "@id": "https://w3id.org/security#proofValue", - "@type": "https://w3id.org/security#multibase", - }, - verificationMethod: { - "@id": "https://w3id.org/security#verificationMethod", - "@type": "@id", - }, - }, - }, - }, - }, -}; diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index c8918d98..925470db 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -4,32 +4,76 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "definitions": { - "type": { - "oneOf": [ - { - "type": "array", - "items": { "type": "string" } - }, - { "type": "string" } - ], - "description": "Specific verifiable credential type as explained by https://www.w3.org/TR/vc-data-model/#types" - } - }, - "properties": { "@context": { - "type": "array", - "items": { - "type": ["string", "object"], - "format": "uri" - }, - "description": "List of URI to determine the terminology used in the verifiable credential as explained by https://www.w3.org/TR/vc-data-model/#contexts" + "description": "Used to define the short-hand names that are used throughout a JSON-LD document.", + "type": ["object", "string", "array", "null"] }, "id": { + "description": "Used to uniquely identify things that are being described in the document with IRIs or blank node identifiers.", "type": "string", - "format": "uri", - "description": "URI to the subject of the credential as explained by https://www.w3.org/TR/vc-data-model/#credential-subject" + "format": "uri" + }, + "type": { + "description": "Used to set the data type of a node or typed value.", + "type": ["string", "null", "array"] + }, + "credentialSchema": { + "type": "object", + "properties": { + "id": { "$ref": "#/definitions/id" }, + "type": { "$ref": "#/definitions/type" } + }, + "description": "A data schema that provide verifiers with enough information to determine whether the provided data conforms to the provided schema(s). More information in https://www.w3.org/TR/vc-data-model-2.0/#data-schemas" + }, + "credentialSubject": { + "type": "object", + "properties": { + "id": { "$ref": "#/definitions/id" }, + "type": { "$ref": "#/definitions/type" } + }, + "description": "A verifiable credential contains claims about one or more subjects. More information in https://www.w3.org/TR/vc-data-model-2.0/#credential-subject" + }, + "termsOfUse": { + "type": "object", + "properties": { + "type": { "$ref": "#/definitions/type" } + } }, + "evidence": { + "type": "object", + "properties": { + "type": { "$ref": "#/definitions/type" } + } + }, + "proof": { + "type": "object", + "properties": { + "type": { "$ref": "#/definitions/type" }, + "proofPurpose": { "type": "string", "enum": ["assertionMethod"] }, + "targetHash": { "type": "string" }, + "proofs": { "type": "array", "items": { "type": "string" } }, + "merkleRoot": { "type": "string" }, + "salts": { "type": "string" }, + "privacy": { + "type": "object", + "properties": { "obfuscated": { "type": "array", "items": { "type": "string" } } } + }, + "key": { "type": "string" }, + "signature": { "type": "string" } + }, + "additionalProperties": false + } + }, + "properties": { + "@context": { "$ref": "#/definitions/@context" }, + "id": { "$ref": "#/definitions/id" }, "type": { "$ref": "#/definitions/type" }, + "credentialSchema": { + "anyOf": [ + { "$ref": "#/definitions/credentialSchema" }, + { "type": "array", "items": { "$ref": "#/definitions/credentialSchema" } } + ] + }, "validFrom": { "type": "string", "format": "date-time", @@ -45,35 +89,40 @@ "description": "Human readable name of this credential" }, "issuer": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URI when dereferenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential. More information in https://www.w3.org/TR/vc-data-model/#issuer" - }, - "type": { "$ref": "#/definitions/type" }, - "name": { - "type": "string", - "description": "Issuer's name" - }, - "identityProof": { + "oneOf": [ + { "type": "string" }, + { "type": "object", "properties": { - "identityProofType": { + "id": { "type": "string", - "enum": ["DNS-TXT", "DNS-DID", "DID"] + "format": "uri", + "description": "URI when dereferenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential. More information in https://www.w3.org/TR/vc-data-model/#issuer" }, - "identifier": { + "type": { "$ref": "#/definitions/type" }, + "name": { "type": "string", - "description": "Identifier to be shown to end user upon verifying the identity" + "description": "Issuer's name" + }, + "identityProof": { + "type": "object", + "properties": { + "identityProofType": { + "type": "string", + "enum": ["DNS-TXT", "DNS-DID", "DID"] + }, + "identifier": { + "type": "string", + "description": "Identifier to be shown to end user upon verifying the identity" + } + }, + "required": ["identityProofType", "identifier"], + "additionalProperties": false } }, - "required": ["identityProofType", "identifier"], - "additionalProperties": false + "required": ["id"] } - }, - "required": ["id", "name", "identityProof"] + ] }, "credentialStatus": { "type": "object", @@ -95,6 +144,11 @@ { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "URI to the status of the credential as explained by https://www.w3.org/TR/vc-data-model/#status" + }, "type": { "$ref": "#/definitions/type" }, @@ -107,9 +161,7 @@ "format": "uri", "examples": ["https://ocsp-responder.example.com"] } - }, - "required": ["credentialStatusType", "location"], - "additionalProperties": false + } } ] }, @@ -136,35 +188,26 @@ "required": ["name"] }, "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "An identifier related to the subject of the verifiable credential. More information in https://www.w3.org/TR/vc-data-model/#credential-subject" - }, - "type": { "$ref": "#/definitions/type" } - } + "anyOf": [ + { "$ref": "#/definitions/credentialSubject" }, + { "type": "array", "items": { "$ref": "#/definitions/credentialSubject" } } + ] + }, + "termsOfUse": { + "anyOf": [ + { "$ref": "#/definitions/termsOfUse" }, + { "type": "array", "items": { "$ref": "#/definitions/termsOfUse" } } + ] + }, + "evidence": { + "anyOf": [ + { "$ref": "#/definitions/evidence" }, + { "type": "array", "items": { "$ref": "#/definitions/evidence" } } + ] }, "proof": { - "type": "object", - "properties": { - "type": { "$ref": "#/definitions/type" }, - "proofPurpose": { "type": "string", "enum": ["assertionMethod"] }, - "targetHash": { "type": "string" }, - "proofs": { "type": "array", "items": { "type": "string" } }, - "merkleRoot": { "type": "string" }, - "salts": { "type": "string" }, - "privacy": { - "type": "object", - "properties": { "obfuscated": { "type": "array", "items": { "type": "string" } } } - }, - "key": { "type": "string" }, - "signature": { "type": "string" } - }, - "required": ["proofPurpose", "targetHash", "proofs", "merkleRoot", "salts", "privacy"], - "additionalProperties": false + "anyOf": [{ "$ref": "#/definitions/proof" }, { "type": "array", "items": { "$ref": "#/definitions/proof" } }] } }, - "required": ["@context", "issuer", "credentialStatus", "credentialSubject"] + "required": ["@context", "type", "issuer", "credentialSubject"] } diff --git a/src/4.0/validate/context.ts b/src/4.0/validate/context.ts new file mode 100644 index 00000000..6683e0f9 --- /dev/null +++ b/src/4.0/validate/context.ts @@ -0,0 +1,43 @@ +import jsonld, { expand, Options, JsonLdDocument } from "jsonld"; +import { ContextUrl } from "../../shared/@types/document"; + +const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; +const contexts: Map = new Map(); + +// FIXME: @types/json-ld seems to be outdated as documentLoaders is not typed +const nodeDocumentLoader = (jsonld as any).documentLoaders.node(); + +// Preload frequently used contexts +// https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader +let isFirstLoad = true; +const documentLoader: Options.DocLoader["documentLoader"] = async (url, callback) => { + if (isFirstLoad) { + isFirstLoad = false; + for (const url of preloadedContextList) { + contexts.set(url, (await nodeDocumentLoader(url)).document); + } + } + if (contexts.get(url)) { + return { + contextUrl: undefined, // this is for a context via a link header + document: contexts.get(url), // this is the actual document that was loaded + documentUrl: url, // this is the actual context URL after redirects + }; + } else { + return nodeDocumentLoader(url); + } +}; + +export const interpretContexts = async (input: JsonLdDocument) => { + const expanded = await expand(input, { documentLoader }).catch((e) => { + console.error(e); + throw new Error(`Unable to interpret @context: Ensure that @context is one or more valid URLs`); + }); + + const type = (expanded[0]["@type"] as string[]) || []; + const unknownTypes = type.filter((t) => t.startsWith("https://www.w3.org/ns/credentials/issuer-dependent#")); // Workaround as expansionMap no longer supported + + if (unknownTypes.length > 0) { + throw new Error(`Unable to interpret @context: type (${unknownTypes.map((t) => t.split("#")[1])}) is not mapped`); + } +}; diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts new file mode 100644 index 00000000..984d2cbd --- /dev/null +++ b/src/4.0/validate/dataModel.ts @@ -0,0 +1,70 @@ +import Joi from "joi"; + +import { ContextUrl as baseContext } from "../../shared/@types/document"; + +const baseType = "VerifiableCredential"; + +export const vcSchema = Joi.object({ + "@context": Joi.alternatives() + .try(Joi.object()) + .try(Joi.string()) + // If array: First item must be baseContext, while remaining items can be string or object + .try(Joi.array().ordered(Joi.string().valid(baseContext["v2_vc"]).required()).items(Joi.string(), Joi.object())) + .required(), + + // [Optional] If string: Must match uri pattern + id: Joi.string().uri(), + + type: Joi.alternatives() + .try(Joi.string()) + // If array: Must have VerifiableCredential, while remaining items can be any string + .try(Joi.array().items(Joi.string().valid(baseType).required(), Joi.string())) + .required(), + + credentialSchema: Joi.alternatives() + // If object: Must have id match uri pattern and type defined + .try(Joi.object({ id: Joi.string().uri().required(), type: Joi.string().required() })) + // If array: Every object must have id match uri pattern and type defined + .try(Joi.array().items({ id: Joi.string().uri().required(), type: Joi.string().required() })), + + issuer: Joi.alternatives() + // If string: Must match uri pattern + .try(Joi.string().uri()) + // If object: Must have id match uri pattern + .try(Joi.object({ id: Joi.string().uri() }).unknown()) + .required(), + + validFrom: Joi.string().isoDate(), + + validUntil: Joi.string().isoDate(), + + credentialSubject: Joi.alternatives() + // If object: Cannot be empty (i.e. minimum 1 key) + .try(Joi.object().min(1)) + // If array: Every object cannot be empty (i.e. minimum 1 key) + .try(Joi.array().items(Joi.object().min(1))) + .required(), + + credentialStatus: + // Must have type defined + // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) + Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() }), + + termsOfUse: Joi.alternatives() + // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + .try(Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() })) + // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + .try(Joi.array().items({ id: Joi.string().uri().optional(), type: Joi.string().required() })), + + evidence: Joi.alternatives() + // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + .try(Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() })) + // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + .try(Joi.array().items({ id: Joi.string().uri().optional(), type: Joi.string().required() })), + + proof: Joi.alternatives() + // If object: Must have type defined + .try(Joi.object({ type: Joi.string().required() })) + // If array: Every object must have type defined + .try(Joi.array().items({ type: Joi.string().required() })), +}).unknown(); // Allow additional properties diff --git a/src/4.0/validate/index.ts b/src/4.0/validate/index.ts index 9431d1c5..a29ffd44 100644 --- a/src/4.0/validate/index.ts +++ b/src/4.0/validate/index.ts @@ -1 +1,2 @@ -export * from "./validate"; +export * from "./context"; +export * from "./dataModel"; diff --git a/src/4.0/validate/validate.ts b/src/4.0/validate/validate.ts deleted file mode 100644 index 389d0184..00000000 --- a/src/4.0/validate/validate.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { OpenAttestationDocument } from "../../__generated__/schema.4.0"; -import { WrappedDocument } from "../../4.0/types"; -import { ContextUrl } from "../../shared/@types/document"; -import { documentLoaders, expand } from "@govtechsg/jsonld"; -import fetch from "cross-fetch"; - -const getId = (objectOrString: string | { id: string }): string => { - if (typeof objectOrString === "string") { - return objectOrString; - } - return objectOrString.id; -}; -/* Based on https://tools.ietf.org/html/rfc3339#section-5.6 */ -const dateFullYear = /[0-9]{4}/; -const dateMonth = /(0[1-9]|1[0-2])/; -const dateMDay = /([12]\d|0[1-9]|3[01])/; -const timeHour = /([01][0-9]|2[0-3])/; -const timeMinute = /[0-5][0-9]/; -const timeSecond = /([0-5][0-9]|60)/; -const timeSecFrac = /(\.[0-9]+)?/; -const timeNumOffset = new RegExp("[-+]".concat(timeHour.source, ":").concat(timeMinute.source)); -const timeOffset = new RegExp("([zZ]|".concat(timeNumOffset.source, ")")); -const partialTime = new RegExp( - "".concat(timeHour.source, ":").concat(timeMinute.source, ":").concat(timeSecond.source).concat(timeSecFrac.source) -); -const fullDate = new RegExp("".concat(dateFullYear.source, "-").concat(dateMonth.source, "-").concat(dateMDay.source)); -const fullTime = new RegExp("".concat(partialTime.source).concat(timeOffset.source)); -const rfc3339 = new RegExp("".concat(fullDate.source, "[ tT]").concat(fullTime.source)); - -const isValidRFC3339 = (str: any) => { - return rfc3339.test(str); -}; - -/* Based on https://tools.ietf.org/html/rfc3986 and https://github.com/ajv-validator/ajv/search?q=uri&unscoped_q=uri */ -const uri = - /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; -const rfc3986 = new RegExp(uri); - -const isValidRFC3986 = (str: any) => { - return rfc3986.test(str); -}; - -const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; -const contexts: Map> = new Map(); -const nodeDocumentLoader = documentLoaders.xhr ? documentLoaders.xhr() : documentLoaders.node(); -let preload = true; - -const documentLoader = async (url: string) => { - if (preload) { - preload = false; - for (const url of preloadedContextList) { - contexts.set( - url, - fetch(url, { headers: { accept: "application/json" } }).then((res: any) => res.json()) - ); - } - } - if (contexts.get(url)) { - const promise = contexts.get(url); - return { - contextUrl: undefined, // this is for a context via a link header - document: await promise, // this is the actual document that was loaded - documentUrl: url, // this is the actual context URL after redirects - }; - } else { - const promise = nodeDocumentLoader(url); - contexts.set( - url, - promise.then(({ document }) => document) - ); - return promise; - } -}; - -export async function validateW3C(credential: WrappedDocument): Promise { - // ensure first context is 'https://www.w3.org/2018/credentials/v1' as it's mandatory, see https://www.w3.org/TR/vc-data-model/#contexts - if ( - !Array.isArray(credential["@context"]) || - (Array.isArray(credential["@context"]) && credential["@context"][0] !== "https://www.w3.org/2018/credentials/v1") - ) { - throw new Error("https://www.w3.org/2018/credentials/v1 needs to be first in the list of contexts"); - } - - // ensure issuer is a valid URI according to RFC3986 - const issuerId = getId(credential.issuer); - if (!isValidRFC3986(issuerId)) { - throw new Error("Property 'issuer' id must be a valid RFC 3986 URI"); - } - - // ensure validFrom is a valid RFC3339 date, see https://www.w3.org/TR/vc-data-model/#issuance-date - if (credential.validFrom && !isValidRFC3339(credential.validFrom)) { - throw new Error("Property 'validFrom' must be a valid RFC 3339 date"); - } - // ensure validUntil is a valid RFC3339 date, see https://www.w3.org/TR/vc-data-model/#expiration - if (credential.validUntil && !isValidRFC3339(credential.validUntil)) { - throw new Error("Property 'validUntil' must be a valid RFC 3339 date"); - } - - // https://www.w3.org/TR/vc-data-model/#types - if (!credential.type || !Array.isArray(credential.type)) { - throw new Error("Property 'type' must exist and be an array"); - } - if (!credential.type.includes("VerifiableCredential")) { - throw new Error("Property 'type' must have VerifiableCredential as one of the items"); - } - - await expand(credential, { - expansionMap: (info) => { - if (info.unmappedProperty) { - throw new Error( - `"The property ${info.activeProperty ? `${info.activeProperty}.` : ""}${ - info.unmappedProperty - } in the input was not defined in the context"` - ); - } - }, - documentLoader, - }); -} diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 61598b79..9509fc81 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -3,11 +3,11 @@ import { MerkleTree } from "../shared/merkle"; import { ContextUrl, SchemaId } from "../shared/@types/document"; import { WrappedDocument } from "./types"; import { digestCredential } from "../4.0/digest"; -import { validateSchema as validate } from "../shared/validate"; +import { validateSchema } from "../shared/validate"; import { WrapDocumentOptionV4 } from "../shared/@types/wrap"; -import { OpenAttestationDocument } from "../__generated__/schema.4.0"; +import { OpenAttestationDocument, ProofPurpose } from "../__generated__/schema.4.0"; import { encodeSalt, salt } from "./salt"; -import { validateW3C } from "./validate"; +import { interpretContexts, vcSchema } from "./validate"; import { getSchema } from "../shared/ajv"; export const wrapDocument = async ( @@ -16,33 +16,40 @@ export const wrapDocument = async ( ): Promise> => { const document = { ...credential }; - // 1. Ensure that required @contexts are present and in the correct order - // @context: [Base, OA, ...] - const contexts = new Set(["https://www.w3.org/2018/credentials/v1", ContextUrl.v4_alpha]); + /* 1. Data model validation */ + const { error } = vcSchema.validate(document); + if (error) { + throw new Error( + `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(error.details)}` + ); + } + + /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ + await interpretContexts(document); + /* 3. Context validation */ + // Ensure that required contexts are present and in the correct order + // type: [Base, OA, ...] + const contexts = new Set([ContextUrl["v2_vc"], ContextUrl["v4_alpha"]]); if (typeof document["@context"] === "string") { contexts.add(document["@context"]); } else if (isStringArray(document["@context"])) { document["@context"].forEach((context) => contexts.add(context)); } + document["@context"] = Array.from(contexts); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this - // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this: - document["@context"] = Array.from(contexts); - - // 2. Ensure that required types are present and in the correct order + /* 4. Type validation */ + // Ensure that required types are present and in the correct order // type: ["VerifiableCredential", "OpenAttestationCredential", ...] const types = new Set(["VerifiableCredential", "OpenAttestationCredential"]); - if (typeof document["type"] === "string") { types.add(document["type"]); } else if (isStringArray(document["type"])) { document["type"].forEach((type) => types.add(type)); } + document["type"] = Array.from(types); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this - // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this: - document["type"] = Array.from(types); - - // 3. OA wrapping + /* 5. OA wrapping */ const salts = salt(document); const digest = digestCredential(document, salts, []); @@ -56,7 +63,7 @@ export const wrapDocument = async ( ...document, proof: { type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: "assertionMethod", + proofPurpose: ProofPurpose.AssertionMethod, targetHash: digest, proofs: merkleProof, merkleRoot, @@ -67,11 +74,11 @@ export const wrapDocument = async ( }, }; - const errors = validate(verifiableCredential, getSchema(SchemaId.v4)); + const errors = validateSchema(verifiableCredential, getSchema(SchemaId.v4)); if (errors.length > 0) { throw new SchemaValidationError("Invalid document", errors, verifiableCredential); } - await validateW3C(verifiableCredential); + return verifiableCredential; }; diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index 310c528d..bc857504 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -218,20 +218,20 @@ const diagnoseV4 = ({ // 2. Ensure that required @contexts are present // @context: [Base, OA, ...] - const contexts = ["https://www.w3.org/2018/credentials/v1", ContextUrl.v4_alpha]; + const contexts = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; if (isStringArray(document["@context"])) { for (let i = 0; i < contexts.length; i++) { if (document["@context"][i] !== contexts[i]) { return handleError( debug, - `The document @context contains an unexpected value or in the wrong order. Expected ${contexts}, received ${document["@context"]}` + `The document @context contains an unexpected value or in the wrong order. Expected "${contexts}" but received "${document["@context"]}"` ); } } } else { return handleError( debug, - `The document @context should be an array of string values. Expected ${contexts}, received ${document["@context"]}` + `The document @context should be an array of string values. Expected "${contexts}" but received "${document["@context"]}"` ); } @@ -243,14 +243,14 @@ const diagnoseV4 = ({ if (document["type"][i] !== types[i]) { return handleError( debug, - `The document type contains an unexpected value or in the wrong order. Expected ${types}, received ${document["type"]}` + `The document type contains an unexpected value or in the wrong order. Expected "${types}" but received "${document["type"]}"` ); } } } else { return handleError( debug, - `The document type should be an array of string values. Expected ${types}, received ${document["type"]}` + `The document type should be an array of string values. Expected "${types}" but received "${document["type"]}"` ); } diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index e5acb85d..acf4d771 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -185,8 +185,9 @@ export const isDocumentRevokable = (document: any): boolean => { return isDocumentStoreRevokableV3 || isDidRevokableV3; } else if (isWrappedV4Document(document)) { + if (typeof document.issuer === "string" || !document.credentialStatus) return false; const isDidRevokableV4 = - document.issuer.identityProof.identityProofType === v4.IdentityProofType.DNSDid + document.issuer.identityProof?.identityProofType === v4.IdentityProofType.DNSDid ? document.credentialStatus.credentialStatusType === v4.CredentialStatusType.OcspResponder || document.credentialStatus.credentialStatusType === v4.CredentialStatusType.RevocationStore : false; diff --git a/src/shared/validate/validate.ts b/src/shared/validate/validate.ts index 3128bdd4..7a7fb1d9 100644 --- a/src/shared/validate/validate.ts +++ b/src/shared/validate/validate.ts @@ -15,7 +15,8 @@ export const validateSchema = (document: any, validator: ValidateFunction, kind? // FIXME: Unable to use isWrappedV4Document() type guard here because it also calls validateSchema (endless recursive call) // Need a better way to determine whether a document needs to be unwrapped first const valid = validator( - (Array.isArray(document["@context"]) && document["@context"].includes(ContextUrl.v4_alpha)) || + (Array.isArray(document["@context"]) && document["@context"].includes(ContextUrl.v2_vc)) || + (Array.isArray(document["@context"]) && document["@context"].includes(ContextUrl.v4_alpha)) || document.version === SchemaId.v3 || kind === "raw" ? document From a80a7da048fc9b3d75f004e85d17e0bdaf79e207 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 20 Mar 2024 16:04:33 +0800 Subject: [PATCH 004/107] fix: set additionalProperties to false due to AJV false positive --- src/4.0/schema/schema.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index 925470db..01296d77 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -161,7 +161,9 @@ "format": "uri", "examples": ["https://ocsp-responder.example.com"] } - } + }, + "required": ["credentialStatusType", "location"], + "additionalProperties": false } ] }, From f44bb8868fe956f8933785e9023f8dfba19fcb6c Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 20 Mar 2024 16:05:43 +0800 Subject: [PATCH 005/107] fix: allow additional properties in credentialStatus --- src/4.0/validate/dataModel.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts index 984d2cbd..219cb556 100644 --- a/src/4.0/validate/dataModel.ts +++ b/src/4.0/validate/dataModel.ts @@ -48,7 +48,9 @@ export const vcSchema = Joi.object({ credentialStatus: // Must have type defined // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) - Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() }), + Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() }) + // Allow additional properties + .unknown(), termsOfUse: Joi.alternatives() // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) @@ -67,4 +69,6 @@ export const vcSchema = Joi.object({ .try(Joi.object({ type: Joi.string().required() })) // If array: Every object must have type defined .try(Joi.array().items({ type: Joi.string().required() })), -}).unknown(); // Allow additional properties +}) + // Allow additional properties + .unknown(); From db0a940d3a4d1c55fc81b10be8e03e64e5231b50 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 20 Mar 2024 16:06:26 +0800 Subject: [PATCH 006/107] fix: include error details instead of console log --- src/4.0/validate/context.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/4.0/validate/context.ts b/src/4.0/validate/context.ts index 6683e0f9..7880afa3 100644 --- a/src/4.0/validate/context.ts +++ b/src/4.0/validate/context.ts @@ -30,8 +30,7 @@ const documentLoader: Options.DocLoader["documentLoader"] = async (url, callback export const interpretContexts = async (input: JsonLdDocument) => { const expanded = await expand(input, { documentLoader }).catch((e) => { - console.error(e); - throw new Error(`Unable to interpret @context: Ensure that @context is one or more valid URLs`); + throw new Error(`Unable to interpret @context: ${JSON.stringify(e)}`); }); const type = (expanded[0]["@type"] as string[]) || []; From 966e7f69df91dd87c480e7c4c885322ad1b46789 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 20 Mar 2024 16:06:34 +0800 Subject: [PATCH 007/107] test: update v4 alpha test fixtures --- test/fixtures/v4/did-raw.json | 2 +- test/fixtures/v4/did-signed-wrapped.json | 12 +++++------- test/fixtures/v4/did-wrapped.json | 12 +++++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/fixtures/v4/did-raw.json b/test/fixtures/v4/did-raw.json index 788b56a2..95117d2c 100644 --- a/test/fixtures/v4/did-raw.json +++ b/test/fixtures/v4/did-raw.json @@ -1,6 +1,6 @@ { "@context": [ - "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" ], "type": ["VerifiableCredential", "OpenAttestationCredential"], diff --git a/test/fixtures/v4/did-signed-wrapped.json b/test/fixtures/v4/did-signed-wrapped.json index f2efde6d..ae24a9e9 100644 --- a/test/fixtures/v4/did-signed-wrapped.json +++ b/test/fixtures/v4/did-signed-wrapped.json @@ -1,6 +1,6 @@ { "@context": [ - "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" ], "type": ["VerifiableCredential", "OpenAttestationCredential"], @@ -39,12 +39,10 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "adb16863b9b92f1f46d67f518f853092404dc1322ffb61b45a831ee113f4ea99", + "targetHash": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", "proofs": [], - "merkleRoot": "adb16863b9b92f1f46d67f518f853092404dc1322ffb61b45a831ee113f4ea99", - "salts": "W3sidmFsdWUiOiJkYTlkMTE5MTMxOWRjOTM4ZTQ2ZjZhYmMzMjU1NTM5NGYwYzEzNDIyMGUwYjgyMDY0NzBhYjlmNmYxN2QyZTJlIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjhhZDM5Njg2NGIwNGI5ZjYxY2I3ODNjY2M1YWEyNTYxODgxYmRiODZmZmI5ZmNjZjViYTQ1YzUyN2MwZGFmMGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMjYwNDllNWExODQ4MDVlYTUxNjQ4OTM3OGI0NDZhNTdiMjdlZWU0YmFhN2VmYjBkNWM3OTcwMGMzZDI5OGM5MyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3N2M2ZGUzMzYzZmE1ZjA2ZDc5NjQyMjAxNTk3ODkzYzdmNDg3NWZlYzFiZTYyOWIxZWRkNTdhZWFlOTE3YzkiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI0NmMyOTY5MTVkZWVhNDMwMDNmY2ZmNjc3NzUxNTc3Zjg2MTdlODliYjBjNDhiNzIwMTdlMzRkOWNhNTM0ZGNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiIwZWQ1Yzg0OTBlYjg2YWM5YmEyNGJlYzc3OTE2MThhZWZjZGE2M2M3OGNmZDJlMTdjMWNlNDRmNzVmZjU3NjljIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzEyNGUxNzc4Y2EzYTc2ZmI0MjUxNGExMmIzNDYzYmY4ZmY4NjNiNDdiZTExNDYyY2RiMGRiNzRmMzk1ZjViYiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTJmNTJhNTdmMWM5MDJhYzQ5MmJiNzc0YTQzOTIxYjk2NWJlM2VmZjRjNTBjMDhiNDhhNjJkOGZkNjNhMWQ1NCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NGZhZWFhNmIxYmZlZmI0NGM4NzJlYzI2Nzc4OWNkNzUwM2U3YTQ1YjZjMDdmMmE0MmJkMDE5OWU1NzI0ZjM1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjJjNzg1Y2NhOGMyODdhZjhmOGM1NWM4MmJkODI3ZGY4M2YyZDIxY2M4MTVhZWJlYTg5MmVjOTZmNGRmMzVkNzAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiOWM0OWNiOGQ1Njc2NjljOTEzMDEwYzE3MmI2YmM5N2Q4MTVlOWZlOGY3Mjg2ZWE0NzA3NWVhNDQxYzc4NmYwMyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjJjZDcxMmFlZTdjZDlkZmI1NjBmYzBjNWVjZmZhY2FkYzkzYmYzZmI1MGRhYTgyYTk2ZTQzYjA5OGU2OTg4YTEiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6ImM4MmUwNmU1NWI1YTY2YmUwNTk5NjVjZDI5YTUzZWE1ODg0YjM3NjU0NWE4ZmMwMzNiMTEwNWE0MzMyMWI5ZjAiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiJiNzBkMTU5MmU4ZTk3NDlkNGU5ZTEzNjg5NTgxNjhlY2MxZWQ2ODE5NWQzMmE4YmI1MDdlOTc3NGU3ZjkxMGFlIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjIxMGI0NGNjYTdmYzZkOTY4ZDk2ZmVjZWY3MGQzNTU1ZDY5MjViYWEwNjA4ZTczMDM3YjBiOGI5Njc4MTQ0ZGEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiODQyZDBhZTQ1ZTI5N2U1YzgyMmJhZWVjMGMxNDg5MGYyNzkwZjNhNzY1M2JkMTAwNzI5YWJiNTM5NzAxMGY3NCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiI4ZDIyYmIwMzJlMjg1N2RlYTk3NDU4ZWZmMjQ5ODgxZGZiZmE2MmUxNDU0MzBkZTdmNWIzZThkNWM5NGVjNmY4IiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNGJiNWMyMzliNzBmZmI2NDkwYmY4MmQwZTBjODQ3ZWE0YWIyYTQyYWU1MmZjMDljZjA0ZjFiMTg1YjQ5ODFmMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkOGJmNmI3NzRlNjY0MGU2MWIwMmY4MmIzNTM5Y2RhMjdjODkyNmVlNjI0ZDE1ODZlOTZlMzhjZjNkMWQ0MjY1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiYTViNjg5NThkOTNiZmM3Y2M0ZDYzM2IxODViZTRlZGNmMGFmZTkwOGRhYjIwOTI3N2RhOGQ3Nzg2ZTc0MjQ3ZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNlMjUyNTkwMmI1MDY3ZjZiMjNhZGFjYmNmMzdiOTBjNTY1N2ViZWY1ZTQwZmQ2ZTNlMGM1N2QyYjBhODFlMmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiODViZDZjYzhkODI4OTRlNWI0OGQ2NmI5ZTM2NGU4MTRhMzIxMWM4ODI0NGVhNmFlZDhkZTYzNWQ1YmFhODljMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJiZWRlZDA0Yjk1MDEzZjBkNjgzZjZmNGI1YmI2ZDBjNjRjMDM0MjUxYjYwOWQwOTNkZGM0ZDE0Njg2MWJkMjVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYzQ2ZTBhMzkyYTExMmM1NjRkNDdmODQ5NmFhMGRiYmZmZTg1NGEwNDQxODAxZmYxNzYyNzBiYmMyYmUxN2MwOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjMGYzMWI2MGYyMjVkZTg1M2YzNzAyYzk4Y2E1OTk0Y2EzNTcyNmUwZTAyNGZkNzRkMWUwZTM1NzZiYjRhNmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjY0YTUxYjRkMmE3OWMyZTI5NTdkOTg0MDkwN2Q3MGEzMzY4NDRiN2QyYzNlNmE5NDk3NzY1NGUwM2RkMzk1ZWMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": [] }, - "key": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90#controller", - "signature": "0xf5743ee83429b0ff71ca40d61f59b9c26f1fc220c2e9a4620bc73128acc0bb2b74764c561058327b19c6992ae6c2a543dd40c7c7e359df48a209477e3cea5bbe1c" + "merkleRoot": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", + "salts": "W3sidmFsdWUiOiJmNWUxOGNhN2M5YjMxM2E0Y2M1NWU4OWExNTllYzc5MGRlY2ZkYzE3ZjA5ZGVkYzBkYWY1YTM1MzY5MjkzMjM5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjFkZDNiYTUzMDE4ZWQzOTYwNjdmZDkwYWMwMTgwYTVlODdjY2QxZTc5NWQ3OTI1ZjM0YzkyMTRmNGNlZmE3M2EiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTg1NWJiMWY2NzM5ODU5OTZiZmUzM2YyMDJiYzVjNGRlMTdkNzBjMWIxODk5NzEzNjY1MTI0ZTIyMzI3ODY0ZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjkwMzc0N2YwOTAxODY0YzVhNjgxMGYzNDAwZTRmYjZjYzFiY2M5NWU1MTJlMDA1MWY1ODE0NzkyYmRlZGViYWUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiJlMWE0M2ZmNmJhMWI1MmE1NzY5M2NmMzMyYmE0ZmUxOWM5NjRmYWQxZmY1ZDU2MmU2MWUwZjg2MDNkMTg2ODUyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2ZDMxNjk5MTI2NWFkMjAzZjYzYTgwZTM3MWI3NTViMzMyMzBjYTc4ZTUzZWVkZDA1ZDI5NGY3ODQ3NDExMWE1IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiY2JmYzI2ZWQ0ZWM1Nzc3ZTlhYmJmMzU0NzM2ZWY1MTJlODc5YTdjYTQwZTAzMzZjYzU1OWYyMGI5NDI2MWI1NiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGM4ZGQ4YTE0MjAyZWQ4NGY4ODMzZmUwZTY0MmQzNGI2MTE3NzI2YWE4Njk5MjQwZGZjMjc3MjBjZGI5YjYwMyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNGE3YWY1MjgyMzM2YjI2YTg2Yjk3ZDliNDU5MTU2ODdkOGVmNmNkZTVmNmFiNGE0NDVlYjIzYzE5YjI5YzVjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImIyY2I3MTE0YTBhOWU5NjYxYzQzZTQ3NDNkM2U3MTM1NDlkNGIyN2NjY2M5OTdkMGU0OGJiMjIzZDA2ZDNhNTgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2YxZTk4MGI5Yjg0Y2Y0ZjMyZjAwN2UwMWM1Mjg0YjFhMTY3ODhhMmIwOWQyZmVmODdkZDA0YjQzYmRkYTAwYiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjU4ZDQzNzFiMjlhYTJhM2UxMjQyYjJjMmE3OGU3YjI3YTI3MmQ1MDVkZTdkNGM0MmNlMzMxZWFlMTkyMjA4OGYiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjBmNTc4ZmZhNjkxOGIwYWZkOTkwYWMwOTY3MjY3NjllZmM5NTU1YmJkMWE2YzkyNmZiOGI1MWMyYTY4ZmYwNTgiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiI1NWE1YjBkZGRmMDBhMGQ1NDAwNWI1MTA4YzNmNWRiMGYxZjQ4MDVjYmMxY2IxODk5YTlkYWUyNGMwMGNjM2Q4IiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjliMjliODk4NjU1MzIwZjUyYzJjMWYzMjNjYmMzMjdiNjUxYTEyMmY0ZDM5NGFiZTY2ZjJhZjIzODVjYjM0NzEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiNzk3NmFjZWYxNmE5ZTk1NTgzZmYwYTYyNmUyYWI1MWFjZTRlOTU0MGNiZjZlZGEzOWUwMWM4NjVkNTBkMTlhOCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiIzODQ1MzExZmM4MTE4NzdiODA2ZmVmYjNiMWZhNGYyY2U4NDFlNjM2YzE2ZTk5ODQxYWQ4ZDcwZDM2ZTU0ZTFhIiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNmI2NGM4ZjZmYWE0NTRhZjgyOWFlM2I2NzdiZTUwNDhmNDJhNzgzMDU3MDI0NmE1NDcwODc4Y2M3NjY5NGIzMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkNzAzZTUyMjNmMDE5YmIwNzgyN2IxNjgxZTExMWE4Y2M4YzljMjE5YmUxMmI2MDYzNGU0YjYxZGE1MWNkYTk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMmMyMzYxZWI1OThlM2M1YWUzNWE3YTRjZTA1ZDU3MjYwOGY5NDUwMDExN2ZiN2NmNGYxYWFiMmY1NzZhYzk0MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjA5ZjZhZjVkN2FhMDViY2EyZjY1MWMxZjYwMmExYjI2ZDMxMmM4ZDk1OTE1MTMyOWY1OGExYjIyZDAyOGZiNDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYTc1ZTg3NmEzOWExMzc2NmYzM2NjMmVkYTM3Mjk5ODYxM2QyMGMzOTgxMGE2M2I0N2UzODhkNmYyYzBjMWUwZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIwZjA2NDc0YjUzOTI2NzdmMzJjMjI4YWFiNGQ2ZjliY2FjZGEwMThhODc0M2NkY2NhMzMxMTgzY2E0MzhkZTRmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOTVjZTE0MGMyNWUzYjMzZTJiN2YyMDE0Yjc3NmM0NDI2YjZjOTUxYjVkOTVmNTZkYTE4NWQ5NzNmM2EwODBlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2ViMTJiNTgyNGM3MGJjNTg3OWYzNzAwYjhkZTVlMzRiMDcwMDUxZjc1NWRlZTdlMzI3ZDFhMGIxNDllYzU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImM1M2ZlNzdlNDM2MDgwZjdmNTgxZmQ5YzgyZTVhNmUyOTBiZWYxNzdkYmRhODA3OTM0NzY3MWQ0NGZiZmI5MzIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "privacy": { "obfuscated": [] } } } diff --git a/test/fixtures/v4/did-wrapped.json b/test/fixtures/v4/did-wrapped.json index 2921df32..3a0d3350 100644 --- a/test/fixtures/v4/did-wrapped.json +++ b/test/fixtures/v4/did-wrapped.json @@ -1,6 +1,6 @@ { "@context": [ - "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" ], "type": ["VerifiableCredential", "OpenAttestationCredential"], @@ -39,10 +39,12 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "adb16863b9b92f1f46d67f518f853092404dc1322ffb61b45a831ee113f4ea99", + "targetHash": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", "proofs": [], - "merkleRoot": "adb16863b9b92f1f46d67f518f853092404dc1322ffb61b45a831ee113f4ea99", - "salts": "W3sidmFsdWUiOiJkYTlkMTE5MTMxOWRjOTM4ZTQ2ZjZhYmMzMjU1NTM5NGYwYzEzNDIyMGUwYjgyMDY0NzBhYjlmNmYxN2QyZTJlIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjhhZDM5Njg2NGIwNGI5ZjYxY2I3ODNjY2M1YWEyNTYxODgxYmRiODZmZmI5ZmNjZjViYTQ1YzUyN2MwZGFmMGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMjYwNDllNWExODQ4MDVlYTUxNjQ4OTM3OGI0NDZhNTdiMjdlZWU0YmFhN2VmYjBkNWM3OTcwMGMzZDI5OGM5MyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3N2M2ZGUzMzYzZmE1ZjA2ZDc5NjQyMjAxNTk3ODkzYzdmNDg3NWZlYzFiZTYyOWIxZWRkNTdhZWFlOTE3YzkiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI0NmMyOTY5MTVkZWVhNDMwMDNmY2ZmNjc3NzUxNTc3Zjg2MTdlODliYjBjNDhiNzIwMTdlMzRkOWNhNTM0ZGNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiIwZWQ1Yzg0OTBlYjg2YWM5YmEyNGJlYzc3OTE2MThhZWZjZGE2M2M3OGNmZDJlMTdjMWNlNDRmNzVmZjU3NjljIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzEyNGUxNzc4Y2EzYTc2ZmI0MjUxNGExMmIzNDYzYmY4ZmY4NjNiNDdiZTExNDYyY2RiMGRiNzRmMzk1ZjViYiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTJmNTJhNTdmMWM5MDJhYzQ5MmJiNzc0YTQzOTIxYjk2NWJlM2VmZjRjNTBjMDhiNDhhNjJkOGZkNjNhMWQ1NCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NGZhZWFhNmIxYmZlZmI0NGM4NzJlYzI2Nzc4OWNkNzUwM2U3YTQ1YjZjMDdmMmE0MmJkMDE5OWU1NzI0ZjM1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjJjNzg1Y2NhOGMyODdhZjhmOGM1NWM4MmJkODI3ZGY4M2YyZDIxY2M4MTVhZWJlYTg5MmVjOTZmNGRmMzVkNzAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiOWM0OWNiOGQ1Njc2NjljOTEzMDEwYzE3MmI2YmM5N2Q4MTVlOWZlOGY3Mjg2ZWE0NzA3NWVhNDQxYzc4NmYwMyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjJjZDcxMmFlZTdjZDlkZmI1NjBmYzBjNWVjZmZhY2FkYzkzYmYzZmI1MGRhYTgyYTk2ZTQzYjA5OGU2OTg4YTEiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6ImM4MmUwNmU1NWI1YTY2YmUwNTk5NjVjZDI5YTUzZWE1ODg0YjM3NjU0NWE4ZmMwMzNiMTEwNWE0MzMyMWI5ZjAiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiJiNzBkMTU5MmU4ZTk3NDlkNGU5ZTEzNjg5NTgxNjhlY2MxZWQ2ODE5NWQzMmE4YmI1MDdlOTc3NGU3ZjkxMGFlIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjIxMGI0NGNjYTdmYzZkOTY4ZDk2ZmVjZWY3MGQzNTU1ZDY5MjViYWEwNjA4ZTczMDM3YjBiOGI5Njc4MTQ0ZGEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiODQyZDBhZTQ1ZTI5N2U1YzgyMmJhZWVjMGMxNDg5MGYyNzkwZjNhNzY1M2JkMTAwNzI5YWJiNTM5NzAxMGY3NCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiI4ZDIyYmIwMzJlMjg1N2RlYTk3NDU4ZWZmMjQ5ODgxZGZiZmE2MmUxNDU0MzBkZTdmNWIzZThkNWM5NGVjNmY4IiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNGJiNWMyMzliNzBmZmI2NDkwYmY4MmQwZTBjODQ3ZWE0YWIyYTQyYWU1MmZjMDljZjA0ZjFiMTg1YjQ5ODFmMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkOGJmNmI3NzRlNjY0MGU2MWIwMmY4MmIzNTM5Y2RhMjdjODkyNmVlNjI0ZDE1ODZlOTZlMzhjZjNkMWQ0MjY1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiYTViNjg5NThkOTNiZmM3Y2M0ZDYzM2IxODViZTRlZGNmMGFmZTkwOGRhYjIwOTI3N2RhOGQ3Nzg2ZTc0MjQ3ZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNlMjUyNTkwMmI1MDY3ZjZiMjNhZGFjYmNmMzdiOTBjNTY1N2ViZWY1ZTQwZmQ2ZTNlMGM1N2QyYjBhODFlMmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiODViZDZjYzhkODI4OTRlNWI0OGQ2NmI5ZTM2NGU4MTRhMzIxMWM4ODI0NGVhNmFlZDhkZTYzNWQ1YmFhODljMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJiZWRlZDA0Yjk1MDEzZjBkNjgzZjZmNGI1YmI2ZDBjNjRjMDM0MjUxYjYwOWQwOTNkZGM0ZDE0Njg2MWJkMjVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYzQ2ZTBhMzkyYTExMmM1NjRkNDdmODQ5NmFhMGRiYmZmZTg1NGEwNDQxODAxZmYxNzYyNzBiYmMyYmUxN2MwOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjMGYzMWI2MGYyMjVkZTg1M2YzNzAyYzk4Y2E1OTk0Y2EzNTcyNmUwZTAyNGZkNzRkMWUwZTM1NzZiYjRhNmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjY0YTUxYjRkMmE3OWMyZTI5NTdkOTg0MDkwN2Q3MGEzMzY4NDRiN2QyYzNlNmE5NDk3NzY1NGUwM2RkMzk1ZWMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": [] } + "merkleRoot": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", + "salts": "W3sidmFsdWUiOiJmNWUxOGNhN2M5YjMxM2E0Y2M1NWU4OWExNTllYzc5MGRlY2ZkYzE3ZjA5ZGVkYzBkYWY1YTM1MzY5MjkzMjM5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjFkZDNiYTUzMDE4ZWQzOTYwNjdmZDkwYWMwMTgwYTVlODdjY2QxZTc5NWQ3OTI1ZjM0YzkyMTRmNGNlZmE3M2EiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTg1NWJiMWY2NzM5ODU5OTZiZmUzM2YyMDJiYzVjNGRlMTdkNzBjMWIxODk5NzEzNjY1MTI0ZTIyMzI3ODY0ZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjkwMzc0N2YwOTAxODY0YzVhNjgxMGYzNDAwZTRmYjZjYzFiY2M5NWU1MTJlMDA1MWY1ODE0NzkyYmRlZGViYWUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiJlMWE0M2ZmNmJhMWI1MmE1NzY5M2NmMzMyYmE0ZmUxOWM5NjRmYWQxZmY1ZDU2MmU2MWUwZjg2MDNkMTg2ODUyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2ZDMxNjk5MTI2NWFkMjAzZjYzYTgwZTM3MWI3NTViMzMyMzBjYTc4ZTUzZWVkZDA1ZDI5NGY3ODQ3NDExMWE1IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiY2JmYzI2ZWQ0ZWM1Nzc3ZTlhYmJmMzU0NzM2ZWY1MTJlODc5YTdjYTQwZTAzMzZjYzU1OWYyMGI5NDI2MWI1NiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGM4ZGQ4YTE0MjAyZWQ4NGY4ODMzZmUwZTY0MmQzNGI2MTE3NzI2YWE4Njk5MjQwZGZjMjc3MjBjZGI5YjYwMyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNGE3YWY1MjgyMzM2YjI2YTg2Yjk3ZDliNDU5MTU2ODdkOGVmNmNkZTVmNmFiNGE0NDVlYjIzYzE5YjI5YzVjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImIyY2I3MTE0YTBhOWU5NjYxYzQzZTQ3NDNkM2U3MTM1NDlkNGIyN2NjY2M5OTdkMGU0OGJiMjIzZDA2ZDNhNTgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2YxZTk4MGI5Yjg0Y2Y0ZjMyZjAwN2UwMWM1Mjg0YjFhMTY3ODhhMmIwOWQyZmVmODdkZDA0YjQzYmRkYTAwYiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjU4ZDQzNzFiMjlhYTJhM2UxMjQyYjJjMmE3OGU3YjI3YTI3MmQ1MDVkZTdkNGM0MmNlMzMxZWFlMTkyMjA4OGYiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjBmNTc4ZmZhNjkxOGIwYWZkOTkwYWMwOTY3MjY3NjllZmM5NTU1YmJkMWE2YzkyNmZiOGI1MWMyYTY4ZmYwNTgiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiI1NWE1YjBkZGRmMDBhMGQ1NDAwNWI1MTA4YzNmNWRiMGYxZjQ4MDVjYmMxY2IxODk5YTlkYWUyNGMwMGNjM2Q4IiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjliMjliODk4NjU1MzIwZjUyYzJjMWYzMjNjYmMzMjdiNjUxYTEyMmY0ZDM5NGFiZTY2ZjJhZjIzODVjYjM0NzEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiNzk3NmFjZWYxNmE5ZTk1NTgzZmYwYTYyNmUyYWI1MWFjZTRlOTU0MGNiZjZlZGEzOWUwMWM4NjVkNTBkMTlhOCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiIzODQ1MzExZmM4MTE4NzdiODA2ZmVmYjNiMWZhNGYyY2U4NDFlNjM2YzE2ZTk5ODQxYWQ4ZDcwZDM2ZTU0ZTFhIiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNmI2NGM4ZjZmYWE0NTRhZjgyOWFlM2I2NzdiZTUwNDhmNDJhNzgzMDU3MDI0NmE1NDcwODc4Y2M3NjY5NGIzMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkNzAzZTUyMjNmMDE5YmIwNzgyN2IxNjgxZTExMWE4Y2M4YzljMjE5YmUxMmI2MDYzNGU0YjYxZGE1MWNkYTk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMmMyMzYxZWI1OThlM2M1YWUzNWE3YTRjZTA1ZDU3MjYwOGY5NDUwMDExN2ZiN2NmNGYxYWFiMmY1NzZhYzk0MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjA5ZjZhZjVkN2FhMDViY2EyZjY1MWMxZjYwMmExYjI2ZDMxMmM4ZDk1OTE1MTMyOWY1OGExYjIyZDAyOGZiNDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYTc1ZTg3NmEzOWExMzc2NmYzM2NjMmVkYTM3Mjk5ODYxM2QyMGMzOTgxMGE2M2I0N2UzODhkNmYyYzBjMWUwZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIwZjA2NDc0YjUzOTI2NzdmMzJjMjI4YWFiNGQ2ZjliY2FjZGEwMThhODc0M2NkY2NhMzMxMTgzY2E0MzhkZTRmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOTVjZTE0MGMyNWUzYjMzZTJiN2YyMDE0Yjc3NmM0NDI2YjZjOTUxYjVkOTVmNTZkYTE4NWQ5NzNmM2EwODBlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2ViMTJiNTgyNGM3MGJjNTg3OWYzNzAwYjhkZTVlMzRiMDcwMDUxZjc1NWRlZTdlMzI3ZDFhMGIxNDllYzU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImM1M2ZlNzdlNDM2MDgwZjdmNTgxZmQ5YzgyZTVhNmUyOTBiZWYxNzdkYmRhODA3OTM0NzY3MWQ0NGZiZmI5MzIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "privacy": { "obfuscated": [] }, + "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + "signature": "0x6b70121fe8fcf93f327f948d22bbd3e44501886f71ec93c717d051ab59864d17205b86489131f0ccd0b72fc13ee7749d8c2269eecb4fd0ccfe350112d9408cf61c" } } From 69cf9314f2167b82d0880bb199d0bc83fd132b0d Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 20 Mar 2024 16:37:48 +0800 Subject: [PATCH 008/107] lint: ignore unused argument options --- src/4.0/validate/context.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/4.0/validate/context.ts b/src/4.0/validate/context.ts index 7880afa3..5f599524 100644 --- a/src/4.0/validate/context.ts +++ b/src/4.0/validate/context.ts @@ -1,20 +1,23 @@ import jsonld, { expand, Options, JsonLdDocument } from "jsonld"; +import type { RemoteDocument } from "jsonld/jsonld-spec"; import { ContextUrl } from "../../shared/@types/document"; const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; const contexts: Map = new Map(); // FIXME: @types/json-ld seems to be outdated as documentLoaders is not typed -const nodeDocumentLoader = (jsonld as any).documentLoaders.node(); +const nodeDocumentLoader: (url: string) => Promise = (jsonld as any).documentLoaders.node(); // Preload frequently used contexts // https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader let isFirstLoad = true; -const documentLoader: Options.DocLoader["documentLoader"] = async (url, callback) => { +// FIXME: @types/json-ld seems to be outdated as callback is supposed to be options +const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => { if (isFirstLoad) { isFirstLoad = false; for (const url of preloadedContextList) { - contexts.set(url, (await nodeDocumentLoader(url)).document); + const { document } = await nodeDocumentLoader(url); + contexts.set(url, document); } } if (contexts.get(url)) { From 2aa66d700117ab30eed115e5162987776724916d Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 27 Mar 2024 17:52:55 +0800 Subject: [PATCH 009/107] chore: upgrade jest and ts-jest --- package-lock.json | 1149 ++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 563 insertions(+), 590 deletions(-) diff --git a/package-lock.json b/package-lock.json index e18a0a93..911f7add 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "eslint-plugin-jest": "^24.4.0", "eslint-plugin-prettier": "^3.4.0", "git-cz": "^4.7.6", - "jest": "^29.6.2", + "jest": "^29.7.0", "jest-watch-typeahead": "^2.2.2", "prettier": "^2.3.2", "qrcode": "^1.5.1", @@ -58,7 +58,7 @@ "rollup": "^2.56.2", "rollup-plugin-commonjs": "^10.1.0", "semantic-release": "^21.1.1", - "ts-jest": "^29.1.1", + "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typescript": "^5.1.6" } @@ -86,109 +86,47 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", + "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", - "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", + "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.10", - "@babel/parser": "^7.22.10", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.1", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.1", + "@babel/parser": "^7.24.1", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -199,12 +137,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/@babel/core/node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -227,14 +159,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -242,9 +174,9 @@ } }, "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -252,14 +184,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -292,22 +224,22 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -326,28 +258,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -357,9 +289,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -390,55 +322,56 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", - "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", + "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.10", - "@babel/types": "^7.22.10" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -507,9 +440,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -579,12 +512,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -681,12 +614,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -696,34 +629,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", - "debug": "^4.1.0", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -740,13 +673,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1973,16 +1906,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1990,37 +1923,37 @@ } }, "node_modules/@jest/core": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -2064,88 +1997,88 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -2154,13 +2087,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -2179,19 +2112,35 @@ } }, "node_modules/@jest/reporters/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -2201,9 +2150,9 @@ } }, "node_modules/@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -2215,9 +2164,9 @@ } }, "node_modules/@jest/source-map/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2225,13 +2174,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -2240,14 +2189,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -2255,22 +2204,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -2291,12 +2240,12 @@ } }, "node_modules/@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -2308,19 +2257,29 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", @@ -2331,9 +2290,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -3651,9 +3610,9 @@ "dev": true }, "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -3693,9 +3652,9 @@ "dev": true }, "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -3706,18 +3665,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -3725,9 +3684,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -3749,9 +3708,9 @@ "dev": true }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -4418,15 +4377,15 @@ } }, "node_modules/babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -4455,9 +4414,9 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -4493,12 +4452,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -4662,9 +4621,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -4681,10 +4640,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -4811,9 +4770,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001599", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", + "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", "dev": true, "funding": [ { @@ -5543,6 +5502,27 @@ "typescript": ">=4" } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -5857,9 +5837,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5953,9 +5933,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.496", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.496.tgz", - "integrity": "sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g==", + "version": "1.4.713", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.713.tgz", + "integrity": "sha512-vDarADhwntXiULEdmWd77g2dV6FrNGa8ecAC29MZ4TwPut2fvosD0/5sJd1qWNNe8HcJFAC+F5Lf9jW1NPtWmw==", "dev": true }, "node_modules/elliptic": { @@ -6961,17 +6941,16 @@ } }, "node_modules/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -8739,9 +8718,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -8767,15 +8746,15 @@ } }, "node_modules/jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -8793,12 +8772,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -8821,28 +8801,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -8880,32 +8860,65 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, "node_modules/jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -8926,24 +8939,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -8953,62 +8966,62 @@ } }, "node_modules/jest-each": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.2", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -9020,46 +9033,46 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -9068,14 +9081,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.2" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -9099,26 +9112,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -9128,43 +9141,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -9188,31 +9201,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -9221,9 +9234,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -9231,20 +9244,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -9252,12 +9265,12 @@ } }, "node_modules/jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -9269,17 +9282,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.2" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -9422,18 +9435,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -9468,13 +9481,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -9497,40 +9510,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", - "dev": true, - "dependencies": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, "node_modules/joi": { "version": "17.12.2", "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", @@ -10422,9 +10401,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/nofilter": { @@ -14325,12 +14304,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -14403,9 +14382,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -16712,9 +16691,9 @@ } }, "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -16730,7 +16709,7 @@ "ts-jest": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", @@ -17069,9 +17048,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -17148,35 +17127,29 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", diff --git a/package.json b/package.json index 62a290cd..623fa5e8 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "eslint-plugin-jest": "^24.4.0", "eslint-plugin-prettier": "^3.4.0", "git-cz": "^4.7.6", - "jest": "^29.6.2", + "jest": "^29.7.0", "jest-watch-typeahead": "^2.2.2", "prettier": "^2.3.2", "qrcode": "^1.5.1", @@ -66,7 +66,7 @@ "rollup": "^2.56.2", "rollup-plugin-commonjs": "^10.1.0", "semantic-release": "^21.1.1", - "ts-jest": "^29.1.1", + "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typescript": "^5.1.6" }, From 6e007d2c64f7798d8e75021fd2e92db20b33bee9 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 27 Mar 2024 17:55:22 +0800 Subject: [PATCH 010/107] fix: segmentation fault when using default nodeDocumentLoader() --- src/4.0/validate/context.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/4.0/validate/context.ts b/src/4.0/validate/context.ts index 5f599524..7fbbd178 100644 --- a/src/4.0/validate/context.ts +++ b/src/4.0/validate/context.ts @@ -1,13 +1,10 @@ -import jsonld, { expand, Options, JsonLdDocument } from "jsonld"; -import type { RemoteDocument } from "jsonld/jsonld-spec"; +import { expand, Options, JsonLdDocument } from "jsonld"; +import { fetch } from "cross-fetch"; import { ContextUrl } from "../../shared/@types/document"; const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; const contexts: Map = new Map(); -// FIXME: @types/json-ld seems to be outdated as documentLoaders is not typed -const nodeDocumentLoader: (url: string) => Promise = (jsonld as any).documentLoaders.node(); - // Preload frequently used contexts // https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader let isFirstLoad = true; @@ -16,7 +13,7 @@ const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => { if (isFirstLoad) { isFirstLoad = false; for (const url of preloadedContextList) { - const { document } = await nodeDocumentLoader(url); + const document = await fetch(url).then((res) => res.json()); contexts.set(url, document); } } @@ -27,7 +24,8 @@ const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => { documentUrl: url, // this is the actual context URL after redirects }; } else { - return nodeDocumentLoader(url); + const document = await fetch(url).then((res) => res.json()); + return { contextUrl: undefined, document, documentUrl: url }; } }; From cd1cf66b04acc6b25cffa9e5ea96ef27c6cc21bf Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 27 Mar 2024 17:57:06 +0800 Subject: [PATCH 011/107] fix: use scoped context types avoid using sub types (e.g. renderMethodType = EMBEDDED_RENDERER) and just utilise a scoped context (e.g. type = OpenAttestationEmbeddedRenderer) --- src/4.0/schema/schema.json | 77 +++++++----------------- src/shared/utils/utils.ts | 5 +- test/fixtures/v4/did-raw.json | 12 ++-- test/fixtures/v4/did-signed-wrapped.json | 19 +++--- test/fixtures/v4/did-wrapped.json | 19 +++--- 5 files changed, 49 insertions(+), 83 deletions(-) diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index 01296d77..845690f6 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -48,7 +48,7 @@ "proof": { "type": "object", "properties": { - "type": { "$ref": "#/definitions/type" }, + "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationMerkleProofSignature2018"] }, "proofPurpose": { "type": "string", "enum": ["assertionMethod"] }, "targetHash": { "type": "string" }, "proofs": { "type": "array", "items": { "type": "string" } }, @@ -67,7 +67,7 @@ "properties": { "@context": { "$ref": "#/definitions/@context" }, "id": { "$ref": "#/definitions/id" }, - "type": { "$ref": "#/definitions/type" }, + "type": { "$ref": "#/definitions/type", "examples": ["VerifiableCredential", "OpenAttestationCredential"] }, "credentialSchema": { "anyOf": [ { "$ref": "#/definitions/credentialSchema" }, @@ -77,12 +77,14 @@ "validFrom": { "type": "string", "format": "date-time", - "description": "The date and time when this credential becomes valid" + "description": "The date and time when this credential becomes valid", + "examples": ["2024-03-08T12:00:00+08:00"] }, "validUntil": { "type": "string", "format": "date-time", - "description": "The date and time when this credential expires" + "description": "The date and time when this credential expires", + "examples": ["2024-03-27T12:00:00+08:00"] }, "name": { "type": "string", @@ -99,7 +101,7 @@ "format": "uri", "description": "URI when dereferenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential. More information in https://www.w3.org/TR/vc-data-model/#issuer" }, - "type": { "$ref": "#/definitions/type" }, + "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationIssuer"] }, "name": { "type": "string", "description": "Issuer's name" @@ -126,68 +128,33 @@ }, "credentialStatus": { "type": "object", - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "$ref": "#/definitions/type" - }, - "credentialStatusType": { - "type": "string", - "enum": ["NONE"] - } - }, - "required": ["credentialStatusType"], - "additionalProperties": false + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "URI to the status of the credential as explained by https://www.w3.org/TR/vc-data-model/#status", + "examples": ["https://ocsp-sandbox.openattestation.com"] }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URI to the status of the credential as explained by https://www.w3.org/TR/vc-data-model/#status" - }, - "type": { - "$ref": "#/definitions/type" - }, - "credentialStatusType": { - "type": "string", - "enum": ["REVOCATION_STORE", "OCSP_RESPONDER"] - }, - "location": { - "type": "string", - "format": "uri", - "examples": ["https://ocsp-responder.example.com"] - } - }, - "required": ["credentialStatusType", "location"], - "additionalProperties": false - } - ] + "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationOcspResponder"] } + }, + "additionalProperties": false }, "renderMethod": { "type": "object", "properties": { - "type": { "$ref": "#/definitions/type" }, - "renderMethodType": { + "id": { "type": "string", - "enum": ["EMBEDDED_RENDERER"], - "description": "Type of renderer template" + "format": "uri", + "description": "URL of a decentralised renderer to render this document", + "examples": ["https://demo-renderer.openattestation.com"] }, + "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationEmbeddedRenderer"] }, "name": { "type": "string", "description": "Template name to be use by template renderer to determine the template to use" - }, - "url": { - "type": "string", - "format": "uri", - "pattern": "^(https?)://", - "description": "URL of a decentralised renderer to render this document" } }, - "required": ["name"] + "required": ["id", "name"] }, "credentialSubject": { "anyOf": [ diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index acf4d771..df7d8dd5 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -132,7 +132,7 @@ export const getTemplateURL = (document: any): string | undefined => { } else if (isRawV3Document(document) || isWrappedV3Document(document)) { return document.openAttestationMetadata.template?.url; } else if (isRawV4Document(document) || isWrappedV4Document(document)) { - return document.renderMethod?.url; + return document.renderMethod?.id; } throw new Error( @@ -188,8 +188,7 @@ export const isDocumentRevokable = (document: any): boolean => { if (typeof document.issuer === "string" || !document.credentialStatus) return false; const isDidRevokableV4 = document.issuer.identityProof?.identityProofType === v4.IdentityProofType.DNSDid - ? document.credentialStatus.credentialStatusType === v4.CredentialStatusType.OcspResponder || - document.credentialStatus.credentialStatusType === v4.CredentialStatusType.RevocationStore + ? document.credentialStatus.type === "OpenAttestationOcspResponder" // TODO: Create suggested typings e.g. "v4.CredentialStatusType.OpenAttestationOcspResponder" : false; // TODO: OA v4 issuer schema not updated to support document store issuance yet // const isDocumentStoreRevokableV4 = ? diff --git a/test/fixtures/v4/did-raw.json b/test/fixtures/v4/did-raw.json index 95117d2c..2fd3987c 100644 --- a/test/fixtures/v4/did-raw.json +++ b/test/fixtures/v4/did-raw.json @@ -12,12 +12,14 @@ "name": "Government Technology Agency of Singapore (GovTech)", "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, - "credentialStatus": { "type": "OpenAttestationCredentialStatus", "credentialStatusType": "NONE" }, + "credentialStatus": { + "id": "https://ocsp-sandbox.openattestation.com", + "type": "OpenAttestationOcspResponder" + }, "renderMethod": { - "type": "OpenAttestationRenderMethod", - "renderMethodType": "EMBEDDED_RENDERER", - "name": "GOVTECH_DEMO", - "url": "https://demo-renderer.openattestation.com" + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "name": "GOVTECH_DEMO" }, "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", diff --git a/test/fixtures/v4/did-signed-wrapped.json b/test/fixtures/v4/did-signed-wrapped.json index ae24a9e9..ca1f87f1 100644 --- a/test/fixtures/v4/did-signed-wrapped.json +++ b/test/fixtures/v4/did-signed-wrapped.json @@ -12,12 +12,11 @@ "name": "Government Technology Agency of Singapore (GovTech)", "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, - "credentialStatus": { "type": "OpenAttestationCredentialStatus", "credentialStatusType": "NONE" }, + "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, "renderMethod": { - "type": "OpenAttestationRenderMethod", - "renderMethodType": "EMBEDDED_RENDERER", - "name": "GOVTECH_DEMO", - "url": "https://demo-renderer.openattestation.com" + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "name": "GOVTECH_DEMO" }, "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", @@ -39,10 +38,12 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", + "targetHash": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", "proofs": [], - "merkleRoot": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", - "salts": "W3sidmFsdWUiOiJmNWUxOGNhN2M5YjMxM2E0Y2M1NWU4OWExNTllYzc5MGRlY2ZkYzE3ZjA5ZGVkYzBkYWY1YTM1MzY5MjkzMjM5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjFkZDNiYTUzMDE4ZWQzOTYwNjdmZDkwYWMwMTgwYTVlODdjY2QxZTc5NWQ3OTI1ZjM0YzkyMTRmNGNlZmE3M2EiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTg1NWJiMWY2NzM5ODU5OTZiZmUzM2YyMDJiYzVjNGRlMTdkNzBjMWIxODk5NzEzNjY1MTI0ZTIyMzI3ODY0ZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjkwMzc0N2YwOTAxODY0YzVhNjgxMGYzNDAwZTRmYjZjYzFiY2M5NWU1MTJlMDA1MWY1ODE0NzkyYmRlZGViYWUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiJlMWE0M2ZmNmJhMWI1MmE1NzY5M2NmMzMyYmE0ZmUxOWM5NjRmYWQxZmY1ZDU2MmU2MWUwZjg2MDNkMTg2ODUyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2ZDMxNjk5MTI2NWFkMjAzZjYzYTgwZTM3MWI3NTViMzMyMzBjYTc4ZTUzZWVkZDA1ZDI5NGY3ODQ3NDExMWE1IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiY2JmYzI2ZWQ0ZWM1Nzc3ZTlhYmJmMzU0NzM2ZWY1MTJlODc5YTdjYTQwZTAzMzZjYzU1OWYyMGI5NDI2MWI1NiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGM4ZGQ4YTE0MjAyZWQ4NGY4ODMzZmUwZTY0MmQzNGI2MTE3NzI2YWE4Njk5MjQwZGZjMjc3MjBjZGI5YjYwMyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNGE3YWY1MjgyMzM2YjI2YTg2Yjk3ZDliNDU5MTU2ODdkOGVmNmNkZTVmNmFiNGE0NDVlYjIzYzE5YjI5YzVjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImIyY2I3MTE0YTBhOWU5NjYxYzQzZTQ3NDNkM2U3MTM1NDlkNGIyN2NjY2M5OTdkMGU0OGJiMjIzZDA2ZDNhNTgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2YxZTk4MGI5Yjg0Y2Y0ZjMyZjAwN2UwMWM1Mjg0YjFhMTY3ODhhMmIwOWQyZmVmODdkZDA0YjQzYmRkYTAwYiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjU4ZDQzNzFiMjlhYTJhM2UxMjQyYjJjMmE3OGU3YjI3YTI3MmQ1MDVkZTdkNGM0MmNlMzMxZWFlMTkyMjA4OGYiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjBmNTc4ZmZhNjkxOGIwYWZkOTkwYWMwOTY3MjY3NjllZmM5NTU1YmJkMWE2YzkyNmZiOGI1MWMyYTY4ZmYwNTgiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiI1NWE1YjBkZGRmMDBhMGQ1NDAwNWI1MTA4YzNmNWRiMGYxZjQ4MDVjYmMxY2IxODk5YTlkYWUyNGMwMGNjM2Q4IiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjliMjliODk4NjU1MzIwZjUyYzJjMWYzMjNjYmMzMjdiNjUxYTEyMmY0ZDM5NGFiZTY2ZjJhZjIzODVjYjM0NzEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiNzk3NmFjZWYxNmE5ZTk1NTgzZmYwYTYyNmUyYWI1MWFjZTRlOTU0MGNiZjZlZGEzOWUwMWM4NjVkNTBkMTlhOCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiIzODQ1MzExZmM4MTE4NzdiODA2ZmVmYjNiMWZhNGYyY2U4NDFlNjM2YzE2ZTk5ODQxYWQ4ZDcwZDM2ZTU0ZTFhIiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNmI2NGM4ZjZmYWE0NTRhZjgyOWFlM2I2NzdiZTUwNDhmNDJhNzgzMDU3MDI0NmE1NDcwODc4Y2M3NjY5NGIzMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkNzAzZTUyMjNmMDE5YmIwNzgyN2IxNjgxZTExMWE4Y2M4YzljMjE5YmUxMmI2MDYzNGU0YjYxZGE1MWNkYTk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMmMyMzYxZWI1OThlM2M1YWUzNWE3YTRjZTA1ZDU3MjYwOGY5NDUwMDExN2ZiN2NmNGYxYWFiMmY1NzZhYzk0MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjA5ZjZhZjVkN2FhMDViY2EyZjY1MWMxZjYwMmExYjI2ZDMxMmM4ZDk1OTE1MTMyOWY1OGExYjIyZDAyOGZiNDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYTc1ZTg3NmEzOWExMzc2NmYzM2NjMmVkYTM3Mjk5ODYxM2QyMGMzOTgxMGE2M2I0N2UzODhkNmYyYzBjMWUwZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIwZjA2NDc0YjUzOTI2NzdmMzJjMjI4YWFiNGQ2ZjliY2FjZGEwMThhODc0M2NkY2NhMzMxMTgzY2E0MzhkZTRmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOTVjZTE0MGMyNWUzYjMzZTJiN2YyMDE0Yjc3NmM0NDI2YjZjOTUxYjVkOTVmNTZkYTE4NWQ5NzNmM2EwODBlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2ViMTJiNTgyNGM3MGJjNTg3OWYzNzAwYjhkZTVlMzRiMDcwMDUxZjc1NWRlZTdlMzI3ZDFhMGIxNDllYzU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImM1M2ZlNzdlNDM2MDgwZjdmNTgxZmQ5YzgyZTVhNmUyOTBiZWYxNzdkYmRhODA3OTM0NzY3MWQ0NGZiZmI5MzIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": [] } + "merkleRoot": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", + "salts": "W3sidmFsdWUiOiJkNTRkYzlkNDc3MDVjNjFmOGRhYTJiMzkzNmQwMDk3YjM5YWI5NzA1MmQxNDAwY2U2ODI1NDhhMzI1NzA5ZmY1IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjJiMjEyN2I3NTc4MTBhMjNiNDNlZWIyNWI3NDIxZTc5YjllM2FmYjY0NDI2YTkyNjY0ODJlYmUwY2EzOTI0NDAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMGFlMTA4NDUwYmZlYzlkYWRmY2MwNjM3MTRiYjQ4NTRlZDI1ZDQxZDFkOTg3ZDRlYzc5YWE0ZmFiM2VlOTJmMyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjZkM2ZhYzI3YzJlZjE3NjJhODRkM2MyMjAwYTcwOGRjMjBjZDZiYjZhNzAyOTBlMzE2ZTUwNGJkZjBmMDQ0YTIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI1MDNkYjI4ZTk5YmNjMDgwMDE2ZWExMzZkNTJmYzJjZTZiMzczZmFhMTc3MzAxNjRiYjRlNDc2MDlhYzg2ZGEyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJiYjljMzU4MGFmZTA4NmEwYzljNmE2ZGI1YzhjYjI0OWY0YWYyZGQ3NDI1YzJmMjViNGZlMzY5NzE5MzdiNDI5IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDgyNmI1MzhkNjgzNGJhM2FmZmE2ZTc3MmE4Yzk0NTFkMWMzNzI5NTY0ODExMDRlOGQyNDU4MDE0ZjIyYTU4ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiODM4YTFjNzQ3NDk1ODY4MWVjOTY1ZjBjYjljMjRhZTIyMWU2ZTQ3N2MxMTk4MDhhYTNkMTQzOTU5ZmRkZWM2OCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyNTE5ZTc0YTY0MDc4OWRkMmU2MDYyYjYzNGI3OWI1YzVhOGQxYTQwMGU3ZTQxNWY0OWYzMGZhNjk5NGQ4ZTY1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjQ4OTI1NzE0N2VkZjU4N2UxYmM3NmUwZWQ0NjIzZGFhYzMwMmQ3ZTRjNmEwMTVmNzY3YjlkMTJhNDhkMjU5MjUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYTA1MWY5MGNhNWIzNzY4NDAyMzRmN2E3M2JlMGU2NmE0YTY5M2FkODFhN2EwM2E1NzQ0MzAwZjZlZTI2YWY5NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI2YmEwZDNmOTVjNzFhMzliYTM0M2UzZWRjODU4N2NjMjQwOGUzZTBmMTM3OGZiNzYzNDA4MGI1ZDkzMjMyMGMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJkZjQ5MmU4YTA4NzZhNmMzYTA1M2RjOTkyOGRmNjBkMTA0NTNhMTZmYTcxZDY5YTdlNTU4ZTg4ZDZmMWZjNDM0IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJlYmI5NDk5MjI0YWJhYjFlOTM2OGM1ZjY4NGVhYWU3ODU0OTJlYmJiYzQyNmVhN2NjM2RhZGY4MjA0YWRkZGNjIiwicGF0aCI6InJlbmRlck1ldGhvZC5pZCJ9LHsidmFsdWUiOiIzN2I1ZDdhMmJkMDRiMTg5MmM4NTVlZjczYjZiNzExYTc5NmUzODQyNGI1ZWJkOWM2MDcyYjQ5Y2Y0M2IzNDRjIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjQ3NTVmZTc4OGNhOGQyYTlmNjdkOTBjYjc0MWZmY2VlMThjNGNjZDg4Njg0ZWE0YmUyYmViMDBhNjViNThhNmQiLCJwYXRoIjoicmVuZGVyTWV0aG9kLm5hbWUifSx7InZhbHVlIjoiODgyNjA1YzU0OTBhZjcxN2E4ZGVkNzYyMTlkOTRlNmI1ZjYzZTY0NGQ4MTJkODNmN2EyZjc2Y2I3YjI5MzQ3OCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI0ZGE2NTE2MjU5YjQyMDkzYjhhY2MzMjVlMjVkNzlhZjYxYWQyOTVhMmY1NzIxMmQxYWM2MzE5ZTg2MWZlNTU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMzg4ODllZjMzMjc4MDg5MjE3MzcxMzVkMzZkYWNjNWZlYWYzNThiYjE5Mjk1ODlhMzIxMmI5NGY4ZTJlOGRjMiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNkZTc5Yjc3NjRjNTA3MzBiNWEzZTQzZDUzMWJhMTljZDg4Njg1OTZlMzk4N2EyNjRjNjI5NmZlYWQ5NTkwZDEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiMzgyNzRmNDA0Nzc3YThhYjEzMzVkODFiYTJmYjAyNzFiYjg0OTE0ODc4YzMzYmI4Y2RhMzYzNGFmNmIyZmJlYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJmYjA2NWM2MjVhZDE0Y2Y1Mzg0MzAwNGQwMDllYzNjNTg3MGJmYzdlMjA1YzRjMTMzOGVkMzU2NmVhY2MwOTEyIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjM0YWQzODRlNjZhZWE2YWRmMTk1MDU3ZGUyM2Q4Yzk1MzFhZTMwYjdkNzRkMjJkMzYxYjcxOTRkM2EzODRjMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJhZjM1ZGU5ZGY5MDg4ODg3NmQ2YjVkMjI2YjkyZDYxZTgyMzgyNzMyMDY4YTNlMTExMmJmZWYwMDcxZmVhZmNmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjA1MDliZWFhZmUxNTA0NDhmMmQ5ZGYwNmZmMmY5OWU4NWIwMjcwNjJjMWJkMzc5MzkwMDViZmZjMWM5NjI3ZDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "privacy": { "obfuscated": [] }, + "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + "signature": "0xe9c534bdace23bcd572f8ef64e2131a02bb124f4cc0a42e50a5e222ab7fd177e1da7bf04c25abc662b22000596d74ce74cd9c70330b7932f1681898c1ad10c891c" } } diff --git a/test/fixtures/v4/did-wrapped.json b/test/fixtures/v4/did-wrapped.json index 3a0d3350..2a415f10 100644 --- a/test/fixtures/v4/did-wrapped.json +++ b/test/fixtures/v4/did-wrapped.json @@ -12,12 +12,11 @@ "name": "Government Technology Agency of Singapore (GovTech)", "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, - "credentialStatus": { "type": "OpenAttestationCredentialStatus", "credentialStatusType": "NONE" }, + "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, "renderMethod": { - "type": "OpenAttestationRenderMethod", - "renderMethodType": "EMBEDDED_RENDERER", - "name": "GOVTECH_DEMO", - "url": "https://demo-renderer.openattestation.com" + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "name": "GOVTECH_DEMO" }, "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", @@ -39,12 +38,10 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", + "targetHash": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", "proofs": [], - "merkleRoot": "0cde145b41276c5eaae9360b55e0177a65d87f3a425cb07b215d64f9d9405fea", - "salts": "W3sidmFsdWUiOiJmNWUxOGNhN2M5YjMxM2E0Y2M1NWU4OWExNTllYzc5MGRlY2ZkYzE3ZjA5ZGVkYzBkYWY1YTM1MzY5MjkzMjM5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjFkZDNiYTUzMDE4ZWQzOTYwNjdmZDkwYWMwMTgwYTVlODdjY2QxZTc5NWQ3OTI1ZjM0YzkyMTRmNGNlZmE3M2EiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTg1NWJiMWY2NzM5ODU5OTZiZmUzM2YyMDJiYzVjNGRlMTdkNzBjMWIxODk5NzEzNjY1MTI0ZTIyMzI3ODY0ZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjkwMzc0N2YwOTAxODY0YzVhNjgxMGYzNDAwZTRmYjZjYzFiY2M5NWU1MTJlMDA1MWY1ODE0NzkyYmRlZGViYWUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiJlMWE0M2ZmNmJhMWI1MmE1NzY5M2NmMzMyYmE0ZmUxOWM5NjRmYWQxZmY1ZDU2MmU2MWUwZjg2MDNkMTg2ODUyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2ZDMxNjk5MTI2NWFkMjAzZjYzYTgwZTM3MWI3NTViMzMyMzBjYTc4ZTUzZWVkZDA1ZDI5NGY3ODQ3NDExMWE1IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiY2JmYzI2ZWQ0ZWM1Nzc3ZTlhYmJmMzU0NzM2ZWY1MTJlODc5YTdjYTQwZTAzMzZjYzU1OWYyMGI5NDI2MWI1NiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGM4ZGQ4YTE0MjAyZWQ4NGY4ODMzZmUwZTY0MmQzNGI2MTE3NzI2YWE4Njk5MjQwZGZjMjc3MjBjZGI5YjYwMyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNGE3YWY1MjgyMzM2YjI2YTg2Yjk3ZDliNDU5MTU2ODdkOGVmNmNkZTVmNmFiNGE0NDVlYjIzYzE5YjI5YzVjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImIyY2I3MTE0YTBhOWU5NjYxYzQzZTQ3NDNkM2U3MTM1NDlkNGIyN2NjY2M5OTdkMGU0OGJiMjIzZDA2ZDNhNTgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2YxZTk4MGI5Yjg0Y2Y0ZjMyZjAwN2UwMWM1Mjg0YjFhMTY3ODhhMmIwOWQyZmVmODdkZDA0YjQzYmRkYTAwYiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjU4ZDQzNzFiMjlhYTJhM2UxMjQyYjJjMmE3OGU3YjI3YTI3MmQ1MDVkZTdkNGM0MmNlMzMxZWFlMTkyMjA4OGYiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjBmNTc4ZmZhNjkxOGIwYWZkOTkwYWMwOTY3MjY3NjllZmM5NTU1YmJkMWE2YzkyNmZiOGI1MWMyYTY4ZmYwNTgiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5jcmVkZW50aWFsU3RhdHVzVHlwZSJ9LHsidmFsdWUiOiI1NWE1YjBkZGRmMDBhMGQ1NDAwNWI1MTA4YzNmNWRiMGYxZjQ4MDVjYmMxY2IxODk5YTlkYWUyNGMwMGNjM2Q4IiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjliMjliODk4NjU1MzIwZjUyYzJjMWYzMjNjYmMzMjdiNjUxYTEyMmY0ZDM5NGFiZTY2ZjJhZjIzODVjYjM0NzEiLCJwYXRoIjoicmVuZGVyTWV0aG9kLnJlbmRlck1ldGhvZFR5cGUifSx7InZhbHVlIjoiNzk3NmFjZWYxNmE5ZTk1NTgzZmYwYTYyNmUyYWI1MWFjZTRlOTU0MGNiZjZlZGEzOWUwMWM4NjVkNTBkMTlhOCIsInBhdGgiOiJyZW5kZXJNZXRob2QubmFtZSJ9LHsidmFsdWUiOiIzODQ1MzExZmM4MTE4NzdiODA2ZmVmYjNiMWZhNGYyY2U4NDFlNjM2YzE2ZTk5ODQxYWQ4ZDcwZDM2ZTU0ZTFhIiwicGF0aCI6InJlbmRlck1ldGhvZC51cmwifSx7InZhbHVlIjoiNmI2NGM4ZjZmYWE0NTRhZjgyOWFlM2I2NzdiZTUwNDhmNDJhNzgzMDU3MDI0NmE1NDcwODc4Y2M3NjY5NGIzMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiJkNzAzZTUyMjNmMDE5YmIwNzgyN2IxNjgxZTExMWE4Y2M4YzljMjE5YmUxMmI2MDYzNGU0YjYxZGE1MWNkYTk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMmMyMzYxZWI1OThlM2M1YWUzNWE3YTRjZTA1ZDU3MjYwOGY5NDUwMDExN2ZiN2NmNGYxYWFiMmY1NzZhYzk0MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjA5ZjZhZjVkN2FhMDViY2EyZjY1MWMxZjYwMmExYjI2ZDMxMmM4ZDk1OTE1MTMyOWY1OGExYjIyZDAyOGZiNDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYTc1ZTg3NmEzOWExMzc2NmYzM2NjMmVkYTM3Mjk5ODYxM2QyMGMzOTgxMGE2M2I0N2UzODhkNmYyYzBjMWUwZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIwZjA2NDc0YjUzOTI2NzdmMzJjMjI4YWFiNGQ2ZjliY2FjZGEwMThhODc0M2NkY2NhMzMxMTgzY2E0MzhkZTRmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOTVjZTE0MGMyNWUzYjMzZTJiN2YyMDE0Yjc3NmM0NDI2YjZjOTUxYjVkOTVmNTZkYTE4NWQ5NzNmM2EwODBlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2ViMTJiNTgyNGM3MGJjNTg3OWYzNzAwYjhkZTVlMzRiMDcwMDUxZjc1NWRlZTdlMzI3ZDFhMGIxNDllYzU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImM1M2ZlNzdlNDM2MDgwZjdmNTgxZmQ5YzgyZTVhNmUyOTBiZWYxNzdkYmRhODA3OTM0NzY3MWQ0NGZiZmI5MzIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": [] }, - "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0x6b70121fe8fcf93f327f948d22bbd3e44501886f71ec93c717d051ab59864d17205b86489131f0ccd0b72fc13ee7749d8c2269eecb4fd0ccfe350112d9408cf61c" + "merkleRoot": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", + "salts": "W3sidmFsdWUiOiJkNTRkYzlkNDc3MDVjNjFmOGRhYTJiMzkzNmQwMDk3YjM5YWI5NzA1MmQxNDAwY2U2ODI1NDhhMzI1NzA5ZmY1IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjJiMjEyN2I3NTc4MTBhMjNiNDNlZWIyNWI3NDIxZTc5YjllM2FmYjY0NDI2YTkyNjY0ODJlYmUwY2EzOTI0NDAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMGFlMTA4NDUwYmZlYzlkYWRmY2MwNjM3MTRiYjQ4NTRlZDI1ZDQxZDFkOTg3ZDRlYzc5YWE0ZmFiM2VlOTJmMyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjZkM2ZhYzI3YzJlZjE3NjJhODRkM2MyMjAwYTcwOGRjMjBjZDZiYjZhNzAyOTBlMzE2ZTUwNGJkZjBmMDQ0YTIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI1MDNkYjI4ZTk5YmNjMDgwMDE2ZWExMzZkNTJmYzJjZTZiMzczZmFhMTc3MzAxNjRiYjRlNDc2MDlhYzg2ZGEyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJiYjljMzU4MGFmZTA4NmEwYzljNmE2ZGI1YzhjYjI0OWY0YWYyZGQ3NDI1YzJmMjViNGZlMzY5NzE5MzdiNDI5IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDgyNmI1MzhkNjgzNGJhM2FmZmE2ZTc3MmE4Yzk0NTFkMWMzNzI5NTY0ODExMDRlOGQyNDU4MDE0ZjIyYTU4ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiODM4YTFjNzQ3NDk1ODY4MWVjOTY1ZjBjYjljMjRhZTIyMWU2ZTQ3N2MxMTk4MDhhYTNkMTQzOTU5ZmRkZWM2OCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyNTE5ZTc0YTY0MDc4OWRkMmU2MDYyYjYzNGI3OWI1YzVhOGQxYTQwMGU3ZTQxNWY0OWYzMGZhNjk5NGQ4ZTY1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjQ4OTI1NzE0N2VkZjU4N2UxYmM3NmUwZWQ0NjIzZGFhYzMwMmQ3ZTRjNmEwMTVmNzY3YjlkMTJhNDhkMjU5MjUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYTA1MWY5MGNhNWIzNzY4NDAyMzRmN2E3M2JlMGU2NmE0YTY5M2FkODFhN2EwM2E1NzQ0MzAwZjZlZTI2YWY5NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI2YmEwZDNmOTVjNzFhMzliYTM0M2UzZWRjODU4N2NjMjQwOGUzZTBmMTM3OGZiNzYzNDA4MGI1ZDkzMjMyMGMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJkZjQ5MmU4YTA4NzZhNmMzYTA1M2RjOTkyOGRmNjBkMTA0NTNhMTZmYTcxZDY5YTdlNTU4ZTg4ZDZmMWZjNDM0IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJlYmI5NDk5MjI0YWJhYjFlOTM2OGM1ZjY4NGVhYWU3ODU0OTJlYmJiYzQyNmVhN2NjM2RhZGY4MjA0YWRkZGNjIiwicGF0aCI6InJlbmRlck1ldGhvZC5pZCJ9LHsidmFsdWUiOiIzN2I1ZDdhMmJkMDRiMTg5MmM4NTVlZjczYjZiNzExYTc5NmUzODQyNGI1ZWJkOWM2MDcyYjQ5Y2Y0M2IzNDRjIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjQ3NTVmZTc4OGNhOGQyYTlmNjdkOTBjYjc0MWZmY2VlMThjNGNjZDg4Njg0ZWE0YmUyYmViMDBhNjViNThhNmQiLCJwYXRoIjoicmVuZGVyTWV0aG9kLm5hbWUifSx7InZhbHVlIjoiODgyNjA1YzU0OTBhZjcxN2E4ZGVkNzYyMTlkOTRlNmI1ZjYzZTY0NGQ4MTJkODNmN2EyZjc2Y2I3YjI5MzQ3OCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI0ZGE2NTE2MjU5YjQyMDkzYjhhY2MzMjVlMjVkNzlhZjYxYWQyOTVhMmY1NzIxMmQxYWM2MzE5ZTg2MWZlNTU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMzg4ODllZjMzMjc4MDg5MjE3MzcxMzVkMzZkYWNjNWZlYWYzNThiYjE5Mjk1ODlhMzIxMmI5NGY4ZTJlOGRjMiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNkZTc5Yjc3NjRjNTA3MzBiNWEzZTQzZDUzMWJhMTljZDg4Njg1OTZlMzk4N2EyNjRjNjI5NmZlYWQ5NTkwZDEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiMzgyNzRmNDA0Nzc3YThhYjEzMzVkODFiYTJmYjAyNzFiYjg0OTE0ODc4YzMzYmI4Y2RhMzYzNGFmNmIyZmJlYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJmYjA2NWM2MjVhZDE0Y2Y1Mzg0MzAwNGQwMDllYzNjNTg3MGJmYzdlMjA1YzRjMTMzOGVkMzU2NmVhY2MwOTEyIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjM0YWQzODRlNjZhZWE2YWRmMTk1MDU3ZGUyM2Q4Yzk1MzFhZTMwYjdkNzRkMjJkMzYxYjcxOTRkM2EzODRjMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJhZjM1ZGU5ZGY5MDg4ODg3NmQ2YjVkMjI2YjkyZDYxZTgyMzgyNzMyMDY4YTNlMTExMmJmZWYwMDcxZmVhZmNmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjA1MDliZWFhZmUxNTA0NDhmMmQ5ZGYwNmZmMmY5OWU4NWIwMjcwNjJjMWJkMzc5MzkwMDViZmZjMWM5NjI3ZDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "privacy": { "obfuscated": [] } } } From 75aec4ada52fb1c54922c17c5cc1ac38e7449f8c Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 27 Mar 2024 17:58:02 +0800 Subject: [PATCH 012/107] test: no more auto-correct for @context based on vc 2.0 test suite expected behaviour is to throw error --- src/4.0/schema/schema.test.ts | 47 +++-------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/src/4.0/schema/schema.test.ts b/src/4.0/schema/schema.test.ts index 7f097616..ee1960b5 100644 --- a/src/4.0/schema/schema.test.ts +++ b/src/4.0/schema/schema.test.ts @@ -25,51 +25,12 @@ describe("schema/4.0", () => { }); describe("@context", () => { - it("should self-correct when @context is a string with a valid context, appending it to the array of @context", async () => { - // @context MUST be an ordered set in W3C VC data model, see https://www.w3.org/TR/vc-data-model/#contexts - const document = { ...cloneDeep(sampleVc), "@context": "https://w3id.org/traceability/v1" }; - const wrappedDocument = await wrapDocumentV4(document as any); - expect(wrappedDocument["@context"]).toStrictEqual([ - "https://www.w3.org/2018/credentials/v1", - ContextUrl.v4_alpha, - "https://w3id.org/traceability/v1", - ]); - }); - it("should self-correct when @context is an array that contains a valid context but is missing the required contexts", async () => { - // This should not have AJV validation errors as it's only caught after - const document = { ...cloneDeep(sampleVc), "@context": ["https://w3id.org/traceability/v1"] }; - const wrappedDocument = await wrapDocumentV4(document); - expect(wrappedDocument["@context"]).toStrictEqual([ - "https://www.w3.org/2018/credentials/v1", - ContextUrl.v4_alpha, - "https://w3id.org/traceability/v1", - ]); - }); - it("should self-correct when @context is an array that contains the required context but not the right order", async () => { - // This should not have AJV validation errors as it's only caught during validateW3C - const document = { - ...cloneDeep(sampleVc), - "@context": ["https://w3id.org/traceability/v1", "https://www.w3.org/2018/credentials/v1"], - }; - const wrappedDocument = await wrapDocumentV4(document); - expect(wrappedDocument["@context"]).toStrictEqual([ - "https://www.w3.org/2018/credentials/v1", - ContextUrl.v4_alpha, - "https://w3id.org/traceability/v1", - ]); - }); it("should be invalid if @context contains one invalid URI", async () => { expect.assertions(1); - const document = { ...cloneDeep(sampleVc), "@context": ["https://www.w3.org/2018/credentials/v1", "any"] }; - await expect(wrapDocumentV4(document)).rejects.toHaveProperty("validationErrors", [ - { - keyword: "format", - instancePath: "/@context/2", - schemaPath: "#/properties/%40context/items/format", - params: { format: "uri" }, - message: 'must match format "uri"', - }, - ]); + const document = { ...cloneDeep(sampleVc), "@context": [ContextUrl.v2_vc, "bad string"] }; + await expect(wrapDocumentV4(document)).rejects.toMatchInlineSnapshot( + `[Error: Unable to interpret @context: {"name":"jsonld.InvalidUrl","details":{"code":"loading remote context failed","url":"bad string","cause":{}}}]` + ); }); }); }); From 8e07b33c3ea68665bb07be647c96cd2fff9d3517 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 17 Apr 2024 13:11:13 +0800 Subject: [PATCH 013/107] fix: digest should handle falsy values correctly --- src/3.0/digest.ts | 3 ++- src/4.0/digest.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/3.0/digest.ts b/src/3.0/digest.ts index 6e8c7de9..436dd0a3 100644 --- a/src/3.0/digest.ts +++ b/src/3.0/digest.ts @@ -6,7 +6,8 @@ import { OpenAttestationDocument } from "../__generated__/schema.3.0"; export const digestCredential = (document: OpenAttestationDocument, salts: Salt[], obfuscatedData: string[]) => { // Prepare array of hashes from visible data const hashedUnhashedDataArray = salts - .filter((salt) => get(document, salt.path)) + // Explictly allow falsy values (e.g. false, 0, etc.) as they can exist in the document + .filter((salt) => get(document, salt.path) !== undefined) .map((salt) => { return keccak256(JSON.stringify({ [salt.path]: `${salt.value}:${get(document, salt.path)}` })); }); diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 966e959d..4cfc7359 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -6,7 +6,8 @@ import { OpenAttestationDocument } from "../__generated__/schema.4.0"; export const digestCredential = (document: OpenAttestationDocument, salts: Salt[], obfuscatedData: string[]) => { // Prepare array of hashes from visible data const hashedUnhashedDataArray = salts - .filter((salt) => get(document, salt.path)) + // Explictly allow falsy values (e.g. false, 0, etc.) as they can exist in the document + .filter((salt) => get(document, salt.path) !== undefined) .map((salt) => { return keccak256(JSON.stringify({ [salt.path]: `${salt.value}:${get(document, salt.path)}` })); }); From d82ac6c779b111418c4509946fbc211ac9c63b44 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 17 Apr 2024 13:23:48 +0800 Subject: [PATCH 014/107] fix: rename to renderMethod.templateName --- src/4.0/schema/schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index 845690f6..b2d4da10 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -149,7 +149,7 @@ "examples": ["https://demo-renderer.openattestation.com"] }, "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationEmbeddedRenderer"] }, - "name": { + "templateName": { "type": "string", "description": "Template name to be use by template renderer to determine the template to use" } From de0dc706d29bd065c9d6fe98026b1fba2749ed35 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 17 Apr 2024 13:37:10 +0800 Subject: [PATCH 015/107] fix: make renderMethod an array --- src/4.0/__tests__/digest.test.ts | 63 ++++++++++++------------ src/4.0/schema/schema.json | 35 ++++++------- src/shared/utils/utils.ts | 2 +- test/fixtures/v4/did-raw.json | 12 +++-- test/fixtures/v4/did-signed-wrapped.json | 16 +++--- test/fixtures/v4/did-wrapped.json | 14 +++--- 6 files changed, 70 insertions(+), 72 deletions(-) diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 172b0fc6..e668e6a7 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -7,7 +7,7 @@ import sample from "../../../test/fixtures/v4/did-wrapped.json"; const verifiableCredential = sample as WrappedDocument; // Digest will change whenever sample document is regenerated -const credentialRoot = "adb16863b9b92f1f46d67f518f853092404dc1322ffb61b45a831ee113f4ea99"; +const credentialRoot = "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373"; const { proof, ...credential } = verifiableCredential; @@ -29,7 +29,7 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "016c17fefa241351dc2950cfbeaef8281b0bc71e1ee445d890e9c37622fa0318", + "e521e95bb3adf4ded40a929af58996c33add5ec9a8d4d0d3db22a69aa29f8517", ] `); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(1); @@ -49,9 +49,9 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "84c8662d07d3b98b7b9b58687a04fd6ff5a90e91f63e70c2399755721630b370", - "4390ee551a3ef3bebaad99c85738b3ebd96932343fb22a59865764125b79565c", - "026dbfc89aaa98005d2f25b0b274a972f1dc5c351d22270eba9d80422dd9850f", + "dc9fb54950b4505553c79505d64f0108f1b7af287e8b909425c17deb7e2d6af4", + "0a5fae6a5a23b95f0fb70bc22b5e38c8c4aacfc476f4214077236315ceb54ce4", + "cadef4aa0ae961b99f4bbb3d11314b8855377156a22341f6fffadef214479e79", ] `); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(3); @@ -71,35 +71,34 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential).toStrictEqual({ proof: obfuscatedVerifiableCredential.proof }); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "f0798858da35807f0e5ae1a253722da6eb073abfad039151a18355d71e18f232", - "867ae2f030d04745d384969efe67b9fdfeea3236aa6b5f10d2bdbfb495a1ecb3", - "671da469c4211222ef05dcfc4fb795b6d774f5a1fc9f1397fab7ff6eba58e5db", - "b92f17321f80808f90ee5c1b089fad1b8ad2ff63e8d4d4ba923e50d0996aba75", - "21d7f40f46a97e9480b48739b560fba4cfe6d2f1b6bf14e3bdf7c9d765590a9a", - "4b13961eed15c275d3b8c11c4d40d8f956ffa8f1361b36058da28349c155d193", - "016c17fefa241351dc2950cfbeaef8281b0bc71e1ee445d890e9c37622fa0318", - "c5796036b237228ddc64bab40cd34200cb8d9dc4574c8a9a7cdda2350d77bdf9", - "b031b7f3cc154783acc36cbdc524bb7d8e4f4093785d25c1000a15c49e0ce58b", - "0913c1f68e821d8b4b07c9406419f083cd326c989bf433ccd13c242fdf834497", - "0c9bbd46994b92637562fef0df5a95eb4549a0198923c66693b547311635455a", - "5dff1a92a0cae0a5d50530968ff06c2baea9d3ac11b415268d98728ff13a4aa6", - "363e3a4656d4b586d1855a4cc22e56b4b446a357f2c76bdc777df6596d22e7e3", - "13e4d679f8cc43a69c7fbc37cd6339ee3864eeaca137687b6c7cff07309f6f98", - "5d027cec038e4f0fbb684da654f12999b973e20788801e13e063f642228d56a5", - "6d8b020b1ef826ce5e05fb034f4d2b9c4ed5bc4d4a0d697a6ec9f6c249970cb0", - "97bbd9a5415e96e1f5f61879e1bdef14db6868304ab3b681c6bbb82e0ecd21b3", - "84c8662d07d3b98b7b9b58687a04fd6ff5a90e91f63e70c2399755721630b370", - "c96b471fec27d76d3b5e1c479ccf690bca845ba78bc2b6b28abd52f9defcf491", - "4390ee551a3ef3bebaad99c85738b3ebd96932343fb22a59865764125b79565c", - "6d38b0c91fa83fd141a20098b121eca264dcf8c20e6bd97d13b9a5e8924026c2", - "026dbfc89aaa98005d2f25b0b274a972f1dc5c351d22270eba9d80422dd9850f", - "5bc0eb80d28496cfe3ed416b91fada582f097bbdaaedcff5aa5e393c8f8be726", - "6fb9f93f2b42bb70a67e6ad5cb22f72083d9d3bf98776e83a470c82800770623", - "aa4414e7a955a034998fd1221c80a2ef77c30c26a7b15fe15b7c2716811bb3d9", - "fba6d49a55387b611fb4dedf401630d3adb1d377e17ba051524a09795ee734ae", + "63285ee08671cb84b18414a813d28cc718ce43b815ff73ce4154faa9c9ad9dcd", + "2d9b6afb3ec3445b22ef6684238a27f32f532dde22875cc9dcf6fd69ae600e23", + "2faef9552eeeb238a9920ce2bc91844fa5b9d313d2377472c2bbc268388e77e6", + "38068d88819bb58dddc47d50657320403ad1c7272248c7f5f8e03d86f7f3a736", + "5c37fad912a96877a1de8697544e647d5956be812d894013724c8d7a7a305daf", + "54d542c2a5074f79d748cd2dad4a4e152254d593ab511cc6190f8dd367625db7", + "e521e95bb3adf4ded40a929af58996c33add5ec9a8d4d0d3db22a69aa29f8517", + "f74a658d3e1b8b6089b2a20002ef53a2c7ef5ccadf391f1863805fb95bb1d4b8", + "2ee3e874b989d2f17f8e1a2b29a0d7a361143f7a0a05eaf6650b74a9e1a6490d", + "10c8faff3d92cf41f0c1392d28baa7bb9bbb0b424c565471cb1205f440c6ee6a", + "9fb9934dfe60ff43436ebf702bbf055b3af7dc7544ceefcc48a0fba5e4777a65", + "79f2841de72244001c83b4aaacba8676655621e254f26fca8feb83233644d12c", + "4ce4ac5a51879fa88298503074e207b345b535d9ecbb6f660ae46e919cec8252", + "de994629b0d88eddff6f4a137f4745f098938b3e8daa5db090b55a8d79db9776", + "f9fbffd43354e65f05e1e3cbf40999e814801651b0da5af53ec2150065e5bba5", + "94b147f4856eca4c66733908e364062de8acf5ee74dca29926b35c31d4fc3739", + "dc9fb54950b4505553c79505d64f0108f1b7af287e8b909425c17deb7e2d6af4", + "e62c543c991fd273357d5bbafcced8d884777d626e7053a2154fabadedd96d69", + "0a5fae6a5a23b95f0fb70bc22b5e38c8c4aacfc476f4214077236315ceb54ce4", + "b612f632c655bbcdc5fd1732e7fdf96c2b18890d812a5218d6bf5dca9394e123", + "cadef4aa0ae961b99f4bbb3d11314b8855377156a22341f6fffadef214479e79", + "8264ebfd62f23fd2953a5b36a574dd3b5b08bd1db21f438ad5415d6b2fbd13bf", + "fd6aef8c5547610005739747971968f39adb521c72e7a40915e2be7398bfc902", + "54b2bd63fa155cb08cb81adea31920845e59a1b0d02e57a63c14c9cf4704de65", + "aa1cf55fca00a814a8bf956641b09585ea31dd10776a6dbc9257fb53ba29e54b", ] `); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(26); + expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(25); expect(digest).toBe(credentialRoot); }); }); diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index b2d4da10..3d16c26c 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -25,6 +25,23 @@ }, "description": "A data schema that provide verifiers with enough information to determine whether the provided data conforms to the provided schema(s). More information in https://www.w3.org/TR/vc-data-model-2.0/#data-schemas" }, + "renderMethod": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri", + "description": "URL of a decentralised renderer to render this document", + "examples": ["https://demo-renderer.openattestation.com"] + }, + "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationEmbeddedRenderer"] }, + "templateName": { + "type": "string", + "description": "Template name to be use by template renderer to determine the template to use" + } + }, + "required": ["id", "name"] + }, "credentialSubject": { "type": "object", "properties": { @@ -139,23 +156,7 @@ }, "additionalProperties": false }, - "renderMethod": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URL of a decentralised renderer to render this document", - "examples": ["https://demo-renderer.openattestation.com"] - }, - "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationEmbeddedRenderer"] }, - "templateName": { - "type": "string", - "description": "Template name to be use by template renderer to determine the template to use" - } - }, - "required": ["id", "name"] - }, + "renderMethod": { "type": "array", "items": { "$ref": "#/definitions/renderMethod" } }, "credentialSubject": { "anyOf": [ { "$ref": "#/definitions/credentialSubject" }, diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index df7d8dd5..50670550 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -132,7 +132,7 @@ export const getTemplateURL = (document: any): string | undefined => { } else if (isRawV3Document(document) || isWrappedV3Document(document)) { return document.openAttestationMetadata.template?.url; } else if (isRawV4Document(document) || isWrappedV4Document(document)) { - return document.renderMethod?.id; + return document.renderMethod && document.renderMethod[0].id; } throw new Error( diff --git a/test/fixtures/v4/did-raw.json b/test/fixtures/v4/did-raw.json index 2fd3987c..77e5a232 100644 --- a/test/fixtures/v4/did-raw.json +++ b/test/fixtures/v4/did-raw.json @@ -16,11 +16,13 @@ "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "name": "GOVTECH_DEMO" - }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "name": "GOVTECH_DEMO" + } + ], "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", "type": ["DriversLicense"], diff --git a/test/fixtures/v4/did-signed-wrapped.json b/test/fixtures/v4/did-signed-wrapped.json index ca1f87f1..d6e3e9db 100644 --- a/test/fixtures/v4/did-signed-wrapped.json +++ b/test/fixtures/v4/did-signed-wrapped.json @@ -13,11 +13,9 @@ "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "name": "GOVTECH_DEMO" - }, + "renderMethod": [ + { "id": "https://demo-renderer.opencerts.io", "type": "OpenAttestationEmbeddedRenderer", "name": "GOVTECH_DEMO" } + ], "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", "type": ["DriversLicense"], @@ -38,12 +36,12 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", + "targetHash": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", "proofs": [], - "merkleRoot": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", - "salts": "W3sidmFsdWUiOiJkNTRkYzlkNDc3MDVjNjFmOGRhYTJiMzkzNmQwMDk3YjM5YWI5NzA1MmQxNDAwY2U2ODI1NDhhMzI1NzA5ZmY1IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjJiMjEyN2I3NTc4MTBhMjNiNDNlZWIyNWI3NDIxZTc5YjllM2FmYjY0NDI2YTkyNjY0ODJlYmUwY2EzOTI0NDAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMGFlMTA4NDUwYmZlYzlkYWRmY2MwNjM3MTRiYjQ4NTRlZDI1ZDQxZDFkOTg3ZDRlYzc5YWE0ZmFiM2VlOTJmMyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjZkM2ZhYzI3YzJlZjE3NjJhODRkM2MyMjAwYTcwOGRjMjBjZDZiYjZhNzAyOTBlMzE2ZTUwNGJkZjBmMDQ0YTIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI1MDNkYjI4ZTk5YmNjMDgwMDE2ZWExMzZkNTJmYzJjZTZiMzczZmFhMTc3MzAxNjRiYjRlNDc2MDlhYzg2ZGEyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJiYjljMzU4MGFmZTA4NmEwYzljNmE2ZGI1YzhjYjI0OWY0YWYyZGQ3NDI1YzJmMjViNGZlMzY5NzE5MzdiNDI5IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDgyNmI1MzhkNjgzNGJhM2FmZmE2ZTc3MmE4Yzk0NTFkMWMzNzI5NTY0ODExMDRlOGQyNDU4MDE0ZjIyYTU4ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiODM4YTFjNzQ3NDk1ODY4MWVjOTY1ZjBjYjljMjRhZTIyMWU2ZTQ3N2MxMTk4MDhhYTNkMTQzOTU5ZmRkZWM2OCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyNTE5ZTc0YTY0MDc4OWRkMmU2MDYyYjYzNGI3OWI1YzVhOGQxYTQwMGU3ZTQxNWY0OWYzMGZhNjk5NGQ4ZTY1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjQ4OTI1NzE0N2VkZjU4N2UxYmM3NmUwZWQ0NjIzZGFhYzMwMmQ3ZTRjNmEwMTVmNzY3YjlkMTJhNDhkMjU5MjUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYTA1MWY5MGNhNWIzNzY4NDAyMzRmN2E3M2JlMGU2NmE0YTY5M2FkODFhN2EwM2E1NzQ0MzAwZjZlZTI2YWY5NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI2YmEwZDNmOTVjNzFhMzliYTM0M2UzZWRjODU4N2NjMjQwOGUzZTBmMTM3OGZiNzYzNDA4MGI1ZDkzMjMyMGMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJkZjQ5MmU4YTA4NzZhNmMzYTA1M2RjOTkyOGRmNjBkMTA0NTNhMTZmYTcxZDY5YTdlNTU4ZTg4ZDZmMWZjNDM0IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJlYmI5NDk5MjI0YWJhYjFlOTM2OGM1ZjY4NGVhYWU3ODU0OTJlYmJiYzQyNmVhN2NjM2RhZGY4MjA0YWRkZGNjIiwicGF0aCI6InJlbmRlck1ldGhvZC5pZCJ9LHsidmFsdWUiOiIzN2I1ZDdhMmJkMDRiMTg5MmM4NTVlZjczYjZiNzExYTc5NmUzODQyNGI1ZWJkOWM2MDcyYjQ5Y2Y0M2IzNDRjIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjQ3NTVmZTc4OGNhOGQyYTlmNjdkOTBjYjc0MWZmY2VlMThjNGNjZDg4Njg0ZWE0YmUyYmViMDBhNjViNThhNmQiLCJwYXRoIjoicmVuZGVyTWV0aG9kLm5hbWUifSx7InZhbHVlIjoiODgyNjA1YzU0OTBhZjcxN2E4ZGVkNzYyMTlkOTRlNmI1ZjYzZTY0NGQ4MTJkODNmN2EyZjc2Y2I3YjI5MzQ3OCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI0ZGE2NTE2MjU5YjQyMDkzYjhhY2MzMjVlMjVkNzlhZjYxYWQyOTVhMmY1NzIxMmQxYWM2MzE5ZTg2MWZlNTU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMzg4ODllZjMzMjc4MDg5MjE3MzcxMzVkMzZkYWNjNWZlYWYzNThiYjE5Mjk1ODlhMzIxMmI5NGY4ZTJlOGRjMiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNkZTc5Yjc3NjRjNTA3MzBiNWEzZTQzZDUzMWJhMTljZDg4Njg1OTZlMzk4N2EyNjRjNjI5NmZlYWQ5NTkwZDEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiMzgyNzRmNDA0Nzc3YThhYjEzMzVkODFiYTJmYjAyNzFiYjg0OTE0ODc4YzMzYmI4Y2RhMzYzNGFmNmIyZmJlYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJmYjA2NWM2MjVhZDE0Y2Y1Mzg0MzAwNGQwMDllYzNjNTg3MGJmYzdlMjA1YzRjMTMzOGVkMzU2NmVhY2MwOTEyIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjM0YWQzODRlNjZhZWE2YWRmMTk1MDU3ZGUyM2Q4Yzk1MzFhZTMwYjdkNzRkMjJkMzYxYjcxOTRkM2EzODRjMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJhZjM1ZGU5ZGY5MDg4ODg3NmQ2YjVkMjI2YjkyZDYxZTgyMzgyNzMyMDY4YTNlMTExMmJmZWYwMDcxZmVhZmNmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjA1MDliZWFhZmUxNTA0NDhmMmQ5ZGYwNmZmMmY5OWU4NWIwMjcwNjJjMWJkMzc5MzkwMDViZmZjMWM5NjI3ZDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "merkleRoot": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", + "salts": "W3sidmFsdWUiOiI5MzFiNGVlNDAzODVhZTQ2MmM1OTg3MTJlOWY0MDBjNDZhNDliZGYxNzkyMTI0M2ZlOGZjNmFkZGI4ZDYwZDBjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjkzYWRlMTZiNzgyYjExZjc0MGYzYmIyMWJiOTcxYzQ2YWQwNWExYjk2OTQxZWUyYmZjMzIzOWFhOTFmODRiYjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTBlYTMxN2Y4NjhjNDVmNzY2N2FlOTViYzA3ODUzNmQwOWNlZmUzMGQ3NjYxYWRmNTVmOTQ3YzFjYjY1ZGYzZCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3OTliZmE5ZDRlYWJhOTkxMzYxZjFkNTlkODQ0ZmY0M2Q4MmM3OTEwN2MxNTM2OGIwN2MyNjVhOTkzODRlNDUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI4YWY2MTQ1ZDA5NzZiNTY2MmQ4ZDgwMzRjZTQxNDBjNjhmNjc0MzIwNzgxMmJjZTc3ODU3YzRjYzIyZGZhNGZkIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI0YTkxZjVmYzRmYzIwZjM2N2RlYjFkM2Q2NWM4NGMxYzA2OTMzMDQ2ZTM1ZmRiMTJkZDliODM4MzM0NmVjNTNkIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzZiNTFlMzFiZTFkMDA3MDBlYzNhYzg5ZjgwN2FiMjAyODE4YjFkNmRkN2Y1N2RhNWVmNWYyNjVhZDY1OTFiMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNTRmZWM3NzVjYTEwODdkOGVmNzM2NDFmOGZkNTBkNTUxMzI2NmE5M2FkZGFmNmQzMGE4NGY2YWY5ODcwZjVlYyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNDJjMzVjMGJiNDI1OTI4YzA4MGJiNzAxZjUwZmJmM2I5NjJkNWQ2ZWU5ZGJjMDczMDM1NGFjYTM0MWY0YWU5IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjFmZjUwYjA3ZWZmNWU0OWQyY2RkMmVjMjk1NjBlM2ZhOTI4ODE2NGE4OGMwMTJjNDVmZWQ3MTE0NWUxMDM0ZjciLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMWNjOTVmODQyNjU4NWE4YTViNTQzNDQzMmY0NGVkODFjMmY4MGIzYTgyZTg1NTM4ZmNmY2I3NWM5Y2M2YjBhMiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjdiYzYwMjNhNDEwYjhkZTRlMTEzYjdjNGM3NWVhZjkwNzczYTY4NGM4NjQyY2NkOTkyZDU5ZjMxODhiMjM4MjciLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiI4ZGNlMTlhYWQ2Yzg3YmIyMjUyZDFmZmFiNTdkNTk2Y2M2OGQxOGNiYmQyYTE2YzM0NDhjOGFmODgyNmE4MjY3IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiIxZTExNDk3Njk3NGFkYTg1NDhjMWNiZjA2NGE0YmMxZGI1MWFlNTNhMDEzOWFkNjdlZDg1ZjU3Y2YwNTM1YjFlIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI1Y2YwYWQzM2QzYzRiYzY0NWRjYjQ2MzY4OTM1ODRjZTBiNGFhNDM4N2E5ZDMyMjlkMjhmNzJmNzcwMGJhZGYzIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjAxMGU4NDc2ZTU5NzBmMDk1YWNhNjA0NzE4OGZiMWRlNmEyYWJmY2Q1YjI5NzRkNjI4MTQwNTljNzIzZjIwYjkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLm5hbWUifSx7InZhbHVlIjoiMzdjMWQzZTNhZTI3ZDA2ZmY4MDg1NWZjMWEwOWYxMWRhMzc0NTNkNzE0MTI5MGY3NWYxNmYxYzlmMzBmZjlkOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI4OWEzN2YxNTJkMmM3NWYzODBkYjYzYjQ5M2M5YmJhZGFjMzdiNjlhMWNmMjdkZGE0MjNlZWFiODk1YWRkNTg5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiZGI3MTVhYTA4MDIyM2IwNTZhNGFiMDcyZDJiMThkMTBlOGRjMTMyN2Y0ODdjYjQ1ZGNiYjU2NjQ1MWFmNjFlNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjIyNjI1MGI4ZmFmZTY2MDhjMmQ5NDIxYjc1ZTAwZGEzOTEyNGY5YTZlMjg3ZWRiNTA2MzNlZmQ3ZmFmODMwMTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYjgzY2RhNTA5YTE1YTIxMDljNDUxMjljOTM0Mjg1NzY0OTY1NDk4ZGU4NmMzYjBlZTg5YzU1Y2Q1MmI4ZGU0NSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjNjNjNzM1NTNlZThlMTZlODQwMTFlNjQwNTI1NDkwNmMzZjc3NjA2ZWQzZmI4YTBkNWE5OTExNzE5YzIwMGZmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNmU2OWVhZGIzNGVlMmFhY2E0MjAxZWU1MDlmYzQ4MzZkY2IxZjdiMjA3MTE4N2UzZDYwMGEyNmZjZTlhYTU4NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiIxMjU5N2QwNTcwNTlkODBmYjM0MWIxNmJhZWFmNmQ1ZGQxMmJjZWY3MGU1ZDFiZDQ2MmE5NGFkNDE3OTc5NTVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjRjNjE2Y2I0OTRiNGJjZDE4NGI5OTAzNmMyMmYzODQyYTllYjE4YTdjZDE2Y2Y5NzMxZTZjOGI5Yzk4MThlMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", "privacy": { "obfuscated": [] }, "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0xe9c534bdace23bcd572f8ef64e2131a02bb124f4cc0a42e50a5e222ab7fd177e1da7bf04c25abc662b22000596d74ce74cd9c70330b7932f1681898c1ad10c891c" + "signature": "0x72fa7bb7ed20879141f4d04596430ab679b0f3be61a0ef513f59f42f993c160c0fd791dac6dd193c7d8c02adeed1fe7585b924fe849d6751f275f9051ceb8b651c" } } diff --git a/test/fixtures/v4/did-wrapped.json b/test/fixtures/v4/did-wrapped.json index 2a415f10..23a0c379 100644 --- a/test/fixtures/v4/did-wrapped.json +++ b/test/fixtures/v4/did-wrapped.json @@ -13,11 +13,9 @@ "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "name": "GOVTECH_DEMO" - }, + "renderMethod": [ + { "id": "https://demo-renderer.opencerts.io", "type": "OpenAttestationEmbeddedRenderer", "name": "GOVTECH_DEMO" } + ], "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", "type": ["DriversLicense"], @@ -38,10 +36,10 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", + "targetHash": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", "proofs": [], - "merkleRoot": "9dbda2b8223c13a107b1bf30223f97372d2eedba78f2fa1143dfc7b38b552fd5", - "salts": "W3sidmFsdWUiOiJkNTRkYzlkNDc3MDVjNjFmOGRhYTJiMzkzNmQwMDk3YjM5YWI5NzA1MmQxNDAwY2U2ODI1NDhhMzI1NzA5ZmY1IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjJiMjEyN2I3NTc4MTBhMjNiNDNlZWIyNWI3NDIxZTc5YjllM2FmYjY0NDI2YTkyNjY0ODJlYmUwY2EzOTI0NDAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMGFlMTA4NDUwYmZlYzlkYWRmY2MwNjM3MTRiYjQ4NTRlZDI1ZDQxZDFkOTg3ZDRlYzc5YWE0ZmFiM2VlOTJmMyIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjZkM2ZhYzI3YzJlZjE3NjJhODRkM2MyMjAwYTcwOGRjMjBjZDZiYjZhNzAyOTBlMzE2ZTUwNGJkZjBmMDQ0YTIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI1MDNkYjI4ZTk5YmNjMDgwMDE2ZWExMzZkNTJmYzJjZTZiMzczZmFhMTc3MzAxNjRiYjRlNDc2MDlhYzg2ZGEyIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJiYjljMzU4MGFmZTA4NmEwYzljNmE2ZGI1YzhjYjI0OWY0YWYyZGQ3NDI1YzJmMjViNGZlMzY5NzE5MzdiNDI5IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDgyNmI1MzhkNjgzNGJhM2FmZmE2ZTc3MmE4Yzk0NTFkMWMzNzI5NTY0ODExMDRlOGQyNDU4MDE0ZjIyYTU4ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiODM4YTFjNzQ3NDk1ODY4MWVjOTY1ZjBjYjljMjRhZTIyMWU2ZTQ3N2MxMTk4MDhhYTNkMTQzOTU5ZmRkZWM2OCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyNTE5ZTc0YTY0MDc4OWRkMmU2MDYyYjYzNGI3OWI1YzVhOGQxYTQwMGU3ZTQxNWY0OWYzMGZhNjk5NGQ4ZTY1IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjQ4OTI1NzE0N2VkZjU4N2UxYmM3NmUwZWQ0NjIzZGFhYzMwMmQ3ZTRjNmEwMTVmNzY3YjlkMTJhNDhkMjU5MjUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYTA1MWY5MGNhNWIzNzY4NDAyMzRmN2E3M2JlMGU2NmE0YTY5M2FkODFhN2EwM2E1NzQ0MzAwZjZlZTI2YWY5NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI2YmEwZDNmOTVjNzFhMzliYTM0M2UzZWRjODU4N2NjMjQwOGUzZTBmMTM3OGZiNzYzNDA4MGI1ZDkzMjMyMGMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJkZjQ5MmU4YTA4NzZhNmMzYTA1M2RjOTkyOGRmNjBkMTA0NTNhMTZmYTcxZDY5YTdlNTU4ZTg4ZDZmMWZjNDM0IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJlYmI5NDk5MjI0YWJhYjFlOTM2OGM1ZjY4NGVhYWU3ODU0OTJlYmJiYzQyNmVhN2NjM2RhZGY4MjA0YWRkZGNjIiwicGF0aCI6InJlbmRlck1ldGhvZC5pZCJ9LHsidmFsdWUiOiIzN2I1ZDdhMmJkMDRiMTg5MmM4NTVlZjczYjZiNzExYTc5NmUzODQyNGI1ZWJkOWM2MDcyYjQ5Y2Y0M2IzNDRjIiwicGF0aCI6InJlbmRlck1ldGhvZC50eXBlIn0seyJ2YWx1ZSI6IjQ3NTVmZTc4OGNhOGQyYTlmNjdkOTBjYjc0MWZmY2VlMThjNGNjZDg4Njg0ZWE0YmUyYmViMDBhNjViNThhNmQiLCJwYXRoIjoicmVuZGVyTWV0aG9kLm5hbWUifSx7InZhbHVlIjoiODgyNjA1YzU0OTBhZjcxN2E4ZGVkNzYyMTlkOTRlNmI1ZjYzZTY0NGQ4MTJkODNmN2EyZjc2Y2I3YjI5MzQ3OCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI0ZGE2NTE2MjU5YjQyMDkzYjhhY2MzMjVlMjVkNzlhZjYxYWQyOTVhMmY1NzIxMmQxYWM2MzE5ZTg2MWZlNTU1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiMzg4ODllZjMzMjc4MDg5MjE3MzcxMzVkMzZkYWNjNWZlYWYzNThiYjE5Mjk1ODlhMzIxMmI5NGY4ZTJlOGRjMiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjNkZTc5Yjc3NjRjNTA3MzBiNWEzZTQzZDUzMWJhMTljZDg4Njg1OTZlMzk4N2EyNjRjNjI5NmZlYWQ5NTkwZDEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiMzgyNzRmNDA0Nzc3YThhYjEzMzVkODFiYTJmYjAyNzFiYjg0OTE0ODc4YzMzYmI4Y2RhMzYzNGFmNmIyZmJlYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJmYjA2NWM2MjVhZDE0Y2Y1Mzg0MzAwNGQwMDllYzNjNTg3MGJmYzdlMjA1YzRjMTMzOGVkMzU2NmVhY2MwOTEyIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjM0YWQzODRlNjZhZWE2YWRmMTk1MDU3ZGUyM2Q4Yzk1MzFhZTMwYjdkNzRkMjJkMzYxYjcxOTRkM2EzODRjMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiJhZjM1ZGU5ZGY5MDg4ODg3NmQ2YjVkMjI2YjkyZDYxZTgyMzgyNzMyMDY4YTNlMTExMmJmZWYwMDcxZmVhZmNmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjA1MDliZWFhZmUxNTA0NDhmMmQ5ZGYwNmZmMmY5OWU4NWIwMjcwNjJjMWJkMzc5MzkwMDViZmZjMWM5NjI3ZDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "merkleRoot": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", + "salts": "W3sidmFsdWUiOiI5MzFiNGVlNDAzODVhZTQ2MmM1OTg3MTJlOWY0MDBjNDZhNDliZGYxNzkyMTI0M2ZlOGZjNmFkZGI4ZDYwZDBjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjkzYWRlMTZiNzgyYjExZjc0MGYzYmIyMWJiOTcxYzQ2YWQwNWExYjk2OTQxZWUyYmZjMzIzOWFhOTFmODRiYjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTBlYTMxN2Y4NjhjNDVmNzY2N2FlOTViYzA3ODUzNmQwOWNlZmUzMGQ3NjYxYWRmNTVmOTQ3YzFjYjY1ZGYzZCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3OTliZmE5ZDRlYWJhOTkxMzYxZjFkNTlkODQ0ZmY0M2Q4MmM3OTEwN2MxNTM2OGIwN2MyNjVhOTkzODRlNDUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI4YWY2MTQ1ZDA5NzZiNTY2MmQ4ZDgwMzRjZTQxNDBjNjhmNjc0MzIwNzgxMmJjZTc3ODU3YzRjYzIyZGZhNGZkIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI0YTkxZjVmYzRmYzIwZjM2N2RlYjFkM2Q2NWM4NGMxYzA2OTMzMDQ2ZTM1ZmRiMTJkZDliODM4MzM0NmVjNTNkIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzZiNTFlMzFiZTFkMDA3MDBlYzNhYzg5ZjgwN2FiMjAyODE4YjFkNmRkN2Y1N2RhNWVmNWYyNjVhZDY1OTFiMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNTRmZWM3NzVjYTEwODdkOGVmNzM2NDFmOGZkNTBkNTUxMzI2NmE5M2FkZGFmNmQzMGE4NGY2YWY5ODcwZjVlYyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNDJjMzVjMGJiNDI1OTI4YzA4MGJiNzAxZjUwZmJmM2I5NjJkNWQ2ZWU5ZGJjMDczMDM1NGFjYTM0MWY0YWU5IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjFmZjUwYjA3ZWZmNWU0OWQyY2RkMmVjMjk1NjBlM2ZhOTI4ODE2NGE4OGMwMTJjNDVmZWQ3MTE0NWUxMDM0ZjciLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMWNjOTVmODQyNjU4NWE4YTViNTQzNDQzMmY0NGVkODFjMmY4MGIzYTgyZTg1NTM4ZmNmY2I3NWM5Y2M2YjBhMiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjdiYzYwMjNhNDEwYjhkZTRlMTEzYjdjNGM3NWVhZjkwNzczYTY4NGM4NjQyY2NkOTkyZDU5ZjMxODhiMjM4MjciLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiI4ZGNlMTlhYWQ2Yzg3YmIyMjUyZDFmZmFiNTdkNTk2Y2M2OGQxOGNiYmQyYTE2YzM0NDhjOGFmODgyNmE4MjY3IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiIxZTExNDk3Njk3NGFkYTg1NDhjMWNiZjA2NGE0YmMxZGI1MWFlNTNhMDEzOWFkNjdlZDg1ZjU3Y2YwNTM1YjFlIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI1Y2YwYWQzM2QzYzRiYzY0NWRjYjQ2MzY4OTM1ODRjZTBiNGFhNDM4N2E5ZDMyMjlkMjhmNzJmNzcwMGJhZGYzIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjAxMGU4NDc2ZTU5NzBmMDk1YWNhNjA0NzE4OGZiMWRlNmEyYWJmY2Q1YjI5NzRkNjI4MTQwNTljNzIzZjIwYjkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLm5hbWUifSx7InZhbHVlIjoiMzdjMWQzZTNhZTI3ZDA2ZmY4MDg1NWZjMWEwOWYxMWRhMzc0NTNkNzE0MTI5MGY3NWYxNmYxYzlmMzBmZjlkOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI4OWEzN2YxNTJkMmM3NWYzODBkYjYzYjQ5M2M5YmJhZGFjMzdiNjlhMWNmMjdkZGE0MjNlZWFiODk1YWRkNTg5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiZGI3MTVhYTA4MDIyM2IwNTZhNGFiMDcyZDJiMThkMTBlOGRjMTMyN2Y0ODdjYjQ1ZGNiYjU2NjQ1MWFmNjFlNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjIyNjI1MGI4ZmFmZTY2MDhjMmQ5NDIxYjc1ZTAwZGEzOTEyNGY5YTZlMjg3ZWRiNTA2MzNlZmQ3ZmFmODMwMTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYjgzY2RhNTA5YTE1YTIxMDljNDUxMjljOTM0Mjg1NzY0OTY1NDk4ZGU4NmMzYjBlZTg5YzU1Y2Q1MmI4ZGU0NSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjNjNjNzM1NTNlZThlMTZlODQwMTFlNjQwNTI1NDkwNmMzZjc3NjA2ZWQzZmI4YTBkNWE5OTExNzE5YzIwMGZmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNmU2OWVhZGIzNGVlMmFhY2E0MjAxZWU1MDlmYzQ4MzZkY2IxZjdiMjA3MTE4N2UzZDYwMGEyNmZjZTlhYTU4NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiIxMjU5N2QwNTcwNTlkODBmYjM0MWIxNmJhZWFmNmQ1ZGQxMmJjZWY3MGU1ZDFiZDQ2MmE5NGFkNDE3OTc5NTVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjRjNjE2Y2I0OTRiNGJjZDE4NGI5OTAzNmMyMmYzODQyYTllYjE4YTdjZDE2Y2Y5NzMxZTZjOGI5Yzk4MThlMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", "privacy": { "obfuscated": [] } } } From 10c305ac411d65b8a9a2c81a2dcb96e0aa73ccba Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 17 Apr 2024 13:37:47 +0800 Subject: [PATCH 016/107] fix: remove json schema validation in v4.0 wrap --- src/4.0/wrap.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 9509fc81..488d7a0d 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,14 +1,12 @@ -import { hashToBuffer, isStringArray, SchemaValidationError } from "../shared/utils"; +import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; -import { ContextUrl, SchemaId } from "../shared/@types/document"; +import { ContextUrl } from "../shared/@types/document"; import { WrappedDocument } from "./types"; import { digestCredential } from "../4.0/digest"; -import { validateSchema } from "../shared/validate"; import { WrapDocumentOptionV4 } from "../shared/@types/wrap"; import { OpenAttestationDocument, ProofPurpose } from "../__generated__/schema.4.0"; import { encodeSalt, salt } from "./salt"; import { interpretContexts, vcSchema } from "./validate"; -import { getSchema } from "../shared/ajv"; export const wrapDocument = async ( credential: T, @@ -74,11 +72,6 @@ export const wrapDocument = async ( }, }; - const errors = validateSchema(verifiableCredential, getSchema(SchemaId.v4)); - if (errors.length > 0) { - throw new SchemaValidationError("Invalid document", errors, verifiableCredential); - } - return verifiableCredential; }; From c86d710f9169f2cbb3673caf45e7dfc6e5448650 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Fri, 19 Apr 2024 11:08:43 +0800 Subject: [PATCH 017/107] test: update v4.0 fixtures --- test/fixtures/v4/did-raw-ocsp.json | 43 ++++++++++++++++ test/fixtures/v4/did-raw.json | 6 +-- .../v4/did-signed-wrapped-obfuscated.json | 47 +++++++++++++++++ test/fixtures/v4/did-signed-wrapped-oscp.json | 51 +++++++++++++++++++ test/fixtures/v4/did-signed-wrapped.json | 15 +++--- test/fixtures/v4/did-wrapped.json | 45 ---------------- 6 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 test/fixtures/v4/did-raw-ocsp.json create mode 100644 test/fixtures/v4/did-signed-wrapped-obfuscated.json create mode 100644 test/fixtures/v4/did-signed-wrapped-oscp.json delete mode 100644 test/fixtures/v4/did-wrapped.json diff --git a/test/fixtures/v4/did-raw-ocsp.json b/test/fixtures/v4/did-raw-ocsp.json new file mode 100644 index 00000000..71861e32 --- /dev/null +++ b/test/fixtures/v4/did-raw-ocsp.json @@ -0,0 +1,43 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "type": ["VerifiableCredential", "OpenAttestationCredential"], + "validFrom": "2021-03-08T12:00:00+08:00", + "name": "Republic of Singapore Driving Licence", + "issuer": { + "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } + }, + "credentialStatus": { + "id": "https://ocsp-sandbox.openattestation.com", + "type": "OpenAttestationOcspResponder" + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": ["DriversLicense"], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + } +} diff --git a/test/fixtures/v4/did-raw.json b/test/fixtures/v4/did-raw.json index 77e5a232..5f3a9860 100644 --- a/test/fixtures/v4/did-raw.json +++ b/test/fixtures/v4/did-raw.json @@ -12,15 +12,11 @@ "name": "Government Technology Agency of Singapore (GovTech)", "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, - "credentialStatus": { - "id": "https://ocsp-sandbox.openattestation.com", - "type": "OpenAttestationOcspResponder" - }, "renderMethod": [ { "id": "https://demo-renderer.opencerts.io", "type": "OpenAttestationEmbeddedRenderer", - "name": "GOVTECH_DEMO" + "templateName": "GOVTECH_DEMO" } ], "credentialSubject": { diff --git a/test/fixtures/v4/did-signed-wrapped-obfuscated.json b/test/fixtures/v4/did-signed-wrapped-obfuscated.json new file mode 100644 index 00000000..e983f7ff --- /dev/null +++ b/test/fixtures/v4/did-signed-wrapped-obfuscated.json @@ -0,0 +1,47 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "type": ["VerifiableCredential", "OpenAttestationCredential"], + "validFrom": "2021-03-08T12:00:00+08:00", + "name": "Republic of Singapore Driving Licence", + "issuer": { + "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } + }, + "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": ["DriversLicense"], + "name": "John Doe", + "licenses": [ + { "class": "3", "effectiveDate": "2013-05-16T00:00:00+08:00" }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + "proofs": [], + "merkleRoot": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + "salts": "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2E1ZTExMzdhYmY3OGYzMzViMzNlNjA3YmZmNmU4YWZkM2YyZTJkOTc2YWZiOWM1OGE1Nzk4YmEwZTllYmVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYmY2YWI5YzQ2ZTVmZjBkNzM1Nzc2MjUxOTQ1NmFlYmI1YTU1NGJjNDg0NTRmN2EyYTU2NTdiNGIyODQ3ZTFmMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiI1NTJmOGQ0Y2MxMjRmYzIyYjQxM2I4YjYzYzBhZjcwZWI0MDgzNDYxOWMxNDRjYWZmYWI3MDFmMGE1MzYzMmJjIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImJhZWE5NWEyYmVlNTU3NWRlY2UzM2ZiZjFhODBhZWYxMmY2NWExYjY4NGRkMjQ0ZjZmNThmMjJmYjJiYWQ4N2UiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "privacy": { "obfuscated": ["c866663b38353fdb46a372cd0302fd7752cc75bee4b8fc4d65c2e3b22f2466f7"] }, + "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + "signature": "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b" + } +} diff --git a/test/fixtures/v4/did-signed-wrapped-oscp.json b/test/fixtures/v4/did-signed-wrapped-oscp.json new file mode 100644 index 00000000..26ccc20c --- /dev/null +++ b/test/fixtures/v4/did-signed-wrapped-oscp.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "type": ["VerifiableCredential", "OpenAttestationCredential"], + "validFrom": "2021-03-08T12:00:00+08:00", + "name": "Republic of Singapore Driving Licence", + "issuer": { + "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } + }, + "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": ["DriversLicense"], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + "proofs": [], + "merkleRoot": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + "salts": "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJkZjNmMjY5NzE5YTczYzA1NDc4Y2Q1YTk0MTQwMDVmMjA2NWNhNWNhODg5NGUwNjc2NDllYmEwMzcyNjBmNDgzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImNjYTVlMTEzN2FiZjc4ZjMzNWIzM2U2MDdiZmY2ZThhZmQzZjJlMmQ5NzZhZmI5YzU4YTU3OThiYTBlOWViZWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiJiZjZhYjljNDZlNWZmMGQ3MzU3NzYyNTE5NDU2YWViYjVhNTU0YmM0ODQ1NGY3YTJhNTY1N2I0YjI4NDdlMWYxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6IjU1MmY4ZDRjYzEyNGZjMjJiNDEzYjhiNjNjMGFmNzBlYjQwODM0NjE5YzE0NGNhZmZhYjcwMWYwYTUzNjMyYmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYmFlYTk1YTJiZWU1NTc1ZGVjZTMzZmJmMWE4MGFlZjEyZjY1YTFiNjg0ZGQyNDRmNmY1OGYyMmZiMmJhZDg3ZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn1d", + "privacy": { "obfuscated": [] }, + "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + "signature": "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b" + } +} diff --git a/test/fixtures/v4/did-signed-wrapped.json b/test/fixtures/v4/did-signed-wrapped.json index d6e3e9db..3e0ab737 100644 --- a/test/fixtures/v4/did-signed-wrapped.json +++ b/test/fixtures/v4/did-signed-wrapped.json @@ -12,9 +12,12 @@ "name": "Government Technology Agency of Singapore (GovTech)", "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } }, - "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, "renderMethod": [ - { "id": "https://demo-renderer.opencerts.io", "type": "OpenAttestationEmbeddedRenderer", "name": "GOVTECH_DEMO" } + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } ], "credentialSubject": { "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", @@ -36,12 +39,12 @@ "proof": { "type": "OpenAttestationMerkleProofSignature2018", "proofPurpose": "assertionMethod", - "targetHash": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", + "targetHash": "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", "proofs": [], - "merkleRoot": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", - "salts": "W3sidmFsdWUiOiI5MzFiNGVlNDAzODVhZTQ2MmM1OTg3MTJlOWY0MDBjNDZhNDliZGYxNzkyMTI0M2ZlOGZjNmFkZGI4ZDYwZDBjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjkzYWRlMTZiNzgyYjExZjc0MGYzYmIyMWJiOTcxYzQ2YWQwNWExYjk2OTQxZWUyYmZjMzIzOWFhOTFmODRiYjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTBlYTMxN2Y4NjhjNDVmNzY2N2FlOTViYzA3ODUzNmQwOWNlZmUzMGQ3NjYxYWRmNTVmOTQ3YzFjYjY1ZGYzZCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3OTliZmE5ZDRlYWJhOTkxMzYxZjFkNTlkODQ0ZmY0M2Q4MmM3OTEwN2MxNTM2OGIwN2MyNjVhOTkzODRlNDUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI4YWY2MTQ1ZDA5NzZiNTY2MmQ4ZDgwMzRjZTQxNDBjNjhmNjc0MzIwNzgxMmJjZTc3ODU3YzRjYzIyZGZhNGZkIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI0YTkxZjVmYzRmYzIwZjM2N2RlYjFkM2Q2NWM4NGMxYzA2OTMzMDQ2ZTM1ZmRiMTJkZDliODM4MzM0NmVjNTNkIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzZiNTFlMzFiZTFkMDA3MDBlYzNhYzg5ZjgwN2FiMjAyODE4YjFkNmRkN2Y1N2RhNWVmNWYyNjVhZDY1OTFiMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNTRmZWM3NzVjYTEwODdkOGVmNzM2NDFmOGZkNTBkNTUxMzI2NmE5M2FkZGFmNmQzMGE4NGY2YWY5ODcwZjVlYyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNDJjMzVjMGJiNDI1OTI4YzA4MGJiNzAxZjUwZmJmM2I5NjJkNWQ2ZWU5ZGJjMDczMDM1NGFjYTM0MWY0YWU5IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjFmZjUwYjA3ZWZmNWU0OWQyY2RkMmVjMjk1NjBlM2ZhOTI4ODE2NGE4OGMwMTJjNDVmZWQ3MTE0NWUxMDM0ZjciLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMWNjOTVmODQyNjU4NWE4YTViNTQzNDQzMmY0NGVkODFjMmY4MGIzYTgyZTg1NTM4ZmNmY2I3NWM5Y2M2YjBhMiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjdiYzYwMjNhNDEwYjhkZTRlMTEzYjdjNGM3NWVhZjkwNzczYTY4NGM4NjQyY2NkOTkyZDU5ZjMxODhiMjM4MjciLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiI4ZGNlMTlhYWQ2Yzg3YmIyMjUyZDFmZmFiNTdkNTk2Y2M2OGQxOGNiYmQyYTE2YzM0NDhjOGFmODgyNmE4MjY3IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiIxZTExNDk3Njk3NGFkYTg1NDhjMWNiZjA2NGE0YmMxZGI1MWFlNTNhMDEzOWFkNjdlZDg1ZjU3Y2YwNTM1YjFlIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI1Y2YwYWQzM2QzYzRiYzY0NWRjYjQ2MzY4OTM1ODRjZTBiNGFhNDM4N2E5ZDMyMjlkMjhmNzJmNzcwMGJhZGYzIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjAxMGU4NDc2ZTU5NzBmMDk1YWNhNjA0NzE4OGZiMWRlNmEyYWJmY2Q1YjI5NzRkNjI4MTQwNTljNzIzZjIwYjkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLm5hbWUifSx7InZhbHVlIjoiMzdjMWQzZTNhZTI3ZDA2ZmY4MDg1NWZjMWEwOWYxMWRhMzc0NTNkNzE0MTI5MGY3NWYxNmYxYzlmMzBmZjlkOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI4OWEzN2YxNTJkMmM3NWYzODBkYjYzYjQ5M2M5YmJhZGFjMzdiNjlhMWNmMjdkZGE0MjNlZWFiODk1YWRkNTg5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiZGI3MTVhYTA4MDIyM2IwNTZhNGFiMDcyZDJiMThkMTBlOGRjMTMyN2Y0ODdjYjQ1ZGNiYjU2NjQ1MWFmNjFlNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjIyNjI1MGI4ZmFmZTY2MDhjMmQ5NDIxYjc1ZTAwZGEzOTEyNGY5YTZlMjg3ZWRiNTA2MzNlZmQ3ZmFmODMwMTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYjgzY2RhNTA5YTE1YTIxMDljNDUxMjljOTM0Mjg1NzY0OTY1NDk4ZGU4NmMzYjBlZTg5YzU1Y2Q1MmI4ZGU0NSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjNjNjNzM1NTNlZThlMTZlODQwMTFlNjQwNTI1NDkwNmMzZjc3NjA2ZWQzZmI4YTBkNWE5OTExNzE5YzIwMGZmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNmU2OWVhZGIzNGVlMmFhY2E0MjAxZWU1MDlmYzQ4MzZkY2IxZjdiMjA3MTE4N2UzZDYwMGEyNmZjZTlhYTU4NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiIxMjU5N2QwNTcwNTlkODBmYjM0MWIxNmJhZWFmNmQ1ZGQxMmJjZWY3MGU1ZDFiZDQ2MmE5NGFkNDE3OTc5NTVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjRjNjE2Y2I0OTRiNGJjZDE4NGI5OTAzNmMyMmYzODQyYTllYjE4YTdjZDE2Y2Y5NzMxZTZjOGI5Yzk4MThlMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + "merkleRoot": "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + "salts": "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", "privacy": { "obfuscated": [] }, "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0x72fa7bb7ed20879141f4d04596430ab679b0f3be61a0ef513f59f42f993c160c0fd791dac6dd193c7d8c02adeed1fe7585b924fe849d6751f275f9051ceb8b651c" + "signature": "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c" } } diff --git a/test/fixtures/v4/did-wrapped.json b/test/fixtures/v4/did-wrapped.json deleted file mode 100644 index 23a0c379..00000000 --- a/test/fixtures/v4/did-wrapped.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": [ - { "id": "https://demo-renderer.opencerts.io", "type": "OpenAttestationEmbeddedRenderer", "name": "GOVTECH_DEMO" } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { - "class": "3", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - }, - "proof": { - "type": "OpenAttestationMerkleProofSignature2018", - "proofPurpose": "assertionMethod", - "targetHash": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", - "proofs": [], - "merkleRoot": "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373", - "salts": "W3sidmFsdWUiOiI5MzFiNGVlNDAzODVhZTQ2MmM1OTg3MTJlOWY0MDBjNDZhNDliZGYxNzkyMTI0M2ZlOGZjNmFkZGI4ZDYwZDBjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjkzYWRlMTZiNzgyYjExZjc0MGYzYmIyMWJiOTcxYzQ2YWQwNWExYjk2OTQxZWUyYmZjMzIzOWFhOTFmODRiYjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiOTBlYTMxN2Y4NjhjNDVmNzY2N2FlOTViYzA3ODUzNmQwOWNlZmUzMGQ3NjYxYWRmNTVmOTQ3YzFjYjY1ZGYzZCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6IjU3OTliZmE5ZDRlYWJhOTkxMzYxZjFkNTlkODQ0ZmY0M2Q4MmM3OTEwN2MxNTM2OGIwN2MyNjVhOTkzODRlNDUiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI4YWY2MTQ1ZDA5NzZiNTY2MmQ4ZDgwMzRjZTQxNDBjNjhmNjc0MzIwNzgxMmJjZTc3ODU3YzRjYzIyZGZhNGZkIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI0YTkxZjVmYzRmYzIwZjM2N2RlYjFkM2Q2NWM4NGMxYzA2OTMzMDQ2ZTM1ZmRiMTJkZDliODM4MzM0NmVjNTNkIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMzZiNTFlMzFiZTFkMDA3MDBlYzNhYzg5ZjgwN2FiMjAyODE4YjFkNmRkN2Y1N2RhNWVmNWYyNjVhZDY1OTFiMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNTRmZWM3NzVjYTEwODdkOGVmNzM2NDFmOGZkNTBkNTUxMzI2NmE5M2FkZGFmNmQzMGE4NGY2YWY5ODcwZjVlYyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjNDJjMzVjMGJiNDI1OTI4YzA4MGJiNzAxZjUwZmJmM2I5NjJkNWQ2ZWU5ZGJjMDczMDM1NGFjYTM0MWY0YWU5IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjFmZjUwYjA3ZWZmNWU0OWQyY2RkMmVjMjk1NjBlM2ZhOTI4ODE2NGE4OGMwMTJjNDVmZWQ3MTE0NWUxMDM0ZjciLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMWNjOTVmODQyNjU4NWE4YTViNTQzNDQzMmY0NGVkODFjMmY4MGIzYTgyZTg1NTM4ZmNmY2I3NWM5Y2M2YjBhMiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjdiYzYwMjNhNDEwYjhkZTRlMTEzYjdjNGM3NWVhZjkwNzczYTY4NGM4NjQyY2NkOTkyZDU5ZjMxODhiMjM4MjciLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiI4ZGNlMTlhYWQ2Yzg3YmIyMjUyZDFmZmFiNTdkNTk2Y2M2OGQxOGNiYmQyYTE2YzM0NDhjOGFmODgyNmE4MjY3IiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiIxZTExNDk3Njk3NGFkYTg1NDhjMWNiZjA2NGE0YmMxZGI1MWFlNTNhMDEzOWFkNjdlZDg1ZjU3Y2YwNTM1YjFlIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI1Y2YwYWQzM2QzYzRiYzY0NWRjYjQ2MzY4OTM1ODRjZTBiNGFhNDM4N2E5ZDMyMjlkMjhmNzJmNzcwMGJhZGYzIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjAxMGU4NDc2ZTU5NzBmMDk1YWNhNjA0NzE4OGZiMWRlNmEyYWJmY2Q1YjI5NzRkNjI4MTQwNTljNzIzZjIwYjkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLm5hbWUifSx7InZhbHVlIjoiMzdjMWQzZTNhZTI3ZDA2ZmY4MDg1NWZjMWEwOWYxMWRhMzc0NTNkNzE0MTI5MGY3NWYxNmYxYzlmMzBmZjlkOSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5pZCJ9LHsidmFsdWUiOiI4OWEzN2YxNTJkMmM3NWYzODBkYjYzYjQ5M2M5YmJhZGFjMzdiNjlhMWNmMjdkZGE0MjNlZWFiODk1YWRkNTg5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LnR5cGVbMF0ifSx7InZhbHVlIjoiZGI3MTVhYTA4MDIyM2IwNTZhNGFiMDcyZDJiMThkMTBlOGRjMTMyN2Y0ODdjYjQ1ZGNiYjU2NjQ1MWFmNjFlNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5uYW1lIn0seyJ2YWx1ZSI6IjIyNjI1MGI4ZmFmZTY2MDhjMmQ5NDIxYjc1ZTAwZGEzOTEyNGY5YTZlMjg3ZWRiNTA2MzNlZmQ3ZmFmODMwMTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uY2xhc3MifSx7InZhbHVlIjoiYjgzY2RhNTA5YTE1YTIxMDljNDUxMjljOTM0Mjg1NzY0OTY1NDk4ZGU4NmMzYjBlZTg5YzU1Y2Q1MmI4ZGU0NSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjNjNjNzM1NTNlZThlMTZlODQwMTFlNjQwNTI1NDkwNmMzZjc3NjA2ZWQzZmI4YTBkNWE5OTExNzE5YzIwMGZmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNmU2OWVhZGIzNGVlMmFhY2E0MjAxZWU1MDlmYzQ4MzZkY2IxZjdiMjA3MTE4N2UzZDYwMGEyNmZjZTlhYTU4NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiIxMjU5N2QwNTcwNTlkODBmYjM0MWIxNmJhZWFmNmQ1ZGQxMmJjZWY3MGU1ZDFiZDQ2MmE5NGFkNDE3OTc5NTVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6IjRjNjE2Y2I0OTRiNGJjZDE4NGI5OTAzNmMyMmYzODQyYTllYjE4YTdjZDE2Y2Y5NzMxZTZjOGI5Yzk4MThlMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": [] } - } -} From e6fbb6bd10ef5d9f67e4e7baf86e2a2c98938826 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 19 Apr 2024 15:44:42 +0800 Subject: [PATCH 018/107] fix: digest test --- src/4.0/__tests__/digest.test.ts | 65 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index e668e6a7..612e6d14 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -3,11 +3,12 @@ import { digestCredential } from "../digest"; import { WrappedDocument } from "../../4.0/types"; import { obfuscateVerifiableCredential } from "../obfuscate"; import { decodeSalt } from "../salt"; -import sample from "../../../test/fixtures/v4/did-wrapped.json"; +import sample from "../../../test/fixtures/v4/did-signed-wrapped.json"; -const verifiableCredential = sample as WrappedDocument; +// TODO: remove unknown +const verifiableCredential = sample as unknown as WrappedDocument; // Digest will change whenever sample document is regenerated -const credentialRoot = "ea4a072f6b88861b0aa14baa04ab423d5d363d2e114adaa679f60aee9aaf8373"; +const credentialRoot = "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"; const { proof, ...credential } = verifiableCredential; @@ -29,7 +30,7 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "e521e95bb3adf4ded40a929af58996c33add5ec9a8d4d0d3db22a69aa29f8517", + "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", ] `); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(1); @@ -49,9 +50,9 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "dc9fb54950b4505553c79505d64f0108f1b7af287e8b909425c17deb7e2d6af4", - "0a5fae6a5a23b95f0fb70bc22b5e38c8c4aacfc476f4214077236315ceb54ce4", - "cadef4aa0ae961b99f4bbb3d11314b8855377156a22341f6fffadef214479e79", + "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", + "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", + "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", ] `); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(3); @@ -71,34 +72,32 @@ describe("digest v4.0", () => { expect(obfuscatedVerifiableCredential).toStrictEqual({ proof: obfuscatedVerifiableCredential.proof }); expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "63285ee08671cb84b18414a813d28cc718ce43b815ff73ce4154faa9c9ad9dcd", - "2d9b6afb3ec3445b22ef6684238a27f32f532dde22875cc9dcf6fd69ae600e23", - "2faef9552eeeb238a9920ce2bc91844fa5b9d313d2377472c2bbc268388e77e6", - "38068d88819bb58dddc47d50657320403ad1c7272248c7f5f8e03d86f7f3a736", - "5c37fad912a96877a1de8697544e647d5956be812d894013724c8d7a7a305daf", - "54d542c2a5074f79d748cd2dad4a4e152254d593ab511cc6190f8dd367625db7", - "e521e95bb3adf4ded40a929af58996c33add5ec9a8d4d0d3db22a69aa29f8517", - "f74a658d3e1b8b6089b2a20002ef53a2c7ef5ccadf391f1863805fb95bb1d4b8", - "2ee3e874b989d2f17f8e1a2b29a0d7a361143f7a0a05eaf6650b74a9e1a6490d", - "10c8faff3d92cf41f0c1392d28baa7bb9bbb0b424c565471cb1205f440c6ee6a", - "9fb9934dfe60ff43436ebf702bbf055b3af7dc7544ceefcc48a0fba5e4777a65", - "79f2841de72244001c83b4aaacba8676655621e254f26fca8feb83233644d12c", - "4ce4ac5a51879fa88298503074e207b345b535d9ecbb6f660ae46e919cec8252", - "de994629b0d88eddff6f4a137f4745f098938b3e8daa5db090b55a8d79db9776", - "f9fbffd43354e65f05e1e3cbf40999e814801651b0da5af53ec2150065e5bba5", - "94b147f4856eca4c66733908e364062de8acf5ee74dca29926b35c31d4fc3739", - "dc9fb54950b4505553c79505d64f0108f1b7af287e8b909425c17deb7e2d6af4", - "e62c543c991fd273357d5bbafcced8d884777d626e7053a2154fabadedd96d69", - "0a5fae6a5a23b95f0fb70bc22b5e38c8c4aacfc476f4214077236315ceb54ce4", - "b612f632c655bbcdc5fd1732e7fdf96c2b18890d812a5218d6bf5dca9394e123", - "cadef4aa0ae961b99f4bbb3d11314b8855377156a22341f6fffadef214479e79", - "8264ebfd62f23fd2953a5b36a574dd3b5b08bd1db21f438ad5415d6b2fbd13bf", - "fd6aef8c5547610005739747971968f39adb521c72e7a40915e2be7398bfc902", - "54b2bd63fa155cb08cb81adea31920845e59a1b0d02e57a63c14c9cf4704de65", - "aa1cf55fca00a814a8bf956641b09585ea31dd10776a6dbc9257fb53ba29e54b", + "a0b3d5c67c33a0dd28b29affc83c45c07bb4c5f43e35ae2b17d7185832372b32", + "e2571635cde03fc4397336176118f7dcad6443d033ff30c0bb20dad6a0b7dc64", + "f1262f889f833ac33db6ad98baa99697a76d2c5b78e8c16de71d70cf4ce9f11e", + "d3e53913d1d0fe997f2a941b38ddc0f7481a737f0958454298a983b622476ae6", + "a9c59e18346b177425016a4dbac19ff521ee2547bc4ae78b6fffda374224985c", + "4322f850d5f17e8db8738e8fcd1901b43d053eb4086fcc747ddb670eec26e1c5", + "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", + "2079505ff19eecf40373b4c8a97858340538c32dd9998b013a75b00591863a86", + "6657f68f32ede3f1f0c23fad3c14ec2150caf127320bb681ac882bbf088314e4", + "da0129d424dfd61470cfbc6db2d0a351456c1347ff8a866f64e773534f50dbc9", + "cdc6780c98d21cf1c2a512c69424901ebc66c657c4364f573dbc77b8f99f1e6e", + "6f8dea4950d0b99674507e2a362887b6fbb5a661d395ab5e0f10b7902369fd57", + "0e8cfba3a420807f116410fd8bcf3f8147133191c17bf00315cafae1ec7231b3", + "c9fa76bd112c9dd65afd38899e58583d39879abb7962368eb91ca9217d49e8fc", + "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", + "0b40ea9dcddca6d570191b3b5bee01dbe4d53047e45f39f496ee4f77baabd2e7", + "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", + "5db7da35fe78cde5f216efca8446fa84762c346c8eb5685c9086841ba8899ea3", + "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", + "6fd29224f74d7b53d5501fb8f111a9cb142d9481367cdec84db6b0919aa60ed8", + "5ea9e34d7e03a436d6c50f7e2af34ee90f1cda674b3dca270228a6041c32430b", + "8ab3fdec4be9c61ca9d0b570896796be343287a9fc92262aadc5a2b1f400ad6a", + "b298f1096b443e1441a8e0f62eef873a45fbbbb6e9bbfd1691106224613cd61b", ] `); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(25); + expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(23); expect(digest).toBe(credentialRoot); }); }); From 6e6f808dd208db747b798ed7e6ceb003f82fcada Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Fri, 19 Apr 2024 16:38:27 +0800 Subject: [PATCH 019/107] fix: make renderMethod.templateName required in v4.0 json schema --- src/4.0/schema/schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json index 3d16c26c..aca24d0e 100644 --- a/src/4.0/schema/schema.json +++ b/src/4.0/schema/schema.json @@ -40,7 +40,7 @@ "description": "Template name to be use by template renderer to determine the template to use" } }, - "required": ["id", "name"] + "required": ["id", "templateName"] }, "credentialSubject": { "type": "object", From bc48b98885a03bc149ff1ea7eea5b2168392193b Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Mon, 22 Apr 2024 17:44:32 +0800 Subject: [PATCH 020/107] fix: migrate to zod for input vc data model validation --- package-lock.json | 55 +++--------- package.json | 4 +- src/4.0/validate/dataModel.ts | 159 +++++++++++++++++++++++----------- src/4.0/wrap.ts | 11 +-- 4 files changed, 125 insertions(+), 104 deletions(-) diff --git a/package-lock.json b/package-lock.json index 911f7add..85864cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,14 +17,14 @@ "debug": "^4.3.2", "ethers": "^5.7.2", "flatley": "^5.2.0", - "joi": "^17.12.2", "js-base64": "^3.6.1", "js-sha3": "^0.8.0", "jsonld": "^8.3.2", "lodash": "^4.17.21", "runtypes": "^6.3.2", "uuid": "^8.3.2", - "validator": "^13.7.0" + "validator": "^13.7.0", + "zod": "^3.22.4" }, "devDependencies": { "@commitlint/cli": "^17.6.7", @@ -1825,19 +1825,6 @@ "node": ">=12" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -3585,24 +3572,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -9510,18 +9479,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/joi": { - "version": "17.12.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", - "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, "node_modules/js-base64": { "version": "3.7.5", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", @@ -17448,6 +17405,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 623fa5e8..516649c6 100644 --- a/package.json +++ b/package.json @@ -78,14 +78,14 @@ "debug": "^4.3.2", "ethers": "^5.7.2", "flatley": "^5.2.0", - "joi": "^17.12.2", "js-base64": "^3.6.1", "js-sha3": "^0.8.0", "jsonld": "^8.3.2", "lodash": "^4.17.21", "runtypes": "^6.3.2", "uuid": "^8.3.2", - "validator": "^13.7.0" + "validator": "^13.7.0", + "zod": "^3.22.4" }, "directories": { "test": "test" diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts index 219cb556..10d38212 100644 --- a/src/4.0/validate/dataModel.ts +++ b/src/4.0/validate/dataModel.ts @@ -1,74 +1,129 @@ -import Joi from "joi"; +import z from "zod"; -import { ContextUrl as baseContext } from "../../shared/@types/document"; +import { ContextUrl } from "../../shared/@types/document"; const baseType = "VerifiableCredential"; -export const vcSchema = Joi.object({ - "@context": Joi.alternatives() - .try(Joi.object()) - .try(Joi.string()) +// Custom URI validation function +const URI_REGEX = + /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; +const zodUri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); + +export const inputVcModel = z.object({ + "@context": z.union([ + z.record(z.any()), + z.string(), // If array: First item must be baseContext, while remaining items can be string or object - .try(Joi.array().ordered(Joi.string().valid(baseContext["v2_vc"]).required()).items(Joi.string(), Joi.object())) - .required(), + z.tuple([z.literal(ContextUrl.v2_vc)]).rest(z.union([z.string(), z.record(z.any())])), + ]), // [Optional] If string: Must match uri pattern - id: Joi.string().uri(), + id: zodUri.optional(), - type: Joi.alternatives() - .try(Joi.string()) + type: z.union([ + z.string(), // If array: Must have VerifiableCredential, while remaining items can be any string - .try(Joi.array().items(Joi.string().valid(baseType).required(), Joi.string())) - .required(), + z.array(z.string()).refine((types) => types.includes(baseType), { message: `Type must include ${baseType}` }), + ]), - credentialSchema: Joi.alternatives() - // If object: Must have id match uri pattern and type defined - .try(Joi.object({ id: Joi.string().uri().required(), type: Joi.string().required() })) - // If array: Every object must have id match uri pattern and type defined - .try(Joi.array().items({ id: Joi.string().uri().required(), type: Joi.string().required() })), + credentialSchema: z + .union([ + // If object: Must have id match uri pattern and type defined + z.object({ + id: zodUri, + type: z.string(), + }), + // If array: Every object must have id match uri pattern and type defined + z.array( + z.object({ + id: zodUri, + type: z.string(), + }) + ), + ]) + .optional(), - issuer: Joi.alternatives() + issuer: z.union([ // If string: Must match uri pattern - .try(Joi.string().uri()) + zodUri, // If object: Must have id match uri pattern - .try(Joi.object({ id: Joi.string().uri() }).unknown()) - .required(), + z.object({ + id: zodUri, + }), + ]), - validFrom: Joi.string().isoDate(), + validFrom: z.string().datetime({ offset: true }).optional(), - validUntil: Joi.string().isoDate(), + validUntil: z.string().datetime({ offset: true }).optional(), - credentialSubject: Joi.alternatives() + credentialSubject: z.union([ // If object: Cannot be empty (i.e. minimum 1 key) - .try(Joi.object().min(1)) + z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { + message: "Must have at least one key", + }), // If array: Every object cannot be empty (i.e. minimum 1 key) - .try(Joi.array().items(Joi.object().min(1))) - .required(), + z.array( + z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { + message: "Must have at least one key", + }) + ), + ]), - credentialStatus: - // Must have type defined - // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) - Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() }) - // Allow additional properties - .unknown(), + credentialStatus: z + .object({ + // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) + id: zodUri.optional(), + // Must have type defined + type: z.string(), + }) + .optional(), - termsOfUse: Joi.alternatives() - // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - .try(Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() })) - // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - .try(Joi.array().items({ id: Joi.string().uri().optional(), type: Joi.string().required() })), + termsOfUse: z + .union([ + // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + z.object({ + id: zodUri.optional(), + type: z.string(), + }), + // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + z.array( + z.object({ + id: zodUri.optional(), + type: z.string(), + }) + ), + ]) + .optional(), - evidence: Joi.alternatives() - // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - .try(Joi.object({ id: Joi.string().uri().optional(), type: Joi.string().required() })) - // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - .try(Joi.array().items({ id: Joi.string().uri().optional(), type: Joi.string().required() })), + evidence: z + .union([ + // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + z.object({ + id: zodUri.optional(), + type: z.string(), + }), + // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + z.array( + z.object({ + id: zodUri.optional(), + type: z.string(), + }) + ), + ]) + .optional(), - proof: Joi.alternatives() - // If object: Must have type defined - .try(Joi.object({ type: Joi.string().required() })) - // If array: Every object must have type defined - .try(Joi.array().items({ type: Joi.string().required() })), -}) - // Allow additional properties - .unknown(); + proof: z + .union([ + // If object: Must have type defined + z.object({ + type: z.string(), + }), + // If array: Every object must have type defined + z.array( + z.object({ + type: z.string(), + }) + ), + ]) + .optional(), +}); diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 488d7a0d..09f2a9ee 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -6,7 +6,7 @@ import { digestCredential } from "../4.0/digest"; import { WrapDocumentOptionV4 } from "../shared/@types/wrap"; import { OpenAttestationDocument, ProofPurpose } from "../__generated__/schema.4.0"; import { encodeSalt, salt } from "./salt"; -import { interpretContexts, vcSchema } from "./validate"; +import { interpretContexts, inputVcModel } from "./validate"; export const wrapDocument = async ( credential: T, @@ -15,12 +15,13 @@ export const wrapDocument = async ( const document = { ...credential }; /* 1. Data model validation */ - const { error } = vcSchema.validate(document); - if (error) { + const result = await inputVcModel.safeParseAsync(document); + if (!result.success) throw new Error( - `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(error.details)}` + `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify( + result.error.issues + )}` ); - } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ await interpretContexts(document); From d3aafc2a165879a71a4d48838a1c86954f1ce073 Mon Sep 17 00:00:00 2001 From: Kyle Huang Junyuan Date: Wed, 24 Apr 2024 16:20:00 +0800 Subject: [PATCH 021/107] wip: oa v4 types --- scripts/postInstall.js | 5 - scripts/publishSchema.sh | 4 - src/4.0/schema/schema.json | 183 ---------------------------------- src/4.0/schema/schema.test.ts | 36 ------- src/4.0/sign.ts | 2 +- src/4.0/types.ts | 136 ++++++++++++++++--------- src/4.0/validate/dataModel.ts | 4 +- src/4.0/wrap.ts | 58 +++++++---- src/index.ts | 26 ++--- src/shared/@types/document.ts | 14 ++- src/shared/@types/wrap.ts | 4 - src/shared/utils/guard.ts | 5 +- src/shared/utils/utils.ts | 21 +++- 13 files changed, 171 insertions(+), 327 deletions(-) delete mode 100644 src/4.0/schema/schema.json delete mode 100644 src/4.0/schema/schema.test.ts diff --git a/scripts/postInstall.js b/scripts/postInstall.js index d09dd7d3..3d561818 100644 --- a/scripts/postInstall.js +++ b/scripts/postInstall.js @@ -16,11 +16,6 @@ if (fs.existsSync(quicktype) && process.env.npm_config_production !== "true") { quicktype + " -s schema -o src/__generated__/schema.3.0.ts -t OpenAttestationDocument --just-types src/3.0/schema/schema.json --no-date-times" ); - console.log('"Creating types from src/4.0/schema/schema.json"'); - execSync( - quicktype + - " -s schema -o src/__generated__/schema.4.0.ts -t OpenAttestationDocument --just-types src/4.0/schema/schema.json --no-date-times" - ); } else { console.log("Not running quicktype"); } diff --git a/scripts/publishSchema.sh b/scripts/publishSchema.sh index 6064f6c4..41680371 100755 --- a/scripts/publishSchema.sh +++ b/scripts/publishSchema.sh @@ -11,7 +11,3 @@ cp src/2.0/schema/schema.json public/2.0/schema.json # Copy 3.0 schema to public folder mkdir -p public/3.0/ cp src/3.0/schema/schema.json public/3.0/schema.json - -# Copy 4.0 schema to public folder -mkdir -p public/4.0/ -cp src/4.0/schema/schema.json public/4.0/schema.json diff --git a/src/4.0/schema/schema.json b/src/4.0/schema/schema.json deleted file mode 100644 index aca24d0e..00000000 --- a/src/4.0/schema/schema.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "title": "OpenAttestation v4.0 Schema", - "$id": "https://schemata.openattestation.com/com/openattestation/4.0/alpha-schema.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "definitions": { - "@context": { - "description": "Used to define the short-hand names that are used throughout a JSON-LD document.", - "type": ["object", "string", "array", "null"] - }, - "id": { - "description": "Used to uniquely identify things that are being described in the document with IRIs or blank node identifiers.", - "type": "string", - "format": "uri" - }, - "type": { - "description": "Used to set the data type of a node or typed value.", - "type": ["string", "null", "array"] - }, - "credentialSchema": { - "type": "object", - "properties": { - "id": { "$ref": "#/definitions/id" }, - "type": { "$ref": "#/definitions/type" } - }, - "description": "A data schema that provide verifiers with enough information to determine whether the provided data conforms to the provided schema(s). More information in https://www.w3.org/TR/vc-data-model-2.0/#data-schemas" - }, - "renderMethod": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URL of a decentralised renderer to render this document", - "examples": ["https://demo-renderer.openattestation.com"] - }, - "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationEmbeddedRenderer"] }, - "templateName": { - "type": "string", - "description": "Template name to be use by template renderer to determine the template to use" - } - }, - "required": ["id", "templateName"] - }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { "$ref": "#/definitions/id" }, - "type": { "$ref": "#/definitions/type" } - }, - "description": "A verifiable credential contains claims about one or more subjects. More information in https://www.w3.org/TR/vc-data-model-2.0/#credential-subject" - }, - "termsOfUse": { - "type": "object", - "properties": { - "type": { "$ref": "#/definitions/type" } - } - }, - "evidence": { - "type": "object", - "properties": { - "type": { "$ref": "#/definitions/type" } - } - }, - "proof": { - "type": "object", - "properties": { - "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationMerkleProofSignature2018"] }, - "proofPurpose": { "type": "string", "enum": ["assertionMethod"] }, - "targetHash": { "type": "string" }, - "proofs": { "type": "array", "items": { "type": "string" } }, - "merkleRoot": { "type": "string" }, - "salts": { "type": "string" }, - "privacy": { - "type": "object", - "properties": { "obfuscated": { "type": "array", "items": { "type": "string" } } } - }, - "key": { "type": "string" }, - "signature": { "type": "string" } - }, - "additionalProperties": false - } - }, - "properties": { - "@context": { "$ref": "#/definitions/@context" }, - "id": { "$ref": "#/definitions/id" }, - "type": { "$ref": "#/definitions/type", "examples": ["VerifiableCredential", "OpenAttestationCredential"] }, - "credentialSchema": { - "anyOf": [ - { "$ref": "#/definitions/credentialSchema" }, - { "type": "array", "items": { "$ref": "#/definitions/credentialSchema" } } - ] - }, - "validFrom": { - "type": "string", - "format": "date-time", - "description": "The date and time when this credential becomes valid", - "examples": ["2024-03-08T12:00:00+08:00"] - }, - "validUntil": { - "type": "string", - "format": "date-time", - "description": "The date and time when this credential expires", - "examples": ["2024-03-27T12:00:00+08:00"] - }, - "name": { - "type": "string", - "description": "Human readable name of this credential" - }, - "issuer": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URI when dereferenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential. More information in https://www.w3.org/TR/vc-data-model/#issuer" - }, - "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationIssuer"] }, - "name": { - "type": "string", - "description": "Issuer's name" - }, - "identityProof": { - "type": "object", - "properties": { - "identityProofType": { - "type": "string", - "enum": ["DNS-TXT", "DNS-DID", "DID"] - }, - "identifier": { - "type": "string", - "description": "Identifier to be shown to end user upon verifying the identity" - } - }, - "required": ["identityProofType", "identifier"], - "additionalProperties": false - } - }, - "required": ["id"] - } - ] - }, - "credentialStatus": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri", - "description": "URI to the status of the credential as explained by https://www.w3.org/TR/vc-data-model/#status", - "examples": ["https://ocsp-sandbox.openattestation.com"] - }, - "type": { "$ref": "#/definitions/type", "examples": ["OpenAttestationOcspResponder"] } - }, - "additionalProperties": false - }, - "renderMethod": { "type": "array", "items": { "$ref": "#/definitions/renderMethod" } }, - "credentialSubject": { - "anyOf": [ - { "$ref": "#/definitions/credentialSubject" }, - { "type": "array", "items": { "$ref": "#/definitions/credentialSubject" } } - ] - }, - "termsOfUse": { - "anyOf": [ - { "$ref": "#/definitions/termsOfUse" }, - { "type": "array", "items": { "$ref": "#/definitions/termsOfUse" } } - ] - }, - "evidence": { - "anyOf": [ - { "$ref": "#/definitions/evidence" }, - { "type": "array", "items": { "$ref": "#/definitions/evidence" } } - ] - }, - "proof": { - "anyOf": [{ "$ref": "#/definitions/proof" }, { "type": "array", "items": { "$ref": "#/definitions/proof" } }] - } - }, - "required": ["@context", "type", "issuer", "credentialSubject"] -} diff --git a/src/4.0/schema/schema.test.ts b/src/4.0/schema/schema.test.ts deleted file mode 100644 index ee1960b5..00000000 --- a/src/4.0/schema/schema.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable jest/no-try-expect,jest/no-conditional-expect */ -import { cloneDeep } from "lodash"; -import { _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocument as wrapDocumentV4 } from "../../index"; -import sample from "../../../test/fixtures/v4/did-raw.json"; -import { ContextUrl } from "../../shared/@types/document"; -import { OpenAttestationDocument } from "../../__generated__/schema.4.0"; - -const sampleVc = sample as OpenAttestationDocument; - -// eslint-disable-next-line jest/no-disabled-tests -describe("schema/4.0", () => { - it("should be valid with sample document", async () => { - const document = cloneDeep(sampleVc); - const wrappedDocument = await wrapDocumentV4(document); - expect(wrappedDocument["type"]).toStrictEqual(["VerifiableCredential", "OpenAttestationCredential"]); - expect(wrappedDocument["proof"]["type"]).toStrictEqual("OpenAttestationMerkleProofSignature2018"); - }); - - it("should be valid when adding any additional data", async () => { - const document = { ...cloneDeep(sampleVc), key1: "some" }; - const wrappedDocument = await wrapDocumentV4(document); - expect(wrappedDocument["key1"]).toStrictEqual("some"); - expect(wrappedDocument["type"]).toStrictEqual(["VerifiableCredential", "OpenAttestationCredential"]); - expect(wrappedDocument["proof"]["type"]).toStrictEqual("OpenAttestationMerkleProofSignature2018"); - }); - - describe("@context", () => { - it("should be invalid if @context contains one invalid URI", async () => { - expect.assertions(1); - const document = { ...cloneDeep(sampleVc), "@context": [ContextUrl.v2_vc, "bad string"] }; - await expect(wrapDocumentV4(document)).rejects.toMatchInlineSnapshot( - `[Error: Unable to interpret @context: {"name":"jsonld.InvalidUrl","details":{"code":"loading remote context failed","url":"bad string","cause":{}}}]` - ); - }); - }); -}); diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index 0519604e..7ba89797 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -1,4 +1,4 @@ -import { OpenAttestationDocument, WrappedDocument, SignedWrappedDocument, SignedWrappedProof } from "./types"; +import { OpenAttestationVC, WrappedDocument, SignedWrappedDocument, SignedWrappedProof } from "./types"; import { sign } from "../shared/signer"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "../shared/@types/sign"; import { isSignedWrappedV4Document } from "../shared/utils"; diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 424fb973..d4e67410 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -1,55 +1,101 @@ -// types generated by quicktype during postinstall phase -import { OpenAttestationDocument as OpenAttestationDocumentV4, ProofPurpose } from "../__generated__/schema.4.0"; -import { OpenAttestationHexString, SignatureAlgorithm } from "../shared/@types/document"; -import { Array, Literal, Record, Static, String, Union } from "runtypes"; - -export interface Salt { - value: string; - path: string; -} - -export const PrivacyObfuscation = Record({ - obfuscated: Array(OpenAttestationHexString), -}); -export type PrivacyObfuscation = Static; - -export const WrappedProof = Record({ - type: SignatureAlgorithm, - /* FIXME: No straightforward way to represent enum in runtypes */ - // proofPurpose: runtypesFromEnum(ProofPurpose), - // proofPurpose: ProofPurpose, - proofPurpose: Union(Literal(ProofPurpose.AssertionMethod)), - targetHash: String, - proofs: Array(String), - merkleRoot: String, - salts: String, - privacy: PrivacyObfuscation, +import z from "zod"; + +// import { OpenAttestationDocument as OpenAttestationDocumentV4, ProofPurpose } from "../__generated__/schema.4.0"; +import { vcDataModel, zodUri } from "./validate/dataModel"; +import { ContextUrl, ContextType, OpenAttestationHexString, SignatureAlgorithm } from "../shared/@types/document"; + +const IdentityProofType = z.enum(["DNS-TXT", "DNS-DID", "DID"]); +type IdentityProofType = z.infer; + +const Salt = z.object({ value: z.string(), path: z.string() }); +type Salt = z.infer; + +// Custom hex string validation function +const HEX_STRING_REGEX = /^(0x)?[0-9a-fA-F]{40}$/; +const zodHexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex String" }); + +const OpenAttestationVC = vcDataModel.extend({ + "@context": z + // Must be an array that starts with [baseContext, v4Context, ...] + .tuple([z.literal(ContextUrl.v2_vc), z.literal(ContextUrl.v4_alpha)]) + // Remaining items can be string or object + .rest(z.union([z.string(), z.record(z.any())])), + + type: z + // Must be an array that starts with [VerifiableCredential, OpenAttestationCredential, ...] + .tuple([z.literal(ContextType.BaseContext as string), z.literal(ContextType.V4AlphaContext as string)]) + // Remaining items can be string + .rest(z.string()), + + issuer: z.object({ + // Must have id match uri pattern + id: zodUri, + type: z.literal("OpenAttestationIssuer"), + name: z.string(), + identityProof: z.object({ + identityProofType: IdentityProofType, + identifier: z.string(), + }), + }), + + // [Optional] OCSP Revocation + credentialStatus: z + .object({ + // Must have id match url pattern (OCSP endpoint) + id: z.string().url(), + type: z.literal("OpenAttestationOcspResponder"), + }) + .optional(), + + // [Optional] Render Method + renderMethod: z + .array( + z.discriminatedUnion("type", [ + /* OA Decentralised Embedded Renderer */ + z.object({ + // Must have id match url pattern + id: z.string().url(), + type: z.literal("OpenAttestationEmbeddedRenderer"), + templateName: z.string(), + }), + /* SVG Renderer (URL or Embedded) */ + z.object({ + // Must have id match url pattern or embeded SVG string + id: z.union([z.string(), z.string().url()]), + type: z.literal("SvgRenderingTemplate2023"), + name: z.string(), + digestMultibase: z.string(), + }), + ]) + ) + .optional(), }); -export type WrappedProof = Static; +type VC = z.infer; +type OpenAttestationVC = z.infer; -export const WrappedProofStrict = WrappedProof.And( - Record({ - targetHash: OpenAttestationHexString, - merkleRoot: OpenAttestationHexString, - proofs: Array(OpenAttestationHexString), - }) -); -export type WrappedProofStrict = Static; +const WrappedProof = z.object({ + type: z.literal("OpenAttestationMerkleProofSignature2018"), + proofPurpose: z.literal("assertionMethod"), + targetHash: zodHexString, + proofs: z.array(zodHexString), + merkleRoot: zodHexString, + salts: z.string(), + privacy: z.object({ obfuscated: z.array(zodHexString) }), +}); -export const SignedWrappedProof = WrappedProof.And( - Record({ - key: String, - signature: String, +const WrappedSignedProof = WrappedProof.and( + z.object({ + key: z.string(), + signature: z.string(), }) ); -export type SignedWrappedProof = Static; -export type WrappedDocument = T & { - proof: WrappedProof; +type WrappedOpenAttestationVC = T & { + proof: z.infer; }; -export type SignedWrappedDocument = T & { - proof: SignedWrappedProof; +type WrappedSignedOpenAttestationVC = T & { + proof: z.infer; }; -export * from "../__generated__/schema.4.0"; +export { VC, OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC, Salt }; diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts index 10d38212..94a7b743 100644 --- a/src/4.0/validate/dataModel.ts +++ b/src/4.0/validate/dataModel.ts @@ -7,9 +7,9 @@ const baseType = "VerifiableCredential"; // Custom URI validation function const URI_REGEX = /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; -const zodUri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); +export const zodUri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); -export const inputVcModel = z.object({ +export const vcDataModel = z.object({ "@context": z.union([ z.record(z.any()), z.string(), diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 09f2a9ee..aa9edfa6 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,27 +1,34 @@ import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; -import { ContextUrl } from "../shared/@types/document"; -import { WrappedDocument } from "./types"; +import { ContextType, ContextUrl } from "../shared/@types/document"; +import { OpenAttestationVC, WrappedOpenAttestationVC } from "./types"; import { digestCredential } from "../4.0/digest"; -import { WrapDocumentOptionV4 } from "../shared/@types/wrap"; -import { OpenAttestationDocument, ProofPurpose } from "../__generated__/schema.4.0"; import { encodeSalt, salt } from "./salt"; -import { interpretContexts, inputVcModel } from "./validate"; +import { interpretContexts, vcDataModel } from "./validate"; -export const wrapDocument = async ( - credential: T, - options: WrapDocumentOptionV4 // eslint-disable-line @typescript-eslint/no-unused-vars -): Promise> => { - const document = { ...credential }; - - /* 1. Data model validation */ - const result = await inputVcModel.safeParseAsync(document); +export const wrapDocument = async ( + credential: T +): Promise> => { + /* 1a. W3C VC data model validation */ + const result = await vcDataModel.safeParseAsync(credential); if (!result.success) throw new Error( `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify( result.error.issues )}` ); + const document = result.data; + + /* 1b. Narrow down to OpenAttestation VC validation */ + const oav4context = await OpenAttestationVC.shape["@context"].safeParseAsync(document["@context"]); // Superficial check on user intention + if (oav4context.success) { + const oav4 = await OpenAttestationVC.safeParseAsync(document); + if (!oav4.success) { + throw new Error( + `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` + ); + } + } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ await interpretContexts(document); @@ -35,18 +42,20 @@ export const wrapDocument = async ( } else if (isStringArray(document["@context"])) { document["@context"].forEach((context) => contexts.add(context)); } - document["@context"] = Array.from(contexts); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this + [ContextUrl.v2_vc, ContextUrl.v4_alpha].forEach((c) => contexts.delete(c)); + const finalContexts: OpenAttestationVC["@context"] = [ContextUrl.v2_vc, ContextUrl.v4_alpha, ...Array.from(contexts)]; // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this /* 4. Type validation */ // Ensure that required types are present and in the correct order // type: ["VerifiableCredential", "OpenAttestationCredential", ...] - const types = new Set(["VerifiableCredential", "OpenAttestationCredential"]); + const types = new Set([ContextType.BaseContext, ContextType.V4AlphaContext]); if (typeof document["type"] === "string") { types.add(document["type"]); } else if (isStringArray(document["type"])) { document["type"].forEach((type) => types.add(type)); } - document["type"] = Array.from(types); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this + [ContextUrl.v2_vc, ContextUrl.v4_alpha].forEach((c) => contexts.delete(c)); + // document["type"] = Array.from(types); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this /* 5. OA wrapping */ const salts = salt(document); @@ -58,11 +67,17 @@ export const wrapDocument = async ( const merkleRoot = merkleTree.getRoot().toString("hex"); const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); - const verifiableCredential: WrappedDocument = { + const verifiableCredential: WrappedOpenAttestationVC = { ...document, + "@context": finalContexts, + type: [ContextType.BaseContext, ContextType.V4AlphaContext], // FIXME: Follow finalContexts + issuer: document["issuer"] as OpenAttestationVC["issuer"], // Assume valid by asserting types + ...(document["credentialStatus"] + ? { credentialStatus: document["credentialStatus"] as OpenAttestationVC["credentialStatus"] } + : {}), proof: { type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: ProofPurpose.AssertionMethod, + proofPurpose: "assertionMethod", targetHash: digest, proofs: merkleProof, merkleRoot, @@ -76,10 +91,9 @@ export const wrapDocument = async ( return verifiableCredential; }; -export const wrapDocuments = async ( - documents: T[], - options: WrapDocumentOptionV4 -): Promise[]> => { +export const wrapDocuments = async ( + documents: T[] +): Promise[]> => { // create individual verifiable credential const verifiableCredentials = await Promise.all(documents.map((document) => wrapDocument(document, options))); diff --git a/src/index.ts b/src/index.ts index 0f80d9d0..d68ef8fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import * as utils from "./shared/utils"; import { SchemaValidationError } from "./shared/utils"; import { validateSchema as validate } from "./shared/validate"; import { SchemaId, WrappedDocument, OpenAttestationDocument } from "./shared/@types/document"; -import { WrapDocumentOptionV2, WrapDocumentOptionV3, WrapDocumentOptionV4 } from "./shared/@types/wrap"; +import { WrapDocumentOptionV2, WrapDocumentOptionV3 } from "./shared/@types/wrap"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "./shared/@types/sign"; import * as v2 from "./2.0/types"; @@ -26,13 +26,11 @@ import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV3 } from import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "./__generated__/schema.3.0"; import * as v4 from "./4.0/types"; -import { WrappedDocument as WrappedDocumentV4 } from "./4.0/types"; import { wrapDocument as wrapDocumentV4, wrapDocuments as wrapDocumentsV4 } from "./4.0/wrap"; import { signDocument as signDocumentV4 } from "./4.0/sign"; import { verify as verifyV4 } from "./4.0/verify"; import { digestCredential as digestCredentialV4 } from "./4.0/digest"; import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV4 } from "./4.0/obfuscate"; -import { OpenAttestationDocument as OpenAttestationDocumentV4 } from "./__generated__/schema.4.0"; export function wrapDocument( data: T, @@ -62,18 +60,16 @@ export function __unsafe__use__it__at__your__own__risks__wrapDocuments( - data: T, - options?: WrapDocumentOptionV4 -): Promise> { - return wrapDocumentV4(data, options ?? { version: SchemaId.v4 }); +export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocument( + data: T +): Promise> { + return wrapDocumentV4(data); } -export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments( - dataArray: T[], - options?: WrapDocumentOptionV4 -): Promise[]> { - return wrapDocumentsV4(dataArray, options ?? { version: SchemaId.v4 }); +export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments( + dataArray: T[] +): Promise[]> { + return wrapDocumentsV4(dataArray); } export const validateSchema = (document: WrappedDocument): boolean => { @@ -97,7 +93,7 @@ export function verifySignature( algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer ): Promise>; -export async function signDocument( +export async function signDocument( document: v4.SignedWrappedDocument | v4.WrappedDocument, algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer diff --git a/src/shared/@types/document.ts b/src/shared/@types/document.ts index 0a62aa56..0d8c8bec 100644 --- a/src/shared/@types/document.ts +++ b/src/shared/@types/document.ts @@ -36,13 +36,17 @@ export type SignedWrappedDocument = T extends export enum SchemaId { v2 = "https://schema.openattestation.com/2.0/schema.json", v3 = "https://schema.openattestation.com/3.0/schema.json", - v4 = "https://schemata.openattestation.com/com/openattestation/4.0/alpha-schema.json", // Note: Schema property is no longer placed in the OA v4 document } -export enum ContextUrl { - v2_vc = "https://www.w3.org/ns/credentials/v2", - v4_alpha = "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", -} +export const ContextUrl = { + v2_vc: "https://www.w3.org/ns/credentials/v2", + v4_alpha: "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", +} as const; + +export const ContextType = { + BaseContext: "VerifiableCredential", + V4AlphaContext: "OpenAttestationCredential", +} as const; export const OpenAttestationHexString = String.withConstraint( (value) => ethers.utils.isHexString(`0x${value}`, 32) || `${value} has not the expected length of 32 bytes` diff --git a/src/shared/@types/wrap.ts b/src/shared/@types/wrap.ts index 6d0c3244..8fa54e04 100644 --- a/src/shared/@types/wrap.ts +++ b/src/shared/@types/wrap.ts @@ -12,7 +12,3 @@ export interface WrapDocumentOptionV3 { externalSchemaId?: string; version: SchemaId.v3; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WrapDocumentOptionV4 { - // If any, add options to wrap utility -} diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index ffd0dedd..0f8f03d1 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,10 +7,7 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { - OpenAttestationDocument as OpenAttestationDocumentV4, - WrappedDocument as WrappedDocumentV4, -} from "../../4.0/types"; +import { OpenAttestationVC as OpenAttestationDocumentV4, WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index 50670550..b6264d70 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -14,7 +14,7 @@ import * as v4 from "../../__generated__/schema.4.0"; import { WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; import { OpenAttestationDocument as OpenAttestationDocumentV4 } from "../../__generated__/schema.4.0"; -import { OpenAttestationDocument, WrappedDocument } from "../@types/document"; +import { OpenAttestationDocument, WrappedDocument, SchemaId, ContextUrl, ContextType } from "../@types/document"; import { isRawV2Document, isWrappedV2Document, @@ -261,3 +261,22 @@ export const getObfuscatedData = ( export const isStringArray = (input: unknown): input is string[] => Array.isArray(input) && input.every((i) => typeof i === "string"); + +export const getVersion = (document: unknown) => { + if (typeof document === "object" && document !== null) { + if ("version" in document && typeof document.version === "string") { + switch (document.version) { + case SchemaId.v2: + return 2; + case SchemaId.v3: + return 3; + } + } else if ("@context" in document && Array.isArray(document["@context"])) { + if (document["@context"].includes(ContextUrl.v4_alpha)) { + return 4; + } + } + } + + throw new Error("Unknown document version: Can only determine between OpenAttestation v2, v3 & v4 documents."); +}; From b102ee9d53828b5609000b4d0460d01a49558710 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 16:30:51 +0800 Subject: [PATCH 022/107] refactor: prefer string union --- src/4.0/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index d4e67410..d333641e 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -2,9 +2,9 @@ import z from "zod"; // import { OpenAttestationDocument as OpenAttestationDocumentV4, ProofPurpose } from "../__generated__/schema.4.0"; import { vcDataModel, zodUri } from "./validate/dataModel"; -import { ContextUrl, ContextType, OpenAttestationHexString, SignatureAlgorithm } from "../shared/@types/document"; +import { ContextUrl, ContextType } from "../shared/@types/document"; -const IdentityProofType = z.enum(["DNS-TXT", "DNS-DID", "DID"]); +const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); type IdentityProofType = z.infer; const Salt = z.object({ value: z.string(), path: z.string() }); From e693b92fc55040ff3194dc16f0f34af0c1bc17a5 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 16:40:40 +0800 Subject: [PATCH 023/107] refactor: reference constant tuple instead --- src/4.0/wrap.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index aa9edfa6..bbe7f661 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -36,14 +36,15 @@ export const wrapDocument = async ( /* 3. Context validation */ // Ensure that required contexts are present and in the correct order // type: [Base, OA, ...] - const contexts = new Set([ContextUrl["v2_vc"], ContextUrl["v4_alpha"]]); + const REQUIRED_CONTEXTS = [ContextUrl.v2_vc, ContextUrl.v4_alpha] as const; + const contexts = new Set(REQUIRED_CONTEXTS); if (typeof document["@context"] === "string") { contexts.add(document["@context"]); } else if (isStringArray(document["@context"])) { document["@context"].forEach((context) => contexts.add(context)); } - [ContextUrl.v2_vc, ContextUrl.v4_alpha].forEach((c) => contexts.delete(c)); - const finalContexts: OpenAttestationVC["@context"] = [ContextUrl.v2_vc, ContextUrl.v4_alpha, ...Array.from(contexts)]; // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this + REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); + const finalContexts: OpenAttestationVC["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this /* 4. Type validation */ // Ensure that required types are present and in the correct order From c4c91febb1aa86661a9af51fcd6535ac08cad683 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 16:53:52 +0800 Subject: [PATCH 024/107] refactor: finalTypes follows pattern of finalContexts --- src/4.0/wrap.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index bbe7f661..990172e1 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -44,19 +44,20 @@ export const wrapDocument = async ( document["@context"].forEach((context) => contexts.add(context)); } REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); - const finalContexts: OpenAttestationVC["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this + const finalContexts: OpenAttestationVC["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; /* 4. Type validation */ // Ensure that required types are present and in the correct order // type: ["VerifiableCredential", "OpenAttestationCredential", ...] + const REQUIRED_TYPES = [ContextType.BaseContext, ContextType.V4AlphaContext] as const; const types = new Set([ContextType.BaseContext, ContextType.V4AlphaContext]); if (typeof document["type"] === "string") { types.add(document["type"]); } else if (isStringArray(document["type"])) { - document["type"].forEach((type) => types.add(type)); + types.forEach((type) => types.add(type)); } - [ContextUrl.v2_vc, ContextUrl.v4_alpha].forEach((c) => contexts.delete(c)); - // document["type"] = Array.from(types); // Since JavaScript Sets preserve insertion order and duplicated inserts do not affect the order, we can do this + REQUIRED_TYPES.forEach((t) => types.delete(t)); + const finalTypes: OpenAttestationVC["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; /* 5. OA wrapping */ const salts = salt(document); From 5ff08a335fa5d5b1fbe1a36ef94dfe39c7de826c Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 16:54:57 +0800 Subject: [PATCH 025/107] refactor: document -> raw document for clarity --- src/4.0/wrap.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 990172e1..04f251ce 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -17,12 +17,12 @@ export const wrapDocument = async ( result.error.issues )}` ); - const document = result.data; + const rawDocument = result.data; /* 1b. Narrow down to OpenAttestation VC validation */ - const oav4context = await OpenAttestationVC.shape["@context"].safeParseAsync(document["@context"]); // Superficial check on user intention + const oav4context = await OpenAttestationVC.shape["@context"].safeParseAsync(rawDocument["@context"]); // Superficial check on user intention if (oav4context.success) { - const oav4 = await OpenAttestationVC.safeParseAsync(document); + const oav4 = await OpenAttestationVC.safeParseAsync(rawDocument); if (!oav4.success) { throw new Error( `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` @@ -31,17 +31,17 @@ export const wrapDocument = async ( } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ - await interpretContexts(document); + await interpretContexts(rawDocument); /* 3. Context validation */ // Ensure that required contexts are present and in the correct order // type: [Base, OA, ...] const REQUIRED_CONTEXTS = [ContextUrl.v2_vc, ContextUrl.v4_alpha] as const; const contexts = new Set(REQUIRED_CONTEXTS); - if (typeof document["@context"] === "string") { - contexts.add(document["@context"]); - } else if (isStringArray(document["@context"])) { - document["@context"].forEach((context) => contexts.add(context)); + if (typeof rawDocument["@context"] === "string") { + contexts.add(rawDocument["@context"]); + } else if (isStringArray(rawDocument["@context"])) { + rawDocument["@context"].forEach((context) => contexts.add(context)); } REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); const finalContexts: OpenAttestationVC["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; @@ -51,17 +51,17 @@ export const wrapDocument = async ( // type: ["VerifiableCredential", "OpenAttestationCredential", ...] const REQUIRED_TYPES = [ContextType.BaseContext, ContextType.V4AlphaContext] as const; const types = new Set([ContextType.BaseContext, ContextType.V4AlphaContext]); - if (typeof document["type"] === "string") { - types.add(document["type"]); - } else if (isStringArray(document["type"])) { + if (typeof rawDocument["type"] === "string") { + types.add(rawDocument["type"]); + } else if (isStringArray(rawDocument["type"])) { types.forEach((type) => types.add(type)); } REQUIRED_TYPES.forEach((t) => types.delete(t)); const finalTypes: OpenAttestationVC["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; /* 5. OA wrapping */ - const salts = salt(document); - const digest = digestCredential(document, salts, []); + const salts = salt(rawDocument); + const digest = digestCredential(rawDocument, salts, []); const batchBuffers = [digest].map(hashToBuffer); @@ -70,12 +70,12 @@ export const wrapDocument = async ( const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); const verifiableCredential: WrappedOpenAttestationVC = { - ...document, + ...rawDocument, "@context": finalContexts, type: [ContextType.BaseContext, ContextType.V4AlphaContext], // FIXME: Follow finalContexts - issuer: document["issuer"] as OpenAttestationVC["issuer"], // Assume valid by asserting types - ...(document["credentialStatus"] - ? { credentialStatus: document["credentialStatus"] as OpenAttestationVC["credentialStatus"] } + issuer: rawDocument["issuer"] as OpenAttestationVC["issuer"], // Assume valid by asserting types + ...(rawDocument["credentialStatus"] + ? { credentialStatus: rawDocument["credentialStatus"] as OpenAttestationVC["credentialStatus"] } : {}), proof: { type: "OpenAttestationMerkleProofSignature2018", From 23af4384156daaa9fda052e144bc6766b45b149e Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 17:04:06 +0800 Subject: [PATCH 026/107] fix: rawDocument should not be used directly for digest and salting --- src/4.0/wrap.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 04f251ce..77b8d7e4 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,7 +1,7 @@ import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; import { ContextType, ContextUrl } from "../shared/@types/document"; -import { OpenAttestationVC, WrappedOpenAttestationVC } from "./types"; +import { OpenAttestationVC, VC, WrappedOpenAttestationVC } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; import { interpretContexts, vcDataModel } from "./validate"; @@ -59,9 +59,15 @@ export const wrapDocument = async ( REQUIRED_TYPES.forEach((t) => types.delete(t)); const finalTypes: OpenAttestationVC["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; + const documentReadyForWrapping = { + ...rawDocument, + "@context": finalContexts, + type: finalTypes, + } satisfies VC; + /* 5. OA wrapping */ - const salts = salt(rawDocument); - const digest = digestCredential(rawDocument, salts, []); + const salts = salt(documentReadyForWrapping); + const digest = digestCredential(documentReadyForWrapping, salts, []); const batchBuffers = [digest].map(hashToBuffer); @@ -70,8 +76,7 @@ export const wrapDocument = async ( const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); const verifiableCredential: WrappedOpenAttestationVC = { - ...rawDocument, - "@context": finalContexts, + ...documentReadyForWrapping, type: [ContextType.BaseContext, ContextType.V4AlphaContext], // FIXME: Follow finalContexts issuer: rawDocument["issuer"] as OpenAttestationVC["issuer"], // Assume valid by asserting types ...(rawDocument["credentialStatus"] From 5cc50cfe6acc2e0785c1a4317a39d1644e86f652 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 17:16:06 +0800 Subject: [PATCH 027/107] fix: remove asserting to string to let the literal infer properly --- src/4.0/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index d333641e..c264090a 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -23,7 +23,7 @@ const OpenAttestationVC = vcDataModel.extend({ type: z // Must be an array that starts with [VerifiableCredential, OpenAttestationCredential, ...] - .tuple([z.literal(ContextType.BaseContext as string), z.literal(ContextType.V4AlphaContext as string)]) + .tuple([z.literal(ContextType.BaseContext), z.literal(ContextType.V4AlphaContext)]) // Remaining items can be string .rest(z.string()), From e7e4c72a1403fea612712537bf557598bcff17aa Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 18:26:51 +0800 Subject: [PATCH 028/107] fix: typings --- src/4.0/wrap.ts | 41 ++++++++--------------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 77b8d7e4..1e34d7d6 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -74,14 +74,9 @@ export const wrapDocument = async ( const merkleTree = new MerkleTree(batchBuffers); const merkleRoot = merkleTree.getRoot().toString("hex"); const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); - const verifiableCredential: WrappedOpenAttestationVC = { ...documentReadyForWrapping, - type: [ContextType.BaseContext, ContextType.V4AlphaContext], // FIXME: Follow finalContexts - issuer: rawDocument["issuer"] as OpenAttestationVC["issuer"], // Assume valid by asserting types - ...(rawDocument["credentialStatus"] - ? { credentialStatus: rawDocument["credentialStatus"] as OpenAttestationVC["credentialStatus"] } - : {}), + ...assertAsOaVcProps(documentReadyForWrapping, ["issuer", "credentialStatus"]), proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", @@ -95,33 +90,13 @@ export const wrapDocument = async ( }, }; - return verifiableCredential; + return verifiableCredential as WrappedOpenAttestationVC; }; -export const wrapDocuments = async ( - documents: T[] -): Promise[]> => { - // create individual verifiable credential - const verifiableCredentials = await Promise.all(documents.map((document) => wrapDocument(document, options))); - - // get all the target hashes to compute the merkle tree and the merkle root - const merkleTree = new MerkleTree( - verifiableCredentials.map((verifiableCredential) => verifiableCredential.proof.targetHash).map(hashToBuffer) - ); - const merkleRoot = merkleTree.getRoot().toString("hex"); - - // for each document, update the merkle root and add the proofs needed - return verifiableCredentials.map((verifiableCredential) => { - const digest = verifiableCredential.proof.targetHash; - const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); - - return { - ...verifiableCredential, - proof: { - ...verifiableCredential.proof, - proofs: merkleProof, - merkleRoot, - }, - }; +function assertAsOaVcProps(obj: VC, keys: K[]) { + const temp: Record = {}; + Object.entries(obj).forEach(([k, v]) => { + if (keys.includes(k as K)) temp[k] = v; }); -}; + return temp as { [key in K]: OpenAttestationVC[key] }; +} From 27e6e8e9eafbf4055b70e963f5102a51d58653d5 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 18:49:05 +0800 Subject: [PATCH 029/107] refactor: move assertion earlier --- src/4.0/wrap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 1e34d7d6..8152c962 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -61,6 +61,7 @@ export const wrapDocument = async ( const documentReadyForWrapping = { ...rawDocument, + ...assertAsOaVcProps(rawDocument, ["issuer", "credentialStatus"]), "@context": finalContexts, type: finalTypes, } satisfies VC; @@ -76,7 +77,6 @@ export const wrapDocument = async ( const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); const verifiableCredential: WrappedOpenAttestationVC = { ...documentReadyForWrapping, - ...assertAsOaVcProps(documentReadyForWrapping, ["issuer", "credentialStatus"]), proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", From 2bc4066b645abc1a292fcf1e996ad257ce293c2d Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 19:18:59 +0800 Subject: [PATCH 030/107] refactor: assert oa vc than try vc --- src/4.0/wrap.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 8152c962..173fae07 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -9,18 +9,9 @@ import { interpretContexts, vcDataModel } from "./validate"; export const wrapDocument = async ( credential: T ): Promise> => { - /* 1a. W3C VC data model validation */ - const result = await vcDataModel.safeParseAsync(credential); - if (!result.success) - throw new Error( - `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify( - result.error.issues - )}` - ); - const rawDocument = result.data; - - /* 1b. Narrow down to OpenAttestation VC validation */ - const oav4context = await OpenAttestationVC.shape["@context"].safeParseAsync(rawDocument["@context"]); // Superficial check on user intention + /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ + const oav4context = await OpenAttestationVC.pick({ "@context": true }).safeParseAsync(credential); // Superficial check on user intention + let rawDocument: VC | undefined; if (oav4context.success) { const oav4 = await OpenAttestationVC.safeParseAsync(rawDocument); if (!oav4.success) { @@ -28,6 +19,20 @@ export const wrapDocument = async ( `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` ); } + rawDocument = oav4.data; + } + + /* 1b. only if OA VC validation fail do we continue with W3C VC data model validation */ + if (!rawDocument) { + const result = await vcDataModel.safeParseAsync(credential); + if (!result.success) + throw new Error( + `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify( + result.error.issues + )}` + ); + + rawDocument = result.data; } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ From 5fa75613869a6164c4969338be4234641dd553fa Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 19:26:37 +0800 Subject: [PATCH 031/107] fix: refer to new oa vc type in digest and verify --- src/4.0/digest.ts | 5 ++--- src/4.0/verify.ts | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 4cfc7359..e171804f 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -1,9 +1,8 @@ import { get, sortBy } from "lodash"; import { keccak256 } from "js-sha3"; -import { Salt } from "./types"; -import { OpenAttestationDocument } from "../__generated__/schema.4.0"; +import { OpenAttestationVC, Salt } from "./types"; -export const digestCredential = (document: OpenAttestationDocument, salts: Salt[], obfuscatedData: string[]) => { +export const digestCredential = (document: OpenAttestationVC, salts: Salt[], obfuscatedData: string[]) => { // Prepare array of hashes from visible data const hashedUnhashedDataArray = salts // Explictly allow falsy values (e.g. false, 0, etc.) as they can exist in the document diff --git a/src/4.0/verify.ts b/src/4.0/verify.ts index ac413a25..3bb7d97c 100644 --- a/src/4.0/verify.ts +++ b/src/4.0/verify.ts @@ -1,9 +1,11 @@ -import { WrappedDocument } from "./types"; +import { WrappedSignedOpenAttestationVC } from "./types"; import { digestCredential } from "./digest"; import { checkProof } from "../shared/merkle"; import { decodeSalt, salt } from "./salt"; -export const verify = (document: T): document is WrappedDocument => { +export const verify = ( + document: T +): document is WrappedSignedOpenAttestationVC => { if (!document.proof) { return false; } From 9ce12558a48800673dff709ed788d443fbe59385 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 19:31:20 +0800 Subject: [PATCH 032/107] fix: refer to new oa vc typings --- src/4.0/obfuscate.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 423c352a..90caa0f3 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -1,11 +1,10 @@ -import { OpenAttestationDocument } from "../__generated__/schema.4.0"; import { toBuffer } from "../shared/utils"; -import { WrappedDocument } from "./types"; import { cloneDeep, get, unset, pick } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; +import { OpenAttestationVC, WrappedOpenAttestationVC } from "./types"; -const obfuscate = (_data: WrappedDocument, fields: string[] | string) => { +const obfuscate = (_data: WrappedOpenAttestationVC, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data const fieldsAsArray = ([] as string[]).concat(fields); @@ -37,10 +36,10 @@ const obfuscate = (_data: WrappedDocument, fields: stri }; }; -export const obfuscateVerifiableCredential = ( - document: WrappedDocument, +export const obfuscateVerifiableCredential = ( + document: WrappedOpenAttestationVC, fields: string[] | string -): WrappedDocument => { +): WrappedOpenAttestationVC => { const { data, obfuscatedData } = obfuscate(document, fields); const currentObfuscatedData = document.proof.privacy.obfuscated; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); @@ -53,5 +52,5 @@ export const obfuscateVerifiableCredential = ( obfuscated: newObfuscatedData, }, }, - }; + } satisfies WrappedOpenAttestationVC as WrappedOpenAttestationVC; }; From e6bff0776d0a97b4d1dd1c5f02a7faadc2d7132b Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 19:49:44 +0800 Subject: [PATCH 033/107] fix: refer to new oa vc typings --- src/4.0/sign.ts | 10 +++++----- src/shared/utils/guard.ts | 18 +++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index 7ba89797..31f7c0f5 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -1,18 +1,18 @@ -import { OpenAttestationVC, WrappedDocument, SignedWrappedDocument, SignedWrappedProof } from "./types"; import { sign } from "../shared/signer"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "../shared/@types/sign"; import { isSignedWrappedV4Document } from "../shared/utils"; import { ethers } from "ethers"; +import { OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC } from "./types"; -export const signDocument = async ( - document: SignedWrappedDocument | WrappedDocument, +export const signDocument = async ( + document: WrappedSignedOpenAttestationVC | WrappedOpenAttestationVC, algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer -): Promise> => { +): Promise> => { if (isSignedWrappedV4Document(document)) throw new Error("Document has been signed"); const merkleRoot = `0x${document.proof.merkleRoot}`; const signature = await sign(algorithm, merkleRoot, keyOrSigner); - const proof: SignedWrappedProof = { + const proof: WrappedSignedOpenAttestationVC["proof"] = { ...document.proof, key: SigningKey.guard(keyOrSigner) ? keyOrSigner.public : `did:ethr:${await keyOrSigner.getAddress()}#controller`, signature, diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index 0f8f03d1..be2dea4d 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,7 +7,11 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { OpenAttestationVC as OpenAttestationDocumentV4, WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; +import { + OpenAttestationVC as OpenAttestationDocumentV4, + WrappedOpenAttestationVC, + WrappedSignedOpenAttestationVC, +} from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; @@ -76,10 +80,10 @@ export const isWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isWrappedV4Document = ( - document: any, +export const isWrappedV4Document = ( + document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is WrappedDocumentV4 => { +): document is T => { return diagnose({ version: "4.0", kind: "wrapped", document, debug: false, mode }).length === 0; }; @@ -112,9 +116,9 @@ export const isSignedWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isSignedWrappedV4Document = ( - document: any, +export const isSignedWrappedV4Document = ( + document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is SignedWrappedDocument => { +): document is T => { return diagnose({ version: "4.0", kind: "signed", document, debug: false, mode }).length === 0; }; From a259eaca3b97849860b50a2afa00b87c14d7acbb Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 20:35:54 +0800 Subject: [PATCH 034/107] fix: refer to new oa vc typings --- src/shared/utils/utils.ts | 44 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index b6264d70..5fb6248a 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -10,11 +10,9 @@ import * as v3 from "../../__generated__/schema.3.0"; import { WrappedDocument as WrappedDocumentV3 } from "../../3.0/types"; import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "../../__generated__/schema.3.0"; -import * as v4 from "../../__generated__/schema.4.0"; -import { WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; -import { OpenAttestationDocument as OpenAttestationDocumentV4 } from "../../__generated__/schema.4.0"; +import { WrappedOpenAttestationVC as WrappedDocumentV4 } from "../../4.0/types"; -import { OpenAttestationDocument, WrappedDocument, SchemaId, ContextUrl, ContextType } from "../@types/document"; +import { OpenAttestationDocument, WrappedDocument, SchemaId, ContextUrl } from "../@types/document"; import { isRawV2Document, isWrappedV2Document, @@ -24,6 +22,13 @@ import { isWrappedV4Document, } from "./guard"; +const VersionGuard = { + 2: isWrappedV2Document, + 3: isWrappedV3Document, + 4: isWrappedV4Document, +} as const satisfies Record boolean>; +type OAVersion = keyof typeof VersionGuard; + export type Hash = string | Buffer; type Extract

= P extends WrappedDocumentV2 ? T : never; export const getData = >(document: T): Extract => { @@ -101,13 +106,20 @@ export function getIssuerAddress(document: any): any { } export const getMerkleRoot = (document: any): string => { - if (isWrappedV2Document(document)) return document.signature.merkleRoot; - else if (isWrappedV3Document(document)) return document.proof.merkleRoot; - else if (isWrappedV4Document(document)) return document.proof.merkleRoot; - - throw new Error( - "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." - ); + const version = getVersion(document); + const getMerkleRoot: Record unknown | undefined> = { + 2: () => document?.signature?.merkleRoot, + 3: () => document?.proof?.merkleRoot, + 4: () => document?.proof?.merkleRoot, + }; + + const merkleRoot = getMerkleRoot[version](); + if (merkleRoot === undefined || typeof merkleRoot !== "string") + throw new Error( + "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." + ); + + return merkleRoot; }; export const getTargetHash = (document: any): string => { @@ -187,8 +199,8 @@ export const isDocumentRevokable = (document: any): boolean => { } else if (isWrappedV4Document(document)) { if (typeof document.issuer === "string" || !document.credentialStatus) return false; const isDidRevokableV4 = - document.issuer.identityProof?.identityProofType === v4.IdentityProofType.DNSDid - ? document.credentialStatus.type === "OpenAttestationOcspResponder" // TODO: Create suggested typings e.g. "v4.CredentialStatusType.OpenAttestationOcspResponder" + document.issuer.identityProof?.identityProofType === "DNS-DID" + ? document.credentialStatus.type === "OpenAttestationOcspResponder" : false; // TODO: OA v4 issuer schema not updated to support document store issuance yet // const isDocumentStoreRevokableV4 = ? @@ -225,7 +237,7 @@ export const isObfuscated = ( document: | WrappedDocumentV2 | WrappedDocumentV3 - | WrappedDocumentV4 + | WrappedDocumentV4 ): boolean => { if (isWrappedV2Document(document)) { return !!document.privacy?.obfuscatedData?.length; @@ -244,7 +256,7 @@ export const getObfuscatedData = ( document: | WrappedDocumentV2 | WrappedDocumentV3 - | WrappedDocumentV4 + | WrappedDocumentV4 ): string[] => { if (isWrappedV2Document(document)) { return document.privacy?.obfuscatedData || []; @@ -262,7 +274,7 @@ export const getObfuscatedData = ( export const isStringArray = (input: unknown): input is string[] => Array.isArray(input) && input.every((i) => typeof i === "string"); -export const getVersion = (document: unknown) => { +export const getVersion = (document: unknown): OAVersion => { if (typeof document === "object" && document !== null) { if ("version" in document && typeof document.version === "string") { switch (document.version) { From 8dfd6af975955e6c5b9b743bfb9138eda6bdf6af Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 20:39:31 +0800 Subject: [PATCH 035/107] refactor: improve naming consistency --- src/4.0/wrap.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 173fae07..fe4b2db8 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -24,15 +24,13 @@ export const wrapDocument = async ( /* 1b. only if OA VC validation fail do we continue with W3C VC data model validation */ if (!rawDocument) { - const result = await vcDataModel.safeParseAsync(credential); - if (!result.success) + const vc = await vcDataModel.safeParseAsync(credential); + if (!vc.success) throw new Error( - `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify( - result.error.issues - )}` + `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(vc.error.issues)}` ); - rawDocument = result.data; + rawDocument = vc.data; } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ From 9fce84aabf755b129ce41d5a6486ae17cee31a1b Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 24 Apr 2024 21:48:50 +0800 Subject: [PATCH 036/107] fix: typing, should be looser --- src/4.0/verify.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/4.0/verify.ts b/src/4.0/verify.ts index 3bb7d97c..1f1a0046 100644 --- a/src/4.0/verify.ts +++ b/src/4.0/verify.ts @@ -1,11 +1,9 @@ -import { WrappedSignedOpenAttestationVC } from "./types"; +import { WrappedOpenAttestationVC } from "./types"; import { digestCredential } from "./digest"; import { checkProof } from "../shared/merkle"; import { decodeSalt, salt } from "./salt"; -export const verify = ( - document: T -): document is WrappedSignedOpenAttestationVC => { +export const verify = (document: T): document is T => { if (!document.proof) { return false; } From d409c68a966f59469048a9fe3c5be70b3c554cf6 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 01:06:02 +0800 Subject: [PATCH 037/107] fix: a union b does not override a props with b props --- src/4.0/types.ts | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index c264090a..1b12d57e 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -90,12 +90,37 @@ const WrappedSignedProof = WrappedProof.and( }) ); -type WrappedOpenAttestationVC = T & { - proof: z.infer; -}; +const WrappedDocument = OpenAttestationVC.extend({ + proof: WrappedProof, +}); + +const SignedDocument = OpenAttestationVC.extend({ + proof: WrappedSignedProof, +}); + +type _WrappedDocument = z.infer; +type WrappedOpenAttestationVC< + T extends OpenAttestationVC = OpenAttestationVC, + U extends _WrappedDocument = Override< + T, + { + proof: z.infer; + } + > +> = U; + +type _SignedDocument = z.infer; +type WrappedSignedOpenAttestationVC< + T extends OpenAttestationVC = OpenAttestationVC, + U extends _SignedDocument = Override< + T, + { + proof: z.infer; + } + > +> = U; -type WrappedSignedOpenAttestationVC = T & { - proof: z.infer; -}; +// a & b does not override a props with b props +type Override, U extends Record> = Omit & U; export { VC, OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC, Salt }; From e30c8e8908be18a646ebc35d8a66bf1f6d0d19d7 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 11:51:35 +0800 Subject: [PATCH 038/107] fix: added assertion type to prevent accident extension to base type --- src/4.0/types.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 1b12d57e..49414b04 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -1,6 +1,5 @@ import z from "zod"; -// import { OpenAttestationDocument as OpenAttestationDocumentV4, ProofPurpose } from "../__generated__/schema.4.0"; import { vcDataModel, zodUri } from "./validate/dataModel"; import { ContextUrl, ContextType } from "../shared/@types/document"; @@ -16,6 +15,7 @@ const zodHexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex const OpenAttestationVC = vcDataModel.extend({ "@context": z + // Must be an array that starts with [baseContext, v4Context, ...] .tuple([z.literal(ContextUrl.v2_vc), z.literal(ContextUrl.v4_alpha)]) // Remaining items can be string or object @@ -71,7 +71,10 @@ const OpenAttestationVC = vcDataModel.extend({ .optional(), }); type VC = z.infer; -type OpenAttestationVC = z.infer; +// AssertStricter is used to ensure that we have zod extended from the base type while +// still being assignable to the base type. For example, if we accidentally extend and +// replaced '@context' to a boolean, this would fail the assertion. +type OpenAttestationVC = AssertStricter>; const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), @@ -120,7 +123,15 @@ type WrappedSignedOpenAttestationVC< > > = U; -// a & b does not override a props with b props -type Override, U extends Record> = Omit & U; +/** Overrides properties in the Target (a & b does not override a props with b props) */ +type Override, OverrideWith extends Record> = Omit< + Target, + keyof OverrideWith +> & + OverrideWith; + +/** Used to assert that StricterType is a stricter version of LooserType, and most importantly, that + * StricterType is STILL assignable to LooserType. */ +type AssertStricter = StricterType extends LooserType ? StricterType : never; export { VC, OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC, Salt }; From 194f19efb9a23d0b26256f6edb5de37d094ee5e6 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 11:55:45 +0800 Subject: [PATCH 039/107] refactor: wording --- src/4.0/types.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 49414b04..e1bf3c1c 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -71,10 +71,10 @@ const OpenAttestationVC = vcDataModel.extend({ .optional(), }); type VC = z.infer; -// AssertStricter is used to ensure that we have zod extended from the base type while +// AssertStricterOrEqual is used to ensure that we have zod extended from the base type while // still being assignable to the base type. For example, if we accidentally extend and // replaced '@context' to a boolean, this would fail the assertion. -type OpenAttestationVC = AssertStricter>; +type OpenAttestationVC = AssertStricterOrEqual>; const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), @@ -130,8 +130,8 @@ type Override, OverrideWith extends Recor > & OverrideWith; -/** Used to assert that StricterType is a stricter version of LooserType, and most importantly, that +/** Used to assert that StricterType is a stricter or equal version of LooserType, and most importantly, that * StricterType is STILL assignable to LooserType. */ -type AssertStricter = StricterType extends LooserType ? StricterType : never; +type AssertStricterOrEqual = StricterType extends LooserType ? StricterType : never; export { VC, OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC, Salt }; From cd34a7d386cd5858de7a823e7a95ac27de0afe96 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 12:04:59 +0800 Subject: [PATCH 040/107] refactor: better readability for wrapped types --- src/4.0/types.ts | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index e1bf3c1c..19a27463 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -93,35 +93,21 @@ const WrappedSignedProof = WrappedProof.and( }) ); -const WrappedDocument = OpenAttestationVC.extend({ - proof: WrappedProof, -}); +const WrappedDocumentProofShape = { proof: WrappedProof } as const; +const WrappedDocument = OpenAttestationVC.extend(WrappedDocumentProofShape); -const SignedDocument = OpenAttestationVC.extend({ - proof: WrappedSignedProof, -}); +const SignedDocumentProofShape = { proof: WrappedSignedProof } as const; +const SignedDocument = OpenAttestationVC.extend(SignedDocumentProofShape); + +type WrappedOpenAttestationVC = Override< + T, + Pick, keyof typeof WrappedDocumentProofShape> +>; -type _WrappedDocument = z.infer; -type WrappedOpenAttestationVC< - T extends OpenAttestationVC = OpenAttestationVC, - U extends _WrappedDocument = Override< - T, - { - proof: z.infer; - } - > -> = U; - -type _SignedDocument = z.infer; -type WrappedSignedOpenAttestationVC< - T extends OpenAttestationVC = OpenAttestationVC, - U extends _SignedDocument = Override< - T, - { - proof: z.infer; - } - > -> = U; +type WrappedSignedOpenAttestationVC = Override< + T, + Pick, keyof typeof SignedDocumentProofShape> +>; /** Overrides properties in the Target (a & b does not override a props with b props) */ type Override, OverrideWith extends Record> = Omit< From d3baf69a18a009a1bad71c1acd49071c0c578d6d Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 12:12:35 +0800 Subject: [PATCH 041/107] refactor: move things within file and some renaming --- src/4.0/types.ts | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 19a27463..ec3d29e1 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -4,16 +4,13 @@ import { vcDataModel, zodUri } from "./validate/dataModel"; import { ContextUrl, ContextType } from "../shared/@types/document"; const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); -type IdentityProofType = z.infer; - const Salt = z.object({ value: z.string(), path: z.string() }); -type Salt = z.infer; // Custom hex string validation function const HEX_STRING_REGEX = /^(0x)?[0-9a-fA-F]{40}$/; -const zodHexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex String" }); +const HexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex String" }); -const OpenAttestationVC = vcDataModel.extend({ +export const OpenAttestationVC = vcDataModel.extend({ "@context": z // Must be an array that starts with [baseContext, v4Context, ...] @@ -70,45 +67,49 @@ const OpenAttestationVC = vcDataModel.extend({ ) .optional(), }); -type VC = z.infer; -// AssertStricterOrEqual is used to ensure that we have zod extended from the base type while -// still being assignable to the base type. For example, if we accidentally extend and -// replaced '@context' to a boolean, this would fail the assertion. -type OpenAttestationVC = AssertStricterOrEqual>; const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), proofPurpose: z.literal("assertionMethod"), - targetHash: zodHexString, - proofs: z.array(zodHexString), - merkleRoot: zodHexString, + targetHash: HexString, + proofs: z.array(HexString), + merkleRoot: HexString, salts: z.string(), - privacy: z.object({ obfuscated: z.array(zodHexString) }), + privacy: z.object({ obfuscated: z.array(HexString) }), }); +const WrappedDocumentExtrasShape = { proof: WrappedProof } as const; +const WrappedDocument = OpenAttestationVC.extend(WrappedDocumentExtrasShape); -const WrappedSignedProof = WrappedProof.and( +const SignedWrappedProof = WrappedProof.and( z.object({ key: z.string(), signature: z.string(), }) ); +const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; +const SignedWrappedDocument = OpenAttestationVC.extend(SignedWrappedDocumentExtrasShape); -const WrappedDocumentProofShape = { proof: WrappedProof } as const; -const WrappedDocument = OpenAttestationVC.extend(WrappedDocumentProofShape); +export type VC = z.infer; -const SignedDocumentProofShape = { proof: WrappedSignedProof } as const; -const SignedDocument = OpenAttestationVC.extend(SignedDocumentProofShape); +// AssertStricterOrEqual is used to ensure that we have zod extended from the base type while +// still being assignable to the base type. For example, if we accidentally extend and +// replaced '@context' to a boolean, this would fail the assertion. +export type OpenAttestationVC = AssertStricterOrEqual>; -type WrappedOpenAttestationVC = Override< +export type WrappedOpenAttestationVC = Override< T, - Pick, keyof typeof WrappedDocumentProofShape> + Pick, keyof typeof WrappedDocumentExtrasShape> >; -type WrappedSignedOpenAttestationVC = Override< +export type WrappedSignedOpenAttestationVC = Override< T, - Pick, keyof typeof SignedDocumentProofShape> + Pick, keyof typeof SignedWrappedDocumentExtrasShape> >; +type IdentityProofType = z.infer; + +export type Salt = z.infer; + /** Overrides properties in the Target (a & b does not override a props with b props) */ type Override, OverrideWith extends Record> = Omit< Target, @@ -119,5 +120,3 @@ type Override, OverrideWith extends Recor /** Used to assert that StricterType is a stricter or equal version of LooserType, and most importantly, that * StricterType is STILL assignable to LooserType. */ type AssertStricterOrEqual = StricterType extends LooserType ? StricterType : never; - -export { VC, OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC, Salt }; From 3bdc13e35e1ab8f493304e6a5d082a18f6d4a5fb Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 12:29:57 +0800 Subject: [PATCH 042/107] refactor: improve naming of variables --- src/4.0/digest.ts | 4 ++-- src/4.0/obfuscate.ts | 12 ++++++------ src/4.0/sign.ts | 10 +++++----- src/4.0/types.ts | 22 +++++++++++----------- src/4.0/validate/dataModel.ts | 24 ++++++++++++------------ src/4.0/verify.ts | 4 ++-- src/4.0/wrap.ts | 24 +++++++++++------------- src/index.ts | 12 ++++++------ src/shared/utils/guard.ts | 10 +++------- src/shared/utils/utils.ts | 2 +- 10 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index e171804f..1ce553b8 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -1,8 +1,8 @@ import { get, sortBy } from "lodash"; import { keccak256 } from "js-sha3"; -import { OpenAttestationVC, Salt } from "./types"; +import { V4Document, Salt } from "./types"; -export const digestCredential = (document: OpenAttestationVC, salts: Salt[], obfuscatedData: string[]) => { +export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => { // Prepare array of hashes from visible data const hashedUnhashedDataArray = salts // Explictly allow falsy values (e.g. false, 0, etc.) as they can exist in the document diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 90caa0f3..56456869 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -2,9 +2,9 @@ import { toBuffer } from "../shared/utils"; import { cloneDeep, get, unset, pick } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; -import { OpenAttestationVC, WrappedOpenAttestationVC } from "./types"; +import { V4Document, V4WrappedDocument } from "./types"; -const obfuscate = (_data: WrappedOpenAttestationVC, fields: string[] | string) => { +const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data const fieldsAsArray = ([] as string[]).concat(fields); @@ -36,10 +36,10 @@ const obfuscate = (_data: WrappedOpenAttestationVC, fields: string[] | string) = }; }; -export const obfuscateVerifiableCredential = ( - document: WrappedOpenAttestationVC, +export const obfuscateVerifiableCredential = ( + document: V4WrappedDocument, fields: string[] | string -): WrappedOpenAttestationVC => { +): V4WrappedDocument => { const { data, obfuscatedData } = obfuscate(document, fields); const currentObfuscatedData = document.proof.privacy.obfuscated; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); @@ -52,5 +52,5 @@ export const obfuscateVerifiableCredential = ; + } satisfies V4WrappedDocument as V4WrappedDocument; }; diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index 31f7c0f5..3d3e6f3e 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -2,17 +2,17 @@ import { sign } from "../shared/signer"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "../shared/@types/sign"; import { isSignedWrappedV4Document } from "../shared/utils"; import { ethers } from "ethers"; -import { OpenAttestationVC, WrappedOpenAttestationVC, WrappedSignedOpenAttestationVC } from "./types"; +import { V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "./types"; -export const signDocument = async ( - document: WrappedSignedOpenAttestationVC | WrappedOpenAttestationVC, +export const signDocument = async ( + document: V4SignedWrappedDocument | V4WrappedDocument, algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer -): Promise> => { +): Promise> => { if (isSignedWrappedV4Document(document)) throw new Error("Document has been signed"); const merkleRoot = `0x${document.proof.merkleRoot}`; const signature = await sign(algorithm, merkleRoot, keyOrSigner); - const proof: WrappedSignedOpenAttestationVC["proof"] = { + const proof: V4SignedWrappedDocument["proof"] = { ...document.proof, key: SigningKey.guard(keyOrSigner) ? keyOrSigner.public : `did:ethr:${await keyOrSigner.getAddress()}#controller`, signature, diff --git a/src/4.0/types.ts b/src/4.0/types.ts index ec3d29e1..1d806d4d 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -1,6 +1,6 @@ import z from "zod"; -import { vcDataModel, zodUri } from "./validate/dataModel"; +import { W3cVerifiableCredential, Uri } from "./validate/dataModel"; import { ContextUrl, ContextType } from "../shared/@types/document"; const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); @@ -10,7 +10,7 @@ const Salt = z.object({ value: z.string(), path: z.string() }); const HEX_STRING_REGEX = /^(0x)?[0-9a-fA-F]{40}$/; const HexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex String" }); -export const OpenAttestationVC = vcDataModel.extend({ +export const V4Document = W3cVerifiableCredential.extend({ "@context": z // Must be an array that starts with [baseContext, v4Context, ...] @@ -26,7 +26,7 @@ export const OpenAttestationVC = vcDataModel.extend({ issuer: z.object({ // Must have id match uri pattern - id: zodUri, + id: Uri, type: z.literal("OpenAttestationIssuer"), name: z.string(), identityProof: z.object({ @@ -78,7 +78,7 @@ const WrappedProof = z.object({ privacy: z.object({ obfuscated: z.array(HexString) }), }); const WrappedDocumentExtrasShape = { proof: WrappedProof } as const; -const WrappedDocument = OpenAttestationVC.extend(WrappedDocumentExtrasShape); +export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape); const SignedWrappedProof = WrappedProof.and( z.object({ @@ -87,23 +87,23 @@ const SignedWrappedProof = WrappedProof.and( }) ); const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; -const SignedWrappedDocument = OpenAttestationVC.extend(SignedWrappedDocumentExtrasShape); +export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape); -export type VC = z.infer; +export type VC = z.infer; // AssertStricterOrEqual is used to ensure that we have zod extended from the base type while // still being assignable to the base type. For example, if we accidentally extend and // replaced '@context' to a boolean, this would fail the assertion. -export type OpenAttestationVC = AssertStricterOrEqual>; +export type V4Document = AssertStricterOrEqual>; -export type WrappedOpenAttestationVC = Override< +export type V4WrappedDocument = Override< T, - Pick, keyof typeof WrappedDocumentExtrasShape> + Pick, keyof typeof WrappedDocumentExtrasShape> >; -export type WrappedSignedOpenAttestationVC = Override< +export type V4SignedWrappedDocument = Override< T, - Pick, keyof typeof SignedWrappedDocumentExtrasShape> + Pick, keyof typeof SignedWrappedDocumentExtrasShape> >; type IdentityProofType = z.infer; diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts index 94a7b743..b867abb2 100644 --- a/src/4.0/validate/dataModel.ts +++ b/src/4.0/validate/dataModel.ts @@ -7,9 +7,9 @@ const baseType = "VerifiableCredential"; // Custom URI validation function const URI_REGEX = /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; -export const zodUri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); +export const Uri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); -export const vcDataModel = z.object({ +export const W3cVerifiableCredential = z.object({ "@context": z.union([ z.record(z.any()), z.string(), @@ -18,7 +18,7 @@ export const vcDataModel = z.object({ ]), // [Optional] If string: Must match uri pattern - id: zodUri.optional(), + id: Uri.optional(), type: z.union([ z.string(), @@ -30,13 +30,13 @@ export const vcDataModel = z.object({ .union([ // If object: Must have id match uri pattern and type defined z.object({ - id: zodUri, + id: Uri, type: z.string(), }), // If array: Every object must have id match uri pattern and type defined z.array( z.object({ - id: zodUri, + id: Uri, type: z.string(), }) ), @@ -45,10 +45,10 @@ export const vcDataModel = z.object({ issuer: z.union([ // If string: Must match uri pattern - zodUri, + Uri, // If object: Must have id match uri pattern z.object({ - id: zodUri, + id: Uri, }), ]), @@ -72,7 +72,7 @@ export const vcDataModel = z.object({ credentialStatus: z .object({ // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) - id: zodUri.optional(), + id: Uri.optional(), // Must have type defined type: z.string(), }) @@ -82,13 +82,13 @@ export const vcDataModel = z.object({ .union([ // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) z.object({ - id: zodUri.optional(), + id: Uri.optional(), type: z.string(), }), // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) z.array( z.object({ - id: zodUri.optional(), + id: Uri.optional(), type: z.string(), }) ), @@ -99,13 +99,13 @@ export const vcDataModel = z.object({ .union([ // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) z.object({ - id: zodUri.optional(), + id: Uri.optional(), type: z.string(), }), // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) z.array( z.object({ - id: zodUri.optional(), + id: Uri.optional(), type: z.string(), }) ), diff --git a/src/4.0/verify.ts b/src/4.0/verify.ts index 1f1a0046..1deaa011 100644 --- a/src/4.0/verify.ts +++ b/src/4.0/verify.ts @@ -1,9 +1,9 @@ -import { WrappedOpenAttestationVC } from "./types"; +import { V4WrappedDocument } from "./types"; import { digestCredential } from "./digest"; import { checkProof } from "../shared/merkle"; import { decodeSalt, salt } from "./salt"; -export const verify = (document: T): document is T => { +export const verify = (document: T): document is T => { if (!document.proof) { return false; } diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index fe4b2db8..a260f9f0 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,19 +1,17 @@ import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; import { ContextType, ContextUrl } from "../shared/@types/document"; -import { OpenAttestationVC, VC, WrappedOpenAttestationVC } from "./types"; +import { V4Document, VC, V4WrappedDocument } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; -import { interpretContexts, vcDataModel } from "./validate"; +import { interpretContexts, W3cVerifiableCredential } from "./validate"; -export const wrapDocument = async ( - credential: T -): Promise> => { +export const wrapDocument = async (document: T): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ - const oav4context = await OpenAttestationVC.pick({ "@context": true }).safeParseAsync(credential); // Superficial check on user intention + const oav4context = await V4Document.pick({ "@context": true }).safeParseAsync(document); // Superficial check on user intention let rawDocument: VC | undefined; if (oav4context.success) { - const oav4 = await OpenAttestationVC.safeParseAsync(rawDocument); + const oav4 = await V4Document.safeParseAsync(rawDocument); if (!oav4.success) { throw new Error( `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` @@ -24,7 +22,7 @@ export const wrapDocument = async ( /* 1b. only if OA VC validation fail do we continue with W3C VC data model validation */ if (!rawDocument) { - const vc = await vcDataModel.safeParseAsync(credential); + const vc = await W3cVerifiableCredential.safeParseAsync(document); if (!vc.success) throw new Error( `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(vc.error.issues)}` @@ -47,7 +45,7 @@ export const wrapDocument = async ( rawDocument["@context"].forEach((context) => contexts.add(context)); } REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); - const finalContexts: OpenAttestationVC["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; + const finalContexts: V4Document["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; /* 4. Type validation */ // Ensure that required types are present and in the correct order @@ -60,7 +58,7 @@ export const wrapDocument = async ( types.forEach((type) => types.add(type)); } REQUIRED_TYPES.forEach((t) => types.delete(t)); - const finalTypes: OpenAttestationVC["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; + const finalTypes: V4Document["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; const documentReadyForWrapping = { ...rawDocument, @@ -78,7 +76,7 @@ export const wrapDocument = async ( const merkleTree = new MerkleTree(batchBuffers); const merkleRoot = merkleTree.getRoot().toString("hex"); const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); - const verifiableCredential: WrappedOpenAttestationVC = { + const verifiableCredential: V4WrappedDocument = { ...documentReadyForWrapping, proof: { type: "OpenAttestationMerkleProofSignature2018", @@ -93,7 +91,7 @@ export const wrapDocument = async ( }, }; - return verifiableCredential as WrappedOpenAttestationVC; + return verifiableCredential as V4WrappedDocument; }; function assertAsOaVcProps(obj: VC, keys: K[]) { @@ -101,5 +99,5 @@ function assertAsOaVcProps(obj: VC, keys: K[]) { Object.entries(obj).forEach(([k, v]) => { if (keys.includes(k as K)) temp[k] = v; }); - return temp as { [key in K]: OpenAttestationVC[key] }; + return temp as { [key in K]: V4Document[key] }; } diff --git a/src/index.ts b/src/index.ts index d68ef8fd..3c7d290e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,15 +60,15 @@ export function __unsafe__use__it__at__your__own__risks__wrapDocuments( +export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocument( data: T -): Promise> { +): Promise> { return wrapDocumentV4(data); } -export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments( +export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments( dataArray: T[] -): Promise[]> { +): Promise[]> { return wrapDocumentsV4(dataArray); } @@ -93,7 +93,7 @@ export function verifySignature( algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer ): Promise>; -export async function signDocument( +export async function signDocument( document: v4.SignedWrappedDocument | v4.WrappedDocument, algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index be2dea4d..508f7678 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,11 +7,7 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { - OpenAttestationVC as OpenAttestationDocumentV4, - WrappedOpenAttestationVC, - WrappedSignedOpenAttestationVC, -} from "../../4.0/types"; +import { V4Document as OpenAttestationDocumentV4, V4WrappedDocument, V4SignedWrappedDocument } from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; @@ -80,7 +76,7 @@ export const isWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isWrappedV4Document = ( +export const isWrappedV4Document = ( document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } ): document is T => { @@ -116,7 +112,7 @@ export const isSignedWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isSignedWrappedV4Document = ( +export const isSignedWrappedV4Document = ( document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } ): document is T => { diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index 5fb6248a..95eb96a6 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -10,7 +10,7 @@ import * as v3 from "../../__generated__/schema.3.0"; import { WrappedDocument as WrappedDocumentV3 } from "../../3.0/types"; import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "../../__generated__/schema.3.0"; -import { WrappedOpenAttestationVC as WrappedDocumentV4 } from "../../4.0/types"; +import { V4WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; import { OpenAttestationDocument, WrappedDocument, SchemaId, ContextUrl } from "../@types/document"; import { From db12f509a966119e89e1f08398b8821fbf97829e Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 25 Apr 2024 12:49:33 +0800 Subject: [PATCH 043/107] fix: use new typings --- .vscode/settings.json | 2 ++ src/4.0/__tests__/digest.test.ts | 4 ++-- src/shared/@types/document.ts | 16 ++++++---------- src/shared/utils/diagnose.ts | 5 ----- 4 files changed, 10 insertions(+), 17 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 612e6d14..7d8609c5 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -1,12 +1,12 @@ import { cloneDeep } from "lodash"; import { digestCredential } from "../digest"; -import { WrappedDocument } from "../../4.0/types"; import { obfuscateVerifiableCredential } from "../obfuscate"; import { decodeSalt } from "../salt"; import sample from "../../../test/fixtures/v4/did-signed-wrapped.json"; +import { V4WrappedDocument } from "../types"; // TODO: remove unknown -const verifiableCredential = sample as unknown as WrappedDocument; +const verifiableCredential = sample as unknown as V4WrappedDocument; // Digest will change whenever sample document is regenerated const credentialRoot = "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"; diff --git a/src/shared/@types/document.ts b/src/shared/@types/document.ts index 0d8c8bec..153dec84 100644 --- a/src/shared/@types/document.ts +++ b/src/shared/@types/document.ts @@ -1,7 +1,6 @@ // types generated by quicktype during postinstall phase import { OpenAttestationDocument as OpenAttestationDocumentV2 } from "../../__generated__/schema.2.0"; import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "../../__generated__/schema.3.0"; -import { OpenAttestationDocument as OpenAttestationDocumentV4 } from "../../__generated__/schema.4.0"; import { SignedWrappedDocument as SignedWrappedDocumentV2, WrappedDocument as WrappedDocumentV2, @@ -10,27 +9,24 @@ import { SignedWrappedDocument as SignedWrappedDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { - SignedWrappedDocument as SignedWrappedDocumentV4, - WrappedDocument as WrappedDocumentV4, -} from "../../4.0/types"; import { Literal, Static, String } from "runtypes"; import { ethers } from "ethers"; +import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "src/4.0/types"; -export type OpenAttestationDocument = OpenAttestationDocumentV2 | OpenAttestationDocumentV3 | OpenAttestationDocumentV4; +export type OpenAttestationDocument = OpenAttestationDocumentV2 | OpenAttestationDocumentV3 | V4Document; export type WrappedDocument = T extends OpenAttestationDocumentV2 ? WrappedDocumentV2 : T extends OpenAttestationDocumentV3 ? WrappedDocumentV3 - : T extends OpenAttestationDocumentV4 - ? WrappedDocumentV4 + : T extends V4Document + ? V4WrappedDocument : unknown; export type SignedWrappedDocument = T extends OpenAttestationDocumentV2 ? SignedWrappedDocumentV2 : T extends OpenAttestationDocumentV3 ? SignedWrappedDocumentV3 - : T extends OpenAttestationDocumentV4 - ? SignedWrappedDocumentV4 + : T extends V4Document + ? V4SignedWrappedDocument : unknown; export enum SchemaId { diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index bc857504..0b20a8f0 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -6,11 +6,6 @@ import { VerifiableCredentialWrappedProofStrict as WrappedProofStrictV3, VerifiableCredentialSignedProof as SignedWrappedProofV3, } from "../../3.0/types"; -import { - WrappedProof as WrappedProofV4, - WrappedProofStrict as WrappedProofStrictV4, - SignedWrappedProof as SignedWrappedProofV4, -} from "../../4.0/types"; import { ArrayProof, Signature, SignatureStrict } from "../../2.0/types"; import { clone, cloneDeepWith } from "lodash"; import { buildAjv, getSchema } from "../ajv"; From 22df1652b96a8211c0c6372551e278b1dd11b039 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 00:37:20 +0800 Subject: [PATCH 044/107] fix: passthroughs needed to allow extension, added name and render method so they dont get stripped --- src/4.0/validate/dataModel.ts | 85 ++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts index b867abb2..94304f25 100644 --- a/src/4.0/validate/dataModel.ts +++ b/src/4.0/validate/dataModel.ts @@ -17,6 +17,9 @@ export const W3cVerifiableCredential = z.object({ z.tuple([z.literal(ContextUrl.v2_vc)]).rest(z.union([z.string(), z.record(z.any())])), ]), + // [Optional] + name: z.string().optional(), + // [Optional] If string: Must match uri pattern id: Uri.optional(), @@ -29,16 +32,20 @@ export const W3cVerifiableCredential = z.object({ credentialSchema: z .union([ // If object: Must have id match uri pattern and type defined - z.object({ - id: Uri, - type: z.string(), - }), - // If array: Every object must have id match uri pattern and type defined - z.array( - z.object({ + z + .object({ id: Uri, type: z.string(), }) + .passthrough(), + // If array: Every object must have id match uri pattern and type defined + z.array( + z + .object({ + id: Uri, + type: z.string(), + }) + .passthrough() ), ]) .optional(), @@ -47,9 +54,11 @@ export const W3cVerifiableCredential = z.object({ // If string: Must match uri pattern Uri, // If object: Must have id match uri pattern - z.object({ - id: Uri, - }), + z + .object({ + id: Uri, + }) + .passthrough(), ]), validFrom: z.string().datetime({ offset: true }).optional(), @@ -76,21 +85,29 @@ export const W3cVerifiableCredential = z.object({ // Must have type defined type: z.string(), }) + .passthrough() .optional(), + // [Optional] This is at risk of being removed from the w3c spec + renderMethod: z.any().optional(), + termsOfUse: z .union([ // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - z.object({ - id: Uri.optional(), - type: z.string(), - }), - // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - z.array( - z.object({ + z + .object({ id: Uri.optional(), type: z.string(), }) + .passthrough(), + // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + z.array( + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough() ), ]) .optional(), @@ -98,16 +115,20 @@ export const W3cVerifiableCredential = z.object({ evidence: z .union([ // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - z.object({ - id: Uri.optional(), - type: z.string(), - }), - // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - z.array( - z.object({ + z + .object({ id: Uri.optional(), type: z.string(), }) + .passthrough(), + // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + z.array( + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough() ), ]) .optional(), @@ -115,14 +136,18 @@ export const W3cVerifiableCredential = z.object({ proof: z .union([ // If object: Must have type defined - z.object({ - type: z.string(), - }), - // If array: Every object must have type defined - z.array( - z.object({ + z + .object({ type: z.string(), }) + .passthrough(), + // If array: Every object must have type defined + z.array( + z + .object({ + type: z.string(), + }) + .passthrough() ), ]) .optional(), From fc0ce61fec95619471220c388a9ad51d95e89dc3 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 00:37:37 +0800 Subject: [PATCH 045/107] test: added a guard test which is currently failing --- src/4.0/__tests__/guard.test.ts | 180 ++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/4.0/__tests__/guard.test.ts diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts new file mode 100644 index 00000000..4660f1f5 --- /dev/null +++ b/src/4.0/__tests__/guard.test.ts @@ -0,0 +1,180 @@ +import { V4SignedWrappedDocument, VC, V4Document, V4WrappedDocument } from "../types"; +import { W3cVerifiableCredential } from "../validate"; + +const RAW_DOCUMENT: V4Document = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, +}; + +const SIGNED_WRAPPED: V4SignedWrappedDocument = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + proofs: [], + merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + salts: + "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", + privacy: { obfuscated: [] }, + key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + signature: + "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", + }, +}; + +describe("v4 guard", () => { + describe("given a raw document", () => { + test("should pass w3c vc validation without removal of any data", () => { + const w3cVerifiableCredential: VC = RAW_DOCUMENT; + const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); + expect(results).toEqual(RAW_DOCUMENT); + }); + + test("should pass document validation without removal of any data", () => { + const results = V4Document.parse(RAW_DOCUMENT); + expect(results).toEqual(RAW_DOCUMENT); + }); + + test("should fail wrapped document validation", () => { + const results = V4WrappedDocument.safeParse(RAW_DOCUMENT); + expect(results.success).toBe(false); + expect((results as { error: unknown }).error).toMatchInlineSnapshot(` + [ZodError: [ + { + "code": "invalid_type", + "expected": "object", + "received": "undefined", + "path": [ + "proof" + ], + "message": "Required" + } + ]] + `); + }); + + test("should fail signed wrapped document validation", () => { + const results = V4SignedWrappedDocument.safeParse(RAW_DOCUMENT); + expect(results.success).toBe(false); + expect((results as { error: unknown }).error).toMatchInlineSnapshot(` + [ZodError: [ + { + "code": "invalid_type", + "expected": "object", + "received": "undefined", + "path": [ + "proof" + ], + "message": "Required" + }, + { + "code": "invalid_type", + "expected": "object", + "received": "undefined", + "path": [ + "proof" + ], + "message": "Required" + } + ]] + `); + }); + }); + + describe("given a signed wrapped document", () => { + test("should pass w3c vc validation without removal of any data", () => { + const w3cVerifiableCredential: VC = SIGNED_WRAPPED; + const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); + expect(results).toEqual(SIGNED_WRAPPED); + }); + + test("should pass document validation without removal of any data", () => { + const v4Document: V4Document = SIGNED_WRAPPED; + const results = V4Document.parse(v4Document); + expect(results).toEqual(SIGNED_WRAPPED); + }); + + test("should pass wrapped document validation without removal of any data", () => { + const v4WrappedDocument: V4WrappedDocument = SIGNED_WRAPPED; + const results = V4WrappedDocument.parse(v4WrappedDocument); + expect(results).toEqual(SIGNED_WRAPPED); + }); + + test("should pass signed wrapped document validation without removal of any data", () => { + const results = V4SignedWrappedDocument.parse(SIGNED_WRAPPED); + expect(results).toEqual(SIGNED_WRAPPED); + }); + }); +}); From 8f421784b58425096b6e470a312b55c8cb2c513e Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 00:59:28 +0800 Subject: [PATCH 046/107] refactor: merged data model with types and renamed VC type --- src/4.0/__tests__/guard.test.ts | 7 +- src/4.0/types.ts | 157 +++++++++++++++++++++++++++++++- src/4.0/validate/dataModel.ts | 154 ------------------------------- src/4.0/validate/index.ts | 1 - src/4.0/wrap.ts | 23 +++-- 5 files changed, 172 insertions(+), 170 deletions(-) delete mode 100644 src/4.0/validate/dataModel.ts diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index 4660f1f5..110d3cde 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -1,5 +1,4 @@ -import { V4SignedWrappedDocument, VC, V4Document, V4WrappedDocument } from "../types"; -import { W3cVerifiableCredential } from "../validate"; +import { W3cVerifiableCredential, V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../types"; const RAW_DOCUMENT: V4Document = { "@context": [ @@ -97,7 +96,7 @@ const SIGNED_WRAPPED: V4SignedWrappedDocument = { describe("v4 guard", () => { describe("given a raw document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: VC = RAW_DOCUMENT; + const w3cVerifiableCredential: W3cVerifiableCredential = RAW_DOCUMENT; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); expect(results).toEqual(RAW_DOCUMENT); }); @@ -155,7 +154,7 @@ describe("v4 guard", () => { describe("given a signed wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: VC = SIGNED_WRAPPED; + const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); expect(results).toEqual(SIGNED_WRAPPED); }); diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 1d806d4d..7d0e809e 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -1,8 +1,157 @@ import z from "zod"; - -import { W3cVerifiableCredential, Uri } from "./validate/dataModel"; import { ContextUrl, ContextType } from "../shared/@types/document"; +const BASE_TYPE = "VerifiableCredential" as const; + +// Custom URI validation function +const URI_REGEX = + /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; +const Uri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); + +export const W3cVerifiableCredential = z.object({ + "@context": z.union([ + z.record(z.any()), + z.string(), + // If array: First item must be baseContext, while remaining items can be string or object + z.tuple([z.literal(ContextUrl.v2_vc)]).rest(z.union([z.string(), z.record(z.any())])), + ]), + + // [Optional] + name: z.string().optional(), + + // [Optional] If string: Must match uri pattern + id: Uri.optional(), + + type: z.union([ + z.string(), + // If array: Must have VerifiableCredential, while remaining items can be any string + z.array(z.string()).refine((types) => types.includes(BASE_TYPE), { message: `Type must include ${BASE_TYPE}` }), + ]), + + credentialSchema: z + .union([ + // If object: Must have id match uri pattern and type defined + z + .object({ + id: Uri, + type: z.string(), + }) + .passthrough(), + // If array: Every object must have id match uri pattern and type defined + z.array( + z + .object({ + id: Uri, + type: z.string(), + }) + .passthrough() + ), + ]) + .optional(), + + issuer: z.union([ + // If string: Must match uri pattern + Uri, + // If object: Must have id match uri pattern + z + .object({ + id: Uri, + }) + .passthrough(), + ]), + + validFrom: z.string().datetime({ offset: true }).optional(), + + validUntil: z.string().datetime({ offset: true }).optional(), + + credentialSubject: z.union([ + // If object: Cannot be empty (i.e. minimum 1 key) + z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { + message: "Must have at least one key", + }), + // If array: Every object cannot be empty (i.e. minimum 1 key) + z.array( + z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { + message: "Must have at least one key", + }) + ), + ]), + + credentialStatus: z + .object({ + // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) + id: Uri.optional(), + // Must have type defined + type: z.string(), + }) + .passthrough() + .optional(), + + // [Optional] This is at risk of being removed from the w3c spec + renderMethod: z.any().optional(), + + termsOfUse: z + .union([ + // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough(), + // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) + z.array( + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough() + ), + ]) + .optional(), + + evidence: z + .union([ + // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough(), + // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) + z.array( + z + .object({ + id: Uri.optional(), + type: z.string(), + }) + .passthrough() + ), + ]) + .optional(), + + proof: z + .union([ + // If object: Must have type defined + z + .object({ + type: z.string(), + }) + .passthrough(), + // If array: Every object must have type defined + z.array( + z + .object({ + type: z.string(), + }) + .passthrough() + ), + ]) + .optional(), +}); + const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); const Salt = z.object({ value: z.string(), path: z.string() }); @@ -89,12 +238,12 @@ const SignedWrappedProof = WrappedProof.and( const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape); -export type VC = z.infer; +export type W3cVerifiableCredential = z.infer; // AssertStricterOrEqual is used to ensure that we have zod extended from the base type while // still being assignable to the base type. For example, if we accidentally extend and // replaced '@context' to a boolean, this would fail the assertion. -export type V4Document = AssertStricterOrEqual>; +export type V4Document = AssertStricterOrEqual>; export type V4WrappedDocument = Override< T, diff --git a/src/4.0/validate/dataModel.ts b/src/4.0/validate/dataModel.ts deleted file mode 100644 index 94304f25..00000000 --- a/src/4.0/validate/dataModel.ts +++ /dev/null @@ -1,154 +0,0 @@ -import z from "zod"; - -import { ContextUrl } from "../../shared/@types/document"; - -const baseType = "VerifiableCredential"; - -// Custom URI validation function -const URI_REGEX = - /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; -export const Uri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); - -export const W3cVerifiableCredential = z.object({ - "@context": z.union([ - z.record(z.any()), - z.string(), - // If array: First item must be baseContext, while remaining items can be string or object - z.tuple([z.literal(ContextUrl.v2_vc)]).rest(z.union([z.string(), z.record(z.any())])), - ]), - - // [Optional] - name: z.string().optional(), - - // [Optional] If string: Must match uri pattern - id: Uri.optional(), - - type: z.union([ - z.string(), - // If array: Must have VerifiableCredential, while remaining items can be any string - z.array(z.string()).refine((types) => types.includes(baseType), { message: `Type must include ${baseType}` }), - ]), - - credentialSchema: z - .union([ - // If object: Must have id match uri pattern and type defined - z - .object({ - id: Uri, - type: z.string(), - }) - .passthrough(), - // If array: Every object must have id match uri pattern and type defined - z.array( - z - .object({ - id: Uri, - type: z.string(), - }) - .passthrough() - ), - ]) - .optional(), - - issuer: z.union([ - // If string: Must match uri pattern - Uri, - // If object: Must have id match uri pattern - z - .object({ - id: Uri, - }) - .passthrough(), - ]), - - validFrom: z.string().datetime({ offset: true }).optional(), - - validUntil: z.string().datetime({ offset: true }).optional(), - - credentialSubject: z.union([ - // If object: Cannot be empty (i.e. minimum 1 key) - z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { - message: "Must have at least one key", - }), - // If array: Every object cannot be empty (i.e. minimum 1 key) - z.array( - z.record(z.any()).refine((obj) => Object.keys(obj).length > 0, { - message: "Must have at least one key", - }) - ), - ]), - - credentialStatus: z - .object({ - // If id is present, id must match uri pattern (credentialStatus.id is optional and can be undefined) - id: Uri.optional(), - // Must have type defined - type: z.string(), - }) - .passthrough() - .optional(), - - // [Optional] This is at risk of being removed from the w3c spec - renderMethod: z.any().optional(), - - termsOfUse: z - .union([ - // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - z - .object({ - id: Uri.optional(), - type: z.string(), - }) - .passthrough(), - // If array: Every object must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) - z.array( - z - .object({ - id: Uri.optional(), - type: z.string(), - }) - .passthrough() - ), - ]) - .optional(), - - evidence: z - .union([ - // If object: Must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - z - .object({ - id: Uri.optional(), - type: z.string(), - }) - .passthrough(), - // If array: Every object must have type defined. If id is present, id must match uri pattern (evidence.id is optional and can be undefined) - z.array( - z - .object({ - id: Uri.optional(), - type: z.string(), - }) - .passthrough() - ), - ]) - .optional(), - - proof: z - .union([ - // If object: Must have type defined - z - .object({ - type: z.string(), - }) - .passthrough(), - // If array: Every object must have type defined - z.array( - z - .object({ - type: z.string(), - }) - .passthrough() - ), - ]) - .optional(), -}); diff --git a/src/4.0/validate/index.ts b/src/4.0/validate/index.ts index a29ffd44..2edd280c 100644 --- a/src/4.0/validate/index.ts +++ b/src/4.0/validate/index.ts @@ -1,2 +1 @@ export * from "./context"; -export * from "./dataModel"; diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index a260f9f0..b1437f1d 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,15 +1,15 @@ import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; import { ContextType, ContextUrl } from "../shared/@types/document"; -import { V4Document, VC, V4WrappedDocument } from "./types"; +import { V4Document, V4WrappedDocument, W3cVerifiableCredential } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; -import { interpretContexts, W3cVerifiableCredential } from "./validate"; +import { interpretContexts } from "./validate"; export const wrapDocument = async (document: T): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ const oav4context = await V4Document.pick({ "@context": true }).safeParseAsync(document); // Superficial check on user intention - let rawDocument: VC | undefined; + let rawDocument: W3cVerifiableCredential | undefined; if (oav4context.success) { const oav4 = await V4Document.safeParseAsync(rawDocument); if (!oav4.success) { @@ -62,10 +62,10 @@ export const wrapDocument = async (document: T): Promise(document: T): Promise; }; -function assertAsOaVcProps(obj: VC, keys: K[]) { +/** Extract a set of properties from w3cVerifiableCredential but only include the ones + * that are defined in the original document. For example, if we extract + * "a" and "b" from { b: "something" } we should only get { b: "something" } NOT + * { a: undefined, b: "something" }. We also assert that the extracted properties + * are of V4Document type. + **/ +function assertAsV4DocumentProps( + original: W3cVerifiableCredential, + keys: K[] +) { const temp: Record = {}; - Object.entries(obj).forEach(([k, v]) => { + Object.entries(original).forEach(([k, v]) => { if (keys.includes(k as K)) temp[k] = v; }); return temp as { [key in K]: V4Document[key] }; From 31872374d83f4aa36aad9000424ca060c82f0015 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 01:00:47 +0800 Subject: [PATCH 047/107] refactor: improve helper fn naming --- src/4.0/wrap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index b1437f1d..c263f316 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -62,7 +62,7 @@ export const wrapDocument = async (document: T): Promise(document: T): Promise( +function extractAndAssertAsV4DocumentProps( original: W3cVerifiableCredential, keys: K[] ) { From 0f3d311cec30bb780bd73e9ba3cbef494b74444b Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 12:41:12 +0800 Subject: [PATCH 048/107] fix: use extend instead of and since it results in dup errors, remove hex validation since these props are generated by us, allow passthrough for wrap proof so that a signed wrap can pass a wrap only validation test --- src/4.0/types.ts | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 7d0e809e..b42d157e 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -155,10 +155,6 @@ export const W3cVerifiableCredential = z.object({ const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); const Salt = z.object({ value: z.string(), path: z.string() }); -// Custom hex string validation function -const HEX_STRING_REGEX = /^(0x)?[0-9a-fA-F]{40}$/; -const HexString = z.string().regex(HEX_STRING_REGEX, { message: "Invalid Hex String" }); - export const V4Document = W3cVerifiableCredential.extend({ "@context": z @@ -220,21 +216,16 @@ export const V4Document = W3cVerifiableCredential.extend({ const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), proofPurpose: z.literal("assertionMethod"), - targetHash: HexString, - proofs: z.array(HexString), - merkleRoot: HexString, + targetHash: z.string(), + proofs: z.array(z.string()), + merkleRoot: z.string(), salts: z.string(), - privacy: z.object({ obfuscated: z.array(HexString) }), + privacy: z.object({ obfuscated: z.array(z.string()) }), }); -const WrappedDocumentExtrasShape = { proof: WrappedProof } as const; +const WrappedDocumentExtrasShape = { proof: WrappedProof.passthrough() } as const; export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape); -const SignedWrappedProof = WrappedProof.and( - z.object({ - key: z.string(), - signature: z.string(), - }) -); +const SignedWrappedProof = WrappedProof.extend({ key: z.string(), signature: z.string() }); const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape); From 3c40ebc3d56613fa34d132062b84ebff93a8bc69 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 12:41:22 +0800 Subject: [PATCH 049/107] test: update snapshot --- src/4.0/__tests__/guard.test.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index 110d3cde..f16712e8 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -129,15 +129,6 @@ describe("v4 guard", () => { expect(results.success).toBe(false); expect((results as { error: unknown }).error).toMatchInlineSnapshot(` [ZodError: [ - { - "code": "invalid_type", - "expected": "object", - "received": "undefined", - "path": [ - "proof" - ], - "message": "Required" - }, { "code": "invalid_type", "expected": "object", From 946ae6325ed281a6f8635b68e458280608f2b8be Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 12:54:05 +0800 Subject: [PATCH 050/107] fix: was using the wrong document for the check --- src/4.0/wrap.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index c263f316..4666eec0 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -9,40 +9,40 @@ import { interpretContexts } from "./validate"; export const wrapDocument = async (document: T): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ const oav4context = await V4Document.pick({ "@context": true }).safeParseAsync(document); // Superficial check on user intention - let rawDocument: W3cVerifiableCredential | undefined; + let validatedRawDocument: W3cVerifiableCredential | undefined; if (oav4context.success) { - const oav4 = await V4Document.safeParseAsync(rawDocument); + const oav4 = await V4Document.safeParseAsync(document); if (!oav4.success) { throw new Error( `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` ); } - rawDocument = oav4.data; + validatedRawDocument = oav4.data; } /* 1b. only if OA VC validation fail do we continue with W3C VC data model validation */ - if (!rawDocument) { + if (!validatedRawDocument) { const vc = await W3cVerifiableCredential.safeParseAsync(document); if (!vc.success) throw new Error( `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(vc.error.issues)}` ); - rawDocument = vc.data; + validatedRawDocument = vc.data; } /* 2. Ensure provided @context are interpretable (e.g. valid @context URL, all types are mapped, etc.) */ - await interpretContexts(rawDocument); + await interpretContexts(validatedRawDocument); /* 3. Context validation */ // Ensure that required contexts are present and in the correct order // type: [Base, OA, ...] const REQUIRED_CONTEXTS = [ContextUrl.v2_vc, ContextUrl.v4_alpha] as const; const contexts = new Set(REQUIRED_CONTEXTS); - if (typeof rawDocument["@context"] === "string") { - contexts.add(rawDocument["@context"]); - } else if (isStringArray(rawDocument["@context"])) { - rawDocument["@context"].forEach((context) => contexts.add(context)); + if (typeof validatedRawDocument["@context"] === "string") { + contexts.add(validatedRawDocument["@context"]); + } else if (isStringArray(validatedRawDocument["@context"])) { + validatedRawDocument["@context"].forEach((context) => contexts.add(context)); } REQUIRED_CONTEXTS.forEach((c) => contexts.delete(c)); const finalContexts: V4Document["@context"] = [...REQUIRED_CONTEXTS, ...Array.from(contexts)]; @@ -52,17 +52,17 @@ export const wrapDocument = async (document: T): Promise([ContextType.BaseContext, ContextType.V4AlphaContext]); - if (typeof rawDocument["type"] === "string") { - types.add(rawDocument["type"]); - } else if (isStringArray(rawDocument["type"])) { + if (typeof validatedRawDocument["type"] === "string") { + types.add(validatedRawDocument["type"]); + } else if (isStringArray(validatedRawDocument["type"])) { types.forEach((type) => types.add(type)); } REQUIRED_TYPES.forEach((t) => types.delete(t)); const finalTypes: V4Document["type"] = [...REQUIRED_TYPES, ...Array.from(types)]; const documentReadyForWrapping = { - ...rawDocument, - ...extractAndAssertAsV4DocumentProps(rawDocument, ["issuer", "credentialStatus"]), + ...validatedRawDocument, + ...extractAndAssertAsV4DocumentProps(validatedRawDocument, ["issuer", "credentialStatus"]), "@context": finalContexts, type: finalTypes, } satisfies W3cVerifiableCredential; From d8c46940bf18005de5a3ffabd2a5c9dd765440e4 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 12:59:47 +0800 Subject: [PATCH 051/107] test: added tests for wrapped only document --- src/4.0/__tests__/guard.test.ts | 90 +++++++++++++++++++++++++++++++++ src/shared/ajv.ts | 2 - src/shared/utils/diagnose.ts | 8 +-- 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index f16712e8..c40f81c3 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -40,6 +40,48 @@ const RAW_DOCUMENT: V4Document = { }, }; +const WRAPPED: V4WrappedDocument = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [[Object], [Object]], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "5746a5bc0a8aa0cfdaa6ab14bd63d10a91713b0d8450f0403d86f777ff4ba81b", + proofs: [], + merkleRoot: "5746a5bc0a8aa0cfdaa6ab14bd63d10a91713b0d8450f0403d86f777ff4ba81b", + salts: + "W3sidmFsdWUiOiI3ZDgzZGNkMWU2NjM1ZjAxZWM3NjZlNzQwZDM1ZTE4MjU2OWM3MjdjNjViNDZlMTQyMDY3NDRiMTFhMThhYWNiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjI3OGJmZjY2ZTA4N2VhNDBkNjcwZTQ0OTgwYzUyZWM4NTRlOWViZjEyYjM2M2VhZTEyNDExOWQwMzgyMTgxMzUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2U0YTJhOWMzMjBhOTU1MDlmNGQwODIwZGE1N2M3OWVlMDE1ZTQyNzgwODg3ZDkzZjE4ODIwMGNhMDNmMGI1OCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImEzMGVmNzM1MTljZmNjMDE3Zjk5MzBkYWJmNjk2YmNjMjY1ZjI4MDljZjBlOGQyNmUwMzUzYjY3NWZiMzMyZjYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIyMzczMzYwMGNjOTBjMjQ3MzhjOThjMWQxMTJiY2ExZDc0NWFkODMyNjczY2RlMWE5ZjhmYWM3MWZmN2FkODQyIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZjhhZDY4MmVjMGMzYzJjMjZlZjhmY2FkNDg1NzE4ODNiMTU3M2ZkMjYzZTA4N2NlMWZmNDQ3ZGUxYjAxYWRiMCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMzIxMzEwYTI4MDMwZjE1NmEzM2VkNzM5N2MzNGE0ZTk2OTkxNjVkZTQyNjRiODgyMGVmNTZkMGJiOWQ1NDM1MyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI3NzM1NGIxMTQ4MTA4ZDIzYWQ5OTI5NDE4MmViYmJmNzM1NzIyNjg4YTdjOGFmYTQxOTM4ZGVkNzUwY2VlZTQzIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjVhZjY1OTQ1YTlkNTEyODUwM2RjMTM3ZDgwM2ZmMzg5OTJhNTQ4MTg3ZjRlZjJkNThlMGQ4MjMwYjE5ZDZlZWMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiN2IxOTE4NjJiNWY0OWQxY2M1NTA0OWI0NGI2ODk4YmQwNThhMjlkNzg5MjljZDZjYjk2MWNiODgwOWE5ZjBmZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI5OGQxMTI0Y2RhYTU2NGY4YjJhZjE1OTE0OWM0MDBiODM4NWZhN2YxZThhYTRjNmExODEzNzg0NWUwMDIyMDEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImFkYTY5ZDZlYjI3OWVjNjk0ZGE1ODMxZDU1N2Q3NjIxYWM4NWQwYzJlN2IxNDdmM2E5ZGNkMjM0NzA3MWZjMzkiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMDdiYjI0NDRkZmQxMWVkY2M5OWI1MWJhMzllNTI4ODM3NWY2YTExY2U5YTg4Y2MxMjZkNmY4YjYxMzM2ZmQ0NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImZlZGIyMDRmNWM4ZWM1OGU3NzUxYTNhNWM3YmU2MmQxZjhkYjg2MTAyOTI1ZGU1MzcyY2E3NGNlZWVlZjhiOWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJjYTM4YjM3MDk0OTM5ZTE0MDNhN2RlNGUzNzljYTAzMzIzNTQ4ZTdmZjc2YmQ1MGI1ZmI0MGY4NjdmYzRkZjk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAxY2NkZjAxMTI3YWRiODAzYjJhNmQ2YmVjZWU2YTk4N2EyNzM1ZWIxYjYzZGI0Njg5NDMyYmY2NzNmMzIwYzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZDJlZGY0NmZiMjQwNzRjNzY0Y2I4MzliMjYyNGNmNjQ5NWU2NTY5NzA1YmI3ZmMyZjllZjYxNDc0ZTdkODBjYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImY4OWQyOWEwOGE5NmI2YjYyNWU5NjM0OWFiMGVlM2JmNDUzODZhODAxMDhiMTE2NTUwMjk0ODg3MmE4ZmExYmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiMDdmY2JmMzhjYWYxOTYyZjI5MTBjNjk1ZGY1OTVlOWYzYzUwNjk2OWRlNDVlOWU5NWNhNzAwYjFiN2E1OGZlMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyYzYyNGQ0NTJmNWE1MGJiMzlkMzY0NDMwMDA5NDYyMDY2NmY2NTk5MzJiODhmMmFjZWNjYzRiYzQwMmQ4MmNlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZmE5NWEyOGRhNjRlMjA4OTBiZWVjMTZiNGY2N2E2ZDMyNThkYThjYTljN2M1MDljNTZjYjMwZjg0NGUzMjNjOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiYTZmM2FlMmYyNjM0OTkxNzg2NzZlZmMzZmVlYjc3OWEwM2IyMDlhOTQwN2Q1NWU1MDNlN2U4YTA5OWExYmFiMSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyYjEyNGIzOGE0MDEyOWM1ZTM3OTZhZjQ3ZTEyZDg4YWI2YWZmNjllNzZiMTgyYWJmNTZiYjQ2OWU1MTZlNjc1IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { obfuscated: [] }, + }, +}; + const SIGNED_WRAPPED: V4SignedWrappedDocument = { "@context": [ "https://www.w3.org/ns/credentials/v2", @@ -143,6 +185,54 @@ describe("v4 guard", () => { }); }); + describe("given a wrapped document", () => { + test("should pass w3c vc validation without removal of any data", () => { + const w3cVerifiableCredential: W3cVerifiableCredential = WRAPPED; + const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); + expect(results).toEqual(WRAPPED); + }); + + test("should pass document validation without removal of any data", () => { + const v4Document: V4Document = WRAPPED; + const results = V4Document.parse(v4Document); + expect(results).toEqual(WRAPPED); + }); + + test("should pass wrapped document validation without removal of any data", () => { + const results = V4WrappedDocument.parse(WRAPPED); + expect(results).toEqual(WRAPPED); + }); + + test("should fail signed wrapped document validation", () => { + const results = V4SignedWrappedDocument.safeParse(WRAPPED); + expect(results.success).toBe(false); + expect((results as { error: unknown }).error).toMatchInlineSnapshot(` + [ZodError: [ + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "proof", + "key" + ], + "message": "Required" + }, + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "proof", + "signature" + ], + "message": "Required" + } + ]] + `); + }); + }); + describe("given a signed wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED; diff --git a/src/shared/ajv.ts b/src/shared/ajv.ts index 53879c1a..81e9405c 100644 --- a/src/shared/ajv.ts +++ b/src/shared/ajv.ts @@ -2,7 +2,6 @@ import Ajv from "ajv"; import addFormats from "ajv-formats"; import openAttestationSchemav2 from "../2.0/schema/schema.json"; import openAttestationSchemav3 from "../3.0/schema/schema.json"; -import openAttestationSchemav4 from "../4.0/schema/schema.json"; import { CurrentOptions } from "ajv/dist/core"; const defaultTransform = (schema: Record) => schema; @@ -17,7 +16,6 @@ export const buildAjv = ( ajv.addKeyword("deprecationMessage"); ajv.compile(transform(openAttestationSchemav2)); ajv.compile(transform(openAttestationSchemav3)); - ajv.compile(transform(openAttestationSchemav4)); return ajv; }; diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index 0b20a8f0..2dc42814 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -88,7 +88,7 @@ export const diagnose = ({ const versionToSchemaId: Record = { "2.0": SchemaId.v2, "3.0": SchemaId.v3, - "4.0": SchemaId.v4, + "4.0": "" as SchemaId, }; const errors = validate( @@ -252,9 +252,9 @@ const diagnoseV4 = ({ // 4. Check proof object // eslint-disable-next-line @typescript-eslint/no-unused-expressions if (mode === "strict") { - WrappedProofStrictV4.check(document.proof); + // WrappedProofStrictV4.check(document.proof); } else { - WrappedProofV4.check(document.proof); + // WrappedProofV4.check(document.proof); } } catch (e) { if (e instanceof Error) { @@ -270,7 +270,7 @@ const diagnoseV4 = ({ return handleError(debug, `The document does not have a proof`); } try { - SignedWrappedProofV4.check(document.proof); + // SignedWrappedProofV4.check(document.proof); } catch (e) { if (e instanceof Error) { return handleError(debug, e.message); From c5a5c9619a34c1485ae429a965342e7f5c373020 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 14:08:35 +0800 Subject: [PATCH 052/107] fix: diagnose to use new guards --- src/4.0/diagnose.ts | 22 +++++ src/shared/utils/@types/diagnose.ts | 3 + src/shared/utils/diagnose.ts | 130 ++++------------------------ 3 files changed, 41 insertions(+), 114 deletions(-) create mode 100644 src/4.0/diagnose.ts diff --git a/src/4.0/diagnose.ts b/src/4.0/diagnose.ts new file mode 100644 index 00000000..9984a53a --- /dev/null +++ b/src/4.0/diagnose.ts @@ -0,0 +1,22 @@ +import { Diagnose } from "src/shared/utils/@types/diagnose"; +import { V4WrappedDocument, V4SignedWrappedDocument } from "./types"; + +export const v4Diagnose: Diagnose = ({ document, kind, debug }) => { + const Validator = kind === "signed" ? V4SignedWrappedDocument : V4WrappedDocument; + + const results = Validator.safeParse(document); + + if (results.success) { + return []; + } + + return results.error.errors.map(({ code, message, path }) => { + const errorMessage = `${code}: ${message} at ${path.join(".")}`; + if (debug) { + console.debug(errorMessage); + } + return { + message: errorMessage, + }; + }); +}; diff --git a/src/shared/utils/@types/diagnose.ts b/src/shared/utils/@types/diagnose.ts index 91fb12af..0cd4e4f7 100644 --- a/src/shared/utils/@types/diagnose.ts +++ b/src/shared/utils/@types/diagnose.ts @@ -1,2 +1,5 @@ export type Kind = "raw" | "wrapped" | "signed"; export type Mode = "strict" | "non-strict"; +export type Diagnose = (props: { kind: Exclude; document: any; debug: boolean; mode: Mode }) => { + message: string; +}[]; diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index 2dc42814..11c83d62 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -1,5 +1,5 @@ import { logger } from "ethers"; -import { ContextUrl, SchemaId } from "../@types/document"; +import { SchemaId } from "../@types/document"; import { validateSchema as validate } from "../validate"; import { VerifiableCredentialWrappedProof as WrappedProofV3, @@ -10,7 +10,7 @@ import { ArrayProof, Signature, SignatureStrict } from "../../2.0/types"; import { clone, cloneDeepWith } from "lodash"; import { buildAjv, getSchema } from "../ajv"; import { Kind, Mode } from "./@types/diagnose"; -import { isStringArray } from "./utils"; +import { v4Diagnose } from "src/4.0/diagnose"; type Version = "2.0" | "3.0" | "4.0"; @@ -85,25 +85,24 @@ export const diagnose = ({ return handleError(debug, "The document must be an object"); } - const versionToSchemaId: Record = { + const versionToSchemaId: Record = { "2.0": SchemaId.v2, "3.0": SchemaId.v3, - "4.0": "" as SchemaId, + "4.0": undefined, }; - const errors = validate( - document, - getSchema(versionToSchemaId[version], mode === "non-strict" ? ajv : undefined), - kind - ); + const schemaId = versionToSchemaId[version]; + if (schemaId) { + const errors = validate(document, getSchema(schemaId, mode === "non-strict" ? ajv : undefined), kind); - if (errors.length > 0) { - // TODO this can be improved later - return handleError( - debug, - `The document does not match OpenAttestation schema ${version}`, - ...errors.map((error) => `${error.instancePath || "document"} - ${error.message}`) - ); + if (errors.length > 0) { + // TODO this can be improved later + return handleError( + debug, + `The document does not match OpenAttestation schema ${version}`, + ...errors.map((error) => `${error.instancePath || "document"} - ${error.message}`) + ); + } } if (kind === "raw") { @@ -112,7 +111,7 @@ export const diagnose = ({ switch (version) { case "4.0": - return diagnoseV4({ mode, debug, document, kind }); + return v4Diagnose({ mode, debug, document, kind }); case "3.0": return diagnoseV3({ mode, debug, document, kind }); case "2.0": @@ -185,100 +184,3 @@ const diagnoseV3 = ({ kind, document, debug, mode }: { kind: Kind; document: any } return []; }; - -const diagnoseV4 = ({ - kind, - document, - debug, - mode, -}: { - kind: Exclude; - document: any; - debug: boolean; - mode: Mode; -}) => { - /* Wrapped document checks */ - try { - // 1. Since OA v4 has deprecated a few properties from v2/v3, check that they are not used - const deprecatedProperties = ["version", "openAttestationMetadata"]; - const documentProperties = Object.keys(document); - const deprecatedDocumentProperties = documentProperties.filter((p) => deprecatedProperties.includes(p)); - - if (deprecatedDocumentProperties.length > 0) { - return handleError( - debug, - `The document has outdated properties previously used in v2/v3. The following properties are no longer in use in a v4 document: ${deprecatedDocumentProperties}` - ); - } - - // 2. Ensure that required @contexts are present - // @context: [Base, OA, ...] - const contexts = [ContextUrl.v2_vc, ContextUrl.v4_alpha]; - if (isStringArray(document["@context"])) { - for (let i = 0; i < contexts.length; i++) { - if (document["@context"][i] !== contexts[i]) { - return handleError( - debug, - `The document @context contains an unexpected value or in the wrong order. Expected "${contexts}" but received "${document["@context"]}"` - ); - } - } - } else { - return handleError( - debug, - `The document @context should be an array of string values. Expected "${contexts}" but received "${document["@context"]}"` - ); - } - - // 3. Ensure that required types are present - // type: ["VerifiableCredential", "OpenAttestationCredential", ...] - const types = ["VerifiableCredential", "OpenAttestationCredential"]; - if (isStringArray(document["type"])) { - for (let i = 0; i < types.length; i++) { - if (document["type"][i] !== types[i]) { - return handleError( - debug, - `The document type contains an unexpected value or in the wrong order. Expected "${types}" but received "${document["type"]}"` - ); - } - } - } else { - return handleError( - debug, - `The document type should be an array of string values. Expected "${types}" but received "${document["type"]}"` - ); - } - - // 4. Check proof object - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - if (mode === "strict") { - // WrappedProofStrictV4.check(document.proof); - } else { - // WrappedProofV4.check(document.proof); - } - } catch (e) { - if (e instanceof Error) { - return handleError(debug, e.message); - } else { - console.error(e); - } - } - - /* Signed & wrapped document checks */ - if (kind === "signed") { - if (!document.proof) { - return handleError(debug, `The document does not have a proof`); - } - try { - // SignedWrappedProofV4.check(document.proof); - } catch (e) { - if (e instanceof Error) { - return handleError(debug, e.message); - } else { - console.error(e); - } - } - } - - return []; -}; From 9561fbeda5e6224c6114e63df597bca7623edbb2 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 14:11:45 +0800 Subject: [PATCH 053/107] fix: path --- src/shared/utils/diagnose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index 11c83d62..ef697584 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -10,7 +10,7 @@ import { ArrayProof, Signature, SignatureStrict } from "../../2.0/types"; import { clone, cloneDeepWith } from "lodash"; import { buildAjv, getSchema } from "../ajv"; import { Kind, Mode } from "./@types/diagnose"; -import { v4Diagnose } from "src/4.0/diagnose"; +import { v4Diagnose } from "../../4.0/diagnose"; type Version = "2.0" | "3.0" | "4.0"; From aa66692901796a635ae1349767980377a353a5bc Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:26:38 +0800 Subject: [PATCH 054/107] chore: remove vscode --- .vscode/settings.json | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7a73a41b..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file From 9b1900e0b7684f7dd63a2d12995e13ba0b2fc531 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:27:20 +0800 Subject: [PATCH 055/107] chore: ignore vscode settings --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e1632f13..1ff9e852 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ yarn.lock *.iml /public /vc-test-suite +/.vscode \ No newline at end of file From ff57b1c5e3efc0fd9fcc7ba093b44a12fecb476a Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:29:53 +0800 Subject: [PATCH 056/107] chore: remove experimental version guard --- src/shared/utils/diagnose.ts | 2 +- src/shared/utils/utils.ts | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index ef697584..97d8d5e7 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -12,7 +12,7 @@ import { buildAjv, getSchema } from "../ajv"; import { Kind, Mode } from "./@types/diagnose"; import { v4Diagnose } from "../../4.0/diagnose"; -type Version = "2.0" | "3.0" | "4.0"; +export type Version = "2.0" | "3.0" | "4.0"; interface DiagnoseError { message: string; diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index 95eb96a6..f82014c4 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -21,13 +21,7 @@ import { isRawV4Document, isWrappedV4Document, } from "./guard"; - -const VersionGuard = { - 2: isWrappedV2Document, - 3: isWrappedV3Document, - 4: isWrappedV4Document, -} as const satisfies Record boolean>; -type OAVersion = keyof typeof VersionGuard; +import { Version } from "./diagnose"; export type Hash = string | Buffer; type Extract

= P extends WrappedDocumentV2 ? T : never; @@ -274,18 +268,18 @@ export const getObfuscatedData = ( export const isStringArray = (input: unknown): input is string[] => Array.isArray(input) && input.every((i) => typeof i === "string"); -export const getVersion = (document: unknown): OAVersion => { +export const getVersion = (document: unknown): Version => { if (typeof document === "object" && document !== null) { if ("version" in document && typeof document.version === "string") { switch (document.version) { case SchemaId.v2: - return 2; + return "2.0"; case SchemaId.v3: - return 3; + return "3.0"; } } else if ("@context" in document && Array.isArray(document["@context"])) { if (document["@context"].includes(ContextUrl.v4_alpha)) { - return 4; + return "4.0"; } } } From 6245038de0c2628a2d1686d5ba63486f20a8b5bc Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:44:55 +0800 Subject: [PATCH 057/107] fix: add back the missing wrapDocuments that was accidentally removed --- src/4.0/wrap.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 4666eec0..07237b39 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -94,6 +94,32 @@ export const wrapDocument = async (document: T): Promise; }; +export const wrapDocuments = async (documents: T[]): Promise[]> => { + // create individual verifiable credential + const verifiableCredentials = await Promise.all(documents.map((document) => wrapDocument(document))); + + // get all the target hashes to compute the merkle tree and the merkle root + const merkleTree = new MerkleTree( + verifiableCredentials.map((verifiableCredential) => verifiableCredential.proof.targetHash).map(hashToBuffer) + ); + const merkleRoot = merkleTree.getRoot().toString("hex"); + + // for each document, update the merkle root and add the proofs needed + return verifiableCredentials.map((verifiableCredential) => { + const digest = verifiableCredential.proof.targetHash; + const merkleProof = merkleTree.getProof(hashToBuffer(digest)).map((buffer: Buffer) => buffer.toString("hex")); + + return { + ...verifiableCredential, + proof: { + ...verifiableCredential.proof, + proofs: merkleProof, + merkleRoot, + }, + }; + }); +}; + /** Extract a set of properties from w3cVerifiableCredential but only include the ones * that are defined in the original document. For example, if we extract * "a" and "b" from { b: "something" } we should only get { b: "something" } NOT From 4d965a4a043f03488b9b8eb5a0b6729a76f941eb Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:47:11 +0800 Subject: [PATCH 058/107] fix: replace back original implementation of getMerkleRoot --- src/shared/utils/utils.ts | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index f82014c4..8cceffff 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -100,20 +100,13 @@ export function getIssuerAddress(document: any): any { } export const getMerkleRoot = (document: any): string => { - const version = getVersion(document); - const getMerkleRoot: Record unknown | undefined> = { - 2: () => document?.signature?.merkleRoot, - 3: () => document?.proof?.merkleRoot, - 4: () => document?.proof?.merkleRoot, - }; - - const merkleRoot = getMerkleRoot[version](); - if (merkleRoot === undefined || typeof merkleRoot !== "string") - throw new Error( - "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." - ); - - return merkleRoot; + if (isWrappedV2Document(document)) return document.signature.merkleRoot; + else if (isWrappedV3Document(document)) return document.proof.merkleRoot; + else if (isWrappedV4Document(document)) return document.proof.merkleRoot; + + throw new Error( + "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." + ); }; export const getTargetHash = (document: any): string => { From 5b5ecea7fb54940baa98b257945bca367db00679 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 16:51:54 +0800 Subject: [PATCH 059/107] fix: make our own real types stricter --- src/4.0/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index b42d157e..6f853a47 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -211,7 +211,7 @@ export const V4Document = W3cVerifiableCredential.extend({ ]) ) .optional(), -}); +}).strict(); const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), @@ -223,11 +223,11 @@ const WrappedProof = z.object({ privacy: z.object({ obfuscated: z.array(z.string()) }), }); const WrappedDocumentExtrasShape = { proof: WrappedProof.passthrough() } as const; -export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape); +export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape).strict(); const SignedWrappedProof = WrappedProof.extend({ key: z.string(), signature: z.string() }); const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; -export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape); +export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape).strict(); export type W3cVerifiableCredential = z.infer; From a99584a78d2213abca791f50de0312c34cc87012 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 17:39:56 +0800 Subject: [PATCH 060/107] fix: diagnose did not handle raw v4 --- src/4.0/diagnose.ts | 11 ++++++-- src/index.ts | 39 +++++++++++------------------ src/shared/utils/@types/diagnose.ts | 2 +- src/shared/utils/diagnose.ts | 4 +-- src/shared/utils/guard.ts | 16 ++++++------ 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/4.0/diagnose.ts b/src/4.0/diagnose.ts index 9984a53a..068b1de6 100644 --- a/src/4.0/diagnose.ts +++ b/src/4.0/diagnose.ts @@ -1,8 +1,15 @@ import { Diagnose } from "src/shared/utils/@types/diagnose"; -import { V4WrappedDocument, V4SignedWrappedDocument } from "./types"; +import { V4WrappedDocument, V4SignedWrappedDocument, V4Document } from "./types"; export const v4Diagnose: Diagnose = ({ document, kind, debug }) => { - const Validator = kind === "signed" ? V4SignedWrappedDocument : V4WrappedDocument; + let Validator: typeof V4Document | typeof V4WrappedDocument | typeof V4SignedWrappedDocument = V4Document; + if (kind === "raw") { + Validator = V4Document; + } else if (kind === "wrapped") { + Validator = V4WrappedDocument; + } else { + Validator = V4SignedWrappedDocument; + } const results = Validator.safeParse(document); diff --git a/src/index.ts b/src/index.ts index 3c7d290e..7abcce74 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import { getSchema } from "./shared/ajv"; import * as utils from "./shared/utils"; import { SchemaValidationError } from "./shared/utils"; import { validateSchema as validate } from "./shared/validate"; -import { SchemaId, WrappedDocument, OpenAttestationDocument } from "./shared/@types/document"; +import { SchemaId, WrappedDocument, OpenAttestationDocument, SignedWrappedDocument } from "./shared/@types/document"; import { WrapDocumentOptionV2, WrapDocumentOptionV3 } from "./shared/@types/wrap"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "./shared/@types/sign"; @@ -31,6 +31,7 @@ import { signDocument as signDocumentV4 } from "./4.0/sign"; import { verify as verifyV4 } from "./4.0/verify"; import { digestCredential as digestCredentialV4 } from "./4.0/digest"; import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV4 } from "./4.0/obfuscate"; +import { v4Diagnose } from "./4.0/diagnose"; export function wrapDocument( data: T, @@ -78,7 +79,7 @@ export const validateSchema = (document: WrappedDocument): boolean => { else if (utils.isWrappedV3Document(document) || document?.version === SchemaId.v3) return validate(document, getSchema(SchemaId.v3)).length === 0; else if (utils.isWrappedV4Document(document)) { - return validate(document, getSchema(SchemaId.v4)).length === 0; + return v4Diagnose({ document, kind: "wrapped", debug: false, mode: "strict" }).length === 0; } return validate(document, getSchema(`${document?.version || SchemaId.v2}`)).length === 0; @@ -95,7 +96,7 @@ export function verifySignature( document: WrappedDocument, fields: string[] | string ): WrappedDocument; -export function obfuscate( +export function obfuscate( document: WrappedDocument, fields: string[] | string ): WrappedDocument; @@ -131,34 +132,22 @@ export const isSchemaValidationError = (error: any): error is SchemaValidationEr return !!error.validationErrors; }; -export async function signDocument( - document: v2.SignedWrappedDocument | v2.WrappedDocument, +export async function signDocument( + document: WrappedDocument | SignedWrappedDocument, algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer -): Promise>; -export async function signDocument( - document: v3.SignedWrappedDocument | v3.WrappedDocument, - algorithm: SUPPORTED_SIGNING_ALGORITHM, - keyOrSigner: SigningKey | ethers.Signer -): Promise>; -export async function signDocument( - document: v4.SignedWrappedDocument | v4.WrappedDocument, - algorithm: SUPPORTED_SIGNING_ALGORITHM, - keyOrSigner: SigningKey | ethers.Signer -): Promise>; -export async function signDocument( - document: any, - algorithm: SUPPORTED_SIGNING_ALGORITHM, - keyOrSigner: SigningKey | ethers.Signer -) { +): Promise> { // rj was worried it could happen deep in the code, so I moved it to the boundaries if (!SigningKey.guard(keyOrSigner) && !Signer.isSigner(keyOrSigner)) { throw new Error(`Either a keypair or ethers.js Signer must be provided`); } - if (utils.isWrappedV2Document(document)) return signDocumentV2(document, algorithm, keyOrSigner); - else if (utils.isWrappedV3Document(document)) return signDocumentV3(document, algorithm, keyOrSigner); - else if (utils.isWrappedV4Document(document)) return signDocumentV4(document, algorithm, keyOrSigner); + let results: unknown; + if (utils.isWrappedV2Document(document)) results = signDocumentV2(document, algorithm, keyOrSigner); + else if (utils.isWrappedV3Document(document)) results = signDocumentV3(document, algorithm, keyOrSigner); + else if (utils.isWrappedV4Document(document)) results = signDocumentV4(document, algorithm, keyOrSigner); + + if (results) return results as SignedWrappedDocument; // Unreachable code atm until utils.isWrappedV2Document & utils.isWrappedV3Document becomes more strict throw new Error("Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be signed"); diff --git a/src/shared/utils/@types/diagnose.ts b/src/shared/utils/@types/diagnose.ts index 0cd4e4f7..dfcdcf7e 100644 --- a/src/shared/utils/@types/diagnose.ts +++ b/src/shared/utils/@types/diagnose.ts @@ -1,5 +1,5 @@ export type Kind = "raw" | "wrapped" | "signed"; export type Mode = "strict" | "non-strict"; -export type Diagnose = (props: { kind: Exclude; document: any; debug: boolean; mode: Mode }) => { +export type Diagnose = (props: { kind: Kind; document: any; debug: boolean; mode?: Mode }) => { message: string; }[]; diff --git a/src/shared/utils/diagnose.ts b/src/shared/utils/diagnose.ts index 97d8d5e7..919553ff 100644 --- a/src/shared/utils/diagnose.ts +++ b/src/shared/utils/diagnose.ts @@ -105,13 +105,13 @@ export const diagnose = ({ } } - if (kind === "raw") { + if (kind === "raw" && version !== "4.0") { return []; } switch (version) { case "4.0": - return v4Diagnose({ mode, debug, document, kind }); + return v4Diagnose({ debug, document, kind }); case "3.0": return diagnoseV3({ mode, debug, document, kind }); case "2.0": diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index 508f7678..dfe8e136 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,7 +7,7 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { V4Document as OpenAttestationDocumentV4, V4WrappedDocument, V4SignedWrappedDocument } from "../../4.0/types"; +import { V4WrappedDocument, V4SignedWrappedDocument, V4Document } from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; @@ -43,7 +43,7 @@ export const isRawV3Document = ( export const isRawV4Document = ( document: any, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is OpenAttestationDocumentV4 => { +): document is V4Document => { return diagnose({ version: "4.0", kind: "raw", document, debug: false, mode }).length === 0; }; @@ -52,10 +52,10 @@ export const isRawV4Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isWrappedV2Document = ( +export const isWrappedV2Document = ( document: any, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is WrappedDocumentV2 => { +): document is WrappedDocumentV2 => { return diagnose({ version: "2.0", kind: "wrapped", document, debug: false, mode }).length === 0; }; @@ -64,10 +64,10 @@ export const isWrappedV2Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isWrappedV3Document = ( +export const isWrappedV3Document = ( document: any, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is WrappedDocumentV3 => { +): document is WrappedDocumentV3 => { return diagnose({ version: "3.0", kind: "wrapped", document, debug: false, mode }).length === 0; }; @@ -76,10 +76,10 @@ export const isWrappedV3Document = ( * @param document * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. */ -export const isWrappedV4Document = ( +export const isWrappedV4Document = ( document: unknown, { mode }: { mode: Mode } = { mode: "non-strict" } -): document is T => { +): document is V4WrappedDocument => { return diagnose({ version: "4.0", kind: "wrapped", document, debug: false, mode }).length === 0; }; From a8219ee0acfcaa9aa80954c24240f47ff646dda6 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 17:41:53 +0800 Subject: [PATCH 061/107] refactor: remove useless renaming --- src/shared/utils/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index 8cceffff..d992735a 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -10,7 +10,7 @@ import * as v3 from "../../__generated__/schema.3.0"; import { WrappedDocument as WrappedDocumentV3 } from "../../3.0/types"; import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "../../__generated__/schema.3.0"; -import { V4WrappedDocument as WrappedDocumentV4 } from "../../4.0/types"; +import { V4WrappedDocument } from "../../4.0/types"; import { OpenAttestationDocument, WrappedDocument, SchemaId, ContextUrl } from "../@types/document"; import { @@ -224,7 +224,7 @@ export const isObfuscated = ( document: | WrappedDocumentV2 | WrappedDocumentV3 - | WrappedDocumentV4 + | V4WrappedDocument ): boolean => { if (isWrappedV2Document(document)) { return !!document.privacy?.obfuscatedData?.length; @@ -243,7 +243,7 @@ export const getObfuscatedData = ( document: | WrappedDocumentV2 | WrappedDocumentV3 - | WrappedDocumentV4 + | V4WrappedDocument ): string[] => { if (isWrappedV2Document(document)) { return document.privacy?.obfuscatedData || []; From 26fa2653d857d6955cf84c779191393a9e9c837d Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 18:08:07 +0800 Subject: [PATCH 062/107] fix: object object in test case --- src/4.0/__tests__/guard.test.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index c40f81c3..07be75b8 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -61,7 +61,18 @@ const WRAPPED: V4WrappedDocument = { id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", type: ["DriversLicense"], name: "John Doe", - licenses: [[Object], [Object]], + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], }, renderMethod: [ { @@ -73,12 +84,14 @@ const WRAPPED: V4WrappedDocument = { proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "5746a5bc0a8aa0cfdaa6ab14bd63d10a91713b0d8450f0403d86f777ff4ba81b", + targetHash: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", proofs: [], - merkleRoot: "5746a5bc0a8aa0cfdaa6ab14bd63d10a91713b0d8450f0403d86f777ff4ba81b", + merkleRoot: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", salts: - "W3sidmFsdWUiOiI3ZDgzZGNkMWU2NjM1ZjAxZWM3NjZlNzQwZDM1ZTE4MjU2OWM3MjdjNjViNDZlMTQyMDY3NDRiMTFhMThhYWNiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjI3OGJmZjY2ZTA4N2VhNDBkNjcwZTQ0OTgwYzUyZWM4NTRlOWViZjEyYjM2M2VhZTEyNDExOWQwMzgyMTgxMzUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2U0YTJhOWMzMjBhOTU1MDlmNGQwODIwZGE1N2M3OWVlMDE1ZTQyNzgwODg3ZDkzZjE4ODIwMGNhMDNmMGI1OCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImEzMGVmNzM1MTljZmNjMDE3Zjk5MzBkYWJmNjk2YmNjMjY1ZjI4MDljZjBlOGQyNmUwMzUzYjY3NWZiMzMyZjYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIyMzczMzYwMGNjOTBjMjQ3MzhjOThjMWQxMTJiY2ExZDc0NWFkODMyNjczY2RlMWE5ZjhmYWM3MWZmN2FkODQyIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZjhhZDY4MmVjMGMzYzJjMjZlZjhmY2FkNDg1NzE4ODNiMTU3M2ZkMjYzZTA4N2NlMWZmNDQ3ZGUxYjAxYWRiMCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMzIxMzEwYTI4MDMwZjE1NmEzM2VkNzM5N2MzNGE0ZTk2OTkxNjVkZTQyNjRiODgyMGVmNTZkMGJiOWQ1NDM1MyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI3NzM1NGIxMTQ4MTA4ZDIzYWQ5OTI5NDE4MmViYmJmNzM1NzIyNjg4YTdjOGFmYTQxOTM4ZGVkNzUwY2VlZTQzIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjVhZjY1OTQ1YTlkNTEyODUwM2RjMTM3ZDgwM2ZmMzg5OTJhNTQ4MTg3ZjRlZjJkNThlMGQ4MjMwYjE5ZDZlZWMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiN2IxOTE4NjJiNWY0OWQxY2M1NTA0OWI0NGI2ODk4YmQwNThhMjlkNzg5MjljZDZjYjk2MWNiODgwOWE5ZjBmZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI5OGQxMTI0Y2RhYTU2NGY4YjJhZjE1OTE0OWM0MDBiODM4NWZhN2YxZThhYTRjNmExODEzNzg0NWUwMDIyMDEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImFkYTY5ZDZlYjI3OWVjNjk0ZGE1ODMxZDU1N2Q3NjIxYWM4NWQwYzJlN2IxNDdmM2E5ZGNkMjM0NzA3MWZjMzkiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMDdiYjI0NDRkZmQxMWVkY2M5OWI1MWJhMzllNTI4ODM3NWY2YTExY2U5YTg4Y2MxMjZkNmY4YjYxMzM2ZmQ0NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImZlZGIyMDRmNWM4ZWM1OGU3NzUxYTNhNWM3YmU2MmQxZjhkYjg2MTAyOTI1ZGU1MzcyY2E3NGNlZWVlZjhiOWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJjYTM4YjM3MDk0OTM5ZTE0MDNhN2RlNGUzNzljYTAzMzIzNTQ4ZTdmZjc2YmQ1MGI1ZmI0MGY4NjdmYzRkZjk2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAxY2NkZjAxMTI3YWRiODAzYjJhNmQ2YmVjZWU2YTk4N2EyNzM1ZWIxYjYzZGI0Njg5NDMyYmY2NzNmMzIwYzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZDJlZGY0NmZiMjQwNzRjNzY0Y2I4MzliMjYyNGNmNjQ5NWU2NTY5NzA1YmI3ZmMyZjllZjYxNDc0ZTdkODBjYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImY4OWQyOWEwOGE5NmI2YjYyNWU5NjM0OWFiMGVlM2JmNDUzODZhODAxMDhiMTE2NTUwMjk0ODg3MmE4ZmExYmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiMDdmY2JmMzhjYWYxOTYyZjI5MTBjNjk1ZGY1OTVlOWYzYzUwNjk2OWRlNDVlOWU5NWNhNzAwYjFiN2E1OGZlMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyYzYyNGQ0NTJmNWE1MGJiMzlkMzY0NDMwMDA5NDYyMDY2NmY2NTk5MzJiODhmMmFjZWNjYzRiYzQwMmQ4MmNlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZmE5NWEyOGRhNjRlMjA4OTBiZWVjMTZiNGY2N2E2ZDMyNThkYThjYTljN2M1MDljNTZjYjMwZjg0NGUzMjNjOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiYTZmM2FlMmYyNjM0OTkxNzg2NzZlZmMzZmVlYjc3OWEwM2IyMDlhOTQwN2Q1NWU1MDNlN2U4YTA5OWExYmFiMSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyYjEyNGIzOGE0MDEyOWM1ZTM3OTZhZjQ3ZTEyZDg4YWI2YWZmNjllNzZiMTgyYWJmNTZiYjQ2OWU1MTZlNjc1IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", - privacy: { obfuscated: [] }, + "W3sidmFsdWUiOiIzMThlMDgwY2NjZWYwZmRjMWEyNjhjN2FjOTEwNmNiYzUwNTEyMzkyNTc0MDNhZTU2MmI3YTFhNmU5YjkzY2ZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjE2MTJkMDliZjUzNGZkMmZhNTFmYjlhOWI0NTk1MzE0NTlmNzU3NDA1ZmExMjllMjY3YWEwNTUxOTlhZDY2ZjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2NlMDA0ZGM0M2NiZGY0ZmE5NjRlN2Q3ZjQ0YzQ5ZTMzNDhiMzExZmQxYjc0MzhhYTI2ZjI5YmI2OGU5MGU3NCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjNhZmJmZDc0Mzg1YWQ2NzM0YjE3MDgyOGJhNWUyYWI1YzM4MTY1YTBmYWNlOTJlNGUwODRmNzkyMjFlZDJkMDAiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIxYTMwZTE4MTI3N2I2ZjFlZTczOTlmMTQxZWU5MjY2MDIwMWUxYTRlYmZkODEzOTJiNjgxYTEwMjMxMTUzY2Q1IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMWU4ZDRlODY5ZjVkMzE1YmIxMzcyZjRhOTQxYWNjNjgxMWY2OWIwZmRiMmFkOWM3MTRjMGJiNTZhMmMzMjdmZiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYzVjN2MxNzVhNmU5ZTFlZDQ1OTkxYTExYjhiNjczZmRiNWU4ZTU4NDU5OTliM2FkODYwMjM5OTdiOWUzNWEzNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyMjNkZDJhOGMwZTZhOGJlMTE2ZmUxMzliM2Q0YjRkYWZlMTBkNWJiNWEwNmVjNzE2YjNhM2JiNDZlYjI3ZDNjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjM0YzJmMTU4OWM2MTFmYWZmOTE4MTAwODk3MGJiMGY1NTc3MWQ1ODI0ODQxOGY3OGVhOWM4NTk3NTgwZDM1ZTkiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYWE0MmVhNDE3MGJhNmRhMTVmZTdiMzIyZTUxMzc3ODA5M2E2NjE3MTE1NzZmOTA2M2JmY2Y4YzdlMGEzYjRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU5ZjNlMTJiYTUzZDA3M2Y0ZjNiZTU2ZDUzN2ZjYjIwZmI0ZTM0YzZkM2RlZGZkNTcyMzkzODAzOTViZGM4ZTkiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjhkNGIyMDYyZmFmNzRhNmZhNGE5NmE2OTQ0NzIyMWFjNjhjZTk0MjBjOWQ2ZTFhNDFjODM2MmI1ODQwMmI5ODQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTMxNmM0YTRhZGJiMzQwYTBjYTViNDljNTZmNWIxZWMzNjA5N2UyMjg4YzJkY2I0YmU4ZjhiY2MxNDA4NjIyYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjJlZmY0Y2JlZjFkNmQ1ZTEwOTZhNDAyNGY4ODhmM2U2YTEwMWZkYjIxZTRjZmI1ZjdmYTA4Y2ZhZmZkMzk2YjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI0YjAxYjNmZWIzOTcyYTBmMDBjNTJkNDU2YmYxZWY1ZWJkODI3YjkxMDFjMzI4NTQwNzExM2NiODZhODNiY2JmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjcxMjE4ZTA2YTNlNGM1NTc5NTIwNzdkNzQ1NTJlZTMyNDIxNzMxMjU2YTcwZDI1Nzg3ZGQyNmFkMDk1YmY5NzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNWM2NzdhYzgwMGQ3MDRiZjUwYzZiYjAyN2Q0OTg1YmQ2OTg0Y2VhYjExNzUzOTk0ZWQ5YTI5OTIxYjhmYWRmNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVhMjY1MDM0OTFlYjhjZjNiN2EzMTg1MGEwZTM3OTQxMjFiN2YzOTQ5OTI1NmIyNGQ3OWNkODE3MDVjNDcxOTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNzlhOWZiOTVkM2U5NTlkNjFlODFhNmQwMGY3ZTlkZjAwZTAzNTMzYTYyZDZlZDkyNWI4MTIyNjY5YThkYzZkNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YzhkOWQ0YzRkYjRhODU4NmJkODgzZmViZmNhNTUzNmYxYmVmNDJhM2NmYTJmYTQxNWY0YWFkZjIyZmY1MTlkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNGNkOWMwOGU3NDE2MzU0YjhiNjBlYzA1YzQzZDY3YWMwOTAzNmQ4YWRlZGYyZGVjMjIxNWU1NmU4MGM1MDg5MCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiOGRjNGY3NDllN2JhZmZmZWJmM2FmOGY4ZDJjYjUwNTAzZGFmOGZhZTVkM2Q3YjhjNTNhZDM1NTFiMWM1NDI0MyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJkZjY0NmI2YjYwMTJmZWQxYzE5N2E5MjhjNGJjZTVhOWJlNTc0YjU4YmFhYjZkN2E4OTAwZDBiZDdkYjg4N2IyIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { + obfuscated: [], + }, }, }; From d6ff500b345f6083e5c9556786ca42f2ef314d68 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 18:08:56 +0800 Subject: [PATCH 063/107] refactor: improve type safety of wrap document v4 --- src/4.0/types.ts | 3 +++ src/4.0/wrap.ts | 12 +++++++++--- src/index.ts | 16 ++++------------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 6f853a47..8ed7eaed 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -260,3 +260,6 @@ type Override, OverrideWith extends Recor /** Used to assert that StricterType is a stricter or equal version of LooserType, and most importantly, that * StricterType is STILL assignable to LooserType. */ type AssertStricterOrEqual = StricterType extends LooserType ? StricterType : never; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export type NoExtraProperties = NewObj extends Reference & infer _ExtraProps ? Reference : NewObj; diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index 07237b39..a310ee49 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -1,12 +1,15 @@ import { hashToBuffer, isStringArray } from "../shared/utils"; import { MerkleTree } from "../shared/merkle"; import { ContextType, ContextUrl } from "../shared/@types/document"; -import { V4Document, V4WrappedDocument, W3cVerifiableCredential } from "./types"; +import { NoExtraProperties, V4Document, V4WrappedDocument, W3cVerifiableCredential } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; import { interpretContexts } from "./validate"; -export const wrapDocument = async (document: T): Promise> => { +export const wrapDocument = async ( + // NoExtraProperties prevents the user from passing in a document with extra properties, which is more aligned to our validation strategy of strict + document: NoExtraProperties +): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ const oav4context = await V4Document.pick({ "@context": true }).safeParseAsync(document); // Superficial check on user intention let validatedRawDocument: W3cVerifiableCredential | undefined; @@ -94,7 +97,10 @@ export const wrapDocument = async (document: T): Promise; }; -export const wrapDocuments = async (documents: T[]): Promise[]> => { +export const wrapDocuments = async ( + // NoExtraProperties prevents the user from passing in a document with extra properties, which is more aligned to our validation strategy of strict + documents: NoExtraProperties[] +): Promise[]> => { // create individual verifiable credential const verifiableCredentials = await Promise.all(documents.map((document) => wrapDocument(document))); diff --git a/src/index.ts b/src/index.ts index 7abcce74..abcd820d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,6 @@ import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV3 } from import { OpenAttestationDocument as OpenAttestationDocumentV3 } from "./__generated__/schema.3.0"; import * as v4 from "./4.0/types"; -import { wrapDocument as wrapDocumentV4, wrapDocuments as wrapDocumentsV4 } from "./4.0/wrap"; import { signDocument as signDocumentV4 } from "./4.0/sign"; import { verify as verifyV4 } from "./4.0/verify"; import { digestCredential as digestCredentialV4 } from "./4.0/digest"; @@ -61,17 +60,10 @@ export function __unsafe__use__it__at__your__own__risks__wrapDocuments( - data: T -): Promise> { - return wrapDocumentV4(data); -} - -export function _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments( - dataArray: T[] -): Promise[]> { - return wrapDocumentsV4(dataArray); -} +export { + wrapDocument as _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocument, + wrapDocuments as _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments, +} from "./4.0/wrap"; export const validateSchema = (document: WrappedDocument): boolean => { if (utils.isWrappedV2Document(document) || document?.version === SchemaId.v2) From 43dd837f4f7d2afbf76a7a712e612120ea541ede Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 21:35:40 +0800 Subject: [PATCH 064/107] chore: remove to make way for auto generated fixtures --- test/fixtures/v4/did-raw-ocsp.json | 43 ---------------- test/fixtures/v4/did-raw.json | 39 -------------- .../v4/did-signed-wrapped-obfuscated.json | 47 ----------------- test/fixtures/v4/did-signed-wrapped-oscp.json | 51 ------------------- test/fixtures/v4/did-signed-wrapped.json | 50 ------------------ 5 files changed, 230 deletions(-) delete mode 100644 test/fixtures/v4/did-raw-ocsp.json delete mode 100644 test/fixtures/v4/did-raw.json delete mode 100644 test/fixtures/v4/did-signed-wrapped-obfuscated.json delete mode 100644 test/fixtures/v4/did-signed-wrapped-oscp.json delete mode 100644 test/fixtures/v4/did-signed-wrapped.json diff --git a/test/fixtures/v4/did-raw-ocsp.json b/test/fixtures/v4/did-raw-ocsp.json deleted file mode 100644 index 71861e32..00000000 --- a/test/fixtures/v4/did-raw-ocsp.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "credentialStatus": { - "id": "https://ocsp-sandbox.openattestation.com", - "type": "OpenAttestationOcspResponder" - }, - "renderMethod": [ - { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "templateName": "GOVTECH_DEMO" - } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { - "class": "3", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - } -} diff --git a/test/fixtures/v4/did-raw.json b/test/fixtures/v4/did-raw.json deleted file mode 100644 index 5f3a9860..00000000 --- a/test/fixtures/v4/did-raw.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "renderMethod": [ - { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "templateName": "GOVTECH_DEMO" - } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { - "class": "3", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - } -} diff --git a/test/fixtures/v4/did-signed-wrapped-obfuscated.json b/test/fixtures/v4/did-signed-wrapped-obfuscated.json deleted file mode 100644 index e983f7ff..00000000 --- a/test/fixtures/v4/did-signed-wrapped-obfuscated.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": [ - { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "templateName": "GOVTECH_DEMO" - } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { "class": "3", "effectiveDate": "2013-05-16T00:00:00+08:00" }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - }, - "proof": { - "type": "OpenAttestationMerkleProofSignature2018", - "proofPurpose": "assertionMethod", - "targetHash": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - "proofs": [], - "merkleRoot": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - "salts": "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2E1ZTExMzdhYmY3OGYzMzViMzNlNjA3YmZmNmU4YWZkM2YyZTJkOTc2YWZiOWM1OGE1Nzk4YmEwZTllYmVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYmY2YWI5YzQ2ZTVmZjBkNzM1Nzc2MjUxOTQ1NmFlYmI1YTU1NGJjNDg0NTRmN2EyYTU2NTdiNGIyODQ3ZTFmMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiI1NTJmOGQ0Y2MxMjRmYzIyYjQxM2I4YjYzYzBhZjcwZWI0MDgzNDYxOWMxNDRjYWZmYWI3MDFmMGE1MzYzMmJjIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImJhZWE5NWEyYmVlNTU3NWRlY2UzM2ZiZjFhODBhZWYxMmY2NWExYjY4NGRkMjQ0ZjZmNThmMjJmYjJiYWQ4N2UiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - "privacy": { "obfuscated": ["c866663b38353fdb46a372cd0302fd7752cc75bee4b8fc4d65c2e3b22f2466f7"] }, - "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b" - } -} diff --git a/test/fixtures/v4/did-signed-wrapped-oscp.json b/test/fixtures/v4/did-signed-wrapped-oscp.json deleted file mode 100644 index 26ccc20c..00000000 --- a/test/fixtures/v4/did-signed-wrapped-oscp.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "credentialStatus": { "id": "https://ocsp-sandbox.openattestation.com", "type": "OpenAttestationOcspResponder" }, - "renderMethod": [ - { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "templateName": "GOVTECH_DEMO" - } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { - "class": "3", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - }, - "proof": { - "type": "OpenAttestationMerkleProofSignature2018", - "proofPurpose": "assertionMethod", - "targetHash": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - "proofs": [], - "merkleRoot": "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - "salts": "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJkZjNmMjY5NzE5YTczYzA1NDc4Y2Q1YTk0MTQwMDVmMjA2NWNhNWNhODg5NGUwNjc2NDllYmEwMzcyNjBmNDgzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImNjYTVlMTEzN2FiZjc4ZjMzNWIzM2U2MDdiZmY2ZThhZmQzZjJlMmQ5NzZhZmI5YzU4YTU3OThiYTBlOWViZWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiJiZjZhYjljNDZlNWZmMGQ3MzU3NzYyNTE5NDU2YWViYjVhNTU0YmM0ODQ1NGY3YTJhNTY1N2I0YjI4NDdlMWYxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6IjU1MmY4ZDRjYzEyNGZjMjJiNDEzYjhiNjNjMGFmNzBlYjQwODM0NjE5YzE0NGNhZmZhYjcwMWYwYTUzNjMyYmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYmFlYTk1YTJiZWU1NTc1ZGVjZTMzZmJmMWE4MGFlZjEyZjY1YTFiNjg0ZGQyNDRmNmY1OGYyMmZiMmJhZDg3ZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn1d", - "privacy": { "obfuscated": [] }, - "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b" - } -} diff --git a/test/fixtures/v4/did-signed-wrapped.json b/test/fixtures/v4/did-signed-wrapped.json deleted file mode 100644 index 3e0ab737..00000000 --- a/test/fixtures/v4/did-signed-wrapped.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" - ], - "type": ["VerifiableCredential", "OpenAttestationCredential"], - "validFrom": "2021-03-08T12:00:00+08:00", - "name": "Republic of Singapore Driving Licence", - "issuer": { - "id": "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - "type": "OpenAttestationIssuer", - "name": "Government Technology Agency of Singapore (GovTech)", - "identityProof": { "identityProofType": "DNS-DID", "identifier": "example.openattestation.com" } - }, - "renderMethod": [ - { - "id": "https://demo-renderer.opencerts.io", - "type": "OpenAttestationEmbeddedRenderer", - "templateName": "GOVTECH_DEMO" - } - ], - "credentialSubject": { - "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - "type": ["DriversLicense"], - "name": "John Doe", - "licenses": [ - { - "class": "3", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - }, - { - "class": "3A", - "description": "Motor cars with unladen weight <= 3000kg", - "effectiveDate": "2013-05-16T00:00:00+08:00" - } - ] - }, - "proof": { - "type": "OpenAttestationMerkleProofSignature2018", - "proofPurpose": "assertionMethod", - "targetHash": "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - "proofs": [], - "merkleRoot": "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - "salts": "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", - "privacy": { "obfuscated": [] }, - "key": "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - "signature": "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c" - } -} From 5b19bc9e5995113869608506366acadf30988a66 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 26 Apr 2024 21:37:41 +0800 Subject: [PATCH 065/107] chore: auto generate v4 fixtures --- .gitignore | 4 +- package.json | 3 +- scripts/generateV4JsonFixtures.ts | 18 ++ src/4.0/__tests__/digest.test.ts | 15 +- src/4.0/__tests__/guard.test.ts | 191 +++---------------- src/4.0/fixtures.ts | 297 ++++++++++++++++++++++++++++++ 6 files changed, 348 insertions(+), 180 deletions(-) create mode 100644 scripts/generateV4JsonFixtures.ts create mode 100644 src/4.0/fixtures.ts diff --git a/.gitignore b/.gitignore index 1ff9e852..22c2dc53 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ yarn.lock *.iml /public /vc-test-suite -/.vscode \ No newline at end of file +/.vscode + +/test/fixtures/v4/__generated__/ \ No newline at end of file diff --git a/package.json b/package.json index 516649c6..28ffba52 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,9 @@ "test:vc": "scripts/runVcTest.sh", "lint": "eslint . --ext .ts,.json --max-warnings 0", "lint:fix": "npm run lint -- --fix", + "generate-v4-fixtures": "npx ts-node scripts/generateV4JsonFixtures.ts", "publish:schema": "./scripts/publishSchema.sh", - "postinstall": "node scripts/postInstall" + "postinstall": "node scripts/postInstall; npm run generate-v4-fixtures" }, "files": [ "/dist", diff --git a/scripts/generateV4JsonFixtures.ts b/scripts/generateV4JsonFixtures.ts new file mode 100644 index 00000000..609aa705 --- /dev/null +++ b/scripts/generateV4JsonFixtures.ts @@ -0,0 +1,18 @@ +import fs from "fs"; +import path from "path"; +import * as V4_FIXTURES from "../src/4.0/fixtures"; + +const OUTPUT_DIR = path.resolve("./test/fixtures/v4/__generated__"); + +// make sure the output directory exists +if (fs.existsSync(OUTPUT_DIR)) { + fs.rmSync(OUTPUT_DIR, { recursive: true }); +} +fs.mkdirSync(OUTPUT_DIR); + +for (const [key, value] of Object.entries(V4_FIXTURES)) { + fs.writeFileSync( + path.join(OUTPUT_DIR, key.replace(/_/g, "-").toLowerCase() + ".json"), + JSON.stringify(value, null, 2) + ); +} diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 7d8609c5..7cc74fc2 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -2,15 +2,12 @@ import { cloneDeep } from "lodash"; import { digestCredential } from "../digest"; import { obfuscateVerifiableCredential } from "../obfuscate"; import { decodeSalt } from "../salt"; -import sample from "../../../test/fixtures/v4/did-signed-wrapped.json"; -import { V4WrappedDocument } from "../types"; +import { SIGNED_WRAPPED_DOCUMENT_DID } from "../fixtures"; -// TODO: remove unknown -const verifiableCredential = sample as unknown as V4WrappedDocument; // Digest will change whenever sample document is regenerated const credentialRoot = "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"; -const { proof, ...credential } = verifiableCredential; +const { proof, ...credential } = SIGNED_WRAPPED_DOCUMENT_DID; describe("digest v4.0", () => { describe("digestCredential", () => { @@ -21,7 +18,7 @@ describe("digest v4.0", () => { expect(digest).toBe(credentialRoot); }); test("digests a document when one single element is obfuscated", () => { - const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(verifiableCredential, "issuer.id"); + const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(SIGNED_WRAPPED_DOCUMENT_DID, "issuer.id"); const digest = digestCredential( obfuscatedVerifiableCredential, decodeSalt(obfuscatedVerifiableCredential.proof.salts), @@ -37,7 +34,7 @@ describe("digest v4.0", () => { expect(digest).toBe(credentialRoot); }); test("digests a document when multiple element are obfuscated", () => { - const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(verifiableCredential, [ + const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(SIGNED_WRAPPED_DOCUMENT_DID, [ "credentialSubject.id", "credentialSubject.name", "credentialSubject.licenses.0.description", @@ -60,8 +57,8 @@ describe("digest v4.0", () => { }); test("digests a document with no visible content correctly", () => { const obfuscatedVerifiableCredential = obfuscateVerifiableCredential( - verifiableCredential, - Object.keys(verifiableCredential).filter((k) => k != "proof") + SIGNED_WRAPPED_DOCUMENT_DID, + Object.keys(SIGNED_WRAPPED_DOCUMENT_DID).filter((k) => k != "proof") ); const digest = digestCredential( obfuscatedVerifiableCredential, diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index 07be75b8..39e59b4e 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -1,168 +1,21 @@ import { W3cVerifiableCredential, V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../types"; - -const RAW_DOCUMENT: V4Document = { - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", - name: "Republic of Singapore Driving Licence", - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", - }, - ], - credentialSubject: { - id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - type: ["DriversLicense"], - name: "John Doe", - licenses: [ - { - class: "3", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - { - class: "3A", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - ], - }, -}; - -const WRAPPED: V4WrappedDocument = { - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - name: "Republic of Singapore Driving Licence", - type: ["VerifiableCredential", "OpenAttestationCredential"], - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { - identityProofType: "DNS-DID", - identifier: "example.openattestation.com", - }, - }, - validFrom: "2021-03-08T12:00:00+08:00", - credentialSubject: { - id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - type: ["DriversLicense"], - name: "John Doe", - licenses: [ - { - class: "3", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - { - class: "3A", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - ], - }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", - }, - ], - proof: { - type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: "assertionMethod", - targetHash: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", - proofs: [], - merkleRoot: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", - salts: - "W3sidmFsdWUiOiIzMThlMDgwY2NjZWYwZmRjMWEyNjhjN2FjOTEwNmNiYzUwNTEyMzkyNTc0MDNhZTU2MmI3YTFhNmU5YjkzY2ZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjE2MTJkMDliZjUzNGZkMmZhNTFmYjlhOWI0NTk1MzE0NTlmNzU3NDA1ZmExMjllMjY3YWEwNTUxOTlhZDY2ZjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2NlMDA0ZGM0M2NiZGY0ZmE5NjRlN2Q3ZjQ0YzQ5ZTMzNDhiMzExZmQxYjc0MzhhYTI2ZjI5YmI2OGU5MGU3NCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjNhZmJmZDc0Mzg1YWQ2NzM0YjE3MDgyOGJhNWUyYWI1YzM4MTY1YTBmYWNlOTJlNGUwODRmNzkyMjFlZDJkMDAiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIxYTMwZTE4MTI3N2I2ZjFlZTczOTlmMTQxZWU5MjY2MDIwMWUxYTRlYmZkODEzOTJiNjgxYTEwMjMxMTUzY2Q1IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMWU4ZDRlODY5ZjVkMzE1YmIxMzcyZjRhOTQxYWNjNjgxMWY2OWIwZmRiMmFkOWM3MTRjMGJiNTZhMmMzMjdmZiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYzVjN2MxNzVhNmU5ZTFlZDQ1OTkxYTExYjhiNjczZmRiNWU4ZTU4NDU5OTliM2FkODYwMjM5OTdiOWUzNWEzNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyMjNkZDJhOGMwZTZhOGJlMTE2ZmUxMzliM2Q0YjRkYWZlMTBkNWJiNWEwNmVjNzE2YjNhM2JiNDZlYjI3ZDNjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjM0YzJmMTU4OWM2MTFmYWZmOTE4MTAwODk3MGJiMGY1NTc3MWQ1ODI0ODQxOGY3OGVhOWM4NTk3NTgwZDM1ZTkiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYWE0MmVhNDE3MGJhNmRhMTVmZTdiMzIyZTUxMzc3ODA5M2E2NjE3MTE1NzZmOTA2M2JmY2Y4YzdlMGEzYjRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU5ZjNlMTJiYTUzZDA3M2Y0ZjNiZTU2ZDUzN2ZjYjIwZmI0ZTM0YzZkM2RlZGZkNTcyMzkzODAzOTViZGM4ZTkiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjhkNGIyMDYyZmFmNzRhNmZhNGE5NmE2OTQ0NzIyMWFjNjhjZTk0MjBjOWQ2ZTFhNDFjODM2MmI1ODQwMmI5ODQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTMxNmM0YTRhZGJiMzQwYTBjYTViNDljNTZmNWIxZWMzNjA5N2UyMjg4YzJkY2I0YmU4ZjhiY2MxNDA4NjIyYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjJlZmY0Y2JlZjFkNmQ1ZTEwOTZhNDAyNGY4ODhmM2U2YTEwMWZkYjIxZTRjZmI1ZjdmYTA4Y2ZhZmZkMzk2YjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI0YjAxYjNmZWIzOTcyYTBmMDBjNTJkNDU2YmYxZWY1ZWJkODI3YjkxMDFjMzI4NTQwNzExM2NiODZhODNiY2JmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjcxMjE4ZTA2YTNlNGM1NTc5NTIwNzdkNzQ1NTJlZTMyNDIxNzMxMjU2YTcwZDI1Nzg3ZGQyNmFkMDk1YmY5NzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNWM2NzdhYzgwMGQ3MDRiZjUwYzZiYjAyN2Q0OTg1YmQ2OTg0Y2VhYjExNzUzOTk0ZWQ5YTI5OTIxYjhmYWRmNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVhMjY1MDM0OTFlYjhjZjNiN2EzMTg1MGEwZTM3OTQxMjFiN2YzOTQ5OTI1NmIyNGQ3OWNkODE3MDVjNDcxOTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNzlhOWZiOTVkM2U5NTlkNjFlODFhNmQwMGY3ZTlkZjAwZTAzNTMzYTYyZDZlZDkyNWI4MTIyNjY5YThkYzZkNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YzhkOWQ0YzRkYjRhODU4NmJkODgzZmViZmNhNTUzNmYxYmVmNDJhM2NmYTJmYTQxNWY0YWFkZjIyZmY1MTlkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNGNkOWMwOGU3NDE2MzU0YjhiNjBlYzA1YzQzZDY3YWMwOTAzNmQ4YWRlZGYyZGVjMjIxNWU1NmU4MGM1MDg5MCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiOGRjNGY3NDllN2JhZmZmZWJmM2FmOGY4ZDJjYjUwNTAzZGFmOGZhZTVkM2Q3YjhjNTNhZDM1NTFiMWM1NDI0MyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJkZjY0NmI2YjYwMTJmZWQxYzE5N2E5MjhjNGJjZTVhOWJlNTc0YjU4YmFhYjZkN2E4OTAwZDBiZDdkYjg4N2IyIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", - privacy: { - obfuscated: [], - }, - }, -}; - -const SIGNED_WRAPPED: V4SignedWrappedDocument = { - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", - name: "Republic of Singapore Driving Licence", - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", - }, - ], - credentialSubject: { - id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - type: ["DriversLicense"], - name: "John Doe", - licenses: [ - { - class: "3", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - { - class: "3A", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - ], - }, - proof: { - type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: "assertionMethod", - targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - proofs: [], - merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - salts: - "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", - privacy: { obfuscated: [] }, - key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - signature: - "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", - }, -}; +import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID, WRAPPED_DOCUMENT_DID } from "../fixtures"; describe("v4 guard", () => { describe("given a raw document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: W3cVerifiableCredential = RAW_DOCUMENT; + const w3cVerifiableCredential: W3cVerifiableCredential = RAW_DOCUMENT_DID; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); - expect(results).toEqual(RAW_DOCUMENT); + expect(results).toEqual(RAW_DOCUMENT_DID); }); test("should pass document validation without removal of any data", () => { - const results = V4Document.parse(RAW_DOCUMENT); - expect(results).toEqual(RAW_DOCUMENT); + const results = V4Document.parse(RAW_DOCUMENT_DID); + expect(results).toEqual(RAW_DOCUMENT_DID); }); test("should fail wrapped document validation", () => { - const results = V4WrappedDocument.safeParse(RAW_DOCUMENT); + const results = V4WrappedDocument.safeParse(RAW_DOCUMENT_DID); expect(results.success).toBe(false); expect((results as { error: unknown }).error).toMatchInlineSnapshot(` [ZodError: [ @@ -180,7 +33,7 @@ describe("v4 guard", () => { }); test("should fail signed wrapped document validation", () => { - const results = V4SignedWrappedDocument.safeParse(RAW_DOCUMENT); + const results = V4SignedWrappedDocument.safeParse(RAW_DOCUMENT_DID); expect(results.success).toBe(false); expect((results as { error: unknown }).error).toMatchInlineSnapshot(` [ZodError: [ @@ -200,24 +53,24 @@ describe("v4 guard", () => { describe("given a wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: W3cVerifiableCredential = WRAPPED; + const w3cVerifiableCredential: W3cVerifiableCredential = WRAPPED_DOCUMENT_DID; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); - expect(results).toEqual(WRAPPED); + expect(results).toEqual(WRAPPED_DOCUMENT_DID); }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = WRAPPED; + const v4Document: V4Document = WRAPPED_DOCUMENT_DID; const results = V4Document.parse(v4Document); - expect(results).toEqual(WRAPPED); + expect(results).toEqual(WRAPPED_DOCUMENT_DID); }); test("should pass wrapped document validation without removal of any data", () => { - const results = V4WrappedDocument.parse(WRAPPED); - expect(results).toEqual(WRAPPED); + const results = V4WrappedDocument.parse(WRAPPED_DOCUMENT_DID); + expect(results).toEqual(WRAPPED_DOCUMENT_DID); }); test("should fail signed wrapped document validation", () => { - const results = V4SignedWrappedDocument.safeParse(WRAPPED); + const results = V4SignedWrappedDocument.safeParse(WRAPPED_DOCUMENT_DID); expect(results.success).toBe(false); expect((results as { error: unknown }).error).toMatchInlineSnapshot(` [ZodError: [ @@ -248,26 +101,26 @@ describe("v4 guard", () => { describe("given a signed wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED; + const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED_DOCUMENT_DID; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); - expect(results).toEqual(SIGNED_WRAPPED); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = SIGNED_WRAPPED; + const v4Document: V4Document = SIGNED_WRAPPED_DOCUMENT_DID; const results = V4Document.parse(v4Document); - expect(results).toEqual(SIGNED_WRAPPED); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); }); test("should pass wrapped document validation without removal of any data", () => { - const v4WrappedDocument: V4WrappedDocument = SIGNED_WRAPPED; + const v4WrappedDocument: V4WrappedDocument = SIGNED_WRAPPED_DOCUMENT_DID; const results = V4WrappedDocument.parse(v4WrappedDocument); - expect(results).toEqual(SIGNED_WRAPPED); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); }); test("should pass signed wrapped document validation without removal of any data", () => { - const results = V4SignedWrappedDocument.parse(SIGNED_WRAPPED); - expect(results).toEqual(SIGNED_WRAPPED); + const results = V4SignedWrappedDocument.parse(SIGNED_WRAPPED_DOCUMENT_DID); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); }); }); }); diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts new file mode 100644 index 00000000..846561eb --- /dev/null +++ b/src/4.0/fixtures.ts @@ -0,0 +1,297 @@ +import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; + +export const RAW_DOCUMENT_DID = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, +} satisfies V4Document; + +export const RAW_DOCUMENT_DID_OSCP = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + credentialStatus: { + id: "https://ocsp-sandbox.openattestation.com", + type: "OpenAttestationOcspResponder", + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, +} satisfies V4Document; + +export const WRAPPED_DOCUMENT_DID = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", + proofs: [], + merkleRoot: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", + salts: + "W3sidmFsdWUiOiIzMThlMDgwY2NjZWYwZmRjMWEyNjhjN2FjOTEwNmNiYzUwNTEyMzkyNTc0MDNhZTU2MmI3YTFhNmU5YjkzY2ZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjE2MTJkMDliZjUzNGZkMmZhNTFmYjlhOWI0NTk1MzE0NTlmNzU3NDA1ZmExMjllMjY3YWEwNTUxOTlhZDY2ZjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2NlMDA0ZGM0M2NiZGY0ZmE5NjRlN2Q3ZjQ0YzQ5ZTMzNDhiMzExZmQxYjc0MzhhYTI2ZjI5YmI2OGU5MGU3NCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjNhZmJmZDc0Mzg1YWQ2NzM0YjE3MDgyOGJhNWUyYWI1YzM4MTY1YTBmYWNlOTJlNGUwODRmNzkyMjFlZDJkMDAiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIxYTMwZTE4MTI3N2I2ZjFlZTczOTlmMTQxZWU5MjY2MDIwMWUxYTRlYmZkODEzOTJiNjgxYTEwMjMxMTUzY2Q1IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMWU4ZDRlODY5ZjVkMzE1YmIxMzcyZjRhOTQxYWNjNjgxMWY2OWIwZmRiMmFkOWM3MTRjMGJiNTZhMmMzMjdmZiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYzVjN2MxNzVhNmU5ZTFlZDQ1OTkxYTExYjhiNjczZmRiNWU4ZTU4NDU5OTliM2FkODYwMjM5OTdiOWUzNWEzNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyMjNkZDJhOGMwZTZhOGJlMTE2ZmUxMzliM2Q0YjRkYWZlMTBkNWJiNWEwNmVjNzE2YjNhM2JiNDZlYjI3ZDNjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjM0YzJmMTU4OWM2MTFmYWZmOTE4MTAwODk3MGJiMGY1NTc3MWQ1ODI0ODQxOGY3OGVhOWM4NTk3NTgwZDM1ZTkiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYWE0MmVhNDE3MGJhNmRhMTVmZTdiMzIyZTUxMzc3ODA5M2E2NjE3MTE1NzZmOTA2M2JmY2Y4YzdlMGEzYjRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU5ZjNlMTJiYTUzZDA3M2Y0ZjNiZTU2ZDUzN2ZjYjIwZmI0ZTM0YzZkM2RlZGZkNTcyMzkzODAzOTViZGM4ZTkiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjhkNGIyMDYyZmFmNzRhNmZhNGE5NmE2OTQ0NzIyMWFjNjhjZTk0MjBjOWQ2ZTFhNDFjODM2MmI1ODQwMmI5ODQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTMxNmM0YTRhZGJiMzQwYTBjYTViNDljNTZmNWIxZWMzNjA5N2UyMjg4YzJkY2I0YmU4ZjhiY2MxNDA4NjIyYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjJlZmY0Y2JlZjFkNmQ1ZTEwOTZhNDAyNGY4ODhmM2U2YTEwMWZkYjIxZTRjZmI1ZjdmYTA4Y2ZhZmZkMzk2YjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI0YjAxYjNmZWIzOTcyYTBmMDBjNTJkNDU2YmYxZWY1ZWJkODI3YjkxMDFjMzI4NTQwNzExM2NiODZhODNiY2JmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjcxMjE4ZTA2YTNlNGM1NTc5NTIwNzdkNzQ1NTJlZTMyNDIxNzMxMjU2YTcwZDI1Nzg3ZGQyNmFkMDk1YmY5NzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNWM2NzdhYzgwMGQ3MDRiZjUwYzZiYjAyN2Q0OTg1YmQ2OTg0Y2VhYjExNzUzOTk0ZWQ5YTI5OTIxYjhmYWRmNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVhMjY1MDM0OTFlYjhjZjNiN2EzMTg1MGEwZTM3OTQxMjFiN2YzOTQ5OTI1NmIyNGQ3OWNkODE3MDVjNDcxOTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNzlhOWZiOTVkM2U5NTlkNjFlODFhNmQwMGY3ZTlkZjAwZTAzNTMzYTYyZDZlZDkyNWI4MTIyNjY5YThkYzZkNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YzhkOWQ0YzRkYjRhODU4NmJkODgzZmViZmNhNTUzNmYxYmVmNDJhM2NmYTJmYTQxNWY0YWFkZjIyZmY1MTlkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNGNkOWMwOGU3NDE2MzU0YjhiNjBlYzA1YzQzZDY3YWMwOTAzNmQ4YWRlZGYyZGVjMjIxNWU1NmU4MGM1MDg5MCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiOGRjNGY3NDllN2JhZmZmZWJmM2FmOGY4ZDJjYjUwNTAzZGFmOGZhZTVkM2Q3YjhjNTNhZDM1NTFiMWM1NDI0MyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJkZjY0NmI2YjYwMTJmZWQxYzE5N2E5MjhjNGJjZTVhOWJlNTc0YjU4YmFhYjZkN2E4OTAwZDBiZDdkYjg4N2IyIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { + obfuscated: [], + }, + }, +} satisfies V4WrappedDocument; + +export const SIGNED_WRAPPED_DOCUMENT_DID = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + proofs: [], + merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + salts: + "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", + privacy: { obfuscated: [] }, + key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + signature: + "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", + }, +} satisfies V4SignedWrappedDocument; + +export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + credentialStatus: { id: "https://ocsp-sandbox.openattestation.com", type: "OpenAttestationOcspResponder" }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { class: "3", effectiveDate: "2013-05-16T00:00:00+08:00" }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + proofs: [], + merkleRoot: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + salts: + "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2E1ZTExMzdhYmY3OGYzMzViMzNlNjA3YmZmNmU4YWZkM2YyZTJkOTc2YWZiOWM1OGE1Nzk4YmEwZTllYmVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYmY2YWI5YzQ2ZTVmZjBkNzM1Nzc2MjUxOTQ1NmFlYmI1YTU1NGJjNDg0NTRmN2EyYTU2NTdiNGIyODQ3ZTFmMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiI1NTJmOGQ0Y2MxMjRmYzIyYjQxM2I4YjYzYzBhZjcwZWI0MDgzNDYxOWMxNDRjYWZmYWI3MDFmMGE1MzYzMmJjIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImJhZWE5NWEyYmVlNTU3NWRlY2UzM2ZiZjFhODBhZWYxMmY2NWExYjY4NGRkMjQ0ZjZmNThmMjJmYjJiYWQ4N2UiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", + privacy: { obfuscated: ["c866663b38353fdb46a372cd0302fd7752cc75bee4b8fc4d65c2e3b22f2466f7"] }, + key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + signature: + "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", + }, +} satisfies V4SignedWrappedDocument; + +export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + credentialStatus: { id: "https://ocsp-sandbox.openattestation.com", type: "OpenAttestationOcspResponder" }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + proofs: [], + merkleRoot: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + salts: + "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJkZjNmMjY5NzE5YTczYzA1NDc4Y2Q1YTk0MTQwMDVmMjA2NWNhNWNhODg5NGUwNjc2NDllYmEwMzcyNjBmNDgzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImNjYTVlMTEzN2FiZjc4ZjMzNWIzM2U2MDdiZmY2ZThhZmQzZjJlMmQ5NzZhZmI5YzU4YTU3OThiYTBlOWViZWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiJiZjZhYjljNDZlNWZmMGQ3MzU3NzYyNTE5NDU2YWViYjVhNTU0YmM0ODQ1NGY3YTJhNTY1N2I0YjI4NDdlMWYxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6IjU1MmY4ZDRjYzEyNGZjMjJiNDEzYjhiNjNjMGFmNzBlYjQwODM0NjE5YzE0NGNhZmZhYjcwMWYwYTUzNjMyYmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYmFlYTk1YTJiZWU1NTc1ZGVjZTMzZmJmMWE4MGFlZjEyZjY1YTFiNjg0ZGQyNDRmNmY1OGYyMmZiMmJhZDg3ZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn1d", + privacy: { obfuscated: [] }, + key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + signature: + "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", + }, +} satisfies V4SignedWrappedDocument; From a14f73cc73bd936d32bd3f7478f11d0e6910c825 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 01:18:55 +0800 Subject: [PATCH 066/107] fix: could not generated nested dir --- scripts/generateV4JsonFixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generateV4JsonFixtures.ts b/scripts/generateV4JsonFixtures.ts index 609aa705..fc62f13a 100644 --- a/scripts/generateV4JsonFixtures.ts +++ b/scripts/generateV4JsonFixtures.ts @@ -8,7 +8,7 @@ const OUTPUT_DIR = path.resolve("./test/fixtures/v4/__generated__"); if (fs.existsSync(OUTPUT_DIR)) { fs.rmSync(OUTPUT_DIR, { recursive: true }); } -fs.mkdirSync(OUTPUT_DIR); +fs.mkdirSync(OUTPUT_DIR, { recursive: true }); for (const [key, value] of Object.entries(V4_FIXTURES)) { fs.writeFileSync( From d27c9efb32cee059ebc247d61db23d424817aa4f Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 10:12:36 +0800 Subject: [PATCH 067/107] test: add wrap unit tests --- src/4.0/__tests__/digest.test.ts | 2 +- src/4.0/__tests__/guard.test.ts | 2 +- src/4.0/__tests__/wrap.test.ts | 107 +++++++++++++++++++++++++++++++ src/4.0/wrap.ts | 2 +- 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/4.0/__tests__/wrap.test.ts diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 7cc74fc2..7e41cd70 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -9,7 +9,7 @@ const credentialRoot = "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7ae const { proof, ...credential } = SIGNED_WRAPPED_DOCUMENT_DID; -describe("digest v4.0", () => { +describe("V4.0 digest", () => { describe("digestCredential", () => { test("digests a document with all visible content correctly", () => { const clonedCredential = cloneDeep(credential); diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index 39e59b4e..5ffc4132 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -1,7 +1,7 @@ import { W3cVerifiableCredential, V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../types"; import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID, WRAPPED_DOCUMENT_DID } from "../fixtures"; -describe("v4 guard", () => { +describe("V4.0 guard", () => { describe("given a raw document", () => { test("should pass w3c vc validation without removal of any data", () => { const w3cVerifiableCredential: W3cVerifiableCredential = RAW_DOCUMENT_DID; diff --git a/src/4.0/__tests__/wrap.test.ts b/src/4.0/__tests__/wrap.test.ts new file mode 100644 index 00000000..21b89f6e --- /dev/null +++ b/src/4.0/__tests__/wrap.test.ts @@ -0,0 +1,107 @@ +import { V4Document, V4WrappedDocument, W3cVerifiableCredential } from "../types"; +import { wrapDocument } from "../wrap"; + +describe("V4.0 wrap document", () => { + test("given a valid v4 document, should wrap correctly", async () => { + const wrapped = await wrapDocument({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + credentialSubject: { + id: "0x1234567890123456789012345678901234567890", + name: "John Doe", + country: "SG", + }, + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + }); + const parsedResults = V4WrappedDocument.safeParse(wrapped); + if (!parsedResults.success) { + throw new Error("Parsing failed"); + } + const { proof } = parsedResults.data; + expect(proof.merkleRoot.length).toBe(64); + expect(proof.privacy.obfuscated).toEqual([]); + expect(proof.proofPurpose).toBe("assertionMethod"); + expect(proof.proofs).toEqual([]); + expect(proof.salts.length).toBeGreaterThan(0); + expect(proof.targetHash.length).toBe(64); + expect(proof.type).toBe("OpenAttestationMerkleProofSignature2018"); + }); + + test("given a document with explicit v4 contexts, but does not conform to the V4 document schema, should throw", async () => { + await expect( + wrapDocument({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + credentialSubject: { + id: "0x1234567890123456789012345678901234567890", + name: "John Doe", + country: "SG", + }, + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + } as V4Document["issuer"], + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Input document does not conform to OpenAttestation v4.0 Data Model: [{"code":"invalid_literal","expected":"OpenAttestationIssuer","path":["issuer","type"],"message":"Invalid literal value, expected \\"OpenAttestationIssuer\\""}]"` + ); + }); + + test("given a valid v4 document but has an extra field, should throw", async () => { + await expect( + wrapDocument({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + credentialSubject: { + id: "0x1234567890123456789012345678901234567890", + name: "John Doe", + country: "SG", + }, + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + extraField: "extra", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + // this should not exist + extraField: "extra", + } as V4Document) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Input document does not conform to OpenAttestation v4.0 Data Model: [{"code":"unrecognized_keys","keys":["extraField"],"path":[],"message":"Unrecognized key(s) in object: 'extraField'"}]"` + ); + }); + + test("given a generic w3c vc, should wrap with context and type corrected", async () => { + const genericW3cVc: W3cVerifiableCredential = { + "@context": ["https://www.w3.org/ns/credentials/v2"], + type: ["VerifiableCredential"], + credentialSubject: { + id: "0x1234567890123456789012345678901234567890", + name: "John Doe", + country: "SG", + }, + issuer: { + id: "https://example.com/issuer/123", + }, + }; + const wrapped = await wrapDocument(genericW3cVc as unknown as V4Document); + const parsedResults = V4WrappedDocument.pick({ "@context": true, type: true }).passthrough().safeParse(wrapped); + expect(parsedResults.success).toBe(true); + }); +}); diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index a310ee49..a3001480 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -11,7 +11,7 @@ export const wrapDocument = async ( document: NoExtraProperties ): Promise> => { /* 1a. try OpenAttestation VC validation, since most user will be issuing oa v4*/ - const oav4context = await V4Document.pick({ "@context": true }).safeParseAsync(document); // Superficial check on user intention + const oav4context = await V4Document.pick({ "@context": true }).passthrough().safeParseAsync(document); // Superficial check on user intention let validatedRawDocument: W3cVerifiableCredential | undefined; if (oav4context.success) { const oav4 = await V4Document.safeParseAsync(document); From abbbf277a7c98de413a75f6f065fa57b993e9047 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 10:15:14 +0800 Subject: [PATCH 068/107] test: extra assertion for proof --- src/4.0/__tests__/wrap.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/4.0/__tests__/wrap.test.ts b/src/4.0/__tests__/wrap.test.ts index 21b89f6e..490d5659 100644 --- a/src/4.0/__tests__/wrap.test.ts +++ b/src/4.0/__tests__/wrap.test.ts @@ -103,5 +103,12 @@ describe("V4.0 wrap document", () => { const wrapped = await wrapDocument(genericW3cVc as unknown as V4Document); const parsedResults = V4WrappedDocument.pick({ "@context": true, type: true }).passthrough().safeParse(wrapped); expect(parsedResults.success).toBe(true); + expect(wrapped.proof.merkleRoot.length).toBe(64); + expect(wrapped.proof.privacy.obfuscated).toEqual([]); + expect(wrapped.proof.proofPurpose).toBe("assertionMethod"); + expect(wrapped.proof.proofs).toEqual([]); + expect(wrapped.proof.salts.length).toBeGreaterThan(0); + expect(wrapped.proof.targetHash.length).toBe(64); + expect(wrapped.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); }); }); From f761d1fe8059d25ab8e1e933794014b97962db01 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 10:29:47 +0800 Subject: [PATCH 069/107] fix: salt test --- .../__tests__/{salt.test.wip => salt.test.ts} | 43 +------------------ 1 file changed, 1 insertion(+), 42 deletions(-) rename src/4.0/__tests__/{salt.test.wip => salt.test.ts} (77%) diff --git a/src/4.0/__tests__/salt.test.wip b/src/4.0/__tests__/salt.test.ts similarity index 77% rename from src/4.0/__tests__/salt.test.wip rename to src/4.0/__tests__/salt.test.ts index cf2e9a98..66f955d6 100644 --- a/src/4.0/__tests__/salt.test.wip +++ b/src/4.0/__tests__/salt.test.ts @@ -1,42 +1,10 @@ -import { cloneDeep } from "lodash"; -import { Method, ProofType, OpenAttestationDocument, TemplateType } from "../../__generated__/schema.3.0"; import { salt, decodeSalt } from "../salt"; -import * as v3 from "../../__generated__/schema.3.0"; import { Base64 } from "js-base64"; -const sampleDoc: OpenAttestationDocument = { - "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], - id: "http://example.edu/credentials/58473", - type: ["VerifiableCredential", "AlumniCredential"], - issuer: "https://example.edu/issuers/14", - issuanceDate: "2010-01-01T19:23:24Z", - credentialSubject: { - id: "did:example:ebfeb1f712ebc6f1c276e12ec21", - alumniOf: "Example University", - }, - openAttestationMetadata: { - template: { - name: "EXAMPLE_RENDERER", - type: TemplateType.EmbeddedRenderer, - url: "https://renderer.openattestation.com/", - }, - proof: { - type: ProofType.OpenAttestationProofMethod, - method: Method.DocumentStore, - value: "0xED2E50434Ac3623bAD763a35213DAD79b43208E4", - }, - identityProof: { - identifier: "some.example", - type: v3.IdentityProofType.DNSTxt, - }, - }, -}; - -describe("digest v3.0", () => { +describe("V4.0 digest", () => { describe("salt", () => { test("handles shadowed keys correctly (type 1: root, dot notation)", () => { const document = { - ...cloneDeep(sampleDoc), "credentialSubject.alumniOf": "0xSomeMaliciousDocumentStore, this would be at credentialSubject.alumniOf after flatMap if uncaught", }; @@ -46,7 +14,6 @@ describe("digest v3.0", () => { }); test("handles shadowed keys correctly (type 2: root, array index)", () => { const document = { - ...cloneDeep(sampleDoc), "type[1]": "MaliciousCredential, this would be at type[1] after flatMap if uncaught", }; expect(() => { @@ -55,7 +22,6 @@ describe("digest v3.0", () => { }); test("handles shadowed keys correctly (type 3: nested as object, dot notation)", () => { const document = { - ...cloneDeep(sampleDoc), nested: { "credentialSubject.alumniOf": "0xSomeMaliciousDocumentStore, this would be at nested.credentialSubject.alumniOf after flatMap if uncaught", @@ -67,7 +33,6 @@ describe("digest v3.0", () => { }); test("handles shadowed keys correctly (type 4: nested as object, array index)", () => { const document = { - ...cloneDeep(sampleDoc), nested: { "type[1]": "this would be at nested.type[1] after flatMap if uncaught" }, }; expect(() => { @@ -76,7 +41,6 @@ describe("digest v3.0", () => { }); test("handles shadowed keys correctly (type 5: nested as array, dot notation)", () => { const document = { - ...cloneDeep(sampleDoc), nested: [{ "shadowed.key": "this would be at nested[0].shadowed.key after flatMap if uncaught" }], }; expect(() => { @@ -85,7 +49,6 @@ describe("digest v3.0", () => { }); test("handles shadowed keys correctly (type 6: nested as array, array index)", () => { const document = { - ...cloneDeep(sampleDoc), nested: [{ "type[1]": "this would be at nested[0].type[1] after flatMap if uncaught" }], }; expect(() => { @@ -95,7 +58,6 @@ describe("digest v3.0", () => { test("handles null values correctly", () => { const document = { - ...cloneDeep(sampleDoc), grades: null, }; const salted = salt(document); @@ -103,7 +65,6 @@ describe("digest v3.0", () => { }); test("handles undefined values correctly", () => { const document = { - ...cloneDeep(sampleDoc), grades: undefined, }; expect(() => { @@ -112,7 +73,6 @@ describe("digest v3.0", () => { }); test("handles numbers and booleans correctly", () => { const document = { - ...cloneDeep(sampleDoc), grades: ["A+", 100, 50.28, true, "B+"], }; const salted = salt(document); @@ -124,7 +84,6 @@ describe("digest v3.0", () => { }); test("handles sparse arrays correctly", () => { const document = { - ...cloneDeep(sampleDoc), grades: ["A+", 100, , , , true, "B+"], }; const salted = salt(document); From 5452590844ba1bce43a2c12faa68a5b2af35ebde Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 11:01:28 +0800 Subject: [PATCH 070/107] fix: allow resigning and validate proof before using --- src/4.0/sign.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index 3d3e6f3e..892aa47c 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -1,6 +1,5 @@ import { sign } from "../shared/signer"; import { SigningKey, SUPPORTED_SIGNING_ALGORITHM } from "../shared/@types/sign"; -import { isSignedWrappedV4Document } from "../shared/utils"; import { ethers } from "ethers"; import { V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "./types"; @@ -9,11 +8,15 @@ export const signDocument = async ( algorithm: SUPPORTED_SIGNING_ALGORITHM, keyOrSigner: SigningKey | ethers.Signer ): Promise> => { - if (isSignedWrappedV4Document(document)) throw new Error("Document has been signed"); - const merkleRoot = `0x${document.proof.merkleRoot}`; + const parsedResults = V4WrappedDocument.pick({ proof: true }).passthrough().safeParse(document); + if (!parsedResults.success) { + throw new Error("Document has not been wrapped yet"); + } + const { proof: validatedProof } = parsedResults.data; + const merkleRoot = `0x${validatedProof.merkleRoot}`; const signature = await sign(algorithm, merkleRoot, keyOrSigner); const proof: V4SignedWrappedDocument["proof"] = { - ...document.proof, + ...validatedProof, key: SigningKey.guard(keyOrSigner) ? keyOrSigner.public : `did:ethr:${await keyOrSigner.getAddress()}#controller`, signature, }; From 8eb6e9af2463044966bf250c3e41142425a39fbd Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 11:11:11 +0800 Subject: [PATCH 071/107] fix: v4 sign tests --- src/4.0/__tests__/sign.test.ts | 94 +++++++++++++++++++++++++++++++++ src/4.0/__tests__/sign.test.wip | 60 --------------------- src/4.0/sign.ts | 2 +- 3 files changed, 95 insertions(+), 61 deletions(-) create mode 100644 src/4.0/__tests__/sign.test.ts delete mode 100644 src/4.0/__tests__/sign.test.wip diff --git a/src/4.0/__tests__/sign.test.ts b/src/4.0/__tests__/sign.test.ts new file mode 100644 index 00000000..31102c36 --- /dev/null +++ b/src/4.0/__tests__/sign.test.ts @@ -0,0 +1,94 @@ +import { signDocument } from "../../index"; +import { SUPPORTED_SIGNING_ALGORITHM } from "../../shared/@types/sign"; +import { Wallet } from "ethers"; +import { WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { V4SignedWrappedDocument } from "../types"; + +describe("V4 sign", () => { + it("should sign a document", async () => { + const signedWrappedDocument = await signDocument( + WRAPPED_DOCUMENT_DID, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + { + public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", + } + ); + const parsedResults = V4SignedWrappedDocument.safeParse(signedWrappedDocument); + if (!parsedResults.success) { + throw new Error("Parsing failed"); + } + const { proof } = parsedResults.data; + expect(Object.keys(proof).length).toBe(9); + expect(proof.key).toBe("did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller"); + expect(proof.signature).toBe( + "0xcc3f2d88367976231b8cec5d9ceb89b8f1236ae0d3b133c6c06cf66a3cd459e6023277b5f959ffc82755c64edfc38c7131d6dc584e253615c8e3be2ffe7bb7f01c" + ); + }); + it("should sign a document with a wallet", async () => { + const wallet = Wallet.fromMnemonic( + "tourist quality multiply denial diary height funny calm disease buddy speed gold" + ); + const signedWrappedDocument = await signDocument( + WRAPPED_DOCUMENT_DID, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + wallet + ); + const parsedResults = V4SignedWrappedDocument.safeParse(signedWrappedDocument); + if (!parsedResults.success) { + throw new Error("Parsing failed"); + } + const { proof } = parsedResults.data; + expect(Object.keys(proof).length).toBe(9); + expect(proof.key).toBe("did:ethr:0x906FB815De8976b1e38D9a4C1014a3acE16Ce53C#controller"); + expect(proof.signature).toBe( + "0x10d291950915358d2326009f1e1afbea51355abd7bf49e1dedb96dd119fe6798264ba7033930f4de4d19624e6f2c16c4dcf6b4335b8dea849526922e3dc140631b" + ); + }); + + it("should a signed document to be resigned", async () => { + const signedDocument = await signDocument( + WRAPPED_DOCUMENT_DID, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + { + public: "did:ethr:0xb6De3744E1259e1aB692f5a277f053B79429c5a2#controller", + private: "0x812269266b34d2919f737daf22db95f02642f8cdc0ca673bf3f701599f4971f5", + } + ); + + const resignedDocument = await signDocument( + WRAPPED_DOCUMENT_DID, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + { + public: "did:ethr:0xb6De3744E1259e1aB692f5a277f053B79429c5a2#controller", + private: "0x812269266b34d2919f737daf22db95f02642f8cdc0ca673bf3f701599f4971f5", + } + ); + + expect(signedDocument).toEqual(resignedDocument); + }); + + it("should throw error if a key or signer is invalid", async () => { + await expect( + signDocument(WRAPPED_DOCUMENT_DID, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, {} as any) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Either a keypair or ethers.js Signer must be provided"`); + }); + + it("should throw error if proof is malformed", async () => { + await expect( + signDocument( + { + ...WRAPPED_DOCUMENT_DID, + proof: { ...WRAPPED_DOCUMENT_DID.proof, merkleRoot: undefined as unknown as string }, + }, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + { + public: "did:ethr:0xb6De3744E1259e1aB692f5a277f053B79429c5a2#controller", + private: "0x812269266b34d2919f737daf22db95f02642f8cdc0ca673bf3f701599f4971f5", + } + ) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be signed"` + ); + }); +}); diff --git a/src/4.0/__tests__/sign.test.wip b/src/4.0/__tests__/sign.test.wip deleted file mode 100644 index aafde700..00000000 --- a/src/4.0/__tests__/sign.test.wip +++ /dev/null @@ -1,60 +0,0 @@ -import { signDocument, v3 } from "../../index"; -import { SUPPORTED_SIGNING_ALGORITHM } from "../../shared/@types/sign"; -import rawWrappedDocumentV3 from "../../../test/fixtures/v3/did-wrapped.json"; -import { Wallet } from "ethers"; - -const wrappedDocumentV3 = rawWrappedDocumentV3 as v3.WrappedDocument; - -describe("v3", () => { - it("should sign a document", async () => { - const { proof } = await signDocument(wrappedDocumentV3, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, { - public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", - private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", - }); - expect(Object.keys(proof).length).toBe(9); - expect(proof.key).toBe("did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller"); - expect(proof.signature).toBe( - "0x3ce62bb2d6a68f15fabe653dbb71edfaf1e2a00c71c98f71801f3a7438ae477a4cf3dbefaff9359b2d2b97473909ba4b19e9d2ac0c735cec926da0d1025af07b1c" - ); - }); - it("should sign a document with a wallet", async () => { - const wallet = Wallet.fromMnemonic( - "tourist quality multiply denial diary height funny calm disease buddy speed gold" - ); - const { proof } = await signDocument( - wrappedDocumentV3, - SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, - wallet - ); - expect(Object.keys(proof).length).toBe(9); - expect(proof.key).toBe("did:ethr:0x906FB815De8976b1e38D9a4C1014a3acE16Ce53C#controller"); - expect(proof.signature).toBe( - "0xdfb8f7b40933d90004d0a94261f0af573702c0040f727ed813273284ef7329cd4420095b28f7d768866556be9d95fd8b89e0ee2bd1f9c589464d3b16d1247d651c" - ); - }); - - it("should throw error if a document was previously signed", async () => { - const signedDocument = await signDocument( - wrappedDocumentV3, - SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, - { - public: "did:ethr:0xb6De3744E1259e1aB692f5a277f053B79429c5a2#controller", - private: "0x812269266b34d2919f737daf22db95f02642f8cdc0ca673bf3f701599f4971f5", - } - ); - - await expect( - signDocument(signedDocument, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, { - public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", - private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Document has been signed"`); - }); - - it("should throw error if a key or signer is invalid", async () => { - await expect( - // @ts-expect-error invalid call - signDocument(wrappedDocumentV3, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, {}) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Either a keypair or ethers.js Signer must be provided"`); - }); -}); diff --git a/src/4.0/sign.ts b/src/4.0/sign.ts index 892aa47c..b1f83a77 100644 --- a/src/4.0/sign.ts +++ b/src/4.0/sign.ts @@ -10,7 +10,7 @@ export const signDocument = async ( ): Promise> => { const parsedResults = V4WrappedDocument.pick({ proof: true }).passthrough().safeParse(document); if (!parsedResults.success) { - throw new Error("Document has not been wrapped yet"); + throw new Error("Document has not been properly wrapped " + JSON.stringify(parsedResults.error)); } const { proof: validatedProof } = parsedResults.data; const merkleRoot = `0x${validatedProof.merkleRoot}`; From 2ab7b11ae817596a21e4932c09cd3bfeeae75abd Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sat, 27 Apr 2024 11:46:26 +0800 Subject: [PATCH 072/107] chore: add batch wrapped document fixture. freeze all documents before exporting --- src/4.0/fixtures.ts | 143 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 131 insertions(+), 12 deletions(-) diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index 846561eb..9beba540 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -1,6 +1,6 @@ import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; -export const RAW_DOCUMENT_DID = { +export const RAW_DOCUMENT_DID = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -38,9 +38,9 @@ export const RAW_DOCUMENT_DID = { }, ], }, -} satisfies V4Document; +} satisfies V4Document); -export const RAW_DOCUMENT_DID_OSCP = { +export const RAW_DOCUMENT_DID_OSCP = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -82,9 +82,9 @@ export const RAW_DOCUMENT_DID_OSCP = { }, ], }, -} satisfies V4Document; +} satisfies V4Document); -export const WRAPPED_DOCUMENT_DID = { +export const WRAPPED_DOCUMENT_DID = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -137,9 +137,9 @@ export const WRAPPED_DOCUMENT_DID = { obfuscated: [], }, }, -} satisfies V4WrappedDocument; +} satisfies V4WrappedDocument); -export const SIGNED_WRAPPED_DOCUMENT_DID = { +export const SIGNED_WRAPPED_DOCUMENT_DID = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -190,9 +190,9 @@ export const SIGNED_WRAPPED_DOCUMENT_DID = { signature: "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", }, -} satisfies V4SignedWrappedDocument; +} satisfies V4SignedWrappedDocument); -export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = { +export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -240,9 +240,9 @@ export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = { signature: "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", }, -} satisfies V4SignedWrappedDocument; +} satisfies V4SignedWrappedDocument); -export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = { +export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", @@ -294,4 +294,123 @@ export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = { signature: "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", }, -} satisfies V4SignedWrappedDocument; +} satisfies V4SignedWrappedDocument); + +export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "e7fbcdadb66fa5561d1891185f8c16fb80e4fdd04d5fbbb7f49d66426a699b18", + proofs: ["a23257bff49b375448b6a7657c6d6fd16000d2f29a54673030b0c9c66cdeb79f"], + merkleRoot: "ac50e4ffdca1256e5771ecc3f266f5f72d1c9f3686b387811628ad0c28f16836", + salts: + "W3sidmFsdWUiOiJjZjM2NWE5YjlkN2ViNjg1OTQ0ZTkwNGM1YzM1MDg2MGU5ODkxODI5ZGQ3N2NkZWVkZTJmMzk3M2UyZWI0MjFhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFkNzVmMDVmNjJkM2FkZDU2YWJkZjUxMjFmN2VmOWMyZDQ1OGYzMjFmMzYwMGI2MDc2OWE3MTc4ODk1NzRjNGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiODQ3MGU2MDhlMDdlM2IxY2YxMjlkMzQ2ZjVhZGQwZjg1NzQ5OWUxMDc0MWI0Y2NiZjdhYzA1MDVjNjdjZTc5OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjAyYWYwZjc1NzU2NDM3MDQ3Mjg2MGY4MzJkMjAxM2I1N2Y0ZGRlMWZhMzhhNDhmMzZjNjE2MzgwNjhkMjZmNDYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI4MDFhN2JhODI1N2Y2NTEyM2JhN2ZhY2Q4OTY5NTBhNmE1NzNhM2RmMDFmY2M0ODFiNmI0OTMxMGYwYTBjYjM0IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzRlZDM3NjUzNDdjYWE4OTg1ZTdjODU4NmNmODU2M2QzNmNkMGE0Y2Y0NmEzYTRkNjdiZTIzOGJjM2IwN2U1YSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNGQyODdjNGZmZDM1MDliNzg4MjUxMDA5Zjc4YWQxNjkwNWRjMzQwOGJlYmJjNDRjNmI1YzFjMjg5ODdhZGVhNCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI1ZjBkOTUwYWJkNGRiNDFhMTdkM2QxY2Q0ZjI2M2ExYTYwNGZlNzVkN2U4MTkwZDRmN2I4Yzc4NjZjMDNjYWE3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImVhM2IyOGM4MjA1ODNhNDNiOTU5ZDE2YmEzNzI0ZTgwYzMzYTYwNWFhMTNiNjBjMjAzYmRkOTExMDllMWQ4ZWQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNmJiOTk1NmQwYjI3Mjg4MTE1MjhmOGRhOTg4ZDIzNGQwNTlkNzAxYmM1OTc5NzkzODlkMjliYmYyMzMyODA5ZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU3NmM2ZDAxMjBiZGI5NjgzMzc5MzdmZTYwYjgwMmI1OTZjZGQyYzk1MGFhM2JkYmM0NjlmMzkwNGM5YmFlNzQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImVkMGVkYzMwMWQwOTQ5MmM2NjVhMDU0OWIyZjM2OGYzY2JjZWY5ZWIwODg4ZWQ4NGZlNTA1MTc1MjAxYzM3MDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZmUzMmEwYjVlOTljNjZmMWYwZmI0OTQzNWNmNzdkNmZjMDBhMzFhZTQwNDJjYzEzMzdjMTNjNzNjZDUxOTY5MSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjI4YjZmMzViNjk4Mzg1ZmRiYTZlMTFkZmJhZDQzYTEwZDc1OGExMGE1ZDEwODMxY2ZhMmYyYWQ2MzI2NTA5MzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyOGY2ZGE0MmUzMjYyOTE0N2Y4ZDVhZjg3NTRlOTJiZWEzMDZkZWQzNzljYzhhZThiYzcyN2Y4MDMwZmJiY2MzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjViMjg3YTIxMGE4NmQ1ZTFmMGI2ZmY3Y2VlZGYyYzBiZDI0YTNmMjQwOTNiMTY1ZjRhZWRlNTliZGU4ZGUxZjAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiOWQyOGRjNTcxMGJlNWMzMjVhMDQ5YmMwODU2NjZiMjdkMDM2OGU5MjBkZGNiOGExZDUwN2I3ZGJkMDcyZjkzZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjJkZjVlMWZkZjFiY2JmOWM0YjJiODYzYmZjODEyNWZhYjAxOThiMjQxNTA3NjdjNzNmYjZmMDkyZDYxYjQ2OGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjY5NjMxNDhiMjIzY2EzMWFkYWJhMTBiNDQ3ZDhkZWViMzE0ZjY3ZTdkN2E0MmRlNzIzYTg1YTY2OTJmYTY2MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YjAyMjQ3ZTVjZTk1YzE1NDZjNTVmNGQ4OTNkMWY1YjE0MmViYzE3ZTM2OGI1OTM3N2UyMzAwZDQxMjRjM2ZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOGE4MTZjOTY0MTY5MGI1Yjg2MjdkYTA0MDRmYzBmMGY1ZWViMjM3NDA1ZjUxY2IwOTY2YzhiMmRhZDI1NGM1NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNjgzOWYzZmQyNDg1NjU5ZmFhMDIyMzBkMGE1Y2E0YmIzN2NlYTFmMGZkMzRiNjg0MGM3M2QyN2ExMWUyZjU1NCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyZDI0NzUyODZjOTk0ZmY2NmFhZDNkNTRlNDQwOTg4ZjdjZjliNzIxMDRjNmVkNDdmYzFjZjBjMzlkMDEzOWFkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { + obfuscated: [], + }, + }, + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "Jane Doe", + licenses: [ + { + class: "3000A", + description: "Motor spaceships with unladen weight <= 3000tonnes", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "a23257bff49b375448b6a7657c6d6fd16000d2f29a54673030b0c9c66cdeb79f", + proofs: ["e7fbcdadb66fa5561d1891185f8c16fb80e4fdd04d5fbbb7f49d66426a699b18"], + merkleRoot: "ac50e4ffdca1256e5771ecc3f266f5f72d1c9f3686b387811628ad0c28f16836", + salts: + "W3sidmFsdWUiOiJkMjQxZjc3NWNkYjNjOWRhNjI4MjU5M2ViYmY2NGMwZDkxMzM3ZTY1MmU5MWIyYTQ0NzEzNTY1NjdiN2NjMzI5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImQ3NDMxZWIwMWZmYTE0MmEyMThkMjc3OWE3YjY2MGJjZTYxMzRkMTQ3NTUyNjA3MDgwMjc3OGM4OGYyNzczNTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZWZmOWI2ZWFmNzg2OWM1OTgxNjM4NTIyYjIxNjc5NDQ4YWIxNmQyM2YzZjZlZjI0MWY5ZWM3M2ZiYjE3YWU2OCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjlhZjY3MWYyYTUyZTIxMTk5YzU1MDE2ZTlkNzNlN2E2YTBiNmEzMjI2Yzg3ODIyMzlmNDhjZWM3MjZlZjE4YWMiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIwOTYzNDFkMDA1OTU1ZDg2Y2IwZjVjOTJmMGFkMWU5NWIxOWJiZmRhMzk2NmYyNTJiMGUzZGY0MGFmMzYwZjBjIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNjY2MDI2YzkyMjYyMzBlNGEwNzliNWMyZmY0ZWMzMzgxMzM1YzI5NTcwOTZjMzFjZWVjN2ViMmM3OTY5NTA0ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZWE2ZmRjYzc2YjI0OTBkNWE0ODg3ZWFiMTJlM2M4Mjc1YzdkMmM3ZDcwYzdhZmRhNWZlNjVhOWYwNzQwYzY0MSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJlYzhiY2ZlYWFkZDk0NjAyZmU0ZGZkMjBiMzgwNGE1M2ZhZmRkOTI4ZjA0NTQ4NzkxZTg5NzNiYzc4MDI5NTM3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImI1YzdmODYxOGQ0ZjgwNGI2MmQxYjkyY2UwMTJlOGRjOWVjOTY5M2U5NGEzY2Q2YWJkOTBlZDNkZjBiOWVmNDUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNzRjNjgzZjhkNmY4YzAzNDNmMTM1MjVmN2FmMWNjY2RjZmVhNTlkNWYxYjRhOWVkOTRiYmU1NzE4ODkxMWQwZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImE1ZTg4MmU2ZGY0MjdkM2NlODBiY2MyZjNkYzI5NmU1MGU4MTZhMGU0ZTEyMDZlYzgwNGNhNzQzNDY3NjVjYmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjU2YTUyYzFiMmE4ZWE4NGQ3NzZhZDBhYjMzODM3ZDdkZGEwNGZiODYwODJmMzU0NGEwNzM4YTVjZjBlYTYwMDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZjFiZjI4MDFhNzcxNjZkNzZkZDJhZjFmZTM2NGZmYWQzNGViYmNiMmRjZTVkODc4ZjZiYWE1NjJhMGMxNGY2NCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImFmMTA3NjBiYjIzOTBmNmJkYjFiODgwNjViNTgzZTVkZTJkMWEzNDczNTFjMDYwMGU5MDRkZWFlNDA1MTA0MWQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlNzRhOTM3MTg3ZDY1NDQ3MWYxMzMxZWU4MTcxYjE2OWQ3NTgxMWZmMWRhNzNhYzNiOWQ4ZWNmNzUwODk5M2VkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjhlNTE5ZWEzNjQwNmY2YTdkOGNmMzVhMTdmYmI2YTVkMTNkOWU3YThlMjJkMzNmZmMwOGNhN2U1ZGZmZmZjNGMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMDgyMTg5MmRjZmUyNTc0YTc3MmVjYzc1OTc5YzE2NDBhZjg1MDBkYjU4NzAxMTlmMGQyZmE0NjMyNDYxNmM5MyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjgwYzM4OTg5MTY0Y2ExMTRiYjMyNjJmNDFkOWRiYjU2MjdlNTFhMjlhMmFlOWJmY2I5ZjM0M2M0NTc3OWRjMzAiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImVkNTZhZTMxMGZlZDlkN2E4ZjBhOGZiNzhlNzY4MWJlYzkyNzBkYzYyMGIwOGYzZDU3ODI0MTZmYmVkNzA3YWEiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiZTI4MTdhZGFlOWNmMzgzYTE1OWNkNmE0ZjQ1YjY2NjZhNTVmNzQ5ZTRmYzY0NzQ2NDcxM2RlZjQ4N2UwYWExOSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + privacy: { + obfuscated: [], + }, + }, + }, +] satisfies V4WrappedDocument[]); + +// Freeze fixture to prevent accidental changes during tests +function freezeObject(obj: T): T { + return deepFreeze(obj) as T; +} + +function deepFreeze(obj: unknown) { + if (obj && typeof obj === "object" && !Object.isFrozen(obj)) { + Object.freeze(obj); + Object.getOwnPropertyNames(obj).forEach((prop) => deepFreeze(obj[prop as keyof typeof obj])); + } + return obj; +} From b8530ff7c91e4bc23ac19f2ec37e5b03780eb586 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sun, 28 Apr 2024 14:20:40 +0800 Subject: [PATCH 073/107] fix: obfuscate typing was loses the initial input typing --- src/4.0/obfuscate.ts | 10 +++++----- src/index.ts | 5 +---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 56456869..2607fad8 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -2,7 +2,7 @@ import { toBuffer } from "../shared/utils"; import { cloneDeep, get, unset, pick } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; -import { V4Document, V4WrappedDocument } from "./types"; +import { V4WrappedDocument } from "./types"; const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data @@ -36,10 +36,10 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { }; }; -export const obfuscateVerifiableCredential = ( - document: V4WrappedDocument, +export const obfuscateVerifiableCredential = ( + document: T, fields: string[] | string -): V4WrappedDocument => { +): T => { const { data, obfuscatedData } = obfuscate(document, fields); const currentObfuscatedData = document.proof.privacy.obfuscated; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); @@ -52,5 +52,5 @@ export const obfuscateVerifiableCredential = obfuscated: newObfuscatedData, }, }, - } satisfies V4WrappedDocument as V4WrappedDocument; + } satisfies V4WrappedDocument as T; }; diff --git a/src/index.ts b/src/index.ts index abcd820d..6a559916 100644 --- a/src/index.ts +++ b/src/index.ts @@ -108,10 +108,7 @@ export function obfuscate( document: WrappedDocument, fields: string[] | string ): WrappedDocument; -export function obfuscate( - document: WrappedDocument, - fields: string[] | string -): WrappedDocument; +export function obfuscate(document: T, fields: string[] | string): T; export function obfuscate(document: WrappedDocument, fields: string[] | string) { if (utils.isWrappedV2Document(document)) return obfuscateDocumentV2(document, fields); else if (utils.isWrappedV3Document(document)) return obfuscateVerifiableCredentialV3(document, fields); From c484c7ca0e8a7c37493d512077a045203c172cf2 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sun, 28 Apr 2024 14:59:17 +0800 Subject: [PATCH 074/107] fix: should not allow obfuscate to produce non compliant v4 wrapped document --- src/4.0/obfuscate.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 2607fad8..3c453bfe 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -43,7 +43,9 @@ export const obfuscateVerifiableCredential = ( const { data, obfuscatedData } = obfuscate(document, fields); const currentObfuscatedData = document.proof.privacy.obfuscated; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); - return { + + // assert that obfuscated is still compliant to our schema + const parsedResults = V4WrappedDocument.safeParse({ ...data, proof: { ...data.proof, @@ -52,5 +54,20 @@ export const obfuscateVerifiableCredential = ( obfuscated: newObfuscatedData, }, }, - } satisfies V4WrappedDocument as T; + }); + if (!parsedResults.success) { + const paths = parsedResults.error.errors.map(({ path }) => path.join(".")); + throw new ObfuscatedInvalidPaths(paths); + } + return parsedResults.data as T; }; + +export class ObfuscatedInvalidPaths extends Error { + constructor(public paths: string[]) { + super( + `The resultant obfuscated document is not V4 Wrapped Document compliant, please ensure that the following path(s) are not obfuscated: ${paths + .map((val) => `"${val}"`) + .join(", ")}` + ); + } +} From 2ed39fc6f4116eb53e6492f02e2a6d08118ff540 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Sun, 28 Apr 2024 15:13:35 +0800 Subject: [PATCH 075/107] fix: partially fix obfuscate test --- src/4.0/__tests__/obfuscate.test.ts | 266 ++++++++++++++++++++++++ src/4.0/__tests__/obfuscate.test.wip | 291 --------------------------- 2 files changed, 266 insertions(+), 291 deletions(-) create mode 100644 src/4.0/__tests__/obfuscate.test.ts delete mode 100644 src/4.0/__tests__/obfuscate.test.wip diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts new file mode 100644 index 00000000..1a73152c --- /dev/null +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -0,0 +1,266 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { obfuscateVerifiableCredential } from "../obfuscate"; +import { get } from "lodash"; +import { decodeSalt } from "../salt"; +import { SchemaId } from "../../shared/@types/document"; +import { toBuffer, isObfuscated, getObfuscatedData } from "../../shared/utils"; +import { wrapDocument } from "../wrap"; +import { Salt, V4Document, V4WrappedDocument } from "../types"; +import { verifySignature } from "../../"; + +const makeV4RawDocument = (credentialSubject: T) => + ({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + type: ["VerifiableCredential", "OpenAttestationCredential"], + validFrom: "2021-03-08T12:00:00+08:00", + name: "Republic of Singapore Driving Licence", + issuer: { + id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + credentialSubject: credentialSubject as T, + } satisfies V4Document); + +const findSaltByPath = (salts: string, path: string): Salt | undefined => { + return decodeSalt(salts).find((salt) => salt.path === path); +}; + +/** + * /!\ This method doesn't work with array like notation + * This method will ensure + * - the field has been added to the obfuscated array + * - the salt bound to the field has been removed + * - the field has been removed + */ +const expectRemovedFieldsWithoutArrayNotation = ( + field: string, + document: V4WrappedDocument, + obfuscatedDocument: V4WrappedDocument +) => { + const value = get(document, field); + const salt = findSaltByPath(document.proof.salts, field); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); + expect(obfuscatedDocument).not.toHaveProperty(field); +}; + +describe("privacy", () => { + describe("obfuscateDocument", () => { + test("removes one field from the root object", async () => { + const PATH_TO_REMOVE = "name"; + const rawDocument = makeV4RawDocument({ id: "S1234567A", name: "John Doe" }); + const wrappedDocument = await wrapDocument(rawDocument); + const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE); + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + expectRemovedFieldsWithoutArrayNotation(PATH_TO_REMOVE, wrappedDocument, obfuscatedDocument); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); + }); + + test("removes paths that result in an invalid wrapped document, should throw", async () => { + const PATHS_TO_REMOVE = ["credentialSubject.id", "credentialSubject.name", "renderMethod.0.id", "name"]; + const rawDocument = makeV4RawDocument({ id: "S1234567A", name: "John Doe" }); + const wrappedDocument = await wrapDocument(rawDocument); + expect(() => obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE)).toThrowError( + `"credentialSubject", "renderMethod.0.id"` + ); + }); + + test("removes one key of an object from an array", async () => { + const PATH_TO_REMOVE = "credentialSubject.arrayOfObject[0].foo" as const; + const rawDocument = makeV4RawDocument({ + id: "did:example:ebfeb1f712ebc6f1c276e12ec21", + alumniOf: "Example University", + array: ["one", "two", "three", "four"], + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }); + const newDocument = await wrapDocument(rawDocument); + const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, PATH_TO_REMOVE); + + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + const value = get(newDocument, PATH_TO_REMOVE); + const salt = findSaltByPath(newDocument.proof.salts, PATH_TO_REMOVE); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [PATH_TO_REMOVE]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, PATH_TO_REMOVE)).toBeUndefined(); + expect(obfuscatedDocument.credentialSubject.arrayOfObject[0]).toStrictEqual({ doo: "foo" }); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); + }); + + test("removes one object from an array", async () => { + const field = "credentialSubject.arrayOfObject[0]"; + const PATHS_TO_REMOVE = ["credentialSubject.arrayOfObject[0].foo", "credentialSubject.arrayOfObject[0].doo"]; + const rawDocument = makeV4RawDocument({ + id: "did:example:ebfeb1f712ebc6f1c276e12ec21", + alumniOf: "Example University", + array: ["one", "two", "three", "four"], + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }); + const wrappedDocument = await wrapDocument(rawDocument); + const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE); + + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + PATHS_TO_REMOVE.forEach((field) => { + const value = get(wrappedDocument, field); + const salt = findSaltByPath(wrappedDocument.proof.salts, field); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); + }); + expect(obfuscatedDocument.credentialSubject.arrayOfObject[0]).toBeUndefined(); + expect(obfuscatedDocument.credentialSubject.arrayOfObject[1]).not.toBeUndefined(); // let's make sure only the first item has been removed + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); + }); + // test("removes an array of object", async () => { + // const field = "attachments"; + // const expectedFieldsToBeRemoved = [ + // "attachments[0].mimeType", + // "attachments[0].fileName", + // "attachments[0].data", + // "attachments[1].mimeType", + // "attachments[1].fileName", + // "attachments[1].data", + // ]; + // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); + // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); + + // const verified = verifySignature(obfuscatedDocument); + // expect(verified).toBe(true); + + // expectedFieldsToBeRemoved.forEach((field) => { + // const value = get(newDocument, field); + // const salt = findSaltByPath(newDocument.proof.salts, field); + + // expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + // toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") + // ); + // expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); + // }); + // expect(obfuscatedDocument.attachments).toBeUndefined(); + // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(6); + // }); + + // test("removes multiple fields", async () => { + // const fields = ["key1", "key2"]; + // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); + // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); + // const verified = verifySignature(obfuscatedDocument); + // expect(verified).toBe(true); + + // fields.forEach((field) => { + // expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); + // }); + // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); + // }); + + // test("removes values from nested object", async () => { + // const field = "credentialSubject.alumniOf"; + // const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); + // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); + // const verified = verifySignature(obfuscatedDocument); + // expect(verified).toBe(true); + + // expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); + // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); + // }); + + // test("removes values from arrays", async () => { + // const fields = ["credentialSubject.array[2]", "credentialSubject.array[3]"]; + // const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); + // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); + // const verified = verifySignature(obfuscatedDocument); + // expect(verified).toBe(true); + + // const salts = decodeSalt(newDocument.proof.salts); + // const salt1 = salts.find((s) => s.path === fields[0]); + // const value1 = get(newDocument, fields[0]); + // const salt2 = salts.find((s) => s.path === fields[1]); + // const value2 = get(newDocument, fields[1]); + + // expect(obfuscatedDocument.proof.privacy.obfuscated).toEqual([ + // toBuffer({ [fields[0]]: `${salt1?.value}:${value1}` }).toString("hex"), + // toBuffer({ [fields[1]]: `${salt2?.value}:${value2}` }).toString("hex"), + // ]); + // expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[0])).toBeUndefined(); + // expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[1])).toBeUndefined(); + // // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // // @ts-ignore not typable + // expect(obfuscatedDocument.credentialSubject.array).not.toContain("three"); + // // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // // @ts-ignore not typable + // expect(obfuscatedDocument.credentialSubject.array).not.toContain("four"); + // }); + + // test("is transitive", async () => { + // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); + // const intermediateDoc = obfuscateVerifiableCredential(newDocument, "key1"); + // const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); + // const finalDoc2 = obfuscateVerifiableCredential(newDocument, ["key1", "key2"]); + + // expect(finalDoc1).toEqual(finalDoc2); + // expect(intermediateDoc).not.toHaveProperty("key1"); + // expect(finalDoc1).not.toHaveProperty("key1"); + // expect(finalDoc1).not.toHaveProperty("key2"); + // expect(finalDoc2).not.toHaveProperty("key1"); + // expect(finalDoc2).not.toHaveProperty("key2"); + // }); + }); + + // describe("getObfuscated", () => { + // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; + // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; + + // test("should return empty array when there is no obfuscated data in document v3", () => { + // expect(getObfuscatedData(documentNotObfuscatedV3)).toHaveLength(0); + // }); + + // test("should return array of hashes when there is obfuscated data in document v3", () => { + // const obfuscatedData = getObfuscatedData(documentObfuscatedV3); + // expect(obfuscatedData.length).toBe(1); + // expect(obfuscatedData?.[0]).toBe("e411260249d681968bdde76246350f7ca1c9bf1fae59b6bbf147692961b12e26"); + // }); + // }); + + // describe("isObfuscated", () => { + // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; + // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; + + // test("should return false when there is no obfuscated data in document v3", () => { + // expect(isObfuscated(documentNotObfuscatedV3)).toBe(false); + // }); + + // test("should return true where there is obfuscated data in document v3", () => { + // expect(isObfuscated(documentObfuscatedV3)).toBe(true); + // }); + // }); +}); diff --git a/src/4.0/__tests__/obfuscate.test.wip b/src/4.0/__tests__/obfuscate.test.wip deleted file mode 100644 index 91adbbcf..00000000 --- a/src/4.0/__tests__/obfuscate.test.wip +++ /dev/null @@ -1,291 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { obfuscateVerifiableCredential } from "../obfuscate"; -import { __unsafe__use__it__at__your__own__risks__wrapDocument as wrapDocument, verifySignature } from "../.."; -import { Salt, WrappedDocument } from "../../3.0/types"; -import { get } from "lodash"; -import { decodeSalt } from "../salt"; -import { SchemaId } from "../../shared/@types/document"; -import * as v3 from "../../__generated__/schema.3.0"; -import { Method, OpenAttestationDocument, ProofType } from "../../__generated__/schema.3.0"; -import { toBuffer, isObfuscated, getObfuscatedData } from "../../shared/utils"; -import ObfuscatedWrapped from "../../../test/fixtures/v3/obfuscated-wrapped.json"; -import NotObfuscatedWrapped from "../../../test/fixtures/v3/not-obfuscated-wrapped.json"; - -jest.mock("../../3.0/validate"); // Skipping schema verification while wrapping - -const openAttestationData: OpenAttestationDocument = { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - "https://schemata.openattestation.com/com/openattestation/1.0/OpenAttestation.v3.json", - "https://schemata.openattestation.com/com/openattestation/1.0/CustomContext.json", - ], - issuanceDate: "2010-01-01T19:23:24Z", - name: "document owner name", - type: ["VerifiableCredential", "AlumniCredential"], - credentialSubject: { - id: "did:example:ebfeb1f712ebc6f1c276e12ec21", - alumniOf: "Example University", - array: ["one", "two", "three", "four"], - arrayOfObject: [ - { foo: "bar", doo: "foo" }, - { foo: "baz", doo: "faz" }, - ], - }, - issuer: "https://example.edu/issuers/14", - openAttestationMetadata: { - proof: { - type: ProofType.OpenAttestationProofMethod, - value: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - method: Method.TokenRegistry, - }, - identityProof: { - identifier: "some.example", - type: v3.IdentityProofType.DNSTxt, - }, - }, - attachments: [ - { - mimeType: "image/png", - fileName: "aaa", - data: "abcd", - }, - { - mimeType: "image/png", - fileName: "bbb", - data: "abcd", - }, - ], -}; - -const testData = { - key1: "value1", - key2: "value2", - keyObject: { foo: "bar", bar: "dod" }, - ...openAttestationData, -}; - -const findSaltByPath = (salts: string, path: string): Salt | undefined => { - return decodeSalt(salts).find((salt) => salt.path === path); -}; - -/** - * /!\ This method doesn't work with array like notation - * This method will ensure - * - the field has been added to the obfuscated array - * - the salt bound to the field has been removed - * - the field has been removed - */ -const expectRemovedFieldsWithoutArrayNotation = ( - field: string, - document: WrappedDocument, - obfuscatedDocument: WrappedDocument -) => { - const value = get(document, field); - const salt = findSaltByPath(document.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - expect(obfuscatedDocument).not.toHaveProperty(field); -}; - -describe("privacy", () => { - describe("obfuscateDocument", () => { - test("removes one field from the root object", async () => { - const field = "key1"; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - }); - test("removes one object from the root object", async () => { - const field = "keyObject"; - const expectedFieldsToBeRemoved = ["keyObject.foo", "keyObject.bar"]; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectedFieldsToBeRemoved.forEach((field) => { - expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - }); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); - }); - test("removes one key of an object from an array", async () => { - const field = "credentialSubject.arrayOfObject[0].foo"; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - const value = get(newDocument, field); - const salt = findSaltByPath(newDocument.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(obfuscatedDocument.credentialSubject.arrayOfObject![0]).toStrictEqual({ doo: "foo" }); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - }); - test("removes one object from an array", async () => { - const field = "credentialSubject.arrayOfObject[0]"; - const expectedFieldsToBeRemoved = [ - "credentialSubject.arrayOfObject[0].foo", - "credentialSubject.arrayOfObject[0].doo", - ]; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectedFieldsToBeRemoved.forEach((field) => { - const value = get(newDocument, field); - const salt = findSaltByPath(newDocument.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(obfuscatedDocument.credentialSubject.arrayOfObject![0]).toBeUndefined(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(obfuscatedDocument.credentialSubject.arrayOfObject![1]).not.toBeUndefined(); // let's make sure only the first item has been removed - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); - }); - test("removes an array of object", async () => { - const field = "attachments"; - const expectedFieldsToBeRemoved = [ - "attachments[0].mimeType", - "attachments[0].fileName", - "attachments[0].data", - "attachments[1].mimeType", - "attachments[1].fileName", - "attachments[1].data", - ]; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectedFieldsToBeRemoved.forEach((field) => { - const value = get(newDocument, field); - const salt = findSaltByPath(newDocument.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - }); - expect(obfuscatedDocument.attachments).toBeUndefined(); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(6); - }); - - test("removes multiple fields", async () => { - const fields = ["key1", "key2"]; - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - fields.forEach((field) => { - expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - }); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); - }); - - test("removes values from nested object", async () => { - const field = "credentialSubject.alumniOf"; - const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - }); - - test("removes values from arrays", async () => { - const fields = ["credentialSubject.array[2]", "credentialSubject.array[3]"]; - const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - const salts = decodeSalt(newDocument.proof.salts); - const salt1 = salts.find((s) => s.path === fields[0]); - const value1 = get(newDocument, fields[0]); - const salt2 = salts.find((s) => s.path === fields[1]); - const value2 = get(newDocument, fields[1]); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toEqual([ - toBuffer({ [fields[0]]: `${salt1?.value}:${value1}` }).toString("hex"), - toBuffer({ [fields[1]]: `${salt2?.value}:${value2}` }).toString("hex"), - ]); - expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[0])).toBeUndefined(); - expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[1])).toBeUndefined(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore not typable - expect(obfuscatedDocument.credentialSubject.array).not.toContain("three"); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore not typable - expect(obfuscatedDocument.credentialSubject.array).not.toContain("four"); - }); - - test("is transitive", async () => { - const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - const intermediateDoc = obfuscateVerifiableCredential(newDocument, "key1"); - const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); - const finalDoc2 = obfuscateVerifiableCredential(newDocument, ["key1", "key2"]); - - expect(finalDoc1).toEqual(finalDoc2); - expect(intermediateDoc).not.toHaveProperty("key1"); - expect(finalDoc1).not.toHaveProperty("key1"); - expect(finalDoc1).not.toHaveProperty("key2"); - expect(finalDoc2).not.toHaveProperty("key1"); - expect(finalDoc2).not.toHaveProperty("key2"); - }); - }); - - describe("getObfuscated", () => { - const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - test("should return empty array when there is no obfuscated data in document v3", () => { - expect(getObfuscatedData(documentNotObfuscatedV3)).toHaveLength(0); - }); - - test("should return array of hashes when there is obfuscated data in document v3", () => { - const obfuscatedData = getObfuscatedData(documentObfuscatedV3); - expect(obfuscatedData.length).toBe(1); - expect(obfuscatedData?.[0]).toBe("e411260249d681968bdde76246350f7ca1c9bf1fae59b6bbf147692961b12e26"); - }); - }); - - describe("isObfuscated", () => { - const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - test("should return false when there is no obfuscated data in document v3", () => { - expect(isObfuscated(documentNotObfuscatedV3)).toBe(false); - }); - - test("should return true where there is obfuscated data in document v3", () => { - expect(isObfuscated(documentObfuscatedV3)).toBe(true); - }); - }); -}); From bc2bdc079d0fbbe8a658d86f39e9c24c6f7d1931 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 00:41:56 +0800 Subject: [PATCH 076/107] fix: it is more accurate that credentials subject after obfuscastion shd be deeply partial --- src/4.0/obfuscate.ts | 12 ++++++++---- src/4.0/types.ts | 20 +++++++++++++++++++- src/index.ts | 10 ++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 3c453bfe..76b89844 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -2,7 +2,7 @@ import { toBuffer } from "../shared/utils"; import { cloneDeep, get, unset, pick } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; -import { V4WrappedDocument } from "./types"; +import { Override, PartialDeep, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data @@ -36,10 +36,14 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { }; }; -export const obfuscateVerifiableCredential = ( +export type ObfuscateVerifiableCredentialResult = Override< + T extends V4SignedWrappedDocument ? V4SignedWrappedDocument : V4WrappedDocument, + Required>> +>; +export const obfuscateVerifiableCredential = ( document: T, fields: string[] | string -): T => { +): ObfuscateVerifiableCredentialResult => { const { data, obfuscatedData } = obfuscate(document, fields); const currentObfuscatedData = document.proof.privacy.obfuscated; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); @@ -59,7 +63,7 @@ export const obfuscateVerifiableCredential = ( const paths = parsedResults.error.errors.map(({ path }) => path.join(".")); throw new ObfuscatedInvalidPaths(paths); } - return parsedResults.data as T; + return parsedResults.data as ObfuscateVerifiableCredentialResult; }; export class ObfuscatedInvalidPaths extends Error { diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 8ed7eaed..a6948f14 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -251,7 +251,7 @@ type IdentityProofType = z.infer; export type Salt = z.infer; /** Overrides properties in the Target (a & b does not override a props with b props) */ -type Override, OverrideWith extends Record> = Omit< +export type Override, OverrideWith extends Record> = Omit< Target, keyof OverrideWith > & @@ -263,3 +263,21 @@ type AssertStricterOrEqual = StricterType extends Loos // eslint-disable-next-line @typescript-eslint/no-unused-vars export type NoExtraProperties = NewObj extends Reference & infer _ExtraProps ? Reference : NewObj; + +export type PartialDeep = T extends string | number | bigint | boolean | null | undefined | symbol | Date + ? T | undefined + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray + : T extends Set + ? Set> + : T extends ReadonlySet + ? ReadonlySet + : T extends Map + ? Map, PartialDeep> + : T extends ReadonlyMap + ? ReadonlyMap, PartialDeep> + : { + [K in keyof T]?: PartialDeep; + }; diff --git a/src/index.ts b/src/index.ts index 6a559916..451da6f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,7 +29,10 @@ import * as v4 from "./4.0/types"; import { signDocument as signDocumentV4 } from "./4.0/sign"; import { verify as verifyV4 } from "./4.0/verify"; import { digestCredential as digestCredentialV4 } from "./4.0/digest"; -import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV4 } from "./4.0/obfuscate"; +import { + ObfuscateVerifiableCredentialResult, + obfuscateVerifiableCredential as obfuscateVerifiableCredentialV4, +} from "./4.0/obfuscate"; import { v4Diagnose } from "./4.0/diagnose"; export function wrapDocument( @@ -108,7 +111,10 @@ export function obfuscate( document: WrappedDocument, fields: string[] | string ): WrappedDocument; -export function obfuscate(document: T, fields: string[] | string): T; +export function obfuscate( + document: T, + fields: string[] | string +): ObfuscateVerifiableCredentialResult; export function obfuscate(document: WrappedDocument, fields: string[] | string) { if (utils.isWrappedV2Document(document)) return obfuscateDocumentV2(document, fields); else if (utils.isWrappedV3Document(document)) return obfuscateVerifiableCredentialV3(document, fields); From ac4ca375a61fe24fe843a9b9a868e19bf82a3087 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 09:37:55 +0800 Subject: [PATCH 077/107] fix: clean up digest test --- src/4.0/__tests__/digest.test.ts | 234 ++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 80 deletions(-) diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 7e41cd70..3cf71e5c 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -1,101 +1,175 @@ -import { cloneDeep } from "lodash"; import { digestCredential } from "../digest"; -import { obfuscateVerifiableCredential } from "../obfuscate"; import { decodeSalt } from "../salt"; -import { SIGNED_WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { SIGNED_WRAPPED_DOCUMENT_DID as ROOT_CREDENTIAL } from "../fixtures"; +import { V4SignedWrappedDocument } from "../types"; +import { obfuscateVerifiableCredential } from "../obfuscate"; -// Digest will change whenever sample document is regenerated -const credentialRoot = "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"; +// All obfuscated documents are generated from the ROOT_CREDENTIAL +const ROOT_CREDENTIAL_TARGET_HASH = ROOT_CREDENTIAL.proof.targetHash; -const { proof, ...credential } = SIGNED_WRAPPED_DOCUMENT_DID; +describe("V4 digestCredential", () => { + test("given all testobfuscated documents are generated from the ROOT_CREDENTIAL, ROOT_CREDENTIAL_TARGET_HASH should match snapshot", () => { + expect(ROOT_CREDENTIAL_TARGET_HASH).toMatchInlineSnapshot( + `"f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"` + ); + }); -describe("V4.0 digest", () => { - describe("digestCredential", () => { - test("digests a document with all visible content correctly", () => { - const clonedCredential = cloneDeep(credential); + test("given a document with ALL FIELDS VISIBLE, should digest and match the root credential's target hash", () => { + expect(ROOT_CREDENTIAL.credentialSubject).toMatchInlineSnapshot(` + { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + ], + "name": "John Doe", + "type": [ + "DriversLicense", + ], + } + `); + expect(ROOT_CREDENTIAL.proof.privacy.obfuscated).toMatchInlineSnapshot(`[]`); - const digest = digestCredential(clonedCredential, decodeSalt(proof.salts), []); - expect(digest).toBe(credentialRoot); - }); - test("digests a document when one single element is obfuscated", () => { - const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(SIGNED_WRAPPED_DOCUMENT_DID, "issuer.id"); - const digest = digestCredential( - obfuscatedVerifiableCredential, - decodeSalt(obfuscatedVerifiableCredential.proof.salts), - obfuscatedVerifiableCredential.proof.privacy.obfuscated - ); + const digest = digestCredential(ROOT_CREDENTIAL, decodeSalt(ROOT_CREDENTIAL.proof.salts), []); + expect(digest).toBe(ROOT_CREDENTIAL_TARGET_HASH); + }); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` - [ - "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", - ] + test("given a document with ONE element obfuscated, should digest and match the root credential's target hash", () => { + const OBFUSCATED_WRAPPED_DOCUMENT = obfuscateVerifiableCredential(ROOT_CREDENTIAL, "credentialSubject.id"); + expect(OBFUSCATED_WRAPPED_DOCUMENT.credentialSubject).toMatchInlineSnapshot(` + { + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + ], + "name": "John Doe", + "type": [ + "DriversLicense", + ], + } `); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(1); - expect(digest).toBe(credentialRoot); - }); - test("digests a document when multiple element are obfuscated", () => { - const obfuscatedVerifiableCredential = obfuscateVerifiableCredential(SIGNED_WRAPPED_DOCUMENT_DID, [ - "credentialSubject.id", - "credentialSubject.name", - "credentialSubject.licenses.0.description", - ]); - const digest = digestCredential( - obfuscatedVerifiableCredential, - decodeSalt(obfuscatedVerifiableCredential.proof.salts), - obfuscatedVerifiableCredential.proof.privacy.obfuscated - ); - - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` + expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", - "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", - "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", ] `); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(3); - expect(digest).toBe(credentialRoot); - }); - test("digests a document with no visible content correctly", () => { - const obfuscatedVerifiableCredential = obfuscateVerifiableCredential( - SIGNED_WRAPPED_DOCUMENT_DID, - Object.keys(SIGNED_WRAPPED_DOCUMENT_DID).filter((k) => k != "proof") - ); - const digest = digestCredential( - obfuscatedVerifiableCredential, - decodeSalt(obfuscatedVerifiableCredential.proof.salts), - obfuscatedVerifiableCredential.proof.privacy.obfuscated - ); - expect(obfuscatedVerifiableCredential).toStrictEqual({ proof: obfuscatedVerifiableCredential.proof }); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toMatchInlineSnapshot(` + const digest = digestCredential( + OBFUSCATED_WRAPPED_DOCUMENT, + decodeSalt(OBFUSCATED_WRAPPED_DOCUMENT.proof.salts), + OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated + ); + expect(digest).toBe(ROOT_CREDENTIAL_TARGET_HASH); + expect(digest).toBe(OBFUSCATED_WRAPPED_DOCUMENT.proof.targetHash); + }); + + test("given a document with THREE elements obfuscated, should digest and match the root credential's target hash", () => { + const OBFUSCATED_WRAPPED_DOCUMENT = obfuscateVerifiableCredential(ROOT_CREDENTIAL, [ + "credentialSubject.id", + "credentialSubject.name", + "credentialSubject.licenses[0].description", + ]); + expect(OBFUSCATED_WRAPPED_DOCUMENT.credentialSubject).toMatchInlineSnapshot(` + { + "licenses": [ + { + "class": "3", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00", + }, + ], + "type": [ + "DriversLicense", + ], + } + `); + expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "a0b3d5c67c33a0dd28b29affc83c45c07bb4c5f43e35ae2b17d7185832372b32", - "e2571635cde03fc4397336176118f7dcad6443d033ff30c0bb20dad6a0b7dc64", - "f1262f889f833ac33db6ad98baa99697a76d2c5b78e8c16de71d70cf4ce9f11e", - "d3e53913d1d0fe997f2a941b38ddc0f7481a737f0958454298a983b622476ae6", - "a9c59e18346b177425016a4dbac19ff521ee2547bc4ae78b6fffda374224985c", - "4322f850d5f17e8db8738e8fcd1901b43d053eb4086fcc747ddb670eec26e1c5", - "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", - "2079505ff19eecf40373b4c8a97858340538c32dd9998b013a75b00591863a86", - "6657f68f32ede3f1f0c23fad3c14ec2150caf127320bb681ac882bbf088314e4", - "da0129d424dfd61470cfbc6db2d0a351456c1347ff8a866f64e773534f50dbc9", - "cdc6780c98d21cf1c2a512c69424901ebc66c657c4364f573dbc77b8f99f1e6e", - "6f8dea4950d0b99674507e2a362887b6fbb5a661d395ab5e0f10b7902369fd57", - "0e8cfba3a420807f116410fd8bcf3f8147133191c17bf00315cafae1ec7231b3", - "c9fa76bd112c9dd65afd38899e58583d39879abb7962368eb91ca9217d49e8fc", "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", - "0b40ea9dcddca6d570191b3b5bee01dbe4d53047e45f39f496ee4f77baabd2e7", "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", - "5db7da35fe78cde5f216efca8446fa84762c346c8eb5685c9086841ba8899ea3", "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", - "6fd29224f74d7b53d5501fb8f111a9cb142d9481367cdec84db6b0919aa60ed8", - "5ea9e34d7e03a436d6c50f7e2af34ee90f1cda674b3dca270228a6041c32430b", - "8ab3fdec4be9c61ca9d0b570896796be343287a9fc92262aadc5a2b1f400ad6a", - "b298f1096b443e1441a8e0f62eef873a45fbbbb6e9bbfd1691106224613cd61b", ] `); - expect(obfuscatedVerifiableCredential.proof.privacy.obfuscated).toHaveLength(23); - expect(digest).toBe(credentialRoot); - }); + + const digest = digestCredential( + OBFUSCATED_WRAPPED_DOCUMENT, + decodeSalt(OBFUSCATED_WRAPPED_DOCUMENT.proof.salts), + OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated + ); + expect(digest).toBe(ROOT_CREDENTIAL_TARGET_HASH); + expect(digest).toBe(OBFUSCATED_WRAPPED_DOCUMENT.proof.targetHash); + }); + + test("given a document with NO VISIBLE FIELDS, should digest and match the root credential's target hash", () => { + // this has to be manually generated, since obfuscateVerifiableCredential does not allow obfuscating fields that + // result in a non compliant V4 OA document + const OBFUSCATED_WRAPPED_DOCUMENT = { + // no visible fields + proof: { + key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", + merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + privacy: { + obfuscated: [ + "a0b3d5c67c33a0dd28b29affc83c45c07bb4c5f43e35ae2b17d7185832372b32", + "e2571635cde03fc4397336176118f7dcad6443d033ff30c0bb20dad6a0b7dc64", + "f1262f889f833ac33db6ad98baa99697a76d2c5b78e8c16de71d70cf4ce9f11e", + "d3e53913d1d0fe997f2a941b38ddc0f7481a737f0958454298a983b622476ae6", + "a9c59e18346b177425016a4dbac19ff521ee2547bc4ae78b6fffda374224985c", + "4322f850d5f17e8db8738e8fcd1901b43d053eb4086fcc747ddb670eec26e1c5", + "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", + "2079505ff19eecf40373b4c8a97858340538c32dd9998b013a75b00591863a86", + "6657f68f32ede3f1f0c23fad3c14ec2150caf127320bb681ac882bbf088314e4", + "da0129d424dfd61470cfbc6db2d0a351456c1347ff8a866f64e773534f50dbc9", + "cdc6780c98d21cf1c2a512c69424901ebc66c657c4364f573dbc77b8f99f1e6e", + "6f8dea4950d0b99674507e2a362887b6fbb5a661d395ab5e0f10b7902369fd57", + "0e8cfba3a420807f116410fd8bcf3f8147133191c17bf00315cafae1ec7231b3", + "c9fa76bd112c9dd65afd38899e58583d39879abb7962368eb91ca9217d49e8fc", + "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", + "0b40ea9dcddca6d570191b3b5bee01dbe4d53047e45f39f496ee4f77baabd2e7", + "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", + "5db7da35fe78cde5f216efca8446fa84762c346c8eb5685c9086841ba8899ea3", + "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", + "6fd29224f74d7b53d5501fb8f111a9cb142d9481367cdec84db6b0919aa60ed8", + "5ea9e34d7e03a436d6c50f7e2af34ee90f1cda674b3dca270228a6041c32430b", + "8ab3fdec4be9c61ca9d0b570896796be343287a9fc92262aadc5a2b1f400ad6a", + "b298f1096b443e1441a8e0f62eef873a45fbbbb6e9bbfd1691106224613cd61b", + ], + }, + proofPurpose: "assertionMethod", + proofs: [], + salts: "W10=", + signature: + "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", + targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + type: "OpenAttestationMerkleProofSignature2018", + }, + } as unknown as V4SignedWrappedDocument; + + const digest = digestCredential( + OBFUSCATED_WRAPPED_DOCUMENT, + decodeSalt(OBFUSCATED_WRAPPED_DOCUMENT.proof.salts), + OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated + ); + expect(digest).toBe(ROOT_CREDENTIAL_TARGET_HASH); }); }); From 6c7fa736c0906462b39c9a088b302f9626f26b3f Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 12:57:56 +0800 Subject: [PATCH 078/107] feat: support attachments --- src/4.0/types.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index a6948f14..50cf7b0d 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -211,6 +211,17 @@ export const V4Document = W3cVerifiableCredential.extend({ ]) ) .optional(), + + // [Optional] Attachments + attachments: z + .array( + z.object({ + data: z.string().describe("Base64 encoding of this attachment"), + fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), + mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), + }) + ) + .optional(), }).strict(); const WrappedProof = z.object({ From 90ac57ca9da95a5ee41bb2abc74c07187d1febc5 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 13:07:07 +0800 Subject: [PATCH 079/107] fix: digestMultibase shd be optional, added descriptions to fields --- src/4.0/types.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 50cf7b0d..977219ee 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -196,17 +196,26 @@ export const V4Document = W3cVerifiableCredential.extend({ /* OA Decentralised Embedded Renderer */ z.object({ // Must have id match url pattern - id: z.string().url(), + id: z.string().url().describe("URL of a decentralised renderer to render this document"), type: z.literal("OpenAttestationEmbeddedRenderer"), templateName: z.string(), }), /* SVG Renderer (URL or Embedded) */ z.object({ // Must have id match url pattern or embeded SVG string - id: z.union([z.string(), z.string().url()]), + id: z + .union([z.string(), z.string().url()]) + .describe( + "A URL that dereferences to an SVG image [SVG] with an associated image/svg+xml media type. Or an embedded SVG image [SVG]" + ), type: z.literal("SvgRenderingTemplate2023"), name: z.string(), - digestMultibase: z.string(), + digestMultibase: z + .string() + .describe( + "An optional multibase-encoded multihash of the SVG image. The multibase value MUST be z and the multihash value MUST be SHA-2 with 256-bits of output (0x12)." + ) + .optional(), }), ]) ) From 7f6dd75db3741178e7eca61a380d858b12248cd6 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 13:57:24 +0800 Subject: [PATCH 080/107] refactor: remove renderMethod from w3c model and force it to be exported in passthrough mode --- src/4.0/types.ts | 168 ++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 82 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 977219ee..42516006 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -1,14 +1,12 @@ import z from "zod"; import { ContextUrl, ContextType } from "../shared/@types/document"; -const BASE_TYPE = "VerifiableCredential" as const; - // Custom URI validation function const URI_REGEX = /^(?=.)(?!https?:\/(?:$|[^/]))(?!https?:\/\/\/)(?!https?:[^/])(?:[a-zA-Z][a-zA-Z\d+-\.]*:(?:(?:\/\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:]*@)?(?:\[(?:(?:(?:[\dA-Fa-f]{1,4}:){6}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|::(?:[\dA-Fa-f]{1,4}:){5}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){4}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,1}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){3}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,2}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:){2}(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,3}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}:(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,4}[\dA-Fa-f]{1,4})?::(?:[\dA-Fa-f]{1,4}:[\dA-Fa-f]{1,4}|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(?:(?:[\dA-Fa-f]{1,4}:){0,5}[\dA-Fa-f]{1,4})?::[\dA-Fa-f]{1,4}|(?:(?:[\dA-Fa-f]{1,4}:){0,6}[\dA-Fa-f]{1,4})?::)|v[\dA-Fa-f]+\.[\w-\.~!\$&'\(\)\*\+,;=:]+)\]|(?:(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:0{0,2}\d|0?[1-9]\d|1\d\d|2[0-4]\d|25[0-5])|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=]{1,255})(?::\d*)?(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)|\/(?:[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)?|[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]+(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*|(?:\/\/\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*(?:\/[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@]*)*)))(?:\?[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*(?=#|$))?(?:#[\w-\.~%\dA-Fa-f!\$&'\(\)\*\+,;=:@\/\?]*)?$/; const Uri = z.string().regex(URI_REGEX, { message: "Invalid URI" }); -export const W3cVerifiableCredential = z.object({ +const _W3cVerifiableCredential = z.object({ "@context": z.union([ z.record(z.any()), z.string(), @@ -25,7 +23,9 @@ export const W3cVerifiableCredential = z.object({ type: z.union([ z.string(), // If array: Must have VerifiableCredential, while remaining items can be any string - z.array(z.string()).refine((types) => types.includes(BASE_TYPE), { message: `Type must include ${BASE_TYPE}` }), + z.array(z.string()).refine((types) => types.includes(ContextType.BaseContext), { + message: `Type must include ${ContextType.BaseContext}`, + }), ]), credentialSchema: z @@ -87,9 +87,6 @@ export const W3cVerifiableCredential = z.object({ .passthrough() .optional(), - // [Optional] This is at risk of being removed from the w3c spec - renderMethod: z.any().optional(), - termsOfUse: z .union([ // If object: Must have type defined. If id is present, id must match uri pattern (termsOfUse.id is optional and can be undefined) @@ -151,87 +148,91 @@ export const W3cVerifiableCredential = z.object({ ]) .optional(), }); +// W3cVerifiableCredential should always allow extra root properties +export const W3cVerifiableCredential = _W3cVerifiableCredential.passthrough(); const IdentityProofType = z.union([z.literal("DNS-TXT"), z.literal("DNS-DID"), z.literal("DID")]); const Salt = z.object({ value: z.string(), path: z.string() }); -export const V4Document = W3cVerifiableCredential.extend({ - "@context": z - - // Must be an array that starts with [baseContext, v4Context, ...] - .tuple([z.literal(ContextUrl.v2_vc), z.literal(ContextUrl.v4_alpha)]) - // Remaining items can be string or object - .rest(z.union([z.string(), z.record(z.any())])), - - type: z - // Must be an array that starts with [VerifiableCredential, OpenAttestationCredential, ...] - .tuple([z.literal(ContextType.BaseContext), z.literal(ContextType.V4AlphaContext)]) - // Remaining items can be string - .rest(z.string()), - - issuer: z.object({ - // Must have id match uri pattern - id: Uri, - type: z.literal("OpenAttestationIssuer"), - name: z.string(), - identityProof: z.object({ - identityProofType: IdentityProofType, - identifier: z.string(), +export const V4Document = _W3cVerifiableCredential + .extend({ + "@context": z + + // Must be an array that starts with [baseContext, v4Context, ...] + .tuple([z.literal(ContextUrl.v2_vc), z.literal(ContextUrl.v4_alpha)]) + // Remaining items can be string or object + .rest(z.union([z.string(), z.record(z.any())])), + + type: z + // Must be an array that starts with [VerifiableCredential, OpenAttestationCredential, ...] + .tuple([z.literal(ContextType.BaseContext), z.literal(ContextType.V4AlphaContext)]) + // Remaining items can be string + .rest(z.string()), + + issuer: z.object({ + // Must have id match uri pattern + id: Uri, + type: z.literal("OpenAttestationIssuer"), + name: z.string(), + identityProof: z.object({ + identityProofType: IdentityProofType, + identifier: z.string(), + }), }), - }), - - // [Optional] OCSP Revocation - credentialStatus: z - .object({ - // Must have id match url pattern (OCSP endpoint) - id: z.string().url(), - type: z.literal("OpenAttestationOcspResponder"), - }) - .optional(), - // [Optional] Render Method - renderMethod: z - .array( - z.discriminatedUnion("type", [ - /* OA Decentralised Embedded Renderer */ - z.object({ - // Must have id match url pattern - id: z.string().url().describe("URL of a decentralised renderer to render this document"), - type: z.literal("OpenAttestationEmbeddedRenderer"), - templateName: z.string(), - }), - /* SVG Renderer (URL or Embedded) */ - z.object({ - // Must have id match url pattern or embeded SVG string - id: z - .union([z.string(), z.string().url()]) - .describe( - "A URL that dereferences to an SVG image [SVG] with an associated image/svg+xml media type. Or an embedded SVG image [SVG]" - ), - type: z.literal("SvgRenderingTemplate2023"), - name: z.string(), - digestMultibase: z - .string() - .describe( - "An optional multibase-encoded multihash of the SVG image. The multibase value MUST be z and the multihash value MUST be SHA-2 with 256-bits of output (0x12)." - ) - .optional(), - }), - ]) - ) - .optional(), - - // [Optional] Attachments - attachments: z - .array( - z.object({ - data: z.string().describe("Base64 encoding of this attachment"), - fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), - mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), + // [Optional] OCSP Revocation + credentialStatus: z + .object({ + // Must have id match url pattern (OCSP endpoint) + id: z.string().url(), + type: z.literal("OpenAttestationOcspResponder"), }) - ) - .optional(), -}).strict(); + .optional(), + + // [Optional] Render Method + renderMethod: z + .array( + z.discriminatedUnion("type", [ + /* OA Decentralised Embedded Renderer */ + z.object({ + // Must have id match url pattern + id: z.string().url().describe("URL of a decentralised renderer to render this document"), + type: z.literal("OpenAttestationEmbeddedRenderer"), + templateName: z.string(), + }), + /* SVG Renderer (URL or Embedded) */ + z.object({ + // Must have id match url pattern or embeded SVG string + id: z + .union([z.string(), z.string().url()]) + .describe( + "A URL that dereferences to an SVG image [SVG] with an associated image/svg+xml media type. Or an embedded SVG image [SVG]" + ), + type: z.literal("SvgRenderingTemplate2023"), + name: z.string().optional(), + digestMultibase: z + .string() + .describe( + "An optional multibase-encoded multihash of the SVG image. The multibase value MUST be z and the multihash value MUST be SHA-2 with 256-bits of output (0x12)." + ) + .optional(), + }), + ]) + ) + .optional(), + + // [Optional] Attachments + attachments: z + .array( + z.object({ + data: z.string().describe("Base64 encoding of this attachment"), + fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), + mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), + }) + ) + .optional(), + }) + .strict(); const WrappedProof = z.object({ type: z.literal("OpenAttestationMerkleProofSignature2018"), @@ -243,13 +244,15 @@ const WrappedProof = z.object({ privacy: z.object({ obfuscated: z.array(z.string()) }), }); const WrappedDocumentExtrasShape = { proof: WrappedProof.passthrough() } as const; +// V4WrappedDocument should never allow extra root properties export const V4WrappedDocument = V4Document.extend(WrappedDocumentExtrasShape).strict(); const SignedWrappedProof = WrappedProof.extend({ key: z.string(), signature: z.string() }); const SignedWrappedDocumentExtrasShape = { proof: SignedWrappedProof } as const; +// V4SignedWrappedDocument should never allow extra root properties export const V4SignedWrappedDocument = V4Document.extend(SignedWrappedDocumentExtrasShape).strict(); -export type W3cVerifiableCredential = z.infer; +export type W3cVerifiableCredential = z.infer; // AssertStricterOrEqual is used to ensure that we have zod extended from the base type while // still being assignable to the base type. For example, if we accidentally extend and @@ -270,6 +273,7 @@ type IdentityProofType = z.infer; export type Salt = z.infer; +// Utility types /** Overrides properties in the Target (a & b does not override a props with b props) */ export type Override, OverrideWith extends Record> = Omit< Target, From defbb3978c895a20704db1e9968c1bd36dfdbfea Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 13:58:22 +0800 Subject: [PATCH 081/107] chore: improve guard test to use direct output of our fns as test case --- src/4.0/__tests__/guard.test.ts | 60 ++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/4.0/__tests__/guard.test.ts b/src/4.0/__tests__/guard.test.ts index 5ffc4132..65c9b226 100644 --- a/src/4.0/__tests__/guard.test.ts +++ b/src/4.0/__tests__/guard.test.ts @@ -1,7 +1,35 @@ +import { SUPPORTED_SIGNING_ALGORITHM } from "../../shared/@types/sign"; +import { RAW_DOCUMENT_DID } from "../fixtures"; +import { signDocument } from "../sign"; import { W3cVerifiableCredential, V4Document, V4WrappedDocument, V4SignedWrappedDocument } from "../types"; -import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID, WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { wrapDocument } from "../wrap"; + +const RAW_DOCUMENT = { + ...RAW_DOCUMENT_DID, + attachments: [ + { + mimeType: "image/png", + fileName: "aaa", + data: "abcd", + }, + ], +} satisfies V4Document; describe("V4.0 guard", () => { + let WRAPPED_DOCUMENT: V4WrappedDocument; + let SIGNED_WRAPPED_DOCUMENT: V4SignedWrappedDocument; + beforeAll(async () => { + WRAPPED_DOCUMENT = await wrapDocument(RAW_DOCUMENT); + SIGNED_WRAPPED_DOCUMENT = await signDocument( + WRAPPED_DOCUMENT, + SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, + { + public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", + } + ); + }); + describe("given a raw document", () => { test("should pass w3c vc validation without removal of any data", () => { const w3cVerifiableCredential: W3cVerifiableCredential = RAW_DOCUMENT_DID; @@ -53,24 +81,24 @@ describe("V4.0 guard", () => { describe("given a wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: W3cVerifiableCredential = WRAPPED_DOCUMENT_DID; + const w3cVerifiableCredential: W3cVerifiableCredential = WRAPPED_DOCUMENT; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); - expect(results).toEqual(WRAPPED_DOCUMENT_DID); + expect(results).toEqual(WRAPPED_DOCUMENT); }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = WRAPPED_DOCUMENT_DID; + const v4Document: V4Document = WRAPPED_DOCUMENT; const results = V4Document.parse(v4Document); - expect(results).toEqual(WRAPPED_DOCUMENT_DID); + expect(results).toEqual(WRAPPED_DOCUMENT); }); test("should pass wrapped document validation without removal of any data", () => { - const results = V4WrappedDocument.parse(WRAPPED_DOCUMENT_DID); - expect(results).toEqual(WRAPPED_DOCUMENT_DID); + const results = V4WrappedDocument.parse(WRAPPED_DOCUMENT); + expect(results).toEqual(WRAPPED_DOCUMENT); }); test("should fail signed wrapped document validation", () => { - const results = V4SignedWrappedDocument.safeParse(WRAPPED_DOCUMENT_DID); + const results = V4SignedWrappedDocument.safeParse(WRAPPED_DOCUMENT); expect(results.success).toBe(false); expect((results as { error: unknown }).error).toMatchInlineSnapshot(` [ZodError: [ @@ -101,26 +129,26 @@ describe("V4.0 guard", () => { describe("given a signed wrapped document", () => { test("should pass w3c vc validation without removal of any data", () => { - const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED_DOCUMENT_DID; + const w3cVerifiableCredential: W3cVerifiableCredential = SIGNED_WRAPPED_DOCUMENT; const results = W3cVerifiableCredential.parse(w3cVerifiableCredential); - expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT); }); test("should pass document validation without removal of any data", () => { - const v4Document: V4Document = SIGNED_WRAPPED_DOCUMENT_DID; + const v4Document: V4Document = SIGNED_WRAPPED_DOCUMENT; const results = V4Document.parse(v4Document); - expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT); }); test("should pass wrapped document validation without removal of any data", () => { - const v4WrappedDocument: V4WrappedDocument = SIGNED_WRAPPED_DOCUMENT_DID; + const v4WrappedDocument: V4WrappedDocument = SIGNED_WRAPPED_DOCUMENT; const results = V4WrappedDocument.parse(v4WrappedDocument); - expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT); }); test("should pass signed wrapped document validation without removal of any data", () => { - const results = V4SignedWrappedDocument.parse(SIGNED_WRAPPED_DOCUMENT_DID); - expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT_DID); + const results = V4SignedWrappedDocument.parse(SIGNED_WRAPPED_DOCUMENT); + expect(results).toEqual(SIGNED_WRAPPED_DOCUMENT); }); }); }); From 3734c93ced347422f275a52cd7602f7b62ff9ffa Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 29 Apr 2024 14:05:27 +0800 Subject: [PATCH 082/107] fix: allow attachment item to be empty so that obfuscation of a single attachment item is possible --- src/4.0/types.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 42516006..15b615c1 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -224,11 +224,13 @@ export const V4Document = _W3cVerifiableCredential // [Optional] Attachments attachments: z .array( - z.object({ - data: z.string().describe("Base64 encoding of this attachment"), - fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), - mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), - }) + z + .object({ + data: z.string().describe("Base64 encoding of this attachment"), + fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), + mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), + }) + .optional() ) .optional(), }) From 73329078706e131b0236201e44a276809ff9b220 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Tue, 30 Apr 2024 00:31:45 +0800 Subject: [PATCH 083/107] refactor: improve typing --- src/4.0/traverseAndFlatten.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/4.0/traverseAndFlatten.ts b/src/4.0/traverseAndFlatten.ts index d5a855ad..a878eb36 100644 --- a/src/4.0/traverseAndFlatten.ts +++ b/src/4.0/traverseAndFlatten.ts @@ -1,27 +1,33 @@ import { Options } from "@govtechsg/jsonld"; -interface Options { +type LeafValue = string | number | boolean | null; +interface Options { /* function to run on every field */ - iteratee: (data: { value: any; path: string }) => T; + iteratee: (data: { value: LeafValue; path: string }) => IterateeValue; /* root path of the property being acceded */ path?: string; } -export function traverseAndFlatten(data: any[], options: Options): T[]; -export function traverseAndFlatten(data: string | number | boolean | null, options: Options): T; -export function traverseAndFlatten(data: any, options: Options): T[]; // hmmmm this is probably wrong but it works for the moment :) -export function traverseAndFlatten(data: any, { iteratee, path = "" }: Options): any { +/** Given a record | list, returns a list of all leaf nodes of value that is not undefined */ +export function traverseAndFlatten(data: LeafValue, options: Options): IterateeValue; +export function traverseAndFlatten(data: unknown, options: Options): IterateeValue[]; +export function traverseAndFlatten( + data: unknown, + { iteratee, path = "" }: Options +): IterateeValue | IterateeValue[] { if (Array.isArray(data)) { return data.flatMap((v, index) => traverseAndFlatten(v, { iteratee, path: `${path}[${index}]` })); } - // Since null datas are allowed but typeof null === "object", the "&& data" is used to skip this - if (typeof data === "object" && data) { + + if (typeof data === "object" && data !== null) { return Object.keys(data).flatMap((key) => - traverseAndFlatten(data[key], { iteratee, path: path ? `${path}.${key}` : key }) + traverseAndFlatten(data[key as keyof typeof data], { iteratee, path: path ? `${path}.${key}` : key }) ); } + if (typeof data === "string" || typeof data === "number" || typeof data === "boolean" || data === null) { return iteratee({ value: data, path }); } + throw new Error(`Unexpected data '${data}' in '${path}'`); } From f674eb602ef03a4d445ef79e176d4f9437a7a662 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Tue, 30 Apr 2024 00:32:07 +0800 Subject: [PATCH 084/107] fix: partial fix of obfuscate test --- src/4.0/__tests__/obfuscate.test.ts | 266 ----------------------- src/4.0/__tests__/obfuscate.test.wip | 306 +++++++++++++++++++++++++++ 2 files changed, 306 insertions(+), 266 deletions(-) delete mode 100644 src/4.0/__tests__/obfuscate.test.ts create mode 100644 src/4.0/__tests__/obfuscate.test.wip diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts deleted file mode 100644 index 1a73152c..00000000 --- a/src/4.0/__tests__/obfuscate.test.ts +++ /dev/null @@ -1,266 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { obfuscateVerifiableCredential } from "../obfuscate"; -import { get } from "lodash"; -import { decodeSalt } from "../salt"; -import { SchemaId } from "../../shared/@types/document"; -import { toBuffer, isObfuscated, getObfuscatedData } from "../../shared/utils"; -import { wrapDocument } from "../wrap"; -import { Salt, V4Document, V4WrappedDocument } from "../types"; -import { verifySignature } from "../../"; - -const makeV4RawDocument = (credentialSubject: T) => - ({ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", - name: "Republic of Singapore Driving Licence", - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", - }, - ], - credentialSubject: credentialSubject as T, - } satisfies V4Document); - -const findSaltByPath = (salts: string, path: string): Salt | undefined => { - return decodeSalt(salts).find((salt) => salt.path === path); -}; - -/** - * /!\ This method doesn't work with array like notation - * This method will ensure - * - the field has been added to the obfuscated array - * - the salt bound to the field has been removed - * - the field has been removed - */ -const expectRemovedFieldsWithoutArrayNotation = ( - field: string, - document: V4WrappedDocument, - obfuscatedDocument: V4WrappedDocument -) => { - const value = get(document, field); - const salt = findSaltByPath(document.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - expect(obfuscatedDocument).not.toHaveProperty(field); -}; - -describe("privacy", () => { - describe("obfuscateDocument", () => { - test("removes one field from the root object", async () => { - const PATH_TO_REMOVE = "name"; - const rawDocument = makeV4RawDocument({ id: "S1234567A", name: "John Doe" }); - const wrappedDocument = await wrapDocument(rawDocument); - const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - expectRemovedFieldsWithoutArrayNotation(PATH_TO_REMOVE, wrappedDocument, obfuscatedDocument); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - }); - - test("removes paths that result in an invalid wrapped document, should throw", async () => { - const PATHS_TO_REMOVE = ["credentialSubject.id", "credentialSubject.name", "renderMethod.0.id", "name"]; - const rawDocument = makeV4RawDocument({ id: "S1234567A", name: "John Doe" }); - const wrappedDocument = await wrapDocument(rawDocument); - expect(() => obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE)).toThrowError( - `"credentialSubject", "renderMethod.0.id"` - ); - }); - - test("removes one key of an object from an array", async () => { - const PATH_TO_REMOVE = "credentialSubject.arrayOfObject[0].foo" as const; - const rawDocument = makeV4RawDocument({ - id: "did:example:ebfeb1f712ebc6f1c276e12ec21", - alumniOf: "Example University", - array: ["one", "two", "three", "four"], - arrayOfObject: [ - { foo: "bar", doo: "foo" }, - { foo: "baz", doo: "faz" }, - ], - }); - const newDocument = await wrapDocument(rawDocument); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, PATH_TO_REMOVE); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - const value = get(newDocument, PATH_TO_REMOVE); - const salt = findSaltByPath(newDocument.proof.salts, PATH_TO_REMOVE); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [PATH_TO_REMOVE]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, PATH_TO_REMOVE)).toBeUndefined(); - expect(obfuscatedDocument.credentialSubject.arrayOfObject[0]).toStrictEqual({ doo: "foo" }); - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - }); - - test("removes one object from an array", async () => { - const field = "credentialSubject.arrayOfObject[0]"; - const PATHS_TO_REMOVE = ["credentialSubject.arrayOfObject[0].foo", "credentialSubject.arrayOfObject[0].doo"]; - const rawDocument = makeV4RawDocument({ - id: "did:example:ebfeb1f712ebc6f1c276e12ec21", - alumniOf: "Example University", - array: ["one", "two", "three", "four"], - arrayOfObject: [ - { foo: "bar", doo: "foo" }, - { foo: "baz", doo: "faz" }, - ], - }); - const wrappedDocument = await wrapDocument(rawDocument); - const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE); - - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - - PATHS_TO_REMOVE.forEach((field) => { - const value = get(wrappedDocument, field); - const salt = findSaltByPath(wrappedDocument.proof.salts, field); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - }); - expect(obfuscatedDocument.credentialSubject.arrayOfObject[0]).toBeUndefined(); - expect(obfuscatedDocument.credentialSubject.arrayOfObject[1]).not.toBeUndefined(); // let's make sure only the first item has been removed - expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); - }); - // test("removes an array of object", async () => { - // const field = "attachments"; - // const expectedFieldsToBeRemoved = [ - // "attachments[0].mimeType", - // "attachments[0].fileName", - // "attachments[0].data", - // "attachments[1].mimeType", - // "attachments[1].fileName", - // "attachments[1].data", - // ]; - // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - - // const verified = verifySignature(obfuscatedDocument); - // expect(verified).toBe(true); - - // expectedFieldsToBeRemoved.forEach((field) => { - // const value = get(newDocument, field); - // const salt = findSaltByPath(newDocument.proof.salts, field); - - // expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - // toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") - // ); - // expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); - // }); - // expect(obfuscatedDocument.attachments).toBeUndefined(); - // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(6); - // }); - - // test("removes multiple fields", async () => { - // const fields = ["key1", "key2"]; - // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); - // const verified = verifySignature(obfuscatedDocument); - // expect(verified).toBe(true); - - // fields.forEach((field) => { - // expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - // }); - // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); - // }); - - // test("removes values from nested object", async () => { - // const field = "credentialSubject.alumniOf"; - // const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); - // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, field); - // const verified = verifySignature(obfuscatedDocument); - // expect(verified).toBe(true); - - // expectRemovedFieldsWithoutArrayNotation(field, newDocument, obfuscatedDocument); - // expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); - // }); - - // test("removes values from arrays", async () => { - // const fields = ["credentialSubject.array[2]", "credentialSubject.array[3]"]; - // const newDocument = await wrapDocument(openAttestationData, { version: SchemaId.v3 }); - // const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, fields); - // const verified = verifySignature(obfuscatedDocument); - // expect(verified).toBe(true); - - // const salts = decodeSalt(newDocument.proof.salts); - // const salt1 = salts.find((s) => s.path === fields[0]); - // const value1 = get(newDocument, fields[0]); - // const salt2 = salts.find((s) => s.path === fields[1]); - // const value2 = get(newDocument, fields[1]); - - // expect(obfuscatedDocument.proof.privacy.obfuscated).toEqual([ - // toBuffer({ [fields[0]]: `${salt1?.value}:${value1}` }).toString("hex"), - // toBuffer({ [fields[1]]: `${salt2?.value}:${value2}` }).toString("hex"), - // ]); - // expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[0])).toBeUndefined(); - // expect(findSaltByPath(obfuscatedDocument.proof.salts, fields[1])).toBeUndefined(); - // // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // // @ts-ignore not typable - // expect(obfuscatedDocument.credentialSubject.array).not.toContain("three"); - // // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // // @ts-ignore not typable - // expect(obfuscatedDocument.credentialSubject.array).not.toContain("four"); - // }); - - // test("is transitive", async () => { - // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - // const intermediateDoc = obfuscateVerifiableCredential(newDocument, "key1"); - // const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); - // const finalDoc2 = obfuscateVerifiableCredential(newDocument, ["key1", "key2"]); - - // expect(finalDoc1).toEqual(finalDoc2); - // expect(intermediateDoc).not.toHaveProperty("key1"); - // expect(finalDoc1).not.toHaveProperty("key1"); - // expect(finalDoc1).not.toHaveProperty("key2"); - // expect(finalDoc2).not.toHaveProperty("key1"); - // expect(finalDoc2).not.toHaveProperty("key2"); - // }); - }); - - // describe("getObfuscated", () => { - // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - // test("should return empty array when there is no obfuscated data in document v3", () => { - // expect(getObfuscatedData(documentNotObfuscatedV3)).toHaveLength(0); - // }); - - // test("should return array of hashes when there is obfuscated data in document v3", () => { - // const obfuscatedData = getObfuscatedData(documentObfuscatedV3); - // expect(obfuscatedData.length).toBe(1); - // expect(obfuscatedData?.[0]).toBe("e411260249d681968bdde76246350f7ca1c9bf1fae59b6bbf147692961b12e26"); - // }); - // }); - - // describe("isObfuscated", () => { - // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - // test("should return false when there is no obfuscated data in document v3", () => { - // expect(isObfuscated(documentNotObfuscatedV3)).toBe(false); - // }); - - // test("should return true where there is obfuscated data in document v3", () => { - // expect(isObfuscated(documentObfuscatedV3)).toBe(true); - // }); - // }); -}); diff --git a/src/4.0/__tests__/obfuscate.test.wip b/src/4.0/__tests__/obfuscate.test.wip new file mode 100644 index 00000000..00375f3e --- /dev/null +++ b/src/4.0/__tests__/obfuscate.test.wip @@ -0,0 +1,306 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { obfuscateVerifiableCredential } from "../obfuscate"; +import { get } from "lodash"; +import { decodeSalt } from "../salt"; +import { SchemaId } from "../../shared/@types/document"; +import { toBuffer, isObfuscated, getObfuscatedData } from "../../shared/utils"; +import { wrapDocument } from "../wrap"; +import { Salt, V4Document, V4WrappedDocument } from "../types"; +import { verifySignature } from "../../"; +import { RAW_DOCUMENT_DID } from "../fixtures"; + +const makeV4RawDocument = >(props: T) => + ({ + ...RAW_DOCUMENT_DID, + ...(props as T), + } satisfies V4Document); + +const findSaltByPath = (salts: string, path: string): Salt | undefined => { + return decodeSalt(salts).find((salt) => salt.path === path); +}; + +/** + * /!\ This method doesn't work with array like notation + * This method will ensure + * - the field has been added to the obfuscated array + * - the salt bound to the field has been removed + * - the field has been removed + */ +const expectRemovedFieldsWithoutArrayNotation = ( + field: string, + document: V4WrappedDocument, + obfuscatedDocument: V4WrappedDocument +) => { + const value = get(document, field); + const salt = findSaltByPath(document.proof.salts, field); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); + expect(obfuscatedDocument).not.toHaveProperty(field); +}; + +describe("privacy", () => { + describe("obfuscateDocument", () => { + test("removes one field from the root object", async () => { + const PATH_TO_REMOVE = "name"; + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ credentialSubject: { id: "S1234567A", name: "John Doe" } }) + ); + const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE); + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + expectRemovedFieldsWithoutArrayNotation(PATH_TO_REMOVE, wrappedDocument, obfuscatedDocument); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); + }); + + test("removes paths that result in an invalid wrapped document, should throw", async () => { + const PATHS_TO_REMOVE = ["credentialSubject.id", "credentialSubject.name", "renderMethod.0.id", "name"]; + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ credentialSubject: { id: "S1234567A", name: "John Doe" } }) + ); + expect(() => obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE)).toThrowError( + `"credentialSubject", "renderMethod.0.id"` + ); + }); + + test("removes one key of an object from an array", async () => { + const PATH_TO_REMOVE = "credentialSubject.arrayOfObject[0].foo" as const; + const newDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + id: "did:example:ebfeb1f712ebc6f1c276e12ec21", + alumniOf: "Example University", + array: ["one", "two", "three", "four"], + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }, + }) + ); + const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, PATH_TO_REMOVE); + + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + const value = get(newDocument, PATH_TO_REMOVE); + const salt = findSaltByPath(newDocument.proof.salts, PATH_TO_REMOVE); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [PATH_TO_REMOVE]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, PATH_TO_REMOVE)).toBeUndefined(); + expect(obfuscatedDocument.credentialSubject.arrayOfObject?.[0]).toStrictEqual({ doo: "foo" }); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(1); + }); + + test("given an object is to be removed, should remove the object itself, as well as add each of its key's hash into privacy.obfuscated", async () => { + const PATH_TO_REMOVE = "credentialSubject.arrayOfObject[0]"; + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }, + }) + ); + const obfuscatedDocument = obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE); + + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + // assert that each key of the object has been moved to privacy.obfuscated + ["credentialSubject.arrayOfObject[0].foo", "credentialSubject.arrayOfObject[0].doo"].forEach( + (expectedRemovedField) => { + const value = get(wrappedDocument, expectedRemovedField); + const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); + } + ); + + expect(obfuscatedDocument.credentialSubject?.arrayOfObject?.[0]).toBeUndefined(); + expect(obfuscatedDocument.credentialSubject?.arrayOfObject?.[1]).not.toBeUndefined(); // let's make sure only the first item has been removed + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); + }); + + test("given an entire array of objects to remove, should remove the array itself, then for every item, add each of its key's hash into privacy.obfuscated", async () => { + const PATH_TO_REMOVE = "attachments"; + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }, + attachments: [ + { + mimeType: "image/png", + fileName: "aaa", + data: "abcd", + }, + { + mimeType: "image/png", + fileName: "bbb", + data: "abcd", + }, + ], + }) + ); + const obfuscatedDocument = await obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE); + + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + [ + "attachments[0].mimeType", + "attachments[0].fileName", + "attachments[0].data", + "attachments[1].mimeType", + "attachments[1].fileName", + "attachments[1].data", + ].forEach((expectedRemovedField) => { + const value = get(wrappedDocument, expectedRemovedField); + const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); + }); + expect(obfuscatedDocument.attachments).toBeUndefined(); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(6); + }); + + test("given multiple fields to be removed, should remove fields and add their hash into privacy.obfuscated", async () => { + const PATHS_TO_REMOVE = ["credentialSubject.key1", "credentialSubject.key2"]; + const newDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + key1: "value1", + key2: "value2", + key3: "value3", + }, + }) + ); + const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, PATHS_TO_REMOVE); + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + PATHS_TO_REMOVE.forEach((expectedRemovedField) => { + expectRemovedFieldsWithoutArrayNotation(expectedRemovedField, newDocument, obfuscatedDocument); + }); + expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); + }); + + test("removes values from arrays", async () => { + const PATHS_TO_REMOVE = ["attachments[0]", "attachments[2]"]; + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + }, + attachments: [ + { + mimeType: "image/png", + fileName: "aaa", + data: "abcd", + }, + { + mimeType: "image/png", + fileName: "bbb", + data: "abcd", + }, + { + mimeType: "image/png", + fileName: "ccc", + data: "abcd", + }, + ], + }) + ); + const obfuscatedDocument = await obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE); + const verified = verifySignature(obfuscatedDocument); + expect(verified).toBe(true); + + [ + "attachments[0].mimeType", + "attachments[0].fileName", + "attachments[0].data", + "attachments[2].mimeType", + "attachments[2].fileName", + "attachments[2].data", + ].forEach((expectedRemovedField) => { + const value = get(wrappedDocument, expectedRemovedField); + const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); + }); + + expect(obfuscatedDocument.attachments?.[0]).not.toEqual(undefined); + expect(obfuscatedDocument.attachments?.[1]).toEqual({ + mimeType: "image/png", + fileName: "bbb", + data: "abcd", + }); + expect(obfuscatedDocument.attachments?.[2]).not.toEqual(undefined); + }); + + // test("is transitive", async () => { + // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); + // const intermediateDoc = obfuscateVerifiableCredential(newDocument, "key1"); + // const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); + // const finalDoc2 = obfuscateVerifiableCredential(newDocument, ["key1", "key2"]); + + // expect(finalDoc1).toEqual(finalDoc2); + // expect(intermediateDoc).not.toHaveProperty("key1"); + // expect(finalDoc1).not.toHaveProperty("key1"); + // expect(finalDoc1).not.toHaveProperty("key2"); + // expect(finalDoc2).not.toHaveProperty("key1"); + // expect(finalDoc2).not.toHaveProperty("key2"); + // }); + }); + + // describe("getObfuscated", () => { + // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; + // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; + + // test("should return empty array when there is no obfuscated data in document v3", () => { + // expect(getObfuscatedData(documentNotObfuscatedV3)).toHaveLength(0); + // }); + + // test("should return array of hashes when there is obfuscated data in document v3", () => { + // const obfuscatedData = getObfuscatedData(documentObfuscatedV3); + // expect(obfuscatedData.length).toBe(1); + // expect(obfuscatedData?.[0]).toBe("e411260249d681968bdde76246350f7ca1c9bf1fae59b6bbf147692961b12e26"); + // }); + // }); + + // describe("isObfuscated", () => { + // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; + // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; + + // test("should return false when there is no obfuscated data in document v3", () => { + // expect(isObfuscated(documentNotObfuscatedV3)).toBe(false); + // }); + + // test("should return true where there is obfuscated data in document v3", () => { + // expect(isObfuscated(documentObfuscatedV3)).toBe(true); + // }); + // }); +}); From 5a29fe50262842232b626533bcd0bd05a7418f0c Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Tue, 30 Apr 2024 17:41:23 +0800 Subject: [PATCH 085/107] wip: draft of changes to address issues raised --- .../{obfuscate.test.wip => obfuscate.test.ts} | 108 ++++++++++-------- src/4.0/digest.ts | 52 +++++++-- src/4.0/obfuscate.ts | 8 +- src/4.0/salt.ts | 2 +- src/4.0/traverseAndFlatten.ts | 38 +++--- src/4.0/verify.ts | 6 +- 6 files changed, 132 insertions(+), 82 deletions(-) rename src/4.0/__tests__/{obfuscate.test.wip => obfuscate.test.ts} (79%) diff --git a/src/4.0/__tests__/obfuscate.test.wip b/src/4.0/__tests__/obfuscate.test.ts similarity index 79% rename from src/4.0/__tests__/obfuscate.test.wip rename to src/4.0/__tests__/obfuscate.test.ts index 00375f3e..7d996b12 100644 --- a/src/4.0/__tests__/obfuscate.test.wip +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -3,11 +3,11 @@ import { obfuscateVerifiableCredential } from "../obfuscate"; import { get } from "lodash"; import { decodeSalt } from "../salt"; import { SchemaId } from "../../shared/@types/document"; -import { toBuffer, isObfuscated, getObfuscatedData } from "../../shared/utils"; import { wrapDocument } from "../wrap"; import { Salt, V4Document, V4WrappedDocument } from "../types"; import { verifySignature } from "../../"; import { RAW_DOCUMENT_DID } from "../fixtures"; +import { hashLeafNode } from "../digest"; const makeV4RawDocument = >(props: T) => ({ @@ -34,8 +34,10 @@ const expectRemovedFieldsWithoutArrayNotation = ( const value = get(document, field); const salt = findSaltByPath(document.proof.salts, field); + if (!salt) throw new Error("Salt not found for ${field}"); + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [field]: `${salt?.value}:${value}` }).toString("hex") + hashLeafNode({ value, salt: salt.value, path: salt.path }, { toHexString: true }) ); expect(findSaltByPath(obfuscatedDocument.proof.salts, field)).toBeUndefined(); expect(obfuscatedDocument).not.toHaveProperty(field); @@ -89,8 +91,10 @@ describe("privacy", () => { const value = get(newDocument, PATH_TO_REMOVE); const salt = findSaltByPath(newDocument.proof.salts, PATH_TO_REMOVE); + if (!salt) throw new Error(`Salt not found for ${PATH_TO_REMOVE}`); + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [PATH_TO_REMOVE]: `${salt?.value}:${value}` }).toString("hex") + hashLeafNode({ value, salt: salt.value, path: PATH_TO_REMOVE }, { toHexString: true }) ); expect(findSaltByPath(obfuscatedDocument.proof.salts, PATH_TO_REMOVE)).toBeUndefined(); expect(obfuscatedDocument.credentialSubject.arrayOfObject?.[0]).toStrictEqual({ doo: "foo" }); @@ -120,8 +124,10 @@ describe("privacy", () => { const value = get(wrappedDocument, expectedRemovedField); const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + if (!salt) throw new Error(`Salt not found for ${expectedRemovedField}`); + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") + hashLeafNode({ value, salt: salt.value, path: expectedRemovedField }, { toHexString: true }) ); expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); } @@ -172,8 +178,10 @@ describe("privacy", () => { const value = get(wrappedDocument, expectedRemovedField); const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + if (!salt) throw new Error(`Salt not found for ${expectedRemovedField}`); + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") + hashLeafNode({ value, salt: salt.value, path: expectedRemovedField }, { toHexString: true }) ); expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); }); @@ -183,7 +191,7 @@ describe("privacy", () => { test("given multiple fields to be removed, should remove fields and add their hash into privacy.obfuscated", async () => { const PATHS_TO_REMOVE = ["credentialSubject.key1", "credentialSubject.key2"]; - const newDocument = await wrapDocument( + const wrappedDocument = await wrapDocument( makeV4RawDocument({ credentialSubject: { key1: "value1", @@ -192,17 +200,17 @@ describe("privacy", () => { }, }) ); - const obfuscatedDocument = await obfuscateVerifiableCredential(newDocument, PATHS_TO_REMOVE); + const obfuscatedDocument = await obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE); const verified = verifySignature(obfuscatedDocument); expect(verified).toBe(true); PATHS_TO_REMOVE.forEach((expectedRemovedField) => { - expectRemovedFieldsWithoutArrayNotation(expectedRemovedField, newDocument, obfuscatedDocument); + expectRemovedFieldsWithoutArrayNotation(expectedRemovedField, wrappedDocument, obfuscatedDocument); }); expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); }); - test("removes values from arrays", async () => { + test("given a path to remove an entire item from an array, should throw", async () => { const PATHS_TO_REMOVE = ["attachments[0]", "attachments[2]"]; const wrappedDocument = await wrapDocument( makeV4RawDocument({ @@ -231,49 +239,55 @@ describe("privacy", () => { ], }) ); - const obfuscatedDocument = await obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE); - const verified = verifySignature(obfuscatedDocument); - expect(verified).toBe(true); - [ - "attachments[0].mimeType", - "attachments[0].fileName", - "attachments[0].data", - "attachments[2].mimeType", - "attachments[2].fileName", - "attachments[2].data", - ].forEach((expectedRemovedField) => { - const value = get(wrappedDocument, expectedRemovedField); - const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + expect(() => obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE)).toThrow(); + }); - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - toBuffer({ [expectedRemovedField]: `${salt?.value}:${value}` }).toString("hex") - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); - }); + test("given a path to remove all elements in an object, should throw", async () => { + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + arrayOfObject: [ + { foo: "bar", doo: "foo" }, + { foo: "baz", doo: "faz" }, + ], + object: { + foo: "bar", + }, + }, + }) + ); - expect(obfuscatedDocument.attachments?.[0]).not.toEqual(undefined); - expect(obfuscatedDocument.attachments?.[1]).toEqual({ - mimeType: "image/png", - fileName: "bbb", - data: "abcd", - }); - expect(obfuscatedDocument.attachments?.[2]).not.toEqual(undefined); + expect(() => + obfuscateVerifiableCredential(wrappedDocument, [ + "credentialSubject.arrayOfObject[0].foo", + "credentialSubject.arrayOfObject[0].doo", + ]) + ).toThrow(); + expect(() => obfuscateVerifiableCredential(wrappedDocument, ["credentialSubject.object.foo"])).toThrow(); }); - // test("is transitive", async () => { - // const newDocument = await wrapDocument(testData, { version: SchemaId.v3 }); - // const intermediateDoc = obfuscateVerifiableCredential(newDocument, "key1"); - // const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); - // const finalDoc2 = obfuscateVerifiableCredential(newDocument, ["key1", "key2"]); - - // expect(finalDoc1).toEqual(finalDoc2); - // expect(intermediateDoc).not.toHaveProperty("key1"); - // expect(finalDoc1).not.toHaveProperty("key1"); - // expect(finalDoc1).not.toHaveProperty("key2"); - // expect(finalDoc2).not.toHaveProperty("key1"); - // expect(finalDoc2).not.toHaveProperty("key2"); - // }); + test("is transitive", async () => { + const wrappedDocument = await wrapDocument( + makeV4RawDocument({ + credentialSubject: { + key1: "value1", + key2: "value2", + key3: "value3", + }, + }) + ); + const intermediateDoc = obfuscateVerifiableCredential(wrappedDocument, "key1"); + const finalDoc1 = obfuscateVerifiableCredential(intermediateDoc, "key2"); + const finalDoc2 = obfuscateVerifiableCredential(wrappedDocument, ["key1", "key2"]); + + expect(finalDoc1).toEqual(finalDoc2); + expect(intermediateDoc).not.toHaveProperty("key1"); + expect(finalDoc1).not.toHaveProperty("key1"); + expect(finalDoc1).not.toHaveProperty("key2"); + expect(finalDoc2).not.toHaveProperty("key1"); + expect(finalDoc2).not.toHaveProperty("key2"); + }); }); // describe("getObfuscated", () => { diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 1ce553b8..d691f29d 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -1,15 +1,16 @@ -import { get, sortBy } from "lodash"; +import { sortBy } from "lodash"; import { keccak256 } from "js-sha3"; import { V4Document, Salt } from "./types"; +import { LeafValue, traverseAndFlatten } from "./traverseAndFlatten"; +import { hashToBuffer } from "../shared/utils"; export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => { - // Prepare array of hashes from visible data - const hashedUnhashedDataArray = salts - // Explictly allow falsy values (e.g. false, 0, etc.) as they can exist in the document - .filter((salt) => get(document, salt.path) !== undefined) - .map((salt) => { - return keccak256(JSON.stringify({ [salt.path]: `${salt.value}:${get(document, salt.path)}` })); - }); + const saltsMap = new Map(salts.map((salt) => [salt.path, salt.value])); + const hashedUnhashedDataArray = traverseAndFlatten(document, ({ value, path }) => { + const salt = saltsMap.get(path); + if (!salt) throw new Error(`Salt not found for ${path}`); + return hashLeafNode({ path, salt, value }); + }); // Combine both array and sort them to ensure determinism const combinedHashes = obfuscatedData.concat(hashedUnhashedDataArray); @@ -18,3 +19,38 @@ export const digestCredential = (document: V4Document, salts: Salt[], obfuscated // Finally, return the digest of the entire set of data return keccak256(JSON.stringify(sortedHashes)); }; + +type HashParams = { + salt: string; + value: LeafValue; + path: string; +}; +type HashOptions = { + toHexString: true; +}; +export function hashLeafNode({ path, salt, value }: HashParams, options?: HashOptions) { + const type = deriveType(value); + const hash = keccak256(JSON.stringify({ [path]: `${salt}:${type}:${value}` })); + return !options?.toHexString ? hash : hashToBuffer(hash).toString("hex"); +} + +export function deriveType(value: unknown): "string" | "number" | "object" | "array" | "null" { + if (Array.isArray(value)) { + return "array"; + } else if (value === null) { + return "null"; + } else { + switch (typeof value) { + case "string": + return "string"; + case "number": + return "number"; + case "object": + return "object"; + case "boolean": + return "object"; + default: + throw new Error(`Unsupported type ${typeof value}`); + } + } +} diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 76b89844..ffc330ce 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -1,8 +1,8 @@ -import { toBuffer } from "../shared/utils"; import { cloneDeep, get, unset, pick } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; import { Override, PartialDeep, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; +import { hashLeafNode } from "./digest"; const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data @@ -10,9 +10,7 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const fieldsAsArray = ([] as string[]).concat(fields); // fields to remove will contain the list of each expanded keys from the fields passed in parameter, it's for instance useful in case of // object obfuscation, where the object itself is not part of the salts, but each individual keys are - const fieldsToRemove: string[] = traverseAndFlatten(pick(data, fieldsAsArray), { - iteratee: ({ path }) => path, - }); + const fieldsToRemove = traverseAndFlatten(pick(data, fieldsAsArray), ({ path }) => path); const salts = decodeSalt(data.proof.salts); // Obfuscate data by hashing them with the key @@ -24,7 +22,7 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { throw new Error(`Salt not found for ${field}`); } - return toBuffer({ [salt.path]: `${salt.value}:${value}` }).toString("hex"); + return hashLeafNode({ value, salt: salt.value, path: salt.path }, { toHexString: true }); }); // remove fields from the object fieldsAsArray.forEach((field) => unset(data, field)); diff --git a/src/4.0/salt.ts b/src/4.0/salt.ts index ab4a5a33..6e80ef63 100644 --- a/src/4.0/salt.ts +++ b/src/4.0/salt.ts @@ -26,7 +26,7 @@ export const secureRandomString = () => randomBytes(ENTROPY_IN_BYTES).toString(" export const salt = (data: any): Salt[] => { // Check for illegal characters e.g. '.', '[' or ']' illegalCharactersCheck(data); - return traverseAndFlatten(data, { iteratee: ({ path }) => ({ value: secureRandomString(), path }) }); + return traverseAndFlatten(data, ({ path }) => ({ value: secureRandomString(), path })); }; export const encodeSalt = (salts: Salt[]): string => Base64.encode(JSON.stringify(salts)); diff --git a/src/4.0/traverseAndFlatten.ts b/src/4.0/traverseAndFlatten.ts index a878eb36..b646cec1 100644 --- a/src/4.0/traverseAndFlatten.ts +++ b/src/4.0/traverseAndFlatten.ts @@ -1,27 +1,22 @@ -import { Options } from "@govtechsg/jsonld"; +export type LeafValue = string | number | boolean | null | Record | []; -type LeafValue = string | number | boolean | null; -interface Options { - /* function to run on every field */ - iteratee: (data: { value: LeafValue; path: string }) => IterateeValue; - /* root path of the property being acceded */ - path?: string; -} - -/** Given a record | list, returns a list of all leaf nodes of value that is not undefined */ -export function traverseAndFlatten(data: LeafValue, options: Options): IterateeValue; -export function traverseAndFlatten(data: unknown, options: Options): IterateeValue[]; -export function traverseAndFlatten( +function _traverseAndFlatten( data: unknown, - { iteratee, path = "" }: Options + iteratee: (data: { value: LeafValue; path: string }) => IterateeValue, + path = "" ): IterateeValue | IterateeValue[] { if (Array.isArray(data)) { - return data.flatMap((v, index) => traverseAndFlatten(v, { iteratee, path: `${path}[${index}]` })); + // an empty array is considered a leaf node + if (data.length === 0) return iteratee({ value: [], path }); + return data.flatMap((v, index) => _traverseAndFlatten(v, iteratee, `${path}[${index}]`)); } if (typeof data === "object" && data !== null) { + const keys = Object.keys(data); + // an empty object is considered a leaf node + if (keys.length === 0) return iteratee({ value: {}, path }); return Object.keys(data).flatMap((key) => - traverseAndFlatten(data[key as keyof typeof data], { iteratee, path: path ? `${path}.${key}` : key }) + _traverseAndFlatten(data[key as keyof typeof data], iteratee, path ? `${path}.${key}` : key) ); } @@ -29,5 +24,16 @@ export function traverseAndFlatten( return iteratee({ value: data, path }); } + // our expected input is JSON so it should never have undefined throw new Error(`Unexpected data '${data}' in '${path}'`); } + +/** Given a record, returns a list of all leaf nodes of value that is not undefined */ +export function traverseAndFlatten( + data: Record, + iteratee: (data: { value: LeafValue; path: string }) => IterateeValue, + path = "" +): IterateeValue[] { + const results = _traverseAndFlatten(data, iteratee, path); + return Array.isArray(results) ? results : []; +} diff --git a/src/4.0/verify.ts b/src/4.0/verify.ts index 1deaa011..72d488e3 100644 --- a/src/4.0/verify.ts +++ b/src/4.0/verify.ts @@ -1,7 +1,7 @@ import { V4WrappedDocument } from "./types"; import { digestCredential } from "./digest"; import { checkProof } from "../shared/merkle"; -import { decodeSalt, salt } from "./salt"; +import { decodeSalt } from "./salt"; export const verify = (document: T): document is T => { if (!document.proof) { @@ -13,10 +13,6 @@ export const verify = (document: T): document is T const { proof, ...documentWithoutProof } = document; const decodedSalts = decodeSalt(document.proof.salts); - // Checks to ensure there are no added/removed values, so visibleSalts.length must match decodedSalts.length - const visibleSalts = salt(documentWithoutProof); - if (visibleSalts.length !== decodedSalts.length) return false; - // Checks target hash const digest = digestCredential(documentWithoutProof, decodedSalts, document.proof.privacy.obfuscated); const targetHash = document.proof.targetHash; From 9c293af7fd92bd70cc7c7a64868480be32835e97 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Tue, 30 Apr 2024 22:46:34 +0800 Subject: [PATCH 086/107] fix: wrongly returned object for boolean --- src/4.0/digest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index d691f29d..1305bcaa 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -34,7 +34,7 @@ export function hashLeafNode({ path, salt, value }: HashParams, options?: HashOp return !options?.toHexString ? hash : hashToBuffer(hash).toString("hex"); } -export function deriveType(value: unknown): "string" | "number" | "object" | "array" | "null" { +export function deriveType(value: unknown): "string" | "number" | "boolean" | "null" | "object" | "array" { if (Array.isArray(value)) { return "array"; } else if (value === null) { @@ -48,7 +48,7 @@ export function deriveType(value: unknown): "string" | "number" | "object" | "ar case "object": return "object"; case "boolean": - return "object"; + return "boolean"; default: throw new Error(`Unsupported type ${typeof value}`); } From 70ff03fc1f300e41025552f312f892c1b3ae2856 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 1 May 2024 12:20:58 +0800 Subject: [PATCH 087/107] fix: shd not allow new empty objects after obfuscation or removing array items --- src/4.0/__tests__/obfuscate.test.ts | 50 +++++++++++------------ src/4.0/digest.ts | 4 +- src/4.0/obfuscate.ts | 61 +++++++++++++++++++++++++---- src/4.0/traverseAndFlatten.ts | 16 +++++++- 4 files changed, 94 insertions(+), 37 deletions(-) diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts index 7d996b12..26078b66 100644 --- a/src/4.0/__tests__/obfuscate.test.ts +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -59,7 +59,7 @@ describe("privacy", () => { }); test("removes paths that result in an invalid wrapped document, should throw", async () => { - const PATHS_TO_REMOVE = ["credentialSubject.id", "credentialSubject.name", "renderMethod.0.id", "name"]; + const PATHS_TO_REMOVE = ["credentialSubject", "renderMethod.0.id", "name"]; const wrappedDocument = await wrapDocument( makeV4RawDocument({ credentialSubject: { id: "S1234567A", name: "John Doe" } }) ); @@ -102,14 +102,12 @@ describe("privacy", () => { }); test("given an object is to be removed, should remove the object itself, as well as add each of its key's hash into privacy.obfuscated", async () => { - const PATH_TO_REMOVE = "credentialSubject.arrayOfObject[0]"; + const PATH_TO_REMOVE = "credentialSubject.hee"; const wrappedDocument = await wrapDocument( makeV4RawDocument({ credentialSubject: { - arrayOfObject: [ - { foo: "bar", doo: "foo" }, - { foo: "baz", doo: "faz" }, - ], + hee: { foo: "bar", doo: "foo" }, + haa: { foo: "baz", doo: "faz" }, }, }) ); @@ -119,22 +117,19 @@ describe("privacy", () => { expect(verified).toBe(true); // assert that each key of the object has been moved to privacy.obfuscated - ["credentialSubject.arrayOfObject[0].foo", "credentialSubject.arrayOfObject[0].doo"].forEach( - (expectedRemovedField) => { - const value = get(wrappedDocument, expectedRemovedField); - const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); - - if (!salt) throw new Error(`Salt not found for ${expectedRemovedField}`); - - expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( - hashLeafNode({ value, salt: salt.value, path: expectedRemovedField }, { toHexString: true }) - ); - expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); - } - ); + ["credentialSubject.hee.foo", "credentialSubject.hee.doo"].forEach((expectedRemovedField) => { + const value = get(wrappedDocument, expectedRemovedField); + const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField); + + if (!salt) throw new Error(`Salt not found for ${expectedRemovedField}`); - expect(obfuscatedDocument.credentialSubject?.arrayOfObject?.[0]).toBeUndefined(); - expect(obfuscatedDocument.credentialSubject?.arrayOfObject?.[1]).not.toBeUndefined(); // let's make sure only the first item has been removed + expect(obfuscatedDocument.proof.privacy.obfuscated).toContain( + hashLeafNode({ value, salt: salt.value, path: expectedRemovedField }, { toHexString: true }) + ); + expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined(); + }); + + expect(obfuscatedDocument.credentialSubject?.hee).toBeUndefined(); // let's make sure only the first item has been removed expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(2); }); @@ -211,7 +206,6 @@ describe("privacy", () => { }); test("given a path to remove an entire item from an array, should throw", async () => { - const PATHS_TO_REMOVE = ["attachments[0]", "attachments[2]"]; const wrappedDocument = await wrapDocument( makeV4RawDocument({ credentialSubject: { @@ -240,7 +234,7 @@ describe("privacy", () => { }) ); - expect(() => obfuscateVerifiableCredential(wrappedDocument, PATHS_TO_REMOVE)).toThrow(); + expect(() => obfuscateVerifiableCredential(wrappedDocument, ["attachments[0]", "attachments[2]"])).toThrow(); }); test("given a path to remove all elements in an object, should throw", async () => { @@ -263,8 +257,14 @@ describe("privacy", () => { "credentialSubject.arrayOfObject[0].foo", "credentialSubject.arrayOfObject[0].doo", ]) - ).toThrow(); - expect(() => obfuscateVerifiableCredential(wrappedDocument, ["credentialSubject.object.foo"])).toThrow(); + ).toThrowErrorMatchingInlineSnapshot( + `"Obfuscation of "credentialSubject.arrayOfObject[0].doo" has resulted in an empty {}, this is currently not supported."` + ); + expect(() => + obfuscateVerifiableCredential(wrappedDocument, ["credentialSubject.object.foo"]) + ).toThrowErrorMatchingInlineSnapshot( + `"Obfuscation of "credentialSubject.object.foo" has resulted in an empty {}, this is currently not supported."` + ); }); test("is transitive", async () => { diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 1305bcaa..6ea1567d 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -6,14 +6,14 @@ import { hashToBuffer } from "../shared/utils"; export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => { const saltsMap = new Map(salts.map((salt) => [salt.path, salt.value])); - const hashedUnhashedDataArray = traverseAndFlatten(document, ({ value, path }) => { + const hashedLeafNodes = traverseAndFlatten(document, ({ value, path }) => { const salt = saltsMap.get(path); if (!salt) throw new Error(`Salt not found for ${path}`); return hashLeafNode({ path, salt, value }); }); // Combine both array and sort them to ensure determinism - const combinedHashes = obfuscatedData.concat(hashedUnhashedDataArray); + const combinedHashes = obfuscatedData.concat(hashedLeafNodes); const sortedHashes = sortBy(combinedHashes); // Finally, return the digest of the entire set of data diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index ffc330ce..c559d41f 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -1,4 +1,4 @@ -import { cloneDeep, get, unset, pick } from "lodash"; +import { cloneDeep, get, unset, pick, toPath } from "lodash"; import { decodeSalt, encodeSalt } from "./salt"; import { traverseAndFlatten } from "./traverseAndFlatten"; import { Override, PartialDeep, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; @@ -6,15 +6,15 @@ import { hashLeafNode } from "./digest"; const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { const data = cloneDeep(_data); // Prevents alteration of original data - const fieldsAsArray = ([] as string[]).concat(fields); + // fields to remove will contain the list of each expanded keys from the fields passed in parameter, it's for instance useful in case of // object obfuscation, where the object itself is not part of the salts, but each individual keys are - const fieldsToRemove = traverseAndFlatten(pick(data, fieldsAsArray), ({ path }) => path); + const affectedLeafNodes = traverseAndFlatten(pick(data, fieldsAsArray), ({ path }) => path); const salts = decodeSalt(data.proof.salts); // Obfuscate data by hashing them with the key - const obfuscatedData = fieldsToRemove.map((field) => { + const obfuscatedData = affectedLeafNodes.map((field) => { const value = get(data, field); const salt = salts.find((s) => s.path === field); @@ -24,10 +24,43 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { return hashLeafNode({ value, salt: salt.value, path: salt.path }, { toHexString: true }); }); + // remove fields from the object - fieldsAsArray.forEach((field) => unset(data, field)); + for (const field of fieldsAsArray) { + const path = toPath(field); + const isRemoved = unset(data, path); + if (isRemoved) { + // assertions to ensure that obfuscation does not result in additional leaf nodes + // that would render the resultant document as invalid + path.pop(); + if (path.length > 0) { + const parent = get(data, path); + if (Array.isArray(parent)) { + // Why removing elements in an array is not supported? + // removing an item from an array leaves an empty item/undefined in the array + // but when serialized to JSON, empty items are converted to null + // null is a leaf node and will lead to a problem similar to what is described below + throw new CannotObfuscateArrayItemError(field); + } else if (typeof parent === "object") { + // Why removing elements that lead to empty objects is not supported? + // our obfucsation algoritm works as such: + // given an object { a: { b: 1 }, c: [{ d: 2, e: 3 }] } + // the leaf nodes will be [a.b, c[0].d, c[0].e] + // and the hash, lets call it X, is computed over these leaf nodes + // when we obfuscate say c[0].d, we prehash c[0].d and add it to obfuscatedData and remove c[0].d from the object + // now the hash is computed over [a.b, c[0].e] and prehash(c[0].d) and still equates to X + // the problem comes when we remove both c[0].d and c[0].e, or a.b, which in both cases leaves an empty object behind + // empty objects are considered leaf nodes. so given we obfuscate a.b, a will be an empty object, a leaf node + // the hash now is computed over [a, c[0].d, c[0].e] and prehash(a.b), which does not equate to X + if (Object.keys(parent).length === 0) { + throw new CannotResultInEmptyObjectError(field); + } + } + } + } + } - data.proof.salts = encodeSalt(salts.filter((s) => !fieldsToRemove.includes(s.path))); + data.proof.salts = encodeSalt(salts.filter((s) => !affectedLeafNodes.includes(s.path))); return { data, obfuscatedData, @@ -59,12 +92,12 @@ export const obfuscateVerifiableCredential = path.join(".")); - throw new ObfuscatedInvalidPaths(paths); + throw new CannotObfuscateProtectedPathsError(paths); } return parsedResults.data as ObfuscateVerifiableCredentialResult; }; -export class ObfuscatedInvalidPaths extends Error { +export class CannotObfuscateProtectedPathsError extends Error { constructor(public paths: string[]) { super( `The resultant obfuscated document is not V4 Wrapped Document compliant, please ensure that the following path(s) are not obfuscated: ${paths @@ -73,3 +106,15 @@ export class ObfuscatedInvalidPaths extends Error { ); } } + +export class CannotObfuscateArrayItemError extends Error { + constructor(public field: string) { + super("Obfuscation of an array item is not supported"); + } +} + +export class CannotResultInEmptyObjectError extends Error { + constructor(public field: string) { + super(`Obfuscation of "${field}" has resulted in an empty {}, this is currently not supported.`); + } +} diff --git a/src/4.0/traverseAndFlatten.ts b/src/4.0/traverseAndFlatten.ts index b646cec1..53a39f49 100644 --- a/src/4.0/traverseAndFlatten.ts +++ b/src/4.0/traverseAndFlatten.ts @@ -8,7 +8,20 @@ function _traverseAndFlatten( if (Array.isArray(data)) { // an empty array is considered a leaf node if (data.length === 0) return iteratee({ value: [], path }); - return data.flatMap((v, index) => _traverseAndFlatten(v, iteratee, `${path}[${index}]`)); + + // dont use flat map as it skips empty items in the array + const results: IterateeValue[] = []; + for (let index = 0; index < data.length; index++) { + const value = data[index]; + const result = _traverseAndFlatten(value, iteratee, `${path}[${index}]`); + if (Array.isArray(result)) { + results.push(...result); + } else { + results.push(result); + } + } + + return results; } if (typeof data === "object" && data !== null) { @@ -24,7 +37,6 @@ function _traverseAndFlatten( return iteratee({ value: data, path }); } - // our expected input is JSON so it should never have undefined throw new Error(`Unexpected data '${data}' in '${path}'`); } From 692dbab71cc3496e3ec64e43edf09fb5eb8742f5 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 1 May 2024 23:34:30 +0800 Subject: [PATCH 088/107] chore: update fixtures --- src/4.0/fixtures.ts | 429 ++++++++++++++++++++++++++++++-------------- 1 file changed, 298 insertions(+), 131 deletions(-) diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index 9beba540..e93d77a4 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -1,5 +1,11 @@ import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; +const SAMPLE_SIGNING_KEYS = { + issuerId: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", +} as const; + export const RAW_DOCUMENT_DID = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", @@ -9,7 +15,7 @@ export const RAW_DOCUMENT_DID = freezeObject({ validFrom: "2021-03-08T12:00:00+08:00", name: "Republic of Singapore Driving Licence", issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, @@ -49,7 +55,7 @@ export const RAW_DOCUMENT_DID_OSCP = freezeObject({ validFrom: "2021-03-08T12:00:00+08:00", name: "Republic of Singapore Driving Licence", issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, @@ -84,6 +90,88 @@ export const RAW_DOCUMENT_DID_OSCP = freezeObject({ }, } satisfies V4Document); +export const BATCHED_RAW_DOCUMENTS_DID = freezeObject([ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "Jane Doe", + licenses: [ + { + class: "3000A", + description: "Motor spaceships with unladen weight <= 3000tonnes", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + }, +] satisfies V4Document[]); + export const WRAPPED_DOCUMENT_DID = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", @@ -92,7 +180,7 @@ export const WRAPPED_DOCUMENT_DID = freezeObject({ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -128,38 +216,34 @@ export const WRAPPED_DOCUMENT_DID = freezeObject({ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", + targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", proofs: [], - merkleRoot: "f065a97f2ec23ff1469dedbcf9e41916e2a5e46b001e512d2d27d10ee87d8433", + merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", salts: - "W3sidmFsdWUiOiIzMThlMDgwY2NjZWYwZmRjMWEyNjhjN2FjOTEwNmNiYzUwNTEyMzkyNTc0MDNhZTU2MmI3YTFhNmU5YjkzY2ZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjE2MTJkMDliZjUzNGZkMmZhNTFmYjlhOWI0NTk1MzE0NTlmNzU3NDA1ZmExMjllMjY3YWEwNTUxOTlhZDY2ZjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiY2NlMDA0ZGM0M2NiZGY0ZmE5NjRlN2Q3ZjQ0YzQ5ZTMzNDhiMzExZmQxYjc0MzhhYTI2ZjI5YmI2OGU5MGU3NCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjNhZmJmZDc0Mzg1YWQ2NzM0YjE3MDgyOGJhNWUyYWI1YzM4MTY1YTBmYWNlOTJlNGUwODRmNzkyMjFlZDJkMDAiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIxYTMwZTE4MTI3N2I2ZjFlZTczOTlmMTQxZWU5MjY2MDIwMWUxYTRlYmZkODEzOTJiNjgxYTEwMjMxMTUzY2Q1IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMWU4ZDRlODY5ZjVkMzE1YmIxMzcyZjRhOTQxYWNjNjgxMWY2OWIwZmRiMmFkOWM3MTRjMGJiNTZhMmMzMjdmZiIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYzVjN2MxNzVhNmU5ZTFlZDQ1OTkxYTExYjhiNjczZmRiNWU4ZTU4NDU5OTliM2FkODYwMjM5OTdiOWUzNWEzNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIyMjNkZDJhOGMwZTZhOGJlMTE2ZmUxMzliM2Q0YjRkYWZlMTBkNWJiNWEwNmVjNzE2YjNhM2JiNDZlYjI3ZDNjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjM0YzJmMTU4OWM2MTFmYWZmOTE4MTAwODk3MGJiMGY1NTc3MWQ1ODI0ODQxOGY3OGVhOWM4NTk3NTgwZDM1ZTkiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYWE0MmVhNDE3MGJhNmRhMTVmZTdiMzIyZTUxMzc3ODA5M2E2NjE3MTE1NzZmOTA2M2JmY2Y4YzdlMGEzYjRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU5ZjNlMTJiYTUzZDA3M2Y0ZjNiZTU2ZDUzN2ZjYjIwZmI0ZTM0YzZkM2RlZGZkNTcyMzkzODAzOTViZGM4ZTkiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjhkNGIyMDYyZmFmNzRhNmZhNGE5NmE2OTQ0NzIyMWFjNjhjZTk0MjBjOWQ2ZTFhNDFjODM2MmI1ODQwMmI5ODQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTMxNmM0YTRhZGJiMzQwYTBjYTViNDljNTZmNWIxZWMzNjA5N2UyMjg4YzJkY2I0YmU4ZjhiY2MxNDA4NjIyYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjJlZmY0Y2JlZjFkNmQ1ZTEwOTZhNDAyNGY4ODhmM2U2YTEwMWZkYjIxZTRjZmI1ZjdmYTA4Y2ZhZmZkMzk2YjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI0YjAxYjNmZWIzOTcyYTBmMDBjNTJkNDU2YmYxZWY1ZWJkODI3YjkxMDFjMzI4NTQwNzExM2NiODZhODNiY2JmIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjcxMjE4ZTA2YTNlNGM1NTc5NTIwNzdkNzQ1NTJlZTMyNDIxNzMxMjU2YTcwZDI1Nzg3ZGQyNmFkMDk1YmY5NzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNWM2NzdhYzgwMGQ3MDRiZjUwYzZiYjAyN2Q0OTg1YmQ2OTg0Y2VhYjExNzUzOTk0ZWQ5YTI5OTIxYjhmYWRmNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVhMjY1MDM0OTFlYjhjZjNiN2EzMTg1MGEwZTM3OTQxMjFiN2YzOTQ5OTI1NmIyNGQ3OWNkODE3MDVjNDcxOTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNzlhOWZiOTVkM2U5NTlkNjFlODFhNmQwMGY3ZTlkZjAwZTAzNTMzYTYyZDZlZDkyNWI4MTIyNjY5YThkYzZkNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YzhkOWQ0YzRkYjRhODU4NmJkODgzZmViZmNhNTUzNmYxYmVmNDJhM2NmYTJmYTQxNWY0YWFkZjIyZmY1MTlkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNGNkOWMwOGU3NDE2MzU0YjhiNjBlYzA1YzQzZDY3YWMwOTAzNmQ4YWRlZGYyZGVjMjIxNWU1NmU4MGM1MDg5MCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiOGRjNGY3NDllN2JhZmZmZWJmM2FmOGY4ZDJjYjUwNTAzZGFmOGZhZTVkM2Q3YjhjNTNhZDM1NTFiMWM1NDI0MyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJkZjY0NmI2YjYwMTJmZWQxYzE5N2E5MjhjNGJjZTVhOWJlNTc0YjU4YmFhYjZkN2E4OTAwZDBiZDdkYjg4N2IyIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY1OTc2ZWJlMTE5MjY5ZjZhZjhlZjIzMGI0ZjgwNDgyMzA5ZmFhNWRjOWI3ZjMwYjc3NTZmNDAxNDI4ZjJlZTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmNlZGZiODkxMzcxMjc3NjA2NmU2MmQyMTU5MWNjYzQ0Y2I5MzlmY2U4MDRlYjZjYTUxZWI2ZTIxYTRmNzc1MCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjFjZmMzMDRmNzNkZTFlNTFhYzFmMDczZGI1YjUwYjEzNGMyNjhjYzRjZjIxNDIwZmVhMmY0NjM1YmMzZTllMzEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZmExZDEwNjBkNTc5NTU1YWRkY2IwZDU4ZGI4MjNkMDMzOWNkNThiMGUzOTdhNzJjZWI2NDgzZTY3NjNjMjgxYiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1OTA1MDg3YmU5NWE0YWIzMjY0NGQyZDYzYzE0YjE4OGU0ZTcxODYwN2M1YjYxOWRmNzQyNjIxZTY3N2Q2ZWFkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMzRhZTM3YmEzNjY3M2Q3MzM0ODhhNzlkMzZkZDI0NDBkNzZiMzBhMGUyMGNkMzU0YjYyOTU4ZTljZTU2NTA4NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMWM2NTM2YmI0MDU1ODMyMTMwN2Y2NzE1MTc4MjA3NzU2YTBlNzBiNTE1NzAxM2Q5MWIwN2MxMWVlODdjNzYyYiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyY2E4NjJjMDdjYzkwZTczYjA3ZmQxMmU0YjM2ODhjNDRkZDc5OTI3ZTAzOWQ0NWU5Njc0YzM0ZmYyMmMwYTUxIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", privacy: { obfuscated: [], }, }, } satisfies V4WrappedDocument); -export const SIGNED_WRAPPED_DOCUMENT_DID = freezeObject({ +export const WRAPPED_DOCUMENT_DID_OSCP = freezeObject({ "@context": [ "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", }, - ], + }, + validFrom: "2021-03-08T12:00:00+08:00", credentialSubject: { id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", type: ["DriversLicense"], @@ -177,36 +261,10 @@ export const SIGNED_WRAPPED_DOCUMENT_DID = freezeObject({ }, ], }, - proof: { - type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: "assertionMethod", - targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - proofs: [], - merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - salts: - "W3sidmFsdWUiOiJiMzAzYWIyNmEyNjI1MGQ2YWNkMmI1Yzk0NmY3NDdhMTdkOTRlZTZmZjVhNDE2Mjk5OTQ4MDA0Y2EwNWE3MTBiIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjBiNGRmOGVhZDkwMzMwNjMyYjhkNWNkYWVjOGRkZTI0NzQ0NjFkMTE2NzgwNzU4OTRiMmUwY2JmZDQ1M2ZlNTUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZGE2NDE1MGViNzViYzY2NzdkYTkxYTFhMTk3YWUzMmYwMDBlN2M3OTEyN2Q2M2EzMWRiMzg5MWQ3YjQxNTYwOCIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6Ijc4MDhkZTQyZjdkMWZkNzE4ZDFhMmRhMDUzZDA4ZmQ2Y2JjOTliZDU3YzhmZTQyN2MxNzllZWQ1YmRlY2IyMTQiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2ZDM1ODU2ZTRkMjg1MjllZmIyZjE2ZmRjMDFlMWE5MjQ5NDlhYzE5NzkwYjAxNmJkM2EyOGUxNjg0Mjc4NzNlIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiI2NTQ5ZTkzOGU3MjgxMTM3NzZkOTViNTdhOTg1ZTY0MmFmYmNhZWMzOTg3ODhlZGIzNmZiZDMwZDY4ODMxZWRhIiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiZDVhOTMxMTk5ZjVmZjBiM2MxZjQ3NjhlODNiNWNiODVmYmM0Y2Q4MDY2YmM3OTlkZTVlYWFmODViOWNhNjM4ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTM3YmU4NzU2ZmU4YWUxMGI4ODI1ODQ5N2QyZjBkYmZlZDcwN2U5YTZlMzE1NDJiYjBiZGE3YjFhOTNhNmJmZCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI4NGUyOGZhMWM0MTQyYjg5M2ZiMTNkZjJjYTQyMTkxOGQwODEzNzNlYjc2ZTdiOWU5YTUwMzBmYzJhODBhNWZjIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjdmYTQ0MDRlZTdhNTJjNGI3YWM3N2U1ODIwMDc2OTQ2YTU3MzNmMGJlNjIzOWJiZDUwNWZhYTY5MzVjNjkwMzUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYzcxMGRjZDdjMjNmODhhYWY2ZDJjM2Q0ZDgxYzg3NzkzODcxYjI1NjI4MjJjN2YwNjliZmM4ZTA2ODdjNzRhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImI0NWU5ZjA5Mzg0NTVjMWY1YjhiMzJmN2E1MWZhMDkzOWYyYjg1ZmM4YWFkZDQzMzU2NjdlNzFjYzY2OGMwYmMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjcyNDE3MDVlNTY5YjdmNjVlMmM1ZWZlMmYzZjIwYWVmMDdlMTdhZTIzNzI5NWRkYzJhYWM1MDAxZjI0YjAzNDkiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcyZDdlMmY3NTE4ZGZkYWY1N2JmMzI1Njg0YTNjY2Y3YmI2MDFkNjI0NGE0YzZmNDVhMzJmOTY5NjBiOGI1YiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn0seyJ2YWx1ZSI6IjNhODI4MTc3MDQxZDI5MDk4NjkzNjhhZjQ3OGE2ZjJkYjU2MWIyYWQyZTY1YmYyNzlkNjE2MzAyNjc1MWJhMTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODgyMWY4YzI3OTlkMDVjMmFmODBlZGZmYTc5ODQxMTFiZWM4Y2Y2ZTU1YWZiOWIxMWY3ZGE0YjU4NDE1MmRiYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjBhMjUxOGExNzExMmE3YmY5OTY0M2FiYjc0YWI1ZTllZGViYmEzYzdlMGYzZDM5M2M5MGJjMGZiNDUzOWRmZmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIzMmZiN2JjM2NiZjFjMmZmYjcxYjQ2N2EzNWYyNTFmNGFiNzZkODA0MWUxYTNlMmQ4NzgwODc1NDBhZTQxYzIzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhNTRjNzRkMTYzNzMwZTNlOWRmNzYyMGRhMTllYjcxNjNjNGQyMDNiMDZhYTU0NzZmNzBmMzRiMDMzN2Y4MTIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmUxMWE0MjkyMjEyNDdmNTJiOTU0ZjYxYzc2MTI3ZGYzZWYzY2E4NTA0ZmZlNGUyZDk1NWFjOWNmMjBmNDU3NiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU3M2I3ZTNiM2E3OTA4MmQyNWU0OTA5YjU4MzdkZGFjNzRmZDA4ZjVlNjljOTcxZDJlYmViZGY5OWEyN2Q1MmYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjk1MzcxNWJjMzNlMTAzNzBjNGQ5MWUwMTZmN2M4MThjZWRjMGI1ZGRkMmZiODhmNGNiNGIzZTlhMzMwN2ZiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJjM2NkZWNiZjNkOTgzZmUxNDRhNmI5NTJkZTY4ZmExYjUwZjUxOTQwZDgzMjY3MjQ5MTg1YWNmNTFiNmI2MDQ5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifV0=", - privacy: { obfuscated: [] }, - key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - signature: - "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", - }, -} satisfies V4SignedWrappedDocument); - -export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = freezeObject({ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", - name: "Republic of Singapore Driving Licence", - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, + credentialStatus: { + id: "https://ocsp-sandbox.openattestation.com", + type: "OpenAttestationOcspResponder", }, - credentialStatus: { id: "https://ocsp-sandbox.openattestation.com", type: "OpenAttestationOcspResponder" }, renderMethod: [ { id: "https://demo-renderer.opencerts.io", @@ -214,87 +272,19 @@ export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = freezeObject({ templateName: "GOVTECH_DEMO", }, ], - credentialSubject: { - id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - type: ["DriversLicense"], - name: "John Doe", - licenses: [ - { class: "3", effectiveDate: "2013-05-16T00:00:00+08:00" }, - { - class: "3A", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - ], - }, proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + targetHash: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", proofs: [], - merkleRoot: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", + merkleRoot: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", salts: - "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJjY2E1ZTExMzdhYmY3OGYzMzViMzNlNjA3YmZmNmU4YWZkM2YyZTJkOTc2YWZiOWM1OGE1Nzk4YmEwZTllYmVlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiYmY2YWI5YzQ2ZTVmZjBkNzM1Nzc2MjUxOTQ1NmFlYmI1YTU1NGJjNDg0NTRmN2EyYTU2NTdiNGIyODQ3ZTFmMSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5jbGFzcyJ9LHsidmFsdWUiOiI1NTJmOGQ0Y2MxMjRmYzIyYjQxM2I4YjYzYzBhZjcwZWI0MDgzNDYxOWMxNDRjYWZmYWI3MDFmMGE1MzYzMmJjIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImJhZWE5NWEyYmVlNTU3NWRlY2UzM2ZiZjFhODBhZWYxMmY2NWExYjY4NGRkMjQ0ZjZmNThmMjJmYjJiYWQ4N2UiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZWZmZWN0aXZlRGF0ZSJ9XQ==", - privacy: { obfuscated: ["c866663b38353fdb46a372cd0302fd7752cc75bee4b8fc4d65c2e3b22f2466f7"] }, - key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - signature: - "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", - }, -} satisfies V4SignedWrappedDocument); - -export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = freezeObject({ - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", - ], - type: ["VerifiableCredential", "OpenAttestationCredential"], - validFrom: "2021-03-08T12:00:00+08:00", - name: "Republic of Singapore Driving Licence", - issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", - type: "OpenAttestationIssuer", - name: "Government Technology Agency of Singapore (GovTech)", - identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, - }, - credentialStatus: { id: "https://ocsp-sandbox.openattestation.com", type: "OpenAttestationOcspResponder" }, - renderMethod: [ - { - id: "https://demo-renderer.opencerts.io", - type: "OpenAttestationEmbeddedRenderer", - templateName: "GOVTECH_DEMO", + "W3sidmFsdWUiOiI2ZTJhN2E4MjM1NzMzOTM2MGJkYzE0OTYzMTUwNzAyNTg2N2QyOWYxM2YxYjMwZmFmMDQ4Y2VmM2QyMzc2YWM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImRkZGI5MDVmNmIzZWQ0OGVlMzkwZDljYjQ4MTdkMjk4OWMyY2VmMzQyM2UxNGE4YjQzMDMzYmFhNDM0NmY2ZGUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWNiZjBjYjY2ODZkNWIyMzJkM2QwZDZiNTk1MTA0NDc0OGY4NmNlMjJkZDE1OGIxNzUwZTc2YWUyYzAzZDkwNyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImJlZjJmZjNhM2MxNDEzMDYxMDVkNGMzMTJlZmZkN2M2OTUxN2U3ZjM5YTBjYTQ3ZTZkZTk4Y2M1ZTFkMzllY2UiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1N2M5YWIzYzRlM2RiNjE5Nzc0NjZhNjQ4ZmM5ZThjNjI2NzQ1ZDE4NmIyYWRlZDIxNDhiNDQwYWU0MWRmYjhiIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZmYwNDE0MjcxYWYwNzk5ZWM4YzgwMTQzMzI2NmVmNGE5YzAwNzQxMDVlNTNmMmQ3OWYwYjYzYjBjNjk1Y2Q2MCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiOTEyNjc2YWZjZTkyYjBhNGU4MzhkNzZjZDYyZmNmMGY0YTc1NmE1MTI0NWI0YWU4YTQxN2ZkNzYxNmVhMDVkMSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmNDYzYmNkYWIzYTcwNGYzMWRhOWQ5MjUwNTM3OTE1MDBiYTdjMDQzZTNiZDEzNzU3ZTgyNTI5MzYzZGNhNjc0IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc2YWIzZWQwODk2NzZkOGJmM2FhZjE3OGQyYTUzYzI3NGZjMGUyMmFiMjJkNjU3NmIxYTUzYTAxMjMxNjUxYjAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNjMyYzI5MzdhZGYwMGIwYWU0NGRlYzZkNGI1NmJmN2RkYTU3ZDEzYjk3MWUxNzhlZjJiZTdmNDMwNDJlMTI4YiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkxOGNiYjU1MDg4ZGFjNDEzMzYxODEwZDE0ZTBlM2U4Mzk4N2Y2N2NjZDU1YTE5OGQ2YzZhOWEzYWM4Y2VkMDQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImI5NmVkN2FjZDZjZjQwNmViZGMxZmJlY2VhY2M1ZWYxZDhkODlmZDNhZTZmZTRjMzkyMTM3M2NhY2YxYTc5NDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTVmNDFmYWZiYjNhZmY4NmM4ODU0ZTRjOWQ4MTAzY2E3NGM0NTBiODg3ZmQwOGMwNDFiYzI4NzlmN2RiMDAzYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjU2ZmU1YjZmZDYwNjE0OWZmYzhhZGZkNjY2ZDgxNGJiYzZjNWMzZjU0YjA2ZmQ4YjJjZTFmNDc2M2RkNWMwNmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJmNWZhM2MyMDhiODRiNDdkZDczYTIwYmM5YjczYWY1MTI3MzIwZGUxNTIwNGExMjA0NjgzZDI0NGUwNzhkYmU5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjkzODcxZTQ2MmEzZjIyNDE1YzU2ODhhOGQ3NGYyZjFlZjMwZTEwYWNlOWZiMmIyNTllOWVjMmVlZWVjMWI2NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiN2FmNWFjNzViYjNkZWYyNTQwOTViNWE4ZDFiNGFkZmJiZmQ3YTM3MTY4N2UzZGY2OTJmYjhlMmM2YTMyZjZiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjAwZDA2MTlhNzQ0ZWUxODIwMzQ5NzZhZmRjZmU2ZjE5ZDE1NWZkMjgwZDQzYjM0ZjhjNDM4OGJiMDQwOTBkOTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiOTUyNTdkMjJmMGMyYzRkNTY3MjkzMWM0OGY1OTBjY2RmMGU4NzNmY2ZmMTdjZDA2NmRmNjBjNzMxMzgxZGMwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI3ZTZlODE5MmM1OWVhMDllOGUxZDM3NTI4NGEzN2JhMTFkNmFlNTVmNmFiNGIwMjRhMGU4YjM0M2MwZTlhMWI4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMTRmN2U1ZWU4Zjk0ZTAyZDFiODcxYjE4YzRmZjAwOTA0ZDc4OGNhMjU4NDhlYjVlOTMwOTZmNTFhMzI0NGIwYiIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjFjNzRmODhjODNhOWZhYWQ5YjU1MjE4NjcwZDg5NzM0NTY0MjY2OTg5MzdlYTQyNmFkZmY4ZmVmOTYzMWVkNzMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjA0N2I1YzA0NDAzMjlmM2ZkODZmMDQxODRmMTk1OGZlZDQyNDhiMDI2YmM2NGI5NDM5MmNiMGRjYmU5M2VlYWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjQyZGM4Y2EzZGJiMjI2NGI4ODg0OWIyNWFkZWQ0ZWE2NWQ2ZmMzYTZlNWUyNjExMGE1MDUwNmRjYzdhYzhmNzciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiODg2MjJkNDRjMTBiY2VlMzUxMGRiNmE4ZjNhMGYxM2M1ODBmNmZlNWY3OGNkODVmM2FjYTIzMGE2M2QyYTZjMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + privacy: { + obfuscated: [], }, - ], - credentialSubject: { - id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", - type: ["DriversLicense"], - name: "John Doe", - licenses: [ - { - class: "3", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - { - class: "3A", - description: "Motor cars with unladen weight <= 3000kg", - effectiveDate: "2013-05-16T00:00:00+08:00", - }, - ], }, - proof: { - type: "OpenAttestationMerkleProofSignature2018", - proofPurpose: "assertionMethod", - targetHash: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - proofs: [], - merkleRoot: "a9651b2c97ce35ed512ccce6e39cd575a964c82d2f7d311e3aaf2722147f67d2", - salts: - "W3sidmFsdWUiOiI4MWI1NmU5MjQzNWQ3MTQ4ZDE3MTYwNzJmZmEyYTMzZWUwZDhkMjcyMTIwMGE0MjhjMjU4MTNlNmJhYjk3OGM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFjOGVhYWVlZDdhNDE1ZjY4MzA4ZDRhOGM5ZjJhNmJmYzRmNTQyNTlmNjRjY2ZlYTMzNTk2N2JhYjMyMjJlYTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDUxNWFlNjEzZDYzZWY3MzE1NzhmNmM4MmY0MDczMTJmNTg4MGRmY2UzMWU5MmIwMGUwYzY0MDVmZWIxMTZkZiIsInBhdGgiOiJ0eXBlWzBdIn0seyJ2YWx1ZSI6ImQxZDQ4NzMwNTI2ODQ5NDFjMTU3ZjYzMWJjYTNmODRmY2Q0ZTFjMzRlNzk2MWJkYTBiY2I1OTlhZGM2ZjY3NWIiLCJwYXRoIjoidHlwZVsxXSJ9LHsidmFsdWUiOiI2MDIwNGI2NTA5Mzk0MWEzYmQ4MWI5NGQ2YTYxZjhkNzgzM2Q1MGQyYjQ3OWJiMTY5NWUzNmI4NTMwODkyODYwIiwicGF0aCI6InZhbGlkRnJvbSJ9LHsidmFsdWUiOiJhMWQxMWYwYjEwNDI5OTM1N2Y1OGZlYzI2Y2JmYTkwZTllN2NjMjYyZjIzYmJhMTcwZDU5YzQ3NThiMTBmYTE0IiwicGF0aCI6Im5hbWUifSx7InZhbHVlIjoiMWM1ODQ3MmFjZTE5NWFmZmVlZThkOWU0YmNiMWRlZWIxNzhkZDFiZmE1ZmYwMWJhZmM5ZDNiMjRkZjE2ZGNlMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZmMzM2M0MzYzMDRkM2Y0NTJhMWU3ZDk3NWVkZjJlOWJmZmM5NTQ3NmE4MmJlYjNiOGVlMDg3ZWMxMjVjMjE3ZiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI0NjVlYzZmYTBkNmJlMzliYjFiODliNzY4Y2VlMTFhMzc3NGRmYTBjM2NjN2MxM2VlMjg4NDc0MmM4NmQyZmFiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjhlYTI4M2QzY2RkMjI0MjM5YmE0YjBiNDEyNGJlMmI5YWQzMmFiMDAwYWMzNjkyMjVmZDhhZGUwMzU0ZjdjY2IiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiM2NiNDU3ODBkZWY5M2Y5NGY2NzQwZGU0MDBkYzFmNjY2ZmE0YjdkYmQ1YjQ3NmJmNjFkNjUzZTQ3ZmY2YTA1NyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImZlNzZjNWM2MGQ4YWI4ZDE3NTAxY2Q5YWVmYzczZDk4YWE0MmI0YzUzM2YxOWQxMTUxNTRiMDhjMGIwOWU1ZTkiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy5pZCJ9LHsidmFsdWUiOiJiOGFkYTIxODZhZGY1M2U1ZDg2YmQ3MjI2OTJiODU5ZTZkOTUwYjVjY2M3M2ZkMmFlYTUzZmRhYzQ0NWNiYTczIiwicGF0aCI6ImNyZWRlbnRpYWxTdGF0dXMudHlwZSJ9LHsidmFsdWUiOiJkNjlkZTZiMDZlM2M5YzA1ZDhiYzBiZTY1OWQ1YTY0MWNjNzEyZDIzNGU2ZDkzZTFhZDg2MzAwZjIwYTEwYzk0IiwicGF0aCI6InJlbmRlck1ldGhvZFswXS5pZCJ9LHsidmFsdWUiOiI2YWY1ZWY4ZTliMjFjZDU3NjNkMTg3NGM0ZjIxZTFmZmVjYmEzNWZiMDRhZWFmOWIyYWFjNmMzOTAxYmRkNjFjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50eXBlIn0seyJ2YWx1ZSI6IjQ0MGQ0MDI4OWM1OWYyNWZkZTcxNDBkOTMyNWIyZWJiYjE2MWVmNmM3YjUyMmVmNDg0OTVhN2FjYTRiNDcxZmYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnRlbXBsYXRlTmFtZSJ9LHsidmFsdWUiOiIzY2Q0MGExN2U0YzE3ZmRjNjk4OWRiMjRhZjBmNjI3NzQ1ODU5OTAxZmI2MzBlZTdiMTU5MTQ1Zjg1YTVjMzI1IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmlkIn0seyJ2YWx1ZSI6IjhkMTFlMTg3YjY0MDAxNGUwNzA3Y2M4YjM5NGE3ZTE2ZTAyYTQyOGVlZDhhOWRjNjQ5YTI3MTg3ODYwMWRlNmIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QudHlwZVswXSJ9LHsidmFsdWUiOiIyNmFlYmU4MGI5Y2U3YjBiNjVkM2E2NTVhMmNkYTE2NDI3MTkxNmMwZjRmNTE3YTJiMjc5MDg3NGQyYzRkYmZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0Lm5hbWUifSx7InZhbHVlIjoiODNlZDJjMmQ2OThkY2QxYTBlNDY2N2QzYWYyMjNmMWViY2Q5YzMxY2U5MDlmNTk0ZmRlOTdkYWEzNWEzY2QxNSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5jbGFzcyJ9LHsidmFsdWUiOiJkZjNmMjY5NzE5YTczYzA1NDc4Y2Q1YTk0MTQwMDVmMjA2NWNhNWNhODg5NGUwNjc2NDllYmEwMzcyNjBmNDgzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmRlc2NyaXB0aW9uIn0seyJ2YWx1ZSI6ImNjYTVlMTEzN2FiZjc4ZjMzNWIzM2U2MDdiZmY2ZThhZmQzZjJlMmQ5NzZhZmI5YzU4YTU3OThiYTBlOWViZWUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiJiZjZhYjljNDZlNWZmMGQ3MzU3NzYyNTE5NDU2YWViYjVhNTU0YmM0ODQ1NGY3YTJhNTY1N2I0YjI4NDdlMWYxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6IjU1MmY4ZDRjYzEyNGZjMjJiNDEzYjhiNjNjMGFmNzBlYjQwODM0NjE5YzE0NGNhZmZhYjcwMWYwYTUzNjMyYmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYmFlYTk1YTJiZWU1NTc1ZGVjZTMzZmJmMWE4MGFlZjEyZjY1YTFiNjg0ZGQyNDRmNmY1OGYyMmZiMmJhZDg3ZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn1d", - privacy: { obfuscated: [] }, - key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - signature: - "0xfa3bb0c734fd8853b58447cbf64c87198bf8dea6e5da81a325dd74b0105972be77944ef532f45fdbfe463c061462c7cf468025cbdf772200cbf37c76934448ee1b", - }, -} satisfies V4SignedWrappedDocument); +} satisfies V4WrappedDocument); export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ { @@ -305,7 +295,7 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -341,11 +331,11 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "e7fbcdadb66fa5561d1891185f8c16fb80e4fdd04d5fbbb7f49d66426a699b18", - proofs: ["a23257bff49b375448b6a7657c6d6fd16000d2f29a54673030b0c9c66cdeb79f"], - merkleRoot: "ac50e4ffdca1256e5771ecc3f266f5f72d1c9f3686b387811628ad0c28f16836", + targetHash: "08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681", + proofs: ["ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001"], + merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", salts: - "W3sidmFsdWUiOiJjZjM2NWE5YjlkN2ViNjg1OTQ0ZTkwNGM1YzM1MDg2MGU5ODkxODI5ZGQ3N2NkZWVkZTJmMzk3M2UyZWI0MjFhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImFkNzVmMDVmNjJkM2FkZDU2YWJkZjUxMjFmN2VmOWMyZDQ1OGYzMjFmMzYwMGI2MDc2OWE3MTc4ODk1NzRjNGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiODQ3MGU2MDhlMDdlM2IxY2YxMjlkMzQ2ZjVhZGQwZjg1NzQ5OWUxMDc0MWI0Y2NiZjdhYzA1MDVjNjdjZTc5OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjAyYWYwZjc1NzU2NDM3MDQ3Mjg2MGY4MzJkMjAxM2I1N2Y0ZGRlMWZhMzhhNDhmMzZjNjE2MzgwNjhkMjZmNDYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI4MDFhN2JhODI1N2Y2NTEyM2JhN2ZhY2Q4OTY5NTBhNmE1NzNhM2RmMDFmY2M0ODFiNmI0OTMxMGYwYTBjYjM0IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzRlZDM3NjUzNDdjYWE4OTg1ZTdjODU4NmNmODU2M2QzNmNkMGE0Y2Y0NmEzYTRkNjdiZTIzOGJjM2IwN2U1YSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiNGQyODdjNGZmZDM1MDliNzg4MjUxMDA5Zjc4YWQxNjkwNWRjMzQwOGJlYmJjNDRjNmI1YzFjMjg5ODdhZGVhNCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI1ZjBkOTUwYWJkNGRiNDFhMTdkM2QxY2Q0ZjI2M2ExYTYwNGZlNzVkN2U4MTkwZDRmN2I4Yzc4NjZjMDNjYWE3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImVhM2IyOGM4MjA1ODNhNDNiOTU5ZDE2YmEzNzI0ZTgwYzMzYTYwNWFhMTNiNjBjMjAzYmRkOTExMDllMWQ4ZWQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNmJiOTk1NmQwYjI3Mjg4MTE1MjhmOGRhOTg4ZDIzNGQwNTlkNzAxYmM1OTc5NzkzODlkMjliYmYyMzMyODA5ZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImU3NmM2ZDAxMjBiZGI5NjgzMzc5MzdmZTYwYjgwMmI1OTZjZGQyYzk1MGFhM2JkYmM0NjlmMzkwNGM5YmFlNzQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImVkMGVkYzMwMWQwOTQ5MmM2NjVhMDU0OWIyZjM2OGYzY2JjZWY5ZWIwODg4ZWQ4NGZlNTA1MTc1MjAxYzM3MDQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZmUzMmEwYjVlOTljNjZmMWYwZmI0OTQzNWNmNzdkNmZjMDBhMzFhZTQwNDJjYzEzMzdjMTNjNzNjZDUxOTY5MSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjI4YjZmMzViNjk4Mzg1ZmRiYTZlMTFkZmJhZDQzYTEwZDc1OGExMGE1ZDEwODMxY2ZhMmYyYWQ2MzI2NTA5MzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyOGY2ZGE0MmUzMjYyOTE0N2Y4ZDVhZjg3NTRlOTJiZWEzMDZkZWQzNzljYzhhZThiYzcyN2Y4MDMwZmJiY2MzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjViMjg3YTIxMGE4NmQ1ZTFmMGI2ZmY3Y2VlZGYyYzBiZDI0YTNmMjQwOTNiMTY1ZjRhZWRlNTliZGU4ZGUxZjAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiOWQyOGRjNTcxMGJlNWMzMjVhMDQ5YmMwODU2NjZiMjdkMDM2OGU5MjBkZGNiOGExZDUwN2I3ZGJkMDcyZjkzZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjJkZjVlMWZkZjFiY2JmOWM0YjJiODYzYmZjODEyNWZhYjAxOThiMjQxNTA3NjdjNzNmYjZmMDkyZDYxYjQ2OGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZjY5NjMxNDhiMjIzY2EzMWFkYWJhMTBiNDQ3ZDhkZWViMzE0ZjY3ZTdkN2E0MmRlNzIzYTg1YTY2OTJmYTY2MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI0YjAyMjQ3ZTVjZTk1YzE1NDZjNTVmNGQ4OTNkMWY1YjE0MmViYzE3ZTM2OGI1OTM3N2UyMzAwZDQxMjRjM2ZlIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiOGE4MTZjOTY0MTY5MGI1Yjg2MjdkYTA0MDRmYzBmMGY1ZWViMjM3NDA1ZjUxY2IwOTY2YzhiMmRhZDI1NGM1NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNjgzOWYzZmQyNDg1NjU5ZmFhMDIyMzBkMGE1Y2E0YmIzN2NlYTFmMGZkMzRiNjg0MGM3M2QyN2ExMWUyZjU1NCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyZDI0NzUyODZjOTk0ZmY2NmFhZDNkNTRlNDQwOTg4ZjdjZjliNzIxMDRjNmVkNDdmYzFjZjBjMzlkMDEzOWFkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "W3sidmFsdWUiOiIwMzgyZWU0MjFlNTgyMzJlMGJjNTI0MDY1NjhhZTdiNWZhNDhlOWRjYzczZmVkM2VhOTZkOTAzNGQ4ZWZkNDlhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImViMmI1ZmIwODhlZDk5YzY0NzJmYmQ4MjMyNjVhYjlkMzZhNmQ0MDFiMGEyNmM5N2Q3Zjc0MDkwYWJiYTE4OGEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiYWNmNGFhMGJmZDEyMTRmZGEwZDAzNzczNmNhMDFiMzhkOTBlZDA4MzQ5ODUwMWY5NDlmOTgyNjg1NmE2MTM3OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjM5Yzc1MjFmNDdlNTJlNWZlOGI2NmEyYjhjZmEzYTQwNDM1ZjQ2Y2NiOWY3MGQxYWY4MThkNmNkNzk1MzQ4MzQiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiNWY3MmI3N2M1NjU3YjZiOTMyN2QyMGYwZTUwYzQxMGVmNzRlNDMzYTM0NTRlOTRmMTEzOTZmMTIwYzJjMjc5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiOTU1MjExMzNmNzgzZjI5NzlkODgyMzI4MDlmMDgxNmUzNDVmMjE4MTk0ZTJkMTgwYzZmYTcwMTBhNDI4ODFkYyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTQwZmVlNjI5Y2M2MjY3Njk1NzA4YzY2ZTg2OGI0ZjVhZTBiMDg1YjYzNDczM2I1YWE1N2M4ZTQxM2I1NTliNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJiMjFmZTkyMDNlNWYxNzFjMjE1NDIyY2Y2ZTZkOWEzNzFkMmIyZGJkMjU5ZDE0Y2JmMDZiYTllZjQ4NzEzYzk3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImE5Mjg0OTFiMzYyNzcyMjUxNzM0ZDEwNGQwOGJiMTgzMjczM2JlYzZiYjdmYWI0YTE4ZTdiNGRlY2QzMjE0YmMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNWViMmMyNTU2ODgyZjgzZjM0MTQ5MTczMGM0OWI4NTE4OTljMTA3NzY2Yzk5YWQwYzhhMGRkZTQxNDkzZjYxYyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjVlMTgyNThiODdjMjJiMGIzY2Y4OWVhMjZhOTI5N2ZkYjVmYjI0NTA2Mzg0MzNlYzcxN2YyYTBmMzE0NTJlMmYiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYWU3ODllZmVjZmE4ZmEzMmZmNTdlYTNjYTJmZjJjZmVmOThmOThmZGVhY2U4M2Q0NGEwNTVjZGU4MDE2MDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMGQ1ZWI3YmY3OTE1MmI0M2EwMTQyYjhkNTVkMzRmMzRlOGQ0N2U5MzgwMDVhYTVjMTRjMjk2MWNiMjM0OWFkYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImIyZWM0NTYzMjhlMzhjYWYyYmNlYTk5YWM5NTc0ZWEzZGExNjJkNmJlZDVlZTRkZGM3MzI5NjhkMmIwMDZjZGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3OTIwNmQwNDNlNjVhYmU2MmFmYmIwNmViNGMxMWNmZTg2NjkzNWRhOGYyYzkzODI4NjVlZjg1YjVhMzc1NTUwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAyYjJjMTlhNDY2ZjNiYWU5ZDhmNWI0NzUyZGEzZmU2MWM1MTIxYzc1Mzc0N2I1NzNmNTdlYzBmOGExODIzZDciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTNiOTA2MjdlNWY1OTM4YjdkNWY0NzhjM2JiNDE4NjkwOWI1OTY1MjVhM2I4NWNlZWU2M2JjNGYyZGQ1ZmRkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImNhOGFlMzIwZjU5NWJmMmUwMmQ3MTAxOGE5YjA0YWI1MDNiOGJiMTIyM2I2NDM5MWNjYTZiY2MyOTk5MjdmOTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYjk1MzhlMjkwNzdlMzg1MzlmN2E0MTliZGMzNmFhYWVjMTMyNGY0MjQzOTA2YzIyN2JmMzhkZDU4NTA4M2NlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyN2RmYWY1Y2M1NzJlMTFmODhkMDFmNGUwMzQ0ZWRiMzA4OWQ2NDc5YmY0ODIyODBiZmNmNGE4YzZjOTY1NmIwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTBjMjFhMzQ1NTZjZTMzNjViOTE5YmUwNjlmODVkM2EyNmQ2MTcyM2Q4YjlmN2QwZjY5ZTkzMjMxZDY4ZGM2ZiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiODY4YzNjOTYwNGJlYWMxMDZiZDQzMTc3NDRjYTNjZTI2YzU3YWFiODc2YWUwMWQwZWU5NzNmYWE1NDNlNWRmOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJiNzY3YTcyMmUzNmU4MjAxYWNjMjg2MTAzYzBkZmY4Y2I2MjI4YWQyMmVkZWJmMjc4ODcxOTQ1MDVmMGVlNmNkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", privacy: { obfuscated: [], }, @@ -359,7 +349,7 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90", + id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -390,11 +380,11 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "a23257bff49b375448b6a7657c6d6fd16000d2f29a54673030b0c9c66cdeb79f", - proofs: ["e7fbcdadb66fa5561d1891185f8c16fb80e4fdd04d5fbbb7f49d66426a699b18"], - merkleRoot: "ac50e4ffdca1256e5771ecc3f266f5f72d1c9f3686b387811628ad0c28f16836", + targetHash: "ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001", + proofs: ["08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681"], + merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", salts: - "W3sidmFsdWUiOiJkMjQxZjc3NWNkYjNjOWRhNjI4MjU5M2ViYmY2NGMwZDkxMzM3ZTY1MmU5MWIyYTQ0NzEzNTY1NjdiN2NjMzI5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImQ3NDMxZWIwMWZmYTE0MmEyMThkMjc3OWE3YjY2MGJjZTYxMzRkMTQ3NTUyNjA3MDgwMjc3OGM4OGYyNzczNTciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZWZmOWI2ZWFmNzg2OWM1OTgxNjM4NTIyYjIxNjc5NDQ4YWIxNmQyM2YzZjZlZjI0MWY5ZWM3M2ZiYjE3YWU2OCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjlhZjY3MWYyYTUyZTIxMTk5YzU1MDE2ZTlkNzNlN2E2YTBiNmEzMjI2Yzg3ODIyMzlmNDhjZWM3MjZlZjE4YWMiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiIwOTYzNDFkMDA1OTU1ZDg2Y2IwZjVjOTJmMGFkMWU5NWIxOWJiZmRhMzk2NmYyNTJiMGUzZGY0MGFmMzYwZjBjIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNjY2MDI2YzkyMjYyMzBlNGEwNzliNWMyZmY0ZWMzMzgxMzM1YzI5NTcwOTZjMzFjZWVjN2ViMmM3OTY5NTA0ZCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZWE2ZmRjYzc2YjI0OTBkNWE0ODg3ZWFiMTJlM2M4Mjc1YzdkMmM3ZDcwYzdhZmRhNWZlNjVhOWYwNzQwYzY0MSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJlYzhiY2ZlYWFkZDk0NjAyZmU0ZGZkMjBiMzgwNGE1M2ZhZmRkOTI4ZjA0NTQ4NzkxZTg5NzNiYzc4MDI5NTM3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImI1YzdmODYxOGQ0ZjgwNGI2MmQxYjkyY2UwMTJlOGRjOWVjOTY5M2U5NGEzY2Q2YWJkOTBlZDNkZjBiOWVmNDUiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNzRjNjgzZjhkNmY4YzAzNDNmMTM1MjVmN2FmMWNjY2RjZmVhNTlkNWYxYjRhOWVkOTRiYmU1NzE4ODkxMWQwZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImE1ZTg4MmU2ZGY0MjdkM2NlODBiY2MyZjNkYzI5NmU1MGU4MTZhMGU0ZTEyMDZlYzgwNGNhNzQzNDY3NjVjYmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjU2YTUyYzFiMmE4ZWE4NGQ3NzZhZDBhYjMzODM3ZDdkZGEwNGZiODYwODJmMzU0NGEwNzM4YTVjZjBlYTYwMDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZjFiZjI4MDFhNzcxNjZkNzZkZDJhZjFmZTM2NGZmYWQzNGViYmNiMmRjZTVkODc4ZjZiYWE1NjJhMGMxNGY2NCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImFmMTA3NjBiYjIzOTBmNmJkYjFiODgwNjViNTgzZTVkZTJkMWEzNDczNTFjMDYwMGU5MDRkZWFlNDA1MTA0MWQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlNzRhOTM3MTg3ZDY1NDQ3MWYxMzMxZWU4MTcxYjE2OWQ3NTgxMWZmMWRhNzNhYzNiOWQ4ZWNmNzUwODk5M2VkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjhlNTE5ZWEzNjQwNmY2YTdkOGNmMzVhMTdmYmI2YTVkMTNkOWU3YThlMjJkMzNmZmMwOGNhN2U1ZGZmZmZjNGMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMDgyMTg5MmRjZmUyNTc0YTc3MmVjYzc1OTc5YzE2NDBhZjg1MDBkYjU4NzAxMTlmMGQyZmE0NjMyNDYxNmM5MyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjgwYzM4OTg5MTY0Y2ExMTRiYjMyNjJmNDFkOWRiYjU2MjdlNTFhMjlhMmFlOWJmY2I5ZjM0M2M0NTc3OWRjMzAiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImVkNTZhZTMxMGZlZDlkN2E4ZjBhOGZiNzhlNzY4MWJlYzkyNzBkYzYyMGIwOGYzZDU3ODI0MTZmYmVkNzA3YWEiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiZTI4MTdhZGFlOWNmMzgzYTE1OWNkNmE0ZjQ1YjY2NjZhNTVmNzQ5ZTRmYzY0NzQ2NDcxM2RlZjQ4N2UwYWExOSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "W3sidmFsdWUiOiI1ZGU5NjM1OGRlZGVjODIwMWFhYTJjZjRkOGI4ZjYyNzA0NDc2ZTRkYzdiZDQ5OGEwOTc5MGI1MDc1NzY0NWQyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImI2YzkyMDc4YjM5OTU3NmM1YmNlZjVhOTBjYmMwMzQxNjEzN2UyZTBjODAzM2JhM2FiZDNkYTU3NjEzZGYwZGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNjM0Nzk4NGNiN2Q3MDA4NTczNjJlYmFjZTgwMDEzZjQwODA0NzNiMDkyYjAyMzQ3ZDY1MGUwMGM4MGRjMTQ5ZiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImVkNGJiMDQwMWQ0NDY0NTljNjQ1YWUyMjI2MGQ5M2YxYzllOTkwYzg3ZGZhYzM2YjI2N2QwMTVlZTM4MDRlMWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1NTU4OThjZmE3ZWUxZThjY2VmOGIxYWMyYjBjY2RlNWJlZmFkMDE2NTJjOTY3NmVlM2VlNGExNTNmODlhM2JhIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNmVmYjc3ZDY2ZDQ3MDcxNzQ5NTIyOTI4MzE1MDI5OTlkYjViN2FmOWQ2NjQ3OWRiYzFiMzlhZjhiNmI0ZTU1NSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMmNjOGU1OTcxMDFhYmE0NzIyYmYzZGY0MWQwYjYyNjM3MGFiNzRhOGZiZjhjMmM2MTlmNDIzN2Y3ZWFhMzJmOCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmY2IxNDNjOWUwNmQzMTZlNzdlYmIzMDkxNDFiMWI5ZDE1YTRhMTQwYzk4ODFhYTY0OTY0MTQ1NDVhM2RhYzUxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc0NDVkNDQzMDdmOWU2ODBjNTFkMWYxZjEzYzE5YTU2NDFkNmFiNmMzYmE0NmRjMDFmMWIyZDU0YTE4ZWJkMTEiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYmVjYjY3OTBjMWQ1MmE2YmRlMjFkMzRjYTUxZDQ5ZGViNDBkMmE0MzQ2MGYyZjI4ZDA1NzY0YjVhNzFmODFhNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkzMzQ2ZDg0NmI1MzJkZmY0NTc5NGQ0OTVkNzk3ZDZmYzZkYTRjYTgyMzRmMzQwNjljODNjYTU0MWQwNzA4NzgiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImU5OTZiYTM2NTFiZjZiMGY2YWU4NDUyYjljOTM3NjBiZjRkYzRiNDYwNjZlNGZkYTQ0YzBiZTJkY2FhMmFjZTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZGIwZGQ0NjcyYWYxZjdkZGJiNGVhNTYwOGMzZjBkMjc5MDljMTAxM2FjYmJkZDcwOWVjMDZjYmQ0YTM3OGZmMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjA1MWZkNTUzN2Q0ZjViNzVmNWY2MTk2NmI1ODc4MDhkNGNlZTQ1N2EzOTNlMmQ0MWZmZGFjMzllZjk5ZjcwNTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3NTU3NzYxY2VhZTgzYzVmN2YwZTNkMmM4YjYxYzllMWVkZTNmMTIyNzVmZDgzOTIyNDNjNmQ4ZDNhNTYzNTM4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImQ3NzE4NTkxMDUwNjc1MDhlMGQ5ZTMxMWYxZDRhZTEyNmM3ZTNkMjhjMzY4OWUxNThhNjFjNmUwZGQxZTRkMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZWEzZmEzNDdjNGUxODVhNDhhY2NiMTY4NjMzNDY2ZmMyMjc4YjdkYmEzNzNiNDM1ODI1YmIzNDdiNzMxMDA3YiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVjNDZhODc5ODI0MGQwMGNkOTJkNTBjYzE5ZTVhZDE3NjUyYjJiZjJmOGQyOTlhOWVhOTNlYWE4YmE2MDgwNzgiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZhYTk2NWViNjIwMmNlNDhhNjczNzRiZTA5M2VhNTJjNDRhM2JhMzZmYmNkZjBhMWY1MzZjNTZkM2NmOGVlNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYjlkZTJiZDRkMGZjMmVlYmZkNDE1ZTcwZTRlMjYzY2Y5MDEwM2MyYzEyMTlkZDU0OGYzOWE4Y2I2NWNlYjU5YyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", privacy: { obfuscated: [], }, @@ -402,6 +392,183 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ }, ] satisfies V4WrappedDocument[]); +export const SIGNED_WRAPPED_DOCUMENT_DID = freezeObject({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + proofs: [], + merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + salts: + "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY1OTc2ZWJlMTE5MjY5ZjZhZjhlZjIzMGI0ZjgwNDgyMzA5ZmFhNWRjOWI3ZjMwYjc3NTZmNDAxNDI4ZjJlZTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmNlZGZiODkxMzcxMjc3NjA2NmU2MmQyMTU5MWNjYzQ0Y2I5MzlmY2U4MDRlYjZjYTUxZWI2ZTIxYTRmNzc1MCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjFjZmMzMDRmNzNkZTFlNTFhYzFmMDczZGI1YjUwYjEzNGMyNjhjYzRjZjIxNDIwZmVhMmY0NjM1YmMzZTllMzEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZmExZDEwNjBkNTc5NTU1YWRkY2IwZDU4ZGI4MjNkMDMzOWNkNThiMGUzOTdhNzJjZWI2NDgzZTY3NjNjMjgxYiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1OTA1MDg3YmU5NWE0YWIzMjY0NGQyZDYzYzE0YjE4OGU0ZTcxODYwN2M1YjYxOWRmNzQyNjIxZTY3N2Q2ZWFkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMzRhZTM3YmEzNjY3M2Q3MzM0ODhhNzlkMzZkZDI0NDBkNzZiMzBhMGUyMGNkMzU0YjYyOTU4ZTljZTU2NTA4NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMWM2NTM2YmI0MDU1ODMyMTMwN2Y2NzE1MTc4MjA3NzU2YTBlNzBiNTE1NzAxM2Q5MWIwN2MxMWVlODdjNzYyYiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyY2E4NjJjMDdjYzkwZTczYjA3ZmQxMmU0YjM2ODhjNDRkZDc5OTI3ZTAzOWQ0NWU5Njc0YzM0ZmYyMmMwYTUxIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { + obfuscated: [], + }, + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + signature: + "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c", + }, +} satisfies V4SignedWrappedDocument); + +export const SIGNED_WRAPPED_DOCUMENT_DID_OSCP = freezeObject({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + credentialStatus: { + id: "https://ocsp-sandbox.openattestation.com", + type: "OpenAttestationOcspResponder", + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + proofs: [], + merkleRoot: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + salts: + "W3sidmFsdWUiOiI2ZTJhN2E4MjM1NzMzOTM2MGJkYzE0OTYzMTUwNzAyNTg2N2QyOWYxM2YxYjMwZmFmMDQ4Y2VmM2QyMzc2YWM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImRkZGI5MDVmNmIzZWQ0OGVlMzkwZDljYjQ4MTdkMjk4OWMyY2VmMzQyM2UxNGE4YjQzMDMzYmFhNDM0NmY2ZGUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWNiZjBjYjY2ODZkNWIyMzJkM2QwZDZiNTk1MTA0NDc0OGY4NmNlMjJkZDE1OGIxNzUwZTc2YWUyYzAzZDkwNyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImJlZjJmZjNhM2MxNDEzMDYxMDVkNGMzMTJlZmZkN2M2OTUxN2U3ZjM5YTBjYTQ3ZTZkZTk4Y2M1ZTFkMzllY2UiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1N2M5YWIzYzRlM2RiNjE5Nzc0NjZhNjQ4ZmM5ZThjNjI2NzQ1ZDE4NmIyYWRlZDIxNDhiNDQwYWU0MWRmYjhiIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZmYwNDE0MjcxYWYwNzk5ZWM4YzgwMTQzMzI2NmVmNGE5YzAwNzQxMDVlNTNmMmQ3OWYwYjYzYjBjNjk1Y2Q2MCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiOTEyNjc2YWZjZTkyYjBhNGU4MzhkNzZjZDYyZmNmMGY0YTc1NmE1MTI0NWI0YWU4YTQxN2ZkNzYxNmVhMDVkMSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmNDYzYmNkYWIzYTcwNGYzMWRhOWQ5MjUwNTM3OTE1MDBiYTdjMDQzZTNiZDEzNzU3ZTgyNTI5MzYzZGNhNjc0IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc2YWIzZWQwODk2NzZkOGJmM2FhZjE3OGQyYTUzYzI3NGZjMGUyMmFiMjJkNjU3NmIxYTUzYTAxMjMxNjUxYjAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNjMyYzI5MzdhZGYwMGIwYWU0NGRlYzZkNGI1NmJmN2RkYTU3ZDEzYjk3MWUxNzhlZjJiZTdmNDMwNDJlMTI4YiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkxOGNiYjU1MDg4ZGFjNDEzMzYxODEwZDE0ZTBlM2U4Mzk4N2Y2N2NjZDU1YTE5OGQ2YzZhOWEzYWM4Y2VkMDQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImI5NmVkN2FjZDZjZjQwNmViZGMxZmJlY2VhY2M1ZWYxZDhkODlmZDNhZTZmZTRjMzkyMTM3M2NhY2YxYTc5NDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTVmNDFmYWZiYjNhZmY4NmM4ODU0ZTRjOWQ4MTAzY2E3NGM0NTBiODg3ZmQwOGMwNDFiYzI4NzlmN2RiMDAzYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjU2ZmU1YjZmZDYwNjE0OWZmYzhhZGZkNjY2ZDgxNGJiYzZjNWMzZjU0YjA2ZmQ4YjJjZTFmNDc2M2RkNWMwNmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJmNWZhM2MyMDhiODRiNDdkZDczYTIwYmM5YjczYWY1MTI3MzIwZGUxNTIwNGExMjA0NjgzZDI0NGUwNzhkYmU5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjkzODcxZTQ2MmEzZjIyNDE1YzU2ODhhOGQ3NGYyZjFlZjMwZTEwYWNlOWZiMmIyNTllOWVjMmVlZWVjMWI2NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiN2FmNWFjNzViYjNkZWYyNTQwOTViNWE4ZDFiNGFkZmJiZmQ3YTM3MTY4N2UzZGY2OTJmYjhlMmM2YTMyZjZiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjAwZDA2MTlhNzQ0ZWUxODIwMzQ5NzZhZmRjZmU2ZjE5ZDE1NWZkMjgwZDQzYjM0ZjhjNDM4OGJiMDQwOTBkOTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiOTUyNTdkMjJmMGMyYzRkNTY3MjkzMWM0OGY1OTBjY2RmMGU4NzNmY2ZmMTdjZDA2NmRmNjBjNzMxMzgxZGMwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI3ZTZlODE5MmM1OWVhMDllOGUxZDM3NTI4NGEzN2JhMTFkNmFlNTVmNmFiNGIwMjRhMGU4YjM0M2MwZTlhMWI4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMTRmN2U1ZWU4Zjk0ZTAyZDFiODcxYjE4YzRmZjAwOTA0ZDc4OGNhMjU4NDhlYjVlOTMwOTZmNTFhMzI0NGIwYiIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjFjNzRmODhjODNhOWZhYWQ5YjU1MjE4NjcwZDg5NzM0NTY0MjY2OTg5MzdlYTQyNmFkZmY4ZmVmOTYzMWVkNzMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjA0N2I1YzA0NDAzMjlmM2ZkODZmMDQxODRmMTk1OGZlZDQyNDhiMDI2YmM2NGI5NDM5MmNiMGRjYmU5M2VlYWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjQyZGM4Y2EzZGJiMjI2NGI4ODg0OWIyNWFkZWQ0ZWE2NWQ2ZmMzYTZlNWUyNjExMGE1MDUwNmRjYzdhYzhmNzciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiODg2MjJkNDRjMTBiY2VlMzUxMGRiNmE4ZjNhMGYxM2M1ODBmNmZlNWY3OGNkODVmM2FjYTIzMGE2M2QyYTZjMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + privacy: { + obfuscated: [], + }, + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + signature: + "0x1d889961f3b28e3433ee56e1d3226d1b069d6696f2cac99d1f1504f31c1257b216e9e42bfa8c434eaeb80bc38b0af269cab3ffd03d4410539589d9173bc4ba881c", + }, +} satisfies V4SignedWrappedDocument); + +export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = freezeObject({ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + proofs: [], + merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + salts: + "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjZjZWRmYjg5MTM3MTI3NzYwNjZlNjJkMjE1OTFjY2M0NGNiOTM5ZmNlODA0ZWI2Y2E1MWViNmUyMWE0Zjc3NTAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiIxY2ZjMzA0ZjczZGUxZTUxYWMxZjA3M2RiNWI1MGIxMzRjMjY4Y2M0Y2YyMTQyMGZlYTJmNDYzNWJjM2U5ZTMxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhMWQxMDYwZDU3OTU1NWFkZGNiMGQ1OGRiODIzZDAzMzljZDU4YjBlMzk3YTcyY2ViNjQ4M2U2NzYzYzI4MWIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTkwNTA4N2JlOTVhNGFiMzI2NDRkMmQ2M2MxNGIxODhlNGU3MTg2MDdjNWI2MTlkZjc0MjYyMWU2NzdkNmVhZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjM0YWUzN2JhMzY2NzNkNzMzNDg4YTc5ZDM2ZGQyNDQwZDc2YjMwYTBlMjBjZDM1NGI2Mjk1OGU5Y2U1NjUwODciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjFjNjUzNmJiNDA1NTgzMjEzMDdmNjcxNTE3ODIwNzc1NmEwZTcwYjUxNTcwMTNkOTFiMDdjMTFlZTg3Yzc2MmIiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiMmNhODYyYzA3Y2M5MGU3M2IwN2ZkMTJlNGIzNjg4YzQ0ZGQ3OTkyN2UwMzlkNDVlOTY3NGMzNGZmMjJjMGE1MSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + privacy: { + obfuscated: ["0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f"], + }, + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + signature: + "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c", + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], +} satisfies V4SignedWrappedDocument); + // Freeze fixture to prevent accidental changes during tests function freezeObject(obj: T): T { return deepFreeze(obj) as T; From 79f3c67b6eb0acf706a9f455b5dbf0314804dc14 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Wed, 1 May 2024 23:52:37 +0800 Subject: [PATCH 089/107] fix: digest should not include proof --- .eslintrc.json | 3 ++- src/4.0/digest.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 37003312..f4fd218f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,7 +10,8 @@ "@typescript-eslint/no-unused-vars": [ "error", { - "argsIgnorePattern": "^_" + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" } ], "func-names": ["error", "as-needed"], diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 6ea1567d..31de6fca 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -5,8 +5,10 @@ import { LeafValue, traverseAndFlatten } from "./traverseAndFlatten"; import { hashToBuffer } from "../shared/utils"; export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => { + // proof is not part of the digest + const { proof: _proof, ...documentWithoutProof } = document; const saltsMap = new Map(salts.map((salt) => [salt.path, salt.value])); - const hashedLeafNodes = traverseAndFlatten(document, ({ value, path }) => { + const hashedLeafNodes = traverseAndFlatten(documentWithoutProof, ({ value, path }) => { const salt = saltsMap.get(path); if (!salt) throw new Error(`Salt not found for ${path}`); return hashLeafNode({ path, salt, value }); From 429e8ce3df05a9d642132c3fd256c3ed4f0d6c9a Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 00:19:15 +0800 Subject: [PATCH 090/107] fix: skip finding leaf nodes to hash if document without proof is empty --- src/4.0/digest.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index 31de6fca..f74c95f9 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -5,20 +5,26 @@ import { LeafValue, traverseAndFlatten } from "./traverseAndFlatten"; import { hashToBuffer } from "../shared/utils"; export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => { + // find all leaf nodes in the document and hash them // proof is not part of the digest - const { proof: _proof, ...documentWithoutProof } = document; + const { proof: _, ...documentWithoutProof } = document; const saltsMap = new Map(salts.map((salt) => [salt.path, salt.value])); - const hashedLeafNodes = traverseAndFlatten(documentWithoutProof, ({ value, path }) => { - const salt = saltsMap.get(path); - if (!salt) throw new Error(`Salt not found for ${path}`); - return hashLeafNode({ path, salt, value }); - }); + const isEmptyDocument = Object.keys(documentWithoutProof).length === 0; + const hashedLeafNodes = + // skip if document without proof is empty as it will treat the empty document as a leaf node + isEmptyDocument + ? [] + : traverseAndFlatten(documentWithoutProof, ({ value, path }) => { + const salt = saltsMap.get(path); + if (!salt) throw new Error(`Salt not found for ${path}`); + return hashLeafNode({ path, salt, value }); + }); - // Combine both array and sort them to ensure determinism + // combine both array and sort them to ensure determinism const combinedHashes = obfuscatedData.concat(hashedLeafNodes); const sortedHashes = sortBy(combinedHashes); - // Finally, return the digest of the entire set of data + // finally, return the digest of the entire set of data return keccak256(JSON.stringify(sortedHashes)); }; From 3d48bb8180bb9ac6e9048cde8e659b16b8595c4b Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 00:21:29 +0800 Subject: [PATCH 091/107] refactor: improve naming --- src/4.0/obfuscate.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index c559d41f..55bd2995 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -10,11 +10,10 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { // fields to remove will contain the list of each expanded keys from the fields passed in parameter, it's for instance useful in case of // object obfuscation, where the object itself is not part of the salts, but each individual keys are - const affectedLeafNodes = traverseAndFlatten(pick(data, fieldsAsArray), ({ path }) => path); + const toBeRemovedLeafNodes = traverseAndFlatten(pick(data, fieldsAsArray), ({ path }) => path); const salts = decodeSalt(data.proof.salts); - // Obfuscate data by hashing them with the key - const obfuscatedData = affectedLeafNodes.map((field) => { + const obfuscatedData = toBeRemovedLeafNodes.map((field) => { const value = get(data, field); const salt = salts.find((s) => s.path === field); @@ -60,7 +59,7 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { } } - data.proof.salts = encodeSalt(salts.filter((s) => !affectedLeafNodes.includes(s.path))); + data.proof.salts = encodeSalt(salts.filter((s) => !toBeRemovedLeafNodes.includes(s.path))); return { data, obfuscatedData, From c28879b79710cadc5becfb99261a7fe43681d720 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 00:21:52 +0800 Subject: [PATCH 092/107] fix: broken tests --- src/4.0/__tests__/digest.test.ts | 84 ++++++++++++++++---------------- src/4.0/__tests__/salt.test.ts | 11 +---- src/4.0/__tests__/sign.test.ts | 4 +- tsconfig.json | 2 +- 4 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index 3cf71e5c..ce50445d 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -10,7 +10,7 @@ const ROOT_CREDENTIAL_TARGET_HASH = ROOT_CREDENTIAL.proof.targetHash; describe("V4 digestCredential", () => { test("given all testobfuscated documents are generated from the ROOT_CREDENTIAL, ROOT_CREDENTIAL_TARGET_HASH should match snapshot", () => { expect(ROOT_CREDENTIAL_TARGET_HASH).toMatchInlineSnapshot( - `"f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468"` + `"4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec"` ); }); @@ -65,10 +65,10 @@ describe("V4 digestCredential", () => { } `); expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` - [ - "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", - ] - `); + [ + "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", + ] + `); const digest = digestCredential( OBFUSCATED_WRAPPED_DOCUMENT, @@ -104,12 +104,12 @@ describe("V4 digestCredential", () => { } `); expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` - [ - "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", - "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", - "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", - ] - `); + [ + "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", + "236baa019d96f812bf333c8ed2599823f5c796471860538c4e526edae6c88b2c", + "0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f", + ] + `); const digest = digestCredential( OBFUSCATED_WRAPPED_DOCUMENT, @@ -126,42 +126,42 @@ describe("V4 digestCredential", () => { const OBFUSCATED_WRAPPED_DOCUMENT = { // no visible fields proof: { - key: "did:ethr:0xe93502ce1A52C1c0e99A2eB6666263EA53dB0a5e#controller", - merkleRoot: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + proofs: [], + merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + salts: "W10=", privacy: { obfuscated: [ - "a0b3d5c67c33a0dd28b29affc83c45c07bb4c5f43e35ae2b17d7185832372b32", - "e2571635cde03fc4397336176118f7dcad6443d033ff30c0bb20dad6a0b7dc64", - "f1262f889f833ac33db6ad98baa99697a76d2c5b78e8c16de71d70cf4ce9f11e", - "d3e53913d1d0fe997f2a941b38ddc0f7481a737f0958454298a983b622476ae6", - "a9c59e18346b177425016a4dbac19ff521ee2547bc4ae78b6fffda374224985c", - "4322f850d5f17e8db8738e8fcd1901b43d053eb4086fcc747ddb670eec26e1c5", - "2af1e71e32c51a207e74a18ab33ee64468fc7e0c011cdaf43e2ae3204787e3e6", - "2079505ff19eecf40373b4c8a97858340538c32dd9998b013a75b00591863a86", - "6657f68f32ede3f1f0c23fad3c14ec2150caf127320bb681ac882bbf088314e4", - "da0129d424dfd61470cfbc6db2d0a351456c1347ff8a866f64e773534f50dbc9", - "cdc6780c98d21cf1c2a512c69424901ebc66c657c4364f573dbc77b8f99f1e6e", - "6f8dea4950d0b99674507e2a362887b6fbb5a661d395ab5e0f10b7902369fd57", - "0e8cfba3a420807f116410fd8bcf3f8147133191c17bf00315cafae1ec7231b3", - "c9fa76bd112c9dd65afd38899e58583d39879abb7962368eb91ca9217d49e8fc", - "f8dda89249262ebb124a087da28b6520d5948c16a45532453e8380811851a05f", - "0b40ea9dcddca6d570191b3b5bee01dbe4d53047e45f39f496ee4f77baabd2e7", - "0ee1df415b41b20f0e60d65377d5aa7ac8d56577c8a268a4b9e8fe84ace5e8d3", - "5db7da35fe78cde5f216efca8446fa84762c346c8eb5685c9086841ba8899ea3", - "d96e801852c886f9b14861e1ece3d41b4489c1e2d57f972d7f4b2b4d00abac6b", - "6fd29224f74d7b53d5501fb8f111a9cb142d9481367cdec84db6b0919aa60ed8", - "5ea9e34d7e03a436d6c50f7e2af34ee90f1cda674b3dca270228a6041c32430b", - "8ab3fdec4be9c61ca9d0b570896796be343287a9fc92262aadc5a2b1f400ad6a", - "b298f1096b443e1441a8e0f62eef873a45fbbbb6e9bbfd1691106224613cd61b", + "ba719e50b3af802c74e306f028009c7e4726d29ecd03cd05668fb82eafef34d9", + "45648522f4477a017a60d7131fc191fc1dc49e4de7e67e3e42f04e31f964357a", + "b05fa7c260585b3a73eb3f64610a8ae1a282902d7ef36817bf798aa2e9f25dc5", + "b03c7f68ee230f53a8e0a06f5f5c2ca1b2195b97425ae63623cfc84b50730163", + "bd5e40059db1699bb49d5cf07d306d558ebd241d48f6f83d05ccd2a22acc2387", + "47b25ce748576c172fe6268c8de2916e08ea23fe56bea155c2eb97c787c37b0b", + "0231e0eeaf917998c5d7834172e99add661c91a71ea3ce81d4f10bb7d196337f", + "b2d480de30ca619feae838cb859178476e571fb5cbce50eaa0f9f19ea3d0404f", + "0e4344b5675a18e80c32c683362f99a47aa1e815a6ae495f3696e89b83b4a4bb", + "7e1c13f6b1ef867b6e75810ab6629fab33f2cd895212b1231c354496b50b355a", + "cb0800c5926b75dc379c73c1d6d92d459f9adacd1ebffd8ea8165248d55a2485", + "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", + "668e8bc4e2fd13809dd8c815d8c26819794b28a334a2a6d4dc0564e306cbdde8", + "236baa019d96f812bf333c8ed2599823f5c796471860538c4e526edae6c88b2c", + "110cf75ee55d9e5d7db95becf4fb6d4162cdb8223d83d097b5f47fc1e3e7d378", + "0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f", + "b9d3f903419f585d7f174802496aeeef1e15d9cac513da074026c8b186314a14", + "18c6ebe5e22f6c9a272a8bbec47f261d1c5eff5599eaf47885e0c43b6dd24149", + "ad93c39a9559065c4e133a9fbd26f557fc0c8bed74982162187148d093b03a92", + "02a9da9a4199f9e6f6e2f4205b76a456d954e0835914c69dcb8479b2dc8d1ab0", + "ea083928953e5f9901c58f4cb0c367a2f4ab4b67fbba46b77fa76502512c1493", + "c9687c9bfffa291fee284c2d208b6fb983473e46513ed490c99ec4c5fc6cf194", + "f3e68c691c2fc1a3453c7584a4faf51eda264decd0e469e1807d5055d02e26f8", ], }, - proofPurpose: "assertionMethod", - proofs: [], - salts: "W10=", + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", signature: - "0x170fbb2d5916a7b3a4863feb8b705f5560c0b42311b164b2da32e682a8633b6f2c332f963db8267ab9a1c3be16ba1091388ed70e6e2a4ec240f5c0865557c6aa1c", - targetHash: "f49be3b06f7a7eb074775ad12aae43936084c86646e3640eae18e7aeca4f7468", - type: "OpenAttestationMerkleProofSignature2018", + "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c", }, } as unknown as V4SignedWrappedDocument; diff --git a/src/4.0/__tests__/salt.test.ts b/src/4.0/__tests__/salt.test.ts index 66f955d6..a512668f 100644 --- a/src/4.0/__tests__/salt.test.ts +++ b/src/4.0/__tests__/salt.test.ts @@ -82,18 +82,11 @@ describe("V4.0 digest", () => { expect(salted).toContainEqual(expect.objectContaining({ path: "grades[3]" })); expect(salted).toContainEqual(expect.objectContaining({ path: "grades[4]" })); }); - test("handles sparse arrays correctly", () => { + test("throw on sparse arrays (we do not support obfuscation of array item as JSON turns empty slots into null values)", () => { const document = { grades: ["A+", 100, , , , true, "B+"], }; - const salted = salt(document); - expect(salted).toContainEqual(expect.objectContaining({ path: "grades[0]" })); - expect(salted).toContainEqual(expect.objectContaining({ path: "grades[1]" })); - expect(salted).toContainEqual(expect.objectContaining({ path: "grades[5]" })); - expect(salted).toContainEqual(expect.objectContaining({ path: "grades[6]" })); - expect(salted).not.toContainEqual(expect.objectContaining({ path: "grades[2]" })); - expect(salted).not.toContainEqual(expect.objectContaining({ path: "grades[3]" })); - expect(salted).not.toContainEqual(expect.objectContaining({ path: "grades[4]" })); + expect(() => salt(document)).toThrow(`Unexpected data 'undefined'`); }); }); diff --git a/src/4.0/__tests__/sign.test.ts b/src/4.0/__tests__/sign.test.ts index 31102c36..613452e5 100644 --- a/src/4.0/__tests__/sign.test.ts +++ b/src/4.0/__tests__/sign.test.ts @@ -22,7 +22,7 @@ describe("V4 sign", () => { expect(Object.keys(proof).length).toBe(9); expect(proof.key).toBe("did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller"); expect(proof.signature).toBe( - "0xcc3f2d88367976231b8cec5d9ceb89b8f1236ae0d3b133c6c06cf66a3cd459e6023277b5f959ffc82755c64edfc38c7131d6dc584e253615c8e3be2ffe7bb7f01c" + "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c" ); }); it("should sign a document with a wallet", async () => { @@ -42,7 +42,7 @@ describe("V4 sign", () => { expect(Object.keys(proof).length).toBe(9); expect(proof.key).toBe("did:ethr:0x906FB815De8976b1e38D9a4C1014a3acE16Ce53C#controller"); expect(proof.signature).toBe( - "0x10d291950915358d2326009f1e1afbea51355abd7bf49e1dedb96dd119fe6798264ba7033930f4de4d19624e6f2c16c4dcf6b4335b8dea849526922e3dc140631b" + "0xac4c7fb9ed25878038b42f2da8a2a6f8cd553383debc5d0bf6e362b810c05ba779dd50235f0bab560bf970af564587b4756bd0a06dd4d42862875e68280d39201b" ); }); diff --git a/tsconfig.json b/tsconfig.json index 30a1cf29..9e343fbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ }, "compilerOptions": { "lib": ["es2019"], - "target": "es5", + "target": "ES6", "module": "commonjs", "moduleResolution": "node", "strict": true, From 77ad5b3716dd5a853e946c08d73c7042006adf88 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 00:22:40 +0800 Subject: [PATCH 093/107] fix: revert target bump --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 9e343fbc..30a1cf29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ }, "compilerOptions": { "lib": ["es2019"], - "target": "ES6", + "target": "es5", "module": "commonjs", "moduleResolution": "node", "strict": true, From 9d7a4d544d14c3b2cb02912499b3aedf40fc03d0 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 11:18:31 +0800 Subject: [PATCH 094/107] fix: all obfuscate tests --- src/4.0/__tests__/obfuscate.test.ts | 52 +++++++++++++---------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts index 26078b66..593dbbb2 100644 --- a/src/4.0/__tests__/obfuscate.test.ts +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -2,12 +2,12 @@ import { obfuscateVerifiableCredential } from "../obfuscate"; import { get } from "lodash"; import { decodeSalt } from "../salt"; -import { SchemaId } from "../../shared/@types/document"; import { wrapDocument } from "../wrap"; import { Salt, V4Document, V4WrappedDocument } from "../types"; import { verifySignature } from "../../"; -import { RAW_DOCUMENT_DID } from "../fixtures"; +import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED, WRAPPED_DOCUMENT_DID } from "../fixtures"; import { hashLeafNode } from "../digest"; +import { getObfuscatedData, isObfuscated } from "../../shared/utils"; const makeV4RawDocument = >(props: T) => ({ @@ -290,31 +290,25 @@ describe("privacy", () => { }); }); - // describe("getObfuscated", () => { - // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - // test("should return empty array when there is no obfuscated data in document v3", () => { - // expect(getObfuscatedData(documentNotObfuscatedV3)).toHaveLength(0); - // }); - - // test("should return array of hashes when there is obfuscated data in document v3", () => { - // const obfuscatedData = getObfuscatedData(documentObfuscatedV3); - // expect(obfuscatedData.length).toBe(1); - // expect(obfuscatedData?.[0]).toBe("e411260249d681968bdde76246350f7ca1c9bf1fae59b6bbf147692961b12e26"); - // }); - // }); - - // describe("isObfuscated", () => { - // const documentObfuscatedV3 = ObfuscatedWrapped as WrappedDocument; - // const documentNotObfuscatedV3 = NotObfuscatedWrapped as WrappedDocument; - - // test("should return false when there is no obfuscated data in document v3", () => { - // expect(isObfuscated(documentNotObfuscatedV3)).toBe(false); - // }); - - // test("should return true where there is obfuscated data in document v3", () => { - // expect(isObfuscated(documentObfuscatedV3)).toBe(true); - // }); - // }); + describe("getObfuscated", () => { + test("should return empty array when there is no obfuscated data in document", () => { + expect(getObfuscatedData(WRAPPED_DOCUMENT_DID)).toHaveLength(0); + }); + + test("should return array of hashes when there is obfuscated data in document", () => { + const obfuscatedData = getObfuscatedData(SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED); + expect(obfuscatedData.length).toBe(1); + expect(obfuscatedData?.[0]).toBe("0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f"); + }); + }); + + describe("isObfuscated", () => { + test("should return false when there is no obfuscated data in document", () => { + expect(isObfuscated(WRAPPED_DOCUMENT_DID)).toBe(false); + }); + + test("should return true where there is obfuscated data in document", () => { + expect(isObfuscated(SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED)).toBe(true); + }); + }); }); From 6216a4e4961b6670281b42d929047b426f2d2403 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 12:00:12 +0800 Subject: [PATCH 095/107] fix: verify should treat salt not found error as invalid --- src/4.0/digest.ts | 12 +++++++++++- src/4.0/verify.ts | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/4.0/digest.ts b/src/4.0/digest.ts index f74c95f9..9d96e800 100644 --- a/src/4.0/digest.ts +++ b/src/4.0/digest.ts @@ -16,7 +16,7 @@ export const digestCredential = (document: V4Document, salts: Salt[], obfuscated ? [] : traverseAndFlatten(documentWithoutProof, ({ value, path }) => { const salt = saltsMap.get(path); - if (!salt) throw new Error(`Salt not found for ${path}`); + if (!salt) throw new SaltNotFoundError(path); return hashLeafNode({ path, salt, value }); }); @@ -62,3 +62,13 @@ export function deriveType(value: unknown): "string" | "number" | "boolean" | "n } } } + +export class SaltNotFoundError extends Error { + constructor(public path: string) { + super(`Salt not found for ${path}`); + + // we shd consider changing the compilation target to >= es6 + // https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript + Object.setPrototypeOf(this, SaltNotFoundError.prototype); + } +} diff --git a/src/4.0/verify.ts b/src/4.0/verify.ts index 72d488e3..2f07b64e 100644 --- a/src/4.0/verify.ts +++ b/src/4.0/verify.ts @@ -1,5 +1,5 @@ import { V4WrappedDocument } from "./types"; -import { digestCredential } from "./digest"; +import { SaltNotFoundError, digestCredential } from "./digest"; import { checkProof } from "../shared/merkle"; import { decodeSalt } from "./salt"; @@ -14,10 +14,17 @@ export const verify = (document: T): document is T const decodedSalts = decodeSalt(document.proof.salts); // Checks target hash - const digest = digestCredential(documentWithoutProof, decodedSalts, document.proof.privacy.obfuscated); - const targetHash = document.proof.targetHash; - if (digest !== targetHash) return false; + try { + const digest = digestCredential(documentWithoutProof, decodedSalts, document.proof.privacy.obfuscated); + const targetHash = document.proof.targetHash; + if (digest !== targetHash) return false; - // Calculates merkle root from target hash and proof, then compare to merkle root in document - return checkProof(document.proof.proofs, document.proof.merkleRoot, document.proof.targetHash); + // Calculates merkle root from target hash and proof, then compare to merkle root in document + return checkProof(document.proof.proofs, document.proof.merkleRoot, document.proof.targetHash); + } catch (error: unknown) { + if (error instanceof SaltNotFoundError) { + return false; + } + throw error; + } }; From d5d7f4f030d1b1bcdf096b7738119905c6a72d1e Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 12:04:25 +0800 Subject: [PATCH 096/107] fix: partial fix of verify tests, added bunch of todos --- src/4.0/__tests__/verify.test.ts | 95 ++++++++++++++ src/4.0/__tests__/verify.test.wip | 206 ------------------------------ 2 files changed, 95 insertions(+), 206 deletions(-) create mode 100644 src/4.0/__tests__/verify.test.ts delete mode 100644 src/4.0/__tests__/verify.test.wip diff --git a/src/4.0/__tests__/verify.test.ts b/src/4.0/__tests__/verify.test.ts new file mode 100644 index 00000000..f05cc45e --- /dev/null +++ b/src/4.0/__tests__/verify.test.ts @@ -0,0 +1,95 @@ +import { cloneDeep } from "lodash"; +import { SIGNED_WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { V4SignedWrappedDocument } from "../types"; +import { verify } from "../verify"; + +describe("signature", () => { + describe("verify", () => { + // Documents without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot) + describe("documents without proofs", () => { + test("returns true for documents with unaltered data", () => { + expect(verify(SIGNED_WRAPPED_DOCUMENT_DID)).toBe(true); + }); + + test("returns false for documents with altered value", () => { + expect( + verify({ + ...SIGNED_WRAPPED_DOCUMENT_DID, + issuer: { + ...SIGNED_WRAPPED_DOCUMENT_DID.issuer, + name: "Fake Name", // Value was originally "DEMO STORE" + }, + }) + ).toBe(false); + }); + + test("returns false for documents with altered key", () => { + const { name: _, ...issuerWithoutName } = SIGNED_WRAPPED_DOCUMENT_DID.issuer; + + expect( + verify({ + ...SIGNED_WRAPPED_DOCUMENT_DID, + issuer: { + ...issuerWithoutName, + fakename: "DEMO STORE", // Key was originally "name" + } as unknown as V4SignedWrappedDocument["issuer"], + }) + ).toBe(false); + }); + + test("returns false for documents with additional data not part of salt", () => { + const modifiedCredentialSubject = cloneDeep(SIGNED_WRAPPED_DOCUMENT_DID.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({ + class: "Class 2A", + effectiveDate: "2020-06-05T00:00:00Z", + description: "Motorcycle", + }); + expect(modifiedCredentialSubject.licenses[2].description).toBeDefined(); + + expect( + verify({ + ...SIGNED_WRAPPED_DOCUMENT_DID, + credentialSubject: { + ...modifiedCredentialSubject, + }, + }) + ).toBe(false); + }); + + test("returns false for documents with missing data", () => { + const modifiedCredentialSubject = cloneDeep(SIGNED_WRAPPED_DOCUMENT_DID.credentialSubject); + expect(modifiedCredentialSubject.licenses[0].description).toBeDefined(); + delete (modifiedCredentialSubject.licenses[0] as any).description; + expect(modifiedCredentialSubject.licenses[0].description).toBeUndefined(); + + expect( + verify({ + ...SIGNED_WRAPPED_DOCUMENT_DID, + credentialSubject: { + ...modifiedCredentialSubject, + }, + }) + ).toBe(false); + }); + + test.todo("given insertion of an empty object, should return false"); + test.todo("given insertion of an empty array, should return false"); + test.todo("given an altered value type that string coerce to the same value, should return false"); + test.todo("given a key and value is moved, should return false"); + }); + + // Documents with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot) + describe("documents with proofs", () => { + test.todo("returns true for documents with unaltered data"); + test.todo("returns false for documents with altered value"); + test.todo("returns false for documents with altered key"); + test.todo("returns false for documents with additional data not part of salt"); + test.todo("returns false for documents with missing data"); + test.todo("returns false for documents with altered targetHash"); + test.todo("returns false for documents with altered proofs"); + test.todo("returns false for documents with missing proofs"); + test.todo("returns false for documents with altered merkleRoot"); + }); + }); +}); diff --git a/src/4.0/__tests__/verify.test.wip b/src/4.0/__tests__/verify.test.wip deleted file mode 100644 index 3e56dfe7..00000000 --- a/src/4.0/__tests__/verify.test.wip +++ /dev/null @@ -1,206 +0,0 @@ -import { verify } from "../verify"; -import sample from "../schema/sample-verifiable-credential.json"; -import batched from "../schema/batched-verifiable-credential-1.json"; -import { WrappedDocument } from "../../3.0/types"; - -// sample1: unwrapped (aka credential), sample2: only 1 doc is wrapped (aka verifiable credential/VC) -const sampleVerifiableCredential = sample as WrappedDocument; - -// sample3 & sample4: more than 1 doc wrapped (aka batched VC, where 'proofs' has values) -const sampleBatchedVC = batched as WrappedDocument; - -describe("signature", () => { - describe("verify", () => { - // Documents without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot) - describe("documents without proofs", () => { - test("returns true for documents with unaltered data", () => { - expect(verify(sampleVerifiableCredential)).toBe(true); - }); - test("returns false for documents with altered value", () => { - const verifiableCredential = { - ...sampleVerifiableCredential, - issuer: { - id: "https://example.com", - name: "Fake Name", // Value was originally "DEMO STORE" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with altered key", () => { - const verifiableCredential = { - ...sampleVerifiableCredential, - issuer: { - id: "https://example.com", - fakename: "DEMO STORE", // Key was originally "name" - }, - }; - - expect(verify(verifiableCredential as any)).toBe(false); - }); - test("returns false for documents with additional data not part of salt", () => { - // In this test case, we added the Class 2A licence which is not found in the original salts - const verifiableCredential = { - ...sampleVerifiableCredential, - credentialSubject: { - ...sampleVerifiableCredential.credentialSubject, - class: [ - { - type: "3", - effectiveDate: "2010-01-01T19:23:24Z", - }, - { - type: "3A", - effectiveDate: "2010-01-01T19:23:24Z", - }, - { - // This was added in after it has been wrapped - type: "2A", - effectiveDate: "2020-06-05T00:00:00Z", - }, - ], - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with missing data", () => { - // In this test case, we removed the Class 3A licence which is in the original salts - const verifiableCredential = { - ...sampleVerifiableCredential, - credentialSubject: { - ...sampleVerifiableCredential.credentialSubject, - class: [ - { - type: "3", - effectiveDate: "2010-01-01T19:23:24Z", - }, - // Class 3A was removed - ], - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - }); - - // Documents with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot) - describe("documents with proofs", () => { - test("returns true for documents with unaltered data", () => { - const verifiableCredential = sampleBatchedVC; - expect(verify(verifiableCredential)).toBe(true); - }); - test("returns false for documents with altered value", () => { - const verifiableCredential = { - ...sampleBatchedVC, - issuer: { - id: "https://example.com", - name: "Fake Name", // Value was originally "DEMO STORE" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with altered key", () => { - const verifiableCredential = { - ...sampleBatchedVC, - issuer: { - id: "https://example.com", - fakename: "DEMO STORE", // Key was originally "name" - }, - }; - - expect(verify(verifiableCredential as any)).toBe(false); - }); - test("returns false for documents with additional data not part of salt", () => { - // In this test case, we added the Class 2A licence which is not found in the original salts - const verifiableCredential = { - ...sampleBatchedVC, - credentialSubject: { - ...sampleVerifiableCredential.credentialSubject, - class: [ - { - type: "3", - effectiveDate: "2010-01-01T19:23:24Z", - }, - { - type: "3A", - effectiveDate: "2010-01-01T19:23:24Z", - }, - { - // This was added in after it has been wrapped - type: "2A", - effectiveDate: "2020-06-05T00:00:00Z", - }, - ], - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with missing data", () => { - // In this test case, we removed the Class 3A licence which is in the original salts - const verifiableCredential = { - ...sampleBatchedVC, - credentialSubject: { - ...sampleVerifiableCredential.credentialSubject, - class: [ - { - type: "3", - effectiveDate: "2010-01-01T19:23:24Z", - }, - // Class 3A was removed - ], - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with altered targetHash", () => { - const verifiableCredential = { - ...sampleBatchedVC, - proof: { - ...sampleBatchedVC.proof, - targetHash: "81859d00caadd33f4100b7d37230684b953195786426a1be2c3bfea32b3c2a53", // Was "76eee8fc36924975c00420e463aab1a2e6b24fb8cfb81e8c789b2534da4b59a4" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with altered proofs", () => { - // Since the proofs key only exist when multiple documents are wrapped, we have to use sampleMultiVC1 or sampleMultiVC2 - const verifiableCredential = { - ...sampleBatchedVC, - proof: { - ...sampleBatchedVC.proof, - proofs: ["964b066a78bfec3701760893090fa41bd0c86fb1328f2ba07293252a1a7d5531"], // Was "964b066a78bfec3701760893090fa41bd0c86fb1328f2ba07293252a1a7d5530" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with missing proofs", () => { - const verifiableCredential = { - ...sampleBatchedVC, - proof: { - ...sampleBatchedVC.proof, - proofs: [], // Was "964b066a78bfec3701760893090fa41bd0c86fb1328f2ba07293252a1a7d5530" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - test("returns false for documents with altered merkleRoot", () => { - const verifiableCredential = { - ...sampleBatchedVC, - proof: { - ...sampleBatchedVC.proof, - merkleRoot: "76eee8fc36924975c00420e463aab1a2e6b24fb8cfb81e8c789b2534da4b59a4", // Was "8505f27ea43ca3720b419ab96b80039eb4b2a1126acc9cb90f2a31349c110137" - }, - }; - - expect(verify(verifiableCredential)).toBe(false); - }); - }); - }); -}); From 5eec7a2659e06e0e4ecc4a4883a75ac0752706f4 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 13:37:36 +0800 Subject: [PATCH 097/107] chore: add batched signed documents --- src/4.0/fixtures.ts | 112 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index e93d77a4..4ed4086b 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -569,6 +569,118 @@ export const SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED = freezeObject({ ], } satisfies V4SignedWrappedDocument); +export const BATCHED_SIGNED_WRAPPED_DOCUMENTS_DID = freezeObject([ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "John Doe", + licenses: [ + { + class: "3", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + { + class: "3A", + description: "Motor cars with unladen weight <= 3000kg", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681", + proofs: ["ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001"], + merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + salts: + "W3sidmFsdWUiOiIwMzgyZWU0MjFlNTgyMzJlMGJjNTI0MDY1NjhhZTdiNWZhNDhlOWRjYzczZmVkM2VhOTZkOTAzNGQ4ZWZkNDlhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImViMmI1ZmIwODhlZDk5YzY0NzJmYmQ4MjMyNjVhYjlkMzZhNmQ0MDFiMGEyNmM5N2Q3Zjc0MDkwYWJiYTE4OGEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiYWNmNGFhMGJmZDEyMTRmZGEwZDAzNzczNmNhMDFiMzhkOTBlZDA4MzQ5ODUwMWY5NDlmOTgyNjg1NmE2MTM3OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjM5Yzc1MjFmNDdlNTJlNWZlOGI2NmEyYjhjZmEzYTQwNDM1ZjQ2Y2NiOWY3MGQxYWY4MThkNmNkNzk1MzQ4MzQiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiNWY3MmI3N2M1NjU3YjZiOTMyN2QyMGYwZTUwYzQxMGVmNzRlNDMzYTM0NTRlOTRmMTEzOTZmMTIwYzJjMjc5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiOTU1MjExMzNmNzgzZjI5NzlkODgyMzI4MDlmMDgxNmUzNDVmMjE4MTk0ZTJkMTgwYzZmYTcwMTBhNDI4ODFkYyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTQwZmVlNjI5Y2M2MjY3Njk1NzA4YzY2ZTg2OGI0ZjVhZTBiMDg1YjYzNDczM2I1YWE1N2M4ZTQxM2I1NTliNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJiMjFmZTkyMDNlNWYxNzFjMjE1NDIyY2Y2ZTZkOWEzNzFkMmIyZGJkMjU5ZDE0Y2JmMDZiYTllZjQ4NzEzYzk3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImE5Mjg0OTFiMzYyNzcyMjUxNzM0ZDEwNGQwOGJiMTgzMjczM2JlYzZiYjdmYWI0YTE4ZTdiNGRlY2QzMjE0YmMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNWViMmMyNTU2ODgyZjgzZjM0MTQ5MTczMGM0OWI4NTE4OTljMTA3NzY2Yzk5YWQwYzhhMGRkZTQxNDkzZjYxYyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjVlMTgyNThiODdjMjJiMGIzY2Y4OWVhMjZhOTI5N2ZkYjVmYjI0NTA2Mzg0MzNlYzcxN2YyYTBmMzE0NTJlMmYiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYWU3ODllZmVjZmE4ZmEzMmZmNTdlYTNjYTJmZjJjZmVmOThmOThmZGVhY2U4M2Q0NGEwNTVjZGU4MDE2MDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMGQ1ZWI3YmY3OTE1MmI0M2EwMTQyYjhkNTVkMzRmMzRlOGQ0N2U5MzgwMDVhYTVjMTRjMjk2MWNiMjM0OWFkYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImIyZWM0NTYzMjhlMzhjYWYyYmNlYTk5YWM5NTc0ZWEzZGExNjJkNmJlZDVlZTRkZGM3MzI5NjhkMmIwMDZjZGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3OTIwNmQwNDNlNjVhYmU2MmFmYmIwNmViNGMxMWNmZTg2NjkzNWRhOGYyYzkzODI4NjVlZjg1YjVhMzc1NTUwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAyYjJjMTlhNDY2ZjNiYWU5ZDhmNWI0NzUyZGEzZmU2MWM1MTIxYzc1Mzc0N2I1NzNmNTdlYzBmOGExODIzZDciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTNiOTA2MjdlNWY1OTM4YjdkNWY0NzhjM2JiNDE4NjkwOWI1OTY1MjVhM2I4NWNlZWU2M2JjNGYyZGQ1ZmRkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImNhOGFlMzIwZjU5NWJmMmUwMmQ3MTAxOGE5YjA0YWI1MDNiOGJiMTIyM2I2NDM5MWNjYTZiY2MyOTk5MjdmOTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYjk1MzhlMjkwNzdlMzg1MzlmN2E0MTliZGMzNmFhYWVjMTMyNGY0MjQzOTA2YzIyN2JmMzhkZDU4NTA4M2NlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyN2RmYWY1Y2M1NzJlMTFmODhkMDFmNGUwMzQ0ZWRiMzA4OWQ2NDc5YmY0ODIyODBiZmNmNGE4YzZjOTY1NmIwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTBjMjFhMzQ1NTZjZTMzNjViOTE5YmUwNjlmODVkM2EyNmQ2MTcyM2Q4YjlmN2QwZjY5ZTkzMjMxZDY4ZGM2ZiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiODY4YzNjOTYwNGJlYWMxMDZiZDQzMTc3NDRjYTNjZTI2YzU3YWFiODc2YWUwMWQwZWU5NzNmYWE1NDNlNWRmOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJiNzY3YTcyMmUzNmU4MjAxYWNjMjg2MTAzYzBkZmY4Y2I2MjI4YWQyMmVkZWJmMjc4ODcxOTQ1MDVmMGVlNmNkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + privacy: { + obfuscated: [], + }, + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + signature: + "0x153aeb0f59bef692f1b5bec2f20fc08f72863b0a670e87ac7c68460d311fc7a574189bb92056a15f1ef283c1784303528364d2124a006de3dba586f6162df5481b", + }, + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ], + name: "Republic of Singapore Driving Licence", + type: ["VerifiableCredential", "OpenAttestationCredential"], + issuer: { + id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + type: "OpenAttestationIssuer", + name: "Government Technology Agency of Singapore (GovTech)", + identityProof: { + identityProofType: "DNS-DID", + identifier: "example.openattestation.com", + }, + }, + validFrom: "2021-03-08T12:00:00+08:00", + credentialSubject: { + id: "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + type: ["DriversLicense"], + name: "Jane Doe", + licenses: [ + { + class: "3000A", + description: "Motor spaceships with unladen weight <= 3000tonnes", + effectiveDate: "2013-05-16T00:00:00+08:00", + }, + ], + }, + renderMethod: [ + { + id: "https://demo-renderer.opencerts.io", + type: "OpenAttestationEmbeddedRenderer", + templateName: "GOVTECH_DEMO", + }, + ], + proof: { + type: "OpenAttestationMerkleProofSignature2018", + proofPurpose: "assertionMethod", + targetHash: "ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001", + proofs: ["08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681"], + merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + salts: + "W3sidmFsdWUiOiI1ZGU5NjM1OGRlZGVjODIwMWFhYTJjZjRkOGI4ZjYyNzA0NDc2ZTRkYzdiZDQ5OGEwOTc5MGI1MDc1NzY0NWQyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImI2YzkyMDc4YjM5OTU3NmM1YmNlZjVhOTBjYmMwMzQxNjEzN2UyZTBjODAzM2JhM2FiZDNkYTU3NjEzZGYwZGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNjM0Nzk4NGNiN2Q3MDA4NTczNjJlYmFjZTgwMDEzZjQwODA0NzNiMDkyYjAyMzQ3ZDY1MGUwMGM4MGRjMTQ5ZiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImVkNGJiMDQwMWQ0NDY0NTljNjQ1YWUyMjI2MGQ5M2YxYzllOTkwYzg3ZGZhYzM2YjI2N2QwMTVlZTM4MDRlMWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1NTU4OThjZmE3ZWUxZThjY2VmOGIxYWMyYjBjY2RlNWJlZmFkMDE2NTJjOTY3NmVlM2VlNGExNTNmODlhM2JhIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNmVmYjc3ZDY2ZDQ3MDcxNzQ5NTIyOTI4MzE1MDI5OTlkYjViN2FmOWQ2NjQ3OWRiYzFiMzlhZjhiNmI0ZTU1NSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMmNjOGU1OTcxMDFhYmE0NzIyYmYzZGY0MWQwYjYyNjM3MGFiNzRhOGZiZjhjMmM2MTlmNDIzN2Y3ZWFhMzJmOCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmY2IxNDNjOWUwNmQzMTZlNzdlYmIzMDkxNDFiMWI5ZDE1YTRhMTQwYzk4ODFhYTY0OTY0MTQ1NDVhM2RhYzUxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc0NDVkNDQzMDdmOWU2ODBjNTFkMWYxZjEzYzE5YTU2NDFkNmFiNmMzYmE0NmRjMDFmMWIyZDU0YTE4ZWJkMTEiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYmVjYjY3OTBjMWQ1MmE2YmRlMjFkMzRjYTUxZDQ5ZGViNDBkMmE0MzQ2MGYyZjI4ZDA1NzY0YjVhNzFmODFhNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkzMzQ2ZDg0NmI1MzJkZmY0NTc5NGQ0OTVkNzk3ZDZmYzZkYTRjYTgyMzRmMzQwNjljODNjYTU0MWQwNzA4NzgiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImU5OTZiYTM2NTFiZjZiMGY2YWU4NDUyYjljOTM3NjBiZjRkYzRiNDYwNjZlNGZkYTQ0YzBiZTJkY2FhMmFjZTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZGIwZGQ0NjcyYWYxZjdkZGJiNGVhNTYwOGMzZjBkMjc5MDljMTAxM2FjYmJkZDcwOWVjMDZjYmQ0YTM3OGZmMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjA1MWZkNTUzN2Q0ZjViNzVmNWY2MTk2NmI1ODc4MDhkNGNlZTQ1N2EzOTNlMmQ0MWZmZGFjMzllZjk5ZjcwNTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3NTU3NzYxY2VhZTgzYzVmN2YwZTNkMmM4YjYxYzllMWVkZTNmMTIyNzVmZDgzOTIyNDNjNmQ4ZDNhNTYzNTM4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImQ3NzE4NTkxMDUwNjc1MDhlMGQ5ZTMxMWYxZDRhZTEyNmM3ZTNkMjhjMzY4OWUxNThhNjFjNmUwZGQxZTRkMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZWEzZmEzNDdjNGUxODVhNDhhY2NiMTY4NjMzNDY2ZmMyMjc4YjdkYmEzNzNiNDM1ODI1YmIzNDdiNzMxMDA3YiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVjNDZhODc5ODI0MGQwMGNkOTJkNTBjYzE5ZTVhZDE3NjUyYjJiZjJmOGQyOTlhOWVhOTNlYWE4YmE2MDgwNzgiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZhYTk2NWViNjIwMmNlNDhhNjczNzRiZTA5M2VhNTJjNDRhM2JhMzZmYmNkZjBhMWY1MzZjNTZkM2NmOGVlNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYjlkZTJiZDRkMGZjMmVlYmZkNDE1ZTcwZTRlMjYzY2Y5MDEwM2MyYzEyMTlkZDU0OGYzOWE4Y2I2NWNlYjU5YyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + privacy: { + obfuscated: [], + }, + key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + signature: + "0x153aeb0f59bef692f1b5bec2f20fc08f72863b0a670e87ac7c68460d311fc7a574189bb92056a15f1ef283c1784303528364d2124a006de3dba586f6162df5481b", + }, + }, +]); + // Freeze fixture to prevent accidental changes during tests function freezeObject(obj: T): T { return deepFreeze(obj) as T; From 82a28a053393b9faff0d4ad2c26458eb451c347a Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 13:41:43 +0800 Subject: [PATCH 098/107] fix: complete verify tests --- src/4.0/__tests__/verify.test.ts | 276 ++++++++++++++++++++++--------- src/4.0/fixtures.ts | 2 +- 2 files changed, 197 insertions(+), 81 deletions(-) diff --git a/src/4.0/__tests__/verify.test.ts b/src/4.0/__tests__/verify.test.ts index f05cc45e..eb8061a8 100644 --- a/src/4.0/__tests__/verify.test.ts +++ b/src/4.0/__tests__/verify.test.ts @@ -1,95 +1,211 @@ import { cloneDeep } from "lodash"; -import { SIGNED_WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { BATCHED_SIGNED_WRAPPED_DOCUMENTS_DID, SIGNED_WRAPPED_DOCUMENT_DID } from "../fixtures"; import { V4SignedWrappedDocument } from "../types"; import { verify } from "../verify"; +const TEST_DOCUMENTS = { + "Document without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot)": + SIGNED_WRAPPED_DOCUMENT_DID, + "Document with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot)": + BATCHED_SIGNED_WRAPPED_DOCUMENTS_DID[0], +} as const; + describe("signature", () => { describe("verify", () => { - // Documents without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot) - describe("documents without proofs", () => { - test("returns true for documents with unaltered data", () => { - expect(verify(SIGNED_WRAPPED_DOCUMENT_DID)).toBe(true); - }); + Object.entries(TEST_DOCUMENTS).forEach(([description, document]) => { + describe(`${description}`, () => { + test("given a document wiht unaltered data, should return true", () => { + expect(verify(document)).toBe(true); + }); - test("returns false for documents with altered value", () => { - expect( - verify({ - ...SIGNED_WRAPPED_DOCUMENT_DID, - issuer: { - ...SIGNED_WRAPPED_DOCUMENT_DID.issuer, - name: "Fake Name", // Value was originally "DEMO STORE" - }, - }) - ).toBe(false); - }); + describe("tempering", () => { + test("given a value of a key in object is changed, should return false", () => { + const newName = "Fake Name"; + expect(document.issuer.name).not.toBe(newName); + expect( + verify({ + ...document, + issuer: { + ...document.issuer, + name: "Fake Name", // Value was originally "DEMO STORE" + }, + }) + ).toBe(false); + }); - test("returns false for documents with altered key", () => { - const { name: _, ...issuerWithoutName } = SIGNED_WRAPPED_DOCUMENT_DID.issuer; - - expect( - verify({ - ...SIGNED_WRAPPED_DOCUMENT_DID, - issuer: { - ...issuerWithoutName, - fakename: "DEMO STORE", // Key was originally "name" - } as unknown as V4SignedWrappedDocument["issuer"], - }) - ).toBe(false); - }); + test("given a key in an object is altered (value kept the same), should return false", () => { + const { name, ...issuerWithoutName } = document.issuer; - test("returns false for documents with additional data not part of salt", () => { - const modifiedCredentialSubject = cloneDeep(SIGNED_WRAPPED_DOCUMENT_DID.credentialSubject); - expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); - modifiedCredentialSubject.licenses.push({ - class: "Class 2A", - effectiveDate: "2020-06-05T00:00:00Z", - description: "Motorcycle", - }); - expect(modifiedCredentialSubject.licenses[2].description).toBeDefined(); - - expect( - verify({ - ...SIGNED_WRAPPED_DOCUMENT_DID, - credentialSubject: { - ...modifiedCredentialSubject, - }, - }) - ).toBe(false); - }); + expect( + verify({ + ...document, + issuer: { + ...issuerWithoutName, + fakename: name, // Key was originally "name" + } as unknown as V4SignedWrappedDocument["issuer"], + }) + ).toBe(false); + }); - test("returns false for documents with missing data", () => { - const modifiedCredentialSubject = cloneDeep(SIGNED_WRAPPED_DOCUMENT_DID.credentialSubject); - expect(modifiedCredentialSubject.licenses[0].description).toBeDefined(); - delete (modifiedCredentialSubject.licenses[0] as any).description; - expect(modifiedCredentialSubject.licenses[0].description).toBeUndefined(); - - expect( - verify({ - ...SIGNED_WRAPPED_DOCUMENT_DID, - credentialSubject: { - ...modifiedCredentialSubject, - }, - }) - ).toBe(false); - }); + test("given a new array item is added, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({ + class: "Class 2A", + effectiveDate: "2020-06-05T00:00:00Z", + description: "Motorcycle", + }); + expect(modifiedCredentialSubject.licenses[2].description).toBeDefined(); - test.todo("given insertion of an empty object, should return false"); - test.todo("given insertion of an empty array, should return false"); - test.todo("given an altered value type that string coerce to the same value, should return false"); - test.todo("given a key and value is moved, should return false"); - }); + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + test("given a key in an item is removed, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[0].description).toBeDefined(); + delete (modifiedCredentialSubject.licenses[0] as any).description; + expect(modifiedCredentialSubject.licenses[0].description).toBeUndefined(); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + describe("given insertion of an empty object, should return false", () => { + test("given insertion into an object", () => { + expect( + verify({ + ...document, + credentialSubject: { + ...document.credentialSubject, + newField: {}, + }, + }) + ).toBe(false); + }); + + test("given insertion into an array", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({} as any); + expect(modifiedCredentialSubject.licenses[2]).toEqual({}); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + }); - // Documents with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot) - describe("documents with proofs", () => { - test.todo("returns true for documents with unaltered data"); - test.todo("returns false for documents with altered value"); - test.todo("returns false for documents with altered key"); - test.todo("returns false for documents with additional data not part of salt"); - test.todo("returns false for documents with missing data"); - test.todo("returns false for documents with altered targetHash"); - test.todo("returns false for documents with altered proofs"); - test.todo("returns false for documents with missing proofs"); - test.todo("returns false for documents with altered merkleRoot"); + describe("given insertion of an empty array, should return false", () => { + test("given insertion into an object", () => { + expect( + verify({ + ...document, + credentialSubject: { + ...document.credentialSubject, + newField: [], + }, + }) + ).toBe(false); + }); + + test("given insertion into an array", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push([] as any); + expect(modifiedCredentialSubject.licenses[2]).toEqual([]); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + }); + + test("given insertion of a null value into an object, should return false", () => { + expect( + verify({ + ...document, + credentialSubject: { + ...document.credentialSubject, + newField: null, + }, + }) + ).toBe(false); + + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({} as any); + expect(modifiedCredentialSubject.licenses[2]).toEqual({}); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + test("given a null value is inserted into an array, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push(null as any); + expect(modifiedCredentialSubject.licenses[2]).toBe(null); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + test("given an altered value type that string coerce to the same value, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("string"); + modifiedCredentialSubject.licenses[0].class = 3 as unknown as string; + expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("number"); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + test("given a key and value is moved, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + + // move "id" from credentialSubject to root + expect(modifiedCredentialSubject.id).toBe("urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42"); + const id = modifiedCredentialSubject.id; + delete (modifiedCredentialSubject as any).id; + expect(modifiedCredentialSubject.id).toBeUndefined(); + + expect( + verify({ + ...document, + id, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + }); + }); }); }); }); diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index 4ed4086b..3ea4850a 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -679,7 +679,7 @@ export const BATCHED_SIGNED_WRAPPED_DOCUMENTS_DID = freezeObject([ "0x153aeb0f59bef692f1b5bec2f20fc08f72863b0a670e87ac7c68460d311fc7a574189bb92056a15f1ef283c1784303528364d2124a006de3dba586f6162df5481b", }, }, -]); +] satisfies V4SignedWrappedDocument[]); // Freeze fixture to prevent accidental changes during tests function freezeObject(obj: T): T { From b36426e617d10eaa8fcbedbf5304e7cfbca5778f Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 13:44:17 +0800 Subject: [PATCH 099/107] refactor: typo remove redundant top describe --- src/4.0/__tests__/verify.test.ts | 296 +++++++++++++++---------------- 1 file changed, 147 insertions(+), 149 deletions(-) diff --git a/src/4.0/__tests__/verify.test.ts b/src/4.0/__tests__/verify.test.ts index eb8061a8..edd587fb 100644 --- a/src/4.0/__tests__/verify.test.ts +++ b/src/4.0/__tests__/verify.test.ts @@ -4,148 +4,94 @@ import { V4SignedWrappedDocument } from "../types"; import { verify } from "../verify"; const TEST_DOCUMENTS = { - "Document without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot)": + "Documents without proofs mean these documents are wrapped individually (i.e. targetHash == merkleRoot)": SIGNED_WRAPPED_DOCUMENT_DID, - "Document with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot)": + "Documents with proofs mean these documents are wrapped as a batch (i.e. proofs exist, and targetHash !== merkleRoot)": BATCHED_SIGNED_WRAPPED_DOCUMENTS_DID[0], } as const; -describe("signature", () => { - describe("verify", () => { - Object.entries(TEST_DOCUMENTS).forEach(([description, document]) => { - describe(`${description}`, () => { - test("given a document wiht unaltered data, should return true", () => { - expect(verify(document)).toBe(true); - }); - - describe("tempering", () => { - test("given a value of a key in object is changed, should return false", () => { - const newName = "Fake Name"; - expect(document.issuer.name).not.toBe(newName); - expect( - verify({ - ...document, - issuer: { - ...document.issuer, - name: "Fake Name", // Value was originally "DEMO STORE" - }, - }) - ).toBe(false); - }); - - test("given a key in an object is altered (value kept the same), should return false", () => { - const { name, ...issuerWithoutName } = document.issuer; - - expect( - verify({ - ...document, - issuer: { - ...issuerWithoutName, - fakename: name, // Key was originally "name" - } as unknown as V4SignedWrappedDocument["issuer"], - }) - ).toBe(false); - }); - - test("given a new array item is added, should return false", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); - modifiedCredentialSubject.licenses.push({ - class: "Class 2A", - effectiveDate: "2020-06-05T00:00:00Z", - description: "Motorcycle", - }); - expect(modifiedCredentialSubject.licenses[2].description).toBeDefined(); - - expect( - verify({ - ...document, - credentialSubject: modifiedCredentialSubject, - }) - ).toBe(false); - }); +describe("V4 verify", () => { + Object.entries(TEST_DOCUMENTS).forEach(([description, document]) => { + describe(`${description}`, () => { + test("given a document wiht unaltered data, should return true", () => { + expect(verify(document)).toBe(true); + }); - test("given a key in an item is removed, should return false", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(modifiedCredentialSubject.licenses[0].description).toBeDefined(); - delete (modifiedCredentialSubject.licenses[0] as any).description; - expect(modifiedCredentialSubject.licenses[0].description).toBeUndefined(); + describe("tempering", () => { + test("given a value of a key in object is changed, should return false", () => { + const newName = "Fake Name"; + expect(document.issuer.name).not.toBe(newName); + expect( + verify({ + ...document, + issuer: { + ...document.issuer, + name: "Fake Name", // Value was originally "DEMO STORE" + }, + }) + ).toBe(false); + }); - expect( - verify({ - ...document, - credentialSubject: modifiedCredentialSubject, - }) - ).toBe(false); - }); + test("given a key in an object is altered (value kept the same), should return false", () => { + const { name, ...issuerWithoutName } = document.issuer; + + expect( + verify({ + ...document, + issuer: { + ...issuerWithoutName, + fakename: name, // Key was originally "name" + } as unknown as V4SignedWrappedDocument["issuer"], + }) + ).toBe(false); + }); - describe("given insertion of an empty object, should return false", () => { - test("given insertion into an object", () => { - expect( - verify({ - ...document, - credentialSubject: { - ...document.credentialSubject, - newField: {}, - }, - }) - ).toBe(false); - }); - - test("given insertion into an array", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); - modifiedCredentialSubject.licenses.push({} as any); - expect(modifiedCredentialSubject.licenses[2]).toEqual({}); - - expect( - verify({ - ...document, - credentialSubject: modifiedCredentialSubject, - }) - ).toBe(false); - }); + test("given a new array item is added, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({ + class: "Class 2A", + effectiveDate: "2020-06-05T00:00:00Z", + description: "Motorcycle", }); + expect(modifiedCredentialSubject.licenses[2].description).toBeDefined(); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); - describe("given insertion of an empty array, should return false", () => { - test("given insertion into an object", () => { - expect( - verify({ - ...document, - credentialSubject: { - ...document.credentialSubject, - newField: [], - }, - }) - ).toBe(false); - }); - - test("given insertion into an array", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); - modifiedCredentialSubject.licenses.push([] as any); - expect(modifiedCredentialSubject.licenses[2]).toEqual([]); - - expect( - verify({ - ...document, - credentialSubject: modifiedCredentialSubject, - }) - ).toBe(false); - }); - }); + test("given a key in an item is removed, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[0].description).toBeDefined(); + delete (modifiedCredentialSubject.licenses[0] as any).description; + expect(modifiedCredentialSubject.licenses[0].description).toBeUndefined(); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); - test("given insertion of a null value into an object, should return false", () => { + describe("given insertion of an empty object, should return false", () => { + test("given insertion into an object", () => { expect( verify({ ...document, credentialSubject: { ...document.credentialSubject, - newField: null, + newField: {}, }, }) ).toBe(false); + }); + test("given insertion into an array", () => { const modifiedCredentialSubject = cloneDeep(document.credentialSubject); expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); modifiedCredentialSubject.licenses.push({} as any); @@ -158,26 +104,26 @@ describe("signature", () => { }) ).toBe(false); }); + }); - test("given a null value is inserted into an array, should return false", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); - modifiedCredentialSubject.licenses.push(null as any); - expect(modifiedCredentialSubject.licenses[2]).toBe(null); - + describe("given insertion of an empty array, should return false", () => { + test("given insertion into an object", () => { expect( verify({ ...document, - credentialSubject: modifiedCredentialSubject, + credentialSubject: { + ...document.credentialSubject, + newField: [], + }, }) ).toBe(false); }); - test("given an altered value type that string coerce to the same value, should return false", () => { + test("given insertion into an array", () => { const modifiedCredentialSubject = cloneDeep(document.credentialSubject); - expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("string"); - modifiedCredentialSubject.licenses[0].class = 3 as unknown as string; - expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("number"); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push([] as any); + expect(modifiedCredentialSubject.licenses[2]).toEqual([]); expect( verify({ @@ -186,24 +132,76 @@ describe("signature", () => { }) ).toBe(false); }); + }); - test("given a key and value is moved, should return false", () => { - const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + test("given insertion of a null value into an object, should return false", () => { + expect( + verify({ + ...document, + credentialSubject: { + ...document.credentialSubject, + newField: null, + }, + }) + ).toBe(false); + + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push({} as any); + expect(modifiedCredentialSubject.licenses[2]).toEqual({}); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); - // move "id" from credentialSubject to root - expect(modifiedCredentialSubject.id).toBe("urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42"); - const id = modifiedCredentialSubject.id; - delete (modifiedCredentialSubject as any).id; - expect(modifiedCredentialSubject.id).toBeUndefined(); + test("given a null value is inserted into an array, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(modifiedCredentialSubject.licenses[2]).toBeUndefined(); + modifiedCredentialSubject.licenses.push(null as any); + expect(modifiedCredentialSubject.licenses[2]).toBe(null); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); - expect( - verify({ - ...document, - id, - credentialSubject: modifiedCredentialSubject, - }) - ).toBe(false); - }); + test("given an altered value type that string coerce to the same value, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("string"); + modifiedCredentialSubject.licenses[0].class = 3 as unknown as string; + expect(typeof modifiedCredentialSubject.licenses[0].class).toBe("number"); + + expect( + verify({ + ...document, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); + }); + + test("given a key and value is moved, should return false", () => { + const modifiedCredentialSubject = cloneDeep(document.credentialSubject); + + // move "id" from credentialSubject to root + expect(modifiedCredentialSubject.id).toBe("urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42"); + const id = modifiedCredentialSubject.id; + delete (modifiedCredentialSubject as any).id; + expect(modifiedCredentialSubject.id).toBeUndefined(); + + expect( + verify({ + ...document, + id, + credentialSubject: modifiedCredentialSubject, + }) + ).toBe(false); }); }); }); From 2c0df7d87e764751ca144615438e05190f117e8a Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 14:34:18 +0800 Subject: [PATCH 100/107] fix: fixtures and test it will break on update --- src/4.0/__tests__/digest.test.ts | 62 ++++++++++++++++---------------- src/4.0/__tests__/sign.test.ts | 4 +-- src/4.0/fixtures.ts | 54 ++++++++++++++-------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/4.0/__tests__/digest.test.ts b/src/4.0/__tests__/digest.test.ts index ce50445d..d36e2a39 100644 --- a/src/4.0/__tests__/digest.test.ts +++ b/src/4.0/__tests__/digest.test.ts @@ -10,7 +10,7 @@ const ROOT_CREDENTIAL_TARGET_HASH = ROOT_CREDENTIAL.proof.targetHash; describe("V4 digestCredential", () => { test("given all testobfuscated documents are generated from the ROOT_CREDENTIAL, ROOT_CREDENTIAL_TARGET_HASH should match snapshot", () => { expect(ROOT_CREDENTIAL_TARGET_HASH).toMatchInlineSnapshot( - `"4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec"` + `"dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa"` ); }); @@ -66,7 +66,7 @@ describe("V4 digestCredential", () => { `); expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", + "410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7", ] `); @@ -105,9 +105,9 @@ describe("V4 digestCredential", () => { `); expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(` [ - "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", - "236baa019d96f812bf333c8ed2599823f5c796471860538c4e526edae6c88b2c", - "0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f", + "410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7", + "21f8a6f2f464ff14afbc52e4dcb965d7891a7c63fd37eb93f8f98477dcdfc7f9", + "228eb6b469ca3a475238455f11125b7edc826c6dc3ae727d023d1eb71d0e60d6", ] `); @@ -128,40 +128,40 @@ describe("V4 digestCredential", () => { proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + targetHash: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", proofs: [], - merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + merkleRoot: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", salts: "W10=", privacy: { obfuscated: [ - "ba719e50b3af802c74e306f028009c7e4726d29ecd03cd05668fb82eafef34d9", - "45648522f4477a017a60d7131fc191fc1dc49e4de7e67e3e42f04e31f964357a", - "b05fa7c260585b3a73eb3f64610a8ae1a282902d7ef36817bf798aa2e9f25dc5", - "b03c7f68ee230f53a8e0a06f5f5c2ca1b2195b97425ae63623cfc84b50730163", - "bd5e40059db1699bb49d5cf07d306d558ebd241d48f6f83d05ccd2a22acc2387", - "47b25ce748576c172fe6268c8de2916e08ea23fe56bea155c2eb97c787c37b0b", - "0231e0eeaf917998c5d7834172e99add661c91a71ea3ce81d4f10bb7d196337f", - "b2d480de30ca619feae838cb859178476e571fb5cbce50eaa0f9f19ea3d0404f", - "0e4344b5675a18e80c32c683362f99a47aa1e815a6ae495f3696e89b83b4a4bb", - "7e1c13f6b1ef867b6e75810ab6629fab33f2cd895212b1231c354496b50b355a", - "cb0800c5926b75dc379c73c1d6d92d459f9adacd1ebffd8ea8165248d55a2485", - "5f13a0a8b4e3bd8030bc8b8bd9e30af7f71a2b113e8c67232ddd47f6beffab34", - "668e8bc4e2fd13809dd8c815d8c26819794b28a334a2a6d4dc0564e306cbdde8", - "236baa019d96f812bf333c8ed2599823f5c796471860538c4e526edae6c88b2c", - "110cf75ee55d9e5d7db95becf4fb6d4162cdb8223d83d097b5f47fc1e3e7d378", - "0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f", - "b9d3f903419f585d7f174802496aeeef1e15d9cac513da074026c8b186314a14", - "18c6ebe5e22f6c9a272a8bbec47f261d1c5eff5599eaf47885e0c43b6dd24149", - "ad93c39a9559065c4e133a9fbd26f557fc0c8bed74982162187148d093b03a92", - "02a9da9a4199f9e6f6e2f4205b76a456d954e0835914c69dcb8479b2dc8d1ab0", - "ea083928953e5f9901c58f4cb0c367a2f4ab4b67fbba46b77fa76502512c1493", - "c9687c9bfffa291fee284c2d208b6fb983473e46513ed490c99ec4c5fc6cf194", - "f3e68c691c2fc1a3453c7584a4faf51eda264decd0e469e1807d5055d02e26f8", + "0fd4ac5ade244ee2fe47437ea43cd142479540b878fffc86662600fce4d47ef5", + "0cc6e4c50bb090af720d224a9bd73be4c0d72831fc701907339693cdcb34ede3", + "d2afadbec39872577eae0d5e4ea3b590608cab744f214a2f703f65a5b41683cb", + "569b596d71fb145e9c87be1b301da3cbc89cfc104627f6dd95c8123f7974e9c6", + "5dee7aa2b48b9a5edae5042459c7c3eeaa8c5b13132b1477641c727a89471b36", + "2a1ff14be659821afa68edc245f67b9e25331b450715b41348ed2405334d5abd", + "0f1d172b5352464121dffc550e16f64b8019637e39768c506fd2e517a0da305d", + "ff4eeed1d7bb70aef646d46dc75b3408cf019e0daeaf2ef0313974690d8beef2", + "2a4e9fd43be0221af2f02a6b959edf704b630559268a0b2a657fe046e282fbb8", + "bbdf2b05cc0bd2a64fc3483211806e2f863bea05fba81f63092ec07c4ee8ebd9", + "bd9acf6bef05f94720f0fa9c5540bb9db7d6370c39e2bbbee31e408842985dae", + "410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7", + "32a55f464387e37df8f74cf3de6a8e04626e19c2f4d5ec8931aee4b866329fb8", + "21f8a6f2f464ff14afbc52e4dcb965d7891a7c63fd37eb93f8f98477dcdfc7f9", + "6bc7b2350b59b02a44d2593ee7538590114544bc3a39fe9564ee49b387c883c4", + "228eb6b469ca3a475238455f11125b7edc826c6dc3ae727d023d1eb71d0e60d6", + "d6e7027dd62b265e83aaf306f095446efab2677e940a0fbb118cc91dd8226fdb", + "da26ce90128b1cfd747218cad4c7e86b83f6ab236598ce18aecc3b10426b1b71", + "5edea35aa869577c2fd14159534b08da061a7833665cf1cc28f400474c26be01", + "d55b72f926f7adaa5ea4f271ffc2f574e4859b05c16042f5b604e52f044a11d0", + "dd69de699de90b82cfb658fc304ae4f3131e841d56fd68eb20e17af73613a4d3", + "280bc622006d5ec28a42096b57f15f8238df27762c3f754d56dcd89f3aa02c25", + "3d8bc5cbcd2826489cdc80a64d586a4d220d975bc2848aa535bd1e4f17dc619f", ], }, key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", signature: - "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c", + "0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c", }, } as unknown as V4SignedWrappedDocument; diff --git a/src/4.0/__tests__/sign.test.ts b/src/4.0/__tests__/sign.test.ts index 613452e5..17d8927c 100644 --- a/src/4.0/__tests__/sign.test.ts +++ b/src/4.0/__tests__/sign.test.ts @@ -22,7 +22,7 @@ describe("V4 sign", () => { expect(Object.keys(proof).length).toBe(9); expect(proof.key).toBe("did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller"); expect(proof.signature).toBe( - "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c" + "0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c" ); }); it("should sign a document with a wallet", async () => { @@ -42,7 +42,7 @@ describe("V4 sign", () => { expect(Object.keys(proof).length).toBe(9); expect(proof.key).toBe("did:ethr:0x906FB815De8976b1e38D9a4C1014a3acE16Ce53C#controller"); expect(proof.signature).toBe( - "0xac4c7fb9ed25878038b42f2da8a2a6f8cd553383debc5d0bf6e362b810c05ba779dd50235f0bab560bf970af564587b4756bd0a06dd4d42862875e68280d39201b" + "0xb850c0f34d834a7de4185eead5295eeebf9a56ada4603d94f10a72e0fe144179140ce534ddb4123c6fffbf5594d112e1f679e537b29c5188ccd2b940c4798dd11b" ); }); diff --git a/src/4.0/fixtures.ts b/src/4.0/fixtures.ts index 3ea4850a..d904aca7 100644 --- a/src/4.0/fixtures.ts +++ b/src/4.0/fixtures.ts @@ -1,8 +1,8 @@ import { V4Document, V4SignedWrappedDocument, V4WrappedDocument } from "./types"; -const SAMPLE_SIGNING_KEYS = { - issuerId: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", - public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", +const ISSUER_ID = "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89" as const; +export const SAMPLE_SIGNING_KEYS = { + public: `${ISSUER_ID}#controller`, private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655", } as const; @@ -15,7 +15,7 @@ export const RAW_DOCUMENT_DID = freezeObject({ validFrom: "2021-03-08T12:00:00+08:00", name: "Republic of Singapore Driving Licence", issuer: { - id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + id: ISSUER_ID, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, @@ -55,7 +55,7 @@ export const RAW_DOCUMENT_DID_OSCP = freezeObject({ validFrom: "2021-03-08T12:00:00+08:00", name: "Republic of Singapore Driving Licence", issuer: { - id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + id: ISSUER_ID, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, @@ -99,7 +99,7 @@ export const BATCHED_RAW_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + id: ISSUER_ID, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -141,7 +141,7 @@ export const BATCHED_RAW_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: `did:ethr:${SAMPLE_SIGNING_KEYS.issuerId}`, + id: ISSUER_ID, type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -216,11 +216,11 @@ export const WRAPPED_DOCUMENT_DID = freezeObject({ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + targetHash: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", proofs: [], - merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + merkleRoot: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", salts: - "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY1OTc2ZWJlMTE5MjY5ZjZhZjhlZjIzMGI0ZjgwNDgyMzA5ZmFhNWRjOWI3ZjMwYjc3NTZmNDAxNDI4ZjJlZTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmNlZGZiODkxMzcxMjc3NjA2NmU2MmQyMTU5MWNjYzQ0Y2I5MzlmY2U4MDRlYjZjYTUxZWI2ZTIxYTRmNzc1MCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjFjZmMzMDRmNzNkZTFlNTFhYzFmMDczZGI1YjUwYjEzNGMyNjhjYzRjZjIxNDIwZmVhMmY0NjM1YmMzZTllMzEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZmExZDEwNjBkNTc5NTU1YWRkY2IwZDU4ZGI4MjNkMDMzOWNkNThiMGUzOTdhNzJjZWI2NDgzZTY3NjNjMjgxYiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1OTA1MDg3YmU5NWE0YWIzMjY0NGQyZDYzYzE0YjE4OGU0ZTcxODYwN2M1YjYxOWRmNzQyNjIxZTY3N2Q2ZWFkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMzRhZTM3YmEzNjY3M2Q3MzM0ODhhNzlkMzZkZDI0NDBkNzZiMzBhMGUyMGNkMzU0YjYyOTU4ZTljZTU2NTA4NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMWM2NTM2YmI0MDU1ODMyMTMwN2Y2NzE1MTc4MjA3NzU2YTBlNzBiNTE1NzAxM2Q5MWIwN2MxMWVlODdjNzYyYiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyY2E4NjJjMDdjYzkwZTczYjA3ZmQxMmU0YjM2ODhjNDRkZDc5OTI3ZTAzOWQ0NWU5Njc0YzM0ZmYyMmMwYTUxIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "W3sidmFsdWUiOiJjYTE3MjI0YTg3NTk0NzNhNGUyZjM2YTM3NjljODU0OGM5M2RjZTQ3NmI0MjEwMTNmYTEzYTA2OTUxYTI1YzhjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMyYzk3MDRhMmIxMWU1MzhjYTBlZWJhYWMzNmU0ZmRlYTU4ZGY0MDI4ZGVmZmJjOTkzODBjYjZkZjU3MTdiODEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDE3YTFhNzY2YWM5MzhkOWVlY2FhNWRmMDdhOTBhODkyZTM3ZjA1ZmI3MzY5ZWRjNGQwYWNmNWZkMjE4YjhlMiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImE1Y2FkZDIzMmQ4ZjhlMGU4ZmFhODA3OTMyYWNkZjJhODc1OWY4MWEyMDNmNmEwMjdjMTJhOTBhNjg0MWE1NWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI2NGE4NjBmMmQwOGZjNGIyMDhjZWE3NmU2MjBjM2YwMDRlYzhiMmRhYjdhZTc5ZTZkZmMxZjllYjU1MWMxMmM3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMzcyZmJlNDllMTdjMTA2MGEzNTk1NjVhNDA2M2VlMjkyODU1ODg0OWZhZWRkODE1MGQwYWZjZmY3NzQ2M2NmMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTE0ODAwOGQxY2JjMzM3ODJmNmNlN2UyOTFhMTM5ODE1YmRhYWFjZjg2MWYxNGYwMDk0NWU5YWIwZTk2ZjdkNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI5Yzg4NTc4ZTAzZWRiNjc3NTdiNDI0NzY4MTliNWZiMGQwMDlhMGU0ZjZmMjllOGNhNTBlMWJmNWI4NWQ2ZmZiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImJhOTk1NDA1YWQ4ZmZlZmJmYTExMDA2MzE2ZDdkNjM4NDRlODk2ZmMxNDhkZjdlMWViZjNkYWZhYzI0NTY4YjQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZWY1ZTEzODU4YWM1MzYwNTMzODkxZjkxOGJhYjlmZjI3YmQ5YjhiYzA5MzQ4ZTc4NWMwMmI2YjYzZTcxZDNhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImY2N2ZmNGMyZTUwYzU0M2ZmYmEyNGJiMzA4OTY5NGUxOWViZDNjOWJhMmE5MTM3NTA3N2YzOWRlN2M0ODVmY2MiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYzQ2MjkwMDQwMDMwOGM1YjA3MmM1Y2NlMjQ4YmQ2YjRiOTgxODBjOGQ2ZDQ2YjIwNWE0MGE2YzNjMDhmMzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2QwMThlMjllOTI3MTcxN2UwNGI5ZWYwMDJkNWVmNTRmMTRiZmI3NmJlYzdhMTlkN2ZlYjcyMzM3NjU2NzU4ZiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImYxNDllNmM5M2ZhNTU5ZjM4OWRjZTcwMDMwYmRmYmVlNmU3OWRjODM1NjJhYmVhN2FkOTMzNTA0OWM4MTI5NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyZGZjM2VmNDQ2NzA0MmMzMDI4NDU0MTNmMWE5MDQxYmQ2NjY3NThmNDM4MWU4OWU5NWIzZjc4NjYyYzE1NTc5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjY3MjNhYjA4MzhkODMzNTJlMjhhNjQxMjliMzEwOWQzNzA5YTk3NWYyMDljZmM2NjE0MTNmODUyYzFmZTY0YzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMWZiNTRmMGVhZDgyZmEyMzVlMmU5Y2RkY2ViMGQ2NTllMjRhYzhhMzM4OThmODdmMzkzZTBmZmY5OTE1Y2NmZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjhjMzJlZmNlOGE2ZTIzZGMxNzUzOTVmNzMxM2ZjMGQ4ZTk0ZTA0YjU3ZGMwMzI4YmEyNTk0NTgxMTJjNWQ4MjQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYmQ1YzM2Yjc1YTVmNzEwYmQ2MzJhOWM1MTY1ZGVkNmI4N2YzODY1MTg5YWRjZTc3M2FlNDMxMTlkYzljMGFiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1MmYzYTU1ZWI3MGZmZjMyYjEzZWMyNjlkZjJmODM4Mjg4NTc5YmUyMDg1NmQ3Mjc5NmI3YmY3MDFmMjY5MjU3IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTJjZWZiMjkzZGEzZGNmNTIzZjAwNjkwN2YyNTljNTIyOTc3MzQwZjYzNmY4YmU1MmM5ZTg5ZTEzZGVlZTk0MiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNDNiMjFkZGU2NTY2NjI5MTNjNzJiMDExMzBkMjM0MzlkY2NjMTNiZjVhYjYwMjY5YTVmYjM3NmFlMzBkNjlkMiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiI0NDRmZmU3OTMzMmRkZjExOWRiZWUzZjYzYWQyZTQyYzE2ZTczMDdkMGVmOTE3NDJlZDEzNTkxYWY5M2U0NWRjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", privacy: { obfuscated: [], }, @@ -275,11 +275,11 @@ export const WRAPPED_DOCUMENT_DID_OSCP = freezeObject({ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + targetHash: "96a8e79b4c9b14dde88d9d6db26916215502ed09f458d0dc387c1ca73f3549f0", proofs: [], - merkleRoot: "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + merkleRoot: "96a8e79b4c9b14dde88d9d6db26916215502ed09f458d0dc387c1ca73f3549f0", salts: - "W3sidmFsdWUiOiI2ZTJhN2E4MjM1NzMzOTM2MGJkYzE0OTYzMTUwNzAyNTg2N2QyOWYxM2YxYjMwZmFmMDQ4Y2VmM2QyMzc2YWM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImRkZGI5MDVmNmIzZWQ0OGVlMzkwZDljYjQ4MTdkMjk4OWMyY2VmMzQyM2UxNGE4YjQzMDMzYmFhNDM0NmY2ZGUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWNiZjBjYjY2ODZkNWIyMzJkM2QwZDZiNTk1MTA0NDc0OGY4NmNlMjJkZDE1OGIxNzUwZTc2YWUyYzAzZDkwNyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImJlZjJmZjNhM2MxNDEzMDYxMDVkNGMzMTJlZmZkN2M2OTUxN2U3ZjM5YTBjYTQ3ZTZkZTk4Y2M1ZTFkMzllY2UiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1N2M5YWIzYzRlM2RiNjE5Nzc0NjZhNjQ4ZmM5ZThjNjI2NzQ1ZDE4NmIyYWRlZDIxNDhiNDQwYWU0MWRmYjhiIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZmYwNDE0MjcxYWYwNzk5ZWM4YzgwMTQzMzI2NmVmNGE5YzAwNzQxMDVlNTNmMmQ3OWYwYjYzYjBjNjk1Y2Q2MCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiOTEyNjc2YWZjZTkyYjBhNGU4MzhkNzZjZDYyZmNmMGY0YTc1NmE1MTI0NWI0YWU4YTQxN2ZkNzYxNmVhMDVkMSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmNDYzYmNkYWIzYTcwNGYzMWRhOWQ5MjUwNTM3OTE1MDBiYTdjMDQzZTNiZDEzNzU3ZTgyNTI5MzYzZGNhNjc0IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc2YWIzZWQwODk2NzZkOGJmM2FhZjE3OGQyYTUzYzI3NGZjMGUyMmFiMjJkNjU3NmIxYTUzYTAxMjMxNjUxYjAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNjMyYzI5MzdhZGYwMGIwYWU0NGRlYzZkNGI1NmJmN2RkYTU3ZDEzYjk3MWUxNzhlZjJiZTdmNDMwNDJlMTI4YiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkxOGNiYjU1MDg4ZGFjNDEzMzYxODEwZDE0ZTBlM2U4Mzk4N2Y2N2NjZDU1YTE5OGQ2YzZhOWEzYWM4Y2VkMDQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImI5NmVkN2FjZDZjZjQwNmViZGMxZmJlY2VhY2M1ZWYxZDhkODlmZDNhZTZmZTRjMzkyMTM3M2NhY2YxYTc5NDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTVmNDFmYWZiYjNhZmY4NmM4ODU0ZTRjOWQ4MTAzY2E3NGM0NTBiODg3ZmQwOGMwNDFiYzI4NzlmN2RiMDAzYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjU2ZmU1YjZmZDYwNjE0OWZmYzhhZGZkNjY2ZDgxNGJiYzZjNWMzZjU0YjA2ZmQ4YjJjZTFmNDc2M2RkNWMwNmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJmNWZhM2MyMDhiODRiNDdkZDczYTIwYmM5YjczYWY1MTI3MzIwZGUxNTIwNGExMjA0NjgzZDI0NGUwNzhkYmU5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjkzODcxZTQ2MmEzZjIyNDE1YzU2ODhhOGQ3NGYyZjFlZjMwZTEwYWNlOWZiMmIyNTllOWVjMmVlZWVjMWI2NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiN2FmNWFjNzViYjNkZWYyNTQwOTViNWE4ZDFiNGFkZmJiZmQ3YTM3MTY4N2UzZGY2OTJmYjhlMmM2YTMyZjZiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjAwZDA2MTlhNzQ0ZWUxODIwMzQ5NzZhZmRjZmU2ZjE5ZDE1NWZkMjgwZDQzYjM0ZjhjNDM4OGJiMDQwOTBkOTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiOTUyNTdkMjJmMGMyYzRkNTY3MjkzMWM0OGY1OTBjY2RmMGU4NzNmY2ZmMTdjZDA2NmRmNjBjNzMxMzgxZGMwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI3ZTZlODE5MmM1OWVhMDllOGUxZDM3NTI4NGEzN2JhMTFkNmFlNTVmNmFiNGIwMjRhMGU4YjM0M2MwZTlhMWI4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMTRmN2U1ZWU4Zjk0ZTAyZDFiODcxYjE4YzRmZjAwOTA0ZDc4OGNhMjU4NDhlYjVlOTMwOTZmNTFhMzI0NGIwYiIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjFjNzRmODhjODNhOWZhYWQ5YjU1MjE4NjcwZDg5NzM0NTY0MjY2OTg5MzdlYTQyNmFkZmY4ZmVmOTYzMWVkNzMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjA0N2I1YzA0NDAzMjlmM2ZkODZmMDQxODRmMTk1OGZlZDQyNDhiMDI2YmM2NGI5NDM5MmNiMGRjYmU5M2VlYWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjQyZGM4Y2EzZGJiMjI2NGI4ODg0OWIyNWFkZWQ0ZWE2NWQ2ZmMzYTZlNWUyNjExMGE1MDUwNmRjYzdhYzhmNzciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiODg2MjJkNDRjMTBiY2VlMzUxMGRiNmE4ZjNhMGYxM2M1ODBmNmZlNWY3OGNkODVmM2FjYTIzMGE2M2QyYTZjMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "W3sidmFsdWUiOiI2ZGM0NzZkYmZlOTBiMjMxYzg5MjFiYmIyNTg3NTFkZGJlNzExMGFhOGM2Y2U3ODQwOGZiNmFhMDBiYzI3NTZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImY3MTA2OTA3YzJhMDRjNzRiOWYyMzg2Yjg3MGJhMmE1MmYxMmFiZDA1Y2FiOGUyOGFhOTdhZWJhYTk1ZTRhMjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNGEyNzhiYmM0MTllYTY4YTRlN2M1Y2YxMDdmZDUxNWNkNDgwYmNhNDZlZjdjNjQ1NGFiMjQwZWRmY2JjODM4NyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjVhNDQyMTE0M2YxNGZjYmY4MTdhMjgwMWJhOTUzZDY1ZWQ2NDUxNDE5YTkzY2VkM2Y2MWY1NzcxNjI0YzA3MzUiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiY2M1OGMwZTU3NjgzYmM3NDA2NjM5ZWMwYzI3MjIxMGYzZmQzMGVkYmQ4ZjU5MjM2MjBiMzgxNzYyM2U4YWEwIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiYzUxYWJlZGE2MTNjNGZiMDY5ZDQ2MzNhMTcwNzIyYWE2YmRiMGFlZDkzOTAzZjRiMmJjZTAyZjU1NWZiNDY5OSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiM2U3MjVhNzk1YzZmNDQyNDc5NzZkNWUzNGQ3M2YyMzA4ZTEyNjc3MzNjODY1YWNjYTUwYTc1ODVmNDIwNDI4ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJkY2QyMzM5MTVmODVkMjIzNzFlNWFkMzc2Y2I3M2ZkNGVkODcxNzVjNWUzYTgxYjhiMjhmNWE1ZDA3NDllMzdkIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjJlY2MyOGVmZDNjNTU5ZmU5MGNiYTg2NDQ4MmM3YTk0YmU5NTRhNzY4NWY2ZmI5ZGQ5MDJiZTUwMDE5OTRjMzgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiOTI3ZDM1ZDUzZTE3MThkMWE3ZWM2NDFkMzFmYmU4OThiMGI5YzkyZGRhZmMwMmU4MGU5NjgwMThlNmUyZTgyNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjI3OTVkNzgxODY2ZjNmYTI4YThhMzIxMmI4ODdjMGNjMzkyMWM2Njc2NmIxZDJmZDdkYjY1NzBhMGM1Y2ExZTEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjA2ZmViZjYyNTZhY2FlMWIyZjg5MTE0MjM2NzY2YjNkMzIzZDMwNzQzZGJiZDRmODI0NWU1NjA5MTRhZjcwZmQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiNjBkY2E2Njg3MGNmNzMxZDkwNjcwZTA5YTg2MDA2ZTJlNjRjYzRiZGQ3ZWYyYTQyZjMwYWUwY2M4N2Q3ZmY3MSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjdhMjBiMmMwMTk3MDE0YjA5OWY2MDg4NGM4YjA1NWMzYmIxNjY3YzRiZWRiMjIzMDY4NGI2YTMxZjY1ZjQ4ZTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJiYzRiMGJiODkzZTIxNGYyMzlkZmQ4ZGY2MGQ1ZDdkZjZmNzBjNjI4N2YzNmQ3ZWVlZGVhMjE1NDMzNjE5ODA2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY4N2ZhNjNmMDY1MmY2NzJjYjkyOWI2YzFiYWMwOGFmOTNiZjc5NTgwOWYzMjlmZmEyYmJlMmE1NGJmOThiODEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYzc4MDE5Njk4NWEzNDFjYzJkZmQwMzA0YTdhOTUzZGFkZGE2MmZhYjhlOGYyZTFlZWViNGNlZDM3YTIwZDJiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6Ijc0Y2ZmNjIxZDhhZTM2YmQxNWFlMzFkZTIwMTE1Mzg1MDZmNDhlMjkxNjExYmM3ZmIzNjVkYjNkMTI3ZGYxYzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZDg2ZGQ0ZjNkNDA1N2FiN2I3NzI2ZWU0ZmE5Y2U1NjlkZTc3YmZiNGNhNzBmYTY5NzVlNWZjOWEwOTAwZjdkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI4ZDhmZTQ3MGY3NmEyODNkNzQ1ZmM4MGYwZjEyZmZjOGU5ZjU3OGE5NjY5NDRkODBmOTM2MDhjNWZjOTM4ZTAzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjkwOGFlOTBiYTI0YzZjNmRhZDcxZmEzZmQxNGJiYWYxNTMyOGJhZDliN2Y0NDI5Y2IzN2Q5YjIyYWM4MzRjNCIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjM2MDU1ODQzZWU1MTNkZDVlYmY5MjUzMTM5ZTI0YjkxZWM5MDk4NzI1Y2ZhZDQzYmE3ODkxNWRiMGFhODJjYTEiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjQzMjJhMDA0NzNjYzIzNWVmNzFkYWNiMDFlNjcwMGNkZWE1YjUxNWIzNzM2MDY0ZWU0NzJhNjM4ZWFmYjUxNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjRmNTJjZTRjN2JmNTQxMWNhMGFhOTJhZWY1YWE5NWQyNDYwOThhZmE5N2Q2YWI0YTMwY2JkYTBhNzM5ZTg0ODAiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiMzM3YTkyODViYjFkYzgyYzYyMTFhMTBhMDA5ODkxOTNjMzQyZjczMDFiZTYwZjEzY2E4M2IzYWMxMTQ2MDkzMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", privacy: { obfuscated: [], }, @@ -295,7 +295,7 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -331,11 +331,11 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681", - proofs: ["ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001"], - merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + targetHash: "b449f7fb56ed2961749110037a1c40464548db71c386c925e58211505785e6ce", + proofs: ["9d71ec0572df22c133656a45bd644a7e6bb1f44cb4670473263d7f2b6f2d4b04"], + merkleRoot: "2e8038ad3403396fad4af3158f132c06d34fd31516b328b07001bc44f48fbeff", salts: - "W3sidmFsdWUiOiIwMzgyZWU0MjFlNTgyMzJlMGJjNTI0MDY1NjhhZTdiNWZhNDhlOWRjYzczZmVkM2VhOTZkOTAzNGQ4ZWZkNDlhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImViMmI1ZmIwODhlZDk5YzY0NzJmYmQ4MjMyNjVhYjlkMzZhNmQ0MDFiMGEyNmM5N2Q3Zjc0MDkwYWJiYTE4OGEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiYWNmNGFhMGJmZDEyMTRmZGEwZDAzNzczNmNhMDFiMzhkOTBlZDA4MzQ5ODUwMWY5NDlmOTgyNjg1NmE2MTM3OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjM5Yzc1MjFmNDdlNTJlNWZlOGI2NmEyYjhjZmEzYTQwNDM1ZjQ2Y2NiOWY3MGQxYWY4MThkNmNkNzk1MzQ4MzQiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiNWY3MmI3N2M1NjU3YjZiOTMyN2QyMGYwZTUwYzQxMGVmNzRlNDMzYTM0NTRlOTRmMTEzOTZmMTIwYzJjMjc5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiOTU1MjExMzNmNzgzZjI5NzlkODgyMzI4MDlmMDgxNmUzNDVmMjE4MTk0ZTJkMTgwYzZmYTcwMTBhNDI4ODFkYyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTQwZmVlNjI5Y2M2MjY3Njk1NzA4YzY2ZTg2OGI0ZjVhZTBiMDg1YjYzNDczM2I1YWE1N2M4ZTQxM2I1NTliNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJiMjFmZTkyMDNlNWYxNzFjMjE1NDIyY2Y2ZTZkOWEzNzFkMmIyZGJkMjU5ZDE0Y2JmMDZiYTllZjQ4NzEzYzk3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImE5Mjg0OTFiMzYyNzcyMjUxNzM0ZDEwNGQwOGJiMTgzMjczM2JlYzZiYjdmYWI0YTE4ZTdiNGRlY2QzMjE0YmMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNWViMmMyNTU2ODgyZjgzZjM0MTQ5MTczMGM0OWI4NTE4OTljMTA3NzY2Yzk5YWQwYzhhMGRkZTQxNDkzZjYxYyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjVlMTgyNThiODdjMjJiMGIzY2Y4OWVhMjZhOTI5N2ZkYjVmYjI0NTA2Mzg0MzNlYzcxN2YyYTBmMzE0NTJlMmYiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYWU3ODllZmVjZmE4ZmEzMmZmNTdlYTNjYTJmZjJjZmVmOThmOThmZGVhY2U4M2Q0NGEwNTVjZGU4MDE2MDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMGQ1ZWI3YmY3OTE1MmI0M2EwMTQyYjhkNTVkMzRmMzRlOGQ0N2U5MzgwMDVhYTVjMTRjMjk2MWNiMjM0OWFkYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImIyZWM0NTYzMjhlMzhjYWYyYmNlYTk5YWM5NTc0ZWEzZGExNjJkNmJlZDVlZTRkZGM3MzI5NjhkMmIwMDZjZGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3OTIwNmQwNDNlNjVhYmU2MmFmYmIwNmViNGMxMWNmZTg2NjkzNWRhOGYyYzkzODI4NjVlZjg1YjVhMzc1NTUwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAyYjJjMTlhNDY2ZjNiYWU5ZDhmNWI0NzUyZGEzZmU2MWM1MTIxYzc1Mzc0N2I1NzNmNTdlYzBmOGExODIzZDciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTNiOTA2MjdlNWY1OTM4YjdkNWY0NzhjM2JiNDE4NjkwOWI1OTY1MjVhM2I4NWNlZWU2M2JjNGYyZGQ1ZmRkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImNhOGFlMzIwZjU5NWJmMmUwMmQ3MTAxOGE5YjA0YWI1MDNiOGJiMTIyM2I2NDM5MWNjYTZiY2MyOTk5MjdmOTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYjk1MzhlMjkwNzdlMzg1MzlmN2E0MTliZGMzNmFhYWVjMTMyNGY0MjQzOTA2YzIyN2JmMzhkZDU4NTA4M2NlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyN2RmYWY1Y2M1NzJlMTFmODhkMDFmNGUwMzQ0ZWRiMzA4OWQ2NDc5YmY0ODIyODBiZmNmNGE4YzZjOTY1NmIwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTBjMjFhMzQ1NTZjZTMzNjViOTE5YmUwNjlmODVkM2EyNmQ2MTcyM2Q4YjlmN2QwZjY5ZTkzMjMxZDY4ZGM2ZiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiODY4YzNjOTYwNGJlYWMxMDZiZDQzMTc3NDRjYTNjZTI2YzU3YWFiODc2YWUwMWQwZWU5NzNmYWE1NDNlNWRmOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJiNzY3YTcyMmUzNmU4MjAxYWNjMjg2MTAzYzBkZmY4Y2I2MjI4YWQyMmVkZWJmMjc4ODcxOTQ1MDVmMGVlNmNkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "W3sidmFsdWUiOiIwMmFjZWRiYmRlZTcwMTQwOTA1MjgxN2ZmNDRlNzE3YzMyNTM0MDEyNjM4NzQyYTE1MzEyMjJkNWRhNTMyZDUyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMwZDlmZDBkOWU2ZTc1YTg5ZTYwM2JkZjE3YWZjYzY1NDgzYzIxOWNkMTZiYjJhNmZjOTdhNjI3MjNjZTczNDciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZjQxY2JlNzMyMjVjMDI3YjY0ZmQzMDhlMTMxY2FmMTZkYjVkYTNiNTM1ZDE0NDQ0NThmYjk5NDUwMWU0NGU4MCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjE3OTU1NzVkZjZkYzZmOTg4NzFmMDk2M2I3MTdkZDg2ZmU4MmQzMjg1NzliMGYyNDhlMDIzMDE4ZTU4NjVlMmIiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1MTFhZTZjMzA3NzNlZDhkMWUyZmYwMjU0ZWI3MWUxMGMxNDVmMWRhMGIzNzQ2ZjliNjE5NjNiMTFjYzA1Njg5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiODQ0YzEwZDVlNDhmNTM4NzY4YjkxNTJkOGFkZWE0NDk2NDlmOTk2MjQ1OGQwZTIwYzg0NmRlYjJiZDRhZTg2ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiN2I1MjY1ZjVlYTUwNGI4ZTQ4YTczMWFjNzMzMGE4NWY0OGIzMTQyMTZiOWY0ZDVjM2I5MzQ0ZDZlY2U0MGFhNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjODc5OWRhMWVkNTdiZTk2YWRkNjRlNDc5Y2E0YWNiODVjNzgzMDYxZmM3ODU1NDNkZWZlODdlMmRmYmUzNWMxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImMxYjUyNTkyN2MyM2IwZGJkNWU1ODlkMjFiOWYzODIwMTg3Y2M2MTdiNjhmMjlkMTI4YWU0Njc0MjA0Njg2M2UiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYjZhOWM4M2I4Y2MzYjZjNzlhOWYxNDE3NWJkODgwYjAxZDhkMzJjYTY5N2FkNDUzZWQyYWQyNTM2OWQ0ZGRjMyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjBhZTM4MjhiZmVhMDk0MDZkZmI3MzliNjI0OTE3NTUxMGM4NWZhMzU2NWM1MjViNWYyMjAwNTMzYzMxN2I4Y2EiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjQ2MTVhMTNmNjU4NzA1MjdlNmJlYjc0Y2FhNzVkNTM5ODU0YzA3MjRjMmFhOWNkN2JmMTkxZjE4ZTk1MjViMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2EyZDJkYWQ2MmFiYjAyOGMzZGUzZDMxNzUyNTE4YTQ1MGY1NGZhMjc5YjcwYWU2ZDlhNWQ5ODhkMDQwZGQwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImM0OWExYjc4ZjVkNjM5MzIyMzMxNzg4MTBkMzMwMmIzY2Y3MzUwYjJkMTk3YTAyNzRkNWE0MGVjODE1MzE3NTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJiYjNkMzZhMTI4OGE4NTc0ODhjZDRmNjFmMmU1ZGNmNmM5MmE5YWVhNTM0MTM2ZjJmYjIyZTVmMGM3ZWZjZjYzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImNhYzcwOGEzMTRhYTE3MDU1Y2YwMTRlZjNiOTc4MTUzYTc5Mzk0OGQyZTJhZDgyZmY5MzY2YzA5ODUxMjY5NmEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMGQ5OTE4MzJhNWJmMTMzMmVlMDczZTNiMTE0ZjgzOTUwZTU5ODJkZmU4OWU0YzYwMDM5ZjliZjE4M2FjNDQ4MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjUzMzlkY2I0MDg1YTVlNmJjNWQzMjc5NGY0NjA5MzUwN2EzZGYwNDMxNzBkOTFiZDViOTAwZDA1MjBhYThjZTMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNDc3MGRlNzRkNTkzYzAwZGNjMTQ5OTA0OTExZTNmOTFjODE0NmQyZGM2MTUyZDQ5NjMzNjFiMzE4M2JjZWE5NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJlNzAyM2Q4YjQ1NTAyY2JlM2M2MmY4ZjRhMWU2MWJhOGI5YzRjMDQ0NThiMmM1YzY1OGU0NzE5YTllOTBmZTQ2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiY2FhOGM0YWQ0MjFiNDgxODA1ZTIxMTRhNDExOWI2MjZmNmQ3YTk1Y2ExMjI4YmI4NmM3MTU3ZDI0ZDQ3NGM5ZSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMDQyZmNiZjBmNTcyMzQ3Y2IxNDIwNmEwOGVmZjc4NTM3YjE0NmIwYzE2NjQyMzZmOTkyNjQwZjUwNDc4NjNiNSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJmZjU4OTM0OTUyNGM4MGJhYjlhYzM4YTJlNDAzM2Y2MDc5NmM0ZTJjNDMzY2QwODZmMTE5MWE2NmJiNmYzYjdmIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", privacy: { obfuscated: [], }, @@ -349,7 +349,7 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ name: "Republic of Singapore Driving Licence", type: ["VerifiableCredential", "OpenAttestationCredential"], issuer: { - id: "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + id: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", type: "OpenAttestationIssuer", name: "Government Technology Agency of Singapore (GovTech)", identityProof: { @@ -380,11 +380,11 @@ export const BATCHED_WRAPPED_DOCUMENTS_DID = freezeObject([ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001", - proofs: ["08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681"], - merkleRoot: "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + targetHash: "9d71ec0572df22c133656a45bd644a7e6bb1f44cb4670473263d7f2b6f2d4b04", + proofs: ["b449f7fb56ed2961749110037a1c40464548db71c386c925e58211505785e6ce"], + merkleRoot: "2e8038ad3403396fad4af3158f132c06d34fd31516b328b07001bc44f48fbeff", salts: - "W3sidmFsdWUiOiI1ZGU5NjM1OGRlZGVjODIwMWFhYTJjZjRkOGI4ZjYyNzA0NDc2ZTRkYzdiZDQ5OGEwOTc5MGI1MDc1NzY0NWQyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImI2YzkyMDc4YjM5OTU3NmM1YmNlZjVhOTBjYmMwMzQxNjEzN2UyZTBjODAzM2JhM2FiZDNkYTU3NjEzZGYwZGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNjM0Nzk4NGNiN2Q3MDA4NTczNjJlYmFjZTgwMDEzZjQwODA0NzNiMDkyYjAyMzQ3ZDY1MGUwMGM4MGRjMTQ5ZiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImVkNGJiMDQwMWQ0NDY0NTljNjQ1YWUyMjI2MGQ5M2YxYzllOTkwYzg3ZGZhYzM2YjI2N2QwMTVlZTM4MDRlMWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1NTU4OThjZmE3ZWUxZThjY2VmOGIxYWMyYjBjY2RlNWJlZmFkMDE2NTJjOTY3NmVlM2VlNGExNTNmODlhM2JhIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNmVmYjc3ZDY2ZDQ3MDcxNzQ5NTIyOTI4MzE1MDI5OTlkYjViN2FmOWQ2NjQ3OWRiYzFiMzlhZjhiNmI0ZTU1NSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMmNjOGU1OTcxMDFhYmE0NzIyYmYzZGY0MWQwYjYyNjM3MGFiNzRhOGZiZjhjMmM2MTlmNDIzN2Y3ZWFhMzJmOCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmY2IxNDNjOWUwNmQzMTZlNzdlYmIzMDkxNDFiMWI5ZDE1YTRhMTQwYzk4ODFhYTY0OTY0MTQ1NDVhM2RhYzUxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc0NDVkNDQzMDdmOWU2ODBjNTFkMWYxZjEzYzE5YTU2NDFkNmFiNmMzYmE0NmRjMDFmMWIyZDU0YTE4ZWJkMTEiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYmVjYjY3OTBjMWQ1MmE2YmRlMjFkMzRjYTUxZDQ5ZGViNDBkMmE0MzQ2MGYyZjI4ZDA1NzY0YjVhNzFmODFhNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkzMzQ2ZDg0NmI1MzJkZmY0NTc5NGQ0OTVkNzk3ZDZmYzZkYTRjYTgyMzRmMzQwNjljODNjYTU0MWQwNzA4NzgiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImU5OTZiYTM2NTFiZjZiMGY2YWU4NDUyYjljOTM3NjBiZjRkYzRiNDYwNjZlNGZkYTQ0YzBiZTJkY2FhMmFjZTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZGIwZGQ0NjcyYWYxZjdkZGJiNGVhNTYwOGMzZjBkMjc5MDljMTAxM2FjYmJkZDcwOWVjMDZjYmQ0YTM3OGZmMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjA1MWZkNTUzN2Q0ZjViNzVmNWY2MTk2NmI1ODc4MDhkNGNlZTQ1N2EzOTNlMmQ0MWZmZGFjMzllZjk5ZjcwNTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3NTU3NzYxY2VhZTgzYzVmN2YwZTNkMmM4YjYxYzllMWVkZTNmMTIyNzVmZDgzOTIyNDNjNmQ4ZDNhNTYzNTM4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImQ3NzE4NTkxMDUwNjc1MDhlMGQ5ZTMxMWYxZDRhZTEyNmM3ZTNkMjhjMzY4OWUxNThhNjFjNmUwZGQxZTRkMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZWEzZmEzNDdjNGUxODVhNDhhY2NiMTY4NjMzNDY2ZmMyMjc4YjdkYmEzNzNiNDM1ODI1YmIzNDdiNzMxMDA3YiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVjNDZhODc5ODI0MGQwMGNkOTJkNTBjYzE5ZTVhZDE3NjUyYjJiZjJmOGQyOTlhOWVhOTNlYWE4YmE2MDgwNzgiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZhYTk2NWViNjIwMmNlNDhhNjczNzRiZTA5M2VhNTJjNDRhM2JhMzZmYmNkZjBhMWY1MzZjNTZkM2NmOGVlNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYjlkZTJiZDRkMGZjMmVlYmZkNDE1ZTcwZTRlMjYzY2Y5MDEwM2MyYzEyMTlkZDU0OGYzOWE4Y2I2NWNlYjU5YyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "W3sidmFsdWUiOiIxMmQ0OTZmZjhmN2E3Yzc5YzhhNzJlMDA4ZTc4NGFhNDRkNjkwMmUyNDFlMGM4YTA3NTNmMzY5MjM3YzEzODY5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjI5NjRjZDQxMzgzZjllYjRlN2I3NmY2NjAzMmI1OWFiNDIwOWFjMDY3YTQ2YjUxNGQ5ZDI2NDAyMWNmMmI1YWIiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZDhmNzcwNjI4ZDNjZjMyY2FkNjc4NmRlODIxMjYwMjE5MmY5NDEyNzEwYTk4OWQ2NmVhOTliNDIwYmRhYmYxYSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjA2NGM5NzQyZWRhODgzMzUxNjAwNDIyYWY5YjU3MmU1NzkwMmFiMjMxNDExNzEyMTc1ZDYwYjA5ZWJmNjcyZTYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJhZDQ3NTM3M2Y0MGNmZjA1YTY0OGI2OGNkYjllMDA4Zjk1Zjg1Njg3YzNiM2I5ODJkMTNmNmI1ZmQxMGQ4ODU3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzEzYjcyMWVhNDA5MGQzNDFhMTg2ZmVjYWM3Nzc0ZGJhYjExYjE5ZjYzOTg3ZjZmZDM1ZWZjODkwNjgyNDAzZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGNkNjY4MTFmMGVlNGQxMTdjZGIzYWQ5NTFjNTE1MmIwYWZjYTlmNmZlY2I1YjQwODc2MDRlMGVjMmY3YzAxNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIxYTU4YzJiOWQ4YzQwYjUzMTdmYjFmNjcwNmM5Yzc4YTI4OWJiZTFiZTQ4Y2I1MGE5NTdkYzkwMjIzNjdmY2I4IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImFkYjM3ZDAwYWU0ODhiZDgyOTFkOTAxZWY2NmIyNTY5ZTQwNDRmZjBhN2U5YTU2MTI2N2U2NGYwZmJiOWE3ZWQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZGY2NDNiMGM0ZjI3Y2RiYmJmMDJkZmY2MjVmM2ZlMjA2MGMxMjI4MDNjZDM5ZDA0YWY5YTg0N2M3ZmFlOGUwZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImMzNGE0ODBjM2YxYWMyYmY1ZjgyNjc5NDQ0ZDM1ZWE1ODYwMmQ5ZGU0YjA2ZTc3MmI2YTMyYzI2ZWUyMTA2YjEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6Ijc5MWM1Yjc1ZjkwZjQ4NzEwY2E5ZjY4ZDIzYTBjZTU2MjE0YWZjYjhkOTIwNTljYTRhMzUzNmJmNzYxNTY5N2IiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODJmMjdjMGY4MDk5OTkxMDhmZGRhYmQ4ZTNiN2NjYTQ3YmNhMTVkZmI4ZjE4MzE4MTVmY2VkNjdiMmYwNjhlYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjNlYjFjNDdjZTkwOWRiNWQ1MzhmNjE2OWNiMjlkY2M2NzY0YzAwNWRjY2Y3ZGMwZTU4OTI1YmUzMmFmNTJiOTMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMGJmYmI1MWI4YTc5NTNhZWM2OTE5ZjgwZGU0MzRmOWM1ZWVhZDY2Zjc0MTA1YmY0ZDAxM2Y1MjVjNGE0ZDczIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImE5YjU3YTU2NWRmMjYxZTVkNWQ0YTJiMDM2YjQzMzgwN2NjNTlhZmE0NGU1MjZjOGY2MGViYjJlNzYzZWUwYjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZDJmMDA4OGRiZWU2YjhhZGExZDlmZWJiNzIxZGJmOTZmMDU5MDVmNDc0YjVhYjZjNjNiYzZhYTg0NjdhNzVlOCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU2ODc2MDBiNGU4NTEzMjc1ZGUxNDU1YjNiYmU5ODczMTZkMGMwNDI3ZTA3YjgwYWZhZWE2ZDQ5YjZlYzBhMWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZmOTVmZjlmNGExYTQ0ZTVhMjFlNzQzMWFkODFhOGI2MzAzZjhjODQzOWJhYWFiZmRhYjU4NDhmNTkyN2Y0ZGEiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcxNGVmZGUyYzdiODhjY2IzODIyMzY0OWMxM2U3MmVhNjNmNWViODcyNGM2MzkxMGJhZWJmOTE0YWYyYWJmZCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", privacy: { obfuscated: [], }, @@ -436,17 +436,17 @@ export const SIGNED_WRAPPED_DOCUMENT_DID = freezeObject({ proof: { type: "OpenAttestationMerkleProofSignature2018", proofPurpose: "assertionMethod", - targetHash: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + targetHash: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", proofs: [], - merkleRoot: "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + merkleRoot: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", salts: - "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY1OTc2ZWJlMTE5MjY5ZjZhZjhlZjIzMGI0ZjgwNDgyMzA5ZmFhNWRjOWI3ZjMwYjc3NTZmNDAxNDI4ZjJlZTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNmNlZGZiODkxMzcxMjc3NjA2NmU2MmQyMTU5MWNjYzQ0Y2I5MzlmY2U4MDRlYjZjYTUxZWI2ZTIxYTRmNzc1MCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjFjZmMzMDRmNzNkZTFlNTFhYzFmMDczZGI1YjUwYjEzNGMyNjhjYzRjZjIxNDIwZmVhMmY0NjM1YmMzZTllMzEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZmExZDEwNjBkNTc5NTU1YWRkY2IwZDU4ZGI4MjNkMDMzOWNkNThiMGUzOTdhNzJjZWI2NDgzZTY3NjNjMjgxYiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1OTA1MDg3YmU5NWE0YWIzMjY0NGQyZDYzYzE0YjE4OGU0ZTcxODYwN2M1YjYxOWRmNzQyNjIxZTY3N2Q2ZWFkIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMzRhZTM3YmEzNjY3M2Q3MzM0ODhhNzlkMzZkZDI0NDBkNzZiMzBhMGUyMGNkMzU0YjYyOTU4ZTljZTU2NTA4NyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMWM2NTM2YmI0MDU1ODMyMTMwN2Y2NzE1MTc4MjA3NzU2YTBlNzBiNTE1NzAxM2Q5MWIwN2MxMWVlODdjNzYyYiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiIyY2E4NjJjMDdjYzkwZTczYjA3ZmQxMmU0YjM2ODhjNDRkZDc5OTI3ZTAzOWQ0NWU5Njc0YzM0ZmYyMmMwYTUxIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "W3sidmFsdWUiOiJjYTE3MjI0YTg3NTk0NzNhNGUyZjM2YTM3NjljODU0OGM5M2RjZTQ3NmI0MjEwMTNmYTEzYTA2OTUxYTI1YzhjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMyYzk3MDRhMmIxMWU1MzhjYTBlZWJhYWMzNmU0ZmRlYTU4ZGY0MDI4ZGVmZmJjOTkzODBjYjZkZjU3MTdiODEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDE3YTFhNzY2YWM5MzhkOWVlY2FhNWRmMDdhOTBhODkyZTM3ZjA1ZmI3MzY5ZWRjNGQwYWNmNWZkMjE4YjhlMiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImE1Y2FkZDIzMmQ4ZjhlMGU4ZmFhODA3OTMyYWNkZjJhODc1OWY4MWEyMDNmNmEwMjdjMTJhOTBhNjg0MWE1NWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI2NGE4NjBmMmQwOGZjNGIyMDhjZWE3NmU2MjBjM2YwMDRlYzhiMmRhYjdhZTc5ZTZkZmMxZjllYjU1MWMxMmM3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMzcyZmJlNDllMTdjMTA2MGEzNTk1NjVhNDA2M2VlMjkyODU1ODg0OWZhZWRkODE1MGQwYWZjZmY3NzQ2M2NmMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTE0ODAwOGQxY2JjMzM3ODJmNmNlN2UyOTFhMTM5ODE1YmRhYWFjZjg2MWYxNGYwMDk0NWU5YWIwZTk2ZjdkNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI5Yzg4NTc4ZTAzZWRiNjc3NTdiNDI0NzY4MTliNWZiMGQwMDlhMGU0ZjZmMjllOGNhNTBlMWJmNWI4NWQ2ZmZiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImJhOTk1NDA1YWQ4ZmZlZmJmYTExMDA2MzE2ZDdkNjM4NDRlODk2ZmMxNDhkZjdlMWViZjNkYWZhYzI0NTY4YjQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZWY1ZTEzODU4YWM1MzYwNTMzODkxZjkxOGJhYjlmZjI3YmQ5YjhiYzA5MzQ4ZTc4NWMwMmI2YjYzZTcxZDNhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImY2N2ZmNGMyZTUwYzU0M2ZmYmEyNGJiMzA4OTY5NGUxOWViZDNjOWJhMmE5MTM3NTA3N2YzOWRlN2M0ODVmY2MiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYzQ2MjkwMDQwMDMwOGM1YjA3MmM1Y2NlMjQ4YmQ2YjRiOTgxODBjOGQ2ZDQ2YjIwNWE0MGE2YzNjMDhmMzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2QwMThlMjllOTI3MTcxN2UwNGI5ZWYwMDJkNWVmNTRmMTRiZmI3NmJlYzdhMTlkN2ZlYjcyMzM3NjU2NzU4ZiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImYxNDllNmM5M2ZhNTU5ZjM4OWRjZTcwMDMwYmRmYmVlNmU3OWRjODM1NjJhYmVhN2FkOTMzNTA0OWM4MTI5NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyZGZjM2VmNDQ2NzA0MmMzMDI4NDU0MTNmMWE5MDQxYmQ2NjY3NThmNDM4MWU4OWU5NWIzZjc4NjYyYzE1NTc5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjY3MjNhYjA4MzhkODMzNTJlMjhhNjQxMjliMzEwOWQzNzA5YTk3NWYyMDljZmM2NjE0MTNmODUyYzFmZTY0YzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMWZiNTRmMGVhZDgyZmEyMzVlMmU5Y2RkY2ViMGQ2NTllMjRhYzhhMzM4OThmODdmMzkzZTBmZmY5OTE1Y2NmZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjhjMzJlZmNlOGE2ZTIzZGMxNzUzOTVmNzMxM2ZjMGQ4ZTk0ZTA0YjU3ZGMwMzI4YmEyNTk0NTgxMTJjNWQ4MjQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYmQ1YzM2Yjc1YTVmNzEwYmQ2MzJhOWM1MTY1ZGVkNmI4N2YzODY1MTg5YWRjZTc3M2FlNDMxMTlkYzljMGFiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1MmYzYTU1ZWI3MGZmZjMyYjEzZWMyNjlkZjJmODM4Mjg4NTc5YmUyMDg1NmQ3Mjc5NmI3YmY3MDFmMjY5MjU3IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTJjZWZiMjkzZGEzZGNmNTIzZjAwNjkwN2YyNTljNTIyOTc3MzQwZjYzNmY4YmU1MmM5ZTg5ZTEzZGVlZTk0MiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNDNiMjFkZGU2NTY2NjI5MTNjNzJiMDExMzBkMjM0MzlkY2NjMTNiZjVhYjYwMjY5YTVmYjM3NmFlMzBkNjlkMiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiI0NDRmZmU3OTMzMmRkZjExOWRiZWUzZjYzYWQyZTQyYzE2ZTczMDdkMGVmOTE3NDJlZDEzNTkxYWY5M2U0NWRjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", privacy: { obfuscated: [], }, key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", signature: - "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c", + "0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c", }, } satisfies V4SignedWrappedDocument); From 1f0ddc1763d8a5c522c78024e5f8c0e7f66194e4 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 14:43:15 +0800 Subject: [PATCH 101/107] refactor: improve error message to provide more guidance --- src/4.0/__tests__/obfuscate.test.ts | 4 ++-- src/4.0/obfuscate.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/4.0/__tests__/obfuscate.test.ts b/src/4.0/__tests__/obfuscate.test.ts index 593dbbb2..2b315d2c 100644 --- a/src/4.0/__tests__/obfuscate.test.ts +++ b/src/4.0/__tests__/obfuscate.test.ts @@ -258,12 +258,12 @@ describe("privacy", () => { "credentialSubject.arrayOfObject[0].doo", ]) ).toThrowErrorMatchingInlineSnapshot( - `"Obfuscation of "credentialSubject.arrayOfObject[0].doo" has resulted in an empty {}, this is currently not supported."` + `"Obfuscation of "credentialSubject.arrayOfObject[0].doo" has resulted in an empty {}, this is currently not supported. Alternatively, if the object is not part of an array, you may choose to obfuscate the parent of "credentialSubject.arrayOfObject[0].doo"."` ); expect(() => obfuscateVerifiableCredential(wrappedDocument, ["credentialSubject.object.foo"]) ).toThrowErrorMatchingInlineSnapshot( - `"Obfuscation of "credentialSubject.object.foo" has resulted in an empty {}, this is currently not supported."` + `"Obfuscation of "credentialSubject.object.foo" has resulted in an empty {}, this is currently not supported. Alternatively, if the object is not part of an array, you may choose to obfuscate the parent of "credentialSubject.object.foo"."` ); }); diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 55bd2995..7e0b4020 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -114,6 +114,8 @@ export class CannotObfuscateArrayItemError extends Error { export class CannotResultInEmptyObjectError extends Error { constructor(public field: string) { - super(`Obfuscation of "${field}" has resulted in an empty {}, this is currently not supported.`); + super( + `Obfuscation of "${field}" has resulted in an empty {}, this is currently not supported. Alternatively, if the object is not part of an array, you may choose to obfuscate the parent of "${field}".` + ); } } From 1e5b23db811d60565932715e64f8826273c0e83f Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 14:54:18 +0800 Subject: [PATCH 102/107] fix: array items cannot be undefined --- src/4.0/types.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/4.0/types.ts b/src/4.0/types.ts index 15b615c1..42516006 100644 --- a/src/4.0/types.ts +++ b/src/4.0/types.ts @@ -224,13 +224,11 @@ export const V4Document = _W3cVerifiableCredential // [Optional] Attachments attachments: z .array( - z - .object({ - data: z.string().describe("Base64 encoding of this attachment"), - fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), - mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), - }) - .optional() + z.object({ + data: z.string().describe("Base64 encoding of this attachment"), + fileName: z.string().min(1).describe("Name of this attachment, with appropriate extensions"), + mimeType: z.string().min(1).describe("Media type (or MIME type) of this attachment"), + }) ) .optional(), }) From 9f0ba1fe8aec4b90aa0757b765bcc40209a43880 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 15:16:07 +0800 Subject: [PATCH 103/107] chore: commit generated fixtures and allow ci tests to catch any drifts --- .gitignore | 2 - src/4.0/__tests__/fixtures.test.ts | 17 +++ .../batched-raw-documents-did.json | 91 +++++++++++++ .../batched-signed-wrapped-documents-did.json | 121 ++++++++++++++++++ .../batched-wrapped-documents-did.json | 117 +++++++++++++++++ .../__generated__/raw-document-did-oscp.json | 51 ++++++++ .../v4/__generated__/raw-document-did.json | 47 +++++++ .../v4/__generated__/sample-signing-keys.json | 4 + ...igned-wrapped-document-did-obfuscated.json | 61 +++++++++ .../signed-wrapped-document-did-oscp.json | 64 +++++++++ .../signed-wrapped-document-did.json | 60 +++++++++ .../wrapped-document-did-oscp.json | 62 +++++++++ .../__generated__/wrapped-document-did.json | 58 +++++++++ 13 files changed, 753 insertions(+), 2 deletions(-) create mode 100644 src/4.0/__tests__/fixtures.test.ts create mode 100644 test/fixtures/v4/__generated__/batched-raw-documents-did.json create mode 100644 test/fixtures/v4/__generated__/batched-signed-wrapped-documents-did.json create mode 100644 test/fixtures/v4/__generated__/batched-wrapped-documents-did.json create mode 100644 test/fixtures/v4/__generated__/raw-document-did-oscp.json create mode 100644 test/fixtures/v4/__generated__/raw-document-did.json create mode 100644 test/fixtures/v4/__generated__/sample-signing-keys.json create mode 100644 test/fixtures/v4/__generated__/signed-wrapped-document-did-obfuscated.json create mode 100644 test/fixtures/v4/__generated__/signed-wrapped-document-did-oscp.json create mode 100644 test/fixtures/v4/__generated__/signed-wrapped-document-did.json create mode 100644 test/fixtures/v4/__generated__/wrapped-document-did-oscp.json create mode 100644 test/fixtures/v4/__generated__/wrapped-document-did.json diff --git a/.gitignore b/.gitignore index 22c2dc53..efd31b1a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,3 @@ yarn.lock /public /vc-test-suite /.vscode - -/test/fixtures/v4/__generated__/ \ No newline at end of file diff --git a/src/4.0/__tests__/fixtures.test.ts b/src/4.0/__tests__/fixtures.test.ts new file mode 100644 index 00000000..2593f20a --- /dev/null +++ b/src/4.0/__tests__/fixtures.test.ts @@ -0,0 +1,17 @@ +import fs from "fs"; +import path from "path"; +import * as FIXTURES from "../fixtures"; + +const PATH_TO_GENERATED_FIXTURES = path.resolve(__dirname, "../../../test/fixtures/v4/__generated__"); + +describe("assert fixtures matches generated JSON fixtures", () => { + for (const fixtureName of Object.keys(FIXTURES)) { + const jsonFixtureFilename = fixtureName.replace(/_/g, "-").toLowerCase() + ".json"; + + test(`${jsonFixtureFilename} should match the one defined in fixtures.ts`, () => { + const fixture = FIXTURES[fixtureName as keyof typeof FIXTURES]; + const jsonFixture = fs.readFileSync(path.resolve(PATH_TO_GENERATED_FIXTURES, jsonFixtureFilename), "utf-8"); + expect(fixture).toEqual(JSON.parse(jsonFixture)); + }); + } +}); diff --git a/test/fixtures/v4/__generated__/batched-raw-documents-did.json b/test/fixtures/v4/__generated__/batched-raw-documents-did.json new file mode 100644 index 00000000..965d391e --- /dev/null +++ b/test/fixtures/v4/__generated__/batched-raw-documents-did.json @@ -0,0 +1,91 @@ +[ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ] + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "Jane Doe", + "licenses": [ + { + "class": "3000A", + "description": "Motor spaceships with unladen weight <= 3000tonnes", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ] + } +] \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/batched-signed-wrapped-documents-did.json b/test/fixtures/v4/__generated__/batched-signed-wrapped-documents-did.json new file mode 100644 index 00000000..658d22d9 --- /dev/null +++ b/test/fixtures/v4/__generated__/batched-signed-wrapped-documents-did.json @@ -0,0 +1,121 @@ +[ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681", + "proofs": [ + "ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001" + ], + "merkleRoot": "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + "salts": "W3sidmFsdWUiOiIwMzgyZWU0MjFlNTgyMzJlMGJjNTI0MDY1NjhhZTdiNWZhNDhlOWRjYzczZmVkM2VhOTZkOTAzNGQ4ZWZkNDlhIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImViMmI1ZmIwODhlZDk5YzY0NzJmYmQ4MjMyNjVhYjlkMzZhNmQ0MDFiMGEyNmM5N2Q3Zjc0MDkwYWJiYTE4OGEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiYWNmNGFhMGJmZDEyMTRmZGEwZDAzNzczNmNhMDFiMzhkOTBlZDA4MzQ5ODUwMWY5NDlmOTgyNjg1NmE2MTM3OSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjM5Yzc1MjFmNDdlNTJlNWZlOGI2NmEyYjhjZmEzYTQwNDM1ZjQ2Y2NiOWY3MGQxYWY4MThkNmNkNzk1MzQ4MzQiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiNWY3MmI3N2M1NjU3YjZiOTMyN2QyMGYwZTUwYzQxMGVmNzRlNDMzYTM0NTRlOTRmMTEzOTZmMTIwYzJjMjc5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiOTU1MjExMzNmNzgzZjI5NzlkODgyMzI4MDlmMDgxNmUzNDVmMjE4MTk0ZTJkMTgwYzZmYTcwMTBhNDI4ODFkYyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMTQwZmVlNjI5Y2M2MjY3Njk1NzA4YzY2ZTg2OGI0ZjVhZTBiMDg1YjYzNDczM2I1YWE1N2M4ZTQxM2I1NTliNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJiMjFmZTkyMDNlNWYxNzFjMjE1NDIyY2Y2ZTZkOWEzNzFkMmIyZGJkMjU5ZDE0Y2JmMDZiYTllZjQ4NzEzYzk3IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImE5Mjg0OTFiMzYyNzcyMjUxNzM0ZDEwNGQwOGJiMTgzMjczM2JlYzZiYjdmYWI0YTE4ZTdiNGRlY2QzMjE0YmMiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNWViMmMyNTU2ODgyZjgzZjM0MTQ5MTczMGM0OWI4NTE4OTljMTA3NzY2Yzk5YWQwYzhhMGRkZTQxNDkzZjYxYyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjVlMTgyNThiODdjMjJiMGIzY2Y4OWVhMjZhOTI5N2ZkYjVmYjI0NTA2Mzg0MzNlYzcxN2YyYTBmMzE0NTJlMmYiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYWU3ODllZmVjZmE4ZmEzMmZmNTdlYTNjYTJmZjJjZmVmOThmOThmZGVhY2U4M2Q0NGEwNTVjZGU4MDE2MDgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMGQ1ZWI3YmY3OTE1MmI0M2EwMTQyYjhkNTVkMzRmMzRlOGQ0N2U5MzgwMDVhYTVjMTRjMjk2MWNiMjM0OWFkYSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImIyZWM0NTYzMjhlMzhjYWYyYmNlYTk5YWM5NTc0ZWEzZGExNjJkNmJlZDVlZTRkZGM3MzI5NjhkMmIwMDZjZGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3OTIwNmQwNDNlNjVhYmU2MmFmYmIwNmViNGMxMWNmZTg2NjkzNWRhOGYyYzkzODI4NjVlZjg1YjVhMzc1NTUwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjAyYjJjMTlhNDY2ZjNiYWU5ZDhmNWI0NzUyZGEzZmU2MWM1MTIxYzc1Mzc0N2I1NzNmNTdlYzBmOGExODIzZDciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTNiOTA2MjdlNWY1OTM4YjdkNWY0NzhjM2JiNDE4NjkwOWI1OTY1MjVhM2I4NWNlZWU2M2JjNGYyZGQ1ZmRkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImNhOGFlMzIwZjU5NWJmMmUwMmQ3MTAxOGE5YjA0YWI1MDNiOGJiMTIyM2I2NDM5MWNjYTZiY2MyOTk5MjdmOTciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYjk1MzhlMjkwNzdlMzg1MzlmN2E0MTliZGMzNmFhYWVjMTMyNGY0MjQzOTA2YzIyN2JmMzhkZDU4NTA4M2NlZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiIyN2RmYWY1Y2M1NzJlMTFmODhkMDFmNGUwMzQ0ZWRiMzA4OWQ2NDc5YmY0ODIyODBiZmNmNGE4YzZjOTY1NmIwIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTBjMjFhMzQ1NTZjZTMzNjViOTE5YmUwNjlmODVkM2EyNmQ2MTcyM2Q4YjlmN2QwZjY5ZTkzMjMxZDY4ZGM2ZiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiODY4YzNjOTYwNGJlYWMxMDZiZDQzMTc3NDRjYTNjZTI2YzU3YWFiODc2YWUwMWQwZWU5NzNmYWE1NDNlNWRmOCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJiNzY3YTcyMmUzNmU4MjAxYWNjMjg2MTAzYzBkZmY4Y2I2MjI4YWQyMmVkZWJmMjc4ODcxOTQ1MDVmMGVlNmNkIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "privacy": { + "obfuscated": [] + }, + "key": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "signature": "0x153aeb0f59bef692f1b5bec2f20fc08f72863b0a670e87ac7c68460d311fc7a574189bb92056a15f1ef283c1784303528364d2124a006de3dba586f6162df5481b" + } + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "Jane Doe", + "licenses": [ + { + "class": "3000A", + "description": "Motor spaceships with unladen weight <= 3000tonnes", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "ce284d21e98ac301c0e963ea6d020570091e05591dc2b787a698512e82b39001", + "proofs": [ + "08969c7baf46807ee65e495b2a93c0e27dfdc77085562ce9ab1249a7fb261681" + ], + "merkleRoot": "6fa0bdfa20b114c3e9e92fb511be62f416533a35df99a440ecc28ff4a3f601d2", + "salts": "W3sidmFsdWUiOiI1ZGU5NjM1OGRlZGVjODIwMWFhYTJjZjRkOGI4ZjYyNzA0NDc2ZTRkYzdiZDQ5OGEwOTc5MGI1MDc1NzY0NWQyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImI2YzkyMDc4YjM5OTU3NmM1YmNlZjVhOTBjYmMwMzQxNjEzN2UyZTBjODAzM2JhM2FiZDNkYTU3NjEzZGYwZGMiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNjM0Nzk4NGNiN2Q3MDA4NTczNjJlYmFjZTgwMDEzZjQwODA0NzNiMDkyYjAyMzQ3ZDY1MGUwMGM4MGRjMTQ5ZiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImVkNGJiMDQwMWQ0NDY0NTljNjQ1YWUyMjI2MGQ5M2YxYzllOTkwYzg3ZGZhYzM2YjI2N2QwMTVlZTM4MDRlMWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1NTU4OThjZmE3ZWUxZThjY2VmOGIxYWMyYjBjY2RlNWJlZmFkMDE2NTJjOTY3NmVlM2VlNGExNTNmODlhM2JhIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNmVmYjc3ZDY2ZDQ3MDcxNzQ5NTIyOTI4MzE1MDI5OTlkYjViN2FmOWQ2NjQ3OWRiYzFiMzlhZjhiNmI0ZTU1NSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMmNjOGU1OTcxMDFhYmE0NzIyYmYzZGY0MWQwYjYyNjM3MGFiNzRhOGZiZjhjMmM2MTlmNDIzN2Y3ZWFhMzJmOCIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmY2IxNDNjOWUwNmQzMTZlNzdlYmIzMDkxNDFiMWI5ZDE1YTRhMTQwYzk4ODFhYTY0OTY0MTQ1NDVhM2RhYzUxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc0NDVkNDQzMDdmOWU2ODBjNTFkMWYxZjEzYzE5YTU2NDFkNmFiNmMzYmE0NmRjMDFmMWIyZDU0YTE4ZWJkMTEiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYmVjYjY3OTBjMWQ1MmE2YmRlMjFkMzRjYTUxZDQ5ZGViNDBkMmE0MzQ2MGYyZjI4ZDA1NzY0YjVhNzFmODFhNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkzMzQ2ZDg0NmI1MzJkZmY0NTc5NGQ0OTVkNzk3ZDZmYzZkYTRjYTgyMzRmMzQwNjljODNjYTU0MWQwNzA4NzgiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImU5OTZiYTM2NTFiZjZiMGY2YWU4NDUyYjljOTM3NjBiZjRkYzRiNDYwNjZlNGZkYTQ0YzBiZTJkY2FhMmFjZTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiZGIwZGQ0NjcyYWYxZjdkZGJiNGVhNTYwOGMzZjBkMjc5MDljMTAxM2FjYmJkZDcwOWVjMDZjYmQ0YTM3OGZmMCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjA1MWZkNTUzN2Q0ZjViNzVmNWY2MTk2NmI1ODc4MDhkNGNlZTQ1N2EzOTNlMmQ0MWZmZGFjMzllZjk5ZjcwNTUiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiI3NTU3NzYxY2VhZTgzYzVmN2YwZTNkMmM4YjYxYzllMWVkZTNmMTIyNzVmZDgzOTIyNDNjNmQ4ZDNhNTYzNTM4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImQ3NzE4NTkxMDUwNjc1MDhlMGQ5ZTMxMWYxZDRhZTEyNmM3ZTNkMjhjMzY4OWUxNThhNjFjNmUwZGQxZTRkMjEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZWEzZmEzNDdjNGUxODVhNDhhY2NiMTY4NjMzNDY2ZmMyMjc4YjdkYmEzNzNiNDM1ODI1YmIzNDdiNzMxMDA3YiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImVjNDZhODc5ODI0MGQwMGNkOTJkNTBjYzE5ZTVhZDE3NjUyYjJiZjJmOGQyOTlhOWVhOTNlYWE4YmE2MDgwNzgiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZhYTk2NWViNjIwMmNlNDhhNjczNzRiZTA5M2VhNTJjNDRhM2JhMzZmYmNkZjBhMWY1MzZjNTZkM2NmOGVlNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYjlkZTJiZDRkMGZjMmVlYmZkNDE1ZTcwZTRlMjYzY2Y5MDEwM2MyYzEyMTlkZDU0OGYzOWE4Y2I2NWNlYjU5YyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "privacy": { + "obfuscated": [] + }, + "key": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "signature": "0x153aeb0f59bef692f1b5bec2f20fc08f72863b0a670e87ac7c68460d311fc7a574189bb92056a15f1ef283c1784303528364d2124a006de3dba586f6162df5481b" + } + } +] \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/batched-wrapped-documents-did.json b/test/fixtures/v4/__generated__/batched-wrapped-documents-did.json new file mode 100644 index 00000000..49d074f8 --- /dev/null +++ b/test/fixtures/v4/__generated__/batched-wrapped-documents-did.json @@ -0,0 +1,117 @@ +[ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "b449f7fb56ed2961749110037a1c40464548db71c386c925e58211505785e6ce", + "proofs": [ + "9d71ec0572df22c133656a45bd644a7e6bb1f44cb4670473263d7f2b6f2d4b04" + ], + "merkleRoot": "2e8038ad3403396fad4af3158f132c06d34fd31516b328b07001bc44f48fbeff", + "salts": "W3sidmFsdWUiOiIwMmFjZWRiYmRlZTcwMTQwOTA1MjgxN2ZmNDRlNzE3YzMyNTM0MDEyNjM4NzQyYTE1MzEyMjJkNWRhNTMyZDUyIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMwZDlmZDBkOWU2ZTc1YTg5ZTYwM2JkZjE3YWZjYzY1NDgzYzIxOWNkMTZiYjJhNmZjOTdhNjI3MjNjZTczNDciLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZjQxY2JlNzMyMjVjMDI3YjY0ZmQzMDhlMTMxY2FmMTZkYjVkYTNiNTM1ZDE0NDQ0NThmYjk5NDUwMWU0NGU4MCIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjE3OTU1NzVkZjZkYzZmOTg4NzFmMDk2M2I3MTdkZDg2ZmU4MmQzMjg1NzliMGYyNDhlMDIzMDE4ZTU4NjVlMmIiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1MTFhZTZjMzA3NzNlZDhkMWUyZmYwMjU0ZWI3MWUxMGMxNDVmMWRhMGIzNzQ2ZjliNjE5NjNiMTFjYzA1Njg5IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiODQ0YzEwZDVlNDhmNTM4NzY4YjkxNTJkOGFkZWE0NDk2NDlmOTk2MjQ1OGQwZTIwYzg0NmRlYjJiZDRhZTg2ZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiN2I1MjY1ZjVlYTUwNGI4ZTQ4YTczMWFjNzMzMGE4NWY0OGIzMTQyMTZiOWY0ZDVjM2I5MzQ0ZDZlY2U0MGFhNyIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJjODc5OWRhMWVkNTdiZTk2YWRkNjRlNDc5Y2E0YWNiODVjNzgzMDYxZmM3ODU1NDNkZWZlODdlMmRmYmUzNWMxIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImMxYjUyNTkyN2MyM2IwZGJkNWU1ODlkMjFiOWYzODIwMTg3Y2M2MTdiNjhmMjlkMTI4YWU0Njc0MjA0Njg2M2UiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiYjZhOWM4M2I4Y2MzYjZjNzlhOWYxNDE3NWJkODgwYjAxZDhkMzJjYTY5N2FkNDUzZWQyYWQyNTM2OWQ0ZGRjMyIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjBhZTM4MjhiZmVhMDk0MDZkZmI3MzliNjI0OTE3NTUxMGM4NWZhMzU2NWM1MjViNWYyMjAwNTMzYzMxN2I4Y2EiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjQ2MTVhMTNmNjU4NzA1MjdlNmJlYjc0Y2FhNzVkNTM5ODU0YzA3MjRjMmFhOWNkN2JmMTkxZjE4ZTk1MjViMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2EyZDJkYWQ2MmFiYjAyOGMzZGUzZDMxNzUyNTE4YTQ1MGY1NGZhMjc5YjcwYWU2ZDlhNWQ5ODhkMDQwZGQwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImM0OWExYjc4ZjVkNjM5MzIyMzMxNzg4MTBkMzMwMmIzY2Y3MzUwYjJkMTk3YTAyNzRkNWE0MGVjODE1MzE3NTEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJiYjNkMzZhMTI4OGE4NTc0ODhjZDRmNjFmMmU1ZGNmNmM5MmE5YWVhNTM0MTM2ZjJmYjIyZTVmMGM3ZWZjZjYzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImNhYzcwOGEzMTRhYTE3MDU1Y2YwMTRlZjNiOTc4MTUzYTc5Mzk0OGQyZTJhZDgyZmY5MzY2YzA5ODUxMjY5NmEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMGQ5OTE4MzJhNWJmMTMzMmVlMDczZTNiMTE0ZjgzOTUwZTU5ODJkZmU4OWU0YzYwMDM5ZjliZjE4M2FjNDQ4MiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjUzMzlkY2I0MDg1YTVlNmJjNWQzMjc5NGY0NjA5MzUwN2EzZGYwNDMxNzBkOTFiZDViOTAwZDA1MjBhYThjZTMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiNDc3MGRlNzRkNTkzYzAwZGNjMTQ5OTA0OTExZTNmOTFjODE0NmQyZGM2MTUyZDQ5NjMzNjFiMzE4M2JjZWE5NyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiJlNzAyM2Q4YjQ1NTAyY2JlM2M2MmY4ZjRhMWU2MWJhOGI5YzRjMDQ0NThiMmM1YzY1OGU0NzE5YTllOTBmZTQ2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiY2FhOGM0YWQ0MjFiNDgxODA1ZTIxMTRhNDExOWI2MjZmNmQ3YTk1Y2ExMjI4YmI4NmM3MTU3ZDI0ZDQ3NGM5ZSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiMDQyZmNiZjBmNTcyMzQ3Y2IxNDIwNmEwOGVmZjc4NTM3YjE0NmIwYzE2NjQyMzZmOTkyNjQwZjUwNDc4NjNiNSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiJmZjU4OTM0OTUyNGM4MGJhYjlhYzM4YTJlNDAzM2Y2MDc5NmM0ZTJjNDMzY2QwODZmMTE5MWE2NmJiNmYzYjdmIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "privacy": { + "obfuscated": [] + } + } + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "Jane Doe", + "licenses": [ + { + "class": "3000A", + "description": "Motor spaceships with unladen weight <= 3000tonnes", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "9d71ec0572df22c133656a45bd644a7e6bb1f44cb4670473263d7f2b6f2d4b04", + "proofs": [ + "b449f7fb56ed2961749110037a1c40464548db71c386c925e58211505785e6ce" + ], + "merkleRoot": "2e8038ad3403396fad4af3158f132c06d34fd31516b328b07001bc44f48fbeff", + "salts": "W3sidmFsdWUiOiIxMmQ0OTZmZjhmN2E3Yzc5YzhhNzJlMDA4ZTc4NGFhNDRkNjkwMmUyNDFlMGM4YTA3NTNmMzY5MjM3YzEzODY5IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjI5NjRjZDQxMzgzZjllYjRlN2I3NmY2NjAzMmI1OWFiNDIwOWFjMDY3YTQ2YjUxNGQ5ZDI2NDAyMWNmMmI1YWIiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiZDhmNzcwNjI4ZDNjZjMyY2FkNjc4NmRlODIxMjYwMjE5MmY5NDEyNzEwYTk4OWQ2NmVhOTliNDIwYmRhYmYxYSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjA2NGM5NzQyZWRhODgzMzUxNjAwNDIyYWY5YjU3MmU1NzkwMmFiMjMxNDExNzEyMTc1ZDYwYjA5ZWJmNjcyZTYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJhZDQ3NTM3M2Y0MGNmZjA1YTY0OGI2OGNkYjllMDA4Zjk1Zjg1Njg3YzNiM2I5ODJkMTNmNmI1ZmQxMGQ4ODU3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzEzYjcyMWVhNDA5MGQzNDFhMTg2ZmVjYWM3Nzc0ZGJhYjExYjE5ZjYzOTg3ZjZmZDM1ZWZjODkwNjgyNDAzZSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiZGNkNjY4MTFmMGVlNGQxMTdjZGIzYWQ5NTFjNTE1MmIwYWZjYTlmNmZlY2I1YjQwODc2MDRlMGVjMmY3YzAxNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiIxYTU4YzJiOWQ4YzQwYjUzMTdmYjFmNjcwNmM5Yzc4YTI4OWJiZTFiZTQ4Y2I1MGE5NTdkYzkwMjIzNjdmY2I4IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImFkYjM3ZDAwYWU0ODhiZDgyOTFkOTAxZWY2NmIyNTY5ZTQwNDRmZjBhN2U5YTU2MTI2N2U2NGYwZmJiOWE3ZWQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZGY2NDNiMGM0ZjI3Y2RiYmJmMDJkZmY2MjVmM2ZlMjA2MGMxMjI4MDNjZDM5ZDA0YWY5YTg0N2M3ZmFlOGUwZSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImMzNGE0ODBjM2YxYWMyYmY1ZjgyNjc5NDQ0ZDM1ZWE1ODYwMmQ5ZGU0YjA2ZTc3MmI2YTMyYzI2ZWUyMTA2YjEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6Ijc5MWM1Yjc1ZjkwZjQ4NzEwY2E5ZjY4ZDIzYTBjZTU2MjE0YWZjYjhkOTIwNTljYTRhMzUzNmJmNzYxNTY5N2IiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiODJmMjdjMGY4MDk5OTkxMDhmZGRhYmQ4ZTNiN2NjYTQ3YmNhMTVkZmI4ZjE4MzE4MTVmY2VkNjdiMmYwNjhlYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjNlYjFjNDdjZTkwOWRiNWQ1MzhmNjE2OWNiMjlkY2M2NzY0YzAwNWRjY2Y3ZGMwZTU4OTI1YmUzMmFmNTJiOTMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMGJmYmI1MWI4YTc5NTNhZWM2OTE5ZjgwZGU0MzRmOWM1ZWVhZDY2Zjc0MTA1YmY0ZDAxM2Y1MjVjNGE0ZDczIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImE5YjU3YTU2NWRmMjYxZTVkNWQ0YTJiMDM2YjQzMzgwN2NjNTlhZmE0NGU1MjZjOGY2MGViYjJlNzYzZWUwYjciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiZDJmMDA4OGRiZWU2YjhhZGExZDlmZWJiNzIxZGJmOTZmMDU5MDVmNDc0YjVhYjZjNjNiYzZhYTg0NjdhNzVlOCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6ImU2ODc2MDBiNGU4NTEzMjc1ZGUxNDU1YjNiYmU5ODczMTZkMGMwNDI3ZTA3YjgwYWZhZWE2ZDQ5YjZlYzBhMWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6ImZmOTVmZjlmNGExYTQ0ZTVhMjFlNzQzMWFkODFhOGI2MzAzZjhjODQzOWJhYWFiZmRhYjU4NDhmNTkyN2Y0ZGEiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiYTcxNGVmZGUyYzdiODhjY2IzODIyMzY0OWMxM2U3MmVhNjNmNWViODcyNGM2MzkxMGJhZWJmOTE0YWYyYWJmZCIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "privacy": { + "obfuscated": [] + } + } + } +] \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/raw-document-did-oscp.json b/test/fixtures/v4/__generated__/raw-document-did-oscp.json new file mode 100644 index 00000000..14d8e19c --- /dev/null +++ b/test/fixtures/v4/__generated__/raw-document-did-oscp.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "validFrom": "2021-03-08T12:00:00+08:00", + "name": "Republic of Singapore Driving Licence", + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "credentialStatus": { + "id": "https://ocsp-sandbox.openattestation.com", + "type": "OpenAttestationOcspResponder" + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/raw-document-did.json b/test/fixtures/v4/__generated__/raw-document-did.json new file mode 100644 index 00000000..98a032ca --- /dev/null +++ b/test/fixtures/v4/__generated__/raw-document-did.json @@ -0,0 +1,47 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "validFrom": "2021-03-08T12:00:00+08:00", + "name": "Republic of Singapore Driving Licence", + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/sample-signing-keys.json b/test/fixtures/v4/__generated__/sample-signing-keys.json new file mode 100644 index 00000000..bb83bb41 --- /dev/null +++ b/test/fixtures/v4/__generated__/sample-signing-keys.json @@ -0,0 +1,4 @@ +{ + "public": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "private": "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655" +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/signed-wrapped-document-did-obfuscated.json b/test/fixtures/v4/__generated__/signed-wrapped-document-did-obfuscated.json new file mode 100644 index 00000000..4d2c8ed5 --- /dev/null +++ b/test/fixtures/v4/__generated__/signed-wrapped-document-did-obfuscated.json @@ -0,0 +1,61 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + "proofs": [], + "merkleRoot": "4b178a75faf7d7ecff1341ee1e0907810df23c88a217b814eb12c2a4454631ec", + "salts": "W3sidmFsdWUiOiIyMzkyMzE4OGQ0MzIwYzlkMGEyZGY0MjU2ZTQ2ZTkzZTgzM2FlMWQ4YTU5YzFlYmQ5MTBkOTUxNDc1MjNiMjhmIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjZjMGEzZTZmZDI0NTEyZjg5MWE4ZDY0N2RlZGU1NmIwYjBlMTNkZDcxOTVmMDRiM2EwMzY2ZDZmNDk5OWFkMTAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWFhNDY3NmQwY2FjZGU1ZTljY2MyZmNiYzc1OWRjZTJlNWFhOGY5YTg1ODI5NzFlNjNlM2MzZmVjMzE2OWNiOSIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjZmYjU2MDJkYjg1MjY1M2Y2NDg0M2I0ODcyNTNmNGU3ZWI2NzlmZDY3Mjg4NjNmMWRkNjcyY2U1ZWEzMDAzM2YiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJkMmZiMGQ4YzBlMzFkNmIwZDgxODQ1NGYyZDgxNGZkOTg3ODFjNjk4ZDA0OTg3MGJmOTAwNzA2M2Y4ZDAyNjNlIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiNzBjNDk0MzM5ZDQxZjZkOTBmY2ViNzg5ODk2ZjExMTVjMDI1NWY2Njg4Yzg1MGQxNmUxMGRiNzhhZmJjOWYyMSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiMDkyYThhMTc0NjYwNjA0YTdmZWNiOTgyNGVhNmMwYTk3ZjMyODM2MzE0MmVjMTg3NWUzZTBjZTFhNzFjZTc1ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmZTM5YTA3ZDYwNDVmZDdlMWQxMjRlOTc1ZmQxNjYxZTQ5NzNlM2Q0NDdkMjBiOTdmZmE0YWRkMDA0YzcyZjhhIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjljY2ZlNzNkMjllZDhmYTU2MWRiMTE5NjZhN2RiZWNhY2JhYTNlOWNmNDI5OTg4Y2VjMzNmNTRkOTQ0NTdhYzIiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiMDNhNWMwMDYyMzFlNWQ1MjJhNmYzOGFkNDE3YjEzYmQyYzgwMjVhZjY5ZGVhYTA0MzkwMjkwNjljNjEzMjE3ZCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjNjNzhjMzdlMDA2ZjZlMTkyY2ZhZmZjYzM0NzZhODk0MmMxODNiY2JkN2NlMjJlYjhkZWNmNGQ0YTI5OGJmZmIiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjNiMDI3MjkwOThmMTQxY2I3Y2E5OGM2ZmIxODMyZjVhYmMyN2JmZDA5NTlkNzk5ZjUzNDRkYzVjZWY2YmMyMTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiMzQyYmMwYWM0MjU3MmExY2U1N2YxMjcwZGRhNTlmMDFlMzZiMTM5NzhlOTdhODgzMzMxMGVjZGMyYTgzOGUyNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjlmZGIwN2YwNGNiOTllZWViZTVjODFiZmRkZmIwMDRiNWVlMTVmZTBhMGE4ZDIyNjYzZmMxZGRkMzk5NmI3MjgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJlMTY0NTBiODVmNjJlY2JkMTgyODg4ZDQ0YjIzZGQ1YTVkZTdiNjBiN2UzZWI5YzQ1NDRmZjYzM2E4MzBlNTkxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjZjZWRmYjg5MTM3MTI3NzYwNjZlNjJkMjE1OTFjY2M0NGNiOTM5ZmNlODA0ZWI2Y2E1MWViNmUyMWE0Zjc3NTAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZWZmZWN0aXZlRGF0ZSJ9LHsidmFsdWUiOiIxY2ZjMzA0ZjczZGUxZTUxYWMxZjA3M2RiNWI1MGIxMzRjMjY4Y2M0Y2YyMTQyMGZlYTJmNDYzNWJjM2U5ZTMxIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmNsYXNzIn0seyJ2YWx1ZSI6ImZhMWQxMDYwZDU3OTU1NWFkZGNiMGQ1OGRiODIzZDAzMzljZDU4YjBlMzk3YTcyY2ViNjQ4M2U2NzYzYzI4MWIiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiNTkwNTA4N2JlOTVhNGFiMzI2NDRkMmQ2M2MxNGIxODhlNGU3MTg2MDdjNWI2MTlkZjc0MjYyMWU2NzdkNmVhZCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjM0YWUzN2JhMzY2NzNkNzMzNDg4YTc5ZDM2ZGQyNDQwZDc2YjMwYTBlMjBjZDM1NGI2Mjk1OGU5Y2U1NjUwODciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjFjNjUzNmJiNDA1NTgzMjEzMDdmNjcxNTE3ODIwNzc1NmEwZTcwYjUxNTcwMTNkOTFiMDdjMTFlZTg3Yzc2MmIiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiMmNhODYyYzA3Y2M5MGU3M2IwN2ZkMTJlNGIzNjg4YzQ0ZGQ3OTkyN2UwMzlkNDVlOTY3NGMzNGZmMjJjMGE1MSIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "privacy": { + "obfuscated": [ + "0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f" + ] + }, + "key": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "signature": "0x1744f9615fa8d725cf4ae14f2654762dd8e0ee88a9b6d8af13cec688019a7a501e9bae10fa407fdbe359977f8124a26a0061a0ef0ea212c42fd1d91e0998928d1c" + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/signed-wrapped-document-did-oscp.json b/test/fixtures/v4/__generated__/signed-wrapped-document-did-oscp.json new file mode 100644 index 00000000..f1c406a5 --- /dev/null +++ b/test/fixtures/v4/__generated__/signed-wrapped-document-did-oscp.json @@ -0,0 +1,64 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "credentialStatus": { + "id": "https://ocsp-sandbox.openattestation.com", + "type": "OpenAttestationOcspResponder" + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + "proofs": [], + "merkleRoot": "db256b67a181b7c5a1b312b64901a0c332d72a5f2f631473ba2c28a725c3ab21", + "salts": "W3sidmFsdWUiOiI2ZTJhN2E4MjM1NzMzOTM2MGJkYzE0OTYzMTUwNzAyNTg2N2QyOWYxM2YxYjMwZmFmMDQ4Y2VmM2QyMzc2YWM2IiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImRkZGI5MDVmNmIzZWQ0OGVlMzkwZDljYjQ4MTdkMjk4OWMyY2VmMzQyM2UxNGE4YjQzMDMzYmFhNDM0NmY2ZGUiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiMWNiZjBjYjY2ODZkNWIyMzJkM2QwZDZiNTk1MTA0NDc0OGY4NmNlMjJkZDE1OGIxNzUwZTc2YWUyYzAzZDkwNyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImJlZjJmZjNhM2MxNDEzMDYxMDVkNGMzMTJlZmZkN2M2OTUxN2U3ZjM5YTBjYTQ3ZTZkZTk4Y2M1ZTFkMzllY2UiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI1N2M5YWIzYzRlM2RiNjE5Nzc0NjZhNjQ4ZmM5ZThjNjI2NzQ1ZDE4NmIyYWRlZDIxNDhiNDQwYWU0MWRmYjhiIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiZmYwNDE0MjcxYWYwNzk5ZWM4YzgwMTQzMzI2NmVmNGE5YzAwNzQxMDVlNTNmMmQ3OWYwYjYzYjBjNjk1Y2Q2MCIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiOTEyNjc2YWZjZTkyYjBhNGU4MzhkNzZjZDYyZmNmMGY0YTc1NmE1MTI0NWI0YWU4YTQxN2ZkNzYxNmVhMDVkMSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJmNDYzYmNkYWIzYTcwNGYzMWRhOWQ5MjUwNTM3OTE1MDBiYTdjMDQzZTNiZDEzNzU3ZTgyNTI5MzYzZGNhNjc0IiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6Ijc2YWIzZWQwODk2NzZkOGJmM2FhZjE3OGQyYTUzYzI3NGZjMGUyMmFiMjJkNjU3NmIxYTUzYTAxMjMxNjUxYjAiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiNjMyYzI5MzdhZGYwMGIwYWU0NGRlYzZkNGI1NmJmN2RkYTU3ZDEzYjk3MWUxNzhlZjJiZTdmNDMwNDJlMTI4YiIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjkxOGNiYjU1MDg4ZGFjNDEzMzYxODEwZDE0ZTBlM2U4Mzk4N2Y2N2NjZDU1YTE5OGQ2YzZhOWEzYWM4Y2VkMDQiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImI5NmVkN2FjZDZjZjQwNmViZGMxZmJlY2VhY2M1ZWYxZDhkODlmZDNhZTZmZTRjMzkyMTM3M2NhY2YxYTc5NDAiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiOTVmNDFmYWZiYjNhZmY4NmM4ODU0ZTRjOWQ4MTAzY2E3NGM0NTBiODg3ZmQwOGMwNDFiYzI4NzlmN2RiMDAzYyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjU2ZmU1YjZmZDYwNjE0OWZmYzhhZGZkNjY2ZDgxNGJiYzZjNWMzZjU0YjA2ZmQ4YjJjZTFmNDc2M2RkNWMwNmMiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJmNWZhM2MyMDhiODRiNDdkZDczYTIwYmM5YjczYWY1MTI3MzIwZGUxNTIwNGExMjA0NjgzZDI0NGUwNzhkYmU5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjkzODcxZTQ2MmEzZjIyNDE1YzU2ODhhOGQ3NGYyZjFlZjMwZTEwYWNlOWZiMmIyNTllOWVjMmVlZWVjMWI2NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiN2FmNWFjNzViYjNkZWYyNTQwOTViNWE4ZDFiNGFkZmJiZmQ3YTM3MTY4N2UzZGY2OTJmYjhlMmM2YTMyZjZiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjAwZDA2MTlhNzQ0ZWUxODIwMzQ5NzZhZmRjZmU2ZjE5ZDE1NWZkMjgwZDQzYjM0ZjhjNDM4OGJiMDQwOTBkOTQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiOTUyNTdkMjJmMGMyYzRkNTY3MjkzMWM0OGY1OTBjY2RmMGU4NzNmY2ZmMTdjZDA2NmRmNjBjNzMxMzgxZGMwNyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI3ZTZlODE5MmM1OWVhMDllOGUxZDM3NTI4NGEzN2JhMTFkNmFlNTVmNmFiNGIwMjRhMGU4YjM0M2MwZTlhMWI4IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiMTRmN2U1ZWU4Zjk0ZTAyZDFiODcxYjE4YzRmZjAwOTA0ZDc4OGNhMjU4NDhlYjVlOTMwOTZmNTFhMzI0NGIwYiIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjFjNzRmODhjODNhOWZhYWQ5YjU1MjE4NjcwZDg5NzM0NTY0MjY2OTg5MzdlYTQyNmFkZmY4ZmVmOTYzMWVkNzMiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjA0N2I1YzA0NDAzMjlmM2ZkODZmMDQxODRmMTk1OGZlZDQyNDhiMDI2YmM2NGI5NDM5MmNiMGRjYmU5M2VlYWYiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjQyZGM4Y2EzZGJiMjI2NGI4ODg0OWIyNWFkZWQ0ZWE2NWQ2ZmMzYTZlNWUyNjExMGE1MDUwNmRjYzdhYzhmNzciLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiODg2MjJkNDRjMTBiY2VlMzUxMGRiNmE4ZjNhMGYxM2M1ODBmNmZlNWY3OGNkODVmM2FjYTIzMGE2M2QyYTZjMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "privacy": { + "obfuscated": [] + }, + "key": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "signature": "0x1d889961f3b28e3433ee56e1d3226d1b069d6696f2cac99d1f1504f31c1257b216e9e42bfa8c434eaeb80bc38b0af269cab3ffd03d4410539589d9173bc4ba881c" + } +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/signed-wrapped-document-did.json b/test/fixtures/v4/__generated__/signed-wrapped-document-did.json new file mode 100644 index 00000000..2faaaa32 --- /dev/null +++ b/test/fixtures/v4/__generated__/signed-wrapped-document-did.json @@ -0,0 +1,60 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", + "proofs": [], + "merkleRoot": "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", + "salts": "W3sidmFsdWUiOiJjYTE3MjI0YTg3NTk0NzNhNGUyZjM2YTM3NjljODU0OGM5M2RjZTQ3NmI0MjEwMTNmYTEzYTA2OTUxYTI1YzhjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMyYzk3MDRhMmIxMWU1MzhjYTBlZWJhYWMzNmU0ZmRlYTU4ZGY0MDI4ZGVmZmJjOTkzODBjYjZkZjU3MTdiODEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDE3YTFhNzY2YWM5MzhkOWVlY2FhNWRmMDdhOTBhODkyZTM3ZjA1ZmI3MzY5ZWRjNGQwYWNmNWZkMjE4YjhlMiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImE1Y2FkZDIzMmQ4ZjhlMGU4ZmFhODA3OTMyYWNkZjJhODc1OWY4MWEyMDNmNmEwMjdjMTJhOTBhNjg0MWE1NWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI2NGE4NjBmMmQwOGZjNGIyMDhjZWE3NmU2MjBjM2YwMDRlYzhiMmRhYjdhZTc5ZTZkZmMxZjllYjU1MWMxMmM3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMzcyZmJlNDllMTdjMTA2MGEzNTk1NjVhNDA2M2VlMjkyODU1ODg0OWZhZWRkODE1MGQwYWZjZmY3NzQ2M2NmMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTE0ODAwOGQxY2JjMzM3ODJmNmNlN2UyOTFhMTM5ODE1YmRhYWFjZjg2MWYxNGYwMDk0NWU5YWIwZTk2ZjdkNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI5Yzg4NTc4ZTAzZWRiNjc3NTdiNDI0NzY4MTliNWZiMGQwMDlhMGU0ZjZmMjllOGNhNTBlMWJmNWI4NWQ2ZmZiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImJhOTk1NDA1YWQ4ZmZlZmJmYTExMDA2MzE2ZDdkNjM4NDRlODk2ZmMxNDhkZjdlMWViZjNkYWZhYzI0NTY4YjQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZWY1ZTEzODU4YWM1MzYwNTMzODkxZjkxOGJhYjlmZjI3YmQ5YjhiYzA5MzQ4ZTc4NWMwMmI2YjYzZTcxZDNhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImY2N2ZmNGMyZTUwYzU0M2ZmYmEyNGJiMzA4OTY5NGUxOWViZDNjOWJhMmE5MTM3NTA3N2YzOWRlN2M0ODVmY2MiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYzQ2MjkwMDQwMDMwOGM1YjA3MmM1Y2NlMjQ4YmQ2YjRiOTgxODBjOGQ2ZDQ2YjIwNWE0MGE2YzNjMDhmMzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2QwMThlMjllOTI3MTcxN2UwNGI5ZWYwMDJkNWVmNTRmMTRiZmI3NmJlYzdhMTlkN2ZlYjcyMzM3NjU2NzU4ZiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImYxNDllNmM5M2ZhNTU5ZjM4OWRjZTcwMDMwYmRmYmVlNmU3OWRjODM1NjJhYmVhN2FkOTMzNTA0OWM4MTI5NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyZGZjM2VmNDQ2NzA0MmMzMDI4NDU0MTNmMWE5MDQxYmQ2NjY3NThmNDM4MWU4OWU5NWIzZjc4NjYyYzE1NTc5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjY3MjNhYjA4MzhkODMzNTJlMjhhNjQxMjliMzEwOWQzNzA5YTk3NWYyMDljZmM2NjE0MTNmODUyYzFmZTY0YzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMWZiNTRmMGVhZDgyZmEyMzVlMmU5Y2RkY2ViMGQ2NTllMjRhYzhhMzM4OThmODdmMzkzZTBmZmY5OTE1Y2NmZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjhjMzJlZmNlOGE2ZTIzZGMxNzUzOTVmNzMxM2ZjMGQ4ZTk0ZTA0YjU3ZGMwMzI4YmEyNTk0NTgxMTJjNWQ4MjQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYmQ1YzM2Yjc1YTVmNzEwYmQ2MzJhOWM1MTY1ZGVkNmI4N2YzODY1MTg5YWRjZTc3M2FlNDMxMTlkYzljMGFiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1MmYzYTU1ZWI3MGZmZjMyYjEzZWMyNjlkZjJmODM4Mjg4NTc5YmUyMDg1NmQ3Mjc5NmI3YmY3MDFmMjY5MjU3IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTJjZWZiMjkzZGEzZGNmNTIzZjAwNjkwN2YyNTljNTIyOTc3MzQwZjYzNmY4YmU1MmM5ZTg5ZTEzZGVlZTk0MiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNDNiMjFkZGU2NTY2NjI5MTNjNzJiMDExMzBkMjM0MzlkY2NjMTNiZjVhYjYwMjY5YTVmYjM3NmFlMzBkNjlkMiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiI0NDRmZmU3OTMzMmRkZjExOWRiZWUzZjYzYWQyZTQyYzE2ZTczMDdkMGVmOTE3NDJlZDEzNTkxYWY5M2U0NWRjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "privacy": { + "obfuscated": [] + }, + "key": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller", + "signature": "0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c" + } +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/wrapped-document-did-oscp.json b/test/fixtures/v4/__generated__/wrapped-document-did-oscp.json new file mode 100644 index 00000000..7710fbb2 --- /dev/null +++ b/test/fixtures/v4/__generated__/wrapped-document-did-oscp.json @@ -0,0 +1,62 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "credentialStatus": { + "id": "https://ocsp-sandbox.openattestation.com", + "type": "OpenAttestationOcspResponder" + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "96a8e79b4c9b14dde88d9d6db26916215502ed09f458d0dc387c1ca73f3549f0", + "proofs": [], + "merkleRoot": "96a8e79b4c9b14dde88d9d6db26916215502ed09f458d0dc387c1ca73f3549f0", + "salts": "W3sidmFsdWUiOiI2ZGM0NzZkYmZlOTBiMjMxYzg5MjFiYmIyNTg3NTFkZGJlNzExMGFhOGM2Y2U3ODQwOGZiNmFhMDBiYzI3NTZjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6ImY3MTA2OTA3YzJhMDRjNzRiOWYyMzg2Yjg3MGJhMmE1MmYxMmFiZDA1Y2FiOGUyOGFhOTdhZWJhYTk1ZTRhMjAiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNGEyNzhiYmM0MTllYTY4YTRlN2M1Y2YxMDdmZDUxNWNkNDgwYmNhNDZlZjdjNjQ1NGFiMjQwZWRmY2JjODM4NyIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6IjVhNDQyMTE0M2YxNGZjYmY4MTdhMjgwMWJhOTUzZDY1ZWQ2NDUxNDE5YTkzY2VkM2Y2MWY1NzcxNjI0YzA3MzUiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiJiY2M1OGMwZTU3NjgzYmM3NDA2NjM5ZWMwYzI3MjIxMGYzZmQzMGVkYmQ4ZjU5MjM2MjBiMzgxNzYyM2U4YWEwIiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiYzUxYWJlZGE2MTNjNGZiMDY5ZDQ2MzNhMTcwNzIyYWE2YmRiMGFlZDkzOTAzZjRiMmJjZTAyZjU1NWZiNDY5OSIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiM2U3MjVhNzk1YzZmNDQyNDc5NzZkNWUzNGQ3M2YyMzA4ZTEyNjc3MzNjODY1YWNjYTUwYTc1ODVmNDIwNDI4ZSIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiJkY2QyMzM5MTVmODVkMjIzNzFlNWFkMzc2Y2I3M2ZkNGVkODcxNzVjNWUzYTgxYjhiMjhmNWE1ZDA3NDllMzdkIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6IjJlY2MyOGVmZDNjNTU5ZmU5MGNiYTg2NDQ4MmM3YTk0YmU5NTRhNzY4NWY2ZmI5ZGQ5MDJiZTUwMDE5OTRjMzgiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiOTI3ZDM1ZDUzZTE3MThkMWE3ZWM2NDFkMzFmYmU4OThiMGI5YzkyZGRhZmMwMmU4MGU5NjgwMThlNmUyZTgyNCIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6IjI3OTVkNzgxODY2ZjNmYTI4YThhMzIxMmI4ODdjMGNjMzkyMWM2Njc2NmIxZDJmZDdkYjY1NzBhMGM1Y2ExZTEiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6IjA2ZmViZjYyNTZhY2FlMWIyZjg5MTE0MjM2NzY2YjNkMzIzZDMwNzQzZGJiZDRmODI0NWU1NjA5MTRhZjcwZmQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiNjBkY2E2Njg3MGNmNzMxZDkwNjcwZTA5YTg2MDA2ZTJlNjRjYzRiZGQ3ZWYyYTQyZjMwYWUwY2M4N2Q3ZmY3MSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6IjdhMjBiMmMwMTk3MDE0YjA5OWY2MDg4NGM4YjA1NWMzYmIxNjY3YzRiZWRiMjIzMDY4NGI2YTMxZjY1ZjQ4ZTgiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiJiYzRiMGJiODkzZTIxNGYyMzlkZmQ4ZGY2MGQ1ZDdkZjZmNzBjNjI4N2YzNmQ3ZWVlZGVhMjE1NDMzNjE5ODA2IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6ImY4N2ZhNjNmMDY1MmY2NzJjYjkyOWI2YzFiYWMwOGFmOTNiZjc5NTgwOWYzMjlmZmEyYmJlMmE1NGJmOThiODEiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiYzc4MDE5Njk4NWEzNDFjYzJkZmQwMzA0YTdhOTUzZGFkZGE2MmZhYjhlOGYyZTFlZWViNGNlZDM3YTIwZDJiMyIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6Ijc0Y2ZmNjIxZDhhZTM2YmQxNWFlMzFkZTIwMTE1Mzg1MDZmNDhlMjkxNjExYmM3ZmIzNjVkYjNkMTI3ZGYxYzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiZDg2ZGQ0ZjNkNDA1N2FiN2I3NzI2ZWU0ZmE5Y2U1NjlkZTc3YmZiNGNhNzBmYTY5NzVlNWZjOWEwOTAwZjdkNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI4ZDhmZTQ3MGY3NmEyODNkNzQ1ZmM4MGYwZjEyZmZjOGU5ZjU3OGE5NjY5NDRkODBmOTM2MDhjNWZjOTM4ZTAzIiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiNjkwOGFlOTBiYTI0YzZjNmRhZDcxZmEzZmQxNGJiYWYxNTMyOGJhZDliN2Y0NDI5Y2IzN2Q5YjIyYWM4MzRjNCIsInBhdGgiOiJjcmVkZW50aWFsU3RhdHVzLmlkIn0seyJ2YWx1ZSI6IjM2MDU1ODQzZWU1MTNkZDVlYmY5MjUzMTM5ZTI0YjkxZWM5MDk4NzI1Y2ZhZDQzYmE3ODkxNWRiMGFhODJjYTEiLCJwYXRoIjoiY3JlZGVudGlhbFN0YXR1cy50eXBlIn0seyJ2YWx1ZSI6IjQzMjJhMDA0NzNjYzIzNWVmNzFkYWNiMDFlNjcwMGNkZWE1YjUxNWIzNzM2MDY0ZWU0NzJhNjM4ZWFmYjUxNzMiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLmlkIn0seyJ2YWx1ZSI6IjRmNTJjZTRjN2JmNTQxMWNhMGFhOTJhZWY1YWE5NWQyNDYwOThhZmE5N2Q2YWI0YTMwY2JkYTBhNzM5ZTg0ODAiLCJwYXRoIjoicmVuZGVyTWV0aG9kWzBdLnR5cGUifSx7InZhbHVlIjoiMzM3YTkyODViYjFkYzgyYzYyMTFhMTBhMDA5ODkxOTNjMzQyZjczMDFiZTYwZjEzY2E4M2IzYWMxMTQ2MDkzMyIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udGVtcGxhdGVOYW1lIn1d", + "privacy": { + "obfuscated": [] + } + } +} \ No newline at end of file diff --git a/test/fixtures/v4/__generated__/wrapped-document-did.json b/test/fixtures/v4/__generated__/wrapped-document-did.json new file mode 100644 index 00000000..19039c49 --- /dev/null +++ b/test/fixtures/v4/__generated__/wrapped-document-did.json @@ -0,0 +1,58 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json" + ], + "name": "Republic of Singapore Driving Licence", + "type": [ + "VerifiableCredential", + "OpenAttestationCredential" + ], + "issuer": { + "id": "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89", + "type": "OpenAttestationIssuer", + "name": "Government Technology Agency of Singapore (GovTech)", + "identityProof": { + "identityProofType": "DNS-DID", + "identifier": "example.openattestation.com" + } + }, + "validFrom": "2021-03-08T12:00:00+08:00", + "credentialSubject": { + "id": "urn:uuid:a013fb9d-bb03-4056-b696-05575eceaf42", + "type": [ + "DriversLicense" + ], + "name": "John Doe", + "licenses": [ + { + "class": "3", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + }, + { + "class": "3A", + "description": "Motor cars with unladen weight <= 3000kg", + "effectiveDate": "2013-05-16T00:00:00+08:00" + } + ] + }, + "renderMethod": [ + { + "id": "https://demo-renderer.opencerts.io", + "type": "OpenAttestationEmbeddedRenderer", + "templateName": "GOVTECH_DEMO" + } + ], + "proof": { + "type": "OpenAttestationMerkleProofSignature2018", + "proofPurpose": "assertionMethod", + "targetHash": "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", + "proofs": [], + "merkleRoot": "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa", + "salts": "W3sidmFsdWUiOiJjYTE3MjI0YTg3NTk0NzNhNGUyZjM2YTM3NjljODU0OGM5M2RjZTQ3NmI0MjEwMTNmYTEzYTA2OTUxYTI1YzhjIiwicGF0aCI6IkBjb250ZXh0WzBdIn0seyJ2YWx1ZSI6IjMyYzk3MDRhMmIxMWU1MzhjYTBlZWJhYWMzNmU0ZmRlYTU4ZGY0MDI4ZGVmZmJjOTkzODBjYjZkZjU3MTdiODEiLCJwYXRoIjoiQGNvbnRleHRbMV0ifSx7InZhbHVlIjoiNDE3YTFhNzY2YWM5MzhkOWVlY2FhNWRmMDdhOTBhODkyZTM3ZjA1ZmI3MzY5ZWRjNGQwYWNmNWZkMjE4YjhlMiIsInBhdGgiOiJuYW1lIn0seyJ2YWx1ZSI6ImE1Y2FkZDIzMmQ4ZjhlMGU4ZmFhODA3OTMyYWNkZjJhODc1OWY4MWEyMDNmNmEwMjdjMTJhOTBhNjg0MWE1NWYiLCJwYXRoIjoidHlwZVswXSJ9LHsidmFsdWUiOiI2NGE4NjBmMmQwOGZjNGIyMDhjZWE3NmU2MjBjM2YwMDRlYzhiMmRhYjdhZTc5ZTZkZmMxZjllYjU1MWMxMmM3IiwicGF0aCI6InR5cGVbMV0ifSx7InZhbHVlIjoiMzcyZmJlNDllMTdjMTA2MGEzNTk1NjVhNDA2M2VlMjkyODU1ODg0OWZhZWRkODE1MGQwYWZjZmY3NzQ2M2NmMyIsInBhdGgiOiJpc3N1ZXIuaWQifSx7InZhbHVlIjoiYTE0ODAwOGQxY2JjMzM3ODJmNmNlN2UyOTFhMTM5ODE1YmRhYWFjZjg2MWYxNGYwMDk0NWU5YWIwZTk2ZjdkNiIsInBhdGgiOiJpc3N1ZXIudHlwZSJ9LHsidmFsdWUiOiI5Yzg4NTc4ZTAzZWRiNjc3NTdiNDI0NzY4MTliNWZiMGQwMDlhMGU0ZjZmMjllOGNhNTBlMWJmNWI4NWQ2ZmZiIiwicGF0aCI6Imlzc3Vlci5uYW1lIn0seyJ2YWx1ZSI6ImJhOTk1NDA1YWQ4ZmZlZmJmYTExMDA2MzE2ZDdkNjM4NDRlODk2ZmMxNDhkZjdlMWViZjNkYWZhYzI0NTY4YjQiLCJwYXRoIjoiaXNzdWVyLmlkZW50aXR5UHJvb2YuaWRlbnRpdHlQcm9vZlR5cGUifSx7InZhbHVlIjoiZWY1ZTEzODU4YWM1MzYwNTMzODkxZjkxOGJhYjlmZjI3YmQ5YjhiYzA5MzQ4ZTc4NWMwMmI2YjYzZTcxZDNhYSIsInBhdGgiOiJpc3N1ZXIuaWRlbnRpdHlQcm9vZi5pZGVudGlmaWVyIn0seyJ2YWx1ZSI6ImY2N2ZmNGMyZTUwYzU0M2ZmYmEyNGJiMzA4OTY5NGUxOWViZDNjOWJhMmE5MTM3NTA3N2YzOWRlN2M0ODVmY2MiLCJwYXRoIjoidmFsaWRGcm9tIn0seyJ2YWx1ZSI6ImIyYzQ2MjkwMDQwMDMwOGM1YjA3MmM1Y2NlMjQ4YmQ2YjRiOTgxODBjOGQ2ZDQ2YjIwNWE0MGE2YzNjMDhmMzciLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QuaWQifSx7InZhbHVlIjoiM2QwMThlMjllOTI3MTcxN2UwNGI5ZWYwMDJkNWVmNTRmMTRiZmI3NmJlYzdhMTlkN2ZlYjcyMzM3NjU2NzU4ZiIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC50eXBlWzBdIn0seyJ2YWx1ZSI6ImYxNDllNmM5M2ZhNTU5ZjM4OWRjZTcwMDMwYmRmYmVlNmU3OWRjODM1NjJhYmVhN2FkOTMzNTA0OWM4MTI5NGQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubmFtZSJ9LHsidmFsdWUiOiIyZGZjM2VmNDQ2NzA0MmMzMDI4NDU0MTNmMWE5MDQxYmQ2NjY3NThmNDM4MWU4OWU5NWIzZjc4NjYyYzE1NTc5IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzBdLmNsYXNzIn0seyJ2YWx1ZSI6IjY3MjNhYjA4MzhkODMzNTJlMjhhNjQxMjliMzEwOWQzNzA5YTk3NWYyMDljZmM2NjE0MTNmODUyYzFmZTY0YzYiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMF0uZGVzY3JpcHRpb24ifSx7InZhbHVlIjoiMWZiNTRmMGVhZDgyZmEyMzVlMmU5Y2RkY2ViMGQ2NTllMjRhYzhhMzM4OThmODdmMzkzZTBmZmY5OTE1Y2NmZSIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1swXS5lZmZlY3RpdmVEYXRlIn0seyJ2YWx1ZSI6IjhjMzJlZmNlOGE2ZTIzZGMxNzUzOTVmNzMxM2ZjMGQ4ZTk0ZTA0YjU3ZGMwMzI4YmEyNTk0NTgxMTJjNWQ4MjQiLCJwYXRoIjoiY3JlZGVudGlhbFN1YmplY3QubGljZW5zZXNbMV0uY2xhc3MifSx7InZhbHVlIjoiYmQ1YzM2Yjc1YTVmNzEwYmQ2MzJhOWM1MTY1ZGVkNmI4N2YzODY1MTg5YWRjZTc3M2FlNDMxMTlkYzljMGFiNCIsInBhdGgiOiJjcmVkZW50aWFsU3ViamVjdC5saWNlbnNlc1sxXS5kZXNjcmlwdGlvbiJ9LHsidmFsdWUiOiI1MmYzYTU1ZWI3MGZmZjMyYjEzZWMyNjlkZjJmODM4Mjg4NTc5YmUyMDg1NmQ3Mjc5NmI3YmY3MDFmMjY5MjU3IiwicGF0aCI6ImNyZWRlbnRpYWxTdWJqZWN0LmxpY2Vuc2VzWzFdLmVmZmVjdGl2ZURhdGUifSx7InZhbHVlIjoiZTJjZWZiMjkzZGEzZGNmNTIzZjAwNjkwN2YyNTljNTIyOTc3MzQwZjYzNmY4YmU1MmM5ZTg5ZTEzZGVlZTk0MiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0uaWQifSx7InZhbHVlIjoiNDNiMjFkZGU2NTY2NjI5MTNjNzJiMDExMzBkMjM0MzlkY2NjMTNiZjVhYjYwMjY5YTVmYjM3NmFlMzBkNjlkMiIsInBhdGgiOiJyZW5kZXJNZXRob2RbMF0udHlwZSJ9LHsidmFsdWUiOiI0NDRmZmU3OTMzMmRkZjExOWRiZWUzZjYzYWQyZTQyYzE2ZTczMDdkMGVmOTE3NDJlZDEzNTkxYWY5M2U0NWRjIiwicGF0aCI6InJlbmRlck1ldGhvZFswXS50ZW1wbGF0ZU5hbWUifV0=", + "privacy": { + "obfuscated": [] + } + } +} \ No newline at end of file From aadf79321a1d970b24d3197b47e8c37576318fc1 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 15:19:41 +0800 Subject: [PATCH 104/107] chore: added remedy action if fixture test fails --- src/4.0/__tests__/fixtures.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/4.0/__tests__/fixtures.test.ts b/src/4.0/__tests__/fixtures.test.ts index 2593f20a..4ab9554c 100644 --- a/src/4.0/__tests__/fixtures.test.ts +++ b/src/4.0/__tests__/fixtures.test.ts @@ -4,7 +4,7 @@ import * as FIXTURES from "../fixtures"; const PATH_TO_GENERATED_FIXTURES = path.resolve(__dirname, "../../../test/fixtures/v4/__generated__"); -describe("assert fixtures matches generated JSON fixtures", () => { +describe(`assert fixtures matches generated JSON fixtures, if this fails, run "npm i" or "npm run postinstall" to update the json fixtures, and commit changes if needed`, () => { for (const fixtureName of Object.keys(FIXTURES)) { const jsonFixtureFilename = fixtureName.replace(/_/g, "-").toLowerCase() + ".json"; From 1e8a1e6a6129ba9670e96397f04249aa11f30657 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 15:21:51 +0800 Subject: [PATCH 105/107] chore: remove lint for all generated files --- .eslintignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index 7dc67925..e3229750 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,6 @@ node_modules coverage dist -src/__generated__ +__generated__ vc-test-suite *.d.ts From fb3ad28c2906ceb916a8ace350871c7777794311 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Thu, 2 May 2024 16:21:32 +0800 Subject: [PATCH 106/107] fix: v4 e2e --- src/4.0/__tests__/e2e.test.ts | 217 ++++++++++++++++++ src/4.0/__tests__/e2e.test.wip | 405 --------------------------------- 2 files changed, 217 insertions(+), 405 deletions(-) create mode 100644 src/4.0/__tests__/e2e.test.ts delete mode 100644 src/4.0/__tests__/e2e.test.wip diff --git a/src/4.0/__tests__/e2e.test.ts b/src/4.0/__tests__/e2e.test.ts new file mode 100644 index 00000000..a254e235 --- /dev/null +++ b/src/4.0/__tests__/e2e.test.ts @@ -0,0 +1,217 @@ +import { + obfuscate, + validateSchema, + verifySignature, + _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocument as wrapDocument, + _unsafe_use_it_at_your_own_risk_v4_alpha_wrapDocuments as wrapDocuments, +} from "../.."; +import { cloneDeep, omit } from "lodash"; +import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID, WRAPPED_DOCUMENT_DID } from "../fixtures"; +import { V4Document } from "../types"; + +const DOCUMENT_ONE = { + ...RAW_DOCUMENT_DID, + credentialSubject: { + ...RAW_DOCUMENT_DID.credentialSubject, + key1: "test", + }, +} satisfies V4Document; +const DOCUMENT_TWO = { + ...RAW_DOCUMENT_DID, + credentialSubject: { + ...RAW_DOCUMENT_DID.credentialSubject, + key1: "hello", + key2: "item2", + }, +} satisfies V4Document; + +const DOCUMENT_THREE = { + ...RAW_DOCUMENT_DID, + credentialSubject: { + ...RAW_DOCUMENT_DID.credentialSubject, + key1: "item1", + key2: "true", + key3: 3.14159, + key4: false, + }, +} satisfies V4Document; + +const DOCUMENT_FOUR = { + ...RAW_DOCUMENT_DID, + credentialSubject: { + ...RAW_DOCUMENT_DID.credentialSubject, + key1: "item2", + }, +}; +const DATUM = [DOCUMENT_ONE, DOCUMENT_TWO, DOCUMENT_THREE, DOCUMENT_FOUR] satisfies V4Document[]; + +describe("V4 E2E Test Scenarios", () => { + describe("Issuing a single document", () => { + test("fails for missing data", async () => { + const missingData = { + ...omit(cloneDeep(DOCUMENT_ONE), "issuer"), + }; + await expect(wrapDocument(missingData as unknown as V4Document)).rejects.toThrow( + "Input document does not conform to OpenAttestation" + ); + }); + + test("creates a wrapped document", async () => { + const wrappedDocument = await wrapDocument(RAW_DOCUMENT_DID); + expect(wrappedDocument["@context"]).toEqual([ + "https://www.w3.org/ns/credentials/v2", + "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", + ]); + expect(wrappedDocument.type).toEqual(["VerifiableCredential", "OpenAttestationCredential"]); + expect(wrappedDocument.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); + expect(wrappedDocument.proof.targetHash).toBeDefined(); + expect(wrappedDocument.proof.merkleRoot).toBeDefined(); + expect(wrappedDocument.proof.proofs).toEqual([]); + expect(wrappedDocument.proof.merkleRoot).toBe(wrappedDocument.proof.targetHash); + }); + + test("checks that document is wrapped correctly", async () => { + const wrappedDocument = await wrapDocument(DOCUMENT_ONE); + const verified = verifySignature(wrappedDocument); + expect(verified).toBe(true); + }); + + test("checks that document conforms to the schema", async () => { + const wrappedDocument = await wrapDocument(DOCUMENT_ONE); + expect(validateSchema(wrappedDocument)).toBe(true); + }); + + test("does not allow for the same merkle root to be generated", async () => { + // This test takes some time to run, so we set the timeout to 14s + const wrappedDocument = await wrapDocument(DOCUMENT_ONE); + const newDocument = await wrapDocument(DOCUMENT_ONE); + expect(wrappedDocument.proof.merkleRoot).not.toBe(newDocument.proof.merkleRoot); + }, 14000); + + test("obfuscate data correctly", async () => { + const newDocument = await wrapDocument(DOCUMENT_THREE); + expect(newDocument.credentialSubject.key2).toBeDefined(); + const obfuscatedDocument = obfuscate(newDocument, ["credentialSubject.key2"]); + expect(verifySignature(obfuscatedDocument)).toBe(true); + expect(validateSchema(obfuscatedDocument)).toBe(true); + expect(obfuscatedDocument.credentialSubject.key2).toBeUndefined(); + }); + + test("obfuscate data transistively", async () => { + const newDocument = await wrapDocument(DOCUMENT_THREE); + const intermediateDocument = obfuscate(newDocument, ["credentialSubject.key2"]); + const obfuscatedDocument = obfuscate(intermediateDocument, ["credentialSubject.key3"]); + expect(obfuscate(newDocument, ["credentialSubject.key2", "credentialSubject.key3"])).toEqual(obfuscatedDocument); + }); + + describe("Issuing a batch of documents", () => { + test("fails if there is a malformed document", async () => { + const malformedDatum = [ + ...DATUM, + { + laurent: "task force, assemble!!", + } as unknown as V4Document, + ]; + await expect(wrapDocuments(malformedDatum)).rejects.toThrow( + "Input document does not conform to Verifiable Credentials" + ); + }); + + test("creates a batch of documents if all are in the right format", async () => { + const wrappedDocuments = await wrapDocuments(DATUM); + wrappedDocuments.forEach((doc, i: number) => { + expect(doc.type).toEqual(["VerifiableCredential", "OpenAttestationCredential"]); + expect(doc.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); + expect(doc.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); + expect(doc.credentialSubject.key1).toEqual(expect.stringContaining(DATUM[i].credentialSubject.key1)); + expect(doc.proof.targetHash).toBeDefined(); + expect(doc.proof.merkleRoot).toBeDefined(); + expect(doc.proof.proofs.length).toEqual(2); + }); + }); + + test("checks that documents are wrapped correctly", async () => { + const wrappedDocuments = await wrapDocuments(DATUM); + const verified = wrappedDocuments.reduce((prev, curr) => verifySignature(curr) && prev, true); + expect(verified).toBe(true); + }); + + test("checks that documents conforms to the schema", async () => { + const wrappedDocuments = await wrapDocuments(DATUM); + const validatedSchema = wrappedDocuments.reduce( + (prev: boolean, curr: any) => validateSchema(curr) && prev, + true + ); + expect(validatedSchema).toBe(true); + }); + + test("does not allow for same merkle root to be generated", async () => { + const wrappedDocuments = await wrapDocuments(DATUM); + const newWrappedDocuments = await wrapDocuments(DATUM); + expect(wrappedDocuments[0].proof.merkleRoot).not.toBe(newWrappedDocuments[0].proof.merkleRoot); + }); + }); + }); + + describe("validate", () => { + test("should return true when document is a valid wrapped v4 document and identityProof is DNS-DID", () => { + expect(validateSchema(WRAPPED_DOCUMENT_DID)).toStrictEqual(true); + }); + + test("should return true when signed document is a valid signed wrapped v4 document and identityProof is DNS-DID", () => { + expect(validateSchema(SIGNED_WRAPPED_DOCUMENT_DID)).toStrictEqual(true); + }); + + test("should return false when document is invalid due to no DNS-DID identifier", () => { + const modifiedIssuer = cloneDeep(RAW_DOCUMENT_DID.issuer); + delete (modifiedIssuer as any).id; + const credential = { + ...RAW_DOCUMENT_DID, + issuer: modifiedIssuer, + } satisfies V4Document; + expect(validateSchema(credential)).toStrictEqual(false); + }); + + test("should default to 2.0 when document is valid and version is undefined", () => { + expect( + validateSchema({ + version: undefined, + data: { + issuers: [ + { + name: "issuer.name", + certificateStore: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", + }, + ], + }, + signature: { + merkleRoot: "0xabc", + proof: [], + targetHash: "0xabc", + type: "SHA3MerkleProof", + }, + }) + ).toStrictEqual(true); + }); + }); + + describe("unicode", () => { + test("should not corrupt unicode document", async () => { + const document = { + ...RAW_DOCUMENT_DID, + credentialSubject: { + key1: "哦喷啊特特是他题哦你", + key2: "นยำืฟะะำหะฟะรนื", + key3: "おぺなってsたちおn", + key4: "خحثىشففثسفشفهخى", + }, + }; + const wrapped = await wrapDocument(document); + expect(wrapped.proof.merkleRoot).toBeTruthy(); + expect(wrapped.credentialSubject.key1).toBe(document.credentialSubject.key1); + expect(wrapped.credentialSubject.key2).toBe(document.credentialSubject.key2); + expect(wrapped.credentialSubject.key3).toBe(document.credentialSubject.key3); + expect(wrapped.credentialSubject.key4).toBe(document.credentialSubject.key4); + }); + }); +}); diff --git a/src/4.0/__tests__/e2e.test.wip b/src/4.0/__tests__/e2e.test.wip deleted file mode 100644 index a7d3b699..00000000 --- a/src/4.0/__tests__/e2e.test.wip +++ /dev/null @@ -1,405 +0,0 @@ -import { - __unsafe__use__it__at__your__own__risks__wrapDocument as wrapDocument, - __unsafe__use__it__at__your__own__risks__wrapDocuments as wrapDocuments, - obfuscate, - SchemaId, - validateSchema, - verifySignature, -} from "../.."; -import { SignedWrappedDocument, WrappedDocument } from "../../3.0/types"; -import { - IdentityProofType, - Method, - OpenAttestationDocument, - ProofType, - TemplateType, -} from "../../__generated__/schema.3.0"; -import { cloneDeep, omit } from "lodash"; -import sampleDid from "../schema/sample-credential-did.json"; - -const openAttestationDataWithDid = sampleDid as OpenAttestationDocument; - -const openAttestationData = { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - "https://schemata.openattestation.com/com/openattestation/1.0/OpenAttestation.v3.json", - "https://schemata.openattestation.com/com/openattestation/1.0/CustomContext.json", - ], - reference: "document identifier", - validFrom: "2010-01-01T19:23:24Z", - issuanceDate: "2010-01-01T19:23:24Z", - name: "document owner name", - type: ["VerifiableCredential", "UniversityDegreeCredential", "OpenAttestationCredential"], - credentialSubject: { - id: "did:example:ebfeb1f712ebc6f1c276e12ec21", - degree: { - type: "BachelorDegree", - name: "Bachelor of Science in Mechanical Engineering", - }, - }, - openAttestationMetadata: { - template: { - name: "any", - type: TemplateType.EmbeddedRenderer, - url: "http://some.example.com", - }, - proof: { - type: ProofType.OpenAttestationProofMethod, - value: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - method: Method.TokenRegistry, - }, - identityProof: { - type: IdentityProofType.DNSTxt, - identifier: "tradetrust.io", - }, - }, - issuer: { - id: "http://some.example.com", - type: "OpenAttestationIssuer", - name: "DEMO STORE", - }, -}; - -const datum = [ - { - key1: "test", - ...openAttestationData, - }, - { - key1: "hello", - key2: "item2", - ...openAttestationData, - }, - { - key1: "item1", - key2: "true", - key3: 3.14159, - key4: false, - ...openAttestationData, - }, - { - key1: "item2", - ...openAttestationData, - }, -]; - -describe("3.0 E2E Test Scenarios", () => { - describe("Issuing a single document", () => { - const document = datum[0]; - - test("fails for missing data", async () => { - const missingData = { - ...omit(cloneDeep(document), "issuer"), - }; - await expect( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - wrapDocument(missingData, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }) - ).rejects.toThrow("Invalid document"); - }); - test("creates a wrapped document", async () => { - const wrappedDocument = await wrapDocument( - { - ...openAttestationData, - key1: "test", - }, - { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - } - ); - expect(wrappedDocument.schema).toBe("http://example.com/schema.json"); - expect(wrappedDocument.key1).toEqual(expect.stringContaining("test")); - expect(wrappedDocument.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); - expect(wrappedDocument.proof.targetHash).toBeDefined(); - expect(wrappedDocument.proof.merkleRoot).toBeDefined(); - expect(wrappedDocument.proof.proofs).toEqual([]); - expect(wrappedDocument.proof.merkleRoot).toBe(wrappedDocument.proof.targetHash); - }); - test("creates a wrapped document with DNS-DID IdentityProof", async () => { - const wrappedDocumentWithDnsDID = await wrapDocument(openAttestationDataWithDid, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - expect(wrappedDocumentWithDnsDID.schema).toBe("http://example.com/schema.json"); - expect(wrappedDocumentWithDnsDID.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); - expect(wrappedDocumentWithDnsDID.proof.targetHash).toBeDefined(); - expect(wrappedDocumentWithDnsDID.proof.merkleRoot).toBeDefined(); - expect(wrappedDocumentWithDnsDID.proof.proofs).toEqual([]); - expect(wrappedDocumentWithDnsDID.proof.merkleRoot).toBe(wrappedDocumentWithDnsDID.proof.targetHash); - expect(wrappedDocumentWithDnsDID.openAttestationMetadata.identityProof?.type).toContain(IdentityProofType.DNSDid); - expect(wrappedDocumentWithDnsDID.openAttestationMetadata.identityProof?.identifier).toContain( - openAttestationDataWithDid.openAttestationMetadata.identityProof?.identifier - ); - }); - test("checks that document is wrapped correctly", async () => { - const wrappedDocument = await wrapDocument(document, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - const verified = verifySignature(wrappedDocument); - expect(verified).toBe(true); - }); - test("checks that document conforms to the schema", async () => { - const wrappedDocument = await wrapDocument(document, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - expect(validateSchema(wrappedDocument)).toBe(true); - }); - test("does not allow for the same merkle root to be generated", async () => { - // This test takes some time to run, so we set the timeout to 14s - const wrappedDocument = await wrapDocument(document, { version: SchemaId.v3 }); - const newDocument = await wrapDocument(document, { version: SchemaId.v3 }); - expect(wrappedDocument.proof.merkleRoot).not.toBe(newDocument.proof.merkleRoot); - }, 14000); - test("obfuscate data correctly", async () => { - const newDocument = await wrapDocument(datum[2], { version: SchemaId.v3 }); - const obfuscatedDocument = await obfuscate(newDocument, ["key2"]); - expect(verifySignature(obfuscatedDocument)).toBe(true); - expect(validateSchema(obfuscatedDocument)).toBe(true); - }); - test("obfuscate data transistively", async () => { - const newDocument = await wrapDocument(datum[2], { version: SchemaId.v3 }); - const intermediateDocument = obfuscate(newDocument, ["key2"]); - const obfuscatedDocument = obfuscate(intermediateDocument, ["key3"]); - expect(obfuscate(newDocument, ["key2", "key3"])).toEqual(obfuscatedDocument); - }); - }); - - describe("Issuing a batch of documents", () => { - test("fails if there is a malformed document", async () => { - const malformedDatum = [ - ...datum, - // @ts-expect-error missing properties from OpenAttestationCredential: "@context", credentialSubject, issuanceDate, issuer, and 2 more. - { - laurent: "task force, assemble!!", - } as WrappedDocument, - ]; - await expect(wrapDocuments(malformedDatum)).rejects.toThrow("Invalid document"); - }); - test("creates a batch of documents if all are in the right format", async () => { - const wrappedDocuments = await wrapDocuments(datum, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - wrappedDocuments.forEach((doc, i: number) => { - expect(doc.schema).toBe("http://example.com/schema.json"); - expect(doc.proof.type).toBe("OpenAttestationMerkleProofSignature2018"); - expect(doc.key1).toEqual(expect.stringContaining(datum[i].key1)); - expect(doc.proof.targetHash).toBeDefined(); - expect(doc.proof.merkleRoot).toBeDefined(); - expect(doc.proof.proofs.length).toEqual(2); - }); - }); - test("checks that documents are wrapped correctly", async () => { - const wrappedDocuments = await wrapDocuments(datum, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - const verified = wrappedDocuments.reduce((prev, curr) => verifySignature(curr) && prev, true); - expect(verified).toBe(true); - }); - test("checks that documents conforms to the schema", async () => { - const wrappedDocuments = await wrapDocuments(datum, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - const validatedSchema = wrappedDocuments.reduce((prev: boolean, curr: any) => validateSchema(curr) && prev, true); - expect(validatedSchema).toBe(true); - }); - test("does not allow for same merkle root to be generated", async () => { - const wrappedDocuments = await wrapDocuments(datum, { - externalSchemaId: "http://example.com/schema.json", - version: SchemaId.v3, - }); - const newWrappedDocuments = await wrapDocuments(datum, { - version: SchemaId.v3, - }); - expect(wrappedDocuments[0].proof.merkleRoot).not.toBe(newWrappedDocuments[0].proof.merkleRoot); - }); - }); - - describe("validate", () => { - test("should return true when document is valid and version is 3.0", () => { - const credential: WrappedDocument = { - version: SchemaId.v3, - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://schemata.openattestation.com/com/openattestation/1.0/OpenAttestation.v3.json", - "https://schemata.openattestation.com/com/openattestation/1.0/CustomContext.json", - "https://schemata.openattestation.com/com/openattestation/1.0/DrivingLicenceCredential.json", - ], - reference: "SERIAL_NUMBER_123", - name: "Republic of Singapore Driving Licence", - issuanceDate: "2010-01-01T19:23:24Z", - validFrom: "2010-01-01T19:23:24Z", - issuer: { - id: "https://example.com", - name: "DEMO STORE", - }, - type: ["VerifiableCredential", "DrivingLicenceCredential"], - credentialSubject: { - id: "did:example:SERIAL_NUMBER_123", - class: [ - { - type: "3", - effectiveDate: "2010-01-01T19:23:24Z", - }, - { - type: "3A", - effectiveDate: "2010-01-01T19:23:24Z", - }, - ], - }, - openAttestationMetadata: { - template: { - name: "CUSTOM_TEMPLATE", - type: TemplateType.EmbeddedRenderer, - url: "https://localhost:3000/renderer", - }, - proof: { - type: ProofType.OpenAttestationProofMethod, - method: Method.DocumentStore, - value: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - }, - identityProof: { - type: IdentityProofType.DNSTxt, - identifier: "tradetrust.io", - }, - }, - attachments: [ - { - fileName: "sample.pdf", - mimeType: "application/pdf", - data: "BASE64_ENCODED_FILE", - }, - ], - proof: { - proofPurpose: "assertionMethod", - salts: "", - merkleRoot: "", - privacy: { - obfuscated: [], - }, - proofs: [], - targetHash: "", - type: "OpenAttestationMerkleProofSignature2018", - }, - }; - expect(validateSchema(credential)).toStrictEqual(true); - }); - test("should return true when document is valid and version is 3.0 and identityProof is DNS-DID", () => { - const credential: WrappedDocument = { - ...openAttestationDataWithDid, - version: SchemaId.v3, - proof: { - proofPurpose: "assertionMethod", - salts: "", - merkleRoot: "", - privacy: { - obfuscated: [], - }, - proofs: [], - targetHash: "", - type: "OpenAttestationMerkleProofSignature2018", - }, - }; - expect(validateSchema(credential)).toStrictEqual(true); - }); - test("should return true when signed document is valid and version is 3.0 and identityProof is DNS-DID", () => { - const credential: SignedWrappedDocument = { - ...openAttestationDataWithDid, - version: SchemaId.v3, - proof: { - proofPurpose: "assertionMethod", - salts: "", - merkleRoot: "", - privacy: { - obfuscated: [], - }, - proofs: [], - targetHash: "", - signature: "", - key: "", - type: "OpenAttestationMerkleProofSignature2018", - }, - }; - expect(validateSchema(credential)).toStrictEqual(true); - }); - test("should return false when document is invalid due to no DNS-DID identifier", () => { - const credential: any = { - ...openAttestationDataWithDid, - openAttestationMetadata: { - ...openAttestationDataWithDid.openAttestationMetadata, - identityProof: { - identifier: "tradetrust.io", - }, - }, - version: SchemaId.v3, - proof: { - proofPurpose: "assertionMethod", - salts: "", - merkleRoot: "", - privacy: { - obfuscated: [], - }, - proofs: [], - targetHash: "", - signature: "", - key: "", - type: "OpenAttestationMerkleProofSignature2018", - }, - }; - expect(validateSchema(credential)).toStrictEqual(false); - }); - test("should default to 2.0 when document is valid and version is undefined", () => { - expect( - validateSchema({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore run test with version being undefined to only ignore that part - version: undefined, - data: { - issuers: [ - { - name: "issuer.name", - certificateStore: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - }, - ], - }, - signature: { - merkleRoot: "0xabc", - proof: [], - targetHash: "0xabc", - type: "SHA3MerkleProof", - }, - }) - ).toStrictEqual(true); - }); - }); - - describe("unicode", () => { - test("should not corrupt unicode document", async () => { - const extraData = { - key1: "哦喷啊特特是他题哦你", - key2: "นยำืฟะะำหะฟะรนื", - key3: "おぺなってsたちおn", - key4: "خحثىشففثسفشفهخى", - }; - const document = { - ...openAttestationData, - ...extraData, - }; - const wrapped = await wrapDocument(document); - expect(wrapped.proof.merkleRoot).toBeTruthy(); - expect(wrapped.key1).toBe(extraData.key1); - expect(wrapped.key2).toBe(extraData.key2); - expect(wrapped.key3).toBe(extraData.key3); - expect(wrapped.key4).toBe(extraData.key4); - }); - }); -}); From 1285c8ea854b81077ba3e762a0f67347cbd8ba19 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Fri, 3 May 2024 08:07:14 +0800 Subject: [PATCH 107/107] refactor: improve error types and exporting --- src/4.0/__tests__/e2e.test.ts | 14 +++++++++++--- src/4.0/__tests__/wrap.test.ts | 30 ++++++++++++++++++++++++------ src/4.0/obfuscate.ts | 16 +++++++++++++--- src/4.0/validate/context.ts | 11 +++++++++-- src/4.0/wrap.ts | 27 ++++++++++++++++++--------- 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/4.0/__tests__/e2e.test.ts b/src/4.0/__tests__/e2e.test.ts index a254e235..8d2d7ac4 100644 --- a/src/4.0/__tests__/e2e.test.ts +++ b/src/4.0/__tests__/e2e.test.ts @@ -51,9 +51,17 @@ describe("V4 E2E Test Scenarios", () => { const missingData = { ...omit(cloneDeep(DOCUMENT_ONE), "issuer"), }; - await expect(wrapDocument(missingData as unknown as V4Document)).rejects.toThrow( - "Input document does not conform to OpenAttestation" - ); + await expect(wrapDocument(missingData as unknown as V4Document)).rejects.toThrowErrorMatchingInlineSnapshot(` + "Input document does not conform to Open Attestation v4.0 Data Model: + { + "_errors": [], + "issuer": { + "_errors": [ + "Required" + ] + } + }" + `); }); test("creates a wrapped document", async () => { diff --git a/src/4.0/__tests__/wrap.test.ts b/src/4.0/__tests__/wrap.test.ts index 490d5659..c8eeace1 100644 --- a/src/4.0/__tests__/wrap.test.ts +++ b/src/4.0/__tests__/wrap.test.ts @@ -42,6 +42,7 @@ describe("V4.0 wrap document", () => { "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", ], + type: ["VerifiableCredential", "OpenAttestationCredential"], credentialSubject: { id: "0x1234567890123456789012345678901234567890", @@ -54,9 +55,20 @@ describe("V4.0 wrap document", () => { identityProof: { identityProofType: "DNS-DID", identifier: "example.openattestation.com" }, } as V4Document["issuer"], }) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Input document does not conform to OpenAttestation v4.0 Data Model: [{"code":"invalid_literal","expected":"OpenAttestationIssuer","path":["issuer","type"],"message":"Invalid literal value, expected \\"OpenAttestationIssuer\\""}]"` - ); + ).rejects.toThrowErrorMatchingInlineSnapshot(` + "Input document does not conform to Open Attestation v4.0 Data Model: + { + "_errors": [], + "issuer": { + "_errors": [], + "type": { + "_errors": [ + "Invalid literal value, expected \\"OpenAttestationIssuer\\"" + ] + } + } + }" + `); }); test("given a valid v4 document but has an extra field, should throw", async () => { @@ -66,6 +78,7 @@ describe("V4.0 wrap document", () => { "https://www.w3.org/ns/credentials/v2", "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json", ], + type: ["VerifiableCredential", "OpenAttestationCredential"], credentialSubject: { id: "0x1234567890123456789012345678901234567890", @@ -82,9 +95,14 @@ describe("V4.0 wrap document", () => { // this should not exist extraField: "extra", } as V4Document) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Input document does not conform to OpenAttestation v4.0 Data Model: [{"code":"unrecognized_keys","keys":["extraField"],"path":[],"message":"Unrecognized key(s) in object: 'extraField'"}]"` - ); + ).rejects.toThrowErrorMatchingInlineSnapshot(` + "Input document does not conform to Open Attestation v4.0 Data Model: + { + "_errors": [ + "Unrecognized key(s) in object: 'extraField'" + ] + }" + `); }); test("given a generic w3c vc, should wrap with context and type corrected", async () => { diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 7e0b4020..9164f34d 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -96,26 +96,36 @@ export const obfuscateVerifiableCredential = ; }; -export class CannotObfuscateProtectedPathsError extends Error { +class CannotObfuscateProtectedPathsError extends Error { constructor(public paths: string[]) { super( `The resultant obfuscated document is not V4 Wrapped Document compliant, please ensure that the following path(s) are not obfuscated: ${paths .map((val) => `"${val}"`) .join(", ")}` ); + // https://www.dannyguo.com/blog/how-to-fix-instanceof-not-working-for-custom-errors-in-typescript + Object.setPrototypeOf(this, CannotObfuscateProtectedPathsError.prototype); } } -export class CannotObfuscateArrayItemError extends Error { +class CannotObfuscateArrayItemError extends Error { constructor(public field: string) { super("Obfuscation of an array item is not supported"); + Object.setPrototypeOf(this, CannotObfuscateArrayItemError.prototype); } } -export class CannotResultInEmptyObjectError extends Error { +class CannotResultInEmptyObjectError extends Error { constructor(public field: string) { super( `Obfuscation of "${field}" has resulted in an empty {}, this is currently not supported. Alternatively, if the object is not part of an array, you may choose to obfuscate the parent of "${field}".` ); + Object.setPrototypeOf(this, CannotResultInEmptyObjectError.prototype); } } + +export const obfuscateErrors = { + CannotObfuscateProtectedPathsError, + CannotObfuscateArrayItemError, + CannotResultInEmptyObjectError, +}; diff --git a/src/4.0/validate/context.ts b/src/4.0/validate/context.ts index 7fbbd178..679281d1 100644 --- a/src/4.0/validate/context.ts +++ b/src/4.0/validate/context.ts @@ -31,13 +31,20 @@ const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => { export const interpretContexts = async (input: JsonLdDocument) => { const expanded = await expand(input, { documentLoader }).catch((e) => { - throw new Error(`Unable to interpret @context: ${JSON.stringify(e)}`); + throw new UnableToInterpretContextError(JSON.stringify(e, null, 2)); }); const type = (expanded[0]["@type"] as string[]) || []; const unknownTypes = type.filter((t) => t.startsWith("https://www.w3.org/ns/credentials/issuer-dependent#")); // Workaround as expansionMap no longer supported if (unknownTypes.length > 0) { - throw new Error(`Unable to interpret @context: type (${unknownTypes.map((t) => t.split("#")[1])}) is not mapped`); + throw new UnableToInterpretContextError(`Unknown types found: ${unknownTypes.map((t) => t.split("#")[1])}`); } }; + +export class UnableToInterpretContextError extends Error { + constructor(details: string) { + super(`Unable to interpret @context:\n${details}`); + Object.setPrototypeOf(this, UnableToInterpretContextError.prototype); + } +} diff --git a/src/4.0/wrap.ts b/src/4.0/wrap.ts index a3001480..0d878928 100644 --- a/src/4.0/wrap.ts +++ b/src/4.0/wrap.ts @@ -4,7 +4,8 @@ import { ContextType, ContextUrl } from "../shared/@types/document"; import { NoExtraProperties, V4Document, V4WrappedDocument, W3cVerifiableCredential } from "./types"; import { digestCredential } from "../4.0/digest"; import { encodeSalt, salt } from "./salt"; -import { interpretContexts } from "./validate"; +import { UnableToInterpretContextError, interpretContexts } from "./validate"; +import { ZodError } from "zod"; export const wrapDocument = async ( // NoExtraProperties prevents the user from passing in a document with extra properties, which is more aligned to our validation strategy of strict @@ -16,9 +17,7 @@ export const wrapDocument = async ( if (oav4context.success) { const oav4 = await V4Document.safeParseAsync(document); if (!oav4.success) { - throw new Error( - `Input document does not conform to OpenAttestation v4.0 Data Model: ${JSON.stringify(oav4.error.issues)}` - ); + throw new DataModelValidationError("Open Attestation v4.0", oav4.error); } validatedRawDocument = oav4.data; } @@ -26,11 +25,9 @@ export const wrapDocument = async ( /* 1b. only if OA VC validation fail do we continue with W3C VC data model validation */ if (!validatedRawDocument) { const vc = await W3cVerifiableCredential.safeParseAsync(document); - if (!vc.success) - throw new Error( - `Input document does not conform to Verifiable Credentials v2.0 Data Model: ${JSON.stringify(vc.error.issues)}` - ); - + if (!vc.success) { + throw new DataModelValidationError("Verifiable Credentials v2.0", vc.error); + } validatedRawDocument = vc.data; } @@ -142,3 +139,15 @@ function extractAndAssertAsV4DocumentProps