From e8095a3d445946f0fdf511a54cbe13bc978298c6 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 11:37:09 +0300 Subject: [PATCH 01/10] chore: add typecheck script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4dd6388..c1e6fdc 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "test": "tsc && vitest run", "test-start": "vitest", "build": "rm -rf dist/ && tsc", + "typecheck": "tsc --noEmit", "prepare": "npm run build" }, "dependencies": { From 2c5210dfc912d6279ddfb840f35f7309de171f28 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 13:12:45 +0300 Subject: [PATCH 02/10] chore: add ESLint configuration --- eslint.config.mjs | 36 +++ package.json | 5 + yarn.lock | 715 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 eslint.config.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..7132e70 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,36 @@ +import eslint from '@eslint/js'; +import { defineConfig } from 'eslint/config'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default defineConfig( + { + ignores: ['dist/**', 'build/**', 'output/**', 'coverage/**', '.nyc_output/**', 'node_modules/**'], + }, + { + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, + }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{ts,mts}'], + rules: { + 'no-undef': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + 'prefer-const': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + 'no-useless-escape': 'off', + 'no-case-declarations': 'off', + }, + }, + { + files: ['src/**/*.test.ts', 'eslint.config.mjs', 'vitest.config.mts', 'setup-vitest.ts'], + languageOptions: { + globals: globals.node, + }, + }, +); diff --git a/package.json b/package.json index c1e6fdc..31d94c3 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test-start": "vitest", "build": "rm -rf dist/ && tsc", "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings=0", "prepare": "npm run build" }, "dependencies": { @@ -37,10 +38,14 @@ "zod": "^3.24.1" }, "devDependencies": { + "@eslint/js": "^9.39.5", "@sd-jwt/types": "^0.10.0", "@types/node": "^24.10.2", "@xmldom/xmldom": "^0.9.12", + "eslint": "^9.39.5", + "globals": "^16.0.0", "typescript": "~5.9.3", + "typescript-eslint": "^8.53.0", "vitest": "^4.1.11" } } diff --git a/yarn.lock b/yarn.lock index 1470889..7ccc0b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,6 +32,105 @@ resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.2.tgz#adcd1cae6cfc5ba3dd1ace65642e7e63db744eca" integrity sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA== +"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz#8911bd72b2c3640a543609e0400b8c4d2e7e7cb6" + integrity sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + +"@eslint/config-array@^0.21.2": + version "0.21.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.2.tgz#f29e22057ad5316cf23836cee9a34c81fffcb7e6" + integrity sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw== + dependencies: + "@eslint/object-schema" "^2.1.7" + debug "^4.3.1" + minimatch "^3.1.5" + +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== + dependencies: + "@eslint/core" "^0.17.0" + +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.6": + version "3.3.7" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.7.tgz#76d3dedec4a30ea32df797bf8a0085c1311b7316" + integrity sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw== + dependencies: + ajv "^6.14.0" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.3.2" + minimatch "^3.1.5" + strip-json-comments "^3.1.1" + +"@eslint/js@9.39.5", "@eslint/js@^9.39.5": + version "9.39.5" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.5.tgz#6f2fbcff75500d229d535e0a949ae13472c84787" + integrity sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A== + +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== + +"@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== + dependencies: + "@eslint/core" "^0.17.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.2.tgz#a8272ca03b2acf492670222b2320b6c421bfde60" + integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA== + dependencies: + "@humanfs/types" "^0.15.0" + +"@humanfs/node@^0.16.6": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.8.tgz#8f800cccc13f4f8cd3116e2d9c0a94939da3e3ed" + integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ== + dependencies: + "@humanfs/core" "^0.19.2" + "@humanfs/types" "^0.15.0" + "@humanwhocodes/retry" "^0.4.0" + +"@humanfs/types@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@humanfs/types/-/types-0.15.0.tgz#f2a09f62012390b2bff3fc6fb248ddec8c09a090" + integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + "@jridgewell/sourcemap-codec@^1.5.5": version "1.6.0" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz#f4c663e862f06dc98ca4d453862c46902789a18d" @@ -332,6 +431,16 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== +"@types/estree@^1.0.6": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24" + integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/node@^24.10.2": version "24.13.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.6.tgz#30ba2d8aae3c321fd170fe52a915231a67a4875c" @@ -339,6 +448,102 @@ dependencies: undici-types "~7.18.0" +"@typescript-eslint/eslint-plugin@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz#9855d73bcc4e9d074cac3a0b8c244a4966e519ba" + integrity sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg== + dependencies: + "@eslint-community/regexpp" "^4.12.2" + "@typescript-eslint/scope-manager" "8.72.0" + "@typescript-eslint/type-utils" "8.72.0" + "@typescript-eslint/utils" "8.72.0" + "@typescript-eslint/visitor-keys" "8.72.0" + ignore "^7.0.5" + natural-compare "^1.4.0" + ts-api-utils "^2.5.0" + +"@typescript-eslint/parser@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.72.0.tgz#e90d951ba97c6e2d8f1032f048292b7e39d8a019" + integrity sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw== + dependencies: + "@typescript-eslint/scope-manager" "8.72.0" + "@typescript-eslint/types" "8.72.0" + "@typescript-eslint/typescript-estree" "8.72.0" + "@typescript-eslint/visitor-keys" "8.72.0" + debug "^4.4.3" + +"@typescript-eslint/project-service@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.72.0.tgz#7667745dee47f92eaf12082b0e33b76db4338423" + integrity sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.72.0" + "@typescript-eslint/types" "^8.72.0" + debug "^4.4.3" + +"@typescript-eslint/scope-manager@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz#673cf580ef6e2377a5ef42a7bee7c7808e0fb886" + integrity sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA== + dependencies: + "@typescript-eslint/types" "8.72.0" + "@typescript-eslint/visitor-keys" "8.72.0" + +"@typescript-eslint/tsconfig-utils@8.72.0", "@typescript-eslint/tsconfig-utils@^8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz#a71e770c420ccbd7afc081f388bfa87d1958dc16" + integrity sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA== + +"@typescript-eslint/type-utils@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz#b66543c7a5cb3c1447bc455222b8c02b7499a8ea" + integrity sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw== + dependencies: + "@typescript-eslint/types" "8.72.0" + "@typescript-eslint/typescript-estree" "8.72.0" + "@typescript-eslint/utils" "8.72.0" + debug "^4.4.3" + ts-api-utils "^2.5.0" + +"@typescript-eslint/types@8.72.0", "@typescript-eslint/types@^8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.72.0.tgz#d90bb9954fefa13acbcdb43bb7c0155f74d60fca" + integrity sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ== + +"@typescript-eslint/typescript-estree@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz#e20f08b852484a49af7fe621d264e8e73138308b" + integrity sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug== + dependencies: + "@typescript-eslint/project-service" "8.72.0" + "@typescript-eslint/tsconfig-utils" "8.72.0" + "@typescript-eslint/types" "8.72.0" + "@typescript-eslint/visitor-keys" "8.72.0" + debug "^4.4.3" + minimatch "^10.2.2" + semver "^7.7.3" + tinyglobby "^0.2.15" + ts-api-utils "^2.5.0" + +"@typescript-eslint/utils@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.72.0.tgz#12de2367030571a0b1d855999cdd5d614cfe7e8b" + integrity sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg== + dependencies: + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/scope-manager" "8.72.0" + "@typescript-eslint/types" "8.72.0" + "@typescript-eslint/typescript-estree" "8.72.0" + +"@typescript-eslint/visitor-keys@8.72.0": + version "8.72.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz#e96d7faafae685e0a0f276dc59b00e9c0ad95869" + integrity sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA== + dependencies: + "@typescript-eslint/types" "8.72.0" + eslint-visitor-keys "^5.0.0" + "@vitest/expect@4.1.11": version "4.1.11" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.11.tgz#5f580d1f9cdbba314dbf23b2d911f8eb23878f5f" @@ -404,6 +609,16 @@ resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.9.12.tgz#1f84c07cb95ccf28202299f77b5fd7fc257151e8" integrity sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.15.0: + version "8.19.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.19.0.tgz#6b26fd97ce1cc9dc50bdf7ad305e9f1384d4f28a" + integrity sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -411,6 +626,28 @@ agent-base@6: dependencies: debug "4" +ajv@^6.14.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492" + integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + asn1js@^3.0.5: version "3.0.5" resolved "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz" @@ -444,11 +681,36 @@ axios@^1.18.0: https-proxy-agent "^5.0.1" proxy-from-env "^2.1.0" +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +brace-expansion@^1.1.7: + version "1.1.21" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.21.tgz#edf4fab5c64d051aea5a8def49aba1c7522279f3" + integrity sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^5.0.8: + version "5.0.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.12.tgz#995fbb4750a77c4d16a7dc942ff2ca6ef8e675ec" + integrity sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ== + dependencies: + balanced-match "^4.0.2" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" @@ -470,6 +732,11 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + cbor-extract@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.2.2.tgz#ee6b04ba56ecc351cfedc68ca70fa60487d52998" @@ -496,6 +763,26 @@ chai@^6.2.2: resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e" integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg== +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -503,11 +790,25 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + dcql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dcql/-/dcql-3.0.1.tgz#0a4ee0027635828076e16c5e946868e9b714a5a6" @@ -515,13 +816,18 @@ dcql@^3.0.1: dependencies: valibot "^1.4.2" -debug@4: +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" @@ -578,6 +884,102 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint-visitor-keys@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" + integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== + +eslint@^9.39.5: + version "9.39.5" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.5.tgz#2a4e3c8b0f753196efae943c8ffaa8730fc6a3fa" + integrity sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.2" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" + "@eslint/eslintrc" "^3.3.6" + "@eslint/js" "9.39.5" + "@eslint/plugin-kit" "^0.4.1" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + ajv "^6.14.0" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.5" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + +esquery@^1.5.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + estree-walker@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" @@ -585,16 +987,64 @@ estree-walker@^3.0.3: dependencies: "@types/estree" "^1.0.0" +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + expect-type@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.4.0.tgz#24edf7f0cc69a44d008567ba4594ab96f3c3a3d6" integrity sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA== +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fdir@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.4.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6" + integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== + follow-redirects@^1.16.0: version "1.16.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" @@ -645,11 +1095,33 @@ get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^16.0.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.5.0.tgz#ccf1594a437b97653b2be13ed4d8f5c9f850cac1" + integrity sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ== + gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" @@ -682,6 +1154,46 @@ ieee754@^1.2.1: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.5: + version "7.0.13" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.13.tgz#f19dfb5374313cf84e1976bb4b650235ebfd948c" + integrity sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + jose@^5.9.6: version "5.9.6" resolved "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz" @@ -692,6 +1204,43 @@ js-base64@^3.7.6: resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz" integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== +js-yaml@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.2.tgz#8e44fb14a2643c59726bb15787b5f1512cb3d3fb" + integrity sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + lightningcss-android-arm64@1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907" @@ -766,6 +1315,18 @@ lightningcss@^1.33.0: lightningcss-win32-arm64-msvc "1.33.0" lightningcss-win32-x64-msvc "1.33.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + magic-string@^0.30.21: version "0.30.21" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" @@ -790,6 +1351,20 @@ mime-types@^2.1.35: dependencies: mime-db "1.52.0" +minimatch@^10.2.2: + version "10.2.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.6.tgz#fd956bbe0b77241e9f15ac5dccb1c638060968ef" + integrity sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A== + dependencies: + brace-expansion "^5.0.8" + +minimatch@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + ms@^2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -800,6 +1375,11 @@ nanoid@^3.3.18: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.19.tgz#336d4aa4bcd4fb24d2cddede7ffeae40bec03f0a" integrity sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + node-gyp-build-optional-packages@5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz" @@ -812,6 +1392,49 @@ obug@^2.1.1: resolved "https://registry.yarnpkg.com/obug/-/obug-2.2.1.tgz#9c453efeeda822050eb9774242e969c784d64d78" integrity sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q== +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + pathe@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" @@ -848,11 +1471,21 @@ postcss@^8.5.28: picocolors "^1.1.1" source-map-js "^1.2.1" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + proxy-from-env@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pvtsutils@^1.3.2, pvtsutils@^1.3.6: version "1.3.6" resolved "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz" @@ -875,6 +1508,11 @@ reflect-metadata@^0.2.2: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + rolldown@~1.2.9: version "1.2.11" resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.11.tgz#89c4e07d9098b88fe0b023de59d63a6216e9285c" @@ -899,6 +1537,23 @@ rolldown@~1.2.9: "@rolldown/binding-win32-arm64-msvc" "1.2.11" "@rolldown/binding-win32-x64-msvc" "1.2.11" +semver@^7.7.3: + version "7.8.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" + integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + siginfo@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" @@ -919,6 +1574,18 @@ std-env@^4.0.0-rc.1: resolved "https://registry.yarnpkg.com/std-env/-/std-env-4.3.0.tgz#4e7231b61fd83219aa7f76f71fa8818b07e41321" integrity sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag== +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + tinybench@^2.9.0: version "2.9.0" resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" @@ -942,6 +1609,11 @@ tinyrainbow@^3.1.0: resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.2.0.tgz#f62df4fda24415b353c08b9c38934b4b13877655" integrity sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A== +ts-api-utils@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" + integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== + tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -959,6 +1631,23 @@ tsyringe@^4.10.0: dependencies: tslib "^1.9.3" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +typescript-eslint@^8.53.0: + version "8.72.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.72.0.tgz#50a8fd6d4304ffd1e7fd375f8a439c6f15488071" + integrity sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A== + dependencies: + "@typescript-eslint/eslint-plugin" "8.72.0" + "@typescript-eslint/parser" "8.72.0" + "@typescript-eslint/typescript-estree" "8.72.0" + "@typescript-eslint/utils" "8.72.0" + typescript@~5.9.3: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" @@ -969,6 +1658,13 @@ undici-types@~7.18.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + valibot@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/valibot/-/valibot-1.5.0.tgz#c05adda6b2823807bb0248e2952c35e1a50046fd" @@ -1013,6 +1709,13 @@ vitest@^4.1.11: vite "^6.0.0 || ^7.0.0 || ^8.0.0" why-is-node-running "^2.3.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + why-is-node-running@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" @@ -1021,6 +1724,16 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zod-validation-error@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-5.0.0.tgz#96db948070b7bfcb13bfec5134113e580f9eee38" From 5006b359d16033903537d0ce3a0cb1c845955d8a Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 13:28:40 +0300 Subject: [PATCH 03/10] chore: configure Prettier and ESLint compatibility --- .editorconfig-checker.json | 1 + .prettierignore | 2 ++ .prettierrc.yaml | 3 +++ eslint.config.mjs | 2 ++ package.json | 4 ++++ yarn.lock | 10 ++++++++++ 6 files changed, 22 insertions(+) create mode 100644 .prettierignore create mode 100644 .prettierrc.yaml diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index fc3b0b8..1f8957c 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -1,4 +1,5 @@ { + "SpacesAfterTabs": true, "Exclude": [ "^(node_modules|dist|output|build)/" ] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..99d553d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +output/ +assets/ diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..1fa06a8 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,3 @@ +singleQuote: true +trailingComma: 'all' +printWidth: 100 diff --git a/eslint.config.mjs b/eslint.config.mjs index 7132e70..55192bc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,6 +2,7 @@ import eslint from '@eslint/js'; import { defineConfig } from 'eslint/config'; import globals from 'globals'; import tseslint from 'typescript-eslint'; +import prettierConfig from 'eslint-config-prettier/flat'; export default defineConfig( { @@ -33,4 +34,5 @@ export default defineConfig( globals: globals.node, }, }, + prettierConfig, ); diff --git a/package.json b/package.json index 31d94c3..48b8c02 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "build": "rm -rf dist/ && tsc", "typecheck": "tsc --noEmit", "lint": "eslint . --max-warnings=0", + "format": "prettier . --write", + "format:check": "prettier . --check", "prepare": "npm run build" }, "dependencies": { @@ -43,7 +45,9 @@ "@types/node": "^24.10.2", "@xmldom/xmldom": "^0.9.12", "eslint": "^9.39.5", + "eslint-config-prettier": "^10.1.8", "globals": "^16.0.0", + "prettier": "^3.8.0", "typescript": "~5.9.3", "typescript-eslint": "^8.53.0", "vitest": "^4.1.11" diff --git a/yarn.lock b/yarn.lock index 7ccc0b2..2db6a6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -889,6 +889,11 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +eslint-config-prettier@^10.1.8: + version "10.1.8" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz#15734ce4af8c2778cc32f0b01b37b0b5cd1ecb97" + integrity sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== + eslint-scope@^8.4.0: version "8.4.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" @@ -1476,6 +1481,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier@^3.8.0: + version "3.9.10" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.10.tgz#ac378e432f1a7381518f2e080816bbaefa38d585" + integrity sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA== + proxy-from-env@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" From 9204d316f00121cf6783adbe2d1d8a8943f6774b Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 13:37:28 +0300 Subject: [PATCH 04/10] style: format project with Prettier --- .editorconfig-checker.json | 4 +- .github/workflows/build.yml | 40 +- .github/workflows/pr-title.yml | 38 +- .github/workflows/release.yml | 80 +- .pre-commit-config.yaml | 2 +- .releaserc.json | 24 +- README.md | 16 +- eslint.config.mjs | 9 +- setup-vitest.ts | 2 +- src/ParsingEngine.ts | 7 +- src/PublicKeyResolverEngine.ts | 6 +- src/VerifyingEngine.ts | 25 +- src/core/ECDH-ES.ts | 4 +- src/core/HS512.test.ts | 10 +- src/core/HS512.ts | 25 +- src/core/MemoryStore.test.ts | 110 +-- src/core/MemoryStore.ts | 2 +- src/core/Result.ts | 3 +- src/core/VctDocumentProvider.test.ts | 70 +- src/core/VctDocumentProvider.ts | 17 +- src/core/communication/RequestMessage.ts | 2 +- src/core/communication/ResponseMessage.ts | 2 +- src/core/index.ts | 17 +- src/credential-parsers/MsoMdocParser.test.ts | 162 ++-- src/credential-parsers/MsoMdocParser.ts | 154 ++-- src/credential-parsers/SDJWTVCParser.test.ts | 107 +-- src/credential-parsers/SDJWTVCParser.ts | 126 +-- .../MsoMdocVerifier.test.ts | 68 +- src/credential-verifiers/MsoMdocVerifier.ts | 210 ++--- .../SDJWTVCVerifier.test.ts | 52 +- src/credential-verifiers/SDJWTVCVerifier.ts | 292 ++++--- src/defaultHttpClient.ts | 20 +- src/error.ts | 105 ++- src/functions/CustomCredentialSvg.test.ts | 59 +- src/functions/CustomCredentialSvg.ts | 72 +- src/functions/convertDataUriToImage.ts | 36 +- .../convertOpenid4vciToSdjwtvcClaims.test.ts | 148 ++-- .../convertOpenid4vciToSdjwtvcClaims.ts | 19 +- .../convertSdjwtvcToOpenid4vciClaims.test.ts | 77 +- .../convertSdjwtvcToOpenid4vciClaims.ts | 20 +- .../convertSdjwtvcToOpenid4vciDisplay.test.ts | 72 +- .../convertSdjwtvcToOpenid4vciDisplay.ts | 20 +- src/functions/formatDate.test.ts | 40 +- src/functions/formatDate.ts | 25 +- src/functions/pickBestSvgTemplate.test.ts | 246 +++--- src/functions/pickBestSvgTemplate.ts | 29 +- src/index.ts | 2 +- src/interfaces.ts | 112 ++- .../openid4vp/OpenID4VPClientAPI.test.ts | 604 ++++++++------- src/protocols/openid4vp/OpenID4VPClientAPI.ts | 606 +++++++++------ .../openid4vp/OpenID4VPServerAPI.test.ts | 529 +++++++------ src/protocols/openid4vp/OpenID4VPServerAPI.ts | 346 +++++---- src/protocols/openid4vp/sessionTranscript.ts | 31 +- src/protocols/openid4vp/transactionData.ts | 296 ++++--- src/protocols/openid4vp/types.ts | 28 +- src/rendering.test.ts | 186 +++-- src/rendering.ts | 23 +- src/resolvers/dataUriResolver.test.ts | 75 +- src/resolvers/dataUriResolver.ts | 49 +- src/resolvers/friendlyNameResolver.ts | 29 +- src/resolvers/index.ts | 4 +- src/resolvers/loadSvgTemplate.ts | 11 +- .../CredentialConfigurationSupportedSchema.ts | 105 +-- src/schemas/CredentialOfferSchema.ts | 4 +- src/schemas/GrantSchema.ts | 22 +- src/schemas/MdocIacasResponseSchema.ts | 12 +- ...OpenidAuthorizationServerMetadataSchema.ts | 8 +- ...orizationServerMetadataSchemaTest1.test.ts | 24 +- ...orizationServerMetadataSchemaTest2.test.ts | 342 +++++++- .../OpenidCredentialIssuerMetadataSchema.ts | 68 +- ...CredentialIssuerMetadataSchemaTest.test.ts | 263 ++++++- ...redentialIssuerMetadataSchemaTest2.test.ts | 728 +++++++++++++++++- ...redentialIssuerMetadataSchemaTest3.test.ts | 166 +++- src/schemas/SdJwtVcPayloadSchema.ts | 4 +- src/schemas/SdJwtVcTypeMetadataSchema.ts | 128 +-- src/types.ts | 81 +- src/utils/cborDate.test.ts | 34 +- src/utils/cborDate.ts | 12 +- src/utils/escapeSVG.ts | 20 +- src/utils/getIssuerMetadata.test.ts | 38 +- src/utils/getIssuerMetadata.ts | 18 +- src/utils/getSdJwtVcMetadata.test.ts | 338 ++++---- src/utils/getSdJwtVcMetadata.ts | 73 +- src/utils/matchLocalizedDisplay.ts | 9 +- src/utils/serializeDcqlQuery.ts | 2 +- src/utils/sri.test.ts | 111 +-- src/utils/sri.ts | 6 +- src/utils/urlPathUtils.test.ts | 76 +- src/utils/urlPathUtils.ts | 7 +- src/utils/util.ts | 25 +- src/utils/verifyCertificate.ts | 16 +- src/utils/verifyX5C.test.ts | 26 +- src/utils/verifyX5C.ts | 11 +- tsconfig.json | 37 +- 94 files changed, 5323 insertions(+), 3096 deletions(-) diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index 1f8957c..6a8e7ce 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -1,6 +1,4 @@ { "SpacesAfterTabs": true, - "Exclude": [ - "^(node_modules|dist|output|build)/" - ] + "Exclude": ["^(node_modules|dist|output|build)/"] } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 724e515..b051a42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,34 +22,34 @@ jobs: name: Build package runs-on: ubuntu-latest steps: - - name: Check out code - uses: actions/checkout@v5 + - name: Check out code + uses: actions/checkout@v5 - - name: Pin node.js version - uses: actions/setup-node@v5 - with: - node-version: 24 + - name: Pin node.js version + uses: actions/setup-node@v5 + with: + node-version: 24 - - name: Install dependencies - run: yarn install --frozen-lockfile + - name: Install dependencies + run: yarn install --frozen-lockfile - - name: Build package - run: yarn build + - name: Build package + run: yarn build test: name: Run tests runs-on: ubuntu-latest steps: - - name: Check out code - uses: actions/checkout@v5 + - name: Check out code + uses: actions/checkout@v5 - - name: Pin node.js version - uses: actions/setup-node@v5 - with: - node-version: 24 + - name: Pin node.js version + uses: actions/setup-node@v5 + with: + node-version: 24 - - name: Install dependencies - run: yarn install --frozen-lockfile + - name: Install dependencies + run: yarn install --frozen-lockfile - - name: Run tests - run: yarn test + - name: Run tests + run: yarn test diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index e92cfb9..248d367 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -12,22 +12,22 @@ jobs: name: Validate PR title runs-on: ubuntu-latest steps: - - name: Check PR title follows Conventional Commits - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - types: | - feat - fix - perf - refactor - docs - test - build - ci - chore - requireScope: false - subjectPattern: ^(?![A-Z]).+$ - subjectPatternError: | - The subject "{subject}" of PR title "{title}" must start with a lower-case letter. + - name: Check PR title follows Conventional Commits + uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + perf + refactor + docs + test + build + ci + chore + requireScope: false + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject "{subject}" of PR title "{title}" must start with a lower-case letter. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 891e5a9..22cb828 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,43 +19,43 @@ jobs: # Holds the release App's key; deployment branches: master only environment: release steps: - - name: Create release App token - id: app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - client-id: ${{ vars.RELEASE_APP_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - - - name: Check out code - uses: actions/checkout@v5 - with: - # semantic-release needs the full history and tags - fetch-depth: 0 - # semantic-release pushes with the App token below - persist-credentials: false - - - name: Pin node.js version - uses: actions/setup-node@v5 - with: - node-version: 24 - check-latest: true - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run tests - run: yarn test - - - name: Release - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - GIT_AUTHOR_NAME: wwwallet-release[bot] - GIT_AUTHOR_EMAIL: 339619412+wwwallet-release[bot]@users.noreply.github.com - GIT_COMMITTER_NAME: wwwallet-release[bot] - GIT_COMMITTER_EMAIL: 339619412+wwwallet-release[bot]@users.noreply.github.com - run: > - npx --yes - -p semantic-release@25.0.9 - -p @semantic-release/git@11.0.1 - -p conventional-changelog-conventionalcommits@9.3.1 - semantic-release + - name: Create release App token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.RELEASE_APP_CLIENT_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + + - name: Check out code + uses: actions/checkout@v5 + with: + # semantic-release needs the full history and tags + fetch-depth: 0 + # semantic-release pushes with the App token below + persist-credentials: false + + - name: Pin node.js version + uses: actions/setup-node@v5 + with: + node-version: 24 + check-latest: true + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run tests + run: yarn test + + - name: Release + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + GIT_AUTHOR_NAME: wwwallet-release[bot] + GIT_AUTHOR_EMAIL: 339619412+wwwallet-release[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: wwwallet-release[bot] + GIT_COMMITTER_EMAIL: 339619412+wwwallet-release[bot]@users.noreply.github.com + run: > + npx --yes + -p semantic-release@25.0.9 + -p @semantic-release/git@11.0.1 + -p conventional-changelog-conventionalcommits@9.3.1 + semantic-release diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 893a264..91c5022 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,5 +11,5 @@ repos: hooks: - id: editorconfig-checker name: Check .editorconfig rules - args: ["-no-color"] + args: ['-no-color'] types_or: [text] diff --git a/.releaserc.json b/.releaserc.json index 078c97f..f6ba47d 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -5,14 +5,20 @@ ["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }], ["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }], ["@semantic-release/npm", { "npmPublish": false }], - ["@semantic-release/git", { - "assets": ["package.json"], - "message": "chore(release): ${nextRelease.version} [skip ci]" - }], - ["@semantic-release/github", { - "successCommentCondition": "<% return issue.pull_request; %>", - "failCommentCondition": false, - "releasedLabels": false - }] + [ + "@semantic-release/git", + { + "assets": ["package.json"], + "message": "chore(release): ${nextRelease.version} [skip ci]" + } + ], + [ + "@semantic-release/github", + { + "successCommentCondition": "<% return issue.pull_request; %>", + "failCommentCondition": false, + "releasedLabels": false + } + ] ] } diff --git a/README.md b/README.md index 93a0c14..a48ceee 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ - # Wallet Common - This library serves as a centralized collection of reusable component-functions that are shared across multiple software stacks. Its main goal is to promote consistency, reduce duplication and streamline development by providing well-tested, commonly used utilities and components in one place. ## Components @@ -16,12 +14,10 @@ This library serves as a centralized collection of reusable component-functions - Common type definitions - Common JSON schema definition for response validations - ### Interfaces The `interface.ts` file defines all the interfaces that are exported from this library. - ## Development ### Pre-commit Hook @@ -80,12 +76,12 @@ type(optional-scope): lower-case summary wallet-common follows [semantic versioning](https://semver.org/). Every merge to `master` runs the `release` workflow, which uses [semantic-release](https://github.com/semantic-release/semantic-release) to work out the next version from the commits merged since the last `v*` tag (PR title and description). If there are several, the largest bump wins: -| PR title | Release (e.g. from `1.0.1`) | -|---|---| -| any type with `!`, e.g. `feat!: …` | major (`2.0.0`) | -| `feat: …` | minor (`1.1.0`) | -| `fix: …`, `perf: …` | patch (`1.0.2`) | -| `docs`, `test`, `ci`, `build`, `refactor`, `chore` | no release | +| PR title | Release (e.g. from `1.0.1`) | +| -------------------------------------------------- | ---------------------------- | +| any type with `!`, e.g. `feat!: …` | major (`2.0.0`) | +| `feat: …` | minor (`1.1.0`) | +| `fix: …`, `perf: …` | patch (`1.0.2`) | +| `docs`, `test`, `ci`, `build`, `refactor`, `chore` | no release | A line starting with `BREAKING CHANGE:` (or `BREAKING-CHANGE:`) in the PR description also makes a major release, whatever the title. diff --git a/eslint.config.mjs b/eslint.config.mjs index 55192bc..3220238 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,7 +6,14 @@ import prettierConfig from 'eslint-config-prettier/flat'; export default defineConfig( { - ignores: ['dist/**', 'build/**', 'output/**', 'coverage/**', '.nyc_output/**', 'node_modules/**'], + ignores: [ + 'dist/**', + 'build/**', + 'output/**', + 'coverage/**', + '.nyc_output/**', + 'node_modules/**', + ], }, { linterOptions: { diff --git a/setup-vitest.ts b/setup-vitest.ts index fded23a..d2c9bc6 100644 --- a/setup-vitest.ts +++ b/setup-vitest.ts @@ -1 +1 @@ -import "reflect-metadata"; +import 'reflect-metadata'; diff --git a/src/ParsingEngine.ts b/src/ParsingEngine.ts index 3cb986a..efceede 100644 --- a/src/ParsingEngine.ts +++ b/src/ParsingEngine.ts @@ -1,5 +1,5 @@ -import { CredentialParsingError } from "./error"; -import { CredentialParser, ParsingEngineI } from "./interfaces"; +import { CredentialParsingError } from './error'; +import { CredentialParser, ParsingEngineI } from './interfaces'; export function ParsingEngine(): ParsingEngineI { const parsers: CredentialParser[] = []; @@ -21,7 +21,6 @@ export function ParsingEngine(): ParsingEngineI { // Otherwise return immediately return result; - } catch { return { success: false, error: CredentialParsingError.UnknownError }; } @@ -29,6 +28,6 @@ export function ParsingEngine(): ParsingEngineI { // No parser handled it return { success: false, error: CredentialParsingError.UnsupportedFormat }; - } + }, }; } diff --git a/src/PublicKeyResolverEngine.ts b/src/PublicKeyResolverEngine.ts index 823d8d3..4622cfa 100644 --- a/src/PublicKeyResolverEngine.ts +++ b/src/PublicKeyResolverEngine.ts @@ -1,5 +1,5 @@ -import { PublicKeyResolutionError } from "./error"; -import { PublicKeyResolver, PublicKeyResolverEngineI } from "./interfaces"; +import { PublicKeyResolutionError } from './error'; +import { PublicKeyResolver, PublicKeyResolverEngineI } from './interfaces'; export function PublicKeyResolverEngine(): PublicKeyResolverEngineI { const resolvers: PublicKeyResolver[] = []; @@ -17,5 +17,5 @@ export function PublicKeyResolverEngine(): PublicKeyResolverEngineI { } return { success: false, error: PublicKeyResolutionError.CannotResolvePublicKey }; }, - } + }; } diff --git a/src/VerifyingEngine.ts b/src/VerifyingEngine.ts index dbbe8e9..34ca3f0 100644 --- a/src/VerifyingEngine.ts +++ b/src/VerifyingEngine.ts @@ -1,8 +1,6 @@ -import { CredentialVerificationError } from "./error"; -import { CredentialVerifier, VerifyingEngineI } from "./interfaces"; -import { VerifiableCredentialFormat } from "./types"; - - +import { CredentialVerificationError } from './error'; +import { CredentialVerifier, VerifyingEngineI } from './interfaces'; +import { VerifiableCredentialFormat } from './types'; export function VerifyingEngine(): VerifyingEngineI { const verifiers: CredentialVerifier[] = []; @@ -12,12 +10,15 @@ export function VerifyingEngine(): VerifyingEngineI { verifiers.push(credentialVerifier); }, - async verify({ rawCredential, opts }: { - rawCredential: unknown, + async verify({ + rawCredential, + opts, + }: { + rawCredential: unknown; opts: { expectedNonce?: string; expectedAudience?: string; - } + }; }) { let lastError: CredentialVerificationError | null = null; for (const v of verifiers) { @@ -30,8 +31,8 @@ export function VerifyingEngine(): VerifyingEngineI { return { success: false, - error: lastError ? lastError : CredentialVerificationError.UnknownProblem - } - } - } + error: lastError ? lastError : CredentialVerificationError.UnknownProblem, + }; + }, + }; } diff --git a/src/core/ECDH-ES.ts b/src/core/ECDH-ES.ts index 7441eac..e854bdc 100644 --- a/src/core/ECDH-ES.ts +++ b/src/core/ECDH-ES.ts @@ -1,5 +1,5 @@ -import { exportJWK, generateKeyPair } from "jose"; -import { generateRandomIdentifier } from "../utils"; +import { exportJWK, generateKeyPair } from 'jose'; +import { generateRandomIdentifier } from '../utils'; export async function generateECDHKeypair() { const { privateKey, publicKey } = await generateKeyPair('ECDH-ES', { extractable: true }); diff --git a/src/core/HS512.test.ts b/src/core/HS512.test.ts index d6aba1f..5be262a 100644 --- a/src/core/HS512.test.ts +++ b/src/core/HS512.test.ts @@ -2,10 +2,10 @@ import { it, describe } from 'vitest'; import { generateHS512Key, importHS512Key } from './HS512'; import { jwtVerify, SignJWT } from 'jose'; -const alg = "HS512"; +const alg = 'HS512'; -describe("The HS512 module", () => { - it("should generate/import/export shared secret key, sign a jwt and then verify it", async () => { +describe('The HS512 module', () => { + it('should generate/import/export shared secret key, sign a jwt and then verify it', async () => { const { secret, exportedKey } = await generateHS512Key(); const jwt = await new SignJWT({ 'urn:example:claim': true }) @@ -19,5 +19,5 @@ describe("The HS512 module", () => { const importedSecret = await importHS512Key(exportedKey); // verify with exported to make sure that export and import operations are working await jwtVerify(jwt, importedSecret); - }) -}) + }); +}); diff --git a/src/core/HS512.ts b/src/core/HS512.ts index b44a8d8..d630202 100644 --- a/src/core/HS512.ts +++ b/src/core/HS512.ts @@ -1,33 +1,32 @@ - export async function generateHS512Key() { const secret = await crypto.subtle.generateKey( { - name: "HMAC", - hash: "SHA-512", - length: 512 + name: 'HMAC', + hash: 'SHA-512', + length: 512, }, true, - ["sign", "verify"] + ['sign', 'verify'], ); - const exportedKey = await crypto.subtle.exportKey("raw", secret as CryptoKey); - const base64Key = Buffer.from(exportedKey).toString("base64"); - return { secret, exportedKey: "$b64:" + base64Key }; + const exportedKey = await crypto.subtle.exportKey('raw', secret as CryptoKey); + const base64Key = Buffer.from(exportedKey).toString('base64'); + return { secret, exportedKey: '$b64:' + base64Key }; } export async function importHS512Key(key: string): Promise { - if (!key.startsWith("$b64:")) { - throw new Error("Could not import HS512 key"); + if (!key.startsWith('$b64:')) { + throw new Error('Could not import HS512 key'); } - const raw = Buffer.from(key.split("$b64:")[1], "base64"); + const raw = Buffer.from(key.split('$b64:')[1], 'base64'); const secret = await crypto.subtle.importKey( - "raw", + 'raw', raw, { name: 'HMAC', hash: { name: 'SHA-512' }, }, false, - ['sign', 'verify'] + ['sign', 'verify'], ); return secret; } diff --git a/src/core/MemoryStore.test.ts b/src/core/MemoryStore.test.ts index 4bb51b8..82a330d 100644 --- a/src/core/MemoryStore.test.ts +++ b/src/core/MemoryStore.test.ts @@ -1,94 +1,94 @@ -import { assert, describe, it } from "vitest"; -import { MemoryStore } from "./MemoryStore"; +import { assert, describe, it } from 'vitest'; +import { MemoryStore } from './MemoryStore'; -describe("MemoryStore", () => { - it("set + get basic behavior", async () => { +describe('MemoryStore', () => { + it('set + get basic behavior', async () => { const store = new MemoryStore(); - await store.set("k1", "v1"); + await store.set('k1', 'v1'); - const value = await store.get("k1"); - assert(value === "v1"); + const value = await store.get('k1'); + assert(value === 'v1'); }); - it("get for missing key returns undefined", async () => { + it('get for missing key returns undefined', async () => { const store = new MemoryStore(); - const value = await store.get("missing"); + const value = await store.get('missing'); assert(value === undefined); }); - it("delete removes key", async () => { + it('delete removes key', async () => { const store = new MemoryStore(); - await store.set("k1", "v1"); - await store.delete("k1"); + await store.set('k1', 'v1'); + await store.delete('k1'); - const value = await store.get("k1"); + const value = await store.get('k1'); assert(value === undefined); }); - it("set on existing key updates value and keeps single entry", async () => { + it('set on existing key updates value and keeps single entry', async () => { const store = new MemoryStore(); - await store.set("k1", "v1"); - await store.set("k1", "v2"); + await store.set('k1', 'v1'); + await store.set('k1', 'v2'); - const value = await store.get("k1"); + const value = await store.get('k1'); const all = await store.getAll(); - assert(value === "v2"); + assert(value === 'v2'); assert(all.length === 1); - assert(all[0] === "v2"); + assert(all[0] === 'v2'); }); - it("capacity eviction removes oldest entry when no reads happen", async () => { + it('capacity eviction removes oldest entry when no reads happen', async () => { const store = new MemoryStore(2); - await store.set("a", "va"); - await store.set("b", "vb"); - await store.set("c", "vc"); + await store.set('a', 'va'); + await store.set('b', 'vb'); + await store.set('c', 'vc'); - assert((await store.get("a")) === undefined); - assert((await store.get("b")) === "vb"); - assert((await store.get("c")) === "vc"); + assert((await store.get('a')) === undefined); + assert((await store.get('b')) === 'vb'); + assert((await store.get('c')) === 'vc'); }); - it("get refreshes recency and affects LRU eviction", async () => { + it('get refreshes recency and affects LRU eviction', async () => { const store = new MemoryStore(2); - await store.set("a", "va"); - await store.set("b", "vb"); - await store.get("a"); - await store.set("c", "vc"); - - assert((await store.get("a")) === "va"); - assert((await store.get("b")) === undefined); - assert((await store.get("c")) === "vc"); + await store.set('a', 'va'); + await store.set('b', 'vb'); + await store.get('a'); + await store.set('c', 'vc'); + + assert((await store.get('a')) === 'va'); + assert((await store.get('b')) === undefined); + assert((await store.get('c')) === 'vc'); }); - it("re-setting existing key refreshes recency and affects LRU eviction", async () => { + it('re-setting existing key refreshes recency and affects LRU eviction', async () => { const store = new MemoryStore(2); - await store.set("a", "va"); - await store.set("b", "vb"); - await store.set("a", "va2"); - await store.set("c", "vc"); - - assert((await store.get("a")) === "va2"); - assert((await store.get("b")) === undefined); - assert((await store.get("c")) === "vc"); + await store.set('a', 'va'); + await store.set('b', 'vb'); + await store.set('a', 'va2'); + await store.set('c', 'vc'); + + assert((await store.get('a')) === 'va2'); + assert((await store.get('b')) === undefined); + assert((await store.get('c')) === 'vc'); }); - it("getAll returns current values after updates, evictions, and deletes", async () => { + it('getAll returns current values after updates, evictions, and deletes', async () => { const store = new MemoryStore(3); - await store.set("a", "va"); - await store.set("b", "vb"); - await store.set("c", "vc"); - await store.set("b", "vb2"); - await store.delete("c"); - await store.set("d", "vd"); - await store.set("e", "ve"); + await store.set('a', 'va'); + await store.set('b', 'vb'); + await store.set('c', 'vc'); + await store.set('b', 'vb2'); + await store.delete('c'); + await store.set('d', 'vd'); + await store.set('e', 've'); const all = await store.getAll(); assert(all.length === 3); - assert(all.includes("vb2")); - assert(all.includes("vd")); - assert(all.includes("ve")); + assert(all.includes('vb2')); + assert(all.includes('vd')); + assert(all.includes('ve')); }); }); diff --git a/src/core/MemoryStore.ts b/src/core/MemoryStore.ts index 49326ba..8142ddc 100644 --- a/src/core/MemoryStore.ts +++ b/src/core/MemoryStore.ts @@ -1,4 +1,4 @@ -import { GenericStore } from "./Store"; +import { GenericStore } from './Store'; export class MemoryStore implements GenericStore { private map = new Map(); diff --git a/src/core/Result.ts b/src/core/Result.ts index b3b6500..a4add4b 100644 --- a/src/core/Result.ts +++ b/src/core/Result.ts @@ -1,6 +1,5 @@ export type Result = - | { ok: true; value: T } - | { ok: false; error: E, error_description?: string }; + { ok: true; value: T } | { ok: false; error: E; error_description?: string }; export function ok(value: T): Result { return { ok: true, value }; diff --git a/src/core/VctDocumentProvider.test.ts b/src/core/VctDocumentProvider.test.ts index cf69d23..2c1721f 100644 --- a/src/core/VctDocumentProvider.test.ts +++ b/src/core/VctDocumentProvider.test.ts @@ -1,43 +1,47 @@ -import { assert, describe, it } from "vitest"; -import { createVctDocumentResolutionEngine, VctDocumentProvider, VctResolutionErrors } from "./VctDocumentProvider"; -import { ok, err } from "./Result"; +import { assert, describe, it } from 'vitest'; +import { + createVctDocumentResolutionEngine, + VctDocumentProvider, + VctResolutionErrors, +} from './VctDocumentProvider'; +import { ok, err } from './Result'; const validDoc = (vct: string) => ({ vct }); -const invalidDoc = { vct: "" }; // fails TypeMetadata's nonempty() check +const invalidDoc = { vct: '' }; // fails TypeMetadata's nonempty() check -const providerReturning = (result: Awaited>): VctDocumentProvider => ({ +const providerReturning = ( + result: Awaited>, +): VctDocumentProvider => ({ getVctMetadataDocument: async () => result, }); -describe("createVctDocumentResolutionEngine", () => { - it("returns the document from a single provider when valid", async () => { +describe('createVctDocumentResolutionEngine', () => { + it('returns the document from a single provider when valid', async () => { const engine = createVctDocumentResolutionEngine([ - providerReturning(ok(validDoc("urn:example:a"))), + providerReturning(ok(validDoc('urn:example:a'))), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(result.ok); - assert(result.value.vct === "urn:example:a"); + assert(result.value.vct === 'urn:example:a'); }); - it("returns NotFound when the single provider has nothing", async () => { + it('returns NotFound when the single provider has nothing', async () => { const engine = createVctDocumentResolutionEngine([ providerReturning(err(VctResolutionErrors.NotFound)), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(!result.ok); assert(result.error === VctResolutionErrors.NotFound); }); it("returns InvalidSchema when the single provider's document fails validation", async () => { - const engine = createVctDocumentResolutionEngine([ - providerReturning(ok(invalidDoc as any)), - ]); + const engine = createVctDocumentResolutionEngine([providerReturning(ok(invalidDoc as any))]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(!result.ok); assert(result.error === VctResolutionErrors.InvalidSchema); @@ -45,68 +49,68 @@ describe("createVctDocumentResolutionEngine", () => { it("prefers the first provider's valid document over a later one", async () => { const engine = createVctDocumentResolutionEngine([ - providerReturning(ok(validDoc("urn:example:first"))), - providerReturning(ok(validDoc("urn:example:second"))), + providerReturning(ok(validDoc('urn:example:first'))), + providerReturning(ok(validDoc('urn:example:second'))), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(result.ok); - assert(result.value.vct === "urn:example:first"); + assert(result.value.vct === 'urn:example:first'); }); - it("falls through to a later provider when an earlier one returns NotFound", async () => { + it('falls through to a later provider when an earlier one returns NotFound', async () => { const engine = createVctDocumentResolutionEngine([ providerReturning(err(VctResolutionErrors.NotFound)), - providerReturning(ok(validDoc("urn:example:second"))), + providerReturning(ok(validDoc('urn:example:second'))), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(result.ok); - assert(result.value.vct === "urn:example:second"); + assert(result.value.vct === 'urn:example:second'); }); it("falls through to a later provider when an earlier one's document is InvalidSchema", async () => { const engine = createVctDocumentResolutionEngine([ providerReturning(ok(invalidDoc as any)), - providerReturning(ok(validDoc("urn:example:second"))), + providerReturning(ok(validDoc('urn:example:second'))), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(result.ok); - assert(result.value.vct === "urn:example:second"); + assert(result.value.vct === 'urn:example:second'); }); - it("returns NotFound when every provider has nothing", async () => { + it('returns NotFound when every provider has nothing', async () => { const engine = createVctDocumentResolutionEngine([ providerReturning(err(VctResolutionErrors.NotFound)), providerReturning(err(VctResolutionErrors.NotFound)), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(!result.ok); assert(result.error === VctResolutionErrors.NotFound); }); - it("falls back to the highest-priority InvalidSchema error when no provider has a valid document", async () => { + it('falls back to the highest-priority InvalidSchema error when no provider has a valid document', async () => { const engine = createVctDocumentResolutionEngine([ providerReturning(ok(invalidDoc as any)), providerReturning(err(VctResolutionErrors.NotFound)), ]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(!result.ok); assert(result.error === VctResolutionErrors.InvalidSchema); }); - it("returns NotFound when there are no providers", async () => { + it('returns NotFound when there are no providers', async () => { const engine = createVctDocumentResolutionEngine([]); - const result = await engine.getVctMetadataDocument("urn:example:a"); + const result = await engine.getVctMetadataDocument('urn:example:a'); assert(!result.ok); assert(result.error === VctResolutionErrors.NotFound); diff --git a/src/core/VctDocumentProvider.ts b/src/core/VctDocumentProvider.ts index 7199b72..2c1ece5 100644 --- a/src/core/VctDocumentProvider.ts +++ b/src/core/VctDocumentProvider.ts @@ -1,20 +1,20 @@ -import { TypeMetadata } from "../schemas/SdJwtVcTypeMetadataSchema"; -import { err, ok, Result } from "./Result"; +import { TypeMetadata } from '../schemas/SdJwtVcTypeMetadataSchema'; +import { err, ok, Result } from './Result'; export const VctResolutionErrors = { - InvalidSchema: "invalid_schema", - NotFound: "not_found", + InvalidSchema: 'invalid_schema', + NotFound: 'not_found', } as const; export type VctResolutionError = (typeof VctResolutionErrors)[keyof typeof VctResolutionErrors]; - export interface VctDocumentProvider { getVctMetadataDocument(vct: string): Promise>; } -export const createVctDocumentResolutionEngine = (providers: VctDocumentProvider[]): VctDocumentProvider => { - +export const createVctDocumentResolutionEngine = ( + providers: VctDocumentProvider[], +): VctDocumentProvider => { return { getVctMetadataDocument: async (vct: string) => { try { @@ -33,8 +33,7 @@ export const createVctDocumentResolutionEngine = (providers: VctDocumentProvider } } return invalidSchemaError ?? err(VctResolutionErrors.NotFound); - } - catch { + } catch { return err(VctResolutionErrors.NotFound); } }, diff --git a/src/core/communication/RequestMessage.ts b/src/core/communication/RequestMessage.ts index 2cfb376..4ea557a 100644 --- a/src/core/communication/RequestMessage.ts +++ b/src/core/communication/RequestMessage.ts @@ -4,4 +4,4 @@ export type RequestMessage = { data: unknown; headers: Record; status: number; -} +}; diff --git a/src/core/communication/ResponseMessage.ts b/src/core/communication/ResponseMessage.ts index 2cc12a5..1ac8f42 100644 --- a/src/core/communication/ResponseMessage.ts +++ b/src/core/communication/ResponseMessage.ts @@ -2,4 +2,4 @@ export type ResponseMessage = { data: unknown; headers: Record; status: number; -} +}; diff --git a/src/core/index.ts b/src/core/index.ts index 39ee5c3..c71e904 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,13 +1,12 @@ +export * from './communication/RequestMessage'; +export * from './communication/ResponseMessage'; -export * from "./communication/RequestMessage"; -export * from "./communication/ResponseMessage"; +export * from './HS512'; +export * from './ECDH-ES'; -export * from "./HS512"; -export * from "./ECDH-ES"; +export * from './Store'; +export * from './MemoryStore'; -export * from "./Store"; -export * from "./MemoryStore"; +export * from './Result'; -export * from "./Result"; - -export * from "./VctDocumentProvider"; +export * from './VctDocumentProvider'; diff --git a/src/credential-parsers/MsoMdocParser.test.ts b/src/credential-parsers/MsoMdocParser.test.ts index aa48a69..3103916 100644 --- a/src/credential-parsers/MsoMdocParser.test.ts +++ b/src/credential-parsers/MsoMdocParser.test.ts @@ -1,15 +1,13 @@ - - -import { assert, expect, describe, it } from "vitest"; -import { Context } from "../interfaces"; +import { assert, expect, describe, it } from 'vitest'; +import { Context } from '../interfaces'; import { MsoMdocParser } from './MsoMdocParser'; -import { defaultHttpClient } from "../defaultHttpClient"; -import { VerifiableCredentialFormat } from "../types"; +import { defaultHttpClient } from '../defaultHttpClient'; +import { VerifiableCredentialFormat } from '../types'; const issuerSignedB64U = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; const deviceResponseB64U = `uQADZ3ZlcnNpb25jMS4waWRvY3VtZW50c4GjZ2RvY1R5cGV3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjFsaXNzdWVyU2lnbmVkuQACam5hbWVTcGFjZXOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xlNgYWFmkZnJhbmRvbVBIKMdczA7zHxWIMiZNiEw6aGRpZ2VzdElEBGxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWFOkZnJhbmRvbVBSAcocu4luUU9h1OG6mb3KaGRpZ2VzdElEAGxlbGVtZW50VmFsdWVlRVJJS0FxZWxlbWVudElkZW50aWZpZXJqZ2l2ZW5fbmFtZdgYWFukZnJhbmRvbVCg8N7UF3UtjNT5rrcv4I0naGRpZ2VzdElECmxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYY6RmcmFuZG9tUPh214HbpajZ129WkN0HEGxoZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBCI4bNHyJLeZiqCCWOeSXSaGRpZ2VzdElEFGxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzEy2BhYT6RmcmFuZG9tUHUL5DoK1Q6XGSIK2KpqX5VoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTTYGFhPpGZyYW5kb21QOsF40fU9yz-5ElPgowWmo2hkaWdlc3RJRBFsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNtgYWE-kZnJhbmRvbVDN7ONl9EGf6JhCDbHBMxjPaGRpZ2VzdElEAWxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYT6RmcmFuZG9tUHP_XGaz0ceKD_vmjJikW-VoZGlnZXN0SUQSbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMjHYGFhPpGZyYW5kb21Q19-nAhWrgR8HBaghg8XwKGhkaWdlc3RJRAlsZWxlbWVudFZhbHVl9HFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl82NdgYWFGkZnJhbmRvbVARyyxLhICjQCleUZjdlJNnaGRpZ2VzdElEAmxlbGVtZW50VmFsdWUYPHFlbGVtZW50SWRlbnRpZmllcmxhZ2VfaW5feWVhcnPYGFhUpGZyYW5kb21Qz5JnWxDm2R3cfbpiXNHuhGhkaWdlc3RJRAtsZWxlbWVudFZhbHVlGQescWVsZW1lbnRJZGVudGlmaWVybmFnZV9iaXJ0aF95ZWFy2BhYVaRmcmFuZG9tUAyz52Pd2a2dIuVbL77HwU9oZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhRpGZyYW5kb21QHrqyCHgoG-r6n5gipksku2hkaWdlc3RJRBBsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVya25hdGlvbmFsaXR52BhYVqRmcmFuZG9tUDs7795UIU3mGifs9fSMyk9oZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYXaRmcmFuZG9tUKQVC8WZ0niGucqWS7wHZpdoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZWU1MTE0N3FlbGVtZW50SWRlbnRpZmllcnRyZXNpZGVudF9wb3N0YWxfY29kZdgYWFakZnJhbmRvbVB-oVAOiGgWAPkD98PcvMfOaGRpZ2VzdElEDmxlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWGOkZnJhbmRvbVBf5UWUDodw1fG9za4erAEEaGRpZ2VzdElEFWxlbGVtZW50VmFsdWVwSEVJREVTVFJB4bqeRSAxN3FlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXTYGFhVpGZyYW5kb21Q-THkF_aDlPbGInevZoEuHmhkaWdlc3RJRA1sZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVyb2lzc3VpbmdfY291bnRyedgYWFekZnJhbmRvbVCQ0ozPJ1JCVER7YRG_k4WRaGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJxaXNzdWluZ19hdXRob3JpdHlqaXNzdWVyQXV0aIRDoQEmoRghglkCeDCCAnQwggIboAMCAQICAQIwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA4MTMxN1oXDTI1MDcwNTA4MTMxN1owbDELMAkGA1UEBhMCREUxHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMQowCAYDVQQLDAFJMTIwMAYDVQQDDClTUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDhQauGDCoOMOX04n7MrcAbiX_-xO1YUT14jZudkt6tREyIAXV8gyt5FcRsYHhz4ryz97rjL0uogxHO6jMZr3bijgZAwgY0wHQYDVR0OBBYEFIj4QpCxKw1zy1tvydFlXoIcsPpiMAwGA1UdEwEB_wQCMAAwDgYDVR0PAQH_BAQDAgeAMC0GA1UdEQQmMCSCImRlbW8ucGlkLWlzc3Vlci5idW5kZXNkcnVja2VyZWkuZGUwHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wCgYIKoZIzj0EAwIDRwAwRAIgG3-U85HEM4X1qCKMotVTe3fCPQbBSptTFpbkaYdm8hkCICmJHazX9sVz41Um41v1P-UubwBAuV8XDmp7rDga6AW1WQJ9MIICeTCCAiCgAwIBAgIUB5E9QVZtmUYcDtCjKB_H3VQv72gwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA2NDgwOVoXDTM0MDUyOTA2NDgwOVowgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYGzdwFDnc7-Kn5ibAvCOM8ke77VQxqfMcwZL8IaIA-WCROcCfmY_giH92qMru5p_kyOivE0RC_IbdMONvDoUyaNmMGQwHQYDVR0OBBYEFNRWGMCJOOgOWIQYyXZiv6u7xZC-MB8GA1UdIwQYMBaAFNRWGMCJOOgOWIQYyXZiv6u7xZC-MBIGA1UdEwEB_wQIMAYBAf8CAQAwDgYDVR0PAQH_BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIGEm7wkZKHt_atb4MdFnXW6yrnwMUT2u136gdtl10Y6hAiBuTFqvVYth1rbxzCP0xWZHmQK9kVyxn8GPfX27EIzzs1kEudgYWQS0p2ZzdGF0dXOha3N0YXR1c19saXN0omNpZHgAY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIEirhRmTGLu7J6NTeywbqo9PTcrHsJ8zPZFIRtAT03UGAVggVP9xFV5GEUYAvMAYTu12XMiETKFyfSY_E1ytQ3wWEXwCWCDBdY3nOghQbKW8IorJBwI7XggfSHMzG_EhbyKkv7I5LANYIPMwBStwx36BqJCsA8GLGmDqnHWctgszyIoRsoaAVWhBBFggRHfmXFEsEHtjn2zJBL2IWKw4ZNOpT8r6kPJjnwZSNS4FWCCSerbybLZiNc9iptNvLp1l3m4GFs1YPcrW6Ru9dhy3NgZYIASk7NwYS5ETt8huiKDQZmQsy_0mZNWnRzpBiuMECgzqB1ggJUBadYwH934ZgXzunsol1Fdaip6maDu3ioiRvZWZs5sIWCDyB1u3PsND6w5Fea16X19w01jow9gIBnj5-Zd1P5iy4QlYIM7KAT7aFfJdjiuasohID2iDnwTJv0lg0CjTdNA-XVsnClggW9xddmd5U39UPems65FkBzGzazlYkAPEbTiepxMa2ZALWCAwBnAfgMI0EZzssorAvT5WqKjAoaExVc-oNQQVMIn6yQxYIA7TtHyGykR4SvhDWhJwkPonZG-dxGEe0n2DwmSKt8eNDVggnfiw1cwSIZs77zRvtCwBBLXhbq_-D3mMLET3qBXhMp4OWCBliBbg1MmVDKt3FmaiUTz8-smu5I9Ca3MEY4VtG9AzBA9YIKQGIYCRdKodGbQKKBMTZSTBCl17JOX5HFKRw4YMDaswEFggZ_fgaCdQpN8KGWla7o9pCbBgEh0uE-uggwTb-1XvdZkRWCCJ45ATjvvzDIxX-GuHmzR1fMjziy8mLFe7gghCiwBTsBJYIDmK_FQLWIXDoyFjRrySy2ICsUNlWOZhwgSBoFf5w6mvE1ggosNW9izG0Erb2Wly9ZuEYd0YmVgEf83_8MzMvk31wS0UWCD_R1--R7wILyc3SiOKmRpJ0Dvr5WJLGn2jEexXsFlbPBVYIJqJxtHSoawDNCSVQZppaM2DXLTswuCaeXklOQMfoTLgbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVggt06a6xedGC3bshSAiuGceNUnReLTFjzvFUQo-Lqc8LsiWCAX0ZfKPf0xPgaoLThiwwy5uq2F0PotADZmO1WVJRbUS29kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAr01sYZ08XiZQ_ZMIjO4_PZSnVQBChwtXAn3LJXupHXU_8Lk9ZU0zm6IHsGS2viFXBdEomPHj6RF3wmieN7iHFmxkZXZpY2VTaWduZWS5AAJqbmFtZVNwYWNlc9gYQ7kAAGpkZXZpY2VBdXRouQABb2RldmljZVNpZ25hdHVyZYRDoQEmoPZYQFNKLb4rfqOsmrYXTmGZ31PW8J0APasTdt9W_ejWSNVag0NLgLURJdz0whCa6cAboZNa52XR09U0HjPWL7G1BYBmc3RhdHVzAA`; -const eudiwPidMdoc = `ompuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZgZ2BhYU6RoZGlnZXN0SUQAZnJhbmRvbVDiRrFLz0Ad09XfRd5pXLWFcWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWROZWFs2BhYU6RoZGlnZXN0SUQBZnJhbmRvbVD3JppqZyKuCyb3-J8f2XfPcWVsZW1lbnRJZGVudGlmaWVyamdpdmVuX25hbWVsZWxlbWVudFZhbHVlZVR5bGVy2BhYW6RoZGlnZXN0SUQCZnJhbmRvbVA0NGuozp-pzYgvr4erZWkacWVsZW1lbnRJZGVudGlmaWVyamJpcnRoX2RhdGVsZWxlbWVudFZhbHVl2QPsajE5NTUtMDQtMTLYGFhepGhkaWdlc3RJRANmcmFuZG9tUNz8yYAFzwpNgQVnwRvsucpxZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2VsZWxlbWVudFZhbHVlbzEwMSBUcmF1bmVyLCBBVNgYWFKkaGRpZ2VzdElEBGZyYW5kb21QIEgxiOL8mURoD5Iz6qUeTnFlbGVtZW50SWRlbnRpZmllcmtuYXRpb25hbGl0eWxlbGVtZW50VmFsdWWBYkFU2BhYVqRoZGlnZXN0SUQFZnJhbmRvbVBdv-U8yh5xtvoU4fqIJ635cWVsZW1lbnRJZGVudGlmaWVycHJlc2lkZW50X2NvdW50cnlsZWxlbWVudFZhbHVlYkFU2BhYX6RoZGlnZXN0SUQGZnJhbmRvbVCmCFzKh_LKFKQtOuAfvTs9cWVsZW1lbnRJZGVudGlmaWVybnJlc2lkZW50X3N0YXRlbGVsZW1lbnRWYWx1ZW1Mb3dlciBBdXN0cmlh2BhYY6RoZGlnZXN0SUQHZnJhbmRvbVB11pJVpSw7UGEMZctmbcSpcWVsZW1lbnRJZGVudGlmaWVybXJlc2lkZW50X2NpdHlsZWxlbWVudFZhbHVlckdlbWVpbmRlIEJpYmVyYmFjaNgYWFykaGRpZ2VzdElECGZyYW5kb21QEdv-Nh2pCYTvkkVf8-ebYHFlbGVtZW50SWRlbnRpZmllcnRyZXNpZGVudF9wb3N0YWxfY29kZWxlbGVtZW50VmFsdWVkMzMzMdgYWFqkaGRpZ2VzdElECWZyYW5kb21QKBzfAAX6c7tdc9FA5I6vEXFlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXRsZWxlbWVudFZhbHVlZ1RyYXVuZXLYGFhcpGhkaWdlc3RJRApmcmFuZG9tUK9mywH11Q4z8evhJWeVEs5xZWxlbWVudElkZW50aWZpZXJ1cmVzaWRlbnRfaG91c2VfbnVtYmVybGVsZW1lbnRWYWx1ZWMxMDHYGFhZpGhkaWdlc3RJRAtmcmFuZG9tUDNruPvSiENfW789-83_tfJxZWxlbWVudElkZW50aWZpZXJxZmFtaWx5X25hbWVfYmlydGhsZWxlbWVudFZhbHVlZE5lYWzYGFhZpGhkaWdlc3RJRAxmcmFuZG9tULy1oHZ0rxVhJn-9m6XyvKdxZWxlbWVudElkZW50aWZpZXJwZ2l2ZW5fbmFtZV9iaXJ0aGxlbGVtZW50VmFsdWVlVHlsZXLYGFhHpGhkaWdlc3RJRA1mcmFuZG9tUF673QqXkaA8C4xHlV3hi3hxZWxlbWVudElkZW50aWZpZXJjc2V4bGVsZW1lbnRWYWx1ZQHYGFhnpGhkaWdlc3RJRA5mcmFuZG9tUHDV3H8wAsFt2NF1PgVhW-ZxZWxlbWVudElkZW50aWZpZXJtZW1haWxfYWRkcmVzc2xlbGVtZW50VmFsdWV2dHlsZXIubmVhbEBleGFtcGxlLmNvbdgYWE-kaGRpZ2VzdElED2ZyYW5kb21QiQvbCV6heKCgEjYLSa0-VnFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xOGxlbGVtZW50VmFsdWX12BhYUaRoZGlnZXN0SUQQZnJhbmRvbVD-iM3GESGdTLfDkfIQzovlcWVsZW1lbnRJZGVudGlmaWVybGFnZV9pbl95ZWFyc2xlbGVtZW50VmFsdWUYR9gYWFSkaGRpZ2VzdElEEWZyYW5kb21Q8cGC3OR4AYbMlZ066AqvTHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhcmxlbGVtZW50VmFsdWUZB6PYGFiIpGhkaWdlc3RJRBJmcmFuZG9tUHOgfu86qxQ9thDfgx72k_RxZWxlbWVudElkZW50aWZpZXJ4HnBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlcmxlbGVtZW50VmFsdWV4JDYwNzcwMjVkLWRiMWUtNDM2Zi1hNDNjLTdlNDQxM2Y2ZjU1ZNgYWFykaGRpZ2VzdElEE2ZyYW5kb21QDjDOtp1cT8YuzIK8KJAxKXFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNS0xMi0yMdgYWHGkaGRpZ2VzdElEFGZyYW5kb21Qtp3kXRbagXaP2TxJYFL4C3FlbGVtZW50SWRlbnRpZmllcnFpc3N1aW5nX2F1dGhvcml0eWxlbGVtZW50VmFsdWV4G0dSIEFkbWluaXN0cmF0aXZlIGF1dGhvcml0edgYWFWkaGRpZ2VzdElEFWZyYW5kb21QGcN_Ms9r5eLhT_Wzr_jKvXFlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnlsZWxlbWVudFZhbHVlYkdS2BhYeKRoZGlnZXN0SUQWZnJhbmRvbVBiNbNDyA9VU3PswHG6v5fgcWVsZW1lbnRJZGVudGlmaWVyb2RvY3VtZW50X251bWJlcmxlbGVtZW50VmFsdWV4JDc5NGYwYTQ1LWI2NWEtNDU0MS05MWJhLTUzN2VmOGUxZjcyOdgYWFykaGRpZ2VzdElEF2ZyYW5kb21Q-pZ6KmADJJTqmLQLijMLWHFlbGVtZW50SWRlbnRpZmllcnRpc3N1aW5nX2p1cmlzZGljdGlvbmxlbGVtZW50VmFsdWVkR1ItSdgYWF-kaGRpZ2VzdElEGBhmcmFuZG9tUJJ_PuYLQu6fOtSAtIG3vw9xZWxlbWVudElkZW50aWZpZXJtaXNzdWFuY2VfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNS0wOS0xMmppc3N1ZXJBdXRohEOhASahGCFZAuswggLnMIICjaADAgECAhQT9nrYAx9IlFbnXEOpxhxkfZkyyzAKBggqhkjOPQQDAjBcMR4wHAYDVQQDDBVQSUQgSXNzdWVyIENBIC0gVVQgMDIxLTArBgNVBAoMJEVVREkgV2FsbGV0IFJlZmVyZW5jZSBJbXBsZW1lbnRhdGlvbjELMAkGA1UEBhMCVVQwHhcNMjUwNDEwMTQyNjM4WhcNMjYwNzA0MTQyNjM3WjBSMRQwEgYDVQQDDAtQSUQgRFMgLSAwNDEtMCsGA1UECgwkRVVESSBXYWxsZXQgUmVmZXJlbmNlIEltcGxlbWVudGF0aW9uMQswCQYDVQQGEwJVVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAWJduiGXqZKA_1Y4DXuwmk1wiR2CGXxhKB_kNlMljFxlPEaYZ77D3290lF6NVP7Wv5-9e0Yh5rrBZophrkuCPejggE1MIIBMTAfBgNVHSMEGDAWgBRix5RHKL0PohYgp5rCSZRE8QHTxzAjBgNVHREEHDAaghhpc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYwFgYDVR0lAQH_BAwwCgYIK4ECAgAAAQIwQwYDVR0fBDwwOjA4oDagNIYyaHR0cHM6Ly9wcmVwcm9kLnBraS5ldWRpdy5kZXYvY3JsL3BpZF9DQV9VVF8wMi5jcmwwHQYDVR0OBBYEFJKwqK5r9u2s-kKSTObMJu-DWKeqMA4GA1UdDwEB_wQEAwIHgDBdBgNVHRIEVjBUhlJodHRwczovL2dpdGh1Yi5jb20vZXUtZGlnaXRhbC1pZGVudGl0eS13YWxsZXQvYXJjaGl0ZWN0dXJlLWFuZC1yZWZlcmVuY2UtZnJhbWV3b3JrMAoGCCqGSM49BAMCA0gAMEUCIHLmJZalSbZAO_K-hGUauL95cNZCjiL1oDvaJujuh8DUAiEAlABXrKH1uGRze_sX1hZhf1l1tNyR_H7ndtDL3D2TNiZZBM_YGFkEyqZndmVyc2lvbmMxLjBvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZsdmFsdWVEaWdlc3RzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMbgZAFggvS-NBqTe6qxFbYBsm_kaCcNw066FeUud1j3MJh94eO0BWCCnKPgPX73RKd6eaNxUmYbOQe4IBEm_uwysxyG-B2bxVwJYIDxdwFiImy1uj7dBdtLC7fhA2NlFEGXoOJ1Mm3o5KOy9A1ggb2dDDFINUryfS3TOfJyPh4fL9VMmHrPAG0vaTvYfDswEWCByk20dTX7fh5ZvK7KXtxX2zuBEVCF64DVtXy6u5vmuLwVYINzh6MbVelXm4sE4haxMDxTZV_47yD5XH82qiXyiqE9XBlggT_9MmL9b6DXOdFPzXSjTgX6OTkR6lb4_hm5nMzUqJK4HWCDewyuxhWfq4JkAiPW2Yemlc_2ZMvUTceFiGYDf94t8ZQhYIO1R4oMTeVozKqoKXMlVv1YEsJoDcoS9GiRUNoMCFNhnCVggpzJN73oe0DPjdE2od1NOK_itIiJP-SDmokwSOz9RKTAKWCCgawxXPXwKgl71OYb3s89yc1TKNPpxVG_9XNsWpr-TUQtYIF-ws9ywwKdnMY0gtSSu1manuHSeOobiT-CWVXPxTZfIDFggiQskeygyv6Hfq8tYdh7DPmAN3i0JnMU_avieNlIEK5cNWCDMg8JHYJKSBA45c0Rw1_0acwj-xKBl0Dd4sbXNDscUTA5YIDpII6w08sr5bpRGvZ4dmrl5QyTtu9rEoSdAW3Uoz01YD1ggma-F51eYn5lMnP_t_KnXxBkgGHtL9IZMZoWsOkONSzAQWCBiyOJvGhB07TzhaJViRMsRNwEyfCKLVuAor2QQuLQ_FhFYIC1mn-54nCqxr_wFx9BGupDNfkdzaqttfH2IVUumsXNMElggyb7KvFH5olWBnquey3fLOXAtfCoP9_KkV4DTBqdbvGcTWCAIzS65u9gNr7UR6kFgSYtFg-p6DdQw-ZOiwPVDnfQq4hRYIGQAEh3ayu3KuB9GDOoiouES2XtA0QG7vNxn-EOAtuBcFVggUmWQpAd4Gm0rJkR8_tfWt_ZTzOPiOdcZLrHIbsYzGqoWWCAxzvmLfhfUE0_rUbfhx6K9JjBqZ3Si07JCFr7Fj50EXBdYIF7-5zmK5UP-s7EOw3UcEUyeiipv4U_4yRSREaCw07EGGBhYIBgaPOk-NwJEFnGlX8rACGFaS9OKoCyaCFSAdLg_e560bWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgg8vYlKAAQ1K2_5PKb8Xt3ig8AnGuFoMZCZCPfuyhP7p4iWCCmK-XY1GrqDCk8o51xCwEuZAa_5G26GLHV4IvuQNomemdkb2NUeXBld2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xbHZhbGlkaXR5SW5mb6Nmc2lnbmVkwHgeMjAyNS0wOS0xMlQxMjozNTo0NS4xOTU1NjU1MjZaaXZhbGlkRnJvbcB4HjIwMjUtMDktMTJUMTI6MzU6NDUuMTk1NTY1NTI2Wmp2YWxpZFVudGlswHgeMjAyNS0xMC0xMlQxMjozNTo0NS4xOTU1NjU1MjZaWECI1G41KFMRRBsWjfzFK5mb1MwZKsxgxdL6Araaqz473RRxg5OJEz88gzW7t_AE7BG1nF96-tQzq32355mbyt6V` +const eudiwPidMdoc = `ompuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZgZ2BhYU6RoZGlnZXN0SUQAZnJhbmRvbVDiRrFLz0Ad09XfRd5pXLWFcWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWROZWFs2BhYU6RoZGlnZXN0SUQBZnJhbmRvbVD3JppqZyKuCyb3-J8f2XfPcWVsZW1lbnRJZGVudGlmaWVyamdpdmVuX25hbWVsZWxlbWVudFZhbHVlZVR5bGVy2BhYW6RoZGlnZXN0SUQCZnJhbmRvbVA0NGuozp-pzYgvr4erZWkacWVsZW1lbnRJZGVudGlmaWVyamJpcnRoX2RhdGVsZWxlbWVudFZhbHVl2QPsajE5NTUtMDQtMTLYGFhepGhkaWdlc3RJRANmcmFuZG9tUNz8yYAFzwpNgQVnwRvsucpxZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2VsZWxlbWVudFZhbHVlbzEwMSBUcmF1bmVyLCBBVNgYWFKkaGRpZ2VzdElEBGZyYW5kb21QIEgxiOL8mURoD5Iz6qUeTnFlbGVtZW50SWRlbnRpZmllcmtuYXRpb25hbGl0eWxlbGVtZW50VmFsdWWBYkFU2BhYVqRoZGlnZXN0SUQFZnJhbmRvbVBdv-U8yh5xtvoU4fqIJ635cWVsZW1lbnRJZGVudGlmaWVycHJlc2lkZW50X2NvdW50cnlsZWxlbWVudFZhbHVlYkFU2BhYX6RoZGlnZXN0SUQGZnJhbmRvbVCmCFzKh_LKFKQtOuAfvTs9cWVsZW1lbnRJZGVudGlmaWVybnJlc2lkZW50X3N0YXRlbGVsZW1lbnRWYWx1ZW1Mb3dlciBBdXN0cmlh2BhYY6RoZGlnZXN0SUQHZnJhbmRvbVB11pJVpSw7UGEMZctmbcSpcWVsZW1lbnRJZGVudGlmaWVybXJlc2lkZW50X2NpdHlsZWxlbWVudFZhbHVlckdlbWVpbmRlIEJpYmVyYmFjaNgYWFykaGRpZ2VzdElECGZyYW5kb21QEdv-Nh2pCYTvkkVf8-ebYHFlbGVtZW50SWRlbnRpZmllcnRyZXNpZGVudF9wb3N0YWxfY29kZWxlbGVtZW50VmFsdWVkMzMzMdgYWFqkaGRpZ2VzdElECWZyYW5kb21QKBzfAAX6c7tdc9FA5I6vEXFlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXRsZWxlbWVudFZhbHVlZ1RyYXVuZXLYGFhcpGhkaWdlc3RJRApmcmFuZG9tUK9mywH11Q4z8evhJWeVEs5xZWxlbWVudElkZW50aWZpZXJ1cmVzaWRlbnRfaG91c2VfbnVtYmVybGVsZW1lbnRWYWx1ZWMxMDHYGFhZpGhkaWdlc3RJRAtmcmFuZG9tUDNruPvSiENfW789-83_tfJxZWxlbWVudElkZW50aWZpZXJxZmFtaWx5X25hbWVfYmlydGhsZWxlbWVudFZhbHVlZE5lYWzYGFhZpGhkaWdlc3RJRAxmcmFuZG9tULy1oHZ0rxVhJn-9m6XyvKdxZWxlbWVudElkZW50aWZpZXJwZ2l2ZW5fbmFtZV9iaXJ0aGxlbGVtZW50VmFsdWVlVHlsZXLYGFhHpGhkaWdlc3RJRA1mcmFuZG9tUF673QqXkaA8C4xHlV3hi3hxZWxlbWVudElkZW50aWZpZXJjc2V4bGVsZW1lbnRWYWx1ZQHYGFhnpGhkaWdlc3RJRA5mcmFuZG9tUHDV3H8wAsFt2NF1PgVhW-ZxZWxlbWVudElkZW50aWZpZXJtZW1haWxfYWRkcmVzc2xlbGVtZW50VmFsdWV2dHlsZXIubmVhbEBleGFtcGxlLmNvbdgYWE-kaGRpZ2VzdElED2ZyYW5kb21QiQvbCV6heKCgEjYLSa0-VnFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xOGxlbGVtZW50VmFsdWX12BhYUaRoZGlnZXN0SUQQZnJhbmRvbVD-iM3GESGdTLfDkfIQzovlcWVsZW1lbnRJZGVudGlmaWVybGFnZV9pbl95ZWFyc2xlbGVtZW50VmFsdWUYR9gYWFSkaGRpZ2VzdElEEWZyYW5kb21Q8cGC3OR4AYbMlZ066AqvTHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhcmxlbGVtZW50VmFsdWUZB6PYGFiIpGhkaWdlc3RJRBJmcmFuZG9tUHOgfu86qxQ9thDfgx72k_RxZWxlbWVudElkZW50aWZpZXJ4HnBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlcmxlbGVtZW50VmFsdWV4JDYwNzcwMjVkLWRiMWUtNDM2Zi1hNDNjLTdlNDQxM2Y2ZjU1ZNgYWFykaGRpZ2VzdElEE2ZyYW5kb21QDjDOtp1cT8YuzIK8KJAxKXFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNS0xMi0yMdgYWHGkaGRpZ2VzdElEFGZyYW5kb21Qtp3kXRbagXaP2TxJYFL4C3FlbGVtZW50SWRlbnRpZmllcnFpc3N1aW5nX2F1dGhvcml0eWxlbGVtZW50VmFsdWV4G0dSIEFkbWluaXN0cmF0aXZlIGF1dGhvcml0edgYWFWkaGRpZ2VzdElEFWZyYW5kb21QGcN_Ms9r5eLhT_Wzr_jKvXFlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnlsZWxlbWVudFZhbHVlYkdS2BhYeKRoZGlnZXN0SUQWZnJhbmRvbVBiNbNDyA9VU3PswHG6v5fgcWVsZW1lbnRJZGVudGlmaWVyb2RvY3VtZW50X251bWJlcmxlbGVtZW50VmFsdWV4JDc5NGYwYTQ1LWI2NWEtNDU0MS05MWJhLTUzN2VmOGUxZjcyOdgYWFykaGRpZ2VzdElEF2ZyYW5kb21Q-pZ6KmADJJTqmLQLijMLWHFlbGVtZW50SWRlbnRpZmllcnRpc3N1aW5nX2p1cmlzZGljdGlvbmxlbGVtZW50VmFsdWVkR1ItSdgYWF-kaGRpZ2VzdElEGBhmcmFuZG9tUJJ_PuYLQu6fOtSAtIG3vw9xZWxlbWVudElkZW50aWZpZXJtaXNzdWFuY2VfZGF0ZWxlbGVtZW50VmFsdWXZA-xqMjAyNS0wOS0xMmppc3N1ZXJBdXRohEOhASahGCFZAuswggLnMIICjaADAgECAhQT9nrYAx9IlFbnXEOpxhxkfZkyyzAKBggqhkjOPQQDAjBcMR4wHAYDVQQDDBVQSUQgSXNzdWVyIENBIC0gVVQgMDIxLTArBgNVBAoMJEVVREkgV2FsbGV0IFJlZmVyZW5jZSBJbXBsZW1lbnRhdGlvbjELMAkGA1UEBhMCVVQwHhcNMjUwNDEwMTQyNjM4WhcNMjYwNzA0MTQyNjM3WjBSMRQwEgYDVQQDDAtQSUQgRFMgLSAwNDEtMCsGA1UECgwkRVVESSBXYWxsZXQgUmVmZXJlbmNlIEltcGxlbWVudGF0aW9uMQswCQYDVQQGEwJVVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAWJduiGXqZKA_1Y4DXuwmk1wiR2CGXxhKB_kNlMljFxlPEaYZ77D3290lF6NVP7Wv5-9e0Yh5rrBZophrkuCPejggE1MIIBMTAfBgNVHSMEGDAWgBRix5RHKL0PohYgp5rCSZRE8QHTxzAjBgNVHREEHDAaghhpc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYwFgYDVR0lAQH_BAwwCgYIK4ECAgAAAQIwQwYDVR0fBDwwOjA4oDagNIYyaHR0cHM6Ly9wcmVwcm9kLnBraS5ldWRpdy5kZXYvY3JsL3BpZF9DQV9VVF8wMi5jcmwwHQYDVR0OBBYEFJKwqK5r9u2s-kKSTObMJu-DWKeqMA4GA1UdDwEB_wQEAwIHgDBdBgNVHRIEVjBUhlJodHRwczovL2dpdGh1Yi5jb20vZXUtZGlnaXRhbC1pZGVudGl0eS13YWxsZXQvYXJjaGl0ZWN0dXJlLWFuZC1yZWZlcmVuY2UtZnJhbWV3b3JrMAoGCCqGSM49BAMCA0gAMEUCIHLmJZalSbZAO_K-hGUauL95cNZCjiL1oDvaJujuh8DUAiEAlABXrKH1uGRze_sX1hZhf1l1tNyR_H7ndtDL3D2TNiZZBM_YGFkEyqZndmVyc2lvbmMxLjBvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZsdmFsdWVEaWdlc3RzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMbgZAFggvS-NBqTe6qxFbYBsm_kaCcNw066FeUud1j3MJh94eO0BWCCnKPgPX73RKd6eaNxUmYbOQe4IBEm_uwysxyG-B2bxVwJYIDxdwFiImy1uj7dBdtLC7fhA2NlFEGXoOJ1Mm3o5KOy9A1ggb2dDDFINUryfS3TOfJyPh4fL9VMmHrPAG0vaTvYfDswEWCByk20dTX7fh5ZvK7KXtxX2zuBEVCF64DVtXy6u5vmuLwVYINzh6MbVelXm4sE4haxMDxTZV_47yD5XH82qiXyiqE9XBlggT_9MmL9b6DXOdFPzXSjTgX6OTkR6lb4_hm5nMzUqJK4HWCDewyuxhWfq4JkAiPW2Yemlc_2ZMvUTceFiGYDf94t8ZQhYIO1R4oMTeVozKqoKXMlVv1YEsJoDcoS9GiRUNoMCFNhnCVggpzJN73oe0DPjdE2od1NOK_itIiJP-SDmokwSOz9RKTAKWCCgawxXPXwKgl71OYb3s89yc1TKNPpxVG_9XNsWpr-TUQtYIF-ws9ywwKdnMY0gtSSu1manuHSeOobiT-CWVXPxTZfIDFggiQskeygyv6Hfq8tYdh7DPmAN3i0JnMU_avieNlIEK5cNWCDMg8JHYJKSBA45c0Rw1_0acwj-xKBl0Dd4sbXNDscUTA5YIDpII6w08sr5bpRGvZ4dmrl5QyTtu9rEoSdAW3Uoz01YD1ggma-F51eYn5lMnP_t_KnXxBkgGHtL9IZMZoWsOkONSzAQWCBiyOJvGhB07TzhaJViRMsRNwEyfCKLVuAor2QQuLQ_FhFYIC1mn-54nCqxr_wFx9BGupDNfkdzaqttfH2IVUumsXNMElggyb7KvFH5olWBnquey3fLOXAtfCoP9_KkV4DTBqdbvGcTWCAIzS65u9gNr7UR6kFgSYtFg-p6DdQw-ZOiwPVDnfQq4hRYIGQAEh3ayu3KuB9GDOoiouES2XtA0QG7vNxn-EOAtuBcFVggUmWQpAd4Gm0rJkR8_tfWt_ZTzOPiOdcZLrHIbsYzGqoWWCAxzvmLfhfUE0_rUbfhx6K9JjBqZ3Si07JCFr7Fj50EXBdYIF7-5zmK5UP-s7EOw3UcEUyeiipv4U_4yRSREaCw07EGGBhYIBgaPOk-NwJEFnGlX8rACGFaS9OKoCyaCFSAdLg_e560bWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgg8vYlKAAQ1K2_5PKb8Xt3ig8AnGuFoMZCZCPfuyhP7p4iWCCmK-XY1GrqDCk8o51xCwEuZAa_5G26GLHV4IvuQNomemdkb2NUeXBld2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xbHZhbGlkaXR5SW5mb6Nmc2lnbmVkwHgeMjAyNS0wOS0xMlQxMjozNTo0NS4xOTU1NjU1MjZaaXZhbGlkRnJvbcB4HjIwMjUtMDktMTJUMTI6MzU6NDUuMTk1NTY1NTI2Wmp2YWxpZFVudGlswHgeMjAyNS0xMC0xMlQxMjozNTo0NS4xOTU1NjU1MjZaWECI1G41KFMRRBsWjfzFK5mb1MwZKsxgxdL6Araaqz473RRxg5OJEz88gzW7t_AE7BG1nF96-tQzq32355mbyt6V`; const eudiwMdlMdoc = `ompuYW1lU3BhY2VzoXFvcmcuaXNvLjE4MDEzLjUuMZga2BhYV6RoZGlnZXN0SUQAZnJhbmRvbVADJfkHgLgW2cPWnS61D2OfcWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWhHZW9yZ2lvddgYWFakaGRpZ2VzdElEAWZyYW5kb21Q0lRS_4koX3PnUBioHCIJXHFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1lbGVsZW1lbnRWYWx1ZWhHZW9yZ2lvc9gYWFukaGRpZ2VzdElEAmZyYW5kb21Q44Nv-ENIXAcrBb7KJuY4FHFlbGVtZW50SWRlbnRpZmllcmpiaXJ0aF9kYXRlbGVsZW1lbnRWYWx1ZdkD7GoxOTQ4LTA1LTMw2BhZSfukaGRpZ2VzdElEA2ZyYW5kb21QfGK63tQ-laaXk653iJE_wXFlbGVtZW50SWRlbnRpZmllcmhwb3J0cmFpdGxlbGVtZW50VmFsdWVZSa3_2P_hAIxFeGlmAABJSSoACAAAAAUAGgEFAAEAAABMAAAAGwEFAAEAAABUAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABcAAAAAAAAAAAAYAAAAAEAAABgAAAAAQAAAAMAAJAHAAQAAAAAAgMAAZEHAAQAAAABAgMAAaADAAEAAAABAAAAAAD_4QE9aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu-7vyIgaWQ9IiIgPz4KCjxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgPgo8cmRmOkRlc2NyaXB0aW9uIHhtbG5zOkFzcG9zZUltYWdpbmc9Imh0dHBzOi8vcHJvZHVjdHMuYXNwb3NlLmNvbS9pbWFnaW5nLyI-CjxBc3Bvc2VJbWFnaW5nOkltYWdlUXVhbGl0eT43NTwvQXNwb3NlSW1hZ2luZzpJbWFnZVF1YWxpdHk-PC9yZGY6RGVzY3JpcHRpb24-Cg0KPC9yZGY6UkRGPgo8P3hwYWNrZXQgZW5kPSdyJz8-_9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy_9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy_8AAEQgCcgJyAwEiAAIRAQMRAf_EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC__EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29_j5-v_EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC__EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5-jp6vLz9PX29_j5-v_aAAwDAQACEQMRAD8A9_ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoorD8Q-MNC8LQCXWNQitsjKoTl2-i9TQBuUyWaOCMvLIsaDqzHAFfPviP9ou4Znh8N6ZGi9BPdgsSPUKCMfjmvKNf8deJfE8pfVNWuJV7RIdiD_gK4FAH1VrPxU8G6EGFzrUMsg6R24MpP4qMfrXn2sftH6bGHXR9GuZ26K9y6xj64GTXznRQB61f_ALQfi25VltYbG1B7rGWI_M1y-ofFTxpqKlZdeu40PVYX2A_l2rjaKANaXxLrc-fN1a8fP96YmqMt9dTZ8y4kf_ebNV6KAJY7iaH_AFUjp_unFXI9f1aEgxaldJj-7KRWdRQB01h8QfFumvm21-_Uf3TMSv5V1GnfHfxpZMBLPbXaek0XP5g15jRQB71pP7SMykrq-hK6_wB-1lwf--W_xrvtH-N_grVtivezWMrfwXURHP1XI_WvkeigD7ysNW0_VIRNYXkF1H6wyBv5Vdr4P0zWtT0adZ9Nvri1kByDE5FeoeHf2gvEmnbItXgttSgXG59vlykf7w-X_wAdoA-oKK4fwj8VfDHi0rDb3Ytr0j_j2uCFYn_ZPRq7gcigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKaxCqWYgAdSaAHVi-IfFGjeFrE3esX8dtHjhT8zv_uqOTXmnxC-OVpoTyaZ4cWK9vhw9wxzFH9MfeNfPGt6_qfiLUJL7VLyS5nc5JY8D2A6AUAep-Mfj7quqGS18PRHT7Y8eeTmVh_7L-FeQXd5c39y9zdzyTzuctJIxZj-JqCigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAFBIORXf8AhH4veJvCuyH7Sb6yXj7PcsWwPQHqK8_ooA-xvBfxU8O-M1WCCc2uoY-a0n4Y_wC6ejfhzXc18C2881tMk0EjxSocq6HBB-tex-BfjxqOleVYeJVN9Zj5Rcj_AFyD3_vfz96APpeiqemalZ6vp8N9YXMdxbSqGSRDkEVcoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiisnxD4i07wxo8-p6pMIoIhnH8TnsqjuTQBY1XVrHRNOlv8AUblLe1iGWkc4H0Hqfavmb4j_ABmv_E7zaboryWekH5SykrJOP9r0B9PzrnviD8SNT8dXx8zNvp0bZhtVbIHu3qa4igAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDq_BPj3WPBGprc2MzPasf31o7Hy5B9Ox96-qPA_wAQNH8dacZ7CTy7qPie1kPzxn19x7iviytPQte1Lw3qkWo6XctBcxngjoR6EdxQB92UV5_8OPidYeObIRSBbbVo1Hm2-7h_Vl9vbtXoFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFGaM0AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVWvb2306ynvLuVYreBDJI7dFUDJNAFXXtd0_wAN6RNqep3Cw28Q6k8seygdyfSvkHx947v_ABxrsl1O7JZRti1ts_LGvr7k9Sa0fif8RrnxzrAWIGHS7YlbeLdy3q7e5rgKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAt6ZqV3pGowX9hO8F1A2-ORDgg_4e1fW3wz-I1p450nZK6RatbgCeDOCw_vqO4NfH1aega7f-G9Yg1TTZjHcQtkejDuD7UAfdlFcr4D8a2fjfw7FqEGI7lQFuYM58t-_4eldVQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFfOPxx-JDX9zL4V0m5H2SFsXjxn_WOD9zPoD19xXpfxc8cp4O8LtFA_8AxM75WjtwP4R_Ex-ma-RXZndnYksxySe5oAbRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB13w98b3ngfxHDeRyM1lIQl1B2dP8R1H0r7G07UbXVtOgv7GZZradQ8bqcgivguvcfgH46Wyv5fDGoSnyLkh7RmPCyd1_EY_KgD6OooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqC7u4bGznu7lxHBAjSSMegUDJNT1458f_Fo0zwzH4ft5MXOoENLg8rEpz-pAH50AeH_EPxfN408WXGpOxFuv7q3jzkIg_qeTXJ0UUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUttcy2dzHcQOyTRMHR1OCCKiooA-z_ht4yj8Z-ELa-Z1-2RARXSjtIOpx79fxrsK-Tfgn4vHhvxlHY3EhWy1JhA3PCueFP54H419ZUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFGKMUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFADJZEhjaR2CooyWPQCvijx54nl8XeL7_VHcmFpCsC_3YwcL-mK-mPjL4jHh_4f3axuVub7_Rotp55-8fy_nXyFQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQA-ORopFkRirqQVI6gjvX2d8NvFP_CWeCrLUHZTcqPKn2_31HP8ASvi6vbv2dPEf2bXNQ0CZvku4hNDns6dR-Ib9KAPpGiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooprusaM7HCqMk-goA-Zf2htee98X2ujI37iwtwxGf-WjnJ_8AHQleOVv-NdV_tzxpq-o84muW25_uj5R-gFYFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABW14S1mXw_4r07VIvvQTKSM4yp4I_ImsWigD77t50ubaKePlJUDr9CMipK4z4U6ydc-G2j3LtuljiMD5OTlGKjP4AV2dABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXNfEHVW0XwHrF-hw6W5VTnoWIUfqa6WvMfj3em1-F9zCDj7VcxQ_XDb__AGSgD5PZi5JY5JOSaSiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA-kP2cdTabQdW0xiStvOsqZ7Bhj-amvb6-ZP2db4w-Mb-z3HbcWudvYlTn-pr6boAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK8N_aSumXQ9Fs_wCF7lpT_wABXH_s1e5V8-_tKzAS-HoQwJ2zuR6fcGf8-lAHgVFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB3vwcvGs_ihpDKf8AWM0R-jDFfYVfFfw1lMXxJ8Pf7V9Ev5sBX2pQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAV82_tIuT4k0dewtX_8AQhX0lXzn-0pAy6xoM-PleCVM-4Zf8aAPDKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDf8EuY_Hnh-Req6jbn_yItfcFfEnw_iE3xD8Ooen9owE_QSCvtugAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArwP9pWPNv4fk_utMv57f8K98rx39ouw8_wAE2F6o5t75Qf8AdZWH88UAfMVFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB2Pwsh874l6EMZ23SN-RzX2dXyf8B9PF78SIZmHy2sDyf8CxgV9YUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFcH8ZbH7f8LNYQLl4hHKvtiRc_pmu8qhrWnpquiX1hIMrcQPEfbIxmgD4Poq1qFo9hqN1ZyqVeCVomB6gg4qrQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAe7_s3afu1DW9RP8EccS_Ukk_0r6Iryz4BaQun_AA5S9KYk1C4klJPXap2D_wBBP516nQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAfH3xk0f-x_iZqSquIrrbcx_Rhz_48Grga-jP2ivDpuNN07xBGvz2xNvMQOqk5X8ju_OvnOgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqS3ia4uI4U-9IwUfUnFR16B8G_Dh8Q_EK0Drm3sgbqXI4IUgAfmRQB9UeF9LXRfC2l6aq4-z2yIw_wBrHzfrmteiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAxvFOiReI_DOoaTMoIuIWRSezY-U_nXw_c28tpcy286FJonKOh6qwOCPzr75r5d-PXg8aN4nTXLdQLXUyS-P4Zep_Pr-dAHkVFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFfTn7PnhttN8L3WtTR7ZdQcLGSOfLXOMexJP5V8-eFfD8_ijxNYaPb5DXMqq7AZ2Jn5m_AZNfbun2EGmafBZWybIIIxGi-gAxQBZooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuT-IvhVfF_gu905UDXQXzbZu4kXkAH36fjXWUUAfA11bzWdzLbTxtHNExR0YYKkdQRUNe3fH3wKbDUo_FFhEPs10dl2qrwknZvxH8q8RoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiup8AeEbjxn4pt9Ni-WEfvLiUjhEH9T0oA9i_Z-8HPa2U_ii8hKvODFalh_B0Zh-ORXulVrCyg03T7eytYxHBBGscaDsoGBVmgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDP1rR7PX9IudMv4_MtrhNrj-o96-MPGXhS88HeIrjSrtWKoxMMpHEqdmFfb9cN8T_AkPjfw1LFGqrqVuDJayH-9_dPselAHxzRVi8tJ7G7ltbqJop4mKujDBBFV6ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAJYIJLmeOCFGeWRgiIoyWJOABX118KfAEXgnw7umXOqXoV7lj_Dxwg9hk1wPwN-G7I0Xi3VovU2MTDn08w_0r36gAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPIvi98Ko_E1s2t6PGI9WiU-ZGvS4X_4oV8wPG0TtG6lWUkMD1B9K-_K8R-LnwhfWJJ_EPh6EG9I3XFqvHnf7S_7Xt3oA-bqKklikgkaKVGSRSQysMEGo6ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr1n4QfC0-K7ka1qyOmkQPhE6G4b0z_dFVvhh8J77xddQ6lqMbW-iK24u3Bnx2Uenv_OvqiztILCzhtLaNY4IVCIijgAUASQxRwQpFEipGihURRgKB0AFPoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKgvb22060ku7ydILeMbnkdsBRXz94--PT3Hnad4T3xRHKtfsNrN7oOo-pwaAPff7Usf7SGnfa4ftuwyeRvG_b64q3Xwjaa5qVlrKatDeTC-V9_nFzuJ9zX1b8M_iXZeN9MSCd1h1mJcTQnjfj-JfX6UAeg0UUUAeSfFT4RQeJ4n1jRVWDWEHzxjAS4GP0b3r5kvrG5068ltLyF4biJtrxuMFTX3tXGeOfhtovji1Y3UQh1BVIivI1w49A394e1AHxnRXS-LvAuueC74warassTEiK5TmOX6H-h5rmqACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooq5p2mXur3sdnp9rLc3MhwscS7iaAKYr2P4W_ByfxA8eseII5INMU7o4MbWn_wAF_nXb_Dj4JWmh-VqniOOO61EfMlscNHEffsx_SvZFAAwOlAEdvbw2lvHBbxpHFGoVEQYCgdAKloooAKp6lqthpECzahdxW0buEVpWCgsegqp4i8Rad4X0afVNTnEUEQ4H8TnsqjuTXyP4_wDHuoeOtaNzcM0dnFlba3zwg9fqaAPs1HV0DoQykZBHenV8feB_ivr3g2SKDzmvNMU_NayscKv-wf4a-mPBvj7Q_G1kJdMucXCjMtrJxJH-Hce4oA6miiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArC8U-K9M8IaNJqWpyhUHEcYPzSt_dFM8X-LtN8G6JJqWoygcERQg_NK3oB_XtXyB4s8X6r4x1d9Q1Odm5PlQg_JEvoo_wA5oA2PHnxN1nxveusshttNU4itI2O3Hqx_iNcPRRQAVe0rVbzRdSg1CwnaG5hcOjqe4qjRQB9d_DX4pWHja0FpcFLbWI1zJCTxJ_tL_hXolfA9rd3FjdR3NrNJDPG25JI2Ksp9QRX0d8MPjTb6tHDo3iadYdQ4SK6bhZvQMezfXrQB7VRSKQRkEEe1LQBS1PSrHWLJ7PUbWK5t36pIuRXgnjn4AzW_m6h4VmMsXLGxl--P9xh1-hr6IooA-B7q0uLG4e3uoXhmQlWR1wQagr7c8S-BvDviyFl1bTYJZSOLhV2yr9HHP4dK8L8X_ADVtPL3PhyT-0Lcc-Q5Cyj6dj_OgDxeird_p17pk5t7-zntZh1SaMofyNVKACiiigAooooAKKKKACiiigAooooAKKKKACiinpG8rqkalnY4CqMkmgBlKAScDk16J4T-DfirxK6SzWb6bZnkzXS7CR7KfmP1xivd_Cfwd8LeGESWSzTUr1efPulDgH1VTwP50AeHeBvg1rviwx3d3nTtMYZE0i5dx_sr_U19H-E_AuheDbMQ6VaASEfPcSfNI_1P-FdIAFAAGAKWgAooooAKxvEvifS_CmkyajqtwIol4Vf4pG9FHc1iePfiNpPgbTWeeRZ9RcfuLNDlmPq390e5_CvlHxT4t1bxfqzahqty8jciOPPyRL6KOgoA1PH3xA1Lxzq7T3B8qyjYi3tlOQi9ifU1x1FFABV7SdWv9F1CO-065kt7mI5V0P8AnNUaKAPqX4YfGK38UIml640Vtqw-VZAdqXH09G9q9ar4CR2jdXRirKcgg4INfRfwg-Lh1Hy_D3iK6zdAYtbqVv8AWf7LH19CaAPcqKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqnqmo22kaZcaheSCO3t0Lux9BVyvmf44fEZdavB4b0qcmxtnzcyIeJnHQe4H86AOG-IXje78b-JJb6QlLNP3dtBnIRB3-p5NcjRRQAUUUUAFFFFABSgkEEHBpKKAPWvAHxt1Pw35en60rahpoG1X3YliHsf4h7cfWvpDQfEWl-JtNjv8ASruO4hcfwn5l9iOxr4VrY8O-KNY8Lagt7pF9LbSZ-YKcq49GU8H8aAPueivIvBvx30PWhFaa5t0y8IA81j-5Y_738P416xBPDdQpNbzJNE4yskbBlYexHWgCWiiigDO1TQ9L1u3a31Kxguo2GCJUB_WvLPEv7Peh6iTLol7JpkvXy2TzYz-oI_WvZKKAPk7V_gR4y0ze9tFbahGoyDbS4Yj6NiuE1Hw_q-kPs1DTrq2b_ppGQPz6V92VDcWlteR-Xc28U8f92VAw_I0AfA1Ffat98NvBuoEm48O2GWzkxReXn_vnFc3e_AXwPdEmG1urQn_njcscf99ZoA-TqK-lp_2b_DzN_o-sanGPR_Lb_wBlFU5P2bbDH7vXrgf70SmgD51or6Hj_ZstQf3mvzEf7MIFWov2b9FBHna3fkdwqoP6UAfN1FfU9p-z34NtyPOk1G59pJwP_QQK6Sx-EvgXT8eT4et3I7zO8v8A6ESKAPjy2sbq9kEdrbyzuTgLGhYn8q7PRvg_411oBo9KNtGTxJdOIx-XX9K-tbDRNK0sY0_TbO0H_TCBU_kKv0AeA6B-zfskWXxBrKuveCzQ8_8AA2_-Jr1rQPAnhvwzGBpmlwJIOfNddzk-uTXSUUAFFFFABRRXEeLfip4Y8IK8dzeLdXqg_wCiWzB3B7BscL-NAHZzzxW8LzTSLHGgyzucACvEvH_x5gsJJdM8LolxOuVe9c5RT_sj-L615b45-KmveNJWgeVrTTN2VtIWwD_vH-L8a4PrQBav9RvNVvJLu-uZLi4lbc8khySaq0UUAFFFFABRRRQAU-OR4pFkjYq6nIYdQaZRQB9W_Bv4iHxbop03UXX-1rIYJzzPH2bHqOh-gPevUq-EtC1u98Paxb6pp8piuYGDKR39j6g-lfZvg_xXY-MfD8GqWLAbxiWInmN-6mgDoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKo6tqlpomlXOpX0qxW1uhd2Y_5-lAHC_F_wAfL4P8ONa2kmNVvkKw7TgxL0L_AOFfJRYsxZjknqTW9408UXXjDxRd6vcsdsjbYY-0cY-6B_P6k1z9ABRRRQAUUUUAFFFFABRRRQAUUUUAFdf4Q-JHiLwZLjTrrzLUtue1n-ZG_qPwrkKKAPqjwt8ePDetLHDqiyaVdtwfMO-In2Yc_mK9QtLy2vYFntZ45onGVdGyCK-B62NE8U674cm83SNUubQ91R_lP1U8H8RQB9z0V85-G_2ir63VIfEOnrdKMAz2-Ef6kdPyxXp2jfGPwVrKD_ibx2cpODHdgxY_4EeP1oA76ioLW7tr2ETWtxFPE3R4nDKfxFT0AFFFFABRRRQAUUUUAFFFFABRRTWYIpZiAAMkntQA6iuW1n4i-EdCyL7XrJZB1jik8xx_wFcmvN9f_aL02FHj0LTZbmTB2y3Hypn_AHetAHuDMFGWOB6muJ8T_Fbwr4WMkVzffabpM_6PbYdgfQ9hXzP4j-J3izxPujvdWmjtieILfES_jtwT-Oa5Aksck80AepeNPjhr_iRWtdOX-yrHPSJyZHHozccewFeXSSNK5d2LMepY5JptFABRRRQAUUUUAFFFFABRRRQAUUUUAFei_CDxw3hHxXHDdSkaZfEQzgnhGJ-V_wAD19ia86ozQB9-I6yIrowZWGQR0Ip9eP8AwK8dPr2hyaFfzbr6wAMRY8vD2-pU8fiK9goAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr55_aB8bCeaDwpZSHbEwmu2B4JxhV_Uk_hXs3jTxLB4S8K32sSlS8MZ8lGP35Dwq_mRXxTqF_canqE97dSGSedy7ue5NAFaiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAL-n61qWkzifTr6e1kH8UTkGu1sfjd44stobUY7kD_nvEGzXndFAHt9j-0hq8e37doVpOAeTFM0f8w1dLa_tI6A6_wCl6JqUTekRjkH6la-a6KAPqaL9ofwbLjdbatGf9uCP-j1di-O3giXG66uov9-H_A18mUUAfW8vxy8DRDK388n-5Cf61Sl_aC8GR52x6nJ_uQJ_V6-VaKAPpyb9o_wsoPk6VrEjf7Ucaj_0M1z2oftKTNldO8Ooo_vz3Of0C_1rwSigD1LUfj34zvX_ANHe0s0xgLFFk_mSa4zWvGviPxASNT1e5nQ_wF8L-QrAooAU5JyTmkoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigDY8MeIbvwv4gtNWsifNgcErnh17qfY19saNq1rrmj2mp2T77e5jEiHvg9j718H173-z740cXEvhS8myjhpbMMehHLKPwyfwNAH0JRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUVzvjbxLD4T8KXurSkb412wqf45D0FAHgHx68X_wBseKRodvJutdN4cjo0pGT-Wcfga8hqa7upr27murhy80zmR2Pdick1DQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFSw28txKsUMbSSMcBVGSaAIqK7zQvhB4x14K8emG1hY_6y6JQY9fU13mm_s23TOG1LxBFGuOVgg3H8yRj8qAPB6v6Lqtxoms2mp2rFZraVZFIOOnb8elfSVl-zv4UgUG5vdTuX75kRV_IL_WtmD4IeBYSC2mSS_wDXSdv6YoA6_wAOa5b-JPD9lq1qwMdxGGI9G7j8Dmtas3RdC03w7p4sNKtVtrUMWEakkAn6k-laVABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXzR-0F4sGpa9a-HraTMFgC8-Dw0rdj_ugf-PGvoXXtWi0LQL_AFWfHl2kDzEE_e2jIH4nivh_VtSm1jVrvUbg5luZWkY_WgClRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRWv4f8ADOreKNRWx0m0eeY9SB8qj1J7UAZFdP4X-H_iTxdMBpWnSNB_FcSYSNR9T1-gya938FfAbSNHSC81-Q6hfDDmEcQofTHVv5e1euW9vDawrDBEkUSjCogwAPpQB4v4Y_Z50yzKT-Ibxr2QYJghJWP6Z6mvWNH8OaNoEPlaTplrZrjBMMQUn6nqa1aKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKQ8DNAHi_7Qvib7F4cttAhlAkvnEkwDc-WpyPwLAflXzTXZfFHxE_iT4gapchiYIZjbwjPGxPlyPrjP41xtABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFOVSzBVBJPAAr3v4U_BhZY49d8UQ5VsNbWJHUf3n_oKAOS-HPwf1LxbJBqOpxyWejE7t7cPOPRAex9fyr6b0Pw9pPhuwFlpFjDaQ9xGuCx9WPUn3NaMcaQxrHGgVFGFVRgAU-gAoooJx1oAKK4Txh8WPDXhFZIZbkXl-v_AC627AkH0Y_w14r4j-P3ibVlaHTIYNKhP8UZMkn_AH0ePyFAH03ealZadH5l9eW9sgGS00qoPzJrjNU-MngbS2KPrIuJP7ttE8n6gY_Wvku_1jUdUnee-vbi4kf7zSSE5qjQB9Nah-0Z4bg4sdN1G5b1ZVRf_Qs_pWQ_7SkY_wBX4ec_704H9K-fKKAPfB-0pLnDeG0C_wDXyf8A4mrUX7SdsT-98Pygd9kwNfPNFAH1HY_tDeEbhV-1QajaN33whgP--Sa63SPih4M1sqtpr1srnjZcZhOfT5wK-L6UEg5FAH31DPFcRiSGVJEPRkYEH8RUlfDejeLdf0CbzdM1W5gbjID5U49QeK9W8O_tF6lb-XB4g0yC6TIBnt2MbgepXkH8MUAfR1Fc74a8ceH_ABZDv0rUI5HAy0TfK6_UV0VABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFc7461xfDngzU9SLYeOEiPHUueBXRV4f-0br7W2j6XocTc3btPKAf4VwF_Mk_lQB85O7SMWYksTkk96bRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFe1_Bf4XjWJU8SazF_oMT_AOjQsP8AXMP4j_s549-aANz4O_CWOGKDxL4itd07APaWsq8KD0dh6-gNe7gADAopaACiivKfiZ8YbTwkG0zSBHeaqync24FIPr6nrxQB2XizxxoXgyxafVbxFlKkxWynMkh9Ao7e54r5t8cfGPXvFUjW1nLJpum5OIoXw7j_AGmHP4dK4bWNZ1DXtSm1DUrl57mZizMx_QDsKz6AFJJJJOSaSiigAooooAKKKKACiiigAooooAKKKKAJra6uLKdZ7WeSGZDlZI2KsPoRXtHgT493mnrFYeKFe7twQovFGZFHq397-deI0UAfeGk6zp2u2CX2mXkN1bP0kibI-h9D7Gr9fE_g7x3rPgnUftOmzbom4ltpDlJB_Q-9fVXgX4g6T460zz7Q-TeRj99au2WQ-3qPegDrqKKKACiiigAooooAKKKKACiiigAooooAKKKKACvkL40a6db-JF6FYmGyVbaP045P6k19ZapeLp2k3l6_3beB5j9FUn-lfCupXcl_qVzdysWeaVnLHvk0AVaKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiipYYZLiZIYULyyMFVV6kntQB2Hw08DT-N_E8VsUYafB-8u5RwAoP3QfU9Pzr7BsbK306yhs7WJYreFAkaL0AFcz8OPByeCvCcGnsFN5J-8unXnLnt9B0rr6ACiivK_i_wDE1fCNh_ZOnMG1e6jPzA8QIeNx9_SgDI-Lvxc_sYTeH_D9wv285S5uUwfJ9VX_AGu3t9a-b5ZZJ5Wlldnkc5ZmOSTSSSPNI0kjFnY5ZickmmUAFFFFABRRRQAUUUUAFFaem-H9X1iUR6dpt1csenlxEj8-ldbYfBfx3f8AP9j_AGdf708yL-mSf0oA8_or1aP9n3xq_wB5tMT_AHrhv6Kaim-AXjiL7sNjL_uXP-IFAHl1FdjrHwu8ZaGhkvNEmMQGS8DLKP8Ax0muSmhlt5DHNE8bjqrqQfyNAEdFFFABRRRQAVo6Lreo-H9Si1DS7p7a5iOQ6Hr7EdCPY1nUUAfYvw4-I1j470rJKQ6pCo-0W2f_AB5fVf5V3VfCega7feG9Zt9U06Ux3EDZGOjDuD7EcV9i-BvGVj438PR6lZ_JID5c8BOTG-On09KAOnooooAKKKKACiiigAooooAKKKKACiiigDg_jJqh0v4X6uyNtknVIF_4E4Df-O5r48r6S_aP1MxeHNL01WwZ7kysPUKp_qRXzbQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFeyfALwemr69Pr93FvttPIWHPQynn9Bg_jXkFvA9zcRwRDMkjhFHqScCvtTwD4Zj8JeDbDSlA81FLzMP4pG5J_p-FAHTUUUUAc3448V2_g3wtdatOVaRAEgjJ-_IeAP6n2Br411vWb3xBrFzqmoTNNczvuZj29APQCu8-M3jn_hK_E7WNq5Onac7Rx88SP0L_4e1eZUAFFFFABRRRQAUUUUAFev_APS9E1nxBqFrqmnwXU0dv50Pm8hQGAPHQ_eFeQV6F8FdUGmfFDTQxxHdLJbt-Kkj9QKAPra3tba0jEdtbxQoOixoFH5Cp6KKACiiigArmfFHgHw74ttGi1LT0MuDsni-SRD65HX8ciumooA-QPiP8ML_wACXYmjZ7rSpTiO528qf7reh_nXn9feWqaba6xplxp97EsttOhR0boRXxb4z8NTeEvFd_o8x3CF8xv_AH0Iyp_IigDn6KKKACiiigArs_hx46uvBHiNLlSWsJiEuoezL6j0IrjKKAPvm1uoL20huraVZYJkEkbqchlIyCPwqavC_gF46-1WZ8J3rkzQBpLRifvJ1KfhyfpXulABRRRQAUUUUAFFFFABRRRQAUUUUAfMn7RV6ZvGVjabsrb2uce7HNeN13vxkvzqHxP1Zt2ViYQgemwbf5iuCoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPRfgr4eTX_iLatMha3sEa6kHqRgKP--mB_CvrqvGf2eNDFn4VvNXePEl5NsVj_dX09sn9K9moAK4r4p-Lf8AhEPBV1dQyBb24BgtvUMf4vw612tfLv7QPiIan4zi0iF90OnQgOB_z1bJP_juygDyMnJyTzSUUUAFFFFABRRRQAUUUUAFaXh-_bS_EWnX6Psa3uY5Nw7YYVm0UAfZ9_8AE_wdpkKPda9aF2QPshfzGGfZc4Nclc_tDeE4s_Z7e-n_AO2e3-dfLtFAH1Fp_wC0J4Xu7lIrq2u7NWODI43Bfc4r1LT9RtNVsIb6xuI57aZd0ckbZDCvguvoP9nDV55I9W0h5WaCILPGh5C5ODigD3yiiigAr55_aP0qOO80jVlQK8qtA5H8WORX0NXiH7SMqjw7o0WxS5u2YN3ACEY_HP6UAfN9FFFABRRRQAUUUUAavhrW7jw54isdXtWxJayh8eq9GH4gkfjX25pGpQ6xpFpqNu2YbiISKfrXwdX05-z34jGoeFbvRpXJm0-UMg_6Zv0_VW_OgD2OiiigAooooAKKKKACiiigAoopG-6fpQB8QeN7j7X488QTA5VtRuCv08xsfpWBVrU7g3WqXdwess7v-bE1VoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKK0NCsv7R1_T7LGftFzHFj13MBQB9meA9G_sHwPpGnn78dspk4xliMn9TXR0iqFAAGAOgpaAIL25Szsp7qQgJDG0jE9gBmvhnXtSk1jX7_UZXLvcTvJk-hPH6V9bfFzVW0j4Z6vMhxJKiwp_wJgD-ma-N6ACiiigAooooAKKKKACiiigAooooAKKKs2Vjdaldx2llbS3FxIdqRRKWZj7AUAVq-l_2ffC91puh3et3SbBf4WFSOSgPX86zPh38CBC0WqeLEBdSGSwzkf8DIP6V7vFHHDEkUSKkaDaqqMBR2AHYUASUUUUAFfOn7R-qpJquk6Upy0MbTOPQngfpX0HfXtvp1lNeXcqxW8Kl3djwoFfFPjjxE_ivxhqOrsWMc0pEIPaNeF_QCgDnqKKKACiiigAooooAK9K-B2snSviPbQNJtivo2t2B6E9V_UfrXmtX9FvpNM1uyvojiSCZXH50AfeFFQ2lwl3ZwXMf3JY1dfoRmpqACiiigAooooAKKKKACkf7h-lLSEZFAHwLMpWaRWGCGINR1r-KrM6f4t1izZcGG9mTH0cisigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArs_hRai7-Juhqf-Wdwsv_fPP9K4yvRPghGZPinpoH8KSt-SmgD67ooooA8e_aLvjB4GsbRT81xfKT_uqj_1Ir5hr6I_aVkxpugRf3ppW_JV_wAa-d6ACiiigAooooAKKKKACiiigAooooA3fCvhTU_GGrpp2mRbnPLu33Y19TX1j4H-Hej-CLBI7WNZ74j97eOvzue-P7o9qy_g14Ti8N-B7e4eJRfah-_lfHzbT91c-mBn6k16LQAUUUUAFMklSKNpJGCIoyzE4AFZXiHxPpHhbTmvtXvYreIAlVY_M59FXqa-ZfiJ8YNU8X-bYWLSWWkNwYlbDTD_AGyO3tQBtfGL4rDX3bw_okn_ABLY2_0icH_XsOw_2R-v4V4xRRQAUUUUAFFFFABRRRQAUoYggjgikooA-2_h_efb_h74fuM5Y2EKsfVlQKf1BrpK4f4Pyeb8KtCb_pk6_k7D-ldxQAUUUUAFFFFABRRRQAUUUUAfJfxz0U6T8RrmdR-6vkE68d8fN-teaV9OftCeG21Dwpb63BGDJp0gEpA58tjj_wBCIr5joAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK9Q-ATIvxOhDdWtZQv1x_hmvL69D-CUwh-KemEn7yyJ-akUAfXlFFFAHgX7SykxeHWz8oacH8Qn-FfPtfRn7ScROi6HNjhbiRc_Vf_AK1fOdABRRRQAUUUUAFFFFABRRRQAVa06EXGp2kLDKyTIh_FgKq1d0l_L1mxf-7cRn_x4UAfdVnGIbG3iUYVI1UD0wAKsVFbtutom_vID-lS0AFcf8RPHVt4E8OtfOizXcp2W0BbG9vU-wHP6V2FfLP7QGrSXnj5bDeTDZW6hVzwGYbj_SgDgPEvinVfFmrSajqtwZZGPyoOFQegHpWLRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH2B8FVK_CXRA3XEx_AzPiu_rivhLEYfhboKkEZgLfmxP8AWu1oAKKKKACiiigAooooAKKKKAKWrabBrGkXem3IzBdRNE_sCMZr4n8V-Hbvwr4ku9IvFw8LfKR0ZT0Ir7lryz4xfDf_AIS_TF1TTo_-JvZoQFUf69Ou0-47fU0AfKdFPdGjdkcFWUkEHqDTKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK6v4a3gsfiNoMrHCtexoT6BmA_rXKVYsrp7G_t7uL78EiyL9Qc0Afe9FQ206XNtFPGcpKgdT7EZqagDyn9oKx-1fDhJgMm1vY5M-gKsv_swr5Wr7Y-IWj_274C1iw2bmeAuoH95CGH8q-KGUqSD1HUUAJRRRQAUUUUAFFFFABRRRQAVLBJ5U8cn9xg35GoqKAPvLSJludHsp0bckkCMCO-VFXa858E_ELwtF4L0mO98Q6dBcx26pJHLcKrKRxyCa3_-Fj-C_wDoaNK_8Ck_xoA6evkD40f8lT1b_gH_AKCK-mP-Fj-C_wDoaNK_8Ck_xr5d-LGo2Wq_EXUrzT7qK5tpNu2WJgynCgcEUAcTRRRQAUUUUAFFFFABRRRQAUUUUAFORC7qo6scU2t3wbpB13xhpemhGYTXChgP7o5P6CgD7I8IWf2DwZoloRgw2ECH6hBn9a2qaihECqMKowB6CnUAFFFFABRRRQAUUUUAFFFFABRRRQB5R8TPg7b-LXk1PRjDaasQS4b5Un-uOh96-a9d8O6t4bvms9XsZbWYHjeOG9wehH0r7rrG8QeF9H8U2DWer2aXEeDtY8MnurdRQB8M0V7R4x-AGp6b5t14dmN_bDnyHx5oHoMcN-VePXdncWNy9vdQyQzIcMki4IoAgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPsv4U6x_bXw30idmDSRQi3f6p8v8ASu0r57_Zz8RkXOpeHJW4ZPtUOT0IIVh_48D-FfQlADZEEiMjDKsMGviXx3ocnh7xvqumydEnZ0OMZVvmX9DX25Xz1-0X4aZbvT_EkKfK6fZp8eoJKk_nj8KAPBaKKKACiiigAooooAKKKKACiiigAor2r4c_A46_p0Wr-Ippra1l-aG2iwHdfViegr162-Efga1gES6BC4xgs8jsT-OaAPjeivq3X_gN4S1WFzp6T6ZcY-VoX3Jn3Vs_oRXzj4s8Jal4N1uTTNSQbhzHIv3ZF7MKAMCiiigAooooAKKKKACiiigAooooAK9h_Z70Fr7xjdaswPk6fBgHHV3OB-gavHq-ufgt4a_4R7wDBJKm251BvtMmeoBACj8ufxoA9GooooAKKKKACiiigAooooAKKKKACiiigAooooAMVheI_CGheK7QQaxp8Nxj7khXDp9GHIrdooA-efE37Otyhebw5qKSp1W2ujtb6Bun54ryLXPB_iHw25XV9JubUf32XKH6MMg_nX3HUFza295C0NzBHNEwwySKGB_A0AfA9FfW3iD4H-D9dleaOCfTpm5LWjgAn3VgR-WK81139nTWLVXl0bVIL1RyI5k8psfXJB_SgDxOiuh1vwR4j8POw1PSbiFF_wCWm3KH6GufIIOD1oASiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA6XwFrzeGvG-l6nuZY45gsuD1RvlI_WvteKRZYkkQ5VgGB9Qa-BK-tfgt4vbxN4KS3uXDX2nMIJOeWT-Bvy4_4DQB6TWN4q0GDxN4avtIuANtzEVVj_AAt_C34HFbNFAHwRfWVxpt_PZXUZjuIJDHIh7MDg1Xr3n4_-BVgdPFdhFhHYR3iqOAx6P-PT8q8GoAKKKKACiiigAooooAK6_wCGGhReIviHpVhcR77YyGSZfVUUt_MCuQrv_gvqI074paUWICT-ZAc_7SHH64oA-vkVY0VFACqMADsKdRRQAV5L-0Do8V54AGpeWDNY3CMH9FY7SPzI_KvWa8w-PWpx2Xw0uLRmUPfTxRKp6kKwc4_75FAHyhRRRQAUUUUAFFFFABRRRQAUUUoBYgAZJ7UAdd8NPCz-LPG9hYlM2sb-dcnHAjXk_mQB-NfZscaxRLGihUQBVA6ACvPPg94HXwl4TiuLqIDU75RLOSOUU9E_Dv716NQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUANZQykEAg9Qa5vU_h_4S1gs154f09nbkyJAqOf8AgS4NdNRQB5Jqn7PvhS7VjYy3li55G196j8G_xriNW_Zy1iFC-l6pbXIHSOUFGP8ASvpKigD431D4ReOdOJ8zQLiVR0MDJLn8FJNc3d-HtasWYXej38BU4Pm2zr_MV92Uxo0cYZVI9xQB8CEFSQQQR2NJmvum88M6HqBY3elWkxbqWiGTXNX_AMH_AARqDs50WOB26mBin6dKAPjyivp-8_Z38KTsWtr3UrcntvRh_wCg_wBayZ_2a7Bifs_iS4j_AOulqH_kwoA-dqK90m_Zrv0_1PiS3k_3rUr_AOzGs6f9nLxOv-o1TS5PTczrn_x00AeOUV6ZN8B_HEXS2tJP-udxn-Yqi_wY8doTjRi3-7Kv9TQBwNFdy3we8eL_AMy_MfpLH_8AFVUl-F_jeFiH8N32R_dUMPzBoA5GiunPw78YKMnw9fj_ALZ1C3gTxUn3tBvh_wBs6AOdordPgzxKpwdEvQf-uRpR4J8THpod9_36NAGDRXRDwH4qPTQb7_v0aePh94tPTw_ff9-6AOaorqU-G3jOQ_J4cvz_ANs60k-Dfjx-ugyL_vSp_jQBwlFeix_A_wAdSddNjT_emWrUfwD8bv1hsU_3rj_61AHmFFexwfs5eJ2AM-p6XH67Wdv_AGUVow_s1X7Y87xJbp67LUtj_wAeFAHhdFfRMH7NViv_AB8eJJ5P9y0Cf-zGtez_AGdvCkJBub3U7gjt5ioD_wCO_wBaAPmCu1-F3jD_AIQ3xjBdyuRYz4hugOfkJ-9j1HWvoi0-C3ga0YE6UZ8f89pCf5YrftfAvhayx9n0GwTHpCP60AdArh1DKQVPII706mqoRQqgBQMACnUAVNT0611fTbjT72JZba4QpIh7j_Gvjr4i-CLnwP4mksnDNZzfvLWY9HT0-oPB_wDr19oVzPjjwbYeNvD8mnXgCyj5oJwMmN_UUAfE1Fa3iPw7qHhfWrjS9SiKTRMRns47MPY1k0AFFFFABRRRQAVLbzyWtzFcQsUlicOjDsQcioqKAPqj4ffGfRdesILTW7tLDVVGxjNxHLj-IN0H44r1CO7t5k3RTxOp7q4Ir4Hq0mpXyLtS9uFUdllYf1oA-1te8Z-HvDVq0-qapbxADhFbe5-irk18qfEjx7ceO9f-1YeKxgyttCx-6vcn3OBXHSzSztulleRvV2JP61HQAUUUUAFFFFABRRRQAUUUUAFex_BH4cf29qH_AAkOq2-dNtXxArjiaQYPTuB_OuW-Gvw7vPHOtKGDQ6XAd1xPjqP7q-pNfXOmaba6RptvYWUQjt4ECIo9KALlFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBx3j_4faZ460hoZ0WK_jGbe6A-ZD6H1U-lfJfiXwvqnhPV5NN1W2aKVeUYj5ZF9VPcV9y1g-K_CGk-MdKNhqkAYDJjlA-eM-oNAHw9ijFd749-FmteCrl5ShvNMJPl3cSEAD0YfwmuCoAKKKKACiiigAooooAKKKKACiiigAooooAKKKcqs7BVUsx6ADJNADa7v4d_DTUvHV8H2vb6XGwE10Rx_ur6mur-HXwPvdZeLVPEga0sB8yWxH7yb6_3R-tfR2nadZ6TZR2djbx29vGMJHGuAKAIND0LTvDmlQ6bpdslvbRDAVRyT3JPcn1rSoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAiuLeG6geC4iSWGQbXjdQysPQg9a8b8dfAXTtT3XvhkrY3RJLWzMfKf6f3T-le00UAfDXiDwnrnhe6Nvq-mz2xzgSMh2N_ut0P4Vi198XlnbX9q9tdwRzwOMNHIoZWHuDXlviX4CeGtX3S6XJJpVwf7g3x_8AfPH86APlmivU9Z-AnjHTmdrNLXUYV5UwS7XI_wB1sc_ia4PUfDGuaTL5d_pN3A3-3Ecfn0oAyKKVlKNhlIPoRikoAKKKKACipI4ZJWxGjOfRRmt_SfAfinXCn9n6JdyK5wHZNi_m2BQBzlFey6F-zv4hu5A2sX1pYQ91jJlk_LgfrXrPhj4PeE_DbJN9j-3XS8-bdYbB9QvQUAfOfhP4ZeJvF0iNZ2EkFoet1cKUjx6gn734V9D-Cfg_4e8JJFczwrqOqDk3E4yqH_YXoPr1r0REWNQqKFUdABgCnUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUCigUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUcsMUyFZY0dfRlBFSUh6GgDg_HegaN_Zxm_siw83afn-zJn88V8_69Y2kany7WBf8AdjAr6R8d_wDIIP0NfOviH7p_GgDibaNDesCikZ6Yr0Tw7p1jNNGJbK3fLDO6JT_SvPbX_j_b616Z4Y_10X-8KAPftC0PSLbTY2g0uxiY9Sluin9BW9GAqKAAAB0FUNJ_5BcVaCfdH0oAWiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA__9nYGFhHpGhkaWdlc3RJRARmcmFuZG9tUEpr5CM5C-dyJPHBYOVYtKVxZWxlbWVudElkZW50aWZpZXJjc2V4bGVsZW1lbnRWYWx1ZQHYGFhLpGhkaWdlc3RJRAVmcmFuZG9tUHEWDLj6PCpL7auQyWFzXgtxZWxlbWVudElkZW50aWZpZXJmaGVpZ2h0bGVsZW1lbnRWYWx1ZRiv2BhYS6RoZGlnZXN0SUQGZnJhbmRvbVCSgJhe8WgtqB59zFzZdZ9ucWVsZW1lbnRJZGVudGlmaWVyZndlaWdodGxlbGVtZW50VmFsdWUYUNgYWFOkaGRpZ2VzdElEB2ZyYW5kb21QpatLroUY3TAlmIwALEc26HFlbGVtZW50SWRlbnRpZmllcmpleWVfY29sb3VybGVsZW1lbnRWYWx1ZWVicm93btgYWFOkaGRpZ2VzdElECGZyYW5kb21QspEMV6cUooQhnmkdFpYsdXFlbGVtZW50SWRlbnRpZmllcmtoYWlyX2NvbG91cmxlbGVtZW50VmFsdWVkZ3JledgYWFakaGRpZ2VzdElECWZyYW5kb21QpmoBZ1HkRE_v0ArJ1ZrX9HFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ5bGVsZW1lbnRWYWx1ZWJHUtgYWFGkaGRpZ2VzdElECmZyYW5kb21QOI1702Mw1aLBGJO88TUaWnFlbGVtZW50SWRlbnRpZmllcmxhZ2VfaW5feWVhcnNsZWxlbWVudFZhbHVlGE_YGFhUpGhkaWdlc3RJRAtmcmFuZG9tUEnq_skpiXieHs44UywfBj5xZWxlbWVudElkZW50aWZpZXJuYWdlX2JpcnRoX3llYXJsZWxlbWVudFZhbHVlGQec2BhYT6RoZGlnZXN0SUQMZnJhbmRvbVB8krnI7Aqcoiemh0GBwjizcWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE4bGVsZW1lbnRWYWx1ZfXYGFhPpGhkaWdlc3RJRA1mcmFuZG9tUNbx5rzy0s819gAvpGzmElRxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMjFsZWxlbWVudFZhbHVl9dgYWFGkaGRpZ2VzdElEDmZyYW5kb21QIIAbKWUNw1l0FI3zl75Y_HFlbGVtZW50SWRlbnRpZmllcmtuYXRpb25hbGl0eWxlbGVtZW50VmFsdWViR1LYGFhzpGhkaWdlc3RJRA9mcmFuZG9tUGTYEbYWk4ypBmyc26Xs0qpxZWxlbWVudElkZW50aWZpZXJ4HmZhbWlseV9uYW1lX25hdGlvbmFsX2NoYXJhY3RlcmxlbGVtZW50VmFsdWVwzpPOtc-Jz4HOs86vzr_PhdgYWHKkaGRpZ2VzdElEEGZyYW5kb21Q1MjG1vBu6uxCPgU9j4n70nFlbGVtZW50SWRlbnRpZmllcngdZ2l2ZW5fbmFtZV9uYXRpb25hbF9jaGFyYWN0ZXJsZWxlbWVudFZhbHVlcM6TzrXPjs-BzrPOuc6_z4LYGFk1jaRoZGlnZXN0SUQRZnJhbmRvbVAQleHq9f8TktVEh5NwOMSrcWVsZW1lbnRJZGVudGlmaWVydHNpZ25hdHVyZV91c3VhbF9tYXJrbGVsZW1lbnRWYWx1ZVk1M__Y_-EBPWh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSIiID8-Cgo8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiID4KPHJkZjpEZXNjcmlwdGlvbiB4bWxuczpBc3Bvc2VJbWFnaW5nPSJodHRwczovL3Byb2R1Y3RzLmFzcG9zZS5jb20vaW1hZ2luZy8iPgo8QXNwb3NlSW1hZ2luZzpJbWFnZVF1YWxpdHk-NzU8L0FzcG9zZUltYWdpbmc6SW1hZ2VRdWFsaXR5PjwvcmRmOkRlc2NyaXB0aW9uPgoNCjwvcmRmOlJERj4KPD94cGFja2V0IGVuZD0ncic_Pv_bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAkLicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv_bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv_AABEIAMIB2gMBIgACEQEDEQH_xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv_xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5-jp6vHy8_T19vf4-fr_xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv_xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4-Tl5ufo6ery8_T19vf4-fr_2gAMAwEAAhEDEQA_APfqq3JIkH0oNy4bGBTlUTjc3BHHFADISfNFW6rmJYVMi5yOmaT7S3otADJCd7c96mtyTuzTRAGAYk5PJxSN_o-NvOeuaALD_cb6VR3v61KJy5CEDB4OKl-zr6mgBYDmIUlwSEGPWojIYT5a4wPWlDGc7G4HXigCHe_rV-ovs6-pqP7S3otABcEiQY9KbCT5o5pygT5ZuCOOKcYkhBkGcj1oAnqnKT5rc0_7S3otOEIkAck5PPFACW5JZs-lTv8A6s_Sq7f6Pgrznrmk89jgEDB4oAi3v61bhJMQz60n2dfU1EZTC3lrggetAE05Ij49aqb39amDGc7GwB14qT7OvqaAJR0FV7kkEY9Kb9oYcYXilUefktxjgYoAZCT5o571cquYRGDICcjnmk-0t6LQAyUnzTz3qS2JJbPpQIRIBIScnnikYfZ8Fec8HNAFk9D9Kob39al89zxgc8VL9nX1NACwEmPn1onJERxURcwHy1wR70gYzny2wB14oAi3v61fHQfSovs6-pqLz3HGBxxQA65JBXHpUcZPmrz3p6j7RktxjpinGIRgsCcjkZoAnqnMT5p570_7S3otOEKSgSHOTzxQA23JMhz6VZPQ1WYeRgryTwc0n2hjxheaAIt7-tW4CTHz60n2dfU1GWMB2LgjrzQBLMcRH61U3v61KJTM3ltjB9Kl-zr6mgCRP9WPpUNySCuKZ57LlQBgcClX_SOW4x0xQAyInzV5q5UBhEYLgnI55pv2lvRaAGzE-aeadbkmQ59KcIkmAkOcn0oYCD515J45oAnqhvf1qb7S3otSfZ19TQAW5JQ59aWc4iNRFjAdq8g880glMxEbYwfSgCLe_rV5PuL9Kj-zr6mojOUJQAYHAzQBJcEjbioYyfMHPenr_pGd3GOmKUwBAWBORyM0AWKKrfaW9Fp_nN6CgCIwyEnj9afGRECH4JqxVW5_1g-lAEjsroUU5Y9qh8iT-5-tFv8A64VcoAhEqKApOCBg0yb99jZzjrUcv-tf61La_wAdADBC4IJHAPPNT-en979KfJ9xvpVCgCZ0MjllGVPeiMGE7pOBjFTQf6laS5_1Y-tAC-en979Kr-RJ_c_Wo60PSgCCIiEEPwSaV5EkUopyT2plz98fSmRf61frQAeRJ_c_Wp1kRQFJwQOlS1Sl_wBa_wBaAJZf32NnOOtMEMgIJHAOTzT7bq9Tv_qz9KAGeen979KhkjLuWUZB71DVyD_VD60AQxKYZNzjAxjNTeen979KS5_1X41UoAk8mQ5OP1qWIiEEPwSanHQfSqt199aAJGkSVCqnJPaofIk_ufrSQ_61frV2gCJZFVArHBA5FMkImACckHPpUU3-tb60-1-8fpQA0QyBgSOAcnmrHnp_e_SpD901n0ATSqZpNyDIxjNEamN9zjA9ay9Z8U6J4YsvP1jUoLQHkKxy7fRRyfwrze8-MGr-IZXsvAfhm6v2Bw13cp8g_AcD8SPpQB7H5qHof0rLm1Cyt5Nkt9bRyE8JJMoJ_M15R_wgPxC8U_vPFPi02MLNk2lnzj8sD-dP1f4MeCfD3hu91PVLnUpfs8Jd5nnAJOOMAL1JwMc9aAPYoXWNcsRhuQQc5p7SIyFVOSRwK8w-CY1D_hW9s98ztE88ptRIekXA_LdmvSIv9an1oAXyJP7n61MsiRIFY4I7VNVKb_Wt9aAJpSJgAnJBqLyZBg4_WnWv32q0eh-lAEfnp_e_SoZQZjuTkY61DVu2_wBV-NAEUcZRwzDAHepvPT-9-lE_-qP1qnQBKYXJJA4J45p8f7nO_jPSp0-4v0qC56pQA9pEYFQckjpUHkSf3P1pIv8AWp9au0AQrKqIFY4YdqSUiYAJyQaim_1pp9t98_SgBnkSf3P1qx56f3v0qT1rPoAmlBmO6PkYxQiGNwzDCjvUtt_qz9aWf_UtQAeen979KgMLkkgcE8c1FV-P7i_SgCCP9znfxnp3p5kRwVB5PFNuv4agi_1i_WgB3kSf3P1qXym9KnooApmRsn5m_OpIAGTLDcc9TTjb5Od36Uzd9n-XG7POaAHzALEWUAEdCKg81v7zfnUvmeb-7xjPfOaPsv8AtfpQBIiKUBKgkjkkVFP-6xt-XPXFHn-X8m3OOM5o_wCPj_Zx-NADEZi4BYkE4IJq15af3V_KoPI2fNuzt5xil-1f7P60AMlLJIQpIHoKWElpMMSRjoaXyvP_AHmcZ7Yo2_Z_mzu7YoAn8tP7q_lVTzW_vN-dS_av9n9aPsv-1-lACwAMpLfMc96c6hYyQACOhAqPP2f5fvZ5o87zv3eMZ75oAj81v7zfnVlEBQEgEkckio_sv-1-lJ5_l_u9uccZzQAT_utm35cnnFRB2JALHBODzUuftHH3cfjR9n2_Nv6c9KAJ_LT-6v5VWmJWQhSQMdBT_tX-z-tJ5Xn_ALzOM9qAEhJaTDEkY6GrHlp_dX8qg2eR-8zn2pftX-z-tAEJkbJ-ZuvrU8H7xSW-bB4zR9nzzv6-1Nz9n4-9nmgCV1AQkAAgcECq3mt_eb86k83zf3eMZ75zS_Zf9r9KAJI1BjBIBJHJIqOf5EG35cnnHFZmt-I9L8MWDXWr3kVrAvALHlvYDqT9K8sufiB4w-IdxJYeA9LksrAHbJqlzwfwPRfoMn6UAeheJPG-h-EIPM1jUlikIysCHdK30UfzOBXnp8Z-PfiGxh8H6OdH0tzg6jdDkj2PT_vkE-9bnhj4MaTpd1_aevzSa5qrHe8lzkxg_Q9fqc_QV6RHMscYVYgqgcKO1AHl2h_BTSLO7-3-JbmfXtQb5na4JEWfp1b8T-FelWNtDaRpbW0MUMCjCxRqFUfgKsbPP_eZx7U0hbZWmZwFUZJJwAP8BQBNIYoozI-1UUZJPQCvCdd1G8-MPi1fD-jyyR-FrCTzLu7XpMR6fyUfjVnxL4m1T4qa1J4U8JSNDokZ_wCJhqPIDrnkA-nt_F9K9U8O-FtO8L6NDpWmpsij5diPmkfuxPc0AXtMsraxsYrK2hSO2t1EcUYHCqBirkigRkgAEDggVFn7Px97PNHneZ-724zxnOaAI_Nb-8351YRQ0YJAJI5JFM-y_wC1-lJ5vlfu8Zx3zigB04CAFflJPaoBI2R8zdfWpc_aOPu45p32fHO_p7UAS-Wn91fyqvMSsmFJAx0FO-1f7P60mzz_AN5nHtQA2ElpAGJIx0NWfLT-6v5VB5XkfvM5x2pftX-z-tAEJdgSAxwDgc1LB-9zu-bHTNH2ff8APv689KP-Pfj72aAJXQBCQACBwQKrea395vzqTz_M_d7cZ4zml-y_7X6UAPRQ0YLAEnuRTZwFUFflOe1N87yf3eM475oz9o-XG3HOaAI_Nb-8351b8tP7q_lUP2X_AGv0o-1f7P60ANmJWQBSQMdBSRF2kAYkg9jTtn2j5s7ccYo8ryP3mc47YoAn8tP7q_lVV2YOQGIAOAAak-1f7P60nkb_AJt2N3OMUAEH73O75sdM1KyIFJAAIHUCov8Aj3_2s_hR5_mfLtxnjOaAI_Nb-8350_e3qfzp32X_AGv0p_k-_wClAEm9f7w_Oq04LuCoyMdRUTdTVm2_1Z-tAEUIIkBIIHqRirW9P7w_OmT_AOpaqdAEjK5ckAkE5BAp8HyZ3cZ6ZqxH_q1-lQ3X8NAD2YMhAYZI7Gqnlv8A3G_KiP8A1ifWr9AEMJCRAEgH0PFE5DJhTk56Cobj_XGlt_8AWn6UAR-W_wDcb8qvb0_vD86Ws_1oAmny0gK8jHamxBhKCQQB1JGKltPuN9afN_qmoAfvT-8PzqoysZCQCQT1AqOr0X-qX6UAQQfKTu4yO9TFlIIDDp61Hc9E-tV0--KADy3_ALjflVmEhYgGIB9DU1U5_wDWn6UATTENHhSCc9BVby3_ALjflUlr_rfwq3QA0OmB8w_Oq8_zEbeeO1QnqfrXN-LPiFoXgiyLahceZeMuYrOI5lf0-g9z-tAHTMyQKZp2EUSDLMxwAPcntXmHiH4wPd350LwHYvrGpscfaQMwp7j1-pwPc1kR6N43-LTi716aXQvDW7dHZx_fmH0PX6tx6CvT_D_hrSPC9gLPSLKO3THzuOWf3ZupNAHn-i_CG61W-Gt-PtRl1a_b5haqx8pPYkdR7LgfWvV7C2gsIFt4IY7eCMbY4412qPoKuRf6pPpUVz9xPrQBIWXB-YdPWqflv_cb8qB1T603VdXstD06bUNSukt7aEZZ5Dx_9c-woAmkuYLOzkuLqVYYYgXkeQ4CjuTntXi-u-INa-L-rSeHPCpktfDcTYvdRKkeaPT6HsvU9TgVDdT698bNVeC3M2leDreT95KRhrgj-Z9ui98mvXtA0bT_AA_YQ6ZpVstvbRDhR39ye5PrQBW8OeGrDwvo8WmaZbGOFeXcj5pX_vMe9dCGXA-YdPWn1QPV_rQBNP8AMRt5wO1MjDhwSCADySKltuj_AFqSb_Ut9KAHb0_vD86qShjKSASD0IGajq9F_qk-lAFeD5Sd3HHerBdMH5h-dRXf-rH1qsOo-tAC-W_9xvyqzCQseGIBz0NTVUuv9b-FAEsxDREKQT6Cq3lv_cb8qfB_rR9KuUAMVlCgFhkD1qGf5yNvOOuKhf8A1h-tWLXo_wBaAIlVhICQQAepFW96f3h-dJL_AKpvpVGgCWQOZCQCQehAzSwZWQluBjvU0H-pWm3P3B9aAJd6f3h-dUfLf-435UnpWhQBXgIRCGODnoadMQ8RAIJ9BzUdz_rh9KbB_rxQAzy3_uN-VW1YKgBYZA7mpKoSf6x_rQBNP8-NvOOuKYquHBIIAOSSKltf4qlf_Vt9DQAu9P7w_Ol3D1H51n1NQBN5EZ7frUMhMJCJwCPrUpuEHr-VRsDOdy9BxzQAiSl3CscqfapvIj9P1qERmEiRsYHXFS_aE_2vyoAhMzIxUHgH0p0f7_O_nHTtSGFnJYYwTnmlT_R87u_TFAEhiRQSByBkc1B58nr-lTGdHBUZyeORUX2Z_b86AJURJFDsMsetJKBCm5ODSCVYR5bZyPShmE6bVznPegCPz5PX9KseRH6frUH2Z_b86m-0J_tflQBHKTEQE4BHNNSV5HCMcqevGKcwM53L0HHNIIzCRI2MDrigCbyI_T9agMroSoPAOBxU32hP9r8qiMJlJcYweRmgBU_f5EvOPwqQwoASByBxzUa_uMlu_TFSGZCCBnJ46UAQefJ6_pUqKky725J4qP7M_t-dSLIIV8tuo9KACVRCu5ODmq0t0IIZJ55ViiQbndmACj3J7VkeK_GWjeEtJN3q9z5Wf9VCnMkx9FH9eg9a8tj0zxd8ZJ1uL9pdE8Jh8xwKfnnHY_7R9zwOwNAGnrvxNv8AxJqTeHvh1aPeXR4l1Ij93Fz1GeP-BH8Aa1fCPwnstBvBrOuTf2xrsh8xppvmjib2B6n_AGj-AFdpoGh6P4X0xNP0i0W2hTrgfM59Se5-tajDzyCvQdc0ANjleQhGOQevFT-RH6frUIhMREhxgdcVL9oT_a_KgCF5XUlVOADgcU6ImYkPyB0pDCZSXGMHpmuC8cfEyy8Hn-zbGMalr83yxWkXzbCehkx_6COT7daAN_xb4s0XwXpTX-qTYY_6m3U5kmb0A_r0FeY6ZoHiD4t6hFrviwvY-Hozvs9OQlfMHr9D_ePJ7YFanhT4cXmr6qvirx_Kb3VJDuhsH5ig9Nw6cf3RwO-TXq_2duPu8cCgBmn2FrZ2MNtbQRwQQjZFFEu1VA9BU8iiFdycHpSKwgGxs59qGYTDy1zn3oAj8-T1_SpxDGRkjk89ah-zP7fnWVrfjTw74bjP9q6tbWzgf6stmQ_8BHNAGvKTCQE4z1pqyu5CseCcHivItT-OttfXH2PwnoF9q11_CzqQP--Rkn8cV3fgq78SX-h_a_E-nw2N75p2Qx8Ex44yMnB68UAdT5Efp-tQSSvGSinAHTipvtCf7X5VEYTKTIMYPTNACxEzEh-QOlS-TGOcdPeokBgJLdDwMVJ9ojPHzc-1AEHnyev6VLEomTc_JqP7M_t-dPVhANjZz7UAK6pCpdRgiovPk9f0qVpBMvlr1PrUf2Z_b86AJhCpAJHJ681HL-4xs4z1p4nRAFOcjjpTW_f4K9vWgBgldyFJ4JweKn8iP0_WoRCYiHOMDk4qX7Qn-1-VAETuYiVU4UdOM0REzEh-QBkUGMzEyLjB6ZpVBgJZuh9KAJfIj9P1qv58nr-lT_aE_wBr8qh-zP7fnQBJEBMm5-TSugjjLKMMOlIrCAbW6nnikMqSrsXOT0zQBH58nr-lTiJGAJHJGTzUP2Z_b86lE6IApzkccCgBkv7jGzjPWmiVmYKTwTzxTn_0jG3t1zSCBlcMcYHJxQBN5Efp-tL5S-n6037Qn-1-VO85fegCm3U1Ztv9WfrU1Vrn74-lAEs_-paqdSQ_60Vc4oASP_Vr9Khuv4ahl_1r_WpbX-OgCCP_AFifWr9I_wBxvpVDmgCS4_1xpbf_AFp-lTwf6laS5_1Y-tAEtZ_rRWhxQBBafcb60-b_AFTVFc_fH0pkP-tH1oAjq9F_ql-lO4qjL_rm-tAE9z0T61XT74qe3-830p08scMDyzMqRqpLMxwAPegCavL_AB58UbTQLxtF0OH-1PEEhEaQxjcsbH-9jqf9kfjiue1_x9rPjbU5PDHw_EnldLrVfuhF77D_AAj36nt613HgL4c6T4MtfPiX7Vqso_fX0o-Y-oX0H6nvQBy_hP4V3GoauviTx7cHUtVk-ZbNzmKH0B7HH90cD3r19VCKEUAAdABwKZc_6r8aqc0AB6n61atfut9amHQVWufvj6UATy_6p_pVAlIwXYhVUZLk4AH-FUda8QaZ4a059S1a7W3t4-56ufQDufYV5PLc-K_jVcGGyEui-Dw-HkcfvLjH_oR9h8o7kmgDT8QfErVPEWonwv8ADyE3N4w2z6kPuQjodpPYf3j-AJrofBXwzsPBqfb7l_7R1yY5mvpecE9dmen16n9K3_D_AIX0nwlp66fpNsIYs5kdvvSn1Y960LrVLDSLZrrUr2G0hA5eeQIP1oAlHVPrV-vI_EHx88O2Ba30W2uNXuDwpVdkRP1PJ_AVzf2j4uePj-6Q6Bpsn8fMHH1P7w_higD17xF4p0Lw4DJq-qW9rxwjtlj9FHJrzXUfjzatcfZfCuh3mq3TcK0ilQfooyx_SrOgfAPRomF54g1C61a6Jy4yY4yf_Qj-Yr03T9B0rQbPyNK0-3s4h2gjCk_U9_xoA8gGk_GLxxzqN8nh-wf_AJZo3lHH0XLfmRWpovwL8NWEvn6vPdavc5y3mt5cefoOT-Jr1Hn1NXx0H0oAzNG0nTtItTb6bY29pEP4YYgo_TrWlL_qn-lV7r7w-lMi_wBan1oAZV6L_VJ9KdxVOb_Wn60AS3f-rH1qsOo-tT233z9KsnoaACql1_rfwqLmrdt_qvxoAgg_1o-lXKjn_wBUfrVPmgBX_wBYfrVi16P9amT7i_SoLj7y_SgCaX_VN9Ko06L_AFy_Wr3FAEcH-pWm3P3B9ahm_wBaafbffP0oAg9K0KOKz6AJrn_XD6U2D_Xip7b_AFZ-tLP_AKlqAJKoSf6x_rSc1fT7i_SgCG1_iqV_9W30NQ3P8NQxf6xfrQA2pqtcUUAQG4IOMCgKJ_mbgjjiozE-T8pp8REKkPwSc4oADGIQZASSOxpPtLei0-VlkQopyx7VD5Mn900ASCBW-Yk5PJxSH_R8bec-tSK6KApOCBjFRzfvcbOcdcUAJ55fCkDDcGn_AGZfU1EInBBKYAOTVrzo_wC-KAK5lMJ8sAED1pQxn-VuB14psiPI5ZRlT0NLEDCdzjAx1oAf9mX1NN-0t6LU_nR_3xVXyZP7poAeB9o-ZuCOOKUwiEeYCSR60QkQgh-CTTpHWRCinJPagBn2lvRaUQLIA5JyeTio_Jk_umqHiDxLpnhbRpNS1ScRRIMAA5Z2_ugdzQBLq-q2Ph3TpdR1G6SC1iHzPIensPUn0rxue78R_GvUntbMy6V4Phl2ySn785z-p9ug75NLp-j658aNaTWteEtl4Wgb_RLJDgzf57t-Ar2ix0-LTbSG1tLaOC1hAWOKMYCgUAVfD_hbSvDGkx6bpVuIIF5J_ic-rHua0jIYT5YAIHrU_nR_3xVeRC7llGR60AKGM58tsAe1O-zL6mmRAxSbnGBjrSy3dvb27zTzLFDGNzyOcBR7k0AJ9pI4wOK4fx18TNK8JILRVN_rT8Q2EJ5BPTzMdB7dT6VwPjr41rcSyaP4UuFiUkpLqj8AD_pmP_ZuvoK4vSPHGi-DS91o2nHVtdk_1mq6lnCk_wDPNM5_EkE0Aek6J8OtY8X6gvib4izkhBug0sHakadfm_uj26nua6bXPi14O8J2_wBjiuY7mWEbVtbABguO2egrx6C1-J3xYfdJNOunt_HIfItx9APvfrXSQeAPh34DAn8Ya1Bf3y8m0U8Kf-ua_OfxwPagCKf4q-O_G13JaeDdDe3jJwZ0TzWX6yH5Vq3YfA7WNZul1Dxt4gllkJyYYnMj_TzDwPwBqwfjNNc-XpHw_wDCTzBOFLJhV_7Zp0HuSK9e02a-u9Hs5NRgSG_aINPDEchGxyAaAMnw94L8N-GFQaTpUEMvTz2G6X_vo8_lium-zD-8ahEUgcErwDk1a86P--KAIC5hPljBHvQHMx8s4A9qSUGWTcgyMdaIlMb7mGBjrQA_7MvqaZ9oI4wOKsedH_fFVTFIXJC8E5FADwPtGS3GOOKUwiMGQEkjkZpIT5OQ_GTxT2lSRCqnJI6UAM-0t6LSiESr5hJBPpUfkyf3TU6yIqhWOCOooAjI-z8rznjmk-0k8YHNOmImACckGo_Jk4-U0AS_Zl9TTSxgOxcEdean86P--KrygyybkGRjrQAokMx8sgAH0p32ZfU1Tu7-z0iI3WpXcFpCo5kmlCj9a4HXfjt4P0relnLPqcw6C2TCf99Nj9M0Aej-eV-QAccUo_0jJbjHTFeE_wDCzviJ4skI8K-FjBC3SZojJ_4-2FqUeCPi_reX1XxUunI3Pli5IP5RDH60Ae5mAJlwSSORmm_aW9FrxGP4S-PbYmS08fyNN_dM8yg_rVm28PfG21byB4gsZIunmSsr_wA0zQB7OIhMBISQT6U0j7P8y8k8c1zXhnSPEVgVn8Q-J21CUIR9mhto4ohn3xuJ_L6V00xEwATkg0AJ9pb0WnfZl9TUXkyf3TVrzo_74oAgLGD5V5B55pBKZm8sgAHuKJQZjuQZGOtJGjxuGYYUdTQBJ9mX1NM88plQBheBVjzo_wC-KqmJySQmQTkUAPH-kZ3cY9Kd5CrlwTketNh_dZ38Z6ZqQyIwIBySMYoAj-0t6LT_ADm9BUPkyf3TUvlN6UAT1Vuf9YPpTTK2T8x_OpIQHQlhuOcZNAEdv_rhVyopFCxllABHQiq3myf3z-dACy_61_rUtr_HUiIrICVBJHUiop_3WNny564oAsSfcb6VQqRGYyAFiQTyCateUn9xfyoAbB_qVpLn_Vj61DKXRyFJAHQClhJkfDHIx0NAENaHpTfKT-4v5VU81v7zfnQBJc_fH0pkX-tX61JABICW-Yg96fIoWMsoAI6EUAZ2v67Y-GtFudW1GURW0C5Y55J7KPUk8V45oGh6n8XPEH_CU-JlaLw_CxFhYZ4k_wDrep7njpUvxovEu_FPhfQ9Uu2tdEmfzrqU9D82Dn6D8s5r1K18QeFLPT4Y4NZ0iK1jjCxqLqPAUdO9AGtZxRwRiKNFSJFCoqjAUDsPYVbf_Vn6VwuqfFXwRpY51-CRh1S1BlJ_754rkbr47R3sptvDHh3UtUnJwCw2g_guT_KgD1mo73WdM0SzE2qX9vZxD-OeULn8-teT-V8Y_FxORZ-GrKTrjCyAfq2fyrzPxZZ-GvD08kM2sXPinXBxJIzlbeE-5yWkI9AQPX0oA9Z8S_Hzw3YK8Okwz6pMOjAeVF-Z5P4CvJ_F3jfXvGVsG1e_g03T85i0-3zul9CV6n6tgegrzxnLPv4BJ6AYrd8P-HvEevXX_Ej066uZM8zRrwv1c8D86ALFj4Qe5CXOo3dvomnNyJ798SOPVIh87_gMe9dDaeIPA3hRl_sTRJvEGojgXepjbED_ALEQ_rzWvpvwhtoZPtXjXxVp9jk_NBHcq0v4seB-Ga7jSNe-D3ghP9BuLSS4H_LdImnlP_AscfhigDlbW0-LXxD2eZO-j6Y3A_5dowPZR8xrsvDfwD8Pacy3Gtzz6tc9Srny4s_Qcn8T-FRXv7QPhpZDHpmlaley9isYQfzz-lVF-K3jrVcjRPAVzsP3ZJ95_oBQB6xaafZaVD9l06zgtIF6RwxhR-lW7X7x-leQj_hd-rjIi0rSlbuRHkf-hGkf4cfEi-A_tb4hSRK3VbYyf020AezSyJGhLuqjHUnFYc2uaPBxPq1hGfR7mMf1rzVfgPZ3TBtU8UaveEnnkDP55rWh-AHgmJcOmozH1e5x_ICgD0DTtW0y9Hl2eo2lw_XbFMrn9DV65_1R-tfP_wASvhn4Y8EeG21rSru_s7-KVVtx52dzk_mMDJyPSvX_AATdX994Q0a41Ulrya0jebd1JxwT7kYNAGtWgPuim-Un9xfyrD1bxHpWhRmTVdWtrNB0EsoBP0HU0Aal194fSmRf61PrXlep_HfRI5Ta6Dpl7rN2fuYj2If5sfyqh5nxh8ZKSkNt4asW7keXJj9X_lQB6_qet6XotubjVNQt7OIc5nkC5-mev4V5rrfxx8M2twYNIhu9YuWOEEEe1CfqeT-ANZJ-EGhaVZ3GueNNevtSEC-ZM5cqv9WOenavN9f-ItqqPp3gzSLfRNOxteeNR9qmHvJ1A9gc-9AG9qvxy8ZSX5tdP0220-UkKIfJMsufTnv7Yr3zRtUuoPCdlfeJ5ILG8MAa68xhGqt-PT6V8zeCrTxYcTeE_Dpa8k4Oqzxbyuf7jP8AKn1GT7131r8Ftc8QXC3fjTxPNNITzDExlI_4EeB-AoA6HXvjf4T0ndFZyTapOOgthiP_AL6P9M1zK-M_ir42Gzw5ov8AZVi_AnK4_wDIkn9BXqGgfDLwl4aVWsdIgecf8vFyPNk_M9PwxXSzErIApIAHAFAHiln8Cb_U5_t3jLxLcXcpOTHAxc_99t_QV3uifDrwl4fANlo0BnXpPOPNk_M9PwxXWwku4DEkehqz5Sf3F_KgBEUKigKAAOAB0qK56pUZkYMQGOAcDmnwfvc7_mx0zQBHF_rU-tXajdFVCQoBA4IFVfNk_vn86AHTf600-2--fpUkahowzAEnqTTZgEUFflJOMigCf1rPp_mt_eb86t-Un9xfyoAZbf6s_Wln_wBS1QzExsAp2jHQUkZd3AYkg9QaAIqvx_cX6UnlJ_cX8qquzCQgMQAeADQBLdfw1BF_rF-tSwfvc7_mx0zUroqoSFAIHUCgCSiqPmyf3z-dT7j_AHzQAhtiTncPypN32f5SM55yKsbx6j86rT5LgjkY7UAO83zf3eMZ79aT7Kf74_KmwgiQEggepGKtb1_vD86AK_niL5NpOOM5oP8ApHT5cetROHLkgHBPGBUtv8ud3GfWgA8gp82_O3nGKX7UP7h_OpWIKEAjp61T2N_cP5UATeX5_wC8BxntS7PJ-cnPbAp8TBYgCQD6HiknIKADk57UAN-1D-4fzpfs5_vj8qr7G_uH8qvb1_vD86AK4P2f5TznnijzvO_d4xnvST_NIMc8dqbCCJQSCB6kYoAzdc8LaV4ksBZ6xZx3cIOV3DBU46gjkH6VxR-B_gRZDmxvDg4x9rbFepb1_vD86qSBy5IBIJ4wKAOT0_4WeCLADyPD1qzLzuuMyn_x41r6lf6J4O0hr28mtdOs4uixoE3ewA6n2Fcr4w-KuneGbh9J0uI6trrny1tYeQjdtxHf_ZHP0rF0T4Y6r4pv18QfEa7e5m-9DpathIh1w2On0H4k0AZt54g8Y_Fx5rLw5E2jeHRlZL2UkNN7ZHr_AHR-Jrgv-FGeOi5UafbFc_eF0mDX1DDbJbQR29vCsUMQ2rHGuAo9gKuwECIAkA-hoA-YrX9n7xeWDz3Gm24B6mYtj8hXW2vwR1meERar40uGtwMCC3Rto_AnH6V7hOQUwDk56Cq2xv7h_KgDzKy_Z68KwZa7u9RvGP8AelCD9BXSWHwv8EaSFEXh21lYcg3AMp_8ertwwwOR-dVp_mIxzx2oAr2lhYWgEVnY29sD08mJUx-VWfsznrJTIwRICQQAepFW96_3h-dAEPnCP93jOO-aaT9o4Hy455pkgJckAkE9QKdb_KW3ccd6AF-zkc7xx7Uv2of3D-dTlhg8j86o7G_uH8qAPDfjZfFfHOgLrNrct4dhUSsIRnzWz8w54zgAfQ1pH42alqDrH4W8F3t023arygkD04UdPxFezLHFJB5cqK4z0cZ_nSuFEGyMAD0WgDxZrD4xeLf-P3Ubfw9aN1WIhWA_4BlvzIrV0n4CaFDMLrXL681i5fl_McopP4cn8TXpuxv7h_KrwYYHI_OgDG0rQtI8OwiHS9MtrVCP-WMQUn6nqfxrT84Sfu8Yz3zTbj5iNvPHamRghwSCADySKAKmqaJZ63pVzpuoIJrS4TbKvT9exFYGj_Dfwd4fkBtdBt3mXpLP-9b_AMf6fhXa71_vD86qSgmUkAkZ6gZoAcm2RBGihFXoMdKX7ORzvHHtSQfKTnjjvVksMHkfnQBB9qH9w_nSbPP_AHgOO2DUOxv7h_KrUJATB4Oe9AEfl-R-8JzjtS_ah_cP50s5BiIBBOegqvsb-4fyoAm8gv8ANvHzc9KB_o_B-bPpUysNg5HT1qG4-Yrt5x6UAHniT93jGeM5o-yn--PyqKIEOCQQAeciru9f7w_OgCv53k_u8Zx3oz9o-UcY5yaZMCZSQCR6gZp0HysSeOO9AD_s5_vj8qT7UP7h_Op96_3h-dUdjf3D-VAE237R8wOMcYNHlmH94TnHanwEBCDwc96WUgxEAgn0FADPtQ_uH86TyC_zb8bucYqHY39w_lVxSAgBI6etAEI_0fr82fSjzxJ8m0jdxnNFx82NvOPSo4w4kBIOM-lAEn2U_wB8flUnlH-9-lSb1_vD86XcPUfnQBQbqas23-rP1p3kIex_OoZCYSFTgEd6AJp_9S1U6mWUykKx4PXtU3kJ6H86AHx_6tfpUN1_DUZmaNioIwD6U6P9_nfzjpigCGP_AFifWr9RGJFBYA5Az1qDz5PUflQAXH-uNLb_AOtP0qRYkmUM3JPWh1EKb04PvQBPWf61J58nqPyqx5Ceh_OgBlp9xvrUk_8AqX-lQsTEQqdCMmvPfFnxWstIvP7H0WE63rbt5a21tyqH_bI7j0H4kUAdbq2sadoVhJf6pdxWltH1aQ9fYep9hXl114r8XfFGeTTfB0Mul6Ep8ufVJvlZx7en0Xn1Iq3o_wAMNX8V36a78Rbx7iTrDpcTYjiHocdPoPxJr1O3hi0-BLS0hjgt4htjijUAKO2KAOY8I_DrRPBFuDaRfaNQf_W30wzI3rj-6PYfiTXUp98VNH-_yH5x6U8wKASAcjpzQBNVOf8A1p-lHnyeo_KpkRJRubkmgCK1_wBb-FW6ruohTcnBz3qPz5PUflQBGep-tWrX7rfWl8iM84PPvUcpMJATgHrQBPL_AKp_pVGpVleUhWPB68VP5Ceh_OgB0X-qT6VFc_cT61GZXjJRTwOBxToiZiQ_IHSgCEdU-tX6hMEYBIByOetQ-fJ6j8qAFuP9afpRb_60fSpVQTLubk9OKGQQruXg9OaAJqoHq_1p_nyeo_KphBGQCQcnnrQAlt0f61JN_qW-lQv-4ICcZ600SvIQjHg8HigCKr0X-qT6U3yE9D-dQNK8RKqeB04oAku_9WPrVYdR9aniJmJD8gdKk8iMc4PHvQBLVS6_1v4Unnyeo_KpEUTJufk57UARwf60fSrlQsqQruXgiofPk9R-VADH_wBYfrVi16P9acIVIBIOT15qOT9xjZxnrmgCeX_VN9Ko1KJmZgpPBPpU_kJ6H86ACD_UrTbn7g-tRtKYiVU8Dp3pY2MxKycgDNAEHpWhUfkJ6H86r-fJ6j8qAHXP-uH0psH-vFSRqJk3vyfalaJIVLLwR0oAnqhJ_rH-tP8APk9R-VTiJGAYg5Iz1oAba_xVK_8Aq2-hqCT9xjZxnrmmiVmYKTwTzxQBFU1TeQnofzpfJX3_ADoAQzoPX8qiYGc7l6AY5qFupqzbf6s_WgCNY2hIdsYHXFS_aY_f8qWf_UtVOgCYwsxLDGCc9aVP3Gd_fpirEf8Aq1-lQ3X8NACmZGBUZyRjpUX2eT2_Oo4_9Yn1q_QBXWRYQI2zkelKzCddq9feorj_AFxpbf8A1p-lAB9nk9vzrnvE3xF8OeE4iuoXwa6_htIPnlb8B0_HFXPE3hkeJ7WG2fVtU0-NGJf7BceUZQezccisfQPh54Y8NSefY6csl3nJuro-bL-Z6fhigDkJF8e_FFsBX8MeG345z9pnX-eD-A-td34V8CaF4Kg26ZZqspXEt1Id0r_U9h7DArprflT9afP_AKl_pQA37TH7_lURiZiXGMHkZNQ1ei_1S_SgCBP3GS_fpipDMhBAzk8dKbc9E-tV0--KAJPs8nt-dPRxEvltnI9KsVTn_wBafpQBK7Cddi9evNR_Z5Pb86LX_W_hVugCLz0HHPHtUbgzkFOg4OagPU_WrVr91vrQBGI2iIdsYHJxUv2mP3_Kny_6p_pVGgCYwmUlxjB5GaVB5GS_fpip4v8AVJ9KiufuJ9aAHeeh4Gefaofs8nt-dRjqn1q_QBCjiIbG69eKGYTDYuc9eaiuP9afpRb_AOtH0oAPs8nt-dTeeg4OePapaoHq_wBaAJnHn4KduuaQQmIhzjA5OKfbdH-tSTf6lvpQA37TH7_lURiMpMi4wemahq9F_qk-lAECAwEl-h4GKk89Dxzz7U27_wBWPrVYdR9aAJfs8nt-dSIwgXY3XrxU9VLr_W_hQA9pRMvlrnJ9eKZ9nk9vzpIP9aPpVygCETooAOcjjpTH_f42duuahf8A1h-tWLXo_wBaAGCJlIc4wOTg1L9pj9_yp8v-qb6VRoAmMZmJkXGD0zSqphJZ-hGOKlg_1K025-4PrQAv2mP3_Kofs8nt-dRelaFAECsIBtbqeeKRpFmBjXOT6025_wBcPpTYP9eKAF-zye351KJkUBTnIGOlTVQk_wBY_wBaAJn_AH-NnbrmkELqQxxgHJ5p9r_FUr_6tvoaAGfaY_f8qd5y-_5VSqagC1gegqrccSDHHHanm4IOMCgL5_zHgjjigCKHmUZq5gegquUEI8wEkjsab9qP90UARyk725PWp7fndnn60vkK_wAxJyeaYf8AR8Y5z60ATsBsPHaqOT6n86m88t8mBzxT_sqf3zQA-DmIZpLjiMY457VGZDEfLABA7mgMZ_kPA9qAIMn1P51oYHoKg-yp_fNN-0t_dFACXHEgxxx2psOfNFPA-0fMeMccUpjEI8wEkjsaALGB6CqMpPmtyetSfaj_AHRTxAH-ckgnmgBtvyTnnjvU7AbDwOlQH_R-Rzn1pPPLfJgc8UAQ5Pqfzq3BzEM-vem_ZU_vmmmUwHywMgdzQBJPxFxxz2qpk-p_Opw5n_dkYHqKd9lT--aAJgBgcCq1zwRjjjtR9pI4wOKUD7RyeMelADIc-aPrVzA9BVcwiMeYCSRzg037Uf7ooAZLnzT160-25Jzzx3pwhEn7wkgnnApCPs_I5zxzQBYIGDwOlUMn1P51N9oJ4wOaf9lT--aAHQcxc-veifiI49e1RlzB-7AyPU0CQznyyMD1FAEGT6n86vgDA4HSofsqf3zTPtBHGBxQAXPBGOOO1RxE-avJ61KB9o5PGPSlMIj_AHgJJHODQBYwPQVTmz5p-tO-1H-6KcIhKPMJIJ7CgBtvzIc88d6skDB4FVyPs_I5zxzSfaSeMDmgCHJ9T-dW7fmHnnnvTfsqf3zTSxg-Qcj3oAkn4hOPWqmT6n86nEhmPlkAA9xTvsqf3zQBKoGwcDpUFxwVxx9KTzyvyYHHFKP9I5PG30oAiiJ81eT1q9gegqAwBPnBJI5pn2o_3RQA2bIlOKdb8yHPPHenCMTDzCSCewpCPs_zDnPHNAFnA9BWfk-p_Op_tLf3RTvsqf3zQA635jOeee9LPxCcVEW8j5RyDzzQJDKfLIAB7igCDJ9T-dXlA2DjtUX2VP75pnnlfkwOOKAH3HG3HH0qCMnzByetSj_SM54x6U_yFX5sn5eaAJsD0FGB6VV-1H-6Kl80-goAi9PpU0P3PxqH0-lTQ_c_GgB0v-rNV6sS_wCrNV6ALKf6sfSo5u1SJ_qx9Kjm7UAMj--PrVmq0f3x9as0AQSffNEP-sP0ok--aIf9YfpQBPVb1qzVb1oAkh-4frT5PuN9KZD9w_Wnyfcb6UAVqsp_qx9KrVZT_Vj6UAMm6D61Cv3x9amm6D61Cv3x9aALdQS_fqeoJfv0AEP-sP0qeoIf9YfpU9AFY9T9akh-4frUZ6n61JD9w_WgB7_6s_Sq1WX_ANWfpVagCyn-rH0pk3QfWnp_qx9KZN0H1oAjH3h9as1WH3h9as0AQS_foi-_RL9-iL79AE9Vj94_WrNVj94_WgCSHofrT3_1Z-lMh6H609_9WfpQBWqzH9xfpVarMf3F-lADJvuD61GOo-tSTfcH1qMdR9aALNQTf6wfSp6gm_1g-lABD94fSp6gh-8PpU9AFRvvn61ND0P1qFvvn61ND0P1oAe_-rP0qtVl_wDVn6VWoAsx_cFMm-5-NPj-4KZN9z8aAI_SrNVvSrNAEE3-sH0pIvvilm_1g-lJF98UAWKrSffP1qzVaT75-tAD4e9SN9w_So4e9SN9w_SgCtSN94_WlpG-8frQB__Z2BhYW6RoZGlnZXN0SUQSZnJhbmRvbVD8BW4_1Ej4ZDp8gj5VxUiMcWVsZW1lbnRJZGVudGlmaWVyamlzc3VlX2RhdGVsZWxlbWVudFZhbHVl2QPsajIwMDAtMDEtMDHYGFhcpGhkaWdlc3RJRBNmcmFuZG9tUOQhAAGwKv1ZM5EMgsxrqddxZWxlbWVudElkZW50aWZpZXJrZXhwaXJ5X2RhdGVsZWxlbWVudFZhbHVl2QPsajIwNDAtMTItMzHYGFhVpGhkaWdlc3RJRBRmcmFuZG9tUOuopc_kY8Ed4WRawa-R2XNxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ5bGVsZW1lbnRWYWx1ZWJHUtgYWIOkaGRpZ2VzdElEFWZyYW5kb21QkDfqhs5yRJR-Tm_nk99YQnFlbGVtZW50SWRlbnRpZmllcnFpc3N1aW5nX2F1dGhvcml0eWxlbGVtZW50VmFsdWV4LU1pbmlzdHJ5IG9mIEluZnJhc3RydWN0dXJlIGFuZCBUcmFuc3BvcnRhdGlvbtgYWFykaGRpZ2VzdElEFmZyYW5kb21Q5VYZ9AzgRrVWrdgfUhavCHFlbGVtZW50SWRlbnRpZmllcnZ1bl9kaXN0aW5ndWlzaGluZ19zaWdubGVsZW1lbnRWYWx1ZWJHUtgYWGGkaGRpZ2VzdElEF2ZyYW5kb21QZZ4wEXGhX8zgPeTqDh5f0HFlbGVtZW50SWRlbnRpZmllcm9kb2N1bWVudF9udW1iZXJsZWxlbWVudFZhbHVlbkExMjMtNDU2Ny04OTAw2BhZATmkaGRpZ2VzdElEGBhmcmFuZG9tUKx1uu7qywtln6etWErE0eFxZWxlbWVudElkZW50aWZpZXJyZHJpdmluZ19wcml2aWxlZ2VzbGVsZW1lbnRWYWx1ZYKkdXZlaGljbGVfY2F0ZWdvcnlfY29kZWFCamlzc3VlX2RhdGXZA-xqMjAwMC0wMS0wMWtleHBpcnlfZGF0ZdkD7GoyMDQwLTEyLTMxZWNvZGVzgqFkY29kZWI3OKNkY29kZWNTMDJkc2lnbmI8PWV2YWx1ZQWkdXZlaGljbGVfY2F0ZWdvcnlfY29kZWFBamlzc3VlX2RhdGXZA-xqMjAwMC0wMS0wMWtleHBpcnlfZGF0ZdkD7GoyMDQwLTEyLTMxZWNvZGVzgaNkY29kZWNTMDNkc2lnbmI8PWV2YWx1ZRh92BhYZaRoZGlnZXN0SUQYGWZyYW5kb21QPzBywGpCi1VmQcRHlIE75nFlbGVtZW50SWRlbnRpZmllcnVhZG1pbmlzdHJhdGl2ZV9udW1iZXJsZWxlbWVudFZhbHVlazEyMzQ1Njc4OTAwamlzc3VlckF1dGiEQ6EBJqEYIVkC6zCCAucwggKNoAMCAQICFBP2etgDH0iUVudcQ6nGHGR9mTLLMAoGCCqGSM49BAMCMFwxHjAcBgNVBAMMFVBJRCBJc3N1ZXIgQ0EgLSBVVCAwMjEtMCsGA1UECgwkRVVESSBXYWxsZXQgUmVmZXJlbmNlIEltcGxlbWVudGF0aW9uMQswCQYDVQQGEwJVVDAeFw0yNTA0MTAxNDI2MzhaFw0yNjA3MDQxNDI2MzdaMFIxFDASBgNVBAMMC1BJRCBEUyAtIDA0MS0wKwYDVQQKDCRFVURJIFdhbGxldCBSZWZlcmVuY2UgSW1wbGVtZW50YXRpb24xCzAJBgNVBAYTAlVUMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBYl26IZepkoD_VjgNe7CaTXCJHYIZfGEoH-Q2UyWMXGU8RphnvsPfb3SUXo1U_ta_n717RiHmusFmimGuS4I96OCATUwggExMB8GA1UdIwQYMBaAFGLHlEcovQ-iFiCnmsJJlETxAdPHMCMGA1UdEQQcMBqCGGlzc3Vlci1iYWNrZW5kLmV1ZGl3LmRldjAWBgNVHSUBAf8EDDAKBggrgQICAAABAjBDBgNVHR8EPDA6MDigNqA0hjJodHRwczovL3ByZXByb2QucGtpLmV1ZGl3LmRldi9jcmwvcGlkX0NBX1VUXzAyLmNybDAdBgNVHQ4EFgQUkrCormv27az6QpJM5swm74NYp6owDgYDVR0PAQH_BAQDAgeAMF0GA1UdEgRWMFSGUmh0dHBzOi8vZ2l0aHViLmNvbS9ldS1kaWdpdGFsLWlkZW50aXR5LXdhbGxldC9hcmNoaXRlY3R1cmUtYW5kLXJlZmVyZW5jZS1mcmFtZXdvcmswCgYIKoZIzj0EAwIDSAAwRQIgcuYllqVJtkA78r6EZRq4v3lw1kKOIvWgO9om6O6HwNQCIQCUAFesofW4ZHN7-xfWFmF_WXW03JH8fud20MvcPZM2JlkE69gYWQTmpmd2ZXJzaW9uYzEuMG9kaWdlc3RBbGdvcml0aG1nU0hBLTI1Nmx2YWx1ZURpZ2VzdHOhcW9yZy5pc28uMTgwMTMuNS4xuBoAWCDvfp91hO1WqkAmr_VKyWP5j3RxbM59rEDogWDeq1n5XwFYIPTEVPhNVqsZVnbqFpAmCAkYoQZ9bOeiS8ntbkzOl7CqAlgghZyUuYqKQSBP8OdTHDmCKgqCWk-ZYfsYAGIrvtdIp_oDWCDfNzCURhp_i-MSb8m4k04PUB8Ko92T2TRqWO5jLXuNMARYIMyXoAo0JQJKA8IVMNVkh_neOChQxhOrIhRpTKUsL5SGBVggt6rjR6WtATq7K7QRr3CtQYJRTd-6A30m53q85t9lob0GWCB7TERPMUkEt9VZUmQjrxCm-5JFu4Mv_86rvVanWhrvDwdYILE-BDDGWZiUsl4aS4ROw2es7hTHlsb6yDhP6OqYj9UOCFggWP03FE1hnK4tu-L1JN-pmJepS8oadhgeReOA_ihd-e0JWCAGmJEHTHD3jXnwjEdiAe51_Kk9NkMkgSVeTo6kc6_MoQpYICmv5D7p9Zj5k1oswvxUE-83jeVgreEPvSWL6R9VDvfKC1gg1z0NaRUOiZ-qgwHJF44VxjcStga_-82SQ63qNDat30kMWCAOu-_RHsVMWAG-YdY51vuVl_k8dX8fsnWIaVnaKgqQBA1YIIR-2E94WPTLdcs31kFE6lgFTyn1L5U8cmRvz8QdNViqDlgg7cJen2SihJ21t7HSys_UFiwhYZnZMdaWzwwen5XVLxoPWCBVzTYRJ5WqALOuuTaiRWJyFPE2hfpWiHI5tovF0M4NVBBYIKEUxtCHacph4UEWFdN4ARzROz5cmChZWFj8YY8K4NpFEVggHGSy4IP9Uj-U2hqgX2-BxeS7-h1zdHvZJatCLV9TWsASWCAtoyIoXf7v_FXLFg6zWPpR6Yoah-2DcbjBY7LoRxJVNBNYIADVLfGJdWKMmEiYAjuXwFzt47WsL-mGgOSjAvqEArHpFFggPP1Dk6PzqcjULlQnU9_i-3AM_Kt20hWV8f3H1jg7DpEVWCBeVDGFvjpN1jBNMYERwJn9LerYrCIBIO-p-Pm48E3FmxZYIEgfy2lRRn8ylXQS4FQlR-Pv14RX22eDlEpDEgdmiSCJF1ggnfT8we0aQmjz4KmsLfpty_4JdWpGZ6syzWwrC7gKWscYGFggsGaHXuVMuUjOomLOQggcYUpqgqWwSAFenWbqDnxIbsYYGVggG2fdFTzlfP-2yJsXcJvZJ141LEp58vflp2ViVFrGgo1tZGV2aWNlS2V5SW5mb6FpZGV2aWNlS2V5pAECIAEhWCD59OMwthHb5EgMF7Kk0GYsFr7wjNv9pt8UnYDpIIgyvyJYIFGYsxPIOf2M3xv7bg4d_khEJqq-2hiB764idiaVD74KZ2RvY1R5cGV1b3JnLmlzby4xODAxMy41LjEubURMbHZhbGlkaXR5SW5mb6Nmc2lnbmVkwHgeMjAyNS0wOS0xMlQwOTo1NzoxOC42MTg2MjYzMjNaaXZhbGlkRnJvbcB4HjIwMjUtMDktMTJUMDk6NTc6MTguNjE4NjI2MzIzWmp2YWxpZFVudGlswHgeMjAyNS0wOS0xN1QwOTo1NzoxOC42MTg2MjYzMjNaWEDb95rW6qUCUhUucTeYXcEh1BqnovonJqQma99FKwyvgKadAK7VxUcrSNDELwTyThqCZkgeBTcjH0_cpsbozAUm`; const owfIssuedCredential = `ompuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZge2BhYZ6RoZGlnZXN0SUQarQOuY2ZyYW5kb21YIJJDFGySlDdQos63SVlfnNFcXox10_GKD0gVPpiNzMA-cWVsZW1lbnRJZGVudGlmaWVya2ZhbWlseV9uYW1lbGVsZW1lbnRWYWx1ZWNEb2XYGFhtpGhkaWdlc3RJRBoWQ4MZZnJhbmRvbVgg8jIFlQJA7gZQ-rgid9OjUzugTg7A1z_Vh_t2YCZjQypxZWxlbWVudElkZW50aWZpZXJxZmFtaWx5X25hbWVfYmlydGhsZWxlbWVudFZhbHVlY0RvZdgYWGekaGRpZ2VzdElEGj6ZAm5mcmFuZG9tWCDjAjQSYlJF4M-27VH4s6uZveiwSAC7NhlRpk-D4ov_l3FlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1lbGVsZW1lbnRWYWx1ZWRKb2hu2BhYbaRoZGlnZXN0SUQaTZ4X2WZyYW5kb21YIHEYDFRoIMg_JcERB86_zimbQHmGyu33_aQKMdNOLYLqcWVsZW1lbnRJZGVudGlmaWVycGdpdmVuX25hbWVfYmlydGhsZWxlbWVudFZhbHVlZEpvaG7YGFiBpGhkaWdlc3RJRBpQT5WHZnJhbmRvbVggj871VAkkyIdNcMfnlXa37z9Wyt3kYVEw_Xc8axLSHCJxZWxlbWVudElkZW50aWZpZXJ4HnBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlcmxlbGVtZW50VmFsdWVpMTIzNDU2Nzg52BhYbaRoZGlnZXN0SUQaizOrRWZyYW5kb21YIBUo2JqNtOhB7ZqoX4J1wv18_WZcFdnwXlWvyf5MscwBcWVsZW1lbnRJZGVudGlmaWVyamJpcnRoX2RhdGVsZWxlbWVudFZhbHVlajE5OTAtMTAtMTXYGFhzpGhkaWdlc3RJRBratVPrZnJhbmRvbVggQawIkwMlAV6gyjBb_jpdDHW5oQEJaKD3ZHH106lH6zVxZWxlbWVudElkZW50aWZpZXJxaXNzdWluZ19hdXRob3JpdHlsZWxlbWVudFZhbHVlaVBJRDowMDAwMdgYWGqkaGRpZ2VzdElEGkLsnYFmcmFuZG9tWCDLVMKCeeL2zCHQAV1_qgf4rOPdAlXcBjivQrfTZ7qYb3FlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnlsZWxlbWVudFZhbHVlYkdS2BhYcaRoZGlnZXN0SUQaDiDlomZyYW5kb21YIM6mHAJ-p20r2KepnGwwdVBL5PsFMTc4F1_n4w84KayvcWVsZW1lbnRJZGVudGlmaWVydGlzc3VpbmdfanVyaXNkaWN0aW9ubGVsZW1lbnRWYWx1ZWRHUi1G2BhYcKRoZGlnZXN0SUQamPmaNWZyYW5kb21YIAOiWcM1MdMzlEuvIZW71tUw8iDLr3ZRaCwxrHGuHQgLcWVsZW1lbnRJZGVudGlmaWVyb2RvY3VtZW50X251bWJlcmxlbGVtZW50VmFsdWVoMTIzMTMyMTPYGFhmpGhkaWdlc3RJRBoGMc-lZnJhbmRvbVggpL5dxbd4r1QqP_06Ug8ZgAUcBwMIptxDUHjm69UookNxZWxlbWVudElkZW50aWZpZXJtaXNzdWFuY2VfZGF0ZWxlbGVtZW50VmFsdWVg2BhYbqRoZGlnZXN0SUQaINKc32ZyYW5kb21YIGu-ThkwXOS9szcIo9iCIVq41g3_jtsxZIkzcqx1spd5cWVsZW1lbnRJZGVudGlmaWVya2V4cGlyeV9kYXRlbGVsZW1lbnRWYWx1ZWoyMDM1LTA0LTIx2BhYZKRoZGlnZXN0SUQa8mAZzGZyYW5kb21YIK0x3pmENrar6VRbiGOLEoINrxHLdyA5Rs_XyvLn5yYncWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE4bGVsZW1lbnRWYWx1ZfXYGFhkpGhkaWdlc3RJRBoHNpFVZnJhbmRvbVggbg8djI9CTgbA1ZzhhE--aPw8v3Ly5o6gutGJkGKUkp9xZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMjFsZWxlbWVudFZhbHVl9dgYWGakaGRpZ2VzdElEGpUekNRmcmFuZG9tWCBY8L5hfuoqyjdrwODKVuYzQS8ucFOv_ijxMcjOIzEqv3FlbGVtZW50SWRlbnRpZmllcmxhZ2VfaW5feWVhcnNsZWxlbWVudFZhbHVlGCPYGFhppGhkaWdlc3RJRBqInvcjZnJhbmRvbVggk3McQQFc35N5koe8tAgHGMKFxFXOCv2oDVtbECO0eBRxZWxlbWVudElkZW50aWZpZXJuYWdlX2JpcnRoX3llYXJsZWxlbWVudFZhbHVlGQfG2BhYXKRoZGlnZXN0SUQa-uKCl2ZyYW5kb21YIGRuD5wrBEM6k6ZsgBrXGyu-Xf0PtnVbNn8OfpUJN5nFcWVsZW1lbnRJZGVudGlmaWVyY3NleGxlbGVtZW50VmFsdWUB2BhYaqRoZGlnZXN0SUQaZHpCaGZyYW5kb21YIAyjJuWak4tG7Kz3rHnsilMbbNiWo2npmq0CQylcjDUqcWVsZW1lbnRJZGVudGlmaWVya25hdGlvbmFsaXR5bGVsZW1lbnRWYWx1ZYJiVVNiR1LYGFhmpGhkaWdlc3RJRBpBM1WaZnJhbmRvbVgge5rAKOIDxWw61txIN61ah3VZJcga8MomBOndkZJFFANxZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2VsZWxlbWVudFZhbHVlYlVT2BhYlqRoZGlnZXN0SUQaKxeBFWZyYW5kb21YIHyGJC4CkXuPk5mEACRkbFnHyH3i-qcTyfCs4Tf7hweBcWVsZW1lbnRJZGVudGlmaWVycHJlc2lkZW50X2FkZHJlc3NsZWxlbWVudFZhbHVleCxSYW5kb20gc3RyLiAzLCAzNDc5MyBNYW5oYXR0YW4sIE5ldyBZb3JrLCBVU9gYWGukaGRpZ2VzdElEGvlg1P1mcmFuZG9tWCCe_lwIQoLhJh2F5-KOnwgxgcVPqM9_zHqAJXIO1Qz903FlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ5bGVsZW1lbnRWYWx1ZWJVU9gYWG-kaGRpZ2VzdElEGqmRrPlmcmFuZG9tWCASu4-0Y5ipP8yJA0UrB5dfZnxxxQYWB28lnu3OSgAvBHFlbGVtZW50SWRlbnRpZmllcm5yZXNpZGVudF9zdGF0ZWxlbGVtZW50VmFsdWVoTmV3IFlvcmvYGFhvpGhkaWdlc3RJRBpFffVhZnJhbmRvbVggCtto7Sju06afqL68vYBP12IF900ZcqIxwKGPZM7x-lZxZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0eWxlbGVtZW50VmFsdWVpTWFuaGF0dGFu2BhYcqRoZGlnZXN0SUQaZDIUOWZyYW5kb21YINqQVCe267_jy132OssXXxRZlCDl-ZaOox1IGkEK8oUacWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2RlbGVsZW1lbnRWYWx1ZWUzNDc5M9gYWHOkaGRpZ2VzdElEGk34JwBmcmFuZG9tWCA8C6ej70LEGlzpE2v9ha1tPaXjmI6S32P6SnPxXlTenXFlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXRsZWxlbWVudFZhbHVla1JhbmRvbSBzdHIu2BhYb6RoZGlnZXN0SUQa3nOrhGZyYW5kb21YIK0qt-tv77vqf0foZAA5rU2-KPReWB57syv9VFrpLseScWVsZW1lbnRJZGVudGlmaWVydXJlc2lkZW50X2hvdXNlX251bWJlcmxlbGVtZW50VmFsdWVhM9gYWUerpGhkaWdlc3RJRBpIMsSCZnJhbmRvbVggVlkf3MxFV65DuUU_zlKSZ3x_gXjsoPXRM_jckXqONIhxZWxlbWVudElkZW50aWZpZXJocG9ydHJhaXRsZWxlbWVudFZhbHVlWUdI_9j_4AAQSkZJRgABAQEASABIAAD_4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb_2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P_2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P_wAARCAMAAgADASIAAhEBAxEB_8QAGwABAAIDAQEAAAAAAAAAAAAAAAIDAQQFBgf_xABAEAACAgECBAQEAgkDAwQCAwAAAQIRAwQhEjFBYQVRcYETIjKRBqEUI0JSscHR4fAzcvEkYoIVFjSSQ1NjwtL_xAAYAQEBAQEBAAAAAAAAAAAAAAAAAgEDBP_EACERAQEBAQADAQADAQEBAAAAAAABEQIDEjEhEzJBIgRR_9oADAMBAAIRAxEAPwD0oAAynRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACKdcw8kVzaXqZbgkCKmmRc1foZosBr5NZhx_XJR9TVyeMaXC055Iyi_2oO_yGjpA0I-LaPKv1eox7b7yovhq8OX6JxlXRNP-A0Xp2ZKfjQ6TXfcsWRXTaTGiQI8RlOyhkDoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAI8SK8maME3Jql1s4fiP4gxwg4Yqc724dydbJru_FjB_MzS1fjGl0jXxJ81sl1PG6rxXVahrjyyaV0r5GjPJOcnLJNtvz3Mbj1Of8Uw42sWDij5uX9jlavx7U5205rHHoonFllaezoi5thrdnr88-ebI_WTYeuzSVTzzfrKzncbHF2A25am_qb92Y-OavF2M-9e4ZLrY4974bYWdxdxk17mvfcBrew67PjvhyzXpJnU0_4j1MIcGVrJXJvmedTslxdgPY6f8AFGO7y4pJ8vl3s6uDxvQ5VcdRHvex86UmyayvvH1A-pQzwmrg1L0ZNP8AM-Y6bX6jTP8AVZJRXknsdvSfibNjrjUclewZj2oOPovHdJqZKPHwOXJS_gdZZFJXHc3WWYkCMepIpgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhug3RravUR0uF5JyjFLzMtwTzarFgg5ZJKKXc85r_xQ8blDTYqXSUnuvY4_i_jUtZlfAko-fU5HFxMlUjf1Xiep1VrLlk4t_Te32NL4jI8XCuSvuQSk-qXqGpObZFyvqY282_ci3fRICTdGOLsRBtjNZbswTTsGFmoEuLsZavp9kRao3WWYzxdjNrzIbdAMbqzgU3s16Ed47ESSnXQWEusp32H8Q3GfYdDGpcW3IylTuJAN0Bsxy7bLfqdDQ-N6vSfKsjlHpb5f2OTGV7plnCpKwPe-E-MY9c0qqbjbj5Pkzr8R8tw5smmyJwk4tO00eq8J_E6dY9b8u6qa5e5suMs16lOzJVhywyQ4oSUl5plpsupAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhugMmG6McR5_xzx6GkUsWnleWua6GW42TXQ8T8Z0-gh81ym-UeTPD-KeJ59flc8rXZLkkamozzyZXOcnKT5tvcobslsmJNW0iTVc2ZSrruRlLcNJS5bFZNLa11MfS-rfmwImUr5CUuWyXoR4uwGTEuhn2bAEUrJb-gG_kA37_Yy1XVGDLjXQCDVGCUq2I_f3KlTZjCVEuZFujIsJcCcZb7kDLd8xYSrOHuOHZ7kOLyM_E7EqR5FuPI2yG1NIxGXCwL5fNG2jEZOLqtgnUrfJkpVGTVWB1fBvFp6DPvcsbVONnutNqceoxLJBqn5nzCMrVex1PCfFJ6PKo5czWHk-tBlmvoPF2MnO0mv0-qUVhzRk211N-D4k2jdSkACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMuRlujz_4i8W_RsTw4W_iSXN70ZbjZGt-I_F4QT02B8U9-J1seTlkcm-b7tmczcnxSk5Nvq936mv13JUy1ROMd-ZX19GS3k6AnkdL1IcJc8cpJUjHwmuewbZituuhkNUYTvo_YMY4e5hqiai2Z4H5MCr7ko2nfIs-GzLjfmvYKxD7-6I8Pcujiv9oxwpLzBirh7mGqLuvJ-5n4LvevcMk1Tw9yOxe4JlkNO-tr1RlrfWtPgY4Gb60zS2Ve5B4VukjNPWtPhQ4e5svDuwsdKuZRjX-EzHCbiwyre12HwSdMaiVMSiuHZm38FtbIrngpWbKeta_FtVE-LblyIuFPmY-lrqupqWHOnyLVPaiv6W09zLV8gy3G3otZk0zbxzaTatLa0fQ_DdZHV6SE043XJPdex8x4uF3TrudjwzxvNonCOS8kI7K-i8gfX0ROzJz_AA3xHBr8HxMUq80-aN7iKlSkADQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKNS1DE3KSRluDS8W8UxaPC90pNbdX7Hg9TnefPPLLibb5y5lviutlq9RJtt77dtzRySpLuStic3RXxdjDdsJWwJJcbN3Fg4Ypy2tciWj0zlUnH0OnDS39dOyOr-r5n40sWCTxrb2IZMNXXLzOx8Hiagm-_oa2TDbry6E67ermxwKTbe6Llpbfyo6WLR8T-bkb36PGKW27MvVjJxHDWhdbX7kv0NdYv7Hdhp41yJfo8eyJ1vrHBWja-lGP0OfX-Z3ZadOqS_gRencttvf-w1s4lcP9Ea5V7srjpXO1H70d39E-K_mfyrolVlkdK8fJJfkNZ6xw46Bq9_5kZ6Vxq29_M7zx0um_myuOlbduL380PanpHLxaTauJ-xc9HwLeafZo6i0kmv67mf0bhfS3_28hp6xyFpZPkkvcxLRzkt2vbc7fwEqrajMsNrbmNPV5yOhbtyd-hNaKKW3Xsd79HcVS3ILTpc9x7U9I4601Kop0Shpkrbilb2pHX-AuiS9EZeNIapyVp0la2spnpN_pOw8dPlzK5YuQ1lmvPZtJHyNTLgcWekyYbZrZtIpxaSXudJ0jrmPOtNr0Ip0dDU6RwVpbHPaplyyuNljPFs9jMZ0QMppF2aiXHT8M189FqYzjJpXvufQNDqI6jBGUeqtdz5enTZ6X8NeL_DyR0moacX9En0fkS2zXtwQhK4J-ZMqXUgANAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHJkjjVyaXqeZ_E3iVQ-Er-Zc-R6DUS4IOfypJO3Lkj534rqZajUTm5OTbIVGnJ8WS31ZVm-uvIw5dupnv5hrHDtzNzR6b4mVbWUYY8c0m078j0Og03w4J10JtxfE2rdLpoxhF1yNyOLmZxq0XpUcLdehQsNJ1zIx0zb3pV0o2iSVGCpY0kW8OxksC5EeHuSASsCPCS-EqJJUZAgoV1M8KLOHuSBbingXkvYlwKL9SwBNuo8PcgopFphqw3FbSfMi1RY1RgNQIy6E-HuYaoCDVEGqLWrIAVtWRaosaoi1YTZihpNFcsW5sNWGrDHPz6ZSjvbvzOFrdK4ttI9RJVuaGrwKcH1Z05v6jubHmJx78iLVGxqYOE2mqKWrSO8rzWIFuGVS4irqZTpm2ayV778O-J_pWFQnL54JJpnoE7R818K1stHrIZLpWk_Q-iafJxwT2dq9naMlxtXgApIAAAAAAAAAAAAAAAAAAAAAAAAAABGbpbEinLLhS69urJo5f4g1C0-gnJrik1snyXseByyuTkzufiXVzy6rhk04pbRT2ieey80Ysh8z5bIm1boklwYuHqzOBceVIDe8O0_zcVnfww_VroaWgxbROnjVI49X9ejiZE4x57kyMepYlRzWyATSoCKVkySVIyFyoJWTMpWZ4QIk0qCVD3X3AyAlfVexnhYCJlqwlRkMqAMGW7DQw1ZkAVgsI8PcCvhItWWACgi1Ra1ZACtqyJY1RgIVtWjVnHnubZTNW0BwPE8CVtKjk9T02uxcUGzzmWHBko7838efqfqlqjHUnJWyLVJHWVysxbhd2j2v4Z1s5aP4cm3wvzPDw-V-p6L8MalY9TLHK6kuVkqe5jyJFON3C-hcVLqAAGgAAAAAAAAAAAAAAAAAAAAAAAAaPieX4OmnNSSpPZ9Tdas4_jyrw_NKKcpNc5bKKJrZNeF1mSeXPKWSTlJvds14_Vv0ROXMrSuL9TFJJ236G1oo_OaxvaGPG4q_7k9fFc_Xc0keKHkbqdo1tMuGPobXQ4135WxMkY8iyPUlSQACoknZJKzBYDQwZMxDWErBMg3YBKySVEU6JgYboxHqSAGGrIkyCVgl0CVgzw9wMBqiSVGJAYaorLG7IN2BCXQraplxDYCmfIwSasNWGVXPkUtWi6XQrbtBLW1EeOFHnddjcZtnpmrRxPEcW_EunQ6cX9c_J8cdqiM-TLnFU15bopl09Ds89Sxu16HR8Ik1rMaS-mVnNxq0b_h83i1ePJHmmg19G03-mnTXZmwaejfFii1ai-jXL0Nw2IAAUAAAAAAAAAAAAAAAAAAAAAAAAMSOJ-I5uWgnjh7viqjtTV0ea_FOWMNM8cHxcT3d8iaqPG9WOjCW77IxKVGNZh8z9DpeHc0zmY-Z1_DVdMnr4rn67eLkXlODky-XQ416U4ci1OypOkTTokTSsJWDMeoXJiRNKiAAsCVkEr6kwJ7fvL7kE0gAJJ2Y4jAToCe3mCBJOwMgEZAkwlW12RboyYboDLdgrAGW7It0ZIy6AOLbkY6AAQaoi3ROXQiBVIq6ls-RDh7hCvI6Rydars6uXkcvVK7iVE9OJNVIqa4X6OjYyKsjvzKJ_ntZ6HmOHZbl2B1JPyK1yXqSx8gPo3hLT0cZKUt0rT6HQSo5fg8pPSRjN_PFbNqnT3W_VHUibEMgAoAAAAAAAAAAAAAAAAAAAAAAAARmrR5b8VwUMUafVukq3PVSPJ_ipLgUuabpMmqjyklSfcwlbZMxBbMxpFJL1Z1tEq4Tlrdr1OponbS8iO18x2sDqF-ZcUYeSXkXtUcbdehYlbJp0V9SSdmC0zHqRTsyBMAynQVqSVGUrASsGiVgmA2XUATIJ0Bnh7iJlO-lGQW4jESJGG6AiYassIAR4e44STVACsg3bLZdCDVgVy6ESwrAGG6HF2K-LsBhuwRkRCFedWczO9r8jqZeRzNT1kyonpyNS6m2-pTldZGW6r62VSdv7fyPQ8yEeT-5OK39yEOZZjV-7QHv_AMOtz8MxcVfLsmjrpUc7weKWkhJLh44pyS5XXM6KdmxDIAKAAAAAAAAAAAAAAAAAAAAAAAAEZVW55j8UprApU3cqt-h6hqzi_ieCl4ZN8qdk1UeC4vn5CKthqmgnUmY1LD9dnZ0CtKzh43xTo9B4ZDZbEdr4-upCO12WN2QirRJOzg9DKdMmQJJUBYWFZZCSoCYILJfIOddC5BcnZNKimMuexYnRomGqMN0ZToAADLNAAzHqTJrbdYCVhOiTdG4xFqgSbojKRojIwJO2YlLkJMBumVp2G6Km2zKMy6ETDdNCfIlWoSVor4uxaUtUDRuzBBuzMeoSZFaOVrFTa8zrmjr4XBtFRPTz-feVeRXPkvVfwJZ95N-ZVdp-p6HmQ_a9y7GrKm9vRl2nW6XcD6P4VT0GJ-cUzfNLw2DhpYcSp8KtG6bEAAKAAAAAAAAAAAAAAAAAAAAAAAAGG6OP-I8fH4ZkSdXz7nYas0_EsTzaWeOH1SVImj5rJW2R6vui3Kv18167lclRi2MKvKj1WhjWFHmdLH9clfU9Xp48OJEdunE1fHqR6Eo9TDVNHB3SSom3SK5zUI279jSzahtLo_Ky5GW42Xn3u0l9yazxTu-hyXnSVp3uU_pUrq1fZmyam9u9DURv6kvUfpEZbWeelrHxOKpNfvNFX6bl_ed9jcTeteohqIRXMsx6hNO_4nkv07Lf1N-5mOuypqpP_wAmMJ3I9gs1kvjq-R5vTa2TtNOuqN6GofW6RFdJddiGfzX5lvEjmxzVv3NnHkTRKpNX8VPzssNdO2WhVmpN0Y4iHF2IN0gSYslPlZVLKktuhRkyXdGrPKnyd_8AALcdD4qpdiuedVtuczNqUltKn60c_LrXduT-5cjnbjty1O_L8zD1aumqruebya2bfyzfsQ_S5XVt-rKxzvWvSS1Ka2p-jKp6uLo4UNXJ9eZZ8dtb7oYzXYjqubi0650zYjljNbHCx5k5pNtd7NzFn4X5mWK56b8ugjyK8eoWTy282T60RZjomUanHx4pblzdGJ8vYweV1S-d-pqr6ZLubviCrPkXmaHn3PRz8ebr6dKNvRq8sLdfMka6VfY6XgOBZ_EMSbrhkm_QpL6Fp1eGPoXFOJ_JXkXGxAACgAAAAAAAAAAAAAAAAAAAAAAABiRTqI8WDIlSbi1b9C6RXOPHFrzJo-beIaf4GrlHlTNaf0J-f9Dv_i3D8PVY5pbSXPzOCvoS8kYtZov_AJEV5s9XiVY0eY8Mjx6uCPURVI4d_XbxTInDmYyOle4ToozNtVtt1ZMjpbjT1Oa5X_M1Jycualv-7zZvRxq29n7IsWKLfn_ud_yLc3LljyyXyR4V06lT0mpndqf8j0CxqtlRaopr06i9Y3-OPMT0Oerp_wASp6bOm7g_seruHWl7FGXNgquOP8R70_jjzKxZFz4vcyoPuvVHYy5cD5Jv0izUeTBfPh9VRusvOK8DfFbS28jowdwiu5q41GW8Wn6F0ORlq_Vswbe3Tb-NG_p8m1pcznQ2NzCc3SN7G7ZcVQ5lid9KJ5ajOW3IrlLYsyOkamXJ2NtwVZpfc0Xkpu_RFmaRrTd0ayqtVPifDTXfkaOSMpPZM3mqZV8XEnSbm_8AtVnSVx65aLxTfQlHS5pOlB_Y6WLNj_8A0T_-hv4tViS-aMo_-DRvtT0jh_8Ap-dK-vkia0eaK3T39zv48-ny_TOL7WWpQfLYz3p_HHnPgZsdOWKXD1bpEopp7cSr_uR3ZQi3boonp4c-GK9mJ1rZw0cWXfhla9ToYXcLNXJiSdJ8-hZpm4_JJPfdUGNtOg3Y6GDm6PP-K_8AyZHMq2_U63jMKy8V3scx9Wejn483X0rf1R6f8IYVKWTI4-XNHmW7me7_AAxpvh-HRb2cnf3KS7KVKkWEU6RItAAAAAAAAAAAAAAAAAAAAAAAAAYasyAIT-SLb6HNz67NxXiqMOlrmbPiGT4elb82cyOSGWClCSkuxx76sr0eLidS60PHssddo05r4ebEm1T2kn5Hl3Hdb9H_AJ-Z7PU6aOXFJPyPL67T_Ay8CulujOeraeTic_sW-D4-PU3fI9ElZxPA0m5N_wCbndTsjv6rx_EWqXPma84q9q_gbM-W1e5Bw9H6Ih0UmU6JTTSRo6jLP6MfN9QLp6yKnwY1xz7MpyaiVfrcqh_t5_dmpmyrTY6gt_zZHS6DNrPn1EpJSf0nSRF7qyfiekj-w5v_ALnZiPjaqThhi4pbuuRq-J6RYNV8OMVGKiuRS_0LH4fk-JxvWPIlCnUYx2tv3s6escr3cb68ZxuVTwpLk1RfDLp9UlSSOZmjpn8N6SE4xeNOcZO6n1p-R6Hwzw3Fq_Csbmqmr4Zrml6mXmac9Wxz5aBQfFjdehOLljfzRb7o2JY8um-XJ80V1RdihHJidq1Jc10OduPRPimHzLY28Do1cMfhz4fM2ocyGy46GIv6Gtil2Lk6J5VZqGWPc0MpuZZbcjQy8x9GrkVmq3bpJs280bhS6k8enrFs05S7ckdIhzlhlmddPLzLXDDp4fO0q6Isnc5PHhtvzRvafweKj8XNL4kkrSa2TKk2p66nMcaXi2DE-GOP3D8ayQbSwu1v9L5eZzM6bc5vrJv8zayYfCv_AFDU4sWbO9OsUniyNq5TStXtyb2Os4lceu7GyvGNPmaWXBFy86L8eSEleDO49m7TONhx_rcSpO5LZnR1fhPwn8TTScW-aZl50963VrXilw50kuk48jaWRSVx3ODh1U4yeHPFPtRuaVvDPZuUH9PY52O8uuklfX8kQUKe1eyLcatEuDyObWIR28vYdSSVcyLdoDk-NR-WMvyOM1a_zsd3xdXp_c4SVJr0O_Hx5-_qzRYJajVQxwVtvkfQcWXBoMMMLlc0vojzX9DynhGjzYs6zRlwunVHcx4VFdd-r3bHXeX8Vz4vafrq6fX4ssuFpxk-SfU207ODklHGk5SSvlZ2NLN5tPCUnvW5vHd6qfJ45z-rwAdXEAAAAAAAAAAAAAAAAAAAAAACLdganiMeLSv1R53Den1E2uTe6PR6_wD-N_5I4E1WWZ5e_r2_-f8Aq3oS44JnnfxDHh1GNrquXudyN4_c434ifz45dmvzHFynk-JeBqsTkdZKzmeEKtN7nUhyM6-s4-JJWyxKzEI7KN8i3h25kqamSFXvZz80LTS6nUyw32KniVMDirRqUrkuP1Ojo18N05XXck8dEoeR0nVLJYr8W0H6biWTBL9bBcntZ56eJxbjlhKM10aPSOMkqV36sKbb-lcr33N1z9HH0Ph2TVZI0nDH1m1R6pZ8Gk00cWJcSiq2ZoPJK3sn72R4u249qqeOJ5tTHMqnhk_J3yI4FP47ksbhB_Vb5luNb8vui3Jk4YNom7VTI18kFxowN6tiKtohsjbwukXff3KMXItj1sKQyOzTy8zbyK0akt36AVtcU0ieqc3irGnXWnzRButzZxZVJcl7hMuNbBmx6ZtfCaUu1O-x0dPrsU0lJON-ZrZ2n0Nf1ivdbHSWxPrOq5_jHhrw6iWbB8-Ge-y5HJ-Fju0j1Dl2X2Emk03ji2_yN9kejm-EeHPJmWpzpwhD6U9m2dXU5IuFKvVkPiOStRe3lv0HD5jWzia5mbSxtunv-8i3BjUUkbjxpMlDFsc7dWYoo20qKYR3NmKpGNV8O3MpyJJGxKPLcqnyDHL8SV6ZnE08OPVY4-c0d_xJXppHF0jrXYn5Ss68_HHr-z1uFKONUuZHPnWOklbfJEXkaRBwuScmcno5ak4Sy6mM5ybvp0R6fw9f9NXk2jjuCjlh3OxoneJ_7jp47lR_6JvLbAB6XiAAAAAAAAAAAAAAAAAAAAAAx9Qbow1QGp4h_oRXnL-TOKledrudzWq8F-TOPjjWomzzeSZXt8F_4SnG41fM43jsf1eP_c1-R2cjpHG8Tc8ial05E8_W9zYv8HVaRLuzoROf4Q_1HD5HSHX1PHxYnRNOipOywl1CHVliTa3ItWE2YqeNND4Xky1xbQSpBin4T8x8J-Zsx6mWrA0JYntu36mUkjanjujCxpO2XJitVcPDzISuT3Nhwt8yrI7RNumK2raM9TMVe5gxrYxuybVIqxukWBUQy8kaklbNrI7NbrYZbrDVhJrdFgjHnuGWaylashwb7GxFVug4plaz4144ue7XoS-G_P8AI2Ix4bsmkmZZhrVUGuTMfDfmbLx10HCzEtTgd8i6Ed-ZY1QToDHUn0MN0JdDLcXIxPka76lrls9iuTtmsrT1qvTyXY4eiV67Gu539T_pS9DhaRcGpWR_sdPM6z5XHv8AtHpd-IsyL5G1tRr6XN8SCXDVm4lcWcneTGsn8TUQR2tF_pS_3fyOLpoVnbs7ukVaeLf7TbOnjm1z_wDRf-Y2QAel4gAAAAAAAAAAAAAAAAAADDVmQBji7ESUiIEMyvDNLyOPGP6xu-Z2zi3wykn0dHHyR6fBfyq9RLsc3NiWX5WbmZ2yOHHbd-Rwei_Gj4VFx44tV5-q5nTbs5Xh-X9dPvKX8TqFdfXLn4sTplqdlEOZenZLrLqxKjIXNEwIGE7LB1AGG2uaMgMxAjwsnw9zLdINkxXLka-RW0bLVo158wK5uqIQ5mJO2WY1TAuxcifQwlSJy6AUT8jX6mzkd7mvw7cwJp0ifU107LcbvcDYjzonweZCCtlqdMAE6M8Pcl7L3DMQSsbk9iPD3DZMQ4RtLlsT4e44e4FbVFcvlfqWz5lWXkgIN2U8XYsl0KgyqtT_AKMjn6PT8eKWRc-Jo3NbNLC7XMx4ElLTTi_3r-5efjl7f9LtJHgaR0kuJGmlUjdwcmQ7KMUbm4rq6O5FcMUl0VHL0GP4mtv93f8Az7nVO_jn48vm624klRkGEqOzgyAAAAAAAAAAAAAAAAAABFqiRADLVGAAByc8f1uTf9tnWOXqo_8AVZFfNnLy_Hbw_XOnLfkXQfDBy8kVZ8MlK31MafOuPglte1nCXHqvxx8UlDVylzjxOvudmPM5Ophw6vJUlTdqvudbG-KKfmW4c3FkS7G73Kk7ZOKpHN2lXR6k49SrqWJ0FJN0GrMR6kgMp0YAAjHqSHQw1YGOHuamo-U2sjpGlkfHOwK8UVKTsv6jGuHkW40m9wS6zCN-fuixqkW4MaZb8NVV8ysLcc_IrZq_S_U6efEopbp-hqzj0MswaUlTLMMuwyx25kYrhkn5GDdxq2WkMbtE-oEzKdFadImAMt2iKduuQasCMm3shKXLYEZ8gIyly2fsVT5FsvlXmUt2wW4qyKmQJT5kJBDS180otV0NnwJVppct309WaHiEuKW37J0NFNYvD8b5Nr-bOn-Ocm9Nqf1t-ZfhdpmjCXHyNyPyQbZzd274XGp5p91E6HF2NLwxVpbfWTZuJ0erj48Pk_tUgYTsyWgAAAAAAAAAAGJBKhxdhEDIMJ2ZAw3QboyYboDDdmDLdmAAAAHM8Qi3nvlaOmaniOPixKa5wd-3UnubHTxWTpy3klL5JdepqZtO-m5ttNtdizpR5Httef1PF-mOclzSOlp3cIp9DU8QSeqit9119i3SOoyba8zp_jz36348izGrTKMDuFs2U6VnN1ZTtssTsrSplidgZbsknaI800yUVSC2QOoAAFcm4gU5cvkUxdbEsjuzUebhVoDbbrkRxZGsqOJqvE9Qp8OJcurIaTxbM8qWbEnHzS5FetZr1kMqosjl5nLx6mLgnZP45kuNbefJfM0XkbkV6nVLgd7HGl4w4TajhbXm2JLU-0d7oR4dnuc3S-KfHdSg4vuzeea0hZipdbWnntTfI2krOfgdSTOhjdowSasxw78zLbXSyLdgTBGPUw3aAN2iDdmZdCCVNhlJ8ynL_Etbt15FbdhKiSpv1Kp8i6fU1M86g1RsmstxztfPimku50NOuPHDZ7RS3Ofn-fPFPk5K_wAjv4oxWNUvUrqYzxfUsKSTbE5fEfkkRmrov08Pi5Y4-jdv0RMm10tkm11dLDg02NdrLQD1yZHgt2pfUZMcXYyawAAAAAAAAAAEU6MAASiImG7MAZTowZSswAAAAAADDVqmZAHFStGOhbq8TwZG6fw5O065dio8nW693NnUcrxFVlxzfdJENLajT8q_qX-JxrApbfLJOq5mlpsjSfmVPjn1-V1cMtk5c3ubCdo0MWTiySfdRXfa_wCZvR6kWY2XFkXexb1KodGThzMWlF0zPD3MGZXaoKiXUy1RgBrDVkZR5bkm0iLdgUTiqObqYODvdp-S5HWao18sVL2A5mPHjcWnFfYY9NjxydLnuY1c8eF8knfSW5r4NV8zcn6FRHXTbyQ-H9LIQzuuTNmOX5HaXuZWOLi5unRtmsl1r8HxV8yKpaXHKa5L1Ls-oUFaVrsjTzZNrTtNN-n-bms9mz8HEt4pKvNlmKPE76Gros-OSSnBO-rOvhippNNb-RFmOksqeLFsbWN0iqJcY1JuiJni7GE6ASdoTV1vQDaYFLjbbvmTbtEk7bKpdPuE26wRlzRlO29mq8yM5bWGKcr4U2c3UTfHfmmjbzzVcNc3_c0M7qaa68vUqIt1XgX_AFnDV3V7eh3ocjk6SPFq1JtKl1OtwqtmKribE_qN_wAMxKMHlt8Tdehz-bSSbb5JHZ0uL4OCMG02udFeObU-a5ytAB6XkZToz9RElEBEyYSoyAAAAAAAABAAAAAAAAAAAAAAAAEZwU4uMkmn0Zz8_hjb_UZa8oyV_mdIE3mVU6s-OBrPCpx0uTJmzr5IuSUV19ziRVRV77X_ACPZ62CyaXJGSu0-lnisacZNNriXOt1zOd5i-erW5ge9_wDe79zexScnfoc1ukktrfF_I28OT9alXTp_n-bEOjoJ2W9DVx5ONtL9nr3LpStbddjmvlYZj1Ip2rJfU_QLlxIhJ3uG7Ksk6StMKT4iPxY3S3Zq5ctpxi031a5I1nqFFbtrnsmGa38mRJfK7OfqM9Oqey68ivPqG4W2nS2SfX-ZpZuJxfFK5NW9-Rsmp66a2py_EuW6j06WNO93J7107keDja51Xl1N7Bj4IWk3b2s6244SJRnwQlKT67FjzyjgTXVmhmzcWVQjvHkn5mzqH8sILZKP5kqtVOVzc2k4SXJlU_lkpRT4Jrp58n_InjXxcbg040-fcrlF004vbyKkxNqWCoybuvfmdPBmcJVzT5b8jkRtSjvyextQkmqfo9yepqubjvQyXdtXtz6lyla2OHh1KxraVq97NuGqfFcd65oizHada6Xra9TKdGpDUxnskvRmxBqStEeqpdXDYgnTrzJNWUEnTKoy4r7Mtkr2KfNhCM5KLKck6VNf5RmcqnTNXLNubj5vn7M2TWW4qyy-aO3J2au048L2tWi-btxVVu_5mq3UfavyLcq63hPhn6Vp55_iPHPjpXuq7o6EPCtUtpZcftf9C38Ox4fCoPhceJuW50zrOJYj-TqfGtpNHDTK0-Of7zNkAuSSOdtt2gANYylYSsJ0E6AkAYSoDIAAAAAAAIAAAAAAAAAAAAAAAAAACGRcUGkeKzpwzTbiox47SXlZ7dq1R5X8R4_hatTj-2t77Nf2I7-K5v61q-JGMXS4Ul96NlOnd9L3fsjRhJVGD3tt-v8AY2sa40nJvbp-df55nF2i-OR8MVG11bff-ZtwrhikvQ5kM1W2rtr3_wA_mbWDNeSK7czLNa6EepKLpIohl4nty6PzJ9CHVjJkp7c_4Gnqs6ilbfsXZMlPhRz9T88XK1cuS5t9327GyaddKZ5VJpJt9lt7JFU5Tk0n0d7bLkXYFwrhVt3zf9i3LgqKipN8XX_OhTmqjzp_8lUoPPlk5S-Xqq6G_pcTkrrhjVRRl6eEZNut_wC_9TNV6tPDicoW07e235m1PSScY8uRfDJjxP5Ip-diOdpvbn3GrnOObqvDZQzqUUqXT2IT088kHSfFF_T-8dyGXHlVS5k1p4RtlJ_jjjabR5KU23be6XXyJZ9PxzTSq3TOnPU44Ulb9Cl5rfJe5On8ccdaRwm4NpbWm_v_ADojkw8K4otrtZ2k8eSVy2sjk03G1Xzb3d9hpeMcaCl1TT619y2Ed0rqXR77G3LTSiuV38t-RH9HjSpL1Ww1zV_FlF1Jy9V0fqbuDM1u1fdGtlwbWmuXRWmiGJyjJOqkuTi3v_U2zVS47WOUZLbei1OzQwZd3aW3VdDbTsh0t1Y3Rqznu5LqjOTJwpXun1KMsuFr_OwTbiE8l09k6f8An5fxKbUtT1S4dtjLVpdN3XuRm0oxnSuTv-ZcmOapNtyXk3X2NbHjeTNDCpJccqt9H_iLJNufFunfX3NjwmCl4vj4lxcKk9vSv5lc_U9XI9bgxrFiWONJR5JdCwwlS6eyMneTHAABoAAASTsiTAGE7Ikk7AJUZMJUZAAAAAAIAAAAAAAAAAAAAAAAAAAcb8R6fi00cijbUqtLdKjslOrxrJp5xa6P-BPXxsuV4bTzua26f8m3KTiopc5P-v8AY1skI4dfkxxtK2kpcy6E1Jq1vX82zjXaVNY5PE5NpLi89-f_AAXSqGWS2-lL2tmJyr6fNNepDI1PJs_lj1b5mKb-PIre1JLZJF3E-DY0MUmr2bb6Lobn1R_mRZi-VMlxbt7J8lyb7lEsbyNuSabdJUbU3bSVKlsl0Ksz-ldF9zG2ajGKiui9EV5dbjxNJtK9ty54XljUSWHQ44W5JSk-rQa034niSqL-yK_0vJO-DHNm_wDo8LtRS9DCxpXS5l5FyOesmoa_0H9ya1GSP14Jr0pm-se5NY7T2XuarHMlrI9VXs0ZWvxye8_yOn8GNP5V9iC0-P4kvlXTp3DJNc9apX8uPJLuomfj5pcsElT6tHTeGCX0rn5GJR25JegJNclz1Kb_AFD5dJFkNXkh9eOce7X9Ddcd_chwugWYo_8AUoV9a-5DFqoSdKmbcdOm90n6otlpceRbJRkuTSJqLMVN_Fx7JIpyRj8Pj3jv0fIthBwhUlv_ABKnPgnP5VvT3JTZq3Tz43Uqvo1_Jm5LI0mjQxrhlStwkrSXQ2W2-e39Q1mcra362amaXyOVcv4knk25784tMrU4t8FJxm-RURbo3-qUlv8AKq9eZVdwjF-df59iS3lGPkq_iUynwyppbO_8-xSUJO4bbWrv3_udv8MabhWXUz-qTUV9kzgSvJWOP7cuFfc9h4PpJabRKM-rvkdOZlcur-OgADq5gAAAAAS-oiAAMpWZkBkw3RElEDJhOzIAAACAAAAAAAAAAAAAAAAAAAGGrXOjIA8v-I9J8HLHVJvd1w9OX9EciEuGUZLy_mex8T0sM2Cbm2-GDr1PEvixZfhyraSa9Ecuvrpzfx0Z_LC-b5X3EvlUa5cv8-5RH5cPFb3339yxzScbbUV0ObrK2MXy8W6brm_89Dcw0lXkaEL4HJ9Wy-GSoRn-01sTVS4uySUYS_NjFi4n2NebTfBfJG3haUfqdtfT5Eql1fGEYttLmqDVMkRk7ton2dJFbVEk0-hFuyDyUUW4m1Ri1tua_wAd29uW5R-kTSd7bWGa6DyRXNhZE_scOWsnPK1dblvx3w_NbdXsXJiPd1vix6U9w1fM5WjzOTaZsrPNOrMqp1rb4ESNSGVzXzeZasjbryJbq1qyUORFqyUOVE-zWVC7d0-uxoZ4cMm75rl5HRbTNPO0ur59ehsupsxXiuLbf3MyyWqXl_VFSlwvGuXDJJr3oxNJ0l3X5msQ1D4ZwlXZmvBynGKtNqVpv1_uXZZpQkq7L15laqKjW_FvHvaf86Ojksz2pwa9P5_wTNXPK035rhb7_wDDRbmy3K--1ef_AAc7NN2l1t_0NibXT8Hw5M-rhlxpVjdy7M9ucj8N6aOHw2MlTc93_n3-51ztzHKgAKSAAAAAAAAAACSdmSBJqwMgw3QboA3RkwlRkCAAAAAAAAAAAAAAAAAAAAACGVXil6NHivFdO2o5It8XY9tPkebyQvnXujj3MsdOJuuA8l4eDe4y4XXsXxe6Vrbbbfd_8mvrI_ourbS-WW6XclCVuK60FupjdQSW_V9iUPqUdm-nYonl_V2opu65EsVY1xbycttyFLs7pcTpV3q35ksGRJfLeRvr0ZVmVpOTrrVb_Yp-PKLUYqTf2-5lmjqrI5OpV7FnGjQwptJtb_wNlO5b_Y52a7SrSMo8SJx7k0rIU05Y-FM1cmJpJNnTnjVbMolicYuSrbZWuRcusxyp46nxNcrv8jE1U_8Ax_mjdlglKLVPfZbX9yH6NNT335l6i8YphGnFrokzcSWTfqV4sDTWzVJLlZuYcUeFNIi3FSIQxcLszHZvsX8PcilSbVP3IUxxIjxqNvoyE5b-hVxbbJLzo2TS3F6dp7p-lGrnm3KpRtVzqyGTJtUd76eZTjy_EUk01T5nSTXO3Fjl8iT6dfMy5da6XXfr_nqV8dqUZbN815c9zEPmdStNbWuv-bFSY5s5XS431Sfv1NZzag9vod_mkTy2ocNp2tu9Gqp1BXf1NtteaNkZTUZKht53_n2_Mu8MwrPOeScW1JUn2NTFilq8ijG1jv6vsd7TYI4cEYxboq38ZI9H4dD4eixRbb-VPc2SvTrhwY15RX8Cw68_HG3aAApgAAAAAAAAAABLi7EQBluzKdkQBL6Q3RhqiQEAAAAAAAAAAAAAAAAAAAAAA86_rkvJtHoJdDg5P9Wf-5_xOXk_x18f1z_EdP8ApGLlutkcKMuDK4tJ26vzXkepnHihJXWxwvEdIuD4sKVdOV3uc-bquolHIt09lz5l_wAXhhwpJTlz7HJxZHGS496Zcs8ud83fv5lWayV0FNc20vUjjyJyey36JWzS-L8_OXuW4ZtZVwujMVrpwzcFx3lkfk7Jwk3Kkn83Vs18Sjix8cpO5bW-pJ5uJJJLn_l9ibNXLjqY8kYw3fv5lksijDif2OXCfxF8spSfWuv-epL4nxWqvguopPm_UmzFS63-J2m64vJPkRlLZbbc137lLk3BLz2S8_7cy3E2pNKW72cq32Js1UuLseHfm2-tmIxVWvP-ZKN_EeNN785XuY4rWye--_oaqXUXjSW_m1ZCLcZJbST5OyyeVRja6vbmV542pZIN3HcyzS3Fm1c0vJxKW7fDylyZVkytw4nFra2an6Q6bd8S5P8Az_PuajrpfmlxwatxyRfQ14zcnUqa8rr_ABmZy48fHSfoa3_dclvW5WIt1ZqEk-Umn-y2a30003KL3XUtyyjJK18y-lrajTeR70-u5UibW1kkoyTvn_DoQyZeJxeyp80a-TJ8u6dN9DCmowle_n99zcTanly8SSezi90U5JPO-DGm5N9PYryTcptreTVPudTw3SxhjjN7ykre5XX5Dn9q_wAP0ywYlfub5hKjPU427XT-sejhtBLyJGEqMnrkx5AAGgAAAAAAAAAAAAAAADKVhOjAAAAAAAAAAAAAAAAAAAAAAAMN0cDJ_q5P9z_id6XQ4M_9ef8Auf8AE5eT46-L6j0ZqZ8DlCmr2N3hIZIX1_I4S479POa7RcKeSGzW7dGmstQ4ZL03PR59PxKrT_M4-v0UoXONLsduep8cbGvjnSbT67FuLJKN715s0o5Gur9i_FPiaf7vQ3Eyuh8Xbie75e5NfMlF7OT3b6mnjnwzjx2n3Nl5YpXs7XX_ADuTYtt458buVRxrerJY5UoSUapbRS8_-fyNRz2uSW7_ALFufJU3Lbk2_wCBKpW9ifzRknuoqt75_wDCJRzNZ5q24qqt97_oamF2nbXn_n8SuMuLUtrbcnFS460cvA313f2MvM-K063_AKGi58Tnts5L-hWsspJxWzqvzf8AQY32b3xOSra7JKbTmmv78ihzTrbnQy5KbhfPb-RLbWMslPE05Vt_Y57mrfF039i_WT-Hu1ydmsual5Wi5EddIwyNNvp1_mTb4qdurKsb4YNUtvP2K5yUG4vzRWIlSyNJuLr2NOUlFLfZ7mM2RtNv6pVuVJtR8-pcidWuScVbb7-6_qQ-LL4bhFO76EJS5KPT_P5HS8O0TTU5U5NbX0NtkjJNqfh-iqUZ5Lcq6nXxQ4Ukn-RjHj38i7GqTOHXVtduefxnh7mFzXqiZBc16olXT0idmTCVGT2vGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMuhwcn-tk_3P-J3pdDg5f9fL_vf8Tl5fjr4vqQnHe7MwMt0ed6VEldrka-fEpw4XfelZuyj3K5R5bmy4izHmtdoeBtwp9kc9xlje-x6rMre_ocrWaPiXFFXzO3PX45XmOfHJdV_nUsjkdclyS39il4pQXzc_Ip-JKLdPmVZqHUeZNRvo7r8yW8sdvdvdv3_uaEc3BGuezLsea4NeRlitbyyW-F-RsY3TXt_Q5byXSvqWvUNVW2xGKlbGPUcU6rouvoWSlsmumxo4JcFv0JrMmq5b7_mMbrfjkcm210Tr7GJ5ryqO992aU9TTTi-lkJ6hp31TdjDW1qsttLuynFPdRS5r_wDqazyuVbkfi7yr0RsibcbGTNUUl5bGtmnxP0X8inNmtorStXLkVibdT4uKPuQS4nXmSWNydJWdPTaJLGm9nzNtkJNR0Oi4HxZK4vJ9DsYoJcklfkYxYqXOuyLox2deVnHrq66yZE8a38i7h7kYR35k-iIdJMQIf1LZ8iiXMFmvSx6kivHkhOHFjlGcX1i7RYeyV4gAGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMN0QyZ4Yo8WSUYrzboCw5niPjuh8OtZcilNfsR3f9jjfiD8QOHFpdHNrpPJF_kjyUceTVZ1CNuT8w2TXotR-Kddrs6waDGsXE6Te7_sdPGpKCU5cUur835mj4boYaPFcd5vnJo34Or9TzddS38duebyvSpE-HuYSsylXI5u6PUjwbNci5K-xgDTnz5WamSDW2zXc6LVPbkUzhTpq7NlxFmOXqdPGWN2tuzZy9RpXGe0ft09T0XAra8ir4SS2ivsXOqjHl3Br7EUpQbSfPmegyeHxnck2752aWTQuL-WvdlzqI9a58pOrfX-iM8bs2c2mnBJU-ZWk0nad1b26G6yzEXkd-r_AJj4m-17-bJvhv2HEr_zyGlmKnkbb2a26mOJl1qmtvdEE6Uv86GysUuTp0jCTbd9S6MJ5NoRbLsWhy5WnJqNrqLZGyWtNQp-Zs4dNPLw7Uu5vYvDljfFTfqjbhhjvwpLrsuZN6VOa1tPpI4nxp_NfkzdhHby9CyEJckl3v8AsW48aT6-2xztq5DHCk7d9zYjHbmYxwpvl7IvhCrpkL5RjHZ78zP0k4VTSDVJBSmfMon1NiSvcoyKkB5LFq9Z4XqZRxZZ45Re6T2Z7DwL8TQ1slp9VFY8z-mS2jL-jOV4jo4avG3KlNcpJHn4xnpszi21KPVHpnU6jydcWV9YB5DQ_iucMMVqcXxKVcUZU2dfQ_iPw_VyUFkeOb_ZyKvz5FyodgGE7VoyaAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtq9dp9Hi-JnyRhHpb5gbJq63xDT6HF8TUTUU9kurfY8v4l-LMuRyx6HGsa_flu37dDz-q1WbUzU8-SWSfnJgd3xL8VanLPg0X6iH7z3kziTz5c83PNklkn-9J2aydL1JN0BmT4jreBadcDyPe2cZuz0fga_wCij6nLy_HTxza6PRmcKtDI6g2MCuNeR53o6bUORcVR52Wrp2CgdDPDvZZw7cwNdqka-RVt16G7kha5lM4bcgNOULau1W2zIcC7-rNiSvazHD3CLMa7hapW_bkRyRdWvmXnZsvGr-VcvszEoXTVp_YJav6PGd2n3sqloE38tO-bs34pp0lxej_ky5xcnsn7No2XF-rjS8Lxutl3K_8A0mMuSW1Lc76wp_Tt5k1Cl_8A6v8Aqb7Vl5kcFeCRu5JcIj4RCDpwW_n_AJ2PQSVrl2VIqn9UnS33p8x7VORx46PHiSUOi5pk1CuW_naNualTul78iLjuny_mNFSSfn6UZWO7pv7liVck_ctUWzLdVJqpJJbFkI78yXBtsXwhwNVzMVJiChb36FqVmeFkg1Hh25kJFvRlTVgVzNXUG1PkauX5gItWkcLxrEoZITS8zvRVpehx_HF8mN-bL4v6jubHGToN2YB6nkdvwb8QanQtY8jeXAv2HzXoe50mpxavBHNhkpQktj5W3Ukbmi8T1Wgd6fK4N81zT9gPpwPJaL8X8oazD_54_wCh6LReJaXXQUtPlUn1jyaA2wAAAAAAAAAAAAAAAAAABHi7HN1_jOj0Py5Milk_ci7YHTbo1tbrtPosfHqMigu_N-x5LxD8V6nI2tMlih582zgZtTm1U3LNklN-bdgei8S_FmWcnDRL4cV_-Rrd_wBDzuo1ObPPjzZJZJPq2VcPccPcDKVJsN0RkqHMCUXaBFOkSAHoPw_PixTh5M8-dHwTP8HXRTfyz29zn5JsXxcr02ZWkRxE8vJDFzPM9bZhyotKYci4CfWyaVFUORcBhq0iE47FiVgDUlB0VcL6P2NuStFMoqgKWq5pe5Yla5_nZNKzHwkvp2sMs1VwJvfctgmt4pP1Zlqu5mHMFmp8Pnv7GXbWzoxxDj9wWai1xLp6tFM4xbpNvskWSbkR4V029AmzFWRW128tyDjStXb82XtURatoN9UYRpc37iKtkyWNWFCVLYtSsQi0yxqgIQ5bmW6RPh7kGrAiVT5FjVlcuYFU5b8jXyO17F7VoqyK0BCHJehwvHp3mxwrkmzvSfDBt9DyetzfH1M8idpul6HTxz_py8tyNcBuiB6XmSlzREACadk4ZZ45qWOTjJdU9yklxdgPReGfijVadqGp_XY11_aX9T1eg8X0niEbwZPm_cltL7HzLi7FmPJLHLijJp9GnTQH1cHhPDfxRq9NUcz-PBfvfV9z0mg_EOg1dR43im_2Z7fnyA64MJ2rVezMgAAAAAAwnZByUVcmkurZxfEPxNo9JxRw_wDUSW3yvb7gdziOZr_HNFoU1LIsk1-zDmeP1_j-t1uzm8cP3Y7HK43N3Jgd3xP8S6nUuUMLeHHypPf7nCnllPmCLdgG7MAADKMAAAAHVkuLsR5NGU6AkZjJxkpRdNO0R4uxkD1-k1C1Olx5F7ryZs4uvY8x4PrPgZ_hTfyT2V9Geqxq09zydc5Xq8fUsWx5F3Qr6FhLozHqXFCVlidAWRMtX1ImYgYKeHYv-pEJcwKUmjPWyZBQrkwDVGGk0uhYR4PNJegEUqDTfV-xLh7mNgK_u_VkuHu16FiSRjh7gVuF9Q1XImAKuHuWwjtzBOHyr1AmlQMgCE3TSIydIk1TKwMNWVz3Jy5Fbdv0AhLoUtWy9q0U5pxxwc5tKKVtsDleN6j4Om-FF_Nk_h1PNuRs6_UvVaqWVppPaK8kardnq45yPJ3dpJ2YALQAAAAAAAAypNEvieZAAdfQ-NazQpLFmbh-5LdHotB-KtPmSjqoPDLzW8Tw6dE02gPqmHUY8-NZMU4zg-TTLT5fpdbn0s-LT5ZY32f8j0Oi_FuSFR1mJZF-_HZr2A9eDS0Xiuk1qXwMsXL93qvY3E7A-ba_xnWa9tZMzUP3Y7I57dc9wQAy3ZgAAAAAAAAAAAAAA62AJcXYiAJcXY9P4F4ks0FgyyrIuTfU8xHqThOWOalBtSTtNE9czqK56vNfQ0qMnG8H8YjqYLFmpZUvudnoeWyyvVz1OoJWSTsxLoI9TFJpWWRKixK2BJqyHQmlQasCpqgnRJqyAFhhqyKdIkBjh7jh7kk6AT7IJX1ox0ZPqQCkGqCVkzDVpAIqkWGQ1QBOjDdDoQ6gYboiG7AEG7ZFq2WJUQAjJUjzXj_iPG3pcT2T-drq_I3fG_FFpoPBileWS3a_Z_ueX6nbx8f7XDyeT_IgwAd3AAAAAAAAAAAAAAAABLi7GYz50yAA2MWSUZJxbTXJo7Ph34k1emqOaXx8flL6l7nniayNARZgAAAAAAAAAAAAAAAAAAAABNOyAAthNwmpRbTXJrmem8H8b-JWHUtRl0k9kzy4J65nUVz1ea-kJ2rQas8h4Z47k0zWPUNzxcr6o9Rp8-PUY1PHJNPlR5uuby9HPUxelS3dk4eZXHqSJdFydmSvi4X6lvF2CekJcytqi6UeW5WlQbLqBLiE-RENWGOS9CBLi7AR4uJWk_dGW7It0SSsDBNKmEqMp0Bnh7iRluiDdAQ6sg3bJkAIN20TBhukAbo4vjHiy00Hhw08zX_1KvFfG441LDpWpS5Sn0XoealJyk5SbbfNvmztx4_9rh35P8jM5ucnKTbbdtvmyEuhkxLod3BEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNGAAAM8wMEyBKPUCRsaXW59Jk48U33i-TNYGWStlseu8P8dwahKGSsc_J9fc7MZxmrTPnJvaLxbVaOlGbnD92Ts5deL_46zyb9e-StFkepwdB-IdNnShlk8Uv-7l9ztwnGaTTW5y9b_q_xY1ZBqywg1RlmCtqyBc1ZVJUzHSVgGWqJhnsrJR6mWrMpWGW6zw9yQMx6m2JYlHluRlyJFWWWxipcVy6GDW1Grw4I8ebJGC7s4mu_ESa4dJFv_vlt-RU5tL3I7up1eLTYnPLNRj3PL-J-NZdS3jxXjxP7s5ufUZtTPjzZHN9L6FR258cjh11cAAdXMIEzE-QEQZ5GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGeZgACXD3McxyASVEiPMJWBIAAZNnS-I6rRv9RmlFfu819jVBlkrZbHpNL-LM0aWowRkuri6f2Z1tP-IvD86XFleKXlkVV78jwoIvjlXPJY-lY9Tiyx4scozXnFpoOSZ83hOUJcUJOL806ZtYvFddhf6vVZEvJyv-JN8ap5I983YTo8TD8QeIw__NGX-6CJ_wDuPX__AMX_ANR6U949qnaMppHiP_cPiHSWNekCqXjfiEueokv9qS_kPSnvHvFNM18_iWl09_FzQg_Jy3-x4HJq9Rl_1c2Wa8nNlXF2NnGl7x63VfifSwuOCE8z864V-f8AQ42s8e1mdtQaxRf7vP7nKTowXOJHO92pTnKcuKcnKT5tu2RAKSAAAA3QasCLdjkZexEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcPcfSRAEwR5hOgJAxxdjIAAf0AAAAAAAAAAAAAAAAasAE7Bji7AOHuRAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLMAAAAAAAAAAAAAAAAAADLAwAAABlKwMAlw9xw9wImU6MACXF2MJJ9jAAlw9xb8iJLi7AOLsOLsLHF2AcXYyE7ABujHF2MmOLsBlO-jBji7GQDV9X7AGJdAMhujHD3HD3Aw3ZgyjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMi1RmzEnYGAAAAM8gMEuHuYZnh7gOHuRJcPciAAAAGTAGaFhmAAAAEuLsYSskBj6hRlug1YEUYJgDHD3HD3MgAlQAANWAABjh7mQBjh7mUqAAAAAAAMcXYiTDVgRQRmiIAGaMAAAAAAAAAAAAAAAAAAAABlKzPD3AiDKVmeHuBhKzPD3MNUZ4e4GTHD3MgAlQBjh7gOHuOHuOHuZSoDHD3MpUE7AGOLsY5Eg1YGOHuOHuKHD3AcPccPcR6mQMUYSskAMcPccPcyAAasACMlRINWAAAAAAAAAAAAAAAAAAAAAAAAABjh7mQBHmYMowAMswAAAAAAAAAAAAAAAAAAAAzFWZ4e44e5lKgMcPcyAAAAANWABijKVAAAAAAAAAAAAADVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAasAA1YTsAYoylRFBgSbogZZgDKMGUEBgAAAAAAAAAAAAAAAAylYSsklQGKMIkAMR6mQY4e4GQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjh7mIuiQAJ2AAMS6ESUuhEDNBBgAjBlGAAMowABLi7GG7AwDKMAADKAwZZgzyAwSW5gzHqBlKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYl0HD3Mp2AIyVGCX1Dh7gYYQkqJAYj1MgARQ5GZdBQEQZaozw9wHD3MNUZoylQGOHuZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnYAAAAAAAAAANWAACdgBqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD_9nYGFh1pGhkaWdlc3RJRBpJv2CmZnJhbmRvbVgg2FuZQ9cx62XqUFGQbJyMa89sBp3nM2koLTj0orQ43LVxZWxlbWVudElkZW50aWZpZXJtZW1haWxfYWRkcmVzc2xlbGVtZW50VmFsdWVvam9obkBzYW1wbGUuY29t2BhYeaRoZGlnZXN0SUQaS3RceWZyYW5kb21YII9ovNYBsSO6ToPNIp-TUXD0EKDQT-BVV_hAOIUb3APAcWVsZW1lbnRJZGVudGlmaWVyc21vYmlsZV9waG9uZV9udW1iZXJsZWxlbWVudFZhbHVlbSszMDgzODgzMzgzODLYGFiGpGhkaWdlc3RJRBpox9i-ZnJhbmRvbVggY40AuTxkk8_0WZTGgXaneB-1JVhmh_nu4gtjOCA7KzFxZWxlbWVudElkZW50aWZpZXJsdHJ1c3RfYW5jaG9ybGVsZW1lbnRWYWx1ZXggaHR0cHM6Ly9leGFtcGxlLmNvbS90cnVzdF9hbmNob3JqaXNzdWVyQXV0aIRDoQEmohghWQLoMIIC5DCCAomgAwIBAgIUItk98qMMqjnvU8i0T2C6Iexd5l8wCgYIKoZIzj0EAwIwPzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQd3dXYWxsZXQgUm9vdCBDQTAeFw0yNjAzMjYwOTQ4MDVaFw0yNzAzMjYwOTQ4MDVaMEMxCzAJBgNVBAYTAkVVMRUwEwYDVQQKDAx3d1dhbGxldC5vcmcxHTAbBgNVBAMMFGV4YW1wbGUud3d3YWxsZXQub3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfgfBv4pQoxFpiW8LneBhU_4sgz1L_qeSLNbyWpBf6ZQypNHpWOtPBaw-AhmD-1CLChqpHEzK5BKayGzHA02DvaOCAV0wggFZMB0GA1UdDgQWBBQcSVXeO8YsvwolYdeOgwAQ2cyMMzAfBgNVHSMEGDAWgBShEi8bTCOL8kwzsT_FO4Le_E4gTjAOBgNVHQ8BAf8EBAMCB4AwOgYDVR0SBDMwMYERaW5mb0B3d3dhbGxldC5vcmeGHGh0dHBzOi8vZXhhbXBsZS53d3dhbGxldC5vcmcwEgYDVR0lBAswCQYHKIGMXQUBBjAMBgNVHRMBAf8EAjAAMEwGA1UdHwRFMEMwQaA_oD2GO2h0dHBzOi8vZXhhbXBsZS53d3dhbGxldC5vcmcvaWFjYS9jcmwvd3d3YWxsZXRfb3JnX2lhY2EuY3JsMFsGA1UdEQRUMFKCFGV4YW1wbGUud3d3YWxsZXQub3JnghtleGFtcGxlLWlzc3Vlci53d3dhbGxldC5vcmeCHWV4YW1wbGUtdmVyaWZpZXIud3d3YWxsZXQub3JnMAoGCCqGSM49BAMCA0kAMEYCIQD4cO_y3QBuEus5iVrWOoSnMqvDdQsDQNgdCgh0Rl08VAIhAPfi9yyuLBhvJH1cHbzPRBg1BHwOtFkWiHJi88_cuf5HBHgkODYzNmFmMDQtNTc5Ni00ZjQ2LWE3M2UtZDY5MGQ3ZDRlN2YzWQYC2BhZBf2mZ3ZlcnNpb25jMS4wb2RpZ2VzdEFsZ29yaXRobWdTSEEtMjU2bHZhbHVlRGlnZXN0c6F3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjG4HhqtA65jWCAJnVlWndnr9U-5bDf8BmLaHtgcsj0dNom74BlwilZKeBoWQ4MZWCA9bvp8t0Ft0ZryjHT9pehklTP-BObrE2zCgShwNsFoTRo-mQJuWCD7nYNnNB5H0bvYhiqnVqaN_375GQOAPXk7ohC0BbKEthpNnhfZWCDYGJVccftZfeTFsDdUWD_88zm7NPEDZoxLBk2BxVYlLRpQT5WHWCAjrSjwChHLAkm-8mvOd3TNvKfeIaoDAV-A-OdhQ3BQ-hqLM6tFWCDIKjRK5UVo661tAgJG20yQ8-TsCK7bQfETWZP0B39dehratVPrWCDhkYIxEa-S2HWc3lkjTD3xZrvFGG51zA_k0FlyA4MmJxpC7J2BWCBd-cRfpqrevwJoEIjuLkD3lDnFmHn0sKutZVtLGE0N7RoOIOWiWCB-DT6ZjyPo6hj8DWvfrcJoUMTdK2EHhW6F_4pe7cmx0hqY-Zo1WCAOMA5gpQjXEkcwoa4If4vM8kll0naK0FRx4D6YrjhdAhoGMc-lWCD0IMKO4Y2M5hmL-gDo7mzwRQFZoArGQIOkQTLJ_-FVXhog0pzfWCCQT9jzuCWiqJnNVzNdt8pdmxvJkYhHNPGUiSC577PEUBryYBnMWCAY_21bprfnHdkNrnC4FYoqQ16GGGkKNrziegRl4ITLuhoHNpFVWCCSPcJ9eZAi9Ox3MObGp6A8JZnd6Kj1dMs2Rk1HPZTroBqVHpDUWCBRoGfTIe6oznMreo8vrv4oJWPtDvrb1mBlBqSCuIcBnxqInvcjWCBq7p39GohK8J8I_J1fq7lLwbcJ8E7iE2hqg-pMx5o9QBr64oKXWCBhNwFGM6nX6HpEBcX3njIbTSsx9ukBUe7m70L99Kw2eRpkekJoWCCk4hoFvqugbCwi8YD8lqkEoDFowalYTRBtbjtYJiU28xpBM1WaWCCPjoAqNY83KgDQXtuBk_KnGghU1oWnlcOEG5sKfcVtCRorF4EVWCCMvDrGfYC9cYfeexQYJTQ13I9PNtEOsrNRPcdpQ3qsUhr5YNT9WCBn4SON-i24kb9oZmlrurx16mXnd9lA-mpS1cnwHZZvtxqpkaz5WCABj07H5h0y9Yx26BCr2snDi1ls6jiqiZep-Raklv-PfRpFffVhWCCh_Vy0bJ3MtXE0MrxxUbFz5DbWFAMoO9a5y5EMhkA66RpkMhQ5WCAvTivec6NRLfeU6rP5x_x6fngzWjGA8HSJhFmZSZ2WAxpN-CcAWCC1RVgSdUVXoBAKnmtVOSAOVyEcAKvj9eV5uqI54mXBURrec6uEWCBl3zw_bKEjm6-iHyzgfgAxSj2dh907Ipe9tcsBrOv75BpIMsSCWCCJZjcn0MLAkYdGIWA9E3zybGv4hPHdIUptE9_Sx6GfZxpJv2CmWCBIz0atzz3sRuCmL3RrFzaG6QODIsE-j7SNP_d0Magi9RpLdFx5WCDR_dp2UJCKAihOReN1d7vK9a0d5lf4ooZM8kZkCqDEqRpox9i-WCAtbKr3k5HriZS_tonbt6bCZUBgOT_5saxJwqO7OP5ttG1kZXZpY2VLZXlJbmZvoWlkZXZpY2VLZXmlAQICWCs5YmF1ZlVaTWEtZ1dKVzFoYmlDMkhidlk5X2F6dTdTTHlKeU1LaXdKSzhZIAEhWCC40UF36-62jyQuu7Iv1yamst8-l2P6BYglbId3yMjLCCJYIF7oXhGVDGH8koAJFJxh94u0PFXVzmyvRpM7bQofkIANZ2RvY1R5cGV3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjFsdmFsaWRpdHlJbmZvo2ZzaWduZWTAdDIwMjYtMDQtMjFUMTM6MTI6NTRaaXZhbGlkRnJvbcB0MjAyNi0wNC0yMVQxMzoxMjo1NVpqdmFsaWRVbnRpbMB0MjAyNy0wNC0yMVQxMzoxMjo1NFpYQD-3QBfcoHa9Nnw1u9JFh3V68zOZUG_Xyx-1rZUiQcSHfpOdktgf7VLAM-kVLYkrSX2KeapGfYJ6bshVM8VTNEA`; @@ -22,105 +20,137 @@ const context: Context = { const httpClient = defaultHttpClient; -describe("The MsoMdocParser", () => { - - - it("should parse a Base64-URL-encoded IssuerSigned object in mso_mdoc format", async () => { +describe('The MsoMdocParser', () => { + it('should parse a Base64-URL-encoded IssuerSigned object in mso_mdoc format', async () => { const parser = MsoMdocParser({ httpClient, context }); const parsedIssuerSigned = await parser.parse({ rawCredential: issuerSignedB64U }); assert(parsedIssuerSigned.success); - assert(parsedIssuerSigned.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC); - assert(parsedIssuerSigned.value.metadata.credential.doctype === "eu.europa.ec.eudi.pid.1"); - assert(parsedIssuerSigned.value.validityInfo.signed?.toISOString() === new Date("2025-02-18T14:12:04.000Z").toISOString()); - assert(parsedIssuerSigned.value.validityInfo.validUntil?.toISOString() === new Date("2025-03-04T14:12:04.000Z").toISOString()); - - const nameSpace = parsedIssuerSigned.value.signedClaims["eu.europa.ec.eudi.pid.1"] as Record; - assert(nameSpace?.["family_name"] === "MUSTERMANN"); - assert(nameSpace?.["given_name"] === "ERIKA"); - assert((nameSpace?.["birth_date"] as Date).toISOString() === new Date("1964-08-12T00:00:00.000Z").toISOString()); - + assert( + parsedIssuerSigned.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC, + ); + assert(parsedIssuerSigned.value.metadata.credential.doctype === 'eu.europa.ec.eudi.pid.1'); + assert( + parsedIssuerSigned.value.validityInfo.signed?.toISOString() === + new Date('2025-02-18T14:12:04.000Z').toISOString(), + ); + assert( + parsedIssuerSigned.value.validityInfo.validUntil?.toISOString() === + new Date('2025-03-04T14:12:04.000Z').toISOString(), + ); + + const nameSpace = parsedIssuerSigned.value.signedClaims['eu.europa.ec.eudi.pid.1'] as Record< + string, + unknown + >; + assert(nameSpace?.['family_name'] === 'MUSTERMANN'); + assert(nameSpace?.['given_name'] === 'ERIKA'); + assert( + (nameSpace?.['birth_date'] as Date).toISOString() === + new Date('1964-08-12T00:00:00.000Z').toISOString(), + ); }); - it("should successfully parse a Base64-URL-encoded DeviceResponse in mso_mdoc format", async () => { + it('should successfully parse a Base64-URL-encoded DeviceResponse in mso_mdoc format', async () => { const parser = MsoMdocParser({ httpClient, context }); const parsedDeviceResponse = await parser.parse({ rawCredential: deviceResponseB64U }); assert(parsedDeviceResponse.success); - assert(parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC); - assert(parsedDeviceResponse.value.metadata.credential.doctype === "eu.europa.ec.eudi.pid.1"); - - const nameSpace = parsedDeviceResponse.value.signedClaims["eu.europa.ec.eudi.pid.1"] as Record; - assert(nameSpace?.["family_name"] === "MUSTERMANN"); - assert(nameSpace?.["given_name"] === "ERIKA"); - assert((nameSpace?.["birth_date"] as Date).toISOString() === new Date("1964-08-12T00:00:00.000Z").toISOString()); - - }) + assert( + parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC, + ); + assert(parsedDeviceResponse.value.metadata.credential.doctype === 'eu.europa.ec.eudi.pid.1'); + + const nameSpace = parsedDeviceResponse.value.signedClaims['eu.europa.ec.eudi.pid.1'] as Record< + string, + unknown + >; + assert(nameSpace?.['family_name'] === 'MUSTERMANN'); + assert(nameSpace?.['given_name'] === 'ERIKA'); + assert( + (nameSpace?.['birth_date'] as Date).toISOString() === + new Date('1964-08-12T00:00:00.000Z').toISOString(), + ); + }); - it("should successfully parse a Base64-URL-encoded DeviceResponse in eudiw Pid mso_mdoc format", async () => { + it('should successfully parse a Base64-URL-encoded DeviceResponse in eudiw Pid mso_mdoc format', async () => { const parser = MsoMdocParser({ httpClient, context }); const parsedDeviceResponse = await parser.parse({ rawCredential: eudiwPidMdoc, credentialIssuer: { credentialConfigurationId: 'eu.europa.ec.eudi.pid_mso_mdoc', - credentialIssuerIdentifier: 'https://issuer-backend.eudiw.dev' - } + credentialIssuerIdentifier: 'https://issuer-backend.eudiw.dev', + }, }); assert(parsedDeviceResponse.success); - assert(parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC); - assert(parsedDeviceResponse.value.metadata.credential.doctype === "eu.europa.ec.eudi.pid.1"); + assert( + parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC, + ); + assert(parsedDeviceResponse.value.metadata.credential.doctype === 'eu.europa.ec.eudi.pid.1'); - const nameSpace = parsedDeviceResponse.value.signedClaims["eu.europa.ec.eudi.pid.1"] as Record; + const nameSpace = parsedDeviceResponse.value.signedClaims['eu.europa.ec.eudi.pid.1'] as Record< + string, + unknown + >; - assert(nameSpace?.["family_name"] === "Neal"); - assert(nameSpace?.["given_name"] === "Tyler"); + assert(nameSpace?.['family_name'] === 'Neal'); + assert(nameSpace?.['given_name'] === 'Tyler'); //@ts-ignore - expect(parsedDeviceResponse.value.metadata.credential.TypeMetadata.claims.some( - w => JSON.stringify(w.path) === JSON.stringify(['eu.europa.ec.eudi.pid.1', 'family_name']) - ) + expect( + parsedDeviceResponse.value.metadata.credential.TypeMetadata.claims.some( + (w) => + JSON.stringify(w.path) === JSON.stringify(['eu.europa.ec.eudi.pid.1', 'family_name']), + ), ).toBe(true); + }); - }) - - it("should successfully parse a Base64-URL-encoded DeviceResponse in eudiw mdl mso_mdoc format", async () => { + it('should successfully parse a Base64-URL-encoded DeviceResponse in eudiw mdl mso_mdoc format', async () => { const parser = MsoMdocParser({ httpClient, context }); const parsedDeviceResponse = await parser.parse({ rawCredential: eudiwMdlMdoc, credentialIssuer: { credentialConfigurationId: 'org.iso.18013.5.1.mDL', - credentialIssuerIdentifier: 'https://issuer-backend.eudiw.dev' - } + credentialIssuerIdentifier: 'https://issuer-backend.eudiw.dev', + }, }); assert(parsedDeviceResponse.success); - assert(parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC); - assert(parsedDeviceResponse.value.metadata.credential.doctype === "org.iso.18013.5.1.mDL"); - - const nameSpace = parsedDeviceResponse.value.signedClaims["org.iso.18013.5.1"] as Record; - assert(nameSpace?.["family_name"] === "Georgiou"); - assert(nameSpace?.["given_name"] === "Georgios"); + assert( + parsedDeviceResponse.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC, + ); + assert(parsedDeviceResponse.value.metadata.credential.doctype === 'org.iso.18013.5.1.mDL'); + + const nameSpace = parsedDeviceResponse.value.signedClaims['org.iso.18013.5.1'] as Record< + string, + unknown + >; + assert(nameSpace?.['family_name'] === 'Georgiou'); + assert(nameSpace?.['given_name'] === 'Georgios'); //@ts-ignore - expect(parsedDeviceResponse.value.metadata.credential.TypeMetadata.claims.some( - w => JSON.stringify(w.path) === JSON.stringify(['org.iso.18013.5.1', 'family_name']) - ) + expect( + parsedDeviceResponse.value.metadata.credential.TypeMetadata.claims.some( + (w) => JSON.stringify(w.path) === JSON.stringify(['org.iso.18013.5.1', 'family_name']), + ), ).toBe(true); + }); - }) - - it("should parse an OWF-issued IssuerSigned mdoc credential", async () => { + it('should parse an OWF-issued IssuerSigned mdoc credential', async () => { const parser = MsoMdocParser({ httpClient, context }); const parsedOwf = await parser.parse({ rawCredential: owfIssuedCredential }); assert(parsedOwf.success); assert(parsedOwf.value.metadata.credential.format === VerifiableCredentialFormat.MSO_MDOC); - assert(parsedOwf.value.metadata.credential.doctype === "eu.europa.ec.eudi.pid.1"); - - const nameSpace = parsedOwf.value.signedClaims["eu.europa.ec.eudi.pid.1"] as Record; - assert(nameSpace?.["family_name"] === "Doe"); - assert(nameSpace?.["given_name"] === "John"); - }) -}) + assert(parsedOwf.value.metadata.credential.doctype === 'eu.europa.ec.eudi.pid.1'); + + const nameSpace = parsedOwf.value.signedClaims['eu.europa.ec.eudi.pid.1'] as Record< + string, + unknown + >; + assert(nameSpace?.['family_name'] === 'Doe'); + assert(nameSpace?.['given_name'] === 'John'); + }); +}); diff --git a/src/credential-parsers/MsoMdocParser.ts b/src/credential-parsers/MsoMdocParser.ts index daffd5e..a9eb914 100644 --- a/src/credential-parsers/MsoMdocParser.ts +++ b/src/credential-parsers/MsoMdocParser.ts @@ -1,25 +1,34 @@ -import { CredentialParsingError } from "../error"; -import { Context, CredentialParser, HttpClient, CredentialIssuerInfo } from "../interfaces"; -import { cborDecode, DeviceResponse, IssuerSigned } from "@owf/mdoc"; -import { X509Certificate } from "@peculiar/x509"; -import { fromBase64Url } from "../utils/util"; -import { FriendlyNameCallback, ImageDataUriCallback, ParsedCredential, VerifiableCredentialFormat, TypeMetadataResult } from "../types"; -import { CustomCredentialSvg } from "../functions/CustomCredentialSvg"; -import { CredentialRenderingService } from "../rendering"; -import { getIssuerMetadata } from "../utils/getIssuerMetadata"; -import { convertOpenid4vciToSdjwtvcClaims } from "../functions/convertOpenid4vciToSdjwtvcClaims"; -import type { z } from "zod"; -import { OpenidCredentialIssuerMetadataSchema, } from "../schemas"; -import { dataUriResolver } from "../resolvers/dataUriResolver"; -import { friendlyNameResolver } from "../resolvers/friendlyNameResolver"; +import { CredentialParsingError } from '../error'; +import { Context, CredentialParser, HttpClient, CredentialIssuerInfo } from '../interfaces'; +import { cborDecode, DeviceResponse, IssuerSigned } from '@owf/mdoc'; +import { X509Certificate } from '@peculiar/x509'; +import { fromBase64Url } from '../utils/util'; +import { + FriendlyNameCallback, + ImageDataUriCallback, + ParsedCredential, + VerifiableCredentialFormat, + TypeMetadataResult, +} from '../types'; +import { CustomCredentialSvg } from '../functions/CustomCredentialSvg'; +import { CredentialRenderingService } from '../rendering'; +import { getIssuerMetadata } from '../utils/getIssuerMetadata'; +import { convertOpenid4vciToSdjwtvcClaims } from '../functions/convertOpenid4vciToSdjwtvcClaims'; +import type { z } from 'zod'; +import { OpenidCredentialIssuerMetadataSchema } from '../schemas'; +import { dataUriResolver } from '../resolvers/dataUriResolver'; +import { friendlyNameResolver } from '../resolvers/friendlyNameResolver'; type IssuerMetadata = z.infer; -export function MsoMdocParser(args: { context: Context, httpClient: HttpClient }): CredentialParser { +export function MsoMdocParser(args: { + context: Context; + httpClient: HttpClient; +}): CredentialParser { const credentialRendering = CredentialRenderingService(); function looksLikeCborMap(raw: unknown): raw is string { - if (typeof raw !== "string") return false; + if (typeof raw !== 'string') return false; let bytes: Uint8Array; try { @@ -35,10 +44,14 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } // Definite maps: 0xA0..0xBB // Indefinite map: 0xBF // 0xBC..0xBE are reserved/invalid. - return (first >= 0xA0 && first <= 0xBB) || first === 0xBF; + return (first >= 0xa0 && first <= 0xbb) || first === 0xbf; } - function extractValidityInfo(issuerSigned: IssuerSigned): { validUntil?: Date, validFrom?: Date, signed?: Date } { + function extractValidityInfo(issuerSigned: IssuerSigned): { + validUntil?: Date; + validFrom?: Date; + signed?: Date; + } { const validityInfo = issuerSigned.issuerAuth.mobileSecurityObject.validityInfo; return { signed: validityInfo.signed, @@ -49,13 +62,16 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } function collectAllAttrValues(issuerSigned: IssuerSigned): Record { const issuerNamespaces = issuerSigned.issuerNamespaces?.issuerNamespaces ?? new Map(); - return Array.from(issuerNamespaces.entries()).reduce>((acc, [ns, items]) => { - acc[ns] = items.reduce>((nsAcc, item) => { - nsAcc[item.elementIdentifier] = item.elementValue; - return nsAcc; - }, {}); - return acc; - }, {}); + return Array.from(issuerNamespaces.entries()).reduce>( + (acc, [ns, items]) => { + acc[ns] = items.reduce>((nsAcc, item) => { + nsAcc[item.elementIdentifier] = item.elementValue; + return nsAcc; + }, {}); + return acc; + }, + {}, + ); } function extractIssuerName(issuerSigned: IssuerSigned): string { @@ -63,32 +79,40 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } const certificateBytes = new Uint8Array(issuerSigned.issuerAuth.certificate); return new X509Certificate(certificateBytes).issuerName.toString(); } catch { - return "Unknown issuer"; + return 'Unknown issuer'; } } async function fetchIssuerMetadataAndDocs( - credentialIssuer?: CredentialIssuerInfo | null + credentialIssuer?: CredentialIssuerInfo | null, ): Promise<{ issuerMetadata: IssuerMetadata | null; TypeMetadata: TypeMetadataResult }> { let issuerMetadata: IssuerMetadata | null = null; let TypeMetadata: TypeMetadataResult = {}; try { if (credentialIssuer?.credentialIssuerIdentifier) { - const { metadata } = await getIssuerMetadata(args.httpClient, credentialIssuer.credentialIssuerIdentifier, []); + const { metadata } = await getIssuerMetadata( + args.httpClient, + credentialIssuer.credentialIssuerIdentifier, + [], + ); issuerMetadata = metadata ?? null; const issuerClaimsArray = credentialIssuer?.credentialConfigurationId - ? issuerMetadata?.credential_configurations_supported?.[credentialIssuer.credentialConfigurationId]?.credential_metadata?.claims + ? issuerMetadata?.credential_configurations_supported?.[ + credentialIssuer.credentialConfigurationId + ]?.credential_metadata?.claims : undefined; - const convertedClaims = issuerClaimsArray ? convertOpenid4vciToSdjwtvcClaims(issuerClaimsArray) : undefined; + const convertedClaims = issuerClaimsArray + ? convertOpenid4vciToSdjwtvcClaims(issuerClaimsArray) + : undefined; if (convertedClaims?.length) { TypeMetadata = { claims: convertedClaims }; } } } catch (e) { - console.warn("Issuer metadata unavailable or invalid:", e); + console.warn('Issuer metadata unavailable or invalid:', e); } return { issuerMetadata, TypeMetadata }; @@ -100,7 +124,7 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } signedClaims: Record, TypeMetadata: TypeMetadataResult, friendlyName: FriendlyNameCallback, - dataUri: ImageDataUriCallback + dataUri: ImageDataUriCallback, ): ParsedCredential { const issuerName = extractIssuerName(issuerSigned); @@ -111,21 +135,21 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } doctype: docType, TypeMetadata, image: { dataUri }, - name: friendlyName + name: friendlyName, }, issuer: { id: issuerName, - name: issuerName - } + name: issuerName, + }, }, signedClaims: { ...signedClaims }, - validityInfo: { ...extractValidityInfo(issuerSigned) } + validityInfo: { ...extractValidityInfo(issuerSigned) }, }; } async function deviceResponseParser( rawCredential: string, - credentialIssuer?: CredentialIssuerInfo | null + credentialIssuer?: CredentialIssuerInfo | null, ): Promise { try { const decodedCred = fromBase64Url(rawCredential); @@ -138,12 +162,14 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } const { issuerMetadata, TypeMetadata } = await fetchIssuerMetadataAndDocs(credentialIssuer); const issuerDisplayArray = credentialIssuer?.credentialConfigurationId - ? issuerMetadata?.credential_configurations_supported?.[credentialIssuer.credentialConfigurationId]?.credential_metadata?.display + ? issuerMetadata?.credential_configurations_supported?.[ + credentialIssuer.credentialConfigurationId + ]?.credential_metadata?.display : undefined; const friendlyName = friendlyNameResolver({ issuerDisplayArray, - fallbackName: "mdoc Verifiable Credential", + fallbackName: 'mdoc Verifiable Credential', }); const dataUri = dataUriResolver({ @@ -153,10 +179,17 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } issuerDisplayArray, sdJwtVcRenderer: credentialRendering, sdJwtVcMetadataClaims: TypeMetadata.claims, - fallbackName: "mdoc Verifiable Credential", + fallbackName: 'mdoc Verifiable Credential', }); - return toParsedCredential(parsedDocument.docType, parsedDocument.issuerSigned, signedClaims, TypeMetadata, friendlyName, dataUri); + return toParsedCredential( + parsedDocument.docType, + parsedDocument.issuerSigned, + signedClaims, + TypeMetadata, + friendlyName, + dataUri, + ); } catch { return null; } @@ -164,7 +197,7 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } async function issuerSignedParser( rawCredential: string, - credentialIssuer?: CredentialIssuerInfo | null + credentialIssuer?: CredentialIssuerInfo | null, ): Promise { try { const credentialBytes = fromBase64Url(rawCredential); @@ -176,12 +209,14 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } const { issuerMetadata, TypeMetadata } = await fetchIssuerMetadataAndDocs(credentialIssuer); const issuerDisplayArray = credentialIssuer?.credentialConfigurationId - ? issuerMetadata?.credential_configurations_supported?.[credentialIssuer.credentialConfigurationId]?.credential_metadata?.display + ? issuerMetadata?.credential_configurations_supported?.[ + credentialIssuer.credentialConfigurationId + ]?.credential_metadata?.display : undefined; const friendlyName = friendlyNameResolver({ issuerDisplayArray, - fallbackName: "mdoc Verifiable Credential", + fallbackName: 'mdoc Verifiable Credential', }); const dataUri = dataUriResolver({ @@ -191,10 +226,17 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } issuerDisplayArray, sdJwtVcRenderer: credentialRendering, sdJwtVcMetadataClaims: TypeMetadata.claims, - fallbackName: "mdoc Verifiable Credential", + fallbackName: 'mdoc Verifiable Credential', }); - return toParsedCredential(docType, issuerSigned, signedClaims, TypeMetadata, friendlyName, dataUri); + return toParsedCredential( + docType, + issuerSigned, + signedClaims, + TypeMetadata, + friendlyName, + dataUri, + ); } catch { return null; } @@ -209,26 +251,32 @@ export function MsoMdocParser(args: { context: Context, httpClient: HttpClient } }; } - const deviceResponseParsingResult = await deviceResponseParser(rawCredential, credentialIssuer ?? null); + const deviceResponseParsingResult = await deviceResponseParser( + rawCredential, + credentialIssuer ?? null, + ); if (deviceResponseParsingResult) { return { success: true, - value: deviceResponseParsingResult - } + value: deviceResponseParsingResult, + }; } - const issuerSignedParsingResult = await issuerSignedParser(rawCredential, credentialIssuer ?? null); + const issuerSignedParsingResult = await issuerSignedParser( + rawCredential, + credentialIssuer ?? null, + ); if (issuerSignedParsingResult) { return { success: true, value: issuerSignedParsingResult, - } + }; } return { success: false, error: CredentialParsingError.CouldNotParse, - } + }; }, - } + }; } diff --git a/src/credential-parsers/SDJWTVCParser.test.ts b/src/credential-parsers/SDJWTVCParser.test.ts index 69ff579..56feaf2 100644 --- a/src/credential-parsers/SDJWTVCParser.test.ts +++ b/src/credential-parsers/SDJWTVCParser.test.ts @@ -1,14 +1,14 @@ -import { assert, describe, it } from "vitest"; -import { SDJWTVCParser } from "./SDJWTVCParser"; -import { Context, HttpClient } from "../interfaces"; -import { defaultHttpClient } from "../defaultHttpClient"; -import { CredentialParsingError } from "../error"; +import { assert, describe, it } from 'vitest'; +import { SDJWTVCParser } from './SDJWTVCParser'; +import { Context, HttpClient } from '../interfaces'; +import { defaultHttpClient } from '../defaultHttpClient'; +import { CredentialParsingError } from '../error'; const rawCredential = `eyJ0eXAiOiJkYytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFZrYVRwd2FXUTZNU0lzSW01aGJXVWlPaUpGZUdGdGNHeGxJRUZTUmlBeExqZ2dVRWxFSUZORUxVcFhWQ0JVV1ZCRklFMUZWRUZFUVZSQklpd2laR1Z6WTNKcGNIUnBiMjRpT2lKRmVHRnRjR3hsSUZCSlJDQlRSQzFLVjFRZ1ZtVnlhV1pwWVdKc1pTQkRjbVZrWlc1MGFXRnNJRlI1Y0dVZ1RXVjBZV1JoZEdFc0lHSmhjMlZrSUc5dUlHbGxkR1l0YjJGMWRHZ3RjMlF0YW5kMExYWmpJQ2hrY21GbWRDQXdPU2tzSUhWemFXNW5JR0VnYzJsdVoyeGxJR3hoYm1kMVlXZGxJSFJoWnlBb1pXNHRWVk1wTGlCQlpHaGxjbVZ6SUhSdklGQkpSQ0JTZFd4bFltOXZheUJCVWtZZ01TNDRJQ2gxY200NlpYVmthVHB3YVdRNk1Ta2dZVzVrSUd4aGRHVnlMQ0JoY3lCdlppQjBhR1VnZEdsdFpTQnZaaUJ3ZFdKc2FXTmhkR2x2Ymk0aUxDSWtZMjl0YldWdWRDSTZJa2x0Y0d4bGJXVnVkR0YwYVc5dUlHOW1JSFJvYVhNZ1pYaGhiWEJzWlNCMGVYQmxJRzFsZEdGa1lYUmhJRzFoZVNCeVpYRjFhWEpsSUUxbGJXSmxjaUJUZEdGMFplS0FrM053WldOcFptbGpJR05zWVhKcFptbGpZWFJwYjI1eklIUnZJR0ZzYVdkdUlIZHBkR2dnYm1GMGFXOXVZV3dnY0c5c2FXTnBaWE1nWjI5MlpYSnVhVzVuSUhSb1pTQmthWE53YkdGNUlHOW1JR2x1WTJ4MVpHVmtJR05zWVdsdGN5NGlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW01aGJXVWlPaUpRU1VRaUxDSnlaVzVrWlhKcGJtY2lPbnNpYzJsdGNHeGxJanA3SW14dloyOGlPbnNpZFhKcElqb2lhSFIwY0RvdkwzZGhiR3hsZEMxbGJuUmxjbkJ5YVhObExXbHpjM1ZsY2pvNE1EQXpMMmx0WVdkbGN5OXNiMmR2TG5CdVp5SXNJblZ5YVNOcGJuUmxaM0pwZEhraU9pSnphR0V5TlRZdFNVWnhSVWhXWmxjd1pVOVBLMG94TkdoNWJFaFlhemhZUmtJNGVXcE1aV2t5TVZKb1RFWXlaMFZKV1QwaUxDSmhiSFJmZEdWNGRDSTZJbEJKUkNCTWIyZHZJbjBzSW1KaFkydG5jbTkxYm1SZlkyOXNiM0lpT2lJak5HTmpNMlJrSWl3aWRHVjRkRjlqYjJ4dmNpSTZJaU5HUmtaR1JrWWlmU3dpYzNablgzUmxiWEJzWVhSbGN5STZXM3NpZFhKcElqb2lhSFIwY0RvdkwzZGhiR3hsZEMxbGJuUmxjbkJ5YVhObExXbHpjM1ZsY2pvNE1EQXpMMmx0WVdkbGN5OTBaVzF3YkdGMFpTMXdhV1F1YzNabklpd2lkWEpwSTJsdWRHVm5jbWwwZVNJNkluTm9ZVEkxTmkxSVRpdDBiSHBDU0haUmRVNXZUMmRSZG1KcFZETkdSRU5UTTBGYVQxZGtlSFZLWVZjMFYyOXBZbEp6UFNJc0luQnliM0JsY25ScFpYTWlPbnNpYjNKcFpXNTBZWFJwYjI0aU9pSnNZVzVrYzJOaGNHVWlMQ0pqYjJ4dmNsOXpZMmhsYldVaU9pSnNhV2RvZENJc0ltTnZiblJ5WVhOMElqb2libTl5YldGc0luMTlYWDE5WFN3aVkyeGhhVzF6SWpwYmV5SndZWFJvSWpwYkluWmpkQ0pkTENKdFlXNWtZWFJ2Y25raU9uUnlkV1VzSW5Oa0lqb2libVYyWlhJaWZTeDdJbkJoZEdnaU9sc2lhblJwSWwwc0luTmtJam9pYm1WMlpYSWlmU3g3SW5CaGRHZ2lPbHNpYzNWaUlsMHNJbTFoYm1SaGRHOXllU0k2ZEhKMVpTd2ljMlFpT2lKdVpYWmxjaUo5TEhzaWNHRjBhQ0k2V3lKcFlYUWlYU3dpYldGdVpHRjBiM0o1SWpwMGNuVmxMQ0p6WkNJNkltNWxkbVZ5SW4wc2V5SndZWFJvSWpwYkltTnVhQ0pkTENKdFlXNWtZWFJ2Y25raU9uUnlkV1VzSW5Oa0lqb2libVYyWlhJaWZTeDdJbkJoZEdnaU9sc2labUZ0YVd4NVgyNWhiV1VpWFN3aWJXRnVaR0YwYjNKNUlqcDBjblZsTENKelpDSTZJbUZzZDJGNWN5SXNJbk4yWjE5cFpDSTZJbVpoYldsc2VWOXVZVzFsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklreGhjM1FnYm1GdFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVEzVnljbVZ1ZENCc1lYTjBJRzVoYldVb2N5a2diM0lnYzNWeWJtRnRaU2h6S1M0aWZWMTlMSHNpY0dGMGFDSTZXeUpuYVhabGJsOXVZVzFsSWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0p6ZG1kZmFXUWlPaUpuYVhabGJsOXVZVzFsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrWnBjbk4wSUc1aGJXVWlMQ0prWlhOamNtbHdkR2x2YmlJNklrTjFjbkpsYm5RZ1ptbHljM1FnYm1GdFpTaHpLU3dnYVc1amJIVmthVzVuSUcxcFpHUnNaU0J1WVcxbEtITXBJR2xtSUdGd2NHeHBZMkZpYkdVdUluMWRmU3g3SW5CaGRHZ2lPbHNpWW1seWRHaGtZWFJsSWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0p6ZG1kZmFXUWlPaUppYVhKMGFGOWtZWFJsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrUmhkR1VnYjJZZ1ltbHlkR2dpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1oxYkd3Z1ltbHlkR2dnWkdGMFpTQW9aR0Y1TENCdGIyNTBhQ3dnZVdWaGNpa3VJbjFkZlN4N0luQmhkR2dpT2xzaWNHeGhZMlZmYjJaZlltbHlkR2dpWFN3aWJXRnVaR0YwYjNKNUlqcDBjblZsTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpRYkdGalpTQnZaaUJpYVhKMGFDSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVVHeGhZMlVnZDJobGNtVWdkR2hsSUhCbGNuTnZiaUIzWVhNZ1ltOXliaTRpZlYxOUxIc2ljR0YwYUNJNld5SndiR0ZqWlY5dlpsOWlhWEowYUNJc0lteHZZMkZzYVhSNUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrTnBkSGtnYjJZZ1ltbHlkR2dpTENKa1pYTmpjbWx3ZEdsdmJpSTZJazExYm1samFYQmhiR2wwZVN3Z1kybDBlU3dnZEc5M2Jpd2diM0lnZG1sc2JHRm5aU0IzYUdWeVpTQjBhR1VnY0dWeWMyOXVJSGRoY3lCaWIzSnVMaUo5WFgwc2V5SndZWFJvSWpwYkluQnNZV05sWDI5bVgySnBjblJvSWl3aWNtVm5hVzl1SWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWxKbFoybHZiaUJ2WmlCaWFYSjBhQ0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lVM1JoZEdVc0lIQnliM1pwYm1ObExDQmthWE4wY21samRDd2diM0lnYkc5allXd2dZWEpsWVNCM2FHVnlaU0IwYUdVZ2NHVnljMjl1SUhkaGN5QmliM0p1TGlKOVhYMHNleUp3WVhSb0lqcGJJbkJzWVdObFgyOW1YMkpwY25Sb0lpd2lZMjkxYm5SeWVTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkRiM1Z1ZEhKNUlHOW1JR0pwY25Sb0lpd2laR1Z6WTNKcGNIUnBiMjRpT2lKRGIzVnVkSEo1SUhkb1pYSmxJSFJvWlNCd1pYSnpiMjRnZDJGeklHSnZjbTR1SW4xZGZTeDdJbkJoZEdnaU9sc2libUYwYVc5dVlXeHBkR2xsY3lJc2JuVnNiRjBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJazVoZEdsdmJtRnNhWFI1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpEYjNWdWRISjVJRzltSUc1aGRHbHZibUZzYVhSNUxpSjlYWDBzZXlKd1lYUm9JanBiSW01aGRHbHZibUZzYVhScFpYTWlYU3dpYldGdVpHRjBiM0o1SWpwMGNuVmxMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSk9ZWFJwYjI1aGJHbDBhV1Z6SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpEYjNWdWRISjVJRzl5SUdOdmRXNTBjbWxsY3lCdlppQnVZWFJwYjI1aGJHbDBlUzRpZlYxOUxIc2ljR0YwYUNJNld5SndaWEp6YjI1aGJGOWhaRzFwYm1semRISmhkR2wyWlY5dWRXMWlaWElpWFN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pVUdWeWMyOXVZV3dnU1VRaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxWdWFYRjFaU0J3WlhKemIyNWhiQ0JwWkdWdWRHbG1hV1Z5SUdGemMybG5ibVZrSUdKNUlIUm9aU0JoZFhSb2IzSnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2ljR2xqZEhWeVpTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0luTjJaMTlwWkNJNkluQnBZM1IxY21VaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lVR2xqZEhWeVpTSjlYWDBzZXlKd1lYUm9JanBiSW1KcGNuUm9YMlpoYldsc2VWOXVZVzFsSWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtKcGNuUm9JR3hoYzNRZ2JtRnRaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lUR0Z6ZENCdVlXMWxLSE1wSUc5eUlITjFjbTVoYldVb2N5a2dZWFFnWW1seWRHZ3VJbjFkZlN4N0luQmhkR2dpT2xzaVltbHlkR2hmWjJsMlpXNWZibUZ0WlNKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKQ2FYSjBhQ0JtYVhKemRDQnVZVzFsSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpHYVhKemRDQnVZVzFsS0hNcExDQnBibU5zZFdScGJtY2diV2xrWkd4bElHNWhiV1VvY3lrc0lHRjBJR0pwY25Sb0xpSjlYWDBzZXlKd1lYUm9JanBiSW5ObGVDSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSlRaWGdpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEpsWTI5eVpHVmtJSE5sZUNCdmNpQm5aVzVrWlhJc0lIVnphVzVuSUhOMFlXNWtZWEprSUdOdlpHVnpMaUo5WFgwc2V5SndZWFJvSWpwYkltVnRZV2xzSWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtWdFlXbHNJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlFaWEp6YjI0bmN5QmxiV0ZwYkNCaFpHUnlaWE56TGlKOVhYMHNleUp3WVhSb0lqcGJJbkJvYjI1bFgyNTFiV0psY2lKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKTmIySnBiR1VpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEJsY25OdmJ1S0FtWE1nYlc5aWFXeGxJSEJvYjI1bElHNTFiV0psY2k0aWZWMTlMSHNpY0dGMGFDSTZXeUpoWkdSeVpYTnpJbDBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa0ZrWkhKbGMzTWlMQ0prWlhOamNtbHdkR2x2YmlJNklsQmxjbk52YmlkeklISmxjMmxrWlc1MGFXRnNJR0ZrWkhKbGMzTXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0ltWnZjbTFoZEhSbFpDSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkdkV3hzSUdGa1pISmxjM01pTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1oxYkd3Z1ptOXliV0YwZEdWa0lHRmtaSEpsYzNNZ2IyWWdjbVZ6YVdSbGJtTmxMaUo5WFgwc2V5SndZWFJvSWpwYkltRmtaSEpsYzNNaUxDSnpkSEpsWlhSZllXUmtjbVZ6Y3lKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKU1pYTnBaR1Z1WTJVZ2MzUnlaV1YwSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpUZEhKbFpYUWdibUZ0WlNCdlppQnlaWE5wWkdWdVkyVXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0ltaHZkWE5sWDI1MWJXSmxjaUpkTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpTWlhOcFpHVnVZMlVnYm5WdFltVnlJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlRkSEpsWlhRZ2JuVnRZbVZ5SUc5bUlISmxjMmxrWlc1alpTNGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFpHUnlaWE56SWl3aWNHOXpkR0ZzWDJOdlpHVWlYU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lVbVZ6YVdSbGJtTmxJRnBKVUNJc0ltUmxjMk55YVhCMGFXOXVJam9pVUc5emRHRnNJRzl5SUZwSlVDQmpiMlJsSUc5bUlISmxjMmxrWlc1alpTNGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFpHUnlaWE56SWl3aWJHOWpZV3hwZEhraVhTd2ljMlFpT2lKaGJIZGhlWE1pTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVEybDBlU0J2WmlCeVpYTnBaR1Z1WTJVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWsxMWJtbGphWEJoYkdsMGVTd2dZMmwwZVN3Z2RHOTNiaXdnYjNJZ2RtbHNiR0ZuWlNCdlppQnlaWE5wWkdWdVkyVXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0luSmxaMmx2YmlKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKVGRHRjBaU0J2WmlCeVpYTnBaR1Z1WTJVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxOMFlYUmxMQ0J3Y205MmFXNWpaU3dnYjNJZ2NtVm5hVzl1WVd3Z1pHbDJhWE5wYjI0Z2IyWWdjbVZ6YVdSbGJtTmxMaUo5WFgwc2V5SndZWFJvSWpwYkltRmtaSEpsYzNNaUxDSmpiM1Z1ZEhKNUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrTnZkVzUwY25rZ2IyWWdjbVZ6YVdSbGJtTmxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSkRiM1Z1ZEhKNUlIZG9aWEpsSUhSb1pTQndaWEp6YjI0Z1kzVnljbVZ1ZEd4NUlISmxjMmxrWlhNdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgyVnhkV0ZzWDI5eVgyOTJaWElpWFN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pUVdkbElHVnhkV0ZzSUc5eUlHOTJaWElpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa0ZuWlNCMGFISmxjMmh2YkdRZ2FXNWthV05oZEc5eWN5NGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFoyVmZaWEYxWVd4ZmIzSmZiM1psY2lJc0lqRTBJbDBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa0ZuWlNCbGNYVmhiQ0J2Y2lCdmRtVnlJREUwSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpKYm1ScFkyRjBaWE1nYVdZZ2RHaGxJSEJsY25OdmJpQnBjeUF4TkNCNVpXRnljeUJ2YkdRZ2IzSWdiMnhrWlhJdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgyVnhkV0ZzWDI5eVgyOTJaWElpTENJeE5pSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkJaMlVnWlhGMVlXd2diM0lnYjNabGNpQXhOaUlzSW1SbGMyTnlhWEIwYVc5dUlqb2lTVzVrYVdOaGRHVnpJR2xtSUhSb1pTQndaWEp6YjI0Z2FYTWdNVFlnZVdWaGNuTWdiMnhrSUc5eUlHOXNaR1Z5TGlKOVhYMHNleUp3WVhSb0lqcGJJbUZuWlY5bGNYVmhiRjl2Y2w5dmRtVnlJaXdpTVRnaVhTd2ljMlFpT2lKaGJIZGhlWE1pTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVFXZGxJR1Z4ZFdGc0lHOXlJRzkyWlhJZ01UZ2lMQ0prWlhOamNtbHdkR2x2YmlJNklrbHVaR2xqWVhSbGN5QnBaaUIwYUdVZ2NHVnljMjl1SUdseklERTRJSGxsWVhKeklHOXNaQ0J2Y2lCdmJHUmxjaTRpZlYxOUxIc2ljR0YwYUNJNld5SmhaMlZmWlhGMVlXeGZiM0pmYjNabGNpSXNJakl4SWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtGblpTQmxjWFZoYkNCdmNpQnZkbVZ5SURJeElpd2laR1Z6WTNKcGNIUnBiMjRpT2lKSmJtUnBZMkYwWlhNZ2FXWWdkR2hsSUhCbGNuTnZiaUJwY3lBeU1TQjVaV0Z5Y3lCdmJHUWdiM0lnYjJ4a1pYSXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXZGxYMlZ4ZFdGc1gyOXlYMjkyWlhJaUxDSTJOU0pkTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpCWjJVZ1pYRjFZV3dnYjNJZ2IzWmxjaUEyTlNJc0ltUmxjMk55YVhCMGFXOXVJam9pU1c1a2FXTmhkR1Z6SUdsbUlIUm9aU0J3WlhKemIyNGdhWE1nTmpVZ2VXVmhjbk1nYjJ4a0lHOXlJRzlzWkdWeUxpSjlYWDBzZXlKd1lYUm9JanBiSW1GblpWOXBibDk1WldGeWN5SmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkJaMlVpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEJsY25OdmJpZHpJR0ZuWlNCcGJpQmpiMjF3YkdWMFpXUWdlV1ZoY25NdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgySnBjblJvWDNsbFlYSWlYU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lRbWx5ZEdnZ2VXVmhjaUlzSW1SbGMyTnlhWEIwYVc5dUlqb2lXV1ZoY2lCcGJpQjNhR2xqYUNCMGFHVWdjR1Z5YzI5dUlIZGhjeUJpYjNKdUxpSjlYWDBzZXlKd1lYUm9JanBiSW1semMzVnBibWRmWVhWMGFHOXlhWFI1SWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXbHVaeUJoZFhSb2IzSnBkSGtpTENKa1pYTmpjbWx3ZEdsdmJpSTZJazVoYldVZ2IyWWdkR2hsSUdsemMzVnBibWNnWW05a2VTQnZjaUJOWlcxaVpYSWdVM1JoZEdVZ0tIUjNieTFzWlhSMFpYSWdZMjlrWlNrdUluMWRmU3g3SW5CaGRHZ2lPbHNpYVhOemRXbHVaMTlqYjNWdWRISjVJbDBzSW0xaGJtUmhkRzl5ZVNJNmRISjFaU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lTWE56ZFdsdVp5QmpiM1Z1ZEhKNUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTlpXMWlaWElnVTNSaGRHVWdkMmhsY21VZ2RHaGxJR1J2WTNWdFpXNTBJSGRoY3lCcGMzTjFaV1F1SW4xZGZTeDdJbkJoZEdnaU9sc2laR0YwWlY5dlpsOWxlSEJwY25raVhTd2liV0Z1WkdGMGIzSjVJanAwY25WbExDSnpaQ0k2SW01bGRtVnlJaXdpYzNablgybGtJam9pWlhod2FYSjVYMlJoZEdVaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lSWGh3YVhKNUlHUmhkR1VpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1Z1WkNCa1lYUmxJRzltSUhSb1pTQmtiMk4xYldWdWRPS0FtWE1nZG1Gc2FXUnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2laR0YwWlY5dlpsOXBjM04xWVc1alpTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkpjM04xWlNCa1lYUmxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlRkR0Z5ZENCa1lYUmxJRzltSUhSb1pTQmtiMk4xYldWdWRPS0FtWE1nZG1Gc2FXUnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2laRzlqZFcxbGJuUmZiblZ0WW1WeUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrUnZZM1Z0Wlc1MElHNTFiV0psY2lJc0ltUmxjMk55YVhCMGFXOXVJam9pVlc1cGNYVmxJR2xrWlc1MGFXWnBaWElnYjJZZ2RHaGxJRkJKUkNCa2IyTjFiV1Z1ZEM0aWZWMTlMSHNpY0dGMGFDSTZXeUpwYzNOMWFXNW5YMnAxY21selpHbGpkR2x2YmlKZExDSnpaQ0k2SW01bGRtVnlJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVnBibWNnY21WbmFXOXVJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlNaV2RwYjI1aGJDQnZjaUJzYjJOaGJDQnpkV0prYVhacGMybHZiaUIwYUdGMElHbHpjM1ZsWkNCMGFHVWdaRzlqZFcxbGJuUXVJbjFkZlN4N0luQmhkR2dpT2xzaWRISjFjM1JmWVc1amFHOXlJbDBzSW5Oa0lqb2libVYyWlhJaWZWMTkiXSwieDVjIjpbIk1JSUN5ekNDQW5HZ0F3SUJBZ0lVTG5yeHV4OXNJMzRvcWJieTNNNGxTS09zOG93d0NnWUlLb1pJemowRUF3SXdQekVMTUFrR0ExVUVCaE1DUlZVeEZUQVRCZ05WQkFvTURIZDNWMkZzYkdWMExtOXlaekVaTUJjR0ExVUVBd3dRZDNkWFlXeHNaWFFnVW05dmRDQkRRVEFlRncweU5UQTBNamt4TURJNU5UTmFGdzB5TmpBME1qa3hNREk1TlROYU1FRXhDekFKQmdOVkJBWVRBa1ZWTVJVd0V3WURWUVFLREF4M2QxZGhiR3hsZEM1dmNtY3hHekFaQmdOVkJBTU1FbXh2WTJGc0xuZDNkMkZzYkdWMExtOXlaekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRlZpdkd0NTNNNHFFUDA2UVQyMEJTbEdpTUlkekxMdkcrYjlmcS9mSEtNK05HVCthM3NuWGlQd1U3WDdqck9GV3h3eWpaZWVhbjQwK3Z4Nkd5MDZWZnFqZ2dGSE1JSUJRekFkQmdOVkhRNEVGZ1FVTS9BM0ZUUUxqd3c1Lzl1MDFNWC9TUnlWcWFVd0h3WURWUjBqQkJnd0ZvQVUwSEd1M1QrL1dxaDN5TmlmejlzTmQrSFBCUzR3RGdZRFZSMFBBUUgvQkFRREFnZUFNRElHQTFVZEVnUXJNQ21CRVdsdVptOUFkM2QzWVd4c1pYUXViM0puaGhSb2RIUndjem92TDNkM2QyRnNiR1YwTG05eVp6QVNCZ05WSFNVRUN6QUpCZ2NvZ1l4ZEJRRUNNQXdHQTFVZEV3RUIvd1FDTUFBd1JBWURWUjBmQkQwd096QTVvRGVnTllZemFIUjBjSE02THk5M2QzZGhiR3hsZEM1dmNtY3ZhV0ZqWVM5amNtd3ZkM2QzWVd4c1pYUmZiM0puWDJsaFkyRXVZM0pzTUZVR0ExVWRFUVJPTUV5Q0VteHZZMkZzTG5kM2QyRnNiR1YwTG05eVo0SVpiRzlqWVd3dGFYTnpkV1Z5TG5kM2QyRnNiR1YwTG05eVo0SWJiRzlqWVd3dGRtVnlhV1pwWlhJdWQzZDNZV3hzWlhRdWIzSm5NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUNROGgrNWtyaE8rZjR3b1JlRFkxRDdDYU02cUNkYTNtODE0ZTZETHZPcGhBSWdIUUwrV203V0ZSd3hnanpNTE4zN1JvakpHclpiRjRPRkNoSWttbTB1dTVvPSJdLCJhbGciOiJFUzI1NiJ9.eyJjbmYiOnsiandrIjp7ImNydiI6IlAtMjU2IiwiZXh0Ijp0cnVlLCJrZXlfb3BzIjpbXSwia3R5IjoiRUMiLCJ4IjoiWE1JZXpBYV9OLUVEdkRaWjlFOGxIZXlRNnBSTDMyNEUzbGUxdmQxTjQ0RSIsInkiOiJ3RnN2TExoMzR5NjdoRlRObXNwcnQxSXhybzBLYzZHOGx6alNTc0g1NVd3In19LCJ2Y3QiOiJ1cm46ZXVkaTpwaWQ6MSIsInZjdCNpbnRlZ3JpdHkiOiJzaGEyNTYtNVJNMkYvNGNmK1EvTFd2N2pCcDVLL3owSG1wR1liUUh1YmJ4VVVkYy9PTT0iLCJqdGkiOiJ1cm46dmlkOjI1MDgwOWVhLTI1OTUtNDdkMS04NzdhLTc4NjIxNWY3N2FmYiIsInBsYWNlX29mX2JpcnRoIjp7Il9zZCI6WyJZNllzOHM0QlNYN0h5LUYwMGlNRzRkZk1pYzlZVFNMVnJTQ2hVX0xxUFBnIiwiWXZtdjRPaUtfZUg3ZW9pSEpmZ0hLSEpkZUx4STB4S2xaVTE5UnZQSS1HVSIsIlptR2pLUnhOZGZfV1VuM1N4NTJYdnZlXzZHMjE3NlBydkgwTUlWTGNYT1EiXX0sImFkZHJlc3MiOnsiX3NkIjpbIjNWOHpfVGJsVUkyNVZ6cEdXd0xDalNrRHZwS3FwWUVmdnJTWUxwVTdCd0UiLCI1UXhoLXRiTzIwa1VISXdXQlQ1SlRMSTU0LUxlM1dIQzhNLWltVmZtQjNzIiwiT2dTQXp1UGg5cHJVYlB3T0NGT1U4eHVTMVp0VG9TVHFyeEpROTBubmt4USIsIlktdGlNNkdDUjBqcUc4RWN0UHp1OHlLWi0wU0hrN3hnekVRREpRb1h0Z0UiLCJrc3Q3SWt4NGF2TWVUWklZQmNLWmdkSUZINEZPeXRtbGhaVzhSVmJDbDJzIiwib1p5TGY1WHgzQnNxMHVSZWlwNmRiNUVnUUJKWU41MzFpazE1Yll1MGYzMCIsInpuUExhQUkybHJlZFRuNC12YWhNbUFtaXJFbUxzS0owWUNIeHRqd0xxZGsiXX0sImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyJiMGdFR0FmeWQzak1nY3NBdTlldlpVZ2t3RGJyMWdxZEFSVGJnWWMwcUY0IiwiZjRhQ2RqS09wMDhKNWZMZS1DdkliVUtpcHFxM0pVSHVoa09OWDNpVkZoUSIsInJidmNvZjhpdm8tYW5pZlB2Qm44aklyTnFqQzJPUndHMVJJc0ZKY2xjNGMiLCJzYjFHRk9pVkZoNEY0ajU5R0pRU3lDeE9lLXN4WDliVVJYUmtQd1ZXQ3lrIiwidndKM0tHMkc1WEhETXdFOWk4SW55LV9WUENxcW9UUGxvZVpFSUQxSGlORSJdfSwiaXNzdWluZ19hdXRob3JpdHkiOiJQSUQ6MDAwMDEiLCJpc3N1aW5nX2NvdW50cnkiOiJHUiIsImlzc3VpbmdfanVyaXNkaWN0aW9uIjoiR1ItRiIsImRhdGVfb2ZfZXhwaXJ5IjoiMjAzNS0wNC0yMSIsInRydXN0X2FuY2hvciI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMy90cnVzdF9hbmNob3IiLCJpYXQiOjE3NjI3NzU4MjIsImV4cCI6MTc5NDMxMTgyMiwic3ViIjoiWTA1d2VhSDNlVzFuUU14X2hFZHFwOTFkZENwRjNYV0daTFNjUy01VWNJMCIsImlzcyI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMyIsIl9zZCI6WyItS0kwZFpNSm15TnFEbk5mMHZlTzRIZjE4dWcwb25rckQ4UzhONy13RUVZIiwiOG5YRUI3emVKdDE0Y2dLbk9pMmlGandvTlJoZ0t1eW1XT0hYMm04RjVUTSIsIkJES01ZcWVBVXkzMVpzMWp6SVhSUmk5alVuT1BZWkdLT3FjNGFLSTZqeVEiLCJGMDV0T1dnRXFGZ1ZKOXRxTTlQTXZRUS0tQS1rU1VVMG50RjZfTG1EVVp3IiwiRng2d3N3bTdUeVFUek90UkpQeXpPZkFEbGdTSnd6X01OSC03Q1JTUnNNdyIsIk92ZnRPdjM0azJieFhIRVlHOExfX1NfMXdES2NEeldSbTN3bnIzRGVfY0EiLCJYc29kX1c0c2ZSenF1ejZjTHlyeU9EWkNmUW5qN0lPdHVjWTcyMHJQNXBRIiwiWVFtaXk2Z0gybnJpWTdFV2U5UTZHN2lBT05PSERFQ3doNnFhaXVqYlBzbyIsImI5dG45aVI2b0lITHJ1V2RRS004UWtvbVUtM3ZMRHluSkszeV9IZjBXUmMiLCJjMmFpTGlKNUFqaGwtVjctNmVkaW50d1VPVW45eEFHVlY3eHE5V3JvbnFjIiwialZ2cDV0Mk1yeVVaeHQwNmtXQ0ZaR2ZvckhYTkhJMjBsdXFZel9WOEN4SSIsIm13dWltaF94TEtyQlliRlp5Ul9fN2Y1c0twQ3I3eld0RHE5V0dfLVNSbDQiLCJ0U3BXbnlPNU1qSnpDVGtjZlBDTVlOU3BQMUJwZFR5SmxzbkFpN0RuSTRjIiwid3I0dlFSbFR3Wml3VmJrVDU0eHUzN0RrMmQza1Y1M09TNFd6ZlhvVzFtVSIsInphcnYtUUhqNjBQRDdfdWtoOC15cUY4ZFlfRVFXU3doMDhBdy12bllMNnciXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.UCKwXB68XPeJDimIDEMUGo105UZGm1h2EXXaWRKCNPTYJ4gySfUMi-VK75SEf8tqULKqvAVZ1MYnPVdIZ4ZIig~WyJScmN4UW0tTXhHSG14UTdRalhYeFhnIiwiY291bnRyeSIsIlVTIl0~WyJ3WmVOcnlyZGtES2xqd2RYUXZOY1F3IiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJVLTQ0bC1PNV9xX0dOZFVDa2NZdmJ3IiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyJxM1ZxLVRaWWJydjZsenNDYXVOUjl3IiwiZm9ybWF0dGVkIiwiUmFuZG9tIHN0ci4gMywgMzQ3OTMgTWFuaGF0dGFuLCBOZXcgWW9yaywgVVNcbiJd~WyJSY0xteEZ5UXJOZFVhRDlWNE5WR29BIiwiY291bnRyeSIsIlVTIl0~WyJrdkx2NzhVQWdTUVgtZWhJbDZnWlRBIiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJQbW9ZejFhcGxoR1ZHakNnVDJ6N09BIiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyI4dGtyanBESkwxUkN3QTJBTnU5UUhnIiwicG9zdGFsX2NvZGUiLCIzNDc5MyJd~WyJuVUJYejlyM0d1VlRRdWZKMTVEREFnIiwic3RyZWV0X2FkZHJlc3MiLCJSYW5kb20gc3RyLiJd~WyJudFZkQTVmN1lTT252NmN1YXJXVkRRIiwiaG91c2VfbnVtYmVyIiwiMyJd~WyJXa3ZUUUd3b3lZQjZkOFVrb1YxMEtRIiwiMTQiLHRydWVd~WyJHeXhLdzBYUHNlanQ1X21uUmRrQmFnIiwiMTYiLHRydWVd~WyJLaXdSLXFkcGFEZS1iZEJvVS00WFhRIiwiMTgiLHRydWVd~WyJmZUdQb18tTUpIbVZ5Vy1GTWpjd21RIiwiMjEiLHRydWVd~WyJyU05aSUwyYzZtWmMzNjJDWGtEOXRnIiwiNjUiLGZhbHNlXQ~WyJscENJMTk5VTIxcjlNQ2NrXzlzem1BIiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJVR2NRZ3Q3NTlBUnJHZmI5WUJPR1dnIiwiYmlydGhfZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJwUlhabV9QSFY3M0pyRjZRdXBTVWFRIiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJONzk4MmJRNnJCVHlSekhLSTVKWXVRIiwiYmlydGhfZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJPNkhTdWhuMHNIQ3JXSEhJd3ZKOVVBIiwicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIiwiMTIzNDU2Nzg5Il0~WyI3RTBMZ3VRR2RFT3huVjBOb0FMeDNRIiwiYWdlX2luX3llYXJzIiwzNV0~WyJKLXpqWVJhX2VQZ0JtaXF2LTM0Tm9nIiwiYWdlX2JpcnRoX3llYXIiLDE5OTBd~WyJzaTk3QXgyeFpUcXBlSVE3bEFlMVZRIiwiYmlydGhkYXRlIiwiMTk5MC0xMC0xNSJd~WyJ3akZIMHJJNUNWWWtSMzFwWl82b3pRIiwiZG9jdW1lbnRfbnVtYmVyIiwiMTIzMTMyMTMiXQ~WyI4ZWFzMXZEaG1iSzJtUjFoTWVteUZBIiwiZGF0ZV9vZl9pc3N1YW5jZSIsIjIwMjUtMTEtMTAiXQ~WyJWN01BNzNjX28tNGZFQlVPeGxWdkFRIiwic2V4IiwxXQ~WyJsU282UlppaVlKajNETE8tSXVKRnlRIiwibmF0aW9uYWxpdGllcyIsWyJVUyIsIkdSIl1d~WyIzcmFya3NDcWRPN1lBNHBYU2Q1eV9BIiwiZW1haWwiLCJqb2huQHNhbXBsZS5jb20iXQ~WyJYeFFMcDFTb0VzVkc0djUwQmZVOUFnIiwicGhvbmVfbnVtYmVyIiwiKzMwODM4ODMzODM4MiJd~WyJlQ3NMY1U1YXQycTEzWldYZldtTGJnIiwicGljdHVyZSIsImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRRUFTQUJJQUFELzRnSFlTVU5EWDFCU1QwWkpURVVBQVFFQUFBSElBQUFBQUFRd0FBQnRiblJ5VWtkQ0lGaFpXaUFINEFBQkFBRUFBQUFBQUFCaFkzTndBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUUFBOXRZQUFRQUFBQURUTFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWxrWlhOakFBQUE4QUFBQUNSeVdGbGFBQUFCRkFBQUFCUm5XRmxhQUFBQktBQUFBQlJpV0ZsYUFBQUJQQUFBQUJSM2RIQjBBQUFCVUFBQUFCUnlWRkpEQUFBQlpBQUFBQ2huVkZKREFBQUJaQUFBQUNoaVZGSkRBQUFCWkFBQUFDaGpjSEowQUFBQmpBQUFBRHh0YkhWakFBQUFBQUFBQUFFQUFBQU1aVzVWVXdBQUFBZ0FBQUFjQUhNQVVnQkhBRUpZV1ZvZ0FBQUFBQUFBYjZJQUFEajFBQUFEa0ZoWldpQUFBQUFBQUFCaW1RQUF0NFVBQUJqYVdGbGFJQUFBQUFBQUFDU2dBQUFQaEFBQXRzOVlXVm9nQUFBQUFBQUE5dFlBQVFBQUFBRFRMWEJoY21FQUFBQUFBQVFBQUFBQ1ptWUFBUEtuQUFBTldRQUFFOUFBQUFwYkFBQUFBQUFBQUFCdGJIVmpBQUFBQUFBQUFBRUFBQUFNWlc1VlV3QUFBQ0FBQUFBY0FFY0Fid0J2QUdjQWJBQmxBQ0FBU1FCdUFHTUFMZ0FnQURJQU1BQXhBRGIvMndCREFCQUxEQTRNQ2hBT0RRNFNFUkFUR0NnYUdCWVdHREVqSlIwb09qTTlQRGt6T0RkQVNGeE9RRVJYUlRjNFVHMVJWMTlpWjJoblBrMXhlWEJrZUZ4bFoyUC8yd0JEQVJFU0VoZ1ZHQzhhR2k5alFqaENZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJQL3dBQVJDQU1BQWdBREFTSUFBaEVCQXhFQi84UUFHd0FCQUFJREFRRUFBQUFBQUFBQUFBQUFBQUlEQVFRRkJnZi94QUJBRUFBQ0FnRUNCQVFFQWdrREF3UUNBd0FBQVFJUkF3UWhFakZCWVFWUmNZRVRJaktSQnFFVUkwSlNzY0hSNGZBemN2RWtZb0lWRmpTU1ExTmp3dEwveEFBWUFRRUJBUUVCQUFBQUFBQUFBQUFBQUFBQUFnRURCUC9FQUNFUkFRRUJBUUFEQVFBREFRRUJBQUFBQUFBQkVRSURFakVoRXpKQklnUlIvOW9BREFNQkFBSVJBeEVBUHdEMG9BQXluUmdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ0tkY3c4a1Z6YVhxWmJna0NLbW1SYzFmb1pvc0JyNU5aaHgvWEpSOVRWeWVNYVhDMDU1SXlpLzJvTy95R2pwQTBJK0xhUEt2MWVveDdiN3lvdmhxOE9YNkp4bFhSTlArQTBYcDJaS2ZqUTZUWGZjc1dSWFRhVEdpUUk4UmxPeWhrRG9BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBZEFBSThTSzhtYU1FM0pxbDFzNGZpUDRneHdnNFlxYzcyNGR5ZGJKcnUvRmpCL016UzFmakdsMGpYeEo4MXNsMVBHNnJ4WFZhaHJqeXlhVjByNUdqUEpPY25MSk50dnozTWJqMU9mOFV3NDJzV0RpajV1WDlqbGF2eDdVNTIwNXJISG9vbkZsbGFlem9pNXRocmRucjg4K2ViSS9XVFlldXpTVlR6emZyS3puY2JIRjJBMjVhbS9xYjkyWStPYXZGMk0rOWU0WkxyWTQ5NzRiWVdkeGR4azE3bXZmY0JyZXc2N1Bqdmh5elhwSm5VMC80ajFNSWNHVnJKWEp2bWVkVHNseGRnUFk2ZjhBRkdPN3k0cEo4dmwzczZ1RHh2UTVWY2RSSHZleDg2VW15YXl2dkgxQStwUXp3bXJnMUwwWk5QOEFNK1k2Ylg2alRQOEFWWkpSWGtuc2R2U2ZpYk5qcmpVY2xld1pqMm9PUG92SGRKcVpLUEh3T1hKUy9nZFpaRkpYSGMzV1dZa0NNZXBJcGdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWczUnJhdlVSMHVGNUp5akZMek10d1R6YXJGZ2c1WkpLS1hjODVyL3hROGJsRFRZcVhTVW51dlk0L2kvalV0WmxmQWtvK2ZVNUhGeE1sVWpmMVhpZXAxVnJMbGs0dC9UZTMyTkw0akk4WEN1U3Z1UVNrK3FYcUdwT2JaRnl2cVkyODIvY2kzZlJJQ1RkR09Mc1JCdGpOWmJzd1RUc0dGbW9FdUxzWmF2cDlrUmFvM1dXWXp4ZGpOcnpJYmRBTWJxemdVM3MxNkVkNDdFU1NuWFFXRXVzcDMySDhRM0dmWWRER3BjVzNJeWxUdUpBTjBCc3h5N2JMZnFkRFErTjZ2U2ZLc2psSHBiNWYyT1RHVjdwbG5DcEt3UGUrRStNWTljMHFxYmpiajVQa3pyOFI4dHc1c21teUp3azR0TzAwZXE4Si9FNmRZOWI4dTZxYTVlNXN1TXMxNmxPekpWaHl3eVE0b1NVbDVwbHBzdXBBQWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWdNbUc2TWNSNS94eng2R2tVc1dubGVXdWE2R1c0MlRYUThUOFowK2doODF5bStVZVRQRCtLZUo1OWZsYzhyWFpMa2thbW96enlaWE9jbktUNXR2Y29ic2xzbUpOVzBpVFZjMlpTcnJ1UmxMY05KUzViRlpOTGExMU1mUytyZm13SW1VcjVDVXVXeVhvUjR1d0dURXVobjJiQUVVckpiK2dHL2tBMzcvWXkxWFZHRExqWFFDRFZHQ1VxMkkvZjNLbFRaakNWRXVaRnVqSXNKY0NjWmI3a0RMZDh4WVNyT0h1T0haN2tPTHlNL0U3RXFSNUZ1UEkyeUcxTkl4R1hDd0w1Zk5HMmpFWk9McXRnblVyZkprcFZHVFZXQjFmQnZGcDZEUHZjc2JWT05udXROcWNlb3hMSkJxbjVuekNNclZleDFQQ2ZGSjZQS281Y3pXSGsrdEJsbXZvUEYyTW5PMG12MCtxVVZoelJrMjExTitENGsyamRTa0FDZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTXVSbHVqei80aThXL1JzVHc0Vy9pU1hONzBaYmpaR3QrSS9GNFFUMDJCOFU5K0oxc2VUbGtjbStiN3RtY3pjbnhTazVOdnE5MzZtdjEzSlV5MVJPTWQrWlgxOUdTM2s2QW5rZEwxSWNKYzhjcEpVakh3bXVld2JaaXR1dWhrTlVZVHZvL1lNWTRlNWhxaWFpMlo0SDVNQ3I3a28ybmZJcytHekxqZm12WUt4RDcrNkk4UGN1aml2OW94d3BMekJpcmg3bUdxTHV2Sis1bjRMdmV2Y01rMVR3OXlPeGU0SmxrTk8rdHIxUmxyZld0UGdZNEdiNjB6UzJWZTVCNFZ1a2pOUFd0UGhRNGU1c3ZEdXdzZEt1WlJqWCtFekhDYml3eXJlMTJId1NkTWFpVk1TaXVIWm0zOEZ0YklybmdwV2JLZXRhL0Z0VkUrTGJseUl1RlBtWStscnF1cHFXSE9ueUxWUGFpdjZXMDl6TFY4Z3kzRzNvdFprMHpieHphVGF0TGEwZlEvRGRaSFY2U0UwNDNYSlBkZXg4eDR1RjNUcnVkand6eHZOb25DT1M4a0k3SytpOGdmWDBST3pKei9BQTN4SEJyOEh4TVVxODArYU43aUtsU2tBRFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFLTlMxREUzS1NSbHVEUzhXOFV4YVBDOTBwTmJkWDdIZzlUbmVmUFBMTGliYjV5NWx2aXV0bHE5Ukp0dDc3ZHR6UnlTcEx1U3RpYzNSWHhkakRkc0pXd0pKY2JOM0ZnNFlweTJ0Y2lXajB6bFVuSDBPbkRTMzlkT3lPcityNW40MHNXQ1R4cmIySVpNTlhYTHpPeDhIaWFnbSsvb2EyVERicnk2RTY3ZXJteHdLVGJlNkxscGJmeW82V0xSOFQrYmtiMzZQR0tXMjdNdlZqSnhIRFdoZGJYN2t2ME5kWXY3SGRocDQxeUpmbzhleUoxdnJIQldqYStsR1AwT2ZYK1ozWmFkT3FTL2dSZW5jdHR2Zit3MXM0bGNQOUVhNVY3c3JqcFhPMUg3MGQzOUUrSy9tZnlyb2xWbGtkSzhmSkpma05aNnh3NDZCcTkvNWtaNlZ4cTI5L003engwdW0vbXl1T2xiZHVMMzgwUGFucEhMeGFUYXVKK3hjOUh3TGVhZlpvNmkwa212NjdtZjBiaGZTMy8yOGhwNnh5RnBaUGtrdmN4TFJ6a3QydmJjN2Z3RXFyYWpNc05yYm1OUFY1eU9oYnR5ZCtoTmFLS1czWHNkNzlIY1ZTM0lMVHBjOXg3VTlJNDYwMUtvcDBTaHBrcmJpbGIycEhYK0F1aVM5RVplTklhcHlWcDBsYTJzcG5wTi9wT3c4ZFBseks1WXVRMWxtdlBadEpIeU5UTGdjV2VreVliWnJadElweGFTWHVkSjBqcm1QT3ROcjBJcDBkRFU2UndWcGJIUGFwbHl5dU5salBGczlqTVowUU1wcEYyYWlYSFQ4TTE4OUZxWXpqSnBYdnVmUU5EcUk2akJHVWVxdGR6NWVuVFo2WDhOZUwvRHlSMG1vYWNYOUVuMGZrUzJ6WHR3UWhLNEorWk1xWFVnQU5BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJISmtqalZ5YVhxZVovRTNpVlErRXIrWmMrUjZEVVM0SU9meXBKTzNMa2o1MzRycVphalVUbTVPVGJJVkduSjhXUzMxWlZtK3V2SXc1ZHVwbnY1aHJIRHR6TnpSNmI0bVZiV1VZWThjMG0wNzhqME9nMDN3NEoxMEp0eGZFMnJkTHBveGhGMXlOeU9MbVp4cTBYcFVjTGRlaFFzTkoxekl4MHpiM3BWMG8yaVNWR0NwWTBrVzhPeGtzQzVFZUh1U0FTc0NQQ1MrRXFKSlVaQWdvVjFNOEtMT0h1U0JiaW5nWGt2WWx3S0w5U3dCTnVvOFBjZ29wRnBocXczRmJTZk1pMVJZMVJnTlFJeTZFK0h1WWFvQ0RWRUdxTFdySUFWdFdSYW9zYW9pMVlUWmlocE5GY3NXNXNOV0dyREhQejZaU2p2YnZ6T0ZyZEs0dHRJOVJKVnVhR3J3S2NIMVowNXY2anViSG1KeDc4aUxWR3hxWU9FMm1xS1dyU084cnpXSUZ1R1ZTNGlycVpUcG0yYXlWNzc4TytKL3BXRlFuTDU0Skpwbm9FN1I4MThLMXN0SHJJWkxwV2svUStpYWZKeHdUMmRxOW5hTWx4dFhnQXBJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCR2JwYkVpbkxMaFM2OXVySm81ZjRnMUMwK2duSnJpazFzbnlYc2VCeXl1VGt6dWZpWFZ6eTZyaGswNHBiUlQyaWVleTgwWXNoOHo1YkltMWJva2x3WXVIcXpPQmNlVklEZThPMC96Y1ZuZnd3L1Zyb2FXZ3hiUk9ualZJNDlYOWVqaVpFNHg1N2t5TWVwWWxSeld5QVRTb0NLVmt5U1ZJeUZ5b0pXVE1wV1o0UUlrMHFDVkQzWDNBeUFsZlZleG5oWUNKbHF3bFJrTXFBTUdXN0RRdzFaa0FWZ3NJOFBjQ3ZoSXRXV0FDZ2kxUmExWkFDdHF5SlkxUmdJVnRXalZuSG51YlpUTlcwQndQRThDVnRLams5VDAydXhjVUd6em1XSEJrbzc4MzhlZnFmcWxxakhVbkpXeUxWSkhXVnlzeGJoZDJqMnY0WjFzNWFQNGNtM3d2elBEdytWK3A2TDhNYWxZOVRMSEs2a3VWa3FlNWp5SkZPTjNDK2hjVkxxQUFHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWFQaWVYNE9tbk5TU3BQWjlUZGFzNC9qeXJ3L05LS2NwTmM1YktLSnJaTmVGMW1TZVhQS1dTVGxKdmRzMTQvVnYwUk9YTXJTdUw5VEZKSjIzNkcxb28vT2F4dmFHUEc0cS83azlmRmMvWGMwa2VLSGticWRvMXRNdUdQb2JYUTQxMzVXeE1rWThpeVBVbFNRQUNva25aSkt6QllEUXdaTXhEV0VyQk1nM1lCS3lTVkVVNkpnWWJveEhxU0FHR3JJa3lDVmdsMENWZ3p3OXdNQnFpU1ZHSkFZYW9yTEc3SU4yQkNYUXJhcGx4RFlDbWZJd1Nhc05XR1ZYUGtVdFdpNlhRcmJ0QkxXMUVlT0ZIbmRkamNadG5wbXJSeFBFY1cvRXVuUTZjWDljL0o4Y2RxaU0rVExuRlUxNWJvcGwwOURzODlTeHUxNkhSOElrMXJNYVMrbVZuTnhxMGIvaDgzaTFlUEpIbW1nMTlHMDMrbW5UWFptd2FlamZGaWkxYWkralhMME53MklBQVVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFNU09KK0k1dVdnbmpoN3ZpcWp0VFYwZWEvRk9XTU5NOGNIeGNUM2Q4aWFxUEc5V09qQ1c3N0l4S1ZHTlpoOHo5RHBlSGMwem1ZK1oxL0RWZE1ucjRybjY3ZUxrWGxPRGt5K1hRNDE2VTRjaTFPeXBPa1RUb2tUU3NKV0RNZW9YSmlSTktpQUFzQ1ZrRXI2a3dKN2Z2TDdrRTBnQUpKMlk0akFUb0NlM21DQkpPd01nRVpBa3dsVzEyUmJveVlib0RMZGdyQUdXN0l0MFpJeTZBT0xia1k2QUFRYW9pM1JPWFFpQlZJcTZscytSRGg3aEN2STZSeWRhcnM2dVhrY3ZWSzdpVkU5T0pOVklxYTRYNk9qWXlLc2p2ektKL250WjZIbU9IWmJsMkIxSlB5SzF5WHFTeDhnUG8zaExUMGNaS1V0MHJUNkhRU281Zmc4cFBTUmpOL1BGYk5xblQzVy9WSFVpYkVNZ0FvQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUm1yUjViOFZ3VU1VYWZWdWtxM1BWU1BKL2lwTGdVdWFicE1tcWp5a2xTZmN3bGJaTXhCYk14cEZKTDFaMXRFcTRUbHJkcjFPcG9uYlM4aU8xOHgyc0RxRitaY1VZZVNYa1h0VWNiZGVoWWxiSnAwVjlTU2RtQzB6SHFSVHN5Qk1BeW5RVnFTVkdVckFTc0dpVmdtQTJYVUFUSUowQm5oN2lKbE8rbEdRVzRqRVNKR0c2QWlZYXNzSUFSNGU0NFNUVkFDc2czYkxaZENEVmdWeTZFU3dyQUdHNkhGMksrTHNCaHV3UmtSQ0ZlZFdjek85cjhqcVplUnpOVDFreW9ucHlOUzZtMitwVGxkWkdXNnI2MlZTZHY3ZnlQUTh5RWVUKzVPSzM5eUVPWlpqVis3UUh2L0FNT3R6OE14Y1ZmTHNtanJwVWM3d2VLV2toSkxoNDRweVM1WFhNNktkbXhESUFLQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRVpWVzU1ajhVcHJBcFUzY3F0K2g2aHF6aS9pZUNsNFpOOHFkazFVZUM0dm41Q0t0aHFtZ25VbVkxTEQ5ZG5aMEN0S3poNDN4VG85QjRaRFpiRWRyNCt1cENPMTJXTjJRaXJSSk96ZzlES2RNbVFKSlVCWVdGWlpDU29DWUlMSmZJT2RkQzVCY25aTktpbU11ZXhZblJvbUdxTU4wWlRvQUFETE5BQXpIcVRKcmJkWUNWaE9pVGRHNHhGcWdTYm9qS1Jvakl3Sk8yWWxMa0pNQnVtVnAyRzZLbTJ6S015NkVURGROQ2ZJbFdvU1ZvcjR1eGFVdFVEUnV6QkJ1ek1lb1NaRmFPVnJGVGE4enJtanI0WEJ0RlJQVHorZmVWZVJYUGt2VmZ3Slo5NU4rWlZkcCtwNkhtUS9hOXk3R3JLbTl2Umwyblc2WGNENlA0VlQwR0orY1V6Zk5MdzJEaHBZY1NwOEt0RzZiRUFBS0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdHNk9QK0k4Zkg0WmtTZFh6N25ZYXMwL0VzVHphV2VPSDFTVkltajVySlcyUjZ2dWkzS3YxODE2N2xjbFJpMk1LdktqMVdoaldGSG1kTEg5Y2xmVTlYcDQ4T0pFZHVuRTFmSHFSNkVvOVREVk5IQjNTU29tM1NLNXpVSTI3OWpTemFodExvL0t5NUdXNDJYbjN1MGw5eWF6eFR1K2h5WG5TVnAzdVUvcFVycTFmWm15YW05dTlEVVJ2Nmt2VWZwRVpiV2VlbHJIeE9LcE5mdk5GWDZibC9lZDlqY1RldGVvaHFJUlhNc3g2aE5PLzRua3YwN0xmMU4rNW1PdXlwcXBQL3dBbU1KM0k5Z3Mxa3ZqcStSNXZUYTJUdE5PdXFONkdvZlc2UkZkSmRkaUdmelg1bHZFam14elZ2M05uSGtUUktwTlg4VlB6c3NOZE8yV2hWbXBOMFk0aUhGMklOMGdTWXNsUGxaVkxLa3R1aFJreVhkR3JQS255ZC84QUFMY2RENHFwZGl1ZWRWdHVjek5xVWx0S242MGMvTHJYZHVUKzVjam5ianR5MU8vTDh6RDFhdW1xcnVlYnlhMmJmeXpmc1EvUzVYVnQrckt4enZXdlNTMUthMnAraktwNnVMbzRVTlhKOWVaWjhkdGI3b1l6WFlqcXViaTA2NTB6WWpsak5iSEN4NWs1cE50ZDdOekZuNFg1bVdLNTZiOHVnanlLOGVvV1R5MjgyVDYwUlpqb21VYW5IeDRwYmx6ZEdKOHZZd2VWMVMrZCtwcXI2Wkx1YnZpQ3JQa1htYUhuM1BSejhlYnI2ZEtOdlJxOHNMZGZNa2E2VmZZNlhnT0JaL0VNU2JyaGttL1FwTDZGcDFlR1BvWEZPSi9KWGtYR3hBQUNnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaVJUcUk4V0RJbFNiaTFiOUM2UlhPUEhGcnpKbytiZUlhZjRHcmxIbFROYWYwSitmOUR2L2kzRDhQVlk1cGJTWFB6T0N2b1M4a1l0Wm92L0FKRVY1czlYaVZZMGVZOE1qeDZ1Q1BVUlZJNGQvWGJ4VEluRG1ZeU9sZTRUb296TnRWdHQxWk1qcGJqVDFPYTVYL00xSnljdWFsdis3elp2UnhxMjluN0lzV0tMZm4vdWQveUxjM0xsanl5WHlSNFYwNmxUMG1wbmRxZjhqMEN4cXRsUmFvcHIwNmk5WTMrT1BNVDBPZXJwL3dBU3A2Yk9tN2cvc2VydUhXbDdGR1hOZ3F1T1A4UjcwL2pqekt4WkZ6NHZjeW9QdXZWSFl5NWNENUp2MGl6VWVUQmZQaDlWUnVzdk9LOERmRmJTMjhqb3dkd2l1NXE0MUdXOFduNkYwT1JscS9Wc3diZTNUYitORy9wOG0xcGN6blEyTnpDYzNTTjdHN1pjVlE1bGlkOUtKNWFqT1czSXJsTFlzeU9rYW1YSjJOdHdWWnBmYzBYa3B1L1JGbWFSclRkMGF5cXRWUGlmRFRYZmthT1NNcFBaTTNtcVpWOFhFblNibS84QXRWblNWeDY1YUx4VGZRbEhTNXBPbEIvWTZXTE5qLzhBMFQvK2h2NHRWaVMrYU1vLytEUnZ0VDBqaC84QXArZEsrdmtpYTBlYUszVDM5enY0OCtueS9UT0w3V1dwUWZMWXozcC9ISG5QZ1pzZE9XS1hEMWJwRW9wcDdjU3IvdVIzWlFpM2Jvb25wNGMrR0s5bUoxclp3MGNXWGZobGE5VG9ZWGNMTlhKaVNkSjgraFpwbTQvSkpQZmRVR050T2czWTZHRG02UFArSy84QXlaSE1xMi9VNjNqTUt5OFYzc2N4OVdlam40ODNYMHJmMVI2ZjhJWVZLV1RJNCtYTkhtVzdtZTcvQUF4cHZoK0hSYjJjbmYzS1M3S1ZLa1dFVTZSSXRBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBWWFzeUFJVCtTTGI2SE56NjdOeFhpcU1PbHJtYlBpR1Q0ZWxiODJjeU9TR1dDbENTa3V4eDc2c3IwZUxpZFM2MFBIc3NkZG8wNXI0ZWJFbTFUMmtuNUhsM0hkYjlIL0FKK1o3UFU2YU9YRkpQeVBMNjdUL0F5OEN1bHVqT2VyYWVUaWMvc1crRDQrUFUzZkk5RWxaeFBBMG01Ti93Q2JuZFRzanY2cngvRVdxWFBtYTg0cTlxL2diTStXMWU1Qnc5SDZJaDBVbVU2SlRUU1JvNmpMUDZNZk45UUxwNnlLbndZMXh6N01weWFpVmZyY3FoL3Q1L2RtcG15clRZNmd0L3paSFM2RE5yUG4xRXBKU2YwblNSRjdxeWZpZWtqK3c1di9BTG5aaVBqYXFUaGhpNHBidXVScStKNlJZTlY4T01WR0tpdVJTLzBMSDRmaytKeHZXUElsQ25VWXgydHYzczZlc2NyM2NiNjhaeHVWVHdwTGsxUmZETHA5VWxTU09abWpwbjhONlNFNHhlTk9jWk82bjFwK1I2SHd6dzNGcS9Dc2JtcW1yNFpybWw2bVhtYWM5V3h6NWFCUWZGamRlaE9MbGpmelJiN28ySlk4dW0rWEo4MFYxUmRpaEhKaWRxMUpjMTBPZHVQUlBpbUh6TFkyOERvMWNNZmh6NGZNMm9jeUd5NDZHSXY2R3RpbDJMazZKNVZacUdXUGMwTXB1WlpiY2pReTh4OUdya1ZtcTNicEpzMjgwYmhTNms4ZW5yRnMwNVM3Y2tkSWh6bGhsbWRkUEx6TFhERHA0Zk8wcTZJc25jNVBIaHR2elJ2YWZ3ZUtqOFhOTDRra3JTYTJUS2sycDY2bk1jYVhpMkRFK0dPUDNEOGF5UWJTd3UxdjlMNWVaek02YmM1dnJKdjh6YXlZZkN2L0FGRFU0c1diTzlPc1VuaXlOcTVUU3RYdHliMk9zNGxjZXU3R3l2R05QbWFXWEJGeTg2TDhlU0VsZURPNDltN1RPTmh4L3JjU3BPNUxablIxZmhQd244VFRTY1crYVpsNTA5NjNWclhpbHc1MGt1azQ4amFXUlNWeDNPRGgxVTR5ZUhQRlB0UnVhVnZEUFp1VUg5UFk1Mk84dXVrbGZYOGtRVUtlMWV5TGNhdEV1RHlPYldJUjI4dllkU1NWY3lMZG9EaytOUitXTXZ5T00xYS96c2QzeGRYcC9jNFNWSnIwTy9IeDUrL3F6UllKYWpWUXh3VnR2a2ZRY1dYQm9NTU1MbGMwdm9qelg5RHluaEdqellzNnpSbHd1blZIY3g0VkZkZCtyM2JIWGVYOFZ6NHZhZnJxNmZYNHNzdUZweGsrU2ZVMjA3T0RrbEhHazVTU3ZsWjJOTE41dFBDVW52VzV2SGQ2cWZKNDV6K3J3QWRYRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTGRnYW5pTWVMU3YxUjUzRGVuMUUydVRlNlBSNi93RCtOLzVJNEUxV1daNWUvcjIvK2Y4QXEzb1M0NEpubmZ4REhoMUdOcnF1WHVkeU40L2M0MzRpZno0NWRtdnpIRnluaytKZUJxc1RrZFpLem1lRUt0TjduVWh5TTYrczQrSkpXeXhLekVJN0tOOGkzaDI1a3FhbVNGWHZaejgwTFRTNm5VeXczMktuaVZNRGlyUnFVcmt1UDFPam8xOE4wNVhYY2s4ZEVvZVIwblZMSllyOFcwSDZiaVdUQkw5YkJjbnRaNTZlSnhiamxoS00xMGFQU09Na3FWMzZzS2JiK2xjcjMzTjF6OUhIMFBoMlRWWkkwbkRIMW0xUjZwWjhHazAwY1dKY1NpcTJab1BKSzNzbjcyUjR1MjQ5cXFlT0o1dFRITXFuaGsvSjN5STRGUDQ3a3NiaEIvVmI1bHVOYjh2dWkzSms0WU5vbTdWVEkxOGtGeG93TjZ0aUt0b2hzamJ3dWtYZmYzS01YSXRqMXNLUXlPelR5OHpieUswYWt0MzZBVnRjVTBpZXFjM2lyR25YV256UkJ1dHpaeFpWSmNsN2hNdU5iQm14Nlp0ZkNhVXUxTyt4MGRQcnNVMGxKT04rWnJaMm4wTmYxaXZkYkhTV3hQck9xNS9qSGhydzZpV2JCOCtHZSt5NUhKK0ZqdTBqMURsMlgyRW1rMDNqaTIveU45a2VqbStFZUhQSm1XcHpwd2hENlU5bTJkWFU1SXVGS3ZWa1BpT1N0UmUzbHYwSEQ1ald6aWE1bWJTeHR1bnYrOGkzQmpVVWtianhwTWxERnNjN2RXWW9vMjBxS1lSM05tS3BHTlY4TzNNcHlKSkd4S1BMY3FueURITDhTVjZabkUwOE9QVlk0K2MwZC94SlhwcEhGMGpyWFluNVNzNjgvSEhyK3oxdUZLT05VdVpIUG5XT2tsYmZKRVhrYVJCd3VTY21jbm81YWs0U3k2bU01eWJ2cDBSNmZ3OWY5TlhrMmpqdUNqbGgzT3hvbmVKLzdqcDQ3bFIvNkp2TGJBQjZYaUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF4OVFib3cxUUdwNGgvb1JYbkwrVE9LbGVkcnVkeldxOEYrVE9QampXb216emVTWlh0OEYvNFNuRzQxZk00M2pzZjFlUC9jMStSMmNqcEhHOFRjOGlhbDA1RTgvVzl6WXY4SFZhUkx1em9ST2Y0US8xSEQ1SFNIWDFQSHhZblJOT2lwT3l3bDFDSFZsaVRhM0l0V0UyWXFlTk5ENFhreTF4YlFTcEJpbjRUOHg4Sitac3g2bVdyQTBKWW50dTM2bVVramFuanVqQ3hwTzJYSml0VmNQRHpJU3VUM05od3Q4eXJJN1JOdW1LMnJhTTlUTVZlNWd4cll4dXliVklxeHVrV0JVUXk4a2FrbGJOckk3TmJyWVpickRWaEpyZEZnakhudUdXYXlsYXNod2I3R3hGVnVnNHBsYXo0MTQ0dWU3WG9TK0cvUDhBSTJJeDRic21rbVpaaHJWVUd1VE1mRGZtYkx4MTBIQ3pFdFRnZDhpNkVkK1pZMVFUb0RIVW4wTU4wSmRETGNYSXhQa2E3NmxybHM5aXVUdG1zclQxcXZUeVhZNGVpVjY3R3U1MzlUL3BTOURoYVJjR3BXUi9zZFBNNno1WEh2OEF0SHBkK0lzeUw1RzF0UnI2WE44U0NYRFZtNGxjV2NuZVRHc244VFVRUjJ0Ri9wUy8zZnlPTHBvVm5iczd1a1ZhZUxmN1RiT25qbTF6L3dEUmYrWTJRQWVsNGdBQUFBQUFBQUFBQUFBQUFBQUFERFZtUUJqaTdFU1VpSUVNeXZETkx5T1BHUDZ4dStaMnppM3d5a24wZEhIeVI2ZkJmeXE5UkxzYzNOaVdYNVdibVoyeU9ISGJkK1J3ZWkvR2o0VkZ4NDR0VjUrcTVuVGJzNVhoK1g5ZFB2S1g4VHFGZGZYTG40c1RwbHFkbEVPWmVuWkxyTHF4S2pJWE5Fd0lHRTdMQjFBR0cydWFNZ014QWp3c253OXpMZElOa3hYTGthK1JXMGJMVm8xNTh3SzV1cUlRNW1KTzJXWTFUQXV4Y2lmUXdsU0p5NkFVVDhqWDZtemtkN212dzdjd0pwMGlmVTEwN0xjYnZjRFlqem9ud2VaQ0N0bHFkTUFFNk04UGNsN0wzRE1RU3NiazlpUEQzRFpNUTRSdExsc1Q0ZTQ0ZTRGYlZGY3ZsZnFXejVsV1hrZ0lOMlU4WFlzbDBLZ3lxdFQvQUtNam42UFQ4ZUtXUmMrSm8zTmJOTEM3WE14NEVsTFRUaS8zcis1ZWZqbDdmOUx0SkhnYVIwa3VKR21sVWpkd2NtUTdLTVVibTRycTZPNUZjTVVsMFZITDBHUDRtdHY5M2Y4QXo3blZPL2puNDh2bTYyNGtsUmtHRXFPemd5QUFBQUFBQUFBQUFBQUFBQUFBQkZxaVJBRExWR0FBQnljOGYxdVRmOXRuV09YcW8vOEFWWkZmTm5MeS9IYncvWE9uTGZrWFFmREJ5OGtWWjhNbEszMU1hZk91UGdsdGUxbkNYSHF2eHg4VWxEVnlsemp4T3Z1ZG1QTTVPcGh3NnZKVWxUZHF2dWRiRytLS2ZtVzRjM0ZrUzdHNzNLazdaT0twSE4ybFhSNms0OVNycVdKMEZKTjBHck1SNmtnTXAwWUFBakhxU0hRdzFZR09IdWFtbytVMnNqcEdsa2ZIT3dLOFVWS1RzdjZqR3VIa1c0MG05d1M2ekNOK2Z1aXhxa1c0TWFaYjhOVlY4eXNMY2MvSXJacS9TL1U2ZWZFb3BicCtocXpqME1zd2FVbFRMTU11d3l4MjVrWXJoa241R0RkeHEyV2tNYnRFK29FektkRmFkSW1BTXQyaUtkdXVRYXNDTW0zc2hLWExZRVo4Z0l5bHkyZnNWVDVGc3ZsWG1VdDJ3VzRxeUttUUpUNWtKQkRTMTgwb3RWME5ud0pWcHBjdDMwOVdhSGlFdUtXMzdKME5GTll2RDhiNU5yK2JPbitPY205TnFmMXQrWmZoZHBtakNYSHlOeVB5UWJaemQyNzRYR3A1cDkxRTZIRjJOTHd4VnBiZldUWnVKMGVyajQ4UGsvdFVnWVRzeVdnQUFBQUFBQUFBQUdKQktoeGRoRURJTUoyWkF3M1Fib3lZYm9ERGRtRExkbUFBQUFITThRaTNudmxhT21hbmlPUGl4S2E1d2QrM1VudWJIVHhXVHB5M2tsTDVKZGVwcVp0TyttNXR0TnRkaXpwUjVIdHRlZjFQRittT2NselNPbHAzY0lwOURVOFFTZXFpdDkxMTlpM1NPb3liYTh6cC9qejM2MzQ4aXpHclRLTUR1RnMyVTZWbk4xWlR0c3NUc3JTcGxpZGdaYnNrbmFJODAweVVWU0MyUU9vQUFGY200Z1U1Y3ZrVXhkYkVzanV6VWViaFZvRGJicmtSeFpHc3FPSnF2RTlRcDhPSmN1cklhVHhiTThxV2JFbkh6UzVGZXRacjFrTXFvc2psNW5MeDZtTGduWlA0NWt1TmJlZkpmTTBYa2JrVjZuVkxnZDdIR2w0dzRUYWpoYlhtMkpMVSswZDdvUjRkbnVjM1MrS2ZIZFNnNHZ1emVlYTBoWmlwZGJXbm50VGZJMmtyT2ZnZFNUT2hqZG93U2FzeHc3OHpMYlhTeUxkZ1RCR1BVdzNhQU4yaURkbVpkQ0NWTmhsSjh5bkwvRXRidDE1RmJkaEtpU3B2MUtwOGk2ZlUxTTg2ZzFSc21zdHh6dGZQaW1rdTUwTk91UEhEWjdSUzNPZm4rZlBGUGs1Sy93QWp2NG94V05VdlVycVl6eGZVc0tTVGJFNWZFZmtrUm1yb3YwOFBpNVk0K2pkdjBSTW0xMHRrbTExZExEZzAyTmRyTFFEMXlaSGd0MnBmVVpNY1hZeWF3QUFBQUFBQUFBQUVVNk1BQVNpSW1HN01BWlRvd1pTc3dBQUFBQUFERFZxbVpBSEZTdEdPaGJxOFR3Wkc2Znc1TzA2NWRpbzhuVzY5M05uVWNyeEZWbHh6ZmRKRU5MYWpUOHEvcVgrSnhyQXBiZkxKT3E1bWxwc2pTZm1WUGpuMStWMWNNdGs1YzN1YkNkbzBNV1RpeVNmZFJYZmEvd0NadlI2a1dZMlhGa1hleGIxS29kR1Roek1XbEYwelBEM01HWlhhb0tpWFV5MVJnQnJEVmtaUjVia20waUxkZ1VUaXFPYnFZT0R2ZHArUzVIV2FvMThzVkwyQTVtUEhqY1duRmZZWTlOanh5ZExudVkxYzhlRjhrbmZTVzVyNE5WOHpjbjZGUkhYVGJ5UStIOUxJUXp1dVRObU9YNUhhWHVaV09MaTV1blJ0bXNsMXI4SHhWOHlLcGFYSEthNUwxTHMrb1VGYVZyc2pUelpOclR0Tk4rbitibXM5bXo4SEV0NHBLdk5sbUtQRTc2R3JvcytPU1NuQk8rck92aGlwcE5OYitSRm1Pa3NxZUxGc2JXTjBpcUpjWTFKdWlKbmk3R0U2QVNkb1RWMXZRRGFZRkxqYmJ2bVRidEVrN2JLcGRQdUUyNndSbHpSbE8yOW1xOHlNNWJXR0tjcjRVMmMzVVRmSGZtbWpienpWY05jMy9jME03cWFhNjh2VXFJdDFYZ1gvQUZuRFYzVjdlaDNvY2prNlNQRnExSnRLbDFPdHdxdG1LcmliRS9xTi93QU14S01IbHQ4VGRlaHorYlNTYmI1SkhaMHVMNE9DTUcwMnVkRmVPYlUrYTV5dEFCNlhrWlRvejlSRWxFQkV5WVNveUFBQUFBQUFBQkFBQUFBQUFBQUFBQUFBQUFFWndVNHVNa21uMFp6OC9oamIvVVphOG95Vi9tZElFM21WVTZzK09CclBDcHgwdVRKbXpyNUl1U1VWMTl6aVJWUlY3N1gvQUNQWjYyQ3lhWEpHU3UwK2xuaXNhY1pOTnJpWE90MXpPZDVpK2VyVzVnZTkvd0RlNzl6ZXhTY25mb2MxdWtrdHJmRi9JMjhPVDlhbFhUcC9uK2JFT2pvSjJXOURWeDVPTnRMOW5yM0xwU3RiZGRqbXZsWVpqMUlwMnJKZlUvUUxseEloSjN1RzdLc2s2U3RNS1Q0aVB4WTNTM1pxNWN0cHhpMDMxYTVJMW5xRkZidHJuc21HYTM4bVJKZks3T2ZxTTlPcWV5NjhpdlBxRzRXMm5TMlNmWCtacFp1SnhmRks1Tlc5K1JzbXA2NmEycHkvRXVXNmowNldOTzkzSjcxMDdrZURqYTUxWGwxTjdCajRJV2szYjJzNjI0NFNKUm53UWxLVDY3Rmp6eWpnVFhWbWhtemNXVlFqdkhrbjVtenFIOHNJTFpLUDVrcXRWT1Z6YzJrNFNYSmxVL2xrcFJUNEpycDU4bi9JbmpYeGNiZzA0MCtmY3JsRjAwNHZieUtreE5xV0NveWJ1dmZtZFBCbWNKVnpUNWI4amtSdFNqdnlleHRRa21xZm85eWVwcXVianZReVhkdFh0ejZseWxhMk9IaDFLeHJhVnE5N051R3FmRmNkNjVvaXpIYWRhNlhyYTlUS2RHcERVeG5za3ZSbXhCcVN0RWVxcGRYRFlnblRyekpOV1VFblRLb3k0cjdNdGtyMktmTmhDTTVLTEtjazZWTmY1Um1jcW5UTlhMTnViajV2bjdNMlRXVzRxeXkrYU8zSjJhdTA0OEwydFdpK2J0eFZWdS81bXEzVWZhdnlMY3E2M2hQaG42VnA1NS9pUEhQanBYdXE3bzZFUEN0VXRwWmNmdGY5QzM4T3g0ZkNvUGhjZUp1VzUwenJPSllqK1RxZkd0cE5IRFRLMCtPZjd6TmtBdVNTT2R0dDJnQU5ZeWxZU3NKMEU2QWtBWVNvRElBQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUNHUmNVR2tlS3pwd3pUYmlveDQ3U1hsWjdkcTFSNVg4UjQvaGF0VGorMnQ3N05mMkk3K0s1djYxcStKR01YUzRVbDk2TmxPbmQ5TDNmc2pSaEpWR0QzdHQrdjhBWTJzYTQwbkp2YnArZGY1NW5GMmkrT1I4TVZHMTFiZmYrWnR3cmhpa3ZRNWtNMVcycnRyMy93QS9tYldETmVTSzdjekxOYTZFZXBLTHBJb2hsNG50eTZQeko5Q0hWakprcDdjLzRHbnFzNmlsYmZzWFpNbFBoUno5VDg4WEsxY3VTNXQ5MzI3R3lhZGRLWjVWSnBKdDlsdDdKRlU1VGswbjBkN2JMa1hZRndyaFZ0M3pmOWkzTGdxS2lwTjhYWC9PaFRtcWp6cC84bFVvUFBsazVTK1hxcTZHL3BjVGtycmhqVlJSbDZlRVpOdXQvd0MvOVROVjZ0UERpY29XMDdlMjM1bTFQU1NjWTh1UmZESmp4UDVJcCtkaU9kcHZibjNHcm5PT2JxdkRaUXpxVVVxWFQySVQwODhrSFNmRkYvVCs4ZHlHWEhsVlM1azFwNFJ0bEovampqYWJSNUtVMjNiZTZYWHlKWjlQeHpUU3EzVE9uUFU0NFVsYjlDbDVyZkplNU9uOGNjZGFSd200TnBiV20vdi9BRG9qa3c4SzRvdHJ0WjJrOGVTVnkyc2prMDNHMVh6YjNkOWhwZU1jYUNsMVRUNjE5eTJFZDBycVhSNzdHM0xUU2l1VjM4dCtSSDlIalNwTDFXdzF6Vi9GbEYxSnk5VjBmcWJ1RE0xdTFmZEd0bHdiV211WFJXbWlHSnlqSk9xa3VUaTN2L1UyelZTNDdXT1VaTGJlaTFPelF3WmQzYVczVmREYlRzaDB0MVkzUnF6bnU1THFqT1RKd3BYdW4xS01zdUZyL093VGJpRThsMDlrNmY4QW41ZnhLYlV0VDFTNGR0akxWcGROM1h1Um0wb3huU3VUditaY21PYXBOdHlYazNYMk5iSGplVE5EQ3BKY2NxdDlIL2lMSk51ZkZ1bmZYM05qd21DbDR2ajRseGNLazl2U3Y1bGMvVTlYSTliZ3hyRmlXT05KUjVKZEN3d2xTNmV5TW5lVEhBQUJvQUFBU1RzaVRBR0U3SWtrN0FKVVpNSlVaQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUFjYjhSNmZpMDBjaWpiVXF0TGRLanNsT3J4ckpwNXhhNlArQlBYeHN1VjRiVHp1YTI2ZjhtM0tUaW9wYzVQK3Y4QVkxc2tJNGRma3h4dEsya3BjeTZFMUpxMXZYODJ6alhhVk5ZNVBFNU5wTGk4OStmL0FBWFNxR1dTMitsTDJ0bUp5cjZmTk5lcERJMVBKcy9sajFiNW1LYitQSXJlMUpMWkpGM0UrRFkwTVVtcjJiYjZMb2JuMVIvbVJaaStWTWx4YnQ3SjhseWI3bEVzYnlOdVNhYmRKVWJVM2JTVktsc2wwS3N6K2xkRjl6RzJhakdLaXVpOUVWNWRianhOSnRLOXR5NTRYbGpVU1dIUTQ0VzVKU2srclFhMDM0bmlTcUwreUsvMHZKTytESE5tL3dEbzhMdFJTOURDeHBYUzVsNUZ5T2VzbW9hLzBIOXlhMUdTUDE0SnIwcG0rc2U1Tlk3VDJYdWFySE1sckk5VlhzMFpXdnh5ZTgveU9uOEdOUDVWOWlDMCtQNGt2bFhUcDNESk5jOWFwWDh1UEpMdW9tZmo1cGNzRWxUNnRIVGVHQ1gwcm41R0pSMjVKZWdKTmNsejFLYi9BRkQ1ZEpGa05Ya2g5ZU9jZTdYOURkY2QvY2h3dWdXWW8vOEFVb1Y5YSs1REZxb1NkS21iY2RPbTkwbjZvdGxwY2VSYkpSa3VUU0pxTE1WTi9GeDdKSXB5Umo4UGozanYwZkl0aEJ3aFVsdi9BQktuUGduUDVWdlQzSlRacTNUejQzVXF2bzEvSm01TEkwbWpReHJobFN0d2tyU1hRMlcyK2UzOVExbWNyYTM2MmFtYVh5T1ZjdjRrbmsyNTc4NHRNclU0dDhGSnhtK1JVUmJvMytxVWx2OEFLcTllWlZkd2pGK2RmNTlpUzNsR1BrcS9pVXlud3lwcGJPLzgreFNVSk80YmJXcnYzL3VkdjhNYWJoV1hVeitxVFVWOWt6Z1N2SldPUDdjdUZmYzloNFBwSmFiUktNK3J2a2RPWmxjdXIrT2dBRHE1Z0FBQUFBUytvaUFBTXBXWmtCa3czUkVsRURKaE96SUFBQUNBQUFBQUFBQUFBQUFBQUFBQUFBR0dyWE9qSUE4ditJOUo4SExIVkp2ZDF3OU9YOUVjaUV1R1VaTHkvbWV4OFQwc00yQ2JtMitHRHIxUEV2aXhaZmh5cmFTYTlFY3V2cnB6ZngwWi9MQytiNVgzRXZsVWE1Y3Y4KzVSSDVjUEZiMzMzOXl4elNjYmJVVjBPYnJLMk1YeThXNmJybS84OURjdzBsWGthRUw0SEo5V3krR1NvUm4rMDFzVFZTNHV5U1VZUy9OakZpNG4yTmViVGZCZkpHM2hhVWZxZHRmVDVFcWwxZkdFWXR0TG1xRFZNa1JrN3RvbjJkSkZiVkVrMCtoRnV5RHlVVVc0bTFSaTF0dWEvd0FkMjl1VzVSK2tUU2Q3YldHYTZEeVJYTmhaRS9zY09Xc25QSzFkYmx2eDN3L05iZFhzWEppUGQxdml4NlU5dzFmTTVXanpPVGFac3JQTk9yTXFwMXJiNEVTTlNHVnpYemVaYXNqYnJ5SmJxMXF5VU9SRnF5VU9WRSt6V1ZDN2QwK3V4b1o0Y01tNzVybDVIUmJUTlBPMHVyNTllaHN1cHN4WGl1TGJmM015eVdxWGwvVkZTbHd2R3VYREpKcjNveE5KMGwzWDVtc1ExRDRad2xYWm12QnluR0t0TnFWcHYxL3VYWlpwUWtxN0wxNWxhcUtqVy9Gdkh2YWY4Nk9qa3N6MnB3YTlQNS93VE5YUEswMzVyaGI3L3dERFJibXkzSysrMWVmL0FBYzdOTjJsMXQvME5pYlhUOEh3NU0rcmhseHBWamR5N005dWNqOE42YU9IdzJNbFRjOTMvbjMrNTF6dHpIS2dBS1NBQUFBQUFBQUFBQ1NkbVNCSnF3TWd3M1Fib0EzUmt3bFJrQ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUNHVlhpbDZOSGl2RmRPMm81SXQ4WFk5dFBrZWJ5UXZuWHVqajNNc2RPSnV1QThsNGVEZTR5NFhYc1h4ZTZWcmJiYmZkLzhtdnJJL291cmJTK1dXNlhjbENWdUs2MEZ1cGpkUVNXL1Y5aVVQcVVkbStuWW9ubC9WMm9wdTY1RXNWWTF4YnljdHR5RkxzN3BjVHBWM3EzNWtzR1JKZkxlUnZyMFpWbVZwT1RyclZiL1lwK1BLTFVZcVRmMis1bG1qcXJJNU9wVjdGbkdqUXdwdEp0Yi93TmxPNWIvWTUyYTdTclNNbzhTSng3azBySVUwNVkrRk0xY21KcEpOblRualZiTW9saWNZdVNyYlpXdVJjdXN4eXA0Nm54TmNydjhqRTFVLzhBeC9tamRsZ2xLTFZQZlpiWDl5SDZOTlQzMzVsNmk4WXBoR25Gcm9remNTV1RmcVY0c0RUV3pWSkxsWnVZY1VlRk5JaTNGU0lReGNMc3pIWnZzWDhQY2lsU2JWUDNJVXh4SWp4cU52b3lFNWIraFZ4YmJKTHpvMlRTM0Y2ZHA3cCtsR3JubTNLcFJ0VnpxeUdUSnRVZDc2ZVpUankvRVVrMDFUNW5TVFhPM0ZqbDhpVDZkZk15NWRhNlhYZnIvbnFWOGRxVVpiTjgxNWM5ekVQbWRTdE5iV3V2K2JGU1k1czVYUzQzMVNmdjFOWnphZzl2b2QvbWtUeTJvY05wMnR1OUdxcDFCWGYxTnR0ZWFOa1pUVVpLaHQ1My9uMi9NdThNd3JQT2VTY1cxSlVuMk5URmlscThpakcxanY2dnNkN1RZSTRjRVl4Ym9xMzhaSTlINGRENGVpeFJiYitWUGMyU3ZUcmh3WTE1Ulg4Q3c2OC9IRzNhQUFwZ0FBQUFBQUFBQUFCTGk3RVFCbHV6S2RrUUJMNlEzUmhxaVFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQTg2L3Jrdkp0SG9KZERnNVA5V2YrNS94T1hrL3gxOGYxei9FZFA4QXBHTGx1dGtjS011REs0dEoyNnZ6WGtlcG5IaWhKWFd4d3ZFZEl1RDRzS1ZkT1YzdWMrYnF1b2xISXQwOWx6NWwvd0FYaGh3cEpUbHo3SEp4WkhHUzQ5Nlpjczh1ZDgzZnY1bFdheVYwRk5jMjB2VWpqeUp5ZXkzNkpXelMrTDgvT1h1VzRadFpWd3VqTVZycHd6Y0Z4M2xrZms3SndrM0trbjgzVnMxOFNqaXg4Y3BPNWJXK3BKNXVKSkpMbi9sOWliTlhManFZOGtZdzNmdjVsa3NpakRpZjJPWENmeEY4c3BTZld1ditlcEw0bnhXcXZndW9wUG0vVW16RlM2MytKMm02NHZKUGtSbExaYmJjMTM3bExrM0JMejJTOC83Y3kzRTJwTktXNzJjcTMySnMxVXVMc2VIZm0yK3RtSXhWV3ZQK1pLTi9FZU5ONzg1WHVZNHJXeWUrKy9vYXFYVVhqU1cvbTFaQ0xjWkpiU1Q1T3l5ZVZSamE2dmJtVjU0MnBaSU4zSGN5elMzRm0xYzB2SnhLVzdmRHlseVpWa3l0dzRuRnJhMmFuNlE2YmQ4UzVQOEF6L1B1YWpycGZtbHh3YXR4eVJmUTE0emNuVXFhOHJyL0FCbVp5NDhmSFNmb2EzL2RjbHZXNVdJdDFacUVrK1Vtbit5MmEzMDAwM0tMM1hVdHl5akpLMTh5K2xyYWpUZVI3MCt1NVVpYlcxa2tveVR2bi9Eb1F5WmVKeGV5cDgwYStUSjh1NmROOURDbW93bGUvbjk5emNUYW5seThTU2V6aTkwVTVKUE8rREdtNU45UFlyeVRjcHRyZVRWUHVkVHczU3hoampON3lrcmU1WFg1RG45cS93QVAweXdZbGZ1YjVoS2pQVTQyN1hUK3Nlamh0Qkx5SkdFcU1ucmt4NUFBR2dBQUFBQUFBQUFBQUFBQUFES1ZoT2pBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU1OMGNESi9xNVA5ei9pZDZYUTRNLzllZjhBdWY4QUU1ZVQ0NitMNmowWnFaOERsQ21yMk4zaElaSVgxL0k0UzQ3OVBPYTdSY0tlU0d6VzdkR21zdFE0WkwwM1BSNTlQeEtyVC9NNCt2MFVvWE9OTHNkdWVwOGNiR3ZqblNiVDY3RnVMSktONzE1czBvNUd1cjlpL0ZQaWFmN3ZRM0V5dWg4WGJpZTc1ZTVOZk1sRjdPVDNiNm1uam53emp4Mm4zTmw1WXBYczdYWC9BRHVUWXR0NDU4YnVWUnhyZXJKWTVVb1NVYXBiUlM4LytmeU5SejJ1U1c3L0FMRnVmSlUzTGJrMi93Q0JLcFc5aWZ6UmtudW9xdDc1L3dEQ0pSek5aNXEyNHFxdDk3L29hbUYybmJYbi9uOFN1TXVMVXRyYmNuRlM0NjBjdkEzMTNmMk12TStLMDYzL0FLR2k1OFRudHM1TCtoV3NzcEp4V3pxdnpmOEFRWTMyYjN4T1NyYTdKS2JUbW12NzhpaHpUcmJuUXk1S2JoZlBiK1JMYldNc2xQRTA1VnQvWTU3bXJmRjAzOWkvV1QrSHUxeWRtc3VhbDVXaTVFZGRJd3lOTnZwMS9tVGI0cWR1cktzYjRZTlV0dlAySzV5VUc0dnpSV0lsU3lOSnVMcjJOT1VsRkxmWjdtTTJSdE52NnBWdVZKdFI4K3BjaWRXdVNjVmJiNys2L3FRK0xMNGJoRk83NkVKUzVLUFQvUDVIUzhPMFRUVTVVNU5iWDBOdGtqSk5xZmgraXFVWjVMY3E2blh4UTRVa24rUmpIajM4aTdHcVRPSFhWdGR1ZWZ4bmg3bUZ6WHFpWkJjMTZvbFhUMGlkbVRDVkdUMnZHQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ011aHdjbit0ay8zUCtKM3BkRGc1ZjlmTC92ZjhUbDVmanI0dnFRbkhlN013TXQwZWQ2VkVsZHJrYStmRXB3NFhmZWxadXlqM0s1UjVibXk0aXpIbXRkb2VCdHdwOWtjOXhsamUreDZyTXJlL29jcldhUGlYRkZYek8zUFg0NVhtT2ZISmRWL25Vc2prZGNseVMzOWlsNHBRWHpjL0lwK0pLTGRQbVZacUhVZVpOUnZvN3I4eVc4c2R2ZHZkdjMvdWFFYzNCR3VlekxzZWE0TmVSbGl0Ynl5VytGK1JzWTNUWHQvUTVieVhTdnFXdlVOVlcyeEdLbGJHUFVjVTZyb3V2b1dTbHNtdW14bzRKY0Z2MEpyTW1xNWI3L21NYnJmamtjbTIxMFRyN0dKNXJ5cU85OTJhVTlUVFRpK2xrSjZocDMxVGRqRFcxcXN0dEx1eW5GUGRSUzVyL3dEcWF6eXVWYmtmaTd5cjBSc2liY2JHVE5VVWw1Ykd0bW54UDBYOGluTm10b3JTdFhMa1ZpYmRUNHVLUHVRUzRuWG1TV055ZEpXZFBUYUpMR205bnpOdGtKTlIwT2k0SHhaSzR2SjlEc1lvSmNrbGZrWXhZcVhPdXlMb3gyZGVWbkhycTY2eVpFOGEzOGk3aDdrWVIzNWsraUlkSk1RSWYxTFo4aWlYTUZtdlN4NmtpdkhraE9IRmpsR2NYMWk3UllleVY0Z0FHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBTU4wUXlaNFlvOFdTVVlyemJvQ3c1bmlQanVoOE90WmNpbE5mc1IzZjlqamZpRDhRT0hGcGRITnJwUEpGL2tqeVVjZVRWWjFDTnVUOHcyVFhvdFIrS2RkcnM2d2FER3NYRTZUZTcvc2RQR3BLQ1U1Y1V1cjgzNW1qNGJvWWFQRmNkNXZuSm8zNE9yOVR6ZGRTMzhkdWVieXZTcEUrSHVZU3N5bFhJNXU2UFVqd2JOY2k1Syt4Z0RUbno1V2FtU0RXMnpYYzZMVlBia1V6aFRwcTdObHhGbU9YcWRQR1dOMnR1elp5OVJwWEdlMGZ0MDlUMFhBcmE4aXI0U1MyaXZzWE9xakhsM0JyN0VVcFFiU2ZQbWVneWVIeG5jazI3NTJhV1RRdUwrV3ZkbHpxSTlhNThwT3JmWCtpTThiczJjMm1uQkpVK1pXazBuYWQxYjI2RzZ5ekVYa2Qrci9BSmo0bSsxNytiSnZodjJIRXIvenlHbG1LbmtiYjJhMjZtT0psMXFtdHZkRUU2VXY4Nkd5c1V1VHAwakNUYmQ5UzZNSjVOb1JiTHNXaHk1V25KcU5ycUxaR3lXdE5RcCtaczRkTlBMdzdVdTV2WXZEbGpmRlRmcWpiaGhqdndwTHJzdVpONlZPYTF0UHBJNG54cC9OZmt6ZGhIYnk5Q3lFSmNrbDN2OEFzVzQ4YVQ2KzJ4enRxNURIQ2s3ZDl6WWpIYm1ZeHdwdmw3SXZoQ3Jwa0w1UmpIWjc4elAwazRWVFNEVkpCU21mTW9uMU5pU3Zjb3lLa0I1TEZxOVo0WHFaUnhaWjQ1UmU2VDJaN0R3TDhUUTFzbHA5VkZZOHorbVMyakwrak9WNGpvNGF2RzNLbE5jcEpIbjR4bnBzemkyMUtQVkhwblU2anlkY1dWOVlCNURRL2l1Y01NVnFjWHhLVmNVWlUyZGZRL2lQdy9WeVVGa2VPYi9aeUt2ejVGeW9kZ0dFN1ZveWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBR3RxOWRwOUhpK0pueVJoSHBiNWdiSnE2M3hEVDZIRjhUVVRVVTlrdXJmWTh2NGwrTE11Unl4NkhHc2EvZmx1MzdkRHorcTFXYlV6VTgrU1dTZm5KZ2QzeEw4VmFuTFBnMFg2aUg3ejNremlUejVjODNQTmtsa24rOUoyYXlkTDFKTjBCbVQ0anJlQmFkY0R5UGUyY1p1ejBmZ2Evd0NpajZuTHkvSFR4emE2UFJtY0t0REk2ZzJNQ3VOZVI1M282YlVPUmNWUjUyV3JwMkNnZERQRHZaWnc3Y3dOZHFrYStSVnQxNkc3a2hhNWxNNGJjZ05PVUxhdTFXMnpJY0M3K3JOaVN2YXpIRDNDTE1hN2hhcFcvYmtSeVJkV3ZtWG5ac3ZHcitWY3ZzekVvWFRWcC9ZSmF2NlBHZDJuM3NxbG9FMzh0TyticzM0cHAwbHhlai9reTV4Y25zbjdObzJYRityalM4THh1dGwzSy84QTBtTXVTVzFMYzc2d3AvVHQ1azFDbC84QTZ2OEFxYjdWbDVrY0ZlQ1J1NUpjSWo0UkNEcHdXL24vQUoyUFFTVnJsMlZJcW45VW5TMzNwOHg3Vk9SeDQ2UEhpU1VPaTVwazFDdVcvbmFOdWFsVHVsNzhpTGp1bnkvbU5GU1NmbjZVWldPN3B2N2xpVmNrL2N0VVd6TGRWSnFwSkpiRmtJNzh5WEJ0c1h3aHdOVnpNVkppQ2hiMzZGcVZtZUZrZzFIaDI1a0pGdlJsVFZnVnpOWFVHMVBrYXVYNWdJdFdrY0x4ckVvWklUUzh6dlJWcGVoeC9IRjhtTitiTDR2Nmp1YkhHVG9OMllCNm5rZHZ3YjhRYW5RdFk4amVYQXYySHpYb2U1MG1weGF2QkhOaGtwUWt0ajVXM1VrYm1pOFQxV2dkNmZLNE44MXpUOWdQcHdQSmFMOFg4b2F6RC81NC93Q2g2TFJlSmFYWFFVdFBsVW4xanlhQTJ3QUFBQUFBQUFBQUFBQUFBQUFBQkhpN0hOMS9qT2owUHk1TWlsay9jaTdZSFRibzF0YnJ0UG9zZkhxTWlndS9OK3g1THhEOFY2bkkydE1saWg1ODJ6Z1p0VG0xVTNMTmtsTitiZGdlaThTL0ZtV2NuRFJMNGNWLytScmQvd0JEenVvMU9iUFBqelpKWkpQcTJWY1BjY1BjREtWSnNOMFJrcUhNQ1VYYUJGT2tTQUhvUHcvUGl4VGg1TTgrZEh3VFA4SFhSVGZ5ejI5em41SnNYeGNyMDJaV2tSeEU4dkpERnpQTTliWmh5b3RLWWNpNENmV3lhVkZVT1JjQmhxMGlFNDdGaVZnRFVsQjBWY0w2UDJOdVN0Rk1vcWdLV3E1cGU1WWxhNS9uWk5Lekh3a3ZwMnNNczFWd0p2ZmN0Z210NHBQMVpscXU1bUhNRm1wOFBudjdHWGJXem94eERqOXdXYWkxeExwNnRGTTR4YnBOdnNrV1Nia1I0VjAyOUFtekZXUlcxMjh0eURqU3RYYjgyWHRVUmF0b045VVlScGMzN2lLdGt5V05XRkNWTFl0U3NRaTB5eHFnSVE1Ym1XNlJQaDdrR3JBaVZUNUZqVmxjdVlGVTViOGpYeU8xN0Y3Vm9xeUswQkNISmVod3ZIcDNteHdya216dlNmREJ0OUR5ZXR6ZkgxTThpZHB1bDZIVHh6L3B5OHR5TmNCdWlCNlhtU2x6UkVBQ2FkazRaWjQ1cVdPVGpKZFU5eWtseGRnUFJlR2ZpalZhZHFHcC9YWTExL2FYOVQxZWc4WDBuaUVid1pQbS9jbHRMN0h6TGk3Rm1QSkxITGlqSnA5R25UUUgxY0hoUERmeFJxOU5VY3orUEJmdmZWOXowbWcvRU9nMWRSNDNpbS8yWjdmbnlBNjRNSjJyVmV6TWdBQUFBQUF3blpCeVVWY21rdXJaeGZFUHhObzlKeFJ3L3dEVVNXM3l2YjdnZHppT1pyL0hORm9VMUxJc2sxK3pEbWVQMS9qK3QxdXptOGNQM1k3SEs0M04zSmdkM3hQOFM2blV1VU1MZUhIeXBQZjduQ25sbFBtQ0xkZ0c3TUFBREtNQUFBQUhWa3VMc1I1TkdVNkFrWmpKeGtwUmROTzBSNHV4a0QxK2sxQzFPbHg1RjdyeVpzNHV2WTh4NFByUGdaL2hUZnlUMlY5R2VxeHEwOXp5ZGM1WHE4ZlVzV3g1RjNRcjZGaExvekhxWEZDVmxpZEFXUk10WDFJbVlnWUtlSFl2K3BFSmN3S1VtalBXeVpCUXJrd0RWR0drMHVoWVI0UE5KZWdFVXFEVGZWK3hMaDdtTmdLL3UvVmt1SHUxNkZpU1JqaDdnVnVGOVExWEltQUt1SHVXd2p0ekJPSHlyMUFtbFFNZ0NFM1RTSXlkSWsxVEt3TU5XVnozSnk1RmJkdjBBaExvVXRXeTlxMFU1cHh4d2M1dEtLVnRzRGxlTjZqNE9tK0ZGL05rL2gxUE51UnM2L1V2VmFxV1ZwcFBhSzhrYXJkbnE0NXlQSjNkcEoyWUFMUUFBQUFBQUFBeXBORXZpZVpBQWRmUStOYXpRcExGbWJoKzVMZEhvdEIrS3RQbVNqcW9QREx6VzhUdzZkRTAyZ1BxbUhVWTgrTlpNVTR6ZytUVExUNWZwZGJuMHMrTFQ1WlkzMmY4ajBPaS9GdVNGUjFtSlpGKy9IWnIyQTllRFMwWGl1azFxWHdNc1hMOTNxdlkzRTdBK2JhL3huV2E5dFpNelVQM1k3STU3ZGM5d1FBeTNaZ0FBQUFBQUFBQUFBQUFBNjJBSmNYWWlBSmNYWTlQNEY0a3MwRmd5eXJJdVRmVTh4SHFUaE9XT2FsQnRTVHRORTljenFLNTZ2TmZRMHFNbkc4SDhZanFZTEZtcFpVdnVkbm9lV3l5dlZ6MU9vSldTVHN4TG9JOVRGSnBXV1JLaXhLMkJKcXlIUW1sUWFzQ3BxZ25SSnF5QUZoaHF5S2RJa0JqaDdqaDdrazZBVDdJSlgxb3gwWlBxUUNrR3FDVmt6RFZwQUlxa1dHUTFRQk9qRGREb1E2Z1lib2lHN0FFRzdaRnEyV0pVUUFqSlVqelhqL2lQRzNwY1QyVCtkcnEvSTNmRy9GRnBvUEJpbGVXUzNhL1ovdWVYNm5ieDhmN1hEeWVUL0lnd0FkM0FBQUFBQUFBQUFBQUFBQUFCTGk3R1l6NTB5QUEyTVdTVVpKeGJUWEpvN1BoMzRrMWVtcU9hWHg4ZmxMNmw3bm5pYXlOQVJaZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQk5PeUFBdGhOd21wUmJUWEpybWVtOEg4YitKV0hVdFJsMGs5a3p5NEo2NW5VVnoxZWEra0oyclFhczhoNFo0N2sweldQVU56eGNyNm85UnA4K1BVWTFQSEpOUGxSNXV1Ynk5SFBVeGVsUzNkazRlWlhIcVNKZEZ5ZG1Tdmk0WDZsdkYyQ2VrSmN5dHFpNlVlVzVXbFFiTHFCTGlFK1JFTldHT1M5Q0JMaTdBUjR1SldrL2RHVzdJdDBTU3NEQk5LbUVxTXAwQm5oN2lSbHVpRGRBUTZzZzNiSmtBSU4yMFRCaHVrQWJvNHZqSGl5MDBIaHcwOHpYLzFLdkZmRzQ0MUxEcFdwUzVTbjBYb2VhbEp5azVTYmJmTnZtenR4NC85cmgzNVA4ak01dWNuS1RiYmR0dm15RXVoa3hMb2QzQkVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETkdBQUFNOHdNRXlCS1BVQ1JzYVhXNTlKazQ4VTMzaStUTllHV1N0bHNldThQOGR3YWhLR1NzYy9KOWZjN01aeG1yVFBuSnZhTHhiVmFPbEdibkQ5MlRzNWRlTC80Nnp5YjllK1N0RmtlcHdkQitJZE5uU2hsazhVdis3bDl6dHduR2FUVFc1eTliL3EveFkxWkJxeXdnMVJsbUN0cXlCYzFaVkpVekhTVmdHV3FKaG5zckpSNm1Xck1wV0dXNnp3OXlRTXg2bTJKWWxIbHVSbHlKRldXV3hpcGNWeTZHRFcxR3J3NEk4ZWJKR0M3czRtdS9FU2E0ZEpGdi92bHQrUlU1dEwzSTd1cDFlTFRZblBMTlJqM1BMK0orTlpkUzNqeFhqeFA3czV1ZlVadFRQanpaSE45TDZGUjI1OGNqaDExY0FBZFhNSUV6RStRRVFaNUdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdlWmdBQ1hEM01jeHlBU1ZFaVBNSldCSUFBWk5uUytJNnJSdjlSbWxGZnU4MTlqVkJsa3JaYkhwTkwrTE0wYVdvd1JrdXJpNmYyWjF0UCtJdkQ4NlhGbGVLWGxrVlY3OGp3b0l2amxYUEpZK2xZOVRpeXg0c2NvelhuRnBvT1NaODNoT1VKY1VKT0w4MDZadFl2RmRkaGY2dlZaRXZKeXYrSk44YXA1STk4M1lUbzhURDhRZUl3Ly9OR1grNkNKL3dEdVBYLy9BTVgvQU5SNlU5NDlxbmFNcHBIaVAvY1BpSFNXTmVrQ3FYamZpRXVlb2t2OXFTL2tQU252SHZGTk0xOC9pV2wwOS9GelFnL0p5Myt4NEhKcTlSbC8xYzJXYThuTmxYRjJObkdsN3g2M1ZmaWZTd3VPQ0U4ejg2NFYrZjhBUTQyczhlMW1kdFFheFJmN3ZQN25LVG93WE9KSE85MnBUbktjdUtjbktUNXR1MlJBS1NBQUFBM1Fhc0NMZGprWmV4RUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUpjUGNmU1JBRXdSNWhPZ0pBeHhkaklBQWYwQUFBQUFBQUFBQUFBQUFBQWFzQUU3QmppN0FPSHVSQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETE1BQUFBQUFBQUFBQUFBQUFBQUFETEF3QUFBQmxLd01BbHc5eHc5d0ltVTZNQUNYRjJNSko5akFBbHc5eGI4aUpMaTdBT0xzT0xzTEhGMkFjWFl5RTdBQnVqSEYyTW1PTHNCbE8rakJqaTdHUURWOVg3QUdKZEFNaHVqSEQzSEQzQXczWmd5akFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJNaTFSbXpFbllHQUFBQU04Z01FdUh1WVpuaDdnT0h1UkpjUGNpQUFBQUdUQUdhRmhtQUFBQUV1THNZU3NrQmo2aFJsdWcxWUVVWUpnREhEM0hEM01nQWxRQUFOV0FBQmpoN21RQmpoN21VcUFBQUFBQUFNY1hZaVREVmdSUVJtaUlBR2FNQUFBQUFBQUFBQUFBQUFBQUFBQUFCbEt6UEQzQWlES1ZtZUh1QmhLelBEM01OVVo0ZTRHVEhEM01nQWxRQmpoN2dPSHVPSHVPSHVaU29ESEQzTXBVRTdBR09Mc1k1RWcxWUdPSHVPSHVLSEQzQWNQY2NQY1I2bVFNVVlTc2tBTWNQY2NQY3lBQWFzQUNNbFJJTldBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQmpoN21RQkhtWU1vd0FNc3dBQUFBQUFBQUFBQUFBQUFBQUFBQXpGV1o0ZTQ0ZTVsS2dNY1BjeUFBQUFBTldBQmlqS1ZBQUFBQUFBQUFBQUFBRFZnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFhc0FBMVlUc0FZb3lsUkZCZ1Nib2daWmdES01HVUVCZ0FBQUFBQUFBQUFBQUFBQUF5bFlTc2tsUUdLTUlrQU1SNm1RWTRlNEdRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJqaDdtSXVpUUFKMkFBTVM2RVNVdWhFRE5CQmdBakJsR0FBTW93QUJMaTdHRzdBd0RLTUFBREtBd1paZ3p5QXdTVzVnekhxQmxLZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQVlsMEhEM01wMkFJeVZHQ1gxRGg3Z1lZUWtxSkFZajFNZ0FSUTVHWmRCUUVRWmFvenc5d0hEM01OVVpveWxRR09IdVpBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFuWUFBQUFBQUFBQUFOV0FBQ2RnQnF3QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRC85az0iXQ~`; const expiredCredential = `eyJ0eXAiOiJkYytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFZrYVRwbGFHbGpPakVpTENKdVlXMWxJam9pUlVoSlF5QlRSQzFLVjFRZ1ZGbFFSU0JOUlZSQlJFRlVRU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lSWFZ5YjNCbFlXNGdTR1ZoYkhSb0lFbHVjM1Z5WVc1alpTQkRZWEprSUNoRlNFbERLU0JUUkMxS1YxUWdWbVZ5YVdacFlXSnNaU0JEY21Wa1pXNTBhV0ZzSUZSNWNHVWdUV1YwWVdSaGRHRXNJR0poYzJWa0lHOXVJR2xsZEdZdGIyRjFkR2d0YzJRdGFuZDBMWFpqSUNoa2NtRm1kQ0F3T1Nrc0lIVnphVzVuSUdFZ2MybHVaMnhsSUd4aGJtZDFZV2RsSUhSaFp5QW9aVzR0VlZNcExpSXNJaVJqYjIxdFpXNTBJam9pU1cxd2JHVnRaVzUwWVhScGIyNGdiMllnZEdocGN5QmxlR0Z0Y0d4bElGUjVjR1VnVFdWMFlXUmhkR0VnYldGNUlISmxjWFZwY21VZ1RXVnRZbVZ5SUZOMFlYUmxMWE53WldOcFptbGpJR05zWVhKcFptbGpZWFJwYjI1eklIUnZJR0ZzYVdkdUlIZHBkR2dnYm1GMGFXOXVZV3dnY0c5c2FXTnBaWE1nWjI5MlpYSnVhVzVuSUhSb1pTQmthWE53YkdGNUlHOW1JR2x1WTJ4MVpHVmtJR05zWVdsdGN5NGlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW01aGJXVWlPaUpGU0VsRElGTkVMVXBYVkNCV1F5SXNJbVJsYzJOeWFYQjBhVzl1SWpvaVJYVnliM0JsWVc0Z1NHVmhiSFJvSUVsdWMzVnlZVzVqWlNCRFlYSmtJQ2hGU0VsREtTQlRSQzFLVjFRZ1ZrTWlMQ0p5Wlc1a1pYSnBibWNpT25zaWMybHRjR3hsSWpwN0ltSmhZMnRuY205MWJtUmZZMjlzYjNJaU9pSWpNV0l5TmpOaUlpd2lkR1Y0ZEY5amIyeHZjaUk2SWlOR1JrWkdSa1lpZlN3aWMzWm5YM1JsYlhCc1lYUmxjeUk2VzNzaWRYSnBJam9pYUhSMGNEb3ZMM2RoYkd4bGRDMWxiblJsY25CeWFYTmxMV2x6YzNWbGNqbzRNREF6TDJsdFlXZGxjeTkwWlcxd2JHRjBaUzFsYUdsakxuTjJaeUlzSW5WeWFTTnBiblJsWjNKcGRIa2lPaUp6YUdFeU5UWXRUazVEUWtWRFZscDNOVWw0VWtzdmRuRXZLM2htTWs5alNIWmhVa3Q2UTJ0NGFIRjRhRmhxV2xoclp6MGlMQ0p3Y205d1pYSjBhV1Z6SWpwN0ltOXlhV1Z1ZEdGMGFXOXVJam9pYkdGdVpITmpZWEJsSWl3aVkyOXNiM0pmYzJOb1pXMWxJam9pYkdsbmFIUWlMQ0pqYjI1MGNtRnpkQ0k2SW01dmNtMWhiQ0o5ZlYxOWZWMHNJbU5zWVdsdGN5STZXM3NpY0dGMGFDSTZXeUoyWTNRaVhTd2liV0Z1WkdGMGIzSjVJanAwY25WbExDSnpaQ0k2SW01bGRtVnlJbjBzZXlKd1lYUm9JanBiSW1wMGFTSmRMQ0p0WVc1a1lYUnZjbmtpT25SeWRXVXNJbk5rSWpvaWJtVjJaWElpZlN4N0luQmhkR2dpT2xzaWMzVmlJbDBzSW0xaGJtUmhkRzl5ZVNJNmRISjFaU3dpYzJRaU9pSnVaWFpsY2lKOUxIc2ljR0YwYUNJNld5SnBjM01pWFN3aWJXRnVaR0YwYjNKNUlqcDBjblZsTENKelpDSTZJbTVsZG1WeUluMHNleUp3WVhSb0lqcGJJbWxoZENKZExDSnRZVzVrWVhSdmNua2lPblJ5ZFdVc0luTmtJam9pYm1WMlpYSWlmU3g3SW5CaGRHZ2lPbHNpWTI1bUlsMHNJbTFoYm1SaGRHOXllU0k2ZEhKMVpTd2ljMlFpT2lKdVpYWmxjaUo5TEhzaWNHRjBhQ0k2V3lKd1pYSnpiMjVoYkY5aFpHMXBibWx6ZEhKaGRHbDJaVjl1ZFcxaVpYSWlYU3dpYldGdVpHRjBiM0o1SWpwMGNuVmxMQ0p6WkNJNkltRnNkMkY1Y3lJc0luTjJaMTlwWkNJNkluQmxjbk52Ym1Gc1gyRmtiV2x1YVhOMGNtRjBhWFpsWDI1MWJXSmxjaUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKVGIyTnBZV3dnVTJWamRYSnBkSGtnVUVsT0lpd2laR1Z6WTNKcGNIUnBiMjRpT2lKVmJtbHhkV1VnY0dWeWMyOXVZV3dnYVdSbGJuUnBabWxsY2lCMWMyVmtJR0o1SUhOdlkybGhiQ0J6WldOMWNtbDBlU0J6WlhKMmFXTmxjeTRpZlYxOUxIc2ljR0YwYUNJNld5SnBjM04xYVc1blgyTnZkVzUwY25raVhTd2liV0Z1WkdGMGIzSjVJanAwY25WbExDSnpaQ0k2SW01bGRtVnlJaXdpYzNablgybGtJam9pYVhOemRXVnlYMk52ZFc1MGNua2lMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXbHVaeUJqYjNWdWRISjVJaXdpWkdWelkzSnBjSFJwYjI0aU9pSkZTRWxESUdsemMzVnBibWNnWTI5MWJuUnllUzRpZlYxOUxIc2ljR0YwYUNJNld5SnBjM04xYVc1blgyRjFkR2h2Y21sMGVTSmRMQ0p0WVc1a1lYUnZjbmtpT25SeWRXVXNJbk5rSWpvaWJtVjJaWElpZlN4N0luQmhkR2dpT2xzaWFYTnpkV2x1WjE5aGRYUm9iM0pwZEhraUxDSnBaQ0pkTENKelpDSTZJbTVsZG1WeUlpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa2x6YzNWcGJtY2dZWFYwYUc5eWFYUjVJR2xrSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpGU0VsRElHbHpjM1ZwYm1jZ1lYVjBhRzl5YVhSNUlIVnVhWEYxWlNCcFpHVnVkR2xtYVdWeUxpSjlYWDBzZXlKd1lYUm9JanBiSW1semMzVnBibWRmWVhWMGFHOXlhWFI1SWl3aWJtRnRaU0pkTENKelpDSTZJbTVsZG1WeUlpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa2x6YzNWcGJtY2dZWFYwYUc5eWFYUjVJRzVoYldVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWtWSVNVTWdhWE56ZFdsdVp5QmhkWFJvYjNKcGRIa2dibUZ0WlM0aWZWMTlMSHNpY0dGMGFDSTZXeUprWVhSbFgyOW1YMlY0Y0dseWVTSmRMQ0p6WkNJNkltNWxkbVZ5SWl3aWMzWm5YMmxrSWpvaVpHRjBaVjl2Wmw5bGVIQnBjbmtpTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVJYaHdhWEo1SUdSaGRHVWlMQ0prWlhOamNtbHdkR2x2YmlJNklrVklTVU1nWlhod2FYSmhkR2x2YmlCa1lYUmxMaUo5WFgwc2V5SndZWFJvSWpwYkltUmhkR1ZmYjJaZmFYTnpkV0Z1WTJVaVhTd2liV0Z1WkdGMGIzSjVJanAwY25WbExDSnpaQ0k2SW01bGRtVnlJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVmxJR1JoZEdVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWtWSVNVTWdkbUZzYVdScGRIa2djM1JoY25RZ1pHRjBaUzRpZlYxOUxIc2ljR0YwYUNJNld5SmhkWFJvWlc1MGFXTmZjMjkxY21ObElsMHNJbTFoYm1SaGRHOXllU0k2ZEhKMVpTd2ljMlFpT2lKdVpYWmxjaUo5TEhzaWNHRjBhQ0k2V3lKaGRYUm9aVzUwYVdOZmMyOTFjbU5sSWl3aWFXUWlYU3dpYzJRaU9pSnVaWFpsY2lJc0luTjJaMTlwWkNJNkltRjFkR2hsYm5ScFkxOXpiM1Z5WTJWZmFXUWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pUTI5dGNHVjBaVzUwSUdsdWMzUnBkSFYwYVc5dUlHbGtJaXdpWkdWelkzSnBjSFJwYjI0aU9pSkpaR1Z1ZEdsbWFXVnlJRzltSUhSb1pTQmpiMjF3WlhSbGJuUWdhVzV6YVhSMWRHbHZiaUJoY3lCeVpXZHBjM1JsY21Wa0lHbHVJSFJvWlNCRlJWTlRTU0JKYm5OMGFYUjFkR2x2YmlCU1pYQnZjMmwwYjNKNUxpSjlYWDBzZXlKd1lYUm9JanBiSW1GMWRHaGxiblJwWTE5emIzVnlZMlVpTENKdVlXMWxJbDBzSW5Oa0lqb2libVYyWlhJaUxDSnpkbWRmYVdRaU9pSmhkWFJvWlc1MGFXTmZjMjkxY21ObFgyNWhiV1VpTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVEyOXRjR1YwWlc1MElHbHVjM1JwZEhWMGFXOXVJRzVoYldVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWs1aGJXVWdiMllnZEdobElHTnZiWEJsZEdWdWRDQnBibk5wZEhWMGFXOXVJR0Z6SUhKbFoybHpkR1Z5WldRZ2FXNGdkR2hsSUVWRlUxTkpJRWx1YzNScGRIVjBhVzl1SUZKbGNHOXphWFJ2Y25rdUluMWRmU3g3SW5CaGRHZ2lPbHNpWlc1a2FXNW5YMlJoZEdVaVhTd2ljMlFpT2lKdVpYWmxjaUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKRmJtUnBibWNnWkdGMFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVJXNWtJR1JoZEdVZ2IyWWdkR2hsSUdsdWMzVnlZVzVqWlNCamIzWmxjbUZuWlM0aWZWMTlMSHNpY0dGMGFDSTZXeUp6ZEdGeWRHbHVaMTlrWVhSbElsMHNJbk5rSWpvaWJtVjJaWElpTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVUzUmhjblJwYm1jZ1pHRjBaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lVM1JoY25RZ1pHRjBaU0J2WmlCMGFHVWdhVzV6ZFhKaGJtTmxJR052ZG1WeVlXZGxMaUo5WFgwc2V5SndZWFJvSWpwYkltUnZZM1Z0Wlc1MFgyNTFiV0psY2lKZExDSnRZVzVrWVhSdmNua2lPblJ5ZFdVc0luTmtJam9pWVd4M1lYbHpJaXdpYzNablgybGtJam9pWkc5amRXMWxiblJmYm5WdFltVnlJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtSdlkzVnRaVzUwSUc1MWJXSmxjaUlzSW1SbGMyTnlhWEIwYVc5dUlqb2lSVWhKUXlCMWJtbHhkV1VnWkc5amRXMWxiblFnYVdSbGJuUnBabWxsY2k0aWZWMTlYWDAiXSwieDVjIjpbIk1JSUN5ekNDQW5HZ0F3SUJBZ0lVTG5yeHV4OXNJMzRvcWJieTNNNGxTS09zOG93d0NnWUlLb1pJemowRUF3SXdQekVMTUFrR0ExVUVCaE1DUlZVeEZUQVRCZ05WQkFvTURIZDNWMkZzYkdWMExtOXlaekVaTUJjR0ExVUVBd3dRZDNkWFlXeHNaWFFnVW05dmRDQkRRVEFlRncweU5UQTBNamt4TURJNU5UTmFGdzB5TmpBME1qa3hNREk1TlROYU1FRXhDekFKQmdOVkJBWVRBa1ZWTVJVd0V3WURWUVFLREF4M2QxZGhiR3hsZEM1dmNtY3hHekFaQmdOVkJBTU1FbXh2WTJGc0xuZDNkMkZzYkdWMExtOXlaekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRlZpdkd0NTNNNHFFUDA2UVQyMEJTbEdpTUlkekxMdkcrYjlmcS9mSEtNK05HVCthM3NuWGlQd1U3WDdqck9GV3h3eWpaZWVhbjQwK3Z4Nkd5MDZWZnFqZ2dGSE1JSUJRekFkQmdOVkhRNEVGZ1FVTS9BM0ZUUUxqd3c1Lzl1MDFNWC9TUnlWcWFVd0h3WURWUjBqQkJnd0ZvQVUwSEd1M1QrL1dxaDN5TmlmejlzTmQrSFBCUzR3RGdZRFZSMFBBUUgvQkFRREFnZUFNRElHQTFVZEVnUXJNQ21CRVdsdVptOUFkM2QzWVd4c1pYUXViM0puaGhSb2RIUndjem92TDNkM2QyRnNiR1YwTG05eVp6QVNCZ05WSFNVRUN6QUpCZ2NvZ1l4ZEJRRUNNQXdHQTFVZEV3RUIvd1FDTUFBd1JBWURWUjBmQkQwd096QTVvRGVnTllZemFIUjBjSE02THk5M2QzZGhiR3hsZEM1dmNtY3ZhV0ZqWVM5amNtd3ZkM2QzWVd4c1pYUmZiM0puWDJsaFkyRXVZM0pzTUZVR0ExVWRFUVJPTUV5Q0VteHZZMkZzTG5kM2QyRnNiR1YwTG05eVo0SVpiRzlqWVd3dGFYTnpkV1Z5TG5kM2QyRnNiR1YwTG05eVo0SWJiRzlqWVd3dGRtVnlhV1pwWlhJdWQzZDNZV3hzWlhRdWIzSm5NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUNROGgrNWtyaE8rZjR3b1JlRFkxRDdDYU02cUNkYTNtODE0ZTZETHZPcGhBSWdIUUwrV203V0ZSd3hnanpNTE4zN1JvakpHclpiRjRPRkNoSWttbTB1dTVvPSJdLCJhbGciOiJFUzI1NiJ9.eyJjbmYiOnsiandrIjp7ImNydiI6IlAtMjU2IiwiZXh0Ijp0cnVlLCJrZXlfb3BzIjpbInZlcmlmeSJdLCJrdHkiOiJFQyIsIngiOiJzeEtmNXVRVXFlc3YyaFc3ODRqM1Jmb0hDQjJGUC13b0Z1b0JqYmJnN1BZIiwieSI6IlpJSW5qRWU1SHFXWGpnTmZTckFRN05kWHF6V2U5QnVWX2RieWFBTEFJaUUiLCJraWQiOiIyUUd2Q1ZWelUwaHNzTUlJdFRyMEJRT0VIOGs1WWpvR3ZtS09TX29LUlNRIn19LCJ2Y3QiOiJ1cm46ZXVkaTplaGljOjEiLCJ2Y3QjaW50ZWdyaXR5Ijoic2hhMjU2LUNUcE9rVkVMWHVsYThQdDlrRVZ1Z1hqbkk2aXBhSFFqdTkzeEdURUJCcU09IiwianRpIjoidXJuOmV1ZGk6ZWhpYzoxOjRjMTJmOTIwLTM1NTItNGUxMS1iNjFkLWUxMzljOTQyOWE2OCIsImRhdGVfb2ZfaXNzdWFuY2UiOiIyMDI1LTExLTEwIiwiaXNzdWluZ19jb3VudHJ5IjoiREUiLCJpc3N1aW5nX2F1dGhvcml0eSI6eyJpZCI6IjIiLCJuYW1lIjoiREUgSXNzdWluZyBBdXRob3JpdHkifSwiYXV0aGVudGljX3NvdXJjZSI6eyJpZCI6IjExMjIzMyIsIm5hbWUiOiJERSBBdXRoZW50aWMgU291cmNlIn0sImRhdGVfb2ZfZXhwaXJ5IjoiMjAyNi0wNC0yMSIsInN0YXJ0aW5nX2RhdGUiOiIyMDIwLTAyLTAyIiwiZW5kaW5nX2RhdGUiOiIyMDI1LTAyLTAyIiwiaWF0IjoxNzYyNzc3MzU1LCJleHAiOjE2NjgwODI5NTUsInN1YiI6IjJRR3ZDVlZ6VTBoc3NNSUl0VHIwQlFPRUg4azVZam9Hdm1LT1Nfb0tSU1EiLCJpc3MiOiJodHRwOi8vd2FsbGV0LWVudGVycHJpc2UtaXNzdWVyOjgwMDMiLCJfc2QiOlsiREVPdlFic1l5ejUyZ0hrWjY0NVZCVl82R1hGWEY5bzVDdUw5dExBZVRXNCIsImdCczFiTndveFA0bUdVWWcyQ0FnN2pLMUJwZ21DOU15bks3X1ZSQ0JMZXciXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.5w9GIA5JNZosk4MD20vDPmDtRMJdIPJhbhtKZdpMdxx8qCgWXHZUSerR75SRaVv4o3GmqXi73BfPOjJeS19SvA~WyJpV183SXlWbEpVM1paV2didWx3ajBnIiwicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIiwiMTIzMTMyMTMyMTMiXQ~WyJYVEFEazZDSGtiVDRsSDJ4dWpkNjBBIiwiZG9jdW1lbnRfbnVtYmVyIiwiMTIzNDU2Nzg5Il0~`; const msoMdocIssuerSigned = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; const rawPresentation = `eyJ0eXAiOiJkYytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFZrYVRwd2FXUTZNU0lzSW01aGJXVWlPaUpGZUdGdGNHeGxJRUZTUmlBeExqZ2dVRWxFSUZORUxVcFhWQ0JVV1ZCRklFMUZWRUZFUVZSQklpd2laR1Z6WTNKcGNIUnBiMjRpT2lKRmVHRnRjR3hsSUZCSlJDQlRSQzFLVjFRZ1ZtVnlhV1pwWVdKc1pTQkRjbVZrWlc1MGFXRnNJRlI1Y0dVZ1RXVjBZV1JoZEdFc0lHSmhjMlZrSUc5dUlHbGxkR1l0YjJGMWRHZ3RjMlF0YW5kMExYWmpJQ2hrY21GbWRDQXdPU2tzSUhWemFXNW5JR0VnYzJsdVoyeGxJR3hoYm1kMVlXZGxJSFJoWnlBb1pXNHRWVk1wTGlCQlpHaGxjbVZ6SUhSdklGQkpSQ0JTZFd4bFltOXZheUJCVWtZZ01TNDRJQ2gxY200NlpYVmthVHB3YVdRNk1Ta2dZVzVrSUd4aGRHVnlMQ0JoY3lCdlppQjBhR1VnZEdsdFpTQnZaaUJ3ZFdKc2FXTmhkR2x2Ymk0aUxDSWtZMjl0YldWdWRDSTZJa2x0Y0d4bGJXVnVkR0YwYVc5dUlHOW1JSFJvYVhNZ1pYaGhiWEJzWlNCMGVYQmxJRzFsZEdGa1lYUmhJRzFoZVNCeVpYRjFhWEpsSUUxbGJXSmxjaUJUZEdGMFplS0FrM053WldOcFptbGpJR05zWVhKcFptbGpZWFJwYjI1eklIUnZJR0ZzYVdkdUlIZHBkR2dnYm1GMGFXOXVZV3dnY0c5c2FXTnBaWE1nWjI5MlpYSnVhVzVuSUhSb1pTQmthWE53YkdGNUlHOW1JR2x1WTJ4MVpHVmtJR05zWVdsdGN5NGlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW01aGJXVWlPaUpRU1VRaUxDSnlaVzVrWlhKcGJtY2lPbnNpYzJsdGNHeGxJanA3SW14dloyOGlPbnNpZFhKcElqb2lhSFIwY0RvdkwzZGhiR3hsZEMxbGJuUmxjbkJ5YVhObExXbHpjM1ZsY2pvNE1EQXpMMmx0WVdkbGN5OXNiMmR2TG5CdVp5SXNJblZ5YVNOcGJuUmxaM0pwZEhraU9pSnphR0V5TlRZdFNVWnhSVWhXWmxjd1pVOVBLMG94TkdoNWJFaFlhemhZUmtJNGVXcE1aV2t5TVZKb1RFWXlaMFZKV1QwaUxDSmhiSFJmZEdWNGRDSTZJbEJKUkNCTWIyZHZJbjBzSW1KaFkydG5jbTkxYm1SZlkyOXNiM0lpT2lJak5HTmpNMlJrSWl3aWRHVjRkRjlqYjJ4dmNpSTZJaU5HUmtaR1JrWWlmU3dpYzNablgzUmxiWEJzWVhSbGN5STZXM3NpZFhKcElqb2lhSFIwY0RvdkwzZGhiR3hsZEMxbGJuUmxjbkJ5YVhObExXbHpjM1ZsY2pvNE1EQXpMMmx0WVdkbGN5OTBaVzF3YkdGMFpTMXdhV1F1YzNabklpd2lkWEpwSTJsdWRHVm5jbWwwZVNJNkluTm9ZVEkxTmkxSVRpdDBiSHBDU0haUmRVNXZUMmRSZG1KcFZETkdSRU5UTTBGYVQxZGtlSFZLWVZjMFYyOXBZbEp6UFNJc0luQnliM0JsY25ScFpYTWlPbnNpYjNKcFpXNTBZWFJwYjI0aU9pSnNZVzVrYzJOaGNHVWlMQ0pqYjJ4dmNsOXpZMmhsYldVaU9pSnNhV2RvZENJc0ltTnZiblJ5WVhOMElqb2libTl5YldGc0luMTlYWDE5WFN3aVkyeGhhVzF6SWpwYmV5SndZWFJvSWpwYkluWmpkQ0pkTENKdFlXNWtZWFJ2Y25raU9uUnlkV1VzSW5Oa0lqb2libVYyWlhJaWZTeDdJbkJoZEdnaU9sc2lhblJwSWwwc0luTmtJam9pYm1WMlpYSWlmU3g3SW5CaGRHZ2lPbHNpYzNWaUlsMHNJbTFoYm1SaGRHOXllU0k2ZEhKMVpTd2ljMlFpT2lKdVpYWmxjaUo5TEhzaWNHRjBhQ0k2V3lKcFlYUWlYU3dpYldGdVpHRjBiM0o1SWpwMGNuVmxMQ0p6WkNJNkltNWxkbVZ5SW4wc2V5SndZWFJvSWpwYkltTnVhQ0pkTENKdFlXNWtZWFJ2Y25raU9uUnlkV1VzSW5Oa0lqb2libVYyWlhJaWZTeDdJbkJoZEdnaU9sc2labUZ0YVd4NVgyNWhiV1VpWFN3aWJXRnVaR0YwYjNKNUlqcDBjblZsTENKelpDSTZJbUZzZDJGNWN5SXNJbk4yWjE5cFpDSTZJbVpoYldsc2VWOXVZVzFsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklreGhjM1FnYm1GdFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVEzVnljbVZ1ZENCc1lYTjBJRzVoYldVb2N5a2diM0lnYzNWeWJtRnRaU2h6S1M0aWZWMTlMSHNpY0dGMGFDSTZXeUpuYVhabGJsOXVZVzFsSWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0p6ZG1kZmFXUWlPaUpuYVhabGJsOXVZVzFsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrWnBjbk4wSUc1aGJXVWlMQ0prWlhOamNtbHdkR2x2YmlJNklrTjFjbkpsYm5RZ1ptbHljM1FnYm1GdFpTaHpLU3dnYVc1amJIVmthVzVuSUcxcFpHUnNaU0J1WVcxbEtITXBJR2xtSUdGd2NHeHBZMkZpYkdVdUluMWRmU3g3SW5CaGRHZ2lPbHNpWW1seWRHaGtZWFJsSWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0p6ZG1kZmFXUWlPaUppYVhKMGFGOWtZWFJsSWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrUmhkR1VnYjJZZ1ltbHlkR2dpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1oxYkd3Z1ltbHlkR2dnWkdGMFpTQW9aR0Y1TENCdGIyNTBhQ3dnZVdWaGNpa3VJbjFkZlN4N0luQmhkR2dpT2xzaWNHeGhZMlZmYjJaZlltbHlkR2dpWFN3aWJXRnVaR0YwYjNKNUlqcDBjblZsTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpRYkdGalpTQnZaaUJpYVhKMGFDSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVVHeGhZMlVnZDJobGNtVWdkR2hsSUhCbGNuTnZiaUIzWVhNZ1ltOXliaTRpZlYxOUxIc2ljR0YwYUNJNld5SndiR0ZqWlY5dlpsOWlhWEowYUNJc0lteHZZMkZzYVhSNUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrTnBkSGtnYjJZZ1ltbHlkR2dpTENKa1pYTmpjbWx3ZEdsdmJpSTZJazExYm1samFYQmhiR2wwZVN3Z1kybDBlU3dnZEc5M2Jpd2diM0lnZG1sc2JHRm5aU0IzYUdWeVpTQjBhR1VnY0dWeWMyOXVJSGRoY3lCaWIzSnVMaUo5WFgwc2V5SndZWFJvSWpwYkluQnNZV05sWDI5bVgySnBjblJvSWl3aWNtVm5hVzl1SWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWxKbFoybHZiaUJ2WmlCaWFYSjBhQ0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lVM1JoZEdVc0lIQnliM1pwYm1ObExDQmthWE4wY21samRDd2diM0lnYkc5allXd2dZWEpsWVNCM2FHVnlaU0IwYUdVZ2NHVnljMjl1SUhkaGN5QmliM0p1TGlKOVhYMHNleUp3WVhSb0lqcGJJbkJzWVdObFgyOW1YMkpwY25Sb0lpd2lZMjkxYm5SeWVTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkRiM1Z1ZEhKNUlHOW1JR0pwY25Sb0lpd2laR1Z6WTNKcGNIUnBiMjRpT2lKRGIzVnVkSEo1SUhkb1pYSmxJSFJvWlNCd1pYSnpiMjRnZDJGeklHSnZjbTR1SW4xZGZTeDdJbkJoZEdnaU9sc2libUYwYVc5dVlXeHBkR2xsY3lJc2JuVnNiRjBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJazVoZEdsdmJtRnNhWFI1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpEYjNWdWRISjVJRzltSUc1aGRHbHZibUZzYVhSNUxpSjlYWDBzZXlKd1lYUm9JanBiSW01aGRHbHZibUZzYVhScFpYTWlYU3dpYldGdVpHRjBiM0o1SWpwMGNuVmxMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSk9ZWFJwYjI1aGJHbDBhV1Z6SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpEYjNWdWRISjVJRzl5SUdOdmRXNTBjbWxsY3lCdlppQnVZWFJwYjI1aGJHbDBlUzRpZlYxOUxIc2ljR0YwYUNJNld5SndaWEp6YjI1aGJGOWhaRzFwYm1semRISmhkR2wyWlY5dWRXMWlaWElpWFN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pVUdWeWMyOXVZV3dnU1VRaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxWdWFYRjFaU0J3WlhKemIyNWhiQ0JwWkdWdWRHbG1hV1Z5SUdGemMybG5ibVZrSUdKNUlIUm9aU0JoZFhSb2IzSnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2ljR2xqZEhWeVpTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0luTjJaMTlwWkNJNkluQnBZM1IxY21VaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lVR2xqZEhWeVpTSjlYWDBzZXlKd1lYUm9JanBiSW1KcGNuUm9YMlpoYldsc2VWOXVZVzFsSWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtKcGNuUm9JR3hoYzNRZ2JtRnRaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lUR0Z6ZENCdVlXMWxLSE1wSUc5eUlITjFjbTVoYldVb2N5a2dZWFFnWW1seWRHZ3VJbjFkZlN4N0luQmhkR2dpT2xzaVltbHlkR2hmWjJsMlpXNWZibUZ0WlNKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKQ2FYSjBhQ0JtYVhKemRDQnVZVzFsSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpHYVhKemRDQnVZVzFsS0hNcExDQnBibU5zZFdScGJtY2diV2xrWkd4bElHNWhiV1VvY3lrc0lHRjBJR0pwY25Sb0xpSjlYWDBzZXlKd1lYUm9JanBiSW5ObGVDSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSlRaWGdpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEpsWTI5eVpHVmtJSE5sZUNCdmNpQm5aVzVrWlhJc0lIVnphVzVuSUhOMFlXNWtZWEprSUdOdlpHVnpMaUo5WFgwc2V5SndZWFJvSWpwYkltVnRZV2xzSWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtWdFlXbHNJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlFaWEp6YjI0bmN5QmxiV0ZwYkNCaFpHUnlaWE56TGlKOVhYMHNleUp3WVhSb0lqcGJJbkJvYjI1bFgyNTFiV0psY2lKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKTmIySnBiR1VpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEJsY25OdmJ1S0FtWE1nYlc5aWFXeGxJSEJvYjI1bElHNTFiV0psY2k0aWZWMTlMSHNpY0dGMGFDSTZXeUpoWkdSeVpYTnpJbDBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa0ZrWkhKbGMzTWlMQ0prWlhOamNtbHdkR2x2YmlJNklsQmxjbk52YmlkeklISmxjMmxrWlc1MGFXRnNJR0ZrWkhKbGMzTXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0ltWnZjbTFoZEhSbFpDSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkdkV3hzSUdGa1pISmxjM01pTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1oxYkd3Z1ptOXliV0YwZEdWa0lHRmtaSEpsYzNNZ2IyWWdjbVZ6YVdSbGJtTmxMaUo5WFgwc2V5SndZWFJvSWpwYkltRmtaSEpsYzNNaUxDSnpkSEpsWlhSZllXUmtjbVZ6Y3lKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKU1pYTnBaR1Z1WTJVZ2MzUnlaV1YwSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpUZEhKbFpYUWdibUZ0WlNCdlppQnlaWE5wWkdWdVkyVXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0ltaHZkWE5sWDI1MWJXSmxjaUpkTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpTWlhOcFpHVnVZMlVnYm5WdFltVnlJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlRkSEpsWlhRZ2JuVnRZbVZ5SUc5bUlISmxjMmxrWlc1alpTNGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFpHUnlaWE56SWl3aWNHOXpkR0ZzWDJOdlpHVWlYU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lVbVZ6YVdSbGJtTmxJRnBKVUNJc0ltUmxjMk55YVhCMGFXOXVJam9pVUc5emRHRnNJRzl5SUZwSlVDQmpiMlJsSUc5bUlISmxjMmxrWlc1alpTNGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFpHUnlaWE56SWl3aWJHOWpZV3hwZEhraVhTd2ljMlFpT2lKaGJIZGhlWE1pTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVEybDBlU0J2WmlCeVpYTnBaR1Z1WTJVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWsxMWJtbGphWEJoYkdsMGVTd2dZMmwwZVN3Z2RHOTNiaXdnYjNJZ2RtbHNiR0ZuWlNCdlppQnlaWE5wWkdWdVkyVXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXUmtjbVZ6Y3lJc0luSmxaMmx2YmlKZExDSnpaQ0k2SW1Gc2QyRjVjeUlzSW1ScGMzQnNZWGtpT2x0N0lteHZZMkZzWlNJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKVGRHRjBaU0J2WmlCeVpYTnBaR1Z1WTJVaUxDSmtaWE5qY21sd2RHbHZiaUk2SWxOMFlYUmxMQ0J3Y205MmFXNWpaU3dnYjNJZ2NtVm5hVzl1WVd3Z1pHbDJhWE5wYjI0Z2IyWWdjbVZ6YVdSbGJtTmxMaUo5WFgwc2V5SndZWFJvSWpwYkltRmtaSEpsYzNNaUxDSmpiM1Z1ZEhKNUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrTnZkVzUwY25rZ2IyWWdjbVZ6YVdSbGJtTmxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSkRiM1Z1ZEhKNUlIZG9aWEpsSUhSb1pTQndaWEp6YjI0Z1kzVnljbVZ1ZEd4NUlISmxjMmxrWlhNdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgyVnhkV0ZzWDI5eVgyOTJaWElpWFN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pUVdkbElHVnhkV0ZzSUc5eUlHOTJaWElpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa0ZuWlNCMGFISmxjMmh2YkdRZ2FXNWthV05oZEc5eWN5NGlmVjE5TEhzaWNHRjBhQ0k2V3lKaFoyVmZaWEYxWVd4ZmIzSmZiM1psY2lJc0lqRTBJbDBzSW5Oa0lqb2lZV3gzWVhseklpd2laR2x6Y0d4aGVTSTZXM3NpYkc5allXeGxJam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa0ZuWlNCbGNYVmhiQ0J2Y2lCdmRtVnlJREUwSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpKYm1ScFkyRjBaWE1nYVdZZ2RHaGxJSEJsY25OdmJpQnBjeUF4TkNCNVpXRnljeUJ2YkdRZ2IzSWdiMnhrWlhJdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgyVnhkV0ZzWDI5eVgyOTJaWElpTENJeE5pSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkJaMlVnWlhGMVlXd2diM0lnYjNabGNpQXhOaUlzSW1SbGMyTnlhWEIwYVc5dUlqb2lTVzVrYVdOaGRHVnpJR2xtSUhSb1pTQndaWEp6YjI0Z2FYTWdNVFlnZVdWaGNuTWdiMnhrSUc5eUlHOXNaR1Z5TGlKOVhYMHNleUp3WVhSb0lqcGJJbUZuWlY5bGNYVmhiRjl2Y2w5dmRtVnlJaXdpTVRnaVhTd2ljMlFpT2lKaGJIZGhlWE1pTENKa2FYTndiR0Y1SWpwYmV5SnNiMk5oYkdVaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVFXZGxJR1Z4ZFdGc0lHOXlJRzkyWlhJZ01UZ2lMQ0prWlhOamNtbHdkR2x2YmlJNklrbHVaR2xqWVhSbGN5QnBaaUIwYUdVZ2NHVnljMjl1SUdseklERTRJSGxsWVhKeklHOXNaQ0J2Y2lCdmJHUmxjaTRpZlYxOUxIc2ljR0YwYUNJNld5SmhaMlZmWlhGMVlXeGZiM0pmYjNabGNpSXNJakl4SWwwc0luTmtJam9pWVd4M1lYbHpJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtGblpTQmxjWFZoYkNCdmNpQnZkbVZ5SURJeElpd2laR1Z6WTNKcGNIUnBiMjRpT2lKSmJtUnBZMkYwWlhNZ2FXWWdkR2hsSUhCbGNuTnZiaUJwY3lBeU1TQjVaV0Z5Y3lCdmJHUWdiM0lnYjJ4a1pYSXVJbjFkZlN4N0luQmhkR2dpT2xzaVlXZGxYMlZ4ZFdGc1gyOXlYMjkyWlhJaUxDSTJOU0pkTENKelpDSTZJbUZzZDJGNWN5SXNJbVJwYzNCc1lYa2lPbHQ3SW14dlkyRnNaU0k2SW1WdUxWVlRJaXdpYkdGaVpXd2lPaUpCWjJVZ1pYRjFZV3dnYjNJZ2IzWmxjaUEyTlNJc0ltUmxjMk55YVhCMGFXOXVJam9pU1c1a2FXTmhkR1Z6SUdsbUlIUm9aU0J3WlhKemIyNGdhWE1nTmpVZ2VXVmhjbk1nYjJ4a0lHOXlJRzlzWkdWeUxpSjlYWDBzZXlKd1lYUm9JanBiSW1GblpWOXBibDk1WldGeWN5SmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkJaMlVpTENKa1pYTmpjbWx3ZEdsdmJpSTZJbEJsY25OdmJpZHpJR0ZuWlNCcGJpQmpiMjF3YkdWMFpXUWdlV1ZoY25NdUluMWRmU3g3SW5CaGRHZ2lPbHNpWVdkbFgySnBjblJvWDNsbFlYSWlYU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lRbWx5ZEdnZ2VXVmhjaUlzSW1SbGMyTnlhWEIwYVc5dUlqb2lXV1ZoY2lCcGJpQjNhR2xqYUNCMGFHVWdjR1Z5YzI5dUlIZGhjeUJpYjNKdUxpSjlYWDBzZXlKd1lYUm9JanBiSW1semMzVnBibWRmWVhWMGFHOXlhWFI1SWwwc0ltMWhibVJoZEc5eWVTSTZkSEoxWlN3aWMyUWlPaUpoYkhkaGVYTWlMQ0prYVhOd2JHRjVJanBiZXlKc2IyTmhiR1VpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXbHVaeUJoZFhSb2IzSnBkSGtpTENKa1pYTmpjbWx3ZEdsdmJpSTZJazVoYldVZ2IyWWdkR2hsSUdsemMzVnBibWNnWW05a2VTQnZjaUJOWlcxaVpYSWdVM1JoZEdVZ0tIUjNieTFzWlhSMFpYSWdZMjlrWlNrdUluMWRmU3g3SW5CaGRHZ2lPbHNpYVhOemRXbHVaMTlqYjNWdWRISjVJbDBzSW0xaGJtUmhkRzl5ZVNJNmRISjFaU3dpYzJRaU9pSmhiSGRoZVhNaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lTWE56ZFdsdVp5QmpiM1Z1ZEhKNUlpd2laR1Z6WTNKcGNIUnBiMjRpT2lKTlpXMWlaWElnVTNSaGRHVWdkMmhsY21VZ2RHaGxJR1J2WTNWdFpXNTBJSGRoY3lCcGMzTjFaV1F1SW4xZGZTeDdJbkJoZEdnaU9sc2laR0YwWlY5dlpsOWxlSEJwY25raVhTd2liV0Z1WkdGMGIzSjVJanAwY25WbExDSnpaQ0k2SW01bGRtVnlJaXdpYzNablgybGtJam9pWlhod2FYSjVYMlJoZEdVaUxDSmthWE53YkdGNUlqcGJleUpzYjJOaGJHVWlPaUpsYmkxVlV5SXNJbXhoWW1Wc0lqb2lSWGh3YVhKNUlHUmhkR1VpTENKa1pYTmpjbWx3ZEdsdmJpSTZJa1Z1WkNCa1lYUmxJRzltSUhSb1pTQmtiMk4xYldWdWRPS0FtWE1nZG1Gc2FXUnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2laR0YwWlY5dlpsOXBjM04xWVc1alpTSmRMQ0p6WkNJNkltRnNkMkY1Y3lJc0ltUnBjM0JzWVhraU9sdDdJbXh2WTJGc1pTSTZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkpjM04xWlNCa1lYUmxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlRkR0Z5ZENCa1lYUmxJRzltSUhSb1pTQmtiMk4xYldWdWRPS0FtWE1nZG1Gc2FXUnBkSGt1SW4xZGZTeDdJbkJoZEdnaU9sc2laRzlqZFcxbGJuUmZiblZ0WW1WeUlsMHNJbk5rSWpvaVlXeDNZWGx6SWl3aVpHbHpjR3hoZVNJNlczc2liRzlqWVd4bElqb2laVzR0VlZNaUxDSnNZV0psYkNJNklrUnZZM1Z0Wlc1MElHNTFiV0psY2lJc0ltUmxjMk55YVhCMGFXOXVJam9pVlc1cGNYVmxJR2xrWlc1MGFXWnBaWElnYjJZZ2RHaGxJRkJKUkNCa2IyTjFiV1Z1ZEM0aWZWMTlMSHNpY0dGMGFDSTZXeUpwYzNOMWFXNW5YMnAxY21selpHbGpkR2x2YmlKZExDSnpaQ0k2SW01bGRtVnlJaXdpWkdsemNHeGhlU0k2VzNzaWJHOWpZV3hsSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVnBibWNnY21WbmFXOXVJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlNaV2RwYjI1aGJDQnZjaUJzYjJOaGJDQnpkV0prYVhacGMybHZiaUIwYUdGMElHbHpjM1ZsWkNCMGFHVWdaRzlqZFcxbGJuUXVJbjFkZlN4N0luQmhkR2dpT2xzaWRISjFjM1JmWVc1amFHOXlJbDBzSW5Oa0lqb2libVYyWlhJaWZWMTkiXSwieDVjIjpbIk1JSUN5ekNDQW5HZ0F3SUJBZ0lVTG5yeHV4OXNJMzRvcWJieTNNNGxTS09zOG93d0NnWUlLb1pJemowRUF3SXdQekVMTUFrR0ExVUVCaE1DUlZVeEZUQVRCZ05WQkFvTURIZDNWMkZzYkdWMExtOXlaekVaTUJjR0ExVUVBd3dRZDNkWFlXeHNaWFFnVW05dmRDQkRRVEFlRncweU5UQTBNamt4TURJNU5UTmFGdzB5TmpBME1qa3hNREk1TlROYU1FRXhDekFKQmdOVkJBWVRBa1ZWTVJVd0V3WURWUVFLREF4M2QxZGhiR3hsZEM1dmNtY3hHekFaQmdOVkJBTU1FbXh2WTJGc0xuZDNkMkZzYkdWMExtOXlaekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRlZpdkd0NTNNNHFFUDA2UVQyMEJTbEdpTUlkekxMdkcrYjlmcS9mSEtNK05HVCthM3NuWGlQd1U3WDdqck9GV3h3eWpaZWVhbjQwK3Z4Nkd5MDZWZnFqZ2dGSE1JSUJRekFkQmdOVkhRNEVGZ1FVTS9BM0ZUUUxqd3c1Lzl1MDFNWC9TUnlWcWFVd0h3WURWUjBqQkJnd0ZvQVUwSEd1M1QrL1dxaDN5TmlmejlzTmQrSFBCUzR3RGdZRFZSMFBBUUgvQkFRREFnZUFNRElHQTFVZEVnUXJNQ21CRVdsdVptOUFkM2QzWVd4c1pYUXViM0puaGhSb2RIUndjem92TDNkM2QyRnNiR1YwTG05eVp6QVNCZ05WSFNVRUN6QUpCZ2NvZ1l4ZEJRRUNNQXdHQTFVZEV3RUIvd1FDTUFBd1JBWURWUjBmQkQwd096QTVvRGVnTllZemFIUjBjSE02THk5M2QzZGhiR3hsZEM1dmNtY3ZhV0ZqWVM5amNtd3ZkM2QzWVd4c1pYUmZiM0puWDJsaFkyRXVZM0pzTUZVR0ExVWRFUVJPTUV5Q0VteHZZMkZzTG5kM2QyRnNiR1YwTG05eVo0SVpiRzlqWVd3dGFYTnpkV1Z5TG5kM2QyRnNiR1YwTG05eVo0SWJiRzlqWVd3dGRtVnlhV1pwWlhJdWQzZDNZV3hzWlhRdWIzSm5NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUNROGgrNWtyaE8rZjR3b1JlRFkxRDdDYU02cUNkYTNtODE0ZTZETHZPcGhBSWdIUUwrV203V0ZSd3hnanpNTE4zN1JvakpHclpiRjRPRkNoSWttbTB1dTVvPSJdLCJhbGciOiJFUzI1NiJ9.eyJjbmYiOnsiandrIjp7ImNydiI6IlAtMjU2IiwiZXh0Ijp0cnVlLCJrZXlfb3BzIjpbXSwia3R5IjoiRUMiLCJ4IjoiWE1JZXpBYV9OLUVEdkRaWjlFOGxIZXlRNnBSTDMyNEUzbGUxdmQxTjQ0RSIsInkiOiJ3RnN2TExoMzR5NjdoRlRObXNwcnQxSXhybzBLYzZHOGx6alNTc0g1NVd3In19LCJ2Y3QiOiJ1cm46ZXVkaTpwaWQ6MSIsInZjdCNpbnRlZ3JpdHkiOiJzaGEyNTYtNVJNMkYvNGNmK1EvTFd2N2pCcDVLL3owSG1wR1liUUh1YmJ4VVVkYy9PTT0iLCJqdGkiOiJ1cm46dmlkOjI1MDgwOWVhLTI1OTUtNDdkMS04NzdhLTc4NjIxNWY3N2FmYiIsInBsYWNlX29mX2JpcnRoIjp7Il9zZCI6WyJZNllzOHM0QlNYN0h5LUYwMGlNRzRkZk1pYzlZVFNMVnJTQ2hVX0xxUFBnIiwiWXZtdjRPaUtfZUg3ZW9pSEpmZ0hLSEpkZUx4STB4S2xaVTE5UnZQSS1HVSIsIlptR2pLUnhOZGZfV1VuM1N4NTJYdnZlXzZHMjE3NlBydkgwTUlWTGNYT1EiXX0sImFkZHJlc3MiOnsiX3NkIjpbIjNWOHpfVGJsVUkyNVZ6cEdXd0xDalNrRHZwS3FwWUVmdnJTWUxwVTdCd0UiLCI1UXhoLXRiTzIwa1VISXdXQlQ1SlRMSTU0LUxlM1dIQzhNLWltVmZtQjNzIiwiT2dTQXp1UGg5cHJVYlB3T0NGT1U4eHVTMVp0VG9TVHFyeEpROTBubmt4USIsIlktdGlNNkdDUjBqcUc4RWN0UHp1OHlLWi0wU0hrN3hnekVRREpRb1h0Z0UiLCJrc3Q3SWt4NGF2TWVUWklZQmNLWmdkSUZINEZPeXRtbGhaVzhSVmJDbDJzIiwib1p5TGY1WHgzQnNxMHVSZWlwNmRiNUVnUUJKWU41MzFpazE1Yll1MGYzMCIsInpuUExhQUkybHJlZFRuNC12YWhNbUFtaXJFbUxzS0owWUNIeHRqd0xxZGsiXX0sImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyJiMGdFR0FmeWQzak1nY3NBdTlldlpVZ2t3RGJyMWdxZEFSVGJnWWMwcUY0IiwiZjRhQ2RqS09wMDhKNWZMZS1DdkliVUtpcHFxM0pVSHVoa09OWDNpVkZoUSIsInJidmNvZjhpdm8tYW5pZlB2Qm44aklyTnFqQzJPUndHMVJJc0ZKY2xjNGMiLCJzYjFHRk9pVkZoNEY0ajU5R0pRU3lDeE9lLXN4WDliVVJYUmtQd1ZXQ3lrIiwidndKM0tHMkc1WEhETXdFOWk4SW55LV9WUENxcW9UUGxvZVpFSUQxSGlORSJdfSwiaXNzdWluZ19hdXRob3JpdHkiOiJQSUQ6MDAwMDEiLCJpc3N1aW5nX2NvdW50cnkiOiJHUiIsImlzc3VpbmdfanVyaXNkaWN0aW9uIjoiR1ItRiIsImRhdGVfb2ZfZXhwaXJ5IjoiMjAzNS0wNC0yMSIsInRydXN0X2FuY2hvciI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMy90cnVzdF9hbmNob3IiLCJpYXQiOjE3NjI3NzU4MjIsImV4cCI6MTc5NDMxMTgyMiwic3ViIjoiWTA1d2VhSDNlVzFuUU14X2hFZHFwOTFkZENwRjNYV0daTFNjUy01VWNJMCIsImlzcyI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMyIsIl9zZCI6WyItS0kwZFpNSm15TnFEbk5mMHZlTzRIZjE4dWcwb25rckQ4UzhONy13RUVZIiwiOG5YRUI3emVKdDE0Y2dLbk9pMmlGandvTlJoZ0t1eW1XT0hYMm04RjVUTSIsIkJES01ZcWVBVXkzMVpzMWp6SVhSUmk5alVuT1BZWkdLT3FjNGFLSTZqeVEiLCJGMDV0T1dnRXFGZ1ZKOXRxTTlQTXZRUS0tQS1rU1VVMG50RjZfTG1EVVp3IiwiRng2d3N3bTdUeVFUek90UkpQeXpPZkFEbGdTSnd6X01OSC03Q1JTUnNNdyIsIk92ZnRPdjM0azJieFhIRVlHOExfX1NfMXdES2NEeldSbTN3bnIzRGVfY0EiLCJYc29kX1c0c2ZSenF1ejZjTHlyeU9EWkNmUW5qN0lPdHVjWTcyMHJQNXBRIiwiWVFtaXk2Z0gybnJpWTdFV2U5UTZHN2lBT05PSERFQ3doNnFhaXVqYlBzbyIsImI5dG45aVI2b0lITHJ1V2RRS004UWtvbVUtM3ZMRHluSkszeV9IZjBXUmMiLCJjMmFpTGlKNUFqaGwtVjctNmVkaW50d1VPVW45eEFHVlY3eHE5V3JvbnFjIiwialZ2cDV0Mk1yeVVaeHQwNmtXQ0ZaR2ZvckhYTkhJMjBsdXFZel9WOEN4SSIsIm13dWltaF94TEtyQlliRlp5Ul9fN2Y1c0twQ3I3eld0RHE5V0dfLVNSbDQiLCJ0U3BXbnlPNU1qSnpDVGtjZlBDTVlOU3BQMUJwZFR5SmxzbkFpN0RuSTRjIiwid3I0dlFSbFR3Wml3VmJrVDU0eHUzN0RrMmQza1Y1M09TNFd6ZlhvVzFtVSIsInphcnYtUUhqNjBQRDdfdWtoOC15cUY4ZFlfRVFXU3doMDhBdy12bllMNnciXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.UCKwXB68XPeJDimIDEMUGo105UZGm1h2EXXaWRKCNPTYJ4gySfUMi-VK75SEf8tqULKqvAVZ1MYnPVdIZ4ZIig~WyJscENJMTk5VTIxcjlNQ2NrXzlzem1BIiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJwUlhabV9QSFY3M0pyRjZRdXBTVWFRIiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJzaTk3QXgyeFpUcXBlSVE3bEFlMVZRIiwiYmlydGhkYXRlIiwiMTk5MC0xMC0xNSJd~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJub25jZSI6IjA2ODAxN2M2LWRmNDMtNDZkOS1hNmQxLWUwMjcxZDQ3MjE2NSIsImF1ZCI6Ing1MDlfc2FuX2Ruczp3YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXIiLCJzZF9oYXNoIjoiTHE2N2dUTVdHNExHclBTeXZFMElwMGRORFc4SVp5R19OQlgwMVVreHN4OCIsImlhdCI6MTc2Mjc3NjY3NX0.BGBaTs-Ugs4B8UhyS1X_zUgrVdq7A4gogexKnWWwXnsAqHxSRLu9KQvFsEqHq9cYGvlifdql9xmlPC38GEWrBw`; -const rawCredentialWithoutMetadata = `eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDUmpDQ0FlMmdBd0lCQWdJVWMweFRYeHJRdkdwdkJJMHB0VlVCbHFDYkFyb3dDZ1lJS29aSXpqMEVBd0l3TERFTE1Ba0dBMVVFQmhNQ1FWUXhEakFNQmdOVkJBb01CVUV0VTBsVU1RMHdDd1lEVlFRRERBUkpRVU5CTUI0WERUSTFNRFV3TlRFek1qQXpOVm9YRFRJMk1EVXdOVEV6TWpBek5Wb3dPakVMTUFrR0ExVUVCaE1DUVZReERqQU1CZ05WQkFvTUJVRXRVMGxVTVJzd0dRWURWUVFEREJKRWIyTjFiV1Z1ZENCVGFXZHVaWElnVFRZd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTTU5XcDFRSDhiM1hwQmVNT3Y5cGRTeHV4QjI0TDZDMXJ5OEhWUmFmWEJLdm4yQkpnWm9CTm53eVhiSURnakJReVhYejUwNFlEdWxWdWljS2JZR1dnT280SGVNSUhiTUE0R0ExVWREd0VCL3dRRUF3SUhnREFWQmdOVkhTVUJBZjhFQ3pBSkJnY29nWXhkQlFFQ01ESUdBMVVkSHdRck1Da3dKNkFsb0NPR0lXaDBkSEJ6T2k4dmQyRnNiR1YwTG1FdGMybDBMbUYwTDJOeWJDOHhMbU55YkRBYUJnTlZIUkVFRXpBUmdnOTNZV3hzWlhRdVlTMXphWFF1WVhRd0lnWURWUjBTQkJzd0dZWVhhSFIwY0hNNkx5OTNZV3hzWlhRdVlTMXphWFF1WVhRd0h3WURWUjBqQkJnd0ZvQVVEUUY1SzQ2WVZnekxwZlY1c3RvdXRCZXpLNlF3SFFZRFZSME9CQllFRk8ya2c0VUxBU1FaNkYrT0N1WmdNamVWVmYvOE1Bb0dDQ3FHU000OUJBTUNBMGNBTUVRQ0lINnhCNkZ1UkVpSVgzbkY4ZERjdkMvS1JjN1FoZTdhSW5LM3VBa1FXaDN1QWlBVmRSUnl5ZmoweTgwSm96SndCTGpncEk5THIwNk1OWXlGRnc4V3I3Si90dz09Il19.eyJhZ2VfZXF1YWxfb3Jfb3ZlciI6eyJfc2QiOlsicjBkNlYzTnJoTFQ4eWphLWFZOFRiRy1aMW5CTEY0VnF4dkNJV2VtMzBFWSIsIjhJdnk4RWJtWHdudW1HZ3hzOFZsbUVlTkQ3TUVFbmhlTzRucmxTelpZU0UiLCJIU052M2JYWlk2T2RYTmNBS09qY1ZkeWNwS3ZYRGRvOEFMRXFfVklqWXhRIiwiOVF2M3daemtUTHQtemg1aWluN0syV3pQWlhveDZLQWpvZXNmbEhtMEtKVSIsIlhTVWtla2hreGs0Z2pjdXlnUzJoWkRrTzdRNVN2ejhSOUVvZWROZ2h4aXciLCI2d3VWUXRrZVRUaE1qT2wyZWJPWUZCMXo1NElETnBiWUNKcE5FUGtncTJzIiwicEdua003cjFDTENLaUxQR285cWlrODRNYkRRM1pCVTRiQXhTN2phR3ZGcyIsIlEtX3JFQlFfUTlSb2pjTnVsaEdKLXlfLVc3Q1ZhN1hBdkM3YkRWZnJZbzgiLCJhMG11Mm5SeG4wejVMc2FnYVpmTDY2eEI2LTRybThmRlVXRUJmUzJ1UWRJIiwiTDk3SXlPUklsb0o0dl8yZHNVZFpRMEo5enBkcm9FNThsVmNPekNxWWRkZyIsInp1UkhrclRMcUJOeVFta0NHd21CVzRRNE83bkpINnBoUjVFd3MyeEoxUUUiXX0sInBsYWNlX29mX2JpcnRoIjp7Il9zZCI6WyJJMjBveUNNRTByQ1ZFMFF3d091c0NtZHduYnpEdTZ1QmRuNEZ6aFhlaFRrIl19LCJhZGRyZXNzIjp7Il9zZCI6WyJUM2pSYkVNal85aVhVNHdleWRZem93dHQzcF8zb3lUNXZISHFjd0tCUjBzIiwiMjRjZGJua3IwallNOU9TdjJ5NkRBekdVakRUOS11cXpHNHVXOEc3aTlRZyIsIkplTkRCRWNSM0JMbTRsSG1nVEROdF9objM1eWdUX2xXci1namU1dG1VQ3MiLCJwYU1ZcVl3YnB4dm55c3J3WnNzNGVTTVRpdERDNWpoS2xFSmppVFFXWGQwIiwiTWstVmpxVTJnZUxoWTBpWmNWTXBoSGxUVnpvUnBwN0E1WGQ4dGlPRDBzbyIsInYwVTE4V2tYOUZ5a1h3NW1OZlFqZV82YUdoODFkT3RDVGxXbXM1dDlEUVUiLCJZNmEyLUhENWRsek9hUjhpbGRncXhaRDRMSUJVVEFlNzgxbjNlVVpaX2JZIl19LCJfc2QiOlsiZFhtR29SUVEzbTFZQXlZWVp3eU1YWlZOcVpkQkxuNnVzblpLSXVfbDVZQSIsIno3VFJCS2h1Uk1xSTg3Z2JhQ09WMnNiWEJZRmhyUW5TeWNwOHdZZTNRMjQiLCJGVV8xZ2ZTR29RamJJU1FOYW1JY213SFNrWWxzNkliSVIzUGFHWGVYYlZ3IiwiRzQzRXpWN1ZiakdlVGhKcF95OFJZOTdhWEdtZUx1eFgyTjFPZmRYQndTWSIsIklyQXZxVWwwdjNOaS15WkhqSG92Y004SU5iNzBjbU9KM0tKbTdHcDB2a0EiLCJCdmJQQ2VvMVV0UGVDQXpFc05aUHBIYkNKZEpieTg2Tl9ETXdKRVNhN3JzIiwiU1Rra3hXRzVBWWJnQUIzMW52M21QNWRVX0c1ZmppY1FxM05PY0ZTX3pwQSIsIlM4M3pfRi1FQmh2ZWxRdERQVTh5MGlIdThyaVdVT2dCYmtYbDVhM0FmTjAiLCJsR3ZBMW13ckVIT3ZhYkhUaWhjUlZ0Ml9YTzZxbEtLSTFqSkNWN3UzZG13IiwiMVlXckVBZzF5QzNwVnpCbzVjVTN5WllReHlJNEM4ajhlVEdvVExzZ3JmYyIsIld0Yi1vNE9TNDNMdDdvcmhYd2VZYksxUUtlRmpNNERZLVNYc3BMNUFxYnciLCJZOWtVWWNmSjd0VDNrNXZQbHpPcENfY2diR2JuSFRTeXY0bHlBclBIelBBIiwiVUdCZDVaS3g4Vm5hdGtNR0tHY3lTVDlVVGd0cnBJVTczVnk4eUhnOXFVZyIsInl1VHhoZEtqWk1obVIxWlJkU2R3WTlwRjc5Z2Z0Tmt6cW5MSlRwdGI2WW8iLCJDRWd6aFQ5ck45R3N2NXZ0b3VOZXVlVlBtci1hYkNrNGp0TVI3OEFwVmlnIiwiZ05jdnlBNURzVkV3T1UxTVVJLXV3N2tiZHRSeldTOFNDbWVUTjNxX1phayIsImFNbXB6UjFsREd1Y2FkLTJDLUNGRVZ3WnQ2a2NUeE1Ja3BPR0cwaHZGbU0iLCJXNG4zTU5vUXZsZEhuVVVGTkVYM0pGRFVldHh2czJSckZlRGVzWGVjUk9RIiwiZDhUdnkwU1NYNGdMOV9NMWhRVUJZRkQ2YUxDcE5jMDljc3BLZ0lZak1DQSIsImNUZkVzQXQwVVZZVklrS1pWMHhWbDNLcWtGVGxCbVRtZnlxWDJ6OHl3Q1EiLCJWNVVKX1hPcTZUUldHajJpVll3RjNFRTJLTHc0VVh3YmxLZUJZR2hqUnFJIiwieEw3Y2pYRmF6WmZFWXEzTlhzOUEzMWlGdThEX0VHUjNfek4yOW5NWGFwWSIsIi1kZ1lOa01DMzZndEZGZXVhdnNYalo1aEZzakJxUGF3S2trSTZ2ODMyRzgiLCJQLWZ1RXdlWm0yd3VfZUhfS29HYTFMNXJJNEJEOG5sMFlBUUhZWkdNRmFBIiwiN0NUVU02SkFmZ0hmSk9LbnM3bEk2R1ZlSTF3dE4xUjVVZUZuRHdLSmtMSSIsImR5REdtZWZEN0tpazRkOTVadlhTS2Q3WGl2QlhaaHNjWFRqSVBub2tVMVUiXSwic3ViIjoiZGlkOmtleTp6RG5hZVg5RGtIMnluWkFUclhwREs3VFo3UDU1RHhMMlZvMzF6MzVBUEREZTh2VGJrIiwibmJmIjoxNzQ3Mzg2NTc1LCJpc3MiOiJodHRwczovL3dhbGxldC5hLXNpdC5hdC9tNiIsImV4cCI6MTc0Nzk5MTM3NSwiaWF0IjoxNzQ3Mzg2NTc1LCJqdGkiOiJ1cm46dXVpZDo4ZjJmNGMyOC04ZGI3LTQ2ZjktYTY0Mi0zYzc0N2I2OTI3N2UiLCJ2Y3QiOiJ1cm46ZXVkaTpwaWQ6MSIsInN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZHgiOjIwNDIsInVyaSI6Imh0dHBzOi8vd2FsbGV0LmEtc2l0LmF0L202L2NyZWRlbnRpYWxzL3N0YXR1cy8xIn19LCJfc2RfYWxnIjoic2hhLTI1NiIsImNuZiI6eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJraWQiOiJkaWQ6a2V5OnpEbmFlWDlEa0gyeW5aQVRyWHBESzdUWjdQNTVEeEwyVm8zMXozNUFQRERlOHZUYmsiLCJrdHkiOiJFQyIsIngiOiJZN2VPbEtMMjhNRlRITTctNHIyWHpwSzlfcmRmMDV3RlVUWWMwbmpUZkY4IiwieSI6InloMFFCVkVIajVTUS10a1NBRU5EVVBYOG15WDhpdm9JZzEyX0RxcGY3NlEifX19.RNPgq6LfnA9L16ena21Wyc5zR42aobSltP1WxbOTqzYtVbuv2hmsJST2TuWNoMK8yeMezw5CapLKui9T0_YPLg~WyJTbjBRdDhscXR3dFBMM1d1UnV2eG9DdDFwVmc1S2E1Q1Nkck85clpIMThnIiwiZ2l2ZW5fbmFtZSIsIkJlcm5kIl0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3NDc0MDI4NDQsImF1ZCI6InFhLXZlcmlmaWVyLnd3d2FsbGV0Lm9yZyIsIm5vbmNlIjoiMmUwZWNmODEtYzE1OC00OGRmLThhNjUtNTAzYTZiYzNhNjhjIiwic2RfaGFzaCI6IjdwMmFtVmh0TkpCeXpSbnB2ZVhWaUJVS0J4N29tUHVERDJYWE1VYUptbVkifQ.-J9Wiwg0H4HT8NT-s3q-fkO020JTiFsSTnMgupfHOmx9h6vrSfI1JWMlBTrLQvVdaCNrM9P1fWtI85DTDuIdgQ` +const rawCredentialWithoutMetadata = `eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDUmpDQ0FlMmdBd0lCQWdJVWMweFRYeHJRdkdwdkJJMHB0VlVCbHFDYkFyb3dDZ1lJS29aSXpqMEVBd0l3TERFTE1Ba0dBMVVFQmhNQ1FWUXhEakFNQmdOVkJBb01CVUV0VTBsVU1RMHdDd1lEVlFRRERBUkpRVU5CTUI0WERUSTFNRFV3TlRFek1qQXpOVm9YRFRJMk1EVXdOVEV6TWpBek5Wb3dPakVMTUFrR0ExVUVCaE1DUVZReERqQU1CZ05WQkFvTUJVRXRVMGxVTVJzd0dRWURWUVFEREJKRWIyTjFiV1Z1ZENCVGFXZHVaWElnVFRZd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTTU5XcDFRSDhiM1hwQmVNT3Y5cGRTeHV4QjI0TDZDMXJ5OEhWUmFmWEJLdm4yQkpnWm9CTm53eVhiSURnakJReVhYejUwNFlEdWxWdWljS2JZR1dnT280SGVNSUhiTUE0R0ExVWREd0VCL3dRRUF3SUhnREFWQmdOVkhTVUJBZjhFQ3pBSkJnY29nWXhkQlFFQ01ESUdBMVVkSHdRck1Da3dKNkFsb0NPR0lXaDBkSEJ6T2k4dmQyRnNiR1YwTG1FdGMybDBMbUYwTDJOeWJDOHhMbU55YkRBYUJnTlZIUkVFRXpBUmdnOTNZV3hzWlhRdVlTMXphWFF1WVhRd0lnWURWUjBTQkJzd0dZWVhhSFIwY0hNNkx5OTNZV3hzWlhRdVlTMXphWFF1WVhRd0h3WURWUjBqQkJnd0ZvQVVEUUY1SzQ2WVZnekxwZlY1c3RvdXRCZXpLNlF3SFFZRFZSME9CQllFRk8ya2c0VUxBU1FaNkYrT0N1WmdNamVWVmYvOE1Bb0dDQ3FHU000OUJBTUNBMGNBTUVRQ0lINnhCNkZ1UkVpSVgzbkY4ZERjdkMvS1JjN1FoZTdhSW5LM3VBa1FXaDN1QWlBVmRSUnl5ZmoweTgwSm96SndCTGpncEk5THIwNk1OWXlGRnc4V3I3Si90dz09Il19.eyJhZ2VfZXF1YWxfb3Jfb3ZlciI6eyJfc2QiOlsicjBkNlYzTnJoTFQ4eWphLWFZOFRiRy1aMW5CTEY0VnF4dkNJV2VtMzBFWSIsIjhJdnk4RWJtWHdudW1HZ3hzOFZsbUVlTkQ3TUVFbmhlTzRucmxTelpZU0UiLCJIU052M2JYWlk2T2RYTmNBS09qY1ZkeWNwS3ZYRGRvOEFMRXFfVklqWXhRIiwiOVF2M3daemtUTHQtemg1aWluN0syV3pQWlhveDZLQWpvZXNmbEhtMEtKVSIsIlhTVWtla2hreGs0Z2pjdXlnUzJoWkRrTzdRNVN2ejhSOUVvZWROZ2h4aXciLCI2d3VWUXRrZVRUaE1qT2wyZWJPWUZCMXo1NElETnBiWUNKcE5FUGtncTJzIiwicEdua003cjFDTENLaUxQR285cWlrODRNYkRRM1pCVTRiQXhTN2phR3ZGcyIsIlEtX3JFQlFfUTlSb2pjTnVsaEdKLXlfLVc3Q1ZhN1hBdkM3YkRWZnJZbzgiLCJhMG11Mm5SeG4wejVMc2FnYVpmTDY2eEI2LTRybThmRlVXRUJmUzJ1UWRJIiwiTDk3SXlPUklsb0o0dl8yZHNVZFpRMEo5enBkcm9FNThsVmNPekNxWWRkZyIsInp1UkhrclRMcUJOeVFta0NHd21CVzRRNE83bkpINnBoUjVFd3MyeEoxUUUiXX0sInBsYWNlX29mX2JpcnRoIjp7Il9zZCI6WyJJMjBveUNNRTByQ1ZFMFF3d091c0NtZHduYnpEdTZ1QmRuNEZ6aFhlaFRrIl19LCJhZGRyZXNzIjp7Il9zZCI6WyJUM2pSYkVNal85aVhVNHdleWRZem93dHQzcF8zb3lUNXZISHFjd0tCUjBzIiwiMjRjZGJua3IwallNOU9TdjJ5NkRBekdVakRUOS11cXpHNHVXOEc3aTlRZyIsIkplTkRCRWNSM0JMbTRsSG1nVEROdF9objM1eWdUX2xXci1namU1dG1VQ3MiLCJwYU1ZcVl3YnB4dm55c3J3WnNzNGVTTVRpdERDNWpoS2xFSmppVFFXWGQwIiwiTWstVmpxVTJnZUxoWTBpWmNWTXBoSGxUVnpvUnBwN0E1WGQ4dGlPRDBzbyIsInYwVTE4V2tYOUZ5a1h3NW1OZlFqZV82YUdoODFkT3RDVGxXbXM1dDlEUVUiLCJZNmEyLUhENWRsek9hUjhpbGRncXhaRDRMSUJVVEFlNzgxbjNlVVpaX2JZIl19LCJfc2QiOlsiZFhtR29SUVEzbTFZQXlZWVp3eU1YWlZOcVpkQkxuNnVzblpLSXVfbDVZQSIsIno3VFJCS2h1Uk1xSTg3Z2JhQ09WMnNiWEJZRmhyUW5TeWNwOHdZZTNRMjQiLCJGVV8xZ2ZTR29RamJJU1FOYW1JY213SFNrWWxzNkliSVIzUGFHWGVYYlZ3IiwiRzQzRXpWN1ZiakdlVGhKcF95OFJZOTdhWEdtZUx1eFgyTjFPZmRYQndTWSIsIklyQXZxVWwwdjNOaS15WkhqSG92Y004SU5iNzBjbU9KM0tKbTdHcDB2a0EiLCJCdmJQQ2VvMVV0UGVDQXpFc05aUHBIYkNKZEpieTg2Tl9ETXdKRVNhN3JzIiwiU1Rra3hXRzVBWWJnQUIzMW52M21QNWRVX0c1ZmppY1FxM05PY0ZTX3pwQSIsIlM4M3pfRi1FQmh2ZWxRdERQVTh5MGlIdThyaVdVT2dCYmtYbDVhM0FmTjAiLCJsR3ZBMW13ckVIT3ZhYkhUaWhjUlZ0Ml9YTzZxbEtLSTFqSkNWN3UzZG13IiwiMVlXckVBZzF5QzNwVnpCbzVjVTN5WllReHlJNEM4ajhlVEdvVExzZ3JmYyIsIld0Yi1vNE9TNDNMdDdvcmhYd2VZYksxUUtlRmpNNERZLVNYc3BMNUFxYnciLCJZOWtVWWNmSjd0VDNrNXZQbHpPcENfY2diR2JuSFRTeXY0bHlBclBIelBBIiwiVUdCZDVaS3g4Vm5hdGtNR0tHY3lTVDlVVGd0cnBJVTczVnk4eUhnOXFVZyIsInl1VHhoZEtqWk1obVIxWlJkU2R3WTlwRjc5Z2Z0Tmt6cW5MSlRwdGI2WW8iLCJDRWd6aFQ5ck45R3N2NXZ0b3VOZXVlVlBtci1hYkNrNGp0TVI3OEFwVmlnIiwiZ05jdnlBNURzVkV3T1UxTVVJLXV3N2tiZHRSeldTOFNDbWVUTjNxX1phayIsImFNbXB6UjFsREd1Y2FkLTJDLUNGRVZ3WnQ2a2NUeE1Ja3BPR0cwaHZGbU0iLCJXNG4zTU5vUXZsZEhuVVVGTkVYM0pGRFVldHh2czJSckZlRGVzWGVjUk9RIiwiZDhUdnkwU1NYNGdMOV9NMWhRVUJZRkQ2YUxDcE5jMDljc3BLZ0lZak1DQSIsImNUZkVzQXQwVVZZVklrS1pWMHhWbDNLcWtGVGxCbVRtZnlxWDJ6OHl3Q1EiLCJWNVVKX1hPcTZUUldHajJpVll3RjNFRTJLTHc0VVh3YmxLZUJZR2hqUnFJIiwieEw3Y2pYRmF6WmZFWXEzTlhzOUEzMWlGdThEX0VHUjNfek4yOW5NWGFwWSIsIi1kZ1lOa01DMzZndEZGZXVhdnNYalo1aEZzakJxUGF3S2trSTZ2ODMyRzgiLCJQLWZ1RXdlWm0yd3VfZUhfS29HYTFMNXJJNEJEOG5sMFlBUUhZWkdNRmFBIiwiN0NUVU02SkFmZ0hmSk9LbnM3bEk2R1ZlSTF3dE4xUjVVZUZuRHdLSmtMSSIsImR5REdtZWZEN0tpazRkOTVadlhTS2Q3WGl2QlhaaHNjWFRqSVBub2tVMVUiXSwic3ViIjoiZGlkOmtleTp6RG5hZVg5RGtIMnluWkFUclhwREs3VFo3UDU1RHhMMlZvMzF6MzVBUEREZTh2VGJrIiwibmJmIjoxNzQ3Mzg2NTc1LCJpc3MiOiJodHRwczovL3dhbGxldC5hLXNpdC5hdC9tNiIsImV4cCI6MTc0Nzk5MTM3NSwiaWF0IjoxNzQ3Mzg2NTc1LCJqdGkiOiJ1cm46dXVpZDo4ZjJmNGMyOC04ZGI3LTQ2ZjktYTY0Mi0zYzc0N2I2OTI3N2UiLCJ2Y3QiOiJ1cm46ZXVkaTpwaWQ6MSIsInN0YXR1cyI6eyJzdGF0dXNfbGlzdCI6eyJpZHgiOjIwNDIsInVyaSI6Imh0dHBzOi8vd2FsbGV0LmEtc2l0LmF0L202L2NyZWRlbnRpYWxzL3N0YXR1cy8xIn19LCJfc2RfYWxnIjoic2hhLTI1NiIsImNuZiI6eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJraWQiOiJkaWQ6a2V5OnpEbmFlWDlEa0gyeW5aQVRyWHBESzdUWjdQNTVEeEwyVm8zMXozNUFQRERlOHZUYmsiLCJrdHkiOiJFQyIsIngiOiJZN2VPbEtMMjhNRlRITTctNHIyWHpwSzlfcmRmMDV3RlVUWWMwbmpUZkY4IiwieSI6InloMFFCVkVIajVTUS10a1NBRU5EVVBYOG15WDhpdm9JZzEyX0RxcGY3NlEifX19.RNPgq6LfnA9L16ena21Wyc5zR42aobSltP1WxbOTqzYtVbuv2hmsJST2TuWNoMK8yeMezw5CapLKui9T0_YPLg~WyJTbjBRdDhscXR3dFBMM1d1UnV2eG9DdDFwVmc1S2E1Q1Nkck85clpIMThnIiwiZ2l2ZW5fbmFtZSIsIkJlcm5kIl0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3NDc0MDI4NDQsImF1ZCI6InFhLXZlcmlmaWVyLnd3d2FsbGV0Lm9yZyIsIm5vbmNlIjoiMmUwZWNmODEtYzE1OC00OGRmLThhNjUtNTAzYTZiYzNhNjhjIiwic2RfaGFzaCI6IjdwMmFtVmh0TkpCeXpSbnB2ZVhWaUJVS0J4N29tUHVERDJYWE1VYUptbVkifQ.-J9Wiwg0H4HT8NT-s3q-fkO020JTiFsSTnMgupfHOmx9h6vrSfI1JWMlBTrLQvVdaCNrM9P1fWtI85DTDuIdgQ`; const rawCredentialAnimo = `eyJ0eXAiOiJkYytzZC1qd3QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlCN3pDQ0FaV2dBd0lCQWdJUUozMUFXTUpTV0xxUHppS21VZGhCUVRBS0JnZ3Foa2pPUFFRREFqQWRNUTR3REFZRFZRUURFd1ZCYm1sdGJ6RUxNQWtHQTFVRUJoTUNUa3d3SGhjTk1qVXhNVEE0TVRZeU9ETXdXaGNOTWpZeE1USTRNVFl5T0RNd1dqQWhNUkl3RUFZRFZRUURFd2xqY21Wa2J5QmtZM014Q3pBSkJnTlZCQVlUQWs1TU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUoxT0VsVHZ4SkhKQmh5bHQvNkx0NDRlVVlzTHlBQ2xGZWgvKzhOeFhNZ2xCeWRNYi96bk9LMCtHM3hHMzExSFpiVTdMTm9BL2FQblNLeVY0MnlWL2dLT0JzakNCcnpBZEJnTlZIUTRFRmdRVXhqL2dCUzIzY3RtN25vZ3hOcEIrWVpJNlV6c3dEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CVUdBMVVkSlFFQi93UUxNQWtHQnlpQmpGMEZBUUl3SHdZRFZSMGpCQmd3Rm9BVVZDNVhXMVBUWU5vNnlXbmtKR2d2QlZDdFdaVXdKZ1lEVlIwU0JCOHdIWVliYUhSMGNITTZMeTl3YkdGNVozSnZkVzVrTG1GdWFXMXZMbWxrTUI0R0ExVWRFUVFYTUJXQ0UzQnNZWGxuY205MWJtUXVZVzVwYlc4dWFXUXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdMNUVHVWh6aHdMKzdJbFlKVGd5M2grcnVobmh5R3YyNUpYSnR1d0w2VGxVQ0lRQ0Zhc1d0OElzd3hwUXhCbHI3UjdNdWxkZmFmREh0aXA3RzVBcExNMTFMZFE9PSJdfQ.eyJpc3N1YW5jZV9kYXRlIjoiMjAyNi0wMS0xNiIsImV4cGlyeV9kYXRlIjoiMjAyNy0wMS0yNiIsImlzc3VpbmdfY291bnRyeSI6IkRFIiwibmJmIjoxNzY4NTIxNjAwLCJleHAiOjE4MDA5MjE2MDAsInZjdCI6Imh0dHBzOi8vZXhhbXBsZS5ldWRpLmVjLmV1cm9wYS5ldS9jb3IvMSIsImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJraWQiOiJMenhYOVkzVUU1ZklueUtqVE5sMDR4b1dPM1NiMEhZa0ptUkpXZk5JcDA4IiwiYWxnIjoiRVMyNTYiLCJrZXlfb3BzIjpbInZlcmlmeSJdLCJleHQiOnRydWUsImNydiI6IlAtMjU2IiwieCI6IkdVWjhWWFZwWmdEVUpTOG8xOUZLUzFITF92OEhrYnhqRGdIa0JhdlRaQWciLCJ5IjoiMU1RNkttLVVBRU1wRS05Rm8yT3pnNmwxTXloYVp3NkRaMkxlWm5lTFNwZyJ9fSwiaWF0IjoxNzcwMTI0NDQwLCJfc2QiOlsiSjBpTzlfZy1KY2xBRjlNdTAtdVJ0aUlRYnRNSURqMzhkWW45ZEFMMFNHdyIsIk9tNTJvbDZKaDluaG5PcnhjcVdrWVFOZ0tHY0hsaEFUY3BlTW1kZnp1akkiLCJQUmtLWVB0TDBuRjlTaEVPYVpFOTJ3WlZKVVhFeC1fQVlIeFNKcldrZWg0IiwiYm1vMHlhUmVXMUlEVTJpbWJIV3dzX2dJQlRqNEJjTkw1eUg1THZFOF9jRSIsImk0d1FQRnhYREhKZ1Nla3pmQW9NU0Qwa3VWVU5YNWJOTTZQdzE5SzZUZXciLCJtMVc5b3FJQm45ajZWWnYyckNqeHpBSnhHX2tnYzF4a25uZ29qaWw3dDk4Iiwidm95VGpGZUVlazVDMkNmY3lqZk0tRWRjVDZxUzJ3X2RWVkxETU1GcmdUWSIsIndybW11dDVySjZ2VEZRLVA4dWcyZUNYdlZwTGh0NGdPcEVpYWxjS0hQdUUiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.OszuhnEVNG1YlCNDU5TEufw22N175rlTNOZjEpCBeKBU-vOx2v9SmiAFiXYNecK2LG-vl7MQeWjOQDOAvAp6Rw~WyJYaGhRN1lIQldtOXMtc3RQIiwiZmFtaWx5X25hbWUiLCJNdXN0ZXJtYW5uIl0~WyJ1Qm1DRC1MX3NiMVh3TWNtIiwiZ2l2ZW5fbmFtZSIsIkVyaWthIl0~WyIwSjc4TVR5TFJwUW5wZ08zIiwiYmlydGhfZGF0ZSIsIjE5NjQtMDgtMTIiXQ~WyJ6TkVIaU9Gd05XMjR4THFkIiwicmVzaWRlbnRfYWRkcmVzcyIsIkhlaWRlc3RyYXNzZSAxNywgNTExNDcgS29sbiJd~WyJpRkRoVms4TGFKSEpPNDctIiwiZ2VuZGVyIiwyXQ~WyJFM2hkeHdOSzRET1VpUUN6IiwiYmlydGhfcGxhY2UiLCJLw7ZsbiJd~WyJrS1JBQTZ4SGNHMHJWVDY2IiwiYXJyaXZhbF9kYXRlIiwiMjAyNC0wMy0wMSJd~WyJXWU9sRmhlb2lkNHdjUVNwIiwibmF0aW9uYWxpdHkiLCJERSJd~`; @@ -24,51 +24,52 @@ const metadataNotFoundHttpClient: HttpClient = { post: async () => ({ status: 404, headers: {}, data: null }), }; -describe("The SDJWTVCParser", () => { - - it("should parse an vc+sd-jwt credential", async () => { +describe('The SDJWTVCParser', () => { + it('should parse an vc+sd-jwt credential', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential }); - console.log('!!!', parsedCredential) + console.log('!!!', parsedCredential); assert(parsedCredential.success); - assert(parsedCredential.value.validityInfo.validUntil?.toISOString() === new Date("2026-11-10T11:57:02.000Z").toISOString()); - }) + assert( + parsedCredential.value.validityInfo.validUntil?.toISOString() === + new Date('2026-11-10T11:57:02.000Z').toISOString(), + ); + }); - it("should detect expired vc+sd-jwt credential", async () => { + it('should detect expired vc+sd-jwt credential', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential: expiredCredential }); assert(parsedCredential.success); - }) - + }); - it("should parse SDJWT with KB+JWT", async () => { + it('should parse SDJWT with KB+JWT', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential: rawPresentation }); assert(parsedCredential.success); - }) + }); - it("should handle the case of invalid format", async () => { + it('should handle the case of invalid format', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential: msoMdocIssuerSigned }); assert(parsedCredential.success === false); - }) + }); - it("should parse credential succesfully", async () => { + it('should parse credential succesfully', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential }); assert(parsedCredential.success === true); assert(parsedCredential.value.signedClaims); - }) + }); - it("should parse animo credential succesfully", async () => { + it('should parse animo credential succesfully', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential: rawCredentialAnimo }); assert(parsedCredential.success === true); assert(parsedCredential.value.signedClaims); - }) + }); - it("should parse credential without metadata succesfully", async () => { + it('should parse credential without metadata succesfully', async () => { const parser = SDJWTVCParser({ httpClient: metadataNotFoundHttpClient, context }); const parsedCredential = await parser.parse({ rawCredential: rawCredentialWithoutMetadata }); @@ -78,87 +79,97 @@ describe("The SDJWTVCParser", () => { } assert(parsedCredential.success === true); assert(parsedCredential.value.signedClaims); + }); - }) - - it("should fail if rawCredential is not a string", async () => { + it('should fail if rawCredential is not a string', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); - const result = await parser.parse({ rawCredential: { some: "object" } as any }); // deliberately incorrect + const result = await parser.parse({ rawCredential: { some: 'object' } as any }); // deliberately incorrect assert(result.success === false); assert(result.error === CredentialParsingError.UnsupportedFormat); }); - it("should fail if try to parse", async () => { + it('should fail if try to parse', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); // Give it completely invalid string that will break base64 parsing - const result = await parser.parse({ rawCredential: "!!!not-a-jwt!!!" }); + const result = await parser.parse({ rawCredential: '!!!not-a-jwt!!!' }); assert(result.success === false); assert(result.error === CredentialParsingError.UnsupportedFormat); }); - it("should fail if JWT typ is not supported", async () => { - + it('should fail if JWT typ is not supported', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); - const result = await parser.parse({ rawCredential: "eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJfc2QiOlsiM3Zrdmtvc0hnYkxLTFRmS2RkcjVRRkxEUkZQMXktcy03T2hVU1hRa0RPayIsIllxOVJCVWJVZEV2U0JRSG94VUt4ZUo3aUh6Uzh6dGdyQ3RoRUZabEF4ZDQiLCJ5VmFGT05pQ3VGX002S0hMaEhlZV9udmtpU09HWENoUkpPaHFZN0Y4RUY4Il0sIl9zZF9hbGciOiJTSEEtMjU2In0.onjWOmZ5biJFnpnWgbxge1qYlvQs-QT_piVI3mV5tw8kvbq3dCIxZP3dxhY3Rk95MxR_37AWYV3ObyM7Te8G0w~WyIyMDIyNzlhN2RjMzIwMzk2IiwiZmlyc3RuYW1lIiwiSm9obiJd~WyI5MjUxYmQzNDQwZTAxMGM3IiwibGFzdG5hbWUiLCJEb2UiXQ~WyIwN2ZhYzIyZWZkN2QzM2Q0Iiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~" }); + const result = await parser.parse({ + rawCredential: + 'eyJ0eXAiOiJzZCtqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJfc2QiOlsiM3Zrdmtvc0hnYkxLTFRmS2RkcjVRRkxEUkZQMXktcy03T2hVU1hRa0RPayIsIllxOVJCVWJVZEV2U0JRSG94VUt4ZUo3aUh6Uzh6dGdyQ3RoRUZabEF4ZDQiLCJ5VmFGT05pQ3VGX002S0hMaEhlZV9udmtpU09HWENoUkpPaHFZN0Y4RUY4Il0sIl9zZF9hbGciOiJTSEEtMjU2In0.onjWOmZ5biJFnpnWgbxge1qYlvQs-QT_piVI3mV5tw8kvbq3dCIxZP3dxhY3Rk95MxR_37AWYV3ObyM7Te8G0w~WyIyMDIyNzlhN2RjMzIwMzk2IiwiZmlyc3RuYW1lIiwiSm9obiJd~WyI5MjUxYmQzNDQwZTAxMGM3IiwibGFzdG5hbWUiLCJEb2UiXQ~WyIwN2ZhYzIyZWZkN2QzM2Q0Iiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~', + }); assert(result.success === false); assert(result.error === CredentialParsingError.UnsupportedFormat); }); - it("should fail with iss", async () => { - + it('should fail with iss', async () => { const parser = SDJWTVCParser({ httpClient: defaultHttpClient, context }); - const result = await parser.parse({ rawCredential: "eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJpc3MiOiJodHRwOi8vd2FsbGV0LWVudGVycHJpc2UtdmlkLWlzc3VlcjE6ODAwMyIsIl9zZCI6WyJMMWtxT1Nocm9kSW5XUEhjc3F2Y3RzZEszeEx0dGRZakoteWpQM01BUUhrIiwiVHBhcVRrSmtSTDNUbHdHc1NGSG5fNUp6LUZUdm8zdmlsUXdmOGYzTWRYdyIsImhDcG5jOUZVdGxpa2ZJTHRjX2phYkcwRFpyQWVUVnFHWlRmRGNOOGJYRGMiXSwiX3NkX2FsZyI6IlNIQS0yNTYifQ.AcXYVFuVaXKqkco1a_Opp9-qTsQVdVgBm-Bzg3S_taahTK2Msiu7_zGnzcVFq9tuvTrfJDMbJg6DtoKVqTxsmA~WyJmNWQ3ZTI5NDcwNmNkNGNjIiwiZmlyc3RuYW1lIiwiSm9obiJd~WyI3YjRlOTUwMjIzZTUzMGJiIiwibGFzdG5hbWUiLCJEb2UiXQ~WyJjODc1MTlhNDg4YmQ1MDEwIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~" }); + const result = await parser.parse({ + rawCredential: + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJpZCI6IjEyMzQiLCJpc3MiOiJodHRwOi8vd2FsbGV0LWVudGVycHJpc2UtdmlkLWlzc3VlcjE6ODAwMyIsIl9zZCI6WyJMMWtxT1Nocm9kSW5XUEhjc3F2Y3RzZEszeEx0dGRZakoteWpQM01BUUhrIiwiVHBhcVRrSmtSTDNUbHdHc1NGSG5fNUp6LUZUdm8zdmlsUXdmOGYzTWRYdyIsImhDcG5jOUZVdGxpa2ZJTHRjX2phYkcwRFpyQWVUVnFHWlRmRGNOOGJYRGMiXSwiX3NkX2FsZyI6IlNIQS0yNTYifQ.AcXYVFuVaXKqkco1a_Opp9-qTsQVdVgBm-Bzg3S_taahTK2Msiu7_zGnzcVFq9tuvTrfJDMbJg6DtoKVqTxsmA~WyJmNWQ3ZTI5NDcwNmNkNGNjIiwiZmlyc3RuYW1lIiwiSm9obiJd~WyI3YjRlOTUwMjIzZTUzMGJiIiwibGFzdG5hbWUiLCJEb2UiXQ~WyJjODc1MTlhNDg4YmQ1MDEwIiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ~', + }); assert(result.success === false); assert(result.error === CredentialParsingError.InvalidSdJwtVcPayload); }); - it("should warn if issuer metadata fetch fail", async () => { + it('should warn if issuer metadata fetch fail', async () => { const malformedHttpClient: HttpClient = { get: async (url: string) => { - if (url.includes(".well-known/openid-credential-issuer")) { + if (url.includes('.well-known/openid-credential-issuer')) { return { status: 400, }; } // fallback to real http call for everything else return defaultHttpClient.get(url); - } + }, }; const parser = SDJWTVCParser({ httpClient: malformedHttpClient, context }); const result = await parser.parse({ rawCredential }); assert(result.success === true); - assert((result.value.warnings ?? []).some(w => w.code === CredentialParsingError.FailFetchIssuerMetadata)); + assert( + (result.value.warnings ?? []).some( + (w) => w.code === CredentialParsingError.FailFetchIssuerMetadata, + ), + ); }); - it("should warn if issuer metadata have invalid schema", async () => { + it('should warn if issuer metadata have invalid schema', async () => { const malformedHttpClient: HttpClient = { get: async (url: string) => { - if (url.includes(".well-known/openid-credential-issuer")) { + if (url.includes('.well-known/openid-credential-issuer')) { return { status: 200, data: { // Malformed on purpose: missing `credential_configurations_supported` - invalid_field: true - } + invalid_field: true, + }, }; } // fallback to real http call for everything else return defaultHttpClient.get(url); - } + }, }; const parser = SDJWTVCParser({ httpClient: malformedHttpClient, context }); const result = await parser.parse({ rawCredential }); assert(result.success === true); - assert((result.value.warnings ?? []).some(w => w.code === CredentialParsingError.FailSchemaIssuerMetadata)); + assert( + (result.value.warnings ?? []).some( + (w) => w.code === CredentialParsingError.FailSchemaIssuerMetadata, + ), + ); }); - -}) +}); diff --git a/src/credential-parsers/SDJWTVCParser.ts b/src/credential-parsers/SDJWTVCParser.ts index f0dc4cd..a3beb30 100644 --- a/src/credential-parsers/SDJWTVCParser.ts +++ b/src/credential-parsers/SDJWTVCParser.ts @@ -1,58 +1,64 @@ -import { SDJwt } from "@sd-jwt/core"; -import type { HasherAndAlg } from "@sd-jwt/types"; -import { CredentialParsingError } from "../error"; -import { Context, CredentialParser, HttpClient } from "../interfaces"; -import { HashAlgorithm, MetadataWarning, VerifiableCredentialFormat } from "../types"; -import { SdJwtVcPayloadSchema } from "../schemas"; -import { CredentialRenderingService } from "../rendering"; -import { getSdJwtVcMetadata } from "../utils/getSdJwtVcMetadata"; -import { CustomCredentialSvg } from "../functions/CustomCredentialSvg"; +import { SDJwt } from '@sd-jwt/core'; +import type { HasherAndAlg } from '@sd-jwt/types'; +import { CredentialParsingError } from '../error'; +import { Context, CredentialParser, HttpClient } from '../interfaces'; +import { HashAlgorithm, MetadataWarning, VerifiableCredentialFormat } from '../types'; +import { SdJwtVcPayloadSchema } from '../schemas'; +import { CredentialRenderingService } from '../rendering'; +import { getSdJwtVcMetadata } from '../utils/getSdJwtVcMetadata'; +import { CustomCredentialSvg } from '../functions/CustomCredentialSvg'; import { z } from 'zod'; -import { getIssuerMetadata } from "../utils/getIssuerMetadata"; -import { TypeMetadata as TypeMetadataSchema } from "../schemas/SdJwtVcTypeMetadataSchema"; -import { convertOpenid4vciToSdjwtvcClaims } from "../functions/convertOpenid4vciToSdjwtvcClaims"; -import { dataUriResolver } from "../resolvers/dataUriResolver"; -import { friendlyNameResolver } from "../resolvers/friendlyNameResolver"; -import { fromBase64Url } from "../utils"; - -export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient }): CredentialParser { +import { getIssuerMetadata } from '../utils/getIssuerMetadata'; +import { TypeMetadata as TypeMetadataSchema } from '../schemas/SdJwtVcTypeMetadataSchema'; +import { convertOpenid4vciToSdjwtvcClaims } from '../functions/convertOpenid4vciToSdjwtvcClaims'; +import { dataUriResolver } from '../resolvers/dataUriResolver'; +import { friendlyNameResolver } from '../resolvers/friendlyNameResolver'; +import { fromBase64Url } from '../utils'; + +export function SDJWTVCParser(args: { + context: Context; + httpClient: HttpClient; +}): CredentialParser { const encoder = new TextEncoder(); function canParseSdJwtVc(raw: unknown): raw is string { const decoder = new TextDecoder(); - if (typeof raw !== "string") return false; + if (typeof raw !== 'string') return false; - if (raw.includes(".")) { + if (raw.includes('.')) { const { typ } = JSON.parse(decoder.decode(fromBase64Url(raw.split('.')[0]))); if (typ === VerifiableCredentialFormat.VC_SDJWT) return true; if (typ === VerifiableCredentialFormat.DC_SDJWT) return true; - } return false; } - function extractValidityInfo(jwtPayload: { exp?: number, iat?: number, nbf?: number }): { validUntil?: Date, validFrom?: Date, signed?: Date } { + function extractValidityInfo(jwtPayload: { exp?: number; iat?: number; nbf?: number }): { + validUntil?: Date; + validFrom?: Date; + signed?: Date; + } { let obj = {}; if (jwtPayload.exp) { obj = { ...obj, validUntil: new Date(jwtPayload.exp * 1000), - } + }; } if (jwtPayload.iat) { obj = { ...obj, signed: new Date(jwtPayload.iat * 1000), - } + }; } if (jwtPayload.nbf) { obj = { ...obj, validFrom: new Date(jwtPayload.nbf * 1000), - } + }; } return obj; } @@ -60,8 +66,7 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } // Encoding the string into a Uint8Array const hasherAndAlgorithm: HasherAndAlg = { hasher: (data: string | ArrayBuffer, alg: string) => { - const encoded = - typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data); + const encoded = typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data); return args.context.subtle.digest(alg, encoded).then((v) => new Uint8Array(v)); }, @@ -71,10 +76,8 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } const cr = CredentialRenderingService(); const renderer = CustomCredentialSvg({ httpClient: args.httpClient }); - return { async parse({ rawCredential, credentialIssuer }) { - if (!canParseSdJwtVc(rawCredential)) { return { success: false, @@ -91,12 +94,15 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } const headers = await parsedSdJwt.jwt?.header; const payload = await parsedSdJwt.jwt?.payload; - return { parsedClaims: claims as Record, parsedHeaders: headers, parsedPayload: payload, err: null }; - } - catch (err) { + return { + parsedClaims: claims as Record, + parsedHeaders: headers, + parsedPayload: payload, + err: null, + }; + } catch (err) { return { parsedClaims: null, parsedHeaders: null, err: err }; } - })(); if (err || !parsedClaims || !parsedHeaders) { return { @@ -105,13 +111,16 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } }; } - const schema = z.enum([VerifiableCredentialFormat.VC_SDJWT, VerifiableCredentialFormat.DC_SDJWT]); + const schema = z.enum([ + VerifiableCredentialFormat.VC_SDJWT, + VerifiableCredentialFormat.DC_SDJWT, + ]); const typParseResult = await schema.safeParseAsync(parsedHeaders.typ); if (typParseResult.error) { return { success: false, error: CredentialParsingError.NotSupportedCredentialType, - } + }; } // sd-jwt vc Payload Schema Validation @@ -125,28 +134,37 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } }; } - - const { metadata: issuerMetadata } = validatedParsedClaims.iss ? await getIssuerMetadata(args.httpClient, validatedParsedClaims.iss, warnings) : { metadata: undefined }; + const { metadata: issuerMetadata } = validatedParsedClaims.iss + ? await getIssuerMetadata(args.httpClient, validatedParsedClaims.iss, warnings) + : { metadata: undefined }; const vctIntegrity = validatedParsedClaims['vct#integrity'] as string | undefined; - const getSdJwtMetadataResult = await getSdJwtVcMetadata(args.context.vctResolutionEngine, args.context.subtle, args.httpClient, validatedParsedClaims.vct, vctIntegrity, warnings); + const getSdJwtMetadataResult = await getSdJwtVcMetadata( + args.context.vctResolutionEngine, + args.context.subtle, + args.httpClient, + validatedParsedClaims.vct, + vctIntegrity, + warnings, + ); if ('error' in getSdJwtMetadataResult) { return { success: false, error: getSdJwtMetadataResult.error, - } + }; } let TypeMetadata: Partial = {}; let credentialMetadata: TypeMetadataSchema | undefined = undefined; const credentialIssuerMetadata = credentialIssuer?.credentialConfigurationId - ? issuerMetadata?.credential_configurations_supported?.[credentialIssuer?.credentialConfigurationId] + ? issuerMetadata?.credential_configurations_supported?.[ + credentialIssuer?.credentialConfigurationId + ] : undefined; if (getSdJwtMetadataResult.credentialMetadata) { - - credentialMetadata = getSdJwtMetadataResult.credentialMetadata + credentialMetadata = getSdJwtMetadataResult.credentialMetadata; if (credentialMetadata?.claims) { TypeMetadata = { claims: credentialMetadata.claims }; @@ -156,7 +174,7 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } const friendlyName = friendlyNameResolver({ credentialDisplayArray: credentialMetadata?.display, issuerDisplayArray: credentialIssuerMetadata?.credential_metadata?.display, - fallbackName: "SD-JWT Verifiable Credential", + fallbackName: 'SD-JWT Verifiable Credential', }); const dataUri = dataUriResolver({ @@ -169,11 +187,13 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } sdJwtVcRenderer: cr, sdJwtVcMetadataClaims: credentialMetadata?.claims, - fallbackName: "SD-JWT Verifiable Credential", + fallbackName: 'SD-JWT Verifiable Credential', }); if (!TypeMetadata?.claims && credentialIssuerMetadata?.credential_metadata?.claims) { - const convertedClaims = convertOpenid4vciToSdjwtvcClaims(credentialIssuerMetadata.credential_metadata.claims); + const convertedClaims = convertOpenid4vciToSdjwtvcClaims( + credentialIssuerMetadata.credential_metadata.claims, + ); if (convertedClaims?.length) { TypeMetadata = { claims: convertedClaims }; } @@ -186,7 +206,7 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } metadata: { credential: { format: typParseResult.data, - vct: validatedParsedClaims?.vct as string | undefined ?? "", + vct: (validatedParsedClaims?.vct as string | undefined) ?? '', TypeMetadata, image: { dataUri: dataUri, @@ -194,16 +214,16 @@ export function SDJWTVCParser(args: { context: Context, httpClient: HttpClient } name: friendlyName, }, issuer: { - id: validatedParsedClaims.iss ?? "UnknownIssuer", - name: validatedParsedClaims.iss ?? "UnknownIssuer", - } + id: validatedParsedClaims.iss ?? 'UnknownIssuer', + name: validatedParsedClaims.iss ?? 'UnknownIssuer', + }, }, validityInfo: { - ...extractValidityInfo(validatedParsedClaims) + ...extractValidityInfo(validatedParsedClaims), }, - warnings: getSdJwtMetadataResult.warnings - } - } + warnings: getSdJwtMetadataResult.warnings, + }, + }; }, - } + }; } diff --git a/src/credential-verifiers/MsoMdocVerifier.test.ts b/src/credential-verifiers/MsoMdocVerifier.test.ts index 610922a..c49904f 100644 --- a/src/credential-verifiers/MsoMdocVerifier.test.ts +++ b/src/credential-verifiers/MsoMdocVerifier.test.ts @@ -1,10 +1,8 @@ - -import { assert, describe, it } from "vitest"; -import { Context } from "../interfaces"; -import { PublicKeyResolverEngine } from "../PublicKeyResolverEngine"; +import { assert, describe, it } from 'vitest'; +import { Context } from '../interfaces'; +import { PublicKeyResolverEngine } from '../PublicKeyResolverEngine'; import { MsoMdocVerifier } from './MsoMdocVerifier'; -import { CredentialVerificationError } from "../error"; - +import { CredentialVerificationError } from '../error'; const issuerSignedB64U = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; const deviceResponseB64U = `uQADZ3ZlcnNpb25jMS4waWRvY3VtZW50c4GjZ2RvY1R5cGV3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjFsaXNzdWVyU2lnbmVkuQACam5hbWVTcGFjZXOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xlNgYWFmkZnJhbmRvbVBUfNevz7XKI6dP-oHvsrT3aGRpZ2VzdElECGxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWFOkZnJhbmRvbVDNT3JVcJ3djTp0BXnyLmIUaGRpZ2VzdElEC2xlbGVtZW50VmFsdWVlRVJJS0FxZWxlbWVudElkZW50aWZpZXJqZ2l2ZW5fbmFtZdgYWFukZnJhbmRvbVC4MtquwSK-63MjkZpprLqoaGRpZ2VzdElEA2xlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYY6RmcmFuZG9tUCazWrq3doaoxVpE0gJe1N9oZGlnZXN0SUQNbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVA7fuNwF3jGD2QiLdenoWMraGRpZ2VzdElEB2xlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzEy2BhYT6RmcmFuZG9tUGa8DfRFMEUpd1o2JghMQjloZGlnZXN0SUQCbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTTYGFhPpGZyYW5kb21Qa80NMqpAB0i2OQyv8j95oWhkaWdlc3RJRAVsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNtgYWE-kZnJhbmRvbVDJ87WH7qvHclbu4PTPvBQdaGRpZ2VzdElEAGxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYT6RmcmFuZG9tUItNiaeMwO7ZzV1CVGW4LeBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMjHYGFhPpGZyYW5kb21Qby0pEbzynWHUOc-7B2pZBmhkaWdlc3RJRBNsZWxlbWVudFZhbHVl9HFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl82NdgYWFGkZnJhbmRvbVDgYlXKzSHnPThOI0y4m673aGRpZ2VzdElEEGxlbGVtZW50VmFsdWUYPHFlbGVtZW50SWRlbnRpZmllcmxhZ2VfaW5feWVhcnPYGFhUpGZyYW5kb21Q4Jd6ZP3DCJFFKVNFVT_B12hkaWdlc3RJRBVsZWxlbWVudFZhbHVlGQescWVsZW1lbnRJZGVudGlmaWVybmFnZV9iaXJ0aF95ZWFy2BhYVaRmcmFuZG9tUH43o3ghMrqucI7lWk9d7mFoZGlnZXN0SUQKbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhRpGZyYW5kb21Q8AOaLrOmDhM7rECRheuQhGhkaWdlc3RJRARsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVya25hdGlvbmFsaXR52BhYVqRmcmFuZG9tUL07clF9bBQDbpWfQQQBOrloZGlnZXN0SUQRbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYXaRmcmFuZG9tUOokZC6YfIpG84ujLfw1PENoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZWU1MTE0N3FlbGVtZW50SWRlbnRpZmllcnRyZXNpZGVudF9wb3N0YWxfY29kZdgYWFakZnJhbmRvbVABAOyRDMIBZSH3cZUmepgUaGRpZ2VzdElEAWxlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWGOkZnJhbmRvbVCVlpl1xTgpFlqC0o_-yGMDaGRpZ2VzdElEBmxlbGVtZW50VmFsdWVwSEVJREVTVFJB4bqeRSAxN3FlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXTYGFhVpGZyYW5kb21QSEdXDVlME8NKeLaDyQPRe2hkaWdlc3RJRBRsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVyb2lzc3VpbmdfY291bnRyedgYWFekZnJhbmRvbVDo81fY-gsCPk-oDjwdxANQaGRpZ2VzdElEDmxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJxaXNzdWluZ19hdXRob3JpdHlqaXNzdWVyQXV0aIRDoQEmoRghglkCeDCCAnQwggIboAMCAQICAQIwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA4MTMxN1oXDTI1MDcwNTA4MTMxN1owbDELMAkGA1UEBhMCREUxHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMQowCAYDVQQLDAFJMTIwMAYDVQQDDClTUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDhQauGDCoOMOX04n7MrcAbiX_-xO1YUT14jZudkt6tREyIAXV8gyt5FcRsYHhz4ryz97rjL0uogxHO6jMZr3bijgZAwgY0wHQYDVR0OBBYEFIj4QpCxKw1zy1tvydFlXoIcsPpiMAwGA1UdEwEB_wQCMAAwDgYDVR0PAQH_BAQDAgeAMC0GA1UdEQQmMCSCImRlbW8ucGlkLWlzc3Vlci5idW5kZXNkcnVja2VyZWkuZGUwHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wCgYIKoZIzj0EAwIDRwAwRAIgG3-U85HEM4X1qCKMotVTe3fCPQbBSptTFpbkaYdm8hkCICmJHazX9sVz41Um41v1P-UubwBAuV8XDmp7rDga6AW1WQJ9MIICeTCCAiCgAwIBAgIUB5E9QVZtmUYcDtCjKB_H3VQv72gwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA2NDgwOVoXDTM0MDUyOTA2NDgwOVowgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYGzdwFDnc7-Kn5ibAvCOM8ke77VQxqfMcwZL8IaIA-WCROcCfmY_giH92qMru5p_kyOivE0RC_IbdMONvDoUyaNmMGQwHQYDVR0OBBYEFNRWGMCJOOgOWIQYyXZiv6u7xZC-MB8GA1UdIwQYMBaAFNRWGMCJOOgOWIQYyXZiv6u7xZC-MBIGA1UdEwEB_wQIMAYBAf8CAQAwDgYDVR0PAQH_BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIGEm7wkZKHt_atb4MdFnXW6yrnwMUT2u136gdtl10Y6hAiBuTFqvVYth1rbxzCP0xWZHmQK9kVyxn8GPfX27EIzzs1kEutgYWQS1p2ZzdGF0dXOha3N0YXR1c19saXN0omNpZHgYIWN1cml4Vmh0dHBzOi8vZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZS9zdGF0dXMvODg3OTNjMDMtMTZhZC00NDY4LWJlZjctYzA4M2QzOGFlMjE5Z2RvY1R5cGV3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjFndmVyc2lvbmMxLjBsdmFsaWRpdHlJbmZvo2ZzaWduZWTAdDIwMjUtMDItMThUMTQ6MTI6MDRaaXZhbGlkRnJvbcB0MjAyNS0wMi0xOFQxNDoxMjowNFpqdmFsaWRVbnRpbMB0MjAyNS0wMy0wNFQxNDoxMjowNFpsdmFsdWVEaWdlc3RzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMbYAWCBCO3oSpWqKZUR8lyQ_w7JqYKlLQz4hz9Vj0pRyWxkkmwFYIHkM735ONBZCYrTOXPW78F4tLznXnVmhvtEl0V9hSdlcAlgg47Cmu7FcRsMQ5qmbTC8dZW39fP8P1Dz0v8pFZ5JLG2wDWCBfBxXYBfUld-d0cYhpP8zgjOtQtX5-dGlf30kCkGpE4ARYIApqGHJxhN18g4mAyGKfvWIfEkg8paVprLpDo1gZOHQFBVggT536a7N4Wy3Hpsit4uRpiBm2JPYiSxuFAhFqQFBX6DIGWCCgVM61tLjIFCEQgNvLx2zuGq3izK3BKfqrqhcRB9qEpAdYIL6p-Yxa3V_j_ztyXQatBHX0mGr0wkowXwyRx4UqqOYiCFgg32f-7ipkR7iEGy0xK57Myii2_P1hRbpK1UQdcELxirkJWCDf93y4i81oMjodX5dcq_lQrEj_Oh_5apigwx6gDfD4jQpYIKD9I4Auzw3scWTiurhgTs5uMvZ20fJ_8k_wfV_D0dAhC1ggckHBrpbStfINvp4LGYyaeooDX4oY8iAtW-0RarG-q4YMWCDm89lYD1h55M4Qa67atX7rsqYaG1THguFxDhv7npXqpA1YIFXi56b9YQFa7i3ONHPYIcEyupNd06a2izkd82CALOibDlggWYt7Vzu4i_lKmYH_A2gfYPMOqvrI3zLzjhxdv1ioZvAPWCBmcIZJdBAIT_D0j5YbN7oW9-Hkoj-3yir2g8BPdtuV1BBYINQGzf5w06WG8yCP1e58Ladx5ZaeVelcHuOCQDGHeYBxEVgg0CpnfXCEXuzuhs8-LQMxziloCZ4w-bvJZTWyOF__fNISWCDNCRYb8KgIO45jYCFO1k09lfdLT87dMYik-BEzoq-JchNYIFUrEIc3bvDFFFgD9KTmk9DKYlk5rWb7qECw4Seb_AzIFFggkz8mwKZwXj8R2kzx7qF3A3U3p4Q8uOl8zN_OHae7HlMVWCAU-2xJ2vH2_CF8mQjcAV4BaxBI04FBOHkZ83A1Sbl-e21kZXZpY2VLZXlJbmZvoWlkZXZpY2VLZXmkAQIgASFYIBJq0ZzR16oAkshkZKlDx3syKecGErSKEYib9mo8npduIlggjeYD3pZFCh-JwZgX7UI5azfRUQxb6iiI2cQ_TJ7_oFhvZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZYQAGN5M3cA06sECoJwmOV68x88ssWjOXuaXVd8ob1aE8FaXbk4_GRx3gp5faUL5eEr9A0Vfuec2402ywsXm6kuQBsZGV2aWNlU2lnbmVkuQACam5hbWVTcGFjZXPYGEO5AABqZGV2aWNlQXV0aLkAAW9kZXZpY2VTaWduYXR1cmWEQ6EBJqD2WEDHBZPJa5dEJlG4OC6wGUv4Y_3cn6vwF1wxEbV7VrjcH5Y6cPBY6o6TX-xDNSCoe1vUpib7Z9PBpTI7c7Q3mlLWZnN0YXR1cwA`; @@ -16,11 +14,11 @@ const credentialResponse = `ompuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZge */ const rootCert = `-----BEGIN CERTIFICATE----- MIICeTCCAiCgAwIBAgIUB5E9QVZtmUYcDtCjKB/H3VQv72gwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA2NDgwOVoXDTM0MDUyOTA2NDgwOVowgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYGzdwFDnc7+Kn5ibAvCOM8ke77VQxqfMcwZL8IaIA+WCROcCfmY/giH92qMru5p/kyOivE0RC/IbdMONvDoUyaNmMGQwHQYDVR0OBBYEFNRWGMCJOOgOWIQYyXZiv6u7xZC+MB8GA1UdIwQYMBaAFNRWGMCJOOgOWIQYyXZiv6u7xZC+MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIGEm7wkZKHt/atb4MdFnXW6yrnwMUT2u136gdtl10Y6hAiBuTFqvVYth1rbxzCP0xWZHmQK9kVyxn8GPfX27EIzzsw== ------END CERTIFICATE-----` +-----END CERTIFICATE-----`; const invalidRootCert = `-----BEGIN CERTIFICATE----- MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf/7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2/J0WVeghyw+mIwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr+ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW/U/5S5vAEC5XxcOanusOBroBbU= ------END CERTIFICATE-----` +-----END CERTIFICATE-----`; const issuerCaCertificate = `-----BEGIN CERTIFICATE----- MIICQDCCAeegAwIBAgIUVXDyMaOF1bP8wVigPfoxfe7OHVgwCgYIKoZIzj0EAwIw @@ -45,37 +43,42 @@ const context: Context = { trustedCertificates: [rootCert], }; -describe("The MsoMdocVerifier", () => { - +describe('The MsoMdocVerifier', () => { const resolverEngine = PublicKeyResolverEngine(); const verifier = MsoMdocVerifier({ context, pkResolverEngine: resolverEngine }); - it("should detected expired IssuerSigned", async () => { + it('should detected expired IssuerSigned', async () => { const result = await verifier.verify({ rawCredential: issuerSignedB64U, opts: {} }); - assert(result.success === false && result.error === CredentialVerificationError.ExpiredCredential); + assert( + result.success === false && result.error === CredentialVerificationError.ExpiredCredential, + ); }); - it("should detect expired DeviceResponse", async () => { + it('should detect expired DeviceResponse', async () => { const result = await verifier.verify({ - rawCredential: deviceResponseB64U, opts: { - responseUri: "http://wallet-enterprise-acme-verifier:8005/verification/direct_post", - expectedAudience: "wallet-enterprise-acme-verifier", - holderNonce: "da1c17aa7d902ef8", - expectedNonce: "6bc90bea-1e01-49a3-a4de-5d98ddb64850" - } + rawCredential: deviceResponseB64U, + opts: { + responseUri: 'http://wallet-enterprise-acme-verifier:8005/verification/direct_post', + expectedAudience: 'wallet-enterprise-acme-verifier', + holderNonce: 'da1c17aa7d902ef8', + expectedNonce: '6bc90bea-1e01-49a3-a4de-5d98ddb64850', + }, }); - assert(result.success === false && result.error === CredentialVerificationError.ExpiredCredential); + assert( + result.success === false && result.error === CredentialVerificationError.ExpiredCredential, + ); }); - it("should produce error because the wrong root certificate was used", async () => { + it('should produce error because the wrong root certificate was used', async () => { // load the invalid root certificate const verifier = MsoMdocVerifier({ context: { ...context, trustedCertificates: [invalidRootCert], - }, pkResolverEngine: resolverEngine + }, + pkResolverEngine: resolverEngine, }); const result = await verifier.verify({ rawCredential: deviceResponseB64U, opts: {} }); @@ -83,25 +86,26 @@ describe("The MsoMdocVerifier", () => { assert(result.error === CredentialVerificationError.NotTrustedIssuer); }); - - it("should pass validation with device response that includes an IssuerSigned object signed by wallet-enterprise", async () => { + it('should pass validation with device response that includes an IssuerSigned object signed by wallet-enterprise', async () => { // load the invalid root certificate const verifier = MsoMdocVerifier({ context: { ...context, trustedCertificates: [issuerCaCertificate], - }, pkResolverEngine: resolverEngine + }, + pkResolverEngine: resolverEngine, }); const result = await verifier.verify({ - rawCredential: credentialResponse, opts: { - expectedAudience: "wallet-enterprise-acme-verifier", - responseUri: "http://wallet-enterprise-acme-verifier:8005/verification/direct_post", - expectedNonce: "7c4aea8b-d236-4253-8003-2cd172f0bb0d", - holderNonce: "bf9aada85729a0ef", - } + rawCredential: credentialResponse, + opts: { + expectedAudience: 'wallet-enterprise-acme-verifier', + responseUri: 'http://wallet-enterprise-acme-verifier:8005/verification/direct_post', + expectedNonce: '7c4aea8b-d236-4253-8003-2cd172f0bb0d', + holderNonce: 'bf9aada85729a0ef', + }, }); assert(result.success === true); }); -}) +}); diff --git a/src/credential-verifiers/MsoMdocVerifier.ts b/src/credential-verifiers/MsoMdocVerifier.ts index 1c45dce..bb46dd2 100644 --- a/src/credential-verifiers/MsoMdocVerifier.ts +++ b/src/credential-verifiers/MsoMdocVerifier.ts @@ -1,25 +1,28 @@ -import { exportJWK, importX509, type JWK } from "jose"; -import * as x509 from "@peculiar/x509"; -import { CredentialVerificationError } from "../error"; -import { Context, CredentialVerifier, PublicKeyResolverEngineI } from "../interfaces"; -import { fromBase64Url } from "../utils/util"; -import { DeviceResponse, IssuerSigned, Verifier, type MdocContext, CoseKey } from "@owf/mdoc"; -import { buildOpenId4VpSessionTranscriptBytes } from "../protocols/openid4vp/sessionTranscript"; +import { exportJWK, importX509, type JWK } from 'jose'; +import * as x509 from '@peculiar/x509'; +import { CredentialVerificationError } from '../error'; +import { Context, CredentialVerifier, PublicKeyResolverEngineI } from '../interfaces'; +import { fromBase64Url } from '../utils/util'; +import { DeviceResponse, IssuerSigned, Verifier, type MdocContext, CoseKey } from '@owf/mdoc'; +import { buildOpenId4VpSessionTranscriptBytes } from '../protocols/openid4vp/sessionTranscript'; import { p256, p384 } from '@noble/curves/nist.js'; import { ed25519 } from '@noble/curves/ed25519.js'; -export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: PublicKeyResolverEngineI }): CredentialVerifier { - let errors: { error: CredentialVerificationError, message: string }[] = []; +export function MsoMdocVerifier(args: { + context: Context; + pkResolverEngine: PublicKeyResolverEngineI; +}): CredentialVerifier { + let errors: { error: CredentialVerificationError; message: string }[] = []; const logError = (error: CredentialVerificationError, message: string): void => { errors.push({ error, message }); - } + }; const isCertificateChainError = (err: Error): boolean => { const fingerprint = `${err.name} ${err.message}`.toLowerCase(); - return fingerprint.includes("certificate") && fingerprint.includes("chain"); + return fingerprint.includes('certificate') && fingerprint.includes('chain'); }; const decodeBase64ToBytes = (value: string): Uint8Array => { - if (typeof atob === "function") { + if (typeof atob === 'function') { const binary = atob(value); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { @@ -27,66 +30,69 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ } return bytes; } - if (typeof Buffer !== "undefined") { - return new Uint8Array(Buffer.from(value, "base64")); + if (typeof Buffer !== 'undefined') { + return new Uint8Array(Buffer.from(value, 'base64')); } - throw new Error("No base64 decoder available in this runtime"); + throw new Error('No base64 decoder available in this runtime'); }; const bytesToHex = (value: Uint8Array | ArrayBuffer): string => { const bytes = value instanceof Uint8Array ? value : new Uint8Array(value); - return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(""); + return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(''); }; const certificatePemRegex = /-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\s+/g; const decodeCertificateToDer = (crt: string): Uint8Array => { - const normalized = crt.includes("BEGIN CERTIFICATE") - ? crt.replace(certificatePemRegex, "") + const normalized = crt.includes('BEGIN CERTIFICATE') + ? crt.replace(certificatePemRegex, '') : crt.trim(); return decodeBase64ToBytes(normalized); }; - const trustedCertificates = (args.context.trustedCertificates ?? []).map( - (crt) => decodeCertificateToDer(crt) + const trustedCertificates = (args.context.trustedCertificates ?? []).map((crt) => + decodeCertificateToDer(crt), ); - const mdocContext: Pick = { + const mdocContext: Pick = { crypto: { digest: async ({ digestAlgorithm, bytes }) => - new Uint8Array(await args.context.subtle.digest(digestAlgorithm, bytes as Uint8Array)), + new Uint8Array( + await args.context.subtle.digest(digestAlgorithm, bytes as Uint8Array), + ), random: () => { - throw new Error("random is not used in verifier flow"); + throw new Error('random is not used in verifier flow'); }, calculateEphemeralMacKey: async () => { - throw new Error("calculateEphemeralMacKey is not used in verifier flow"); + throw new Error('calculateEphemeralMacKey is not used in verifier flow'); }, }, cose: { mac0: { sign: async () => { - throw new Error("mac0.sign is not used in verifier flow"); + throw new Error('mac0.sign is not used in verifier flow'); }, verify: async () => { - throw new Error("mac0.verify is not used in verifier flow"); + throw new Error('mac0.verify is not used in verifier flow'); }, }, sign1: { sign: async () => { - throw new Error("sign1.sign is not used in verifier flow"); + throw new Error('sign1.sign is not used in verifier flow'); }, verify: async ({ sign1, key }) => { switch (sign1.signatureAlgorithmName) { - case "ES256": + case 'ES256': return p256.verify(sign1.signature, sign1.toBeSigned, key.publicKey, { lowS: false }); - case "ES384": + case 'ES384': return p384.verify(sign1.signature, sign1.toBeSigned, key.publicKey, { lowS: false }); - case "EdDSA": + case 'EdDSA': return ed25519.verify(sign1.signature, sign1.toBeSigned, key.publicKey); default: - throw new Error(`Unsupported COSE signature algorithm: ${sign1.signatureAlgorithmName}`); + throw new Error( + `Unsupported COSE signature algorithm: ${sign1.signatureAlgorithmName}`, + ); } }, - }, }, x509: { @@ -100,15 +106,19 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ }, verifyCertificateChain: async ({ trustedCertificates, x5chain, now }) => { - if (!x5chain.length) throw new Error("Certificate chain is empty"); + if (!x5chain.length) throw new Error('Certificate chain is empty'); const leaf = new x509.X509Certificate(x5chain[0] as Uint8Array); const chainBuilder = new x509.X509ChainBuilder({ certificates: [ ...x5chain.map((c) => new x509.X509Certificate(c as Uint8Array)), - ...trustedCertificates.map((c) => new x509.X509Certificate(c as Uint8Array)), + ...trustedCertificates.map( + (c) => new x509.X509Certificate(c as Uint8Array), + ), ], }); - const chain = (await chainBuilder.build(leaf)).map((c) => new x509.X509Certificate(c.rawData)).reverse(); + const chain = (await chainBuilder.build(leaf)) + .map((c) => new x509.X509Certificate(c.rawData)) + .reverse(); for (let i = 0; i < chain.length; i++) { await chain[i]?.verify({ publicKey: chain[i - 1]?.publicKey, date: now ?? new Date() }); } @@ -116,7 +126,7 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ getCertificateData: async ({ certificate }) => { const cert = new x509.X509Certificate(certificate as Uint8Array); - const thumbprint = new Uint8Array(await args.context.subtle.digest("SHA-1", cert.rawData)); + const thumbprint = new Uint8Array(await args.context.subtle.digest('SHA-1', cert.rawData)); return { issuerName: cert.issuerName.toString(), subjectName: cert.subjectName.toString(), @@ -130,10 +140,15 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ }, }; - async function expirationCheck(issuerSigned: IssuerSigned): Promise { + async function expirationCheck( + issuerSigned: IssuerSigned, + ): Promise { const { validUntil } = issuerSigned.issuerAuth.mobileSecurityObject.validityInfo; - if (Math.floor(validUntil.getTime() / 1000) + args.context.clockTolerance < Math.floor(new Date().getTime() / 1000)) { - logError(CredentialVerificationError.ExpiredCredential, "Credential is expired"); + if ( + Math.floor(validUntil.getTime() / 1000) + args.context.clockTolerance < + Math.floor(new Date().getTime() / 1000) + ) { + logError(CredentialVerificationError.ExpiredCredential, 'Credential is expired'); return CredentialVerificationError.ExpiredCredential; } return null; @@ -142,18 +157,23 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ function extractHolderPublicKeyJwk(issuerSigned: IssuerSigned): JWK | null { const deviceKeyInfo = issuerSigned.issuerAuth.mobileSecurityObject.deviceKeyInfo; if (deviceKeyInfo == undefined) { - logError(CredentialVerificationError.MsoMdocMissingDeviceKeyInfo, "MsoMdocMissingDeviceKeyInfo"); + logError( + CredentialVerificationError.MsoMdocMissingDeviceKeyInfo, + 'MsoMdocMissingDeviceKeyInfo', + ); return null; } const holderPublicKeyJwk = deviceKeyInfo.deviceKey.jwk; - if (!("kty" in holderPublicKeyJwk)) { + if (!('kty' in holderPublicKeyJwk)) { return null; } return holderPublicKeyJwk as unknown as JWK; } - async function issuerSignedCheck(rawCredential: string): Promise<{ holderPublicKeyJwk: JWK | null }> { + async function issuerSignedCheck( + rawCredential: string, + ): Promise<{ holderPublicKeyJwk: JWK | null }> { try { const credentialBytes = fromBase64Url(rawCredential); const issuerSigned = IssuerSigned.decode(credentialBytes); @@ -171,45 +191,54 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ now: new Date(), skewSeconds: args.context.clockTolerance, }, - mdocContext + mdocContext, ); } catch { - logError(CredentialVerificationError.NotTrustedIssuer, "Issuer is not trusted"); + logError(CredentialVerificationError.NotTrustedIssuer, 'Issuer is not trusted'); return { holderPublicKeyJwk: null }; } } const holderPublicKeyJwk = extractHolderPublicKeyJwk(issuerSigned); return { holderPublicKeyJwk }; - } - catch (err) { + } catch (err) { if (err instanceof Error) { if (isCertificateChainError(err)) { - logError(CredentialVerificationError.InvalidCertificateChain, "Invalid Certificate chain: " + err.message); + logError( + CredentialVerificationError.InvalidCertificateChain, + 'Invalid Certificate chain: ' + err.message, + ); } else { - logError(CredentialVerificationError.InvalidFormat, "Invalid IssuerSigned format: " + err.message); + logError( + CredentialVerificationError.InvalidFormat, + 'Invalid IssuerSigned format: ' + err.message, + ); } } } return { holderPublicKeyJwk: null }; } - async function deviceResponseCheck(deviceResponse: DeviceResponse, opts: { - expectedNonce?: string; - expectedAudience?: string; - responseUri?: string; - verifierEncryptionJwk?: JWK; - handoverType?: "redirect" | "dc_api"; - dcApiOrigin?: string; - }): Promise<{ holderPublicKeyJwk: JWK | null }> { + async function deviceResponseCheck( + deviceResponse: DeviceResponse, + opts: { + expectedNonce?: string; + expectedAudience?: string; + responseUri?: string; + verifierEncryptionJwk?: JWK; + handoverType?: 'redirect' | 'dc_api'; + dcApiOrigin?: string; + }, + ): Promise<{ holderPublicKeyJwk: JWK | null }> { try { const [parsedDocument] = deviceResponse.documents ?? []; if (!parsedDocument?.deviceSigned) { return { holderPublicKeyJwk: null }; } - const isBoundPresentationCheck = - Boolean(opts.expectedAudience && opts.responseUri && opts.expectedNonce); + const isBoundPresentationCheck = Boolean( + opts.expectedAudience && opts.responseUri && opts.expectedNonce, + ); if (isBoundPresentationCheck) { const expiredResult = await expirationCheck(parsedDocument.issuerSigned); @@ -226,10 +255,10 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ now: new Date(), skewSeconds: args.context.clockTolerance, }, - mdocContext + mdocContext, ); } catch { - logError(CredentialVerificationError.NotTrustedIssuer, "Issuer is not trusted"); + logError(CredentialVerificationError.NotTrustedIssuer, 'Issuer is not trusted'); return { holderPublicKeyJwk: null }; } } @@ -244,18 +273,22 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ const holderPublicKeyJwk = extractHolderPublicKeyJwk(parsedDocument.issuerSigned); if (opts.expectedNonce) { - const handoverType = opts.handoverType ?? "redirect"; - const handoverClientIdInput = handoverType === "dc_api" ? opts.dcApiOrigin : opts.expectedAudience; - const handoverResponseUriInput = handoverType === "dc_api" ? "" : opts.responseUri; - if (!handoverClientIdInput || !handoverResponseUriInput && handoverType === "redirect") { - logError(CredentialVerificationError.MissingOpts, "Missing handover input for mdoc verification"); + const handoverType = opts.handoverType ?? 'redirect'; + const handoverClientIdInput = + handoverType === 'dc_api' ? opts.dcApiOrigin : opts.expectedAudience; + const handoverResponseUriInput = handoverType === 'dc_api' ? '' : opts.responseUri; + if (!handoverClientIdInput || (!handoverResponseUriInput && handoverType === 'redirect')) { + logError( + CredentialVerificationError.MissingOpts, + 'Missing handover input for mdoc verification', + ); return { holderPublicKeyJwk: null }; } const handoverClientId = handoverClientIdInput; - const handoverResponseUri = handoverType === "redirect" ? handoverResponseUriInput! : ""; + const handoverResponseUri = handoverType === 'redirect' ? handoverResponseUriInput! : ''; const expectedAudiences = [handoverClientId]; - if (handoverType === "redirect") { - const schemeSeparatorIndex = handoverClientId.indexOf(":"); + if (handoverType === 'redirect') { + const schemeSeparatorIndex = handoverClientId.indexOf(':'); if (schemeSeparatorIndex > 0 && schemeSeparatorIndex < handoverClientId.length - 1) { expectedAudiences.push(handoverClientId.slice(schemeSeparatorIndex + 1)); } @@ -263,14 +296,16 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ let verified = false; let lastError: unknown = null; - for (const expectedAudienceCandidate of (handoverType === "redirect" ? expectedAudiences : [handoverClientId])) { + for (const expectedAudienceCandidate of handoverType === 'redirect' + ? expectedAudiences + : [handoverClientId]) { try { const sessionTranscript = await buildOpenId4VpSessionTranscriptBytes({ subtle: args.context.subtle, handoverType, - clientId: handoverType === "redirect" ? expectedAudienceCandidate : undefined, - responseUri: handoverType === "redirect" ? handoverResponseUri : undefined, - dcApiOrigin: handoverType === "dc_api" ? handoverClientId : undefined, + clientId: handoverType === 'redirect' ? expectedAudienceCandidate : undefined, + responseUri: handoverType === 'redirect' ? handoverResponseUri : undefined, + dcApiOrigin: handoverType === 'dc_api' ? handoverClientId : undefined, nonce: opts.expectedNonce, verifierEncryptionJwk: opts.verifierEncryptionJwk, }); @@ -283,7 +318,7 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ now: new Date(), skewSeconds: args.context.clockTolerance, }, - mdocContext + mdocContext, ); verified = true; break; @@ -300,14 +335,12 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ } return { holderPublicKeyJwk }; - } - catch (err) { + } catch (err) { if (err instanceof Error) { if (isCertificateChainError(err)) { - logError(CredentialVerificationError.NotTrustedIssuer, "Issuer is not trusted"); + logError(CredentialVerificationError.NotTrustedIssuer, 'Issuer is not trusted'); return { holderPublicKeyJwk: null }; - } - else { + } else { logError(CredentialVerificationError.InvalidSignature, err.message); } } @@ -321,10 +354,9 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ return { success: false, error: CredentialVerificationError.InvalidDatatype, - } + }; } - try { const decodedCred = fromBase64Url(rawCredential); const parsedMDOC = DeviceResponse.decode(decodedCred); @@ -335,7 +367,7 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ success: true, value: { holderPublicKey: holderPublicKeyJwk, - } + }, }; } @@ -345,33 +377,31 @@ export function MsoMdocVerifier(args: { context: Context, pkResolverEngine: Publ error: errors[0]?.error ?? CredentialVerificationError.UnknownProblem, }; } - } - catch { + } catch { const { holderPublicKeyJwk } = await issuerSignedCheck(rawCredential); if (errors.length === 0 && holderPublicKeyJwk !== null) { return { success: true, value: { holderPublicKey: holderPublicKeyJwk, - } - } + }, + }; } if (errors.length > 0) { return { success: false, error: errors[0]?.error ?? CredentialVerificationError.UnknownProblem, - } + }; } } console.error(errors); - return { success: false, - error: CredentialVerificationError.UnknownProblem - } + error: CredentialVerificationError.UnknownProblem, + }; }, - } + }; } diff --git a/src/credential-verifiers/SDJWTVCVerifier.test.ts b/src/credential-verifiers/SDJWTVCVerifier.test.ts index 4d93219..c4db669 100644 --- a/src/credential-verifiers/SDJWTVCVerifier.test.ts +++ b/src/credential-verifiers/SDJWTVCVerifier.test.ts @@ -1,15 +1,15 @@ -import axios from "axios"; -import { assert, describe, it } from "vitest"; -import { Context } from "../interfaces"; -import { SDJWTVCVerifier } from "./SDJWTVCVerifier"; -import { PublicKeyResolverEngine } from "../PublicKeyResolverEngine"; -import { CredentialVerificationError } from "../error"; +import axios from 'axios'; +import { assert, describe, it } from 'vitest'; +import { Context } from '../interfaces'; +import { SDJWTVCVerifier } from './SDJWTVCVerifier'; +import { PublicKeyResolverEngine } from '../PublicKeyResolverEngine'; +import { CredentialVerificationError } from '../error'; const wrongFormatCredential = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; -const exampleCredential = `eyJ0eXAiOiJkYytzZC1qd3QiLCJ4NWMiOlsiTUlJQzVEQ0NBb21nQXdJQkFnSVVUNkpsZTdjK0ljUjBCUjhDSWE0WXNNeGhERDB3Q2dZSUtvWkl6ajBFQXdJd1B6RUxNQWtHQTFVRUJoTUNSVlV4RlRBVEJnTlZCQW9NREhkM1YyRnNiR1YwTG05eVp6RVpNQmNHQTFVRUF3d1FkM2RYWVd4c1pYUWdVbTl2ZENCRFFUQWVGdzB5TmpBek1EVXhOVEF6TURkYUZ3MHlOekF6TURVeE5UQXpNRGRhTUVNeEN6QUpCZ05WQkFZVEFrVlZNUlV3RXdZRFZRUUtEQXgzZDFkaGJHeGxkQzV2Y21jeEhUQWJCZ05WQkFNTUZHVjRZVzF3YkdVdWQzZDNZV3hzWlhRdWIzSm5NRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVMMTEvUG9WMXFTZlB5cldNNWR2VjFLVDVnK1ZGZk12S3FiVHNQRlZ2ZGExWEg4RzdWdXpyVjBYaEJjWld1d2I4bHYzN3BPVDB1WmxJT09kdnZwcWh3Nk9DQVYwd2dnRlpNQjBHQTFVZERnUVdCQlNTOWVObjFPZ1NPUEJKQXZJTklnSzlDUjJNZHpBZkJnTlZIU01FR0RBV2dCU1I2dEk0ZlY2ZG5OZ1FuUTdkc09WRVNhNW4yREFPQmdOVkhROEJBZjhFQkFNQ0I0QXdPZ1lEVlIwU0JETXdNWUVSYVc1bWIwQjNkM2RoYkd4bGRDNXZjbWVHSEdoMGRIQnpPaTh2WlhoaGJYQnNaUzUzZDNkaGJHeGxkQzV2Y21jd0VnWURWUjBsQkFzd0NRWUhLSUdNWFFVQkJqQU1CZ05WSFJNQkFmOEVBakFBTUV3R0ExVWRId1JGTUVNd1FhQS9vRDJHTzJoMGRIQnpPaTh2WlhoaGJYQnNaUzUzZDNkaGJHeGxkQzV2Y21jdmFXRmpZUzlqY213dmQzZDNZV3hzWlhSZmIzSm5YMmxoWTJFdVkzSnNNRnNHQTFVZEVRUlVNRktDRkdWNFlXMXdiR1V1ZDNkM1lXeHNaWFF1YjNKbmdodGxlR0Z0Y0d4bExXbHpjM1ZsY2k1M2QzZGhiR3hsZEM1dmNtZUNIV1Y0WVcxd2JHVXRkbVZ5YVdacFpYSXVkM2QzWVd4c1pYUXViM0puTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFDVlZCZzVCNXV5c0hSTEttMnVINDhWcDB1SHZCTy8zT2VKeHh2UkZQamhLZ0loQU04L3lxRFhpNGFnd212eW1yaDU2Sk9kSnp1QXl0QURwUlRoVndzdWw5QTkiXSwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiI0REpIS2NMa0dkYy0wazhZSDRfWFNjci1jTnFyVEI4MWhrbGRXRFdfaXc4IiwidmN0IjoidXJuOmV1ZGk6cGlkOjEiLCJkYXRlX29mX2V4cGlyeSI6IjIwMzUtMDQtMjEiLCJpc3N1aW5nX2F1dGhvcml0eSI6IlBJRDowMDAwMSIsImlzc3VpbmdfY291bnRyeSI6IkdSIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iOiJHUi1GIiwicGxhY2Vfb2ZfYmlydGgiOnsiX3NkIjpbIlFoMzliNkc0RDNsWDlnUUpCWUJoaGxVY3B5WWZnX2F6bVc5dTRSU04tTE0iLCJUdGUzZ1NVOUpYQmFBWnozcHU1RGxHNm5mRURxZmxLekxDeWcwOEtZelVRIiwia0NJVUQwOXZGaDFsaW9Sc0VxNzZGd2swOXgxYzZxbWxNdGlVbVIxRzVDTSJdfSwiYWRkcmVzcyI6eyJfc2QiOlsiME5wUmdPaTNxb0EtN18tNWtyZ2N4aXZBSFl3MWZuZFZ4bWxHUUt1U1c0NCIsIjVTVEYyV0ZsdGV4ZW1qRHktcm9aYi1JdHVzUGM1OXlOSTZ1Z0JvS0NLT2ciLCJELVViZGxTbF83TDhMb0dHN3lIMWh4NmdmUjJtTEVhejNTUGVIUnZUUFFvIiwiRFhia2dmY2hhZTNNMG9STHJxb21fVGhRUnhKai1mX1lEWElISkdBbVR5YyIsIk9SYjE2aVBWenJKTHU2UWJKd0VubFBpWW8yejBic2dnZkUwdmtGd1dCY1UiLCJpRDlkNWRMYVNOamI3X3BPNGJCLUxqQ2xuekNDWUdrbEYwT1ktZHNPOU1jIiwicG8wRmp6YnlFNHFkSnRkdDFJVV9CQks0RFk3WTBfRzFmTUptaEoxaUJROCJdfSwiYWdlX2VxdWFsX29yX292ZXIiOnsiX3NkIjpbIjExREVkYzl2Q2d2dTM2cS1oYnhBNmprTFZiNWN4UWFpT0VKc0lTS29paTQiLCIzaWM0NDNCQUlOLTFEbXRfV0R2My1JeWwtWGUwUi1zVXhOU2RyTDQ0NVRNIiwiWHVsOUVDVzVZNmd2UEc1cVN2a1NJRFdGM0FpQ1lXT1E1YlFxdXlIT2gzMCIsImpkZUxkNHFGUWxPSjgtZlBYVXpZVGlWOGsxMndDOG9OYi0zV2pWdVFfQUUiLCJrVVI0SGluWG5oRy1hM3FMN2YtR3BlSDJjUTRGVUFSNUVZZlZTNTVDSm1JIl19LCJ0cnVzdF9hbmNob3IiOiJodHRwczovL2V4YW1wbGUuY29tL3RydXN0X2FuY2hvciIsImNuZiI6eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJleHQiOnRydWUsImtleV9vcHMiOltdLCJrdHkiOiJFQyIsIngiOiJKZV81U1JtUC1MZTgxNGYwUjN1TEVoU2owanBLdXFlTW9hUGNNcENad0VNIiwieSI6IkZpY0ZZYnVIR1RaREhzcFUzb3pWSWRoRm53NWRQa1ZNYVBZYXlWMEUwSzAifX0sImlhdCI6MTc3MjgwNTkyOCwiZXhwIjoxODA0MzQxOTI4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwMDMvb3BlbmlkIiwiX3NkIjpbIjRVN0Fmc2JhNTFHYm52NzN3VXFWWm94c3VCOHRDYkN5N3hHZzJwalUzbkkiLCI2UEhwQjVwMndBcDlad2psaWJKUXplRGVVbXk2cjFqeHJpN253VHlkNDJFIiwiOFgtdTA1aGFmVXVpZjlpVDNtWnN3Q2hITEVpdVpMSzRzc0RXM05oaVNsNCIsIkJOWDVZSF83TVBrT3JUZ0d1d1JoZmtsUnVXbmMzUGp1ZXVjTTdIbkZISkUiLCJEYlhVeFVXSjZsOEg5R2UtSWRBZWl5YW9JNzJZWmo5aU0ybkV1NzFZVm1vIiwiX2l2czFkM2lFcDZIODFHUHFpWlVSbHFzd3EyYmNsNlNhaUhvaXo2ZzcxWSIsImJGYVBJQ1VsRUZod0dBS0cwSTRncUJfeWNoQ3NoM3pWWHk5YlJsMUxKbmMiLCJiSTdXeE0tNDJUR0VCNUM2RkhCVWwzdFBEZllfbTl6TkVZMk5yUFowZlJJIiwiYm9qQ3ZXcC1rcS1yR1pTbmtZdWR5QnhuR09VRzk2aUFsV2lTeXVPM3pZcyIsImQtbkVaWTM3ZUdURFZCOE5NRlhLRWJONGk2Sk5MUEc0X25LTjIxY0xGVW8iLCJnbll6N1MwcHl6dlNKRFpnQkZKcGJIWERlVkdqTDBaN1ZtU1RpVHhBSVdjIiwia3YxZFRnRUE5MWJJcGJmZGNTTUtvSFBmdmNmRFpnU3RnTDcyTjlRQnM1byIsImx0SDk2RVpnMFNZNkFmdkNGczRWbTlTNGtUTkVGdjRGajk4MnRwU1RIUzAiLCJuem5hdC1fREJNdWgxWnlNOHpVWEF3VmxqZURucUl5TVowYTJScDdXSXo0IiwidDB2Q1hubW14cjhxcXVCa3YxR08xQzFpOC0zc0sxTTYxYTBZTmFXaDlZZyJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.46oCMb1XM1rayoRmm6pkloU9f_ES3PBRfpQ2eMdRqIJElB5MQ0rTBFlyTnw8yTCjBa6nJW8p0vTZp63rMRQrEA~WyJ1TWF6c3hQamNGeGR4dERGYlI4T0xnIiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyJzM0t2aERrRWtZdUw3Y3F0OHVLa09nIiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJOMW1BUlJ2YVFINzRGOHctek5rbExnIiwiY291bnRyeSIsIlVTIl0~WyI2TkgxY0lDN1dYNHBDbnYyRHVTUGhBIiwiaG91c2VfbnVtYmVyIiwiMyJd~WyIwNDU1MkJyeFd4dHBidXE5SW9JLVBnIiwiZm9ybWF0dGVkIiwiUmFuZG9tIHN0ci4gMywgMzQ3OTMgTWFuaGF0dGFuLCBOZXcgWW9yaywgVVMiXQ~WyJVUV9DRnN4WnpJOTc2N0Ftb2lKcmxRIiwicG9zdGFsX2NvZGUiLCIzNDc5MyJd~WyJyZlBUVExuRURtcllOeVFaUDVROXVBIiwic3RyZWV0X2FkZHJlc3MiLCJSYW5kb20gc3RyLiJd~WyJndTlwUTJraDJyZjF5WEs5aFpLNkVBIiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJyTHdyS0ZPSXdua1A0bVc1LXk1aF93IiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyJLWUc5d3hqRHQwWVdzaXhsbzZFNXJ3IiwiY291bnRyeSIsIlVTIl0~WyJZT3h5MlNzNmZqQUFtb2dRVTdkLURnIiwiMTQiLHRydWVd~WyJKSUFvVVZBNjVFdGRwOE1tR3Z0ZDRnIiwiMTYiLHRydWVd~WyJWR1pZbThzZUxWZkpWVFU2bFBjYlBnIiwiMTgiLHRydWVd~WyJUaG83YW9CYlpjNTJncDFodHE1cnJRIiwiMjEiLHRydWVd~WyJjUXYxdVBSYUdPUHdHNkRscG05QW9BIiwiNjUiLGZhbHNlXQ~WyJCYjJQNTBUbFA5YVF3N2xpVXdfUk53IiwiZGF0ZV9vZl9pc3N1YW5jZSIsIiJd~WyJsRVZ3MDZKWjJWa2Zwand1aTVJTE93IiwiYWdlX2luX3llYXJzIiwzNV0~WyJsSGxhU1ZWZ3haVnpxVEMyZ0pMTVhnIiwiYWdlX2JpcnRoX3llYXIiLDE5OTBd~WyJSaE1CTm0wMjQwZlY1ckZoZW12QWh3IiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyIteG1DYWUxY0NTY2FrSkpxT0ZqN0hBIiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJEQkNIY2hKaW8yUWFPNi1SdnJQUkV3IiwiYmlydGhfZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJEU1FxeFpGOXNfRjNobXVvX1UzWWF3IiwiYmlydGhfZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJfbVRoTVkzMGxzQU5TTjB1eS1lU21BIiwic2V4IiwxXQ~WyJNMDlaWnJfQWRDSC16MTcyTGIzbWF3IiwiYmlydGhkYXRlIiwiMTk5MC0xMC0xNSJd~WyJlUGxvZHRmRkNhd1FIdE41dWVHa2t3IiwibmF0aW9uYWxpdGllcyIsWyJVUyIsIkdSIl1d~WyJVWGp0VlhGVXFmbzJMREFEUGMyQUt3IiwiZG9jdW1lbnRfbnVtYmVyIiwiMTIzMTMyMTMiXQ~WyJCZlVsLTN4UUVCTnc2c1ZKalNabllBIiwiZW1haWwiLCJqb2huQHNhbXBsZS5jb20iXQ~WyJaQV9jMHdxR0dPVVBDUTZUU2NmSkZRIiwicGhvbmVfbnVtYmVyIiwiKzMwODM4ODMzODM4MiJd~WyJVOEFIb0VNVUw1ZmhvNjc5bktsZHVRIiwicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIiwiMTIzNDU2Nzg5Il0~WyJBSllvb280RU42OFg0RXFZVHg2bjh3IiwicGljdHVyZSIsImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRRUFTQUJJQUFELzRnSFlTVU5EWDFCU1QwWkpURVVBQVFFQUFBSElBQUFBQUFRd0FBQnRiblJ5VWtkQ0lGaFpXaUFINEFBQkFBRUFBQUFBQUFCaFkzTndBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUUFBOXRZQUFRQUFBQURUTFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWxrWlhOakFBQUE4QUFBQUNSeVdGbGFBQUFCRkFBQUFCUm5XRmxhQUFBQktBQUFBQlJpV0ZsYUFBQUJQQUFBQUJSM2RIQjBBQUFCVUFBQUFCUnlWRkpEQUFBQlpBQUFBQ2huVkZKREFBQUJaQUFBQUNoaVZGSkRBQUFCWkFBQUFDaGpjSEowQUFBQmpBQUFBRHh0YkhWakFBQUFBQUFBQUFFQUFBQU1aVzVWVXdBQUFBZ0FBQUFjQUhNQVVnQkhBRUpZV1ZvZ0FBQUFBQUFBYjZJQUFEajFBQUFEa0ZoWldpQUFBQUFBQUFCaW1RQUF0NFVBQUJqYVdGbGFJQUFBQUFBQUFDU2dBQUFQaEFBQXRzOVlXVm9nQUFBQUFBQUE5dFlBQVFBQUFBRFRMWEJoY21FQUFBQUFBQVFBQUFBQ1ptWUFBUEtuQUFBTldRQUFFOUFBQUFwYkFBQUFBQUFBQUFCdGJIVmpBQUFBQUFBQUFBRUFBQUFNWlc1VlV3QUFBQ0FBQUFBY0FFY0Fid0J2QUdjQWJBQmxBQ0FBU1FCdUFHTUFMZ0FnQURJQU1BQXhBRGIvMndCREFCQUxEQTRNQ2hBT0RRNFNFUkFUR0NnYUdCWVdHREVqSlIwb09qTTlQRGt6T0RkQVNGeE9RRVJYUlRjNFVHMVJWMTlpWjJoblBrMXhlWEJrZUZ4bFoyUC8yd0JEQVJFU0VoZ1ZHQzhhR2k5alFqaENZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJQL3dBQVJDQU1BQWdBREFTSUFBaEVCQXhFQi84UUFHd0FCQUFJREFRRUFBQUFBQUFBQUFBQUFBQUlEQVFRRkJnZi94QUJBRUFBQ0FnRUNCQVFFQWdrREF3UUNBd0FBQVFJUkF3UWhFakZCWVFWUmNZRVRJaktSQnFFVUkwSlNzY0hSNGZBemN2RWtZb0lWRmpTU1ExTmp3dEwveEFBWUFRRUJBUUVCQUFBQUFBQUFBQUFBQUFBQUFnRURCUC9FQUNFUkFRRUJBUUFEQVFBREFRRUJBQUFBQUFBQkVRSURFakVoRXpKQklnUlIvOW9BREFNQkFBSVJBeEVBUHdEMG9BQXluUmdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ0tkY3c4a1Z6YVhxWmJna0NLbW1SYzFmb1pvc0JyNU5aaHgvWEpSOVRWeWVNYVhDMDU1SXlpLzJvTy95R2pwQTBJK0xhUEt2MWVveDdiN3lvdmhxOE9YNkp4bFhSTlArQTBYcDJaS2ZqUTZUWGZjc1dSWFRhVEdpUUk4UmxPeWhrRG9BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBZEFBSThTSzhtYU1FM0pxbDFzNGZpUDRneHdnNFlxYzcyNGR5ZGJKcnUvRmpCL016UzFmakdsMGpYeEo4MXNsMVBHNnJ4WFZhaHJqeXlhVjByNUdqUEpPY25MSk50dnozTWJqMU9mOFV3NDJzV0RpajV1WDlqbGF2eDdVNTIwNXJISG9vbkZsbGFlem9pNXRocmRucjg4K2ViSS9XVFlldXpTVlR6emZyS3puY2JIRjJBMjVhbS9xYjkyWStPYXZGMk0rOWU0WkxyWTQ5NzRiWVdkeGR4azE3bXZmY0JyZXc2N1Bqdmh5elhwSm5VMC80ajFNSWNHVnJKWEp2bWVkVHNseGRnUFk2ZjhBRkdPN3k0cEo4dmwzczZ1RHh2UTVWY2RSSHZleDg2VW15YXl2dkgxQStwUXp3bXJnMUwwWk5QOEFNK1k2Ylg2alRQOEFWWkpSWGtuc2R2U2ZpYk5qcmpVY2xld1pqMm9PUG92SGRKcVpLUEh3T1hKUy9nZFpaRkpYSGMzV1dZa0NNZXBJcGdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWczUnJhdlVSMHVGNUp5akZMek10d1R6YXJGZ2c1WkpLS1hjODVyL3hROGJsRFRZcVhTVW51dlk0L2kvalV0WmxmQWtvK2ZVNUhGeE1sVWpmMVhpZXAxVnJMbGs0dC9UZTMyTkw0akk4WEN1U3Z1UVNrK3FYcUdwT2JaRnl2cVkyODIvY2kzZlJJQ1RkR09Mc1JCdGpOWmJzd1RUc0dGbW9FdUxzWmF2cDlrUmFvM1dXWXp4ZGpOcnpJYmRBTWJxemdVM3MxNkVkNDdFU1NuWFFXRXVzcDMySDhRM0dmWWRER3BjVzNJeWxUdUpBTjBCc3h5N2JMZnFkRFErTjZ2U2ZLc2psSHBiNWYyT1RHVjdwbG5DcEt3UGUrRStNWTljMHFxYmpiajVQa3pyOFI4dHc1c21teUp3azR0TzAwZXE4Si9FNmRZOWI4dTZxYTVlNXN1TXMxNmxPekpWaHl3eVE0b1NVbDVwbHBzdXBBQWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWdNbUc2TWNSNS94eng2R2tVc1dubGVXdWE2R1c0MlRYUThUOFowK2doODF5bStVZVRQRCtLZUo1OWZsYzhyWFpMa2thbW96enlaWE9jbktUNXR2Y29ic2xzbUpOVzBpVFZjMlpTcnJ1UmxMY05KUzViRlpOTGExMU1mUytyZm13SW1VcjVDVXVXeVhvUjR1d0dURXVobjJiQUVVckpiK2dHL2tBMzcvWXkxWFZHRExqWFFDRFZHQ1VxMkkvZjNLbFRaakNWRXVaRnVqSXNKY0NjWmI3a0RMZDh4WVNyT0h1T0haN2tPTHlNL0U3RXFSNUZ1UEkyeUcxTkl4R1hDd0w1Zk5HMmpFWk9McXRnblVyZkprcFZHVFZXQjFmQnZGcDZEUHZjc2JWT05udXROcWNlb3hMSkJxbjVuekNNclZleDFQQ2ZGSjZQS281Y3pXSGsrdEJsbXZvUEYyTW5PMG12MCtxVVZoelJrMjExTitENGsyamRTa0FDZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTXVSbHVqei80aThXL1JzVHc0Vy9pU1hONzBaYmpaR3QrSS9GNFFUMDJCOFU5K0oxc2VUbGtjbStiN3RtY3pjbnhTazVOdnE5MzZtdjEzSlV5MVJPTWQrWlgxOUdTM2s2QW5rZEwxSWNKYzhjcEpVakh3bXVld2JaaXR1dWhrTlVZVHZvL1lNWTRlNWhxaWFpMlo0SDVNQ3I3a28ybmZJcytHekxqZm12WUt4RDcrNkk4UGN1aml2OW94d3BMekJpcmg3bUdxTHV2Sis1bjRMdmV2Y01rMVR3OXlPeGU0SmxrTk8rdHIxUmxyZld0UGdZNEdiNjB6UzJWZTVCNFZ1a2pOUFd0UGhRNGU1c3ZEdXdzZEt1WlJqWCtFekhDYml3eXJlMTJId1NkTWFpVk1TaXVIWm0zOEZ0YklybmdwV2JLZXRhL0Z0VkUrTGJseUl1RlBtWStscnF1cHFXSE9ueUxWUGFpdjZXMDl6TFY4Z3kzRzNvdFprMHpieHphVGF0TGEwZlEvRGRaSFY2U0UwNDNYSlBkZXg4eDR1RjNUcnVkand6eHZOb25DT1M4a0k3SytpOGdmWDBST3pKei9BQTN4SEJyOEh4TVVxODArYU43aUtsU2tBRFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFLTlMxREUzS1NSbHVEUzhXOFV4YVBDOTBwTmJkWDdIZzlUbmVmUFBMTGliYjV5NWx2aXV0bHE5Ukp0dDc3ZHR6UnlTcEx1U3RpYzNSWHhkakRkc0pXd0pKY2JOM0ZnNFlweTJ0Y2lXajB6bFVuSDBPbkRTMzlkT3lPcityNW40MHNXQ1R4cmIySVpNTlhYTHpPeDhIaWFnbSsvb2EyVERicnk2RTY3ZXJteHdLVGJlNkxscGJmeW82V0xSOFQrYmtiMzZQR0tXMjdNdlZqSnhIRFdoZGJYN2t2ME5kWXY3SGRocDQxeUpmbzhleUoxdnJIQldqYStsR1AwT2ZYK1ozWmFkT3FTL2dSZW5jdHR2Zit3MXM0bGNQOUVhNVY3c3JqcFhPMUg3MGQzOUUrSy9tZnlyb2xWbGtkSzhmSkpma05aNnh3NDZCcTkvNWtaNlZ4cTI5L003engwdW0vbXl1T2xiZHVMMzgwUGFucEhMeGFUYXVKK3hjOUh3TGVhZlpvNmkwa212NjdtZjBiaGZTMy8yOGhwNnh5RnBaUGtrdmN4TFJ6a3QydmJjN2Z3RXFyYWpNc05yYm1OUFY1eU9oYnR5ZCtoTmFLS1czWHNkNzlIY1ZTM0lMVHBjOXg3VTlJNDYwMUtvcDBTaHBrcmJpbGIycEhYK0F1aVM5RVplTklhcHlWcDBsYTJzcG5wTi9wT3c4ZFBseks1WXVRMWxtdlBadEpIeU5UTGdjV2VreVliWnJadElweGFTWHVkSjBqcm1QT3ROcjBJcDBkRFU2UndWcGJIUGFwbHl5dU5salBGczlqTVowUU1wcEYyYWlYSFQ4TTE4OUZxWXpqSnBYdnVmUU5EcUk2akJHVWVxdGR6NWVuVFo2WDhOZUwvRHlSMG1vYWNYOUVuMGZrUzJ6WHR3UWhLNEorWk1xWFVnQU5BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJISmtqalZ5YVhxZVovRTNpVlErRXIrWmMrUjZEVVM0SU9meXBKTzNMa2o1MzRycVphalVUbTVPVGJJVkduSjhXUzMxWlZtK3V2SXc1ZHVwbnY1aHJIRHR6TnpSNmI0bVZiV1VZWThjMG0wNzhqME9nMDN3NEoxMEp0eGZFMnJkTHBveGhGMXlOeU9MbVp4cTBYcFVjTGRlaFFzTkoxekl4MHpiM3BWMG8yaVNWR0NwWTBrVzhPeGtzQzVFZUh1U0FTc0NQQ1MrRXFKSlVaQWdvVjFNOEtMT0h1U0JiaW5nWGt2WWx3S0w5U3dCTnVvOFBjZ29wRnBocXczRmJTZk1pMVJZMVJnTlFJeTZFK0h1WWFvQ0RWRUdxTFdySUFWdFdSYW9zYW9pMVlUWmlocE5GY3NXNXNOV0dyREhQejZaU2p2YnZ6T0ZyZEs0dHRJOVJKVnVhR3J3S2NIMVowNXY2anViSG1KeDc4aUxWR3hxWU9FMm1xS1dyU084cnpXSUZ1R1ZTNGlycVpUcG0yYXlWNzc4TytKL3BXRlFuTDU0Skpwbm9FN1I4MThLMXN0SHJJWkxwV2svUStpYWZKeHdUMmRxOW5hTWx4dFhnQXBJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCR2JwYkVpbkxMaFM2OXVySm81ZjRnMUMwK2duSnJpazFzbnlYc2VCeXl1VGt6dWZpWFZ6eTZyaGswNHBiUlQyaWVleTgwWXNoOHo1YkltMWJva2x3WXVIcXpPQmNlVklEZThPMC96Y1ZuZnd3L1Zyb2FXZ3hiUk9ualZJNDlYOWVqaVpFNHg1N2t5TWVwWWxSeld5QVRTb0NLVmt5U1ZJeUZ5b0pXVE1wV1o0UUlrMHFDVkQzWDNBeUFsZlZleG5oWUNKbHF3bFJrTXFBTUdXN0RRdzFaa0FWZ3NJOFBjQ3ZoSXRXV0FDZ2kxUmExWkFDdHF5SlkxUmdJVnRXalZuSG51YlpUTlcwQndQRThDVnRLams5VDAydXhjVUd6em1XSEJrbzc4MzhlZnFmcWxxakhVbkpXeUxWSkhXVnlzeGJoZDJqMnY0WjFzNWFQNGNtM3d2elBEdytWK3A2TDhNYWxZOVRMSEs2a3VWa3FlNWp5SkZPTjNDK2hjVkxxQUFHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWFQaWVYNE9tbk5TU3BQWjlUZGFzNC9qeXJ3L05LS2NwTmM1YktLSnJaTmVGMW1TZVhQS1dTVGxKdmRzMTQvVnYwUk9YTXJTdUw5VEZKSjIzNkcxb28vT2F4dmFHUEc0cS83azlmRmMvWGMwa2VLSGticWRvMXRNdUdQb2JYUTQxMzVXeE1rWThpeVBVbFNRQUNva25aSkt6QllEUXdaTXhEV0VyQk1nM1lCS3lTVkVVNkpnWWJveEhxU0FHR3JJa3lDVmdsMENWZ3p3OXdNQnFpU1ZHSkFZYW9yTEc3SU4yQkNYUXJhcGx4RFlDbWZJd1Nhc05XR1ZYUGtVdFdpNlhRcmJ0QkxXMUVlT0ZIbmRkamNadG5wbXJSeFBFY1cvRXVuUTZjWDljL0o4Y2RxaU0rVExuRlUxNWJvcGwwOURzODlTeHUxNkhSOElrMXJNYVMrbVZuTnhxMGIvaDgzaTFlUEpIbW1nMTlHMDMrbW5UWFptd2FlamZGaWkxYWkralhMME53MklBQVVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFNU09KK0k1dVdnbmpoN3ZpcWp0VFYwZWEvRk9XTU5NOGNIeGNUM2Q4aWFxUEc5V09qQ1c3N0l4S1ZHTlpoOHo5RHBlSGMwem1ZK1oxL0RWZE1ucjRybjY3ZUxrWGxPRGt5K1hRNDE2VTRjaTFPeXBPa1RUb2tUU3NKV0RNZW9YSmlSTktpQUFzQ1ZrRXI2a3dKN2Z2TDdrRTBnQUpKMlk0akFUb0NlM21DQkpPd01nRVpBa3dsVzEyUmJveVlib0RMZGdyQUdXN0l0MFpJeTZBT0xia1k2QUFRYW9pM1JPWFFpQlZJcTZscytSRGg3aEN2STZSeWRhcnM2dVhrY3ZWSzdpVkU5T0pOVklxYTRYNk9qWXlLc2p2ektKL250WjZIbU9IWmJsMkIxSlB5SzF5WHFTeDhnUG8zaExUMGNaS1V0MHJUNkhRU281Zmc4cFBTUmpOL1BGYk5xblQzVy9WSFVpYkVNZ0FvQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUm1yUjViOFZ3VU1VYWZWdWtxM1BWU1BKL2lwTGdVdWFicE1tcWp5a2xTZmN3bGJaTXhCYk14cEZKTDFaMXRFcTRUbHJkcjFPcG9uYlM4aU8xOHgyc0RxRitaY1VZZVNYa1h0VWNiZGVoWWxiSnAwVjlTU2RtQzB6SHFSVHN5Qk1BeW5RVnFTVkdVckFTc0dpVmdtQTJYVUFUSUowQm5oN2lKbE8rbEdRVzRqRVNKR0c2QWlZYXNzSUFSNGU0NFNUVkFDc2czYkxaZENEVmdWeTZFU3dyQUdHNkhGMksrTHNCaHV3UmtSQ0ZlZFdjek85cjhqcVplUnpOVDFreW9ucHlOUzZtMitwVGxkWkdXNnI2MlZTZHY3ZnlQUTh5RWVUKzVPSzM5eUVPWlpqVis3UUh2L0FNT3R6OE14Y1ZmTHNtanJwVWM3d2VLV2toSkxoNDRweVM1WFhNNktkbXhESUFLQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRVpWVzU1ajhVcHJBcFUzY3F0K2g2aHF6aS9pZUNsNFpOOHFkazFVZUM0dm41Q0t0aHFtZ25VbVkxTEQ5ZG5aMEN0S3poNDN4VG85QjRaRFpiRWRyNCt1cENPMTJXTjJRaXJSSk96ZzlES2RNbVFKSlVCWVdGWlpDU29DWUlMSmZJT2RkQzVCY25aTktpbU11ZXhZblJvbUdxTU4wWlRvQUFETE5BQXpIcVRKcmJkWUNWaE9pVGRHNHhGcWdTYm9qS1Jvakl3Sk8yWWxMa0pNQnVtVnAyRzZLbTJ6S015NkVURGROQ2ZJbFdvU1ZvcjR1eGFVdFVEUnV6QkJ1ek1lb1NaRmFPVnJGVGE4enJtanI0WEJ0RlJQVHorZmVWZVJYUGt2VmZ3Slo5NU4rWlZkcCtwNkhtUS9hOXk3R3JLbTl2Umwyblc2WGNENlA0VlQwR0orY1V6Zk5MdzJEaHBZY1NwOEt0RzZiRUFBS0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdHNk9QK0k4Zkg0WmtTZFh6N25ZYXMwL0VzVHphV2VPSDFTVkltajVySlcyUjZ2dWkzS3YxODE2N2xjbFJpMk1LdktqMVdoaldGSG1kTEg5Y2xmVTlYcDQ4T0pFZHVuRTFmSHFSNkVvOVREVk5IQjNTU29tM1NLNXpVSTI3OWpTemFodExvL0t5NUdXNDJYbjN1MGw5eWF6eFR1K2h5WG5TVnAzdVUvcFVycTFmWm15YW05dTlEVVJ2Nmt2VWZwRVpiV2VlbHJIeE9LcE5mdk5GWDZibC9lZDlqY1RldGVvaHFJUlhNc3g2aE5PLzRua3YwN0xmMU4rNW1PdXlwcXBQL3dBbU1KM0k5Z3Mxa3ZqcStSNXZUYTJUdE5PdXFONkdvZlc2UkZkSmRkaUdmelg1bHZFam14elZ2M05uSGtUUktwTlg4VlB6c3NOZE8yV2hWbXBOMFk0aUhGMklOMGdTWXNsUGxaVkxLa3R1aFJreVhkR3JQS255ZC84QUFMY2RENHFwZGl1ZWRWdHVjek5xVWx0S242MGMvTHJYZHVUKzVjam5ianR5MU8vTDh6RDFhdW1xcnVlYnlhMmJmeXpmc1EvUzVYVnQrckt4enZXdlNTMUthMnAraktwNnVMbzRVTlhKOWVaWjhkdGI3b1l6WFlqcXViaTA2NTB6WWpsak5iSEN4NWs1cE50ZDdOekZuNFg1bVdLNTZiOHVnanlLOGVvV1R5MjgyVDYwUlpqb21VYW5IeDRwYmx6ZEdKOHZZd2VWMVMrZCtwcXI2Wkx1YnZpQ3JQa1htYUhuM1BSejhlYnI2ZEtOdlJxOHNMZGZNa2E2VmZZNlhnT0JaL0VNU2JyaGttL1FwTDZGcDFlR1BvWEZPSi9KWGtYR3hBQUNnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaVJUcUk4V0RJbFNiaTFiOUM2UlhPUEhGcnpKbytiZUlhZjRHcmxIbFROYWYwSitmOUR2L2kzRDhQVlk1cGJTWFB6T0N2b1M4a1l0Wm92L0FKRVY1czlYaVZZMGVZOE1qeDZ1Q1BVUlZJNGQvWGJ4VEluRG1ZeU9sZTRUb296TnRWdHQxWk1qcGJqVDFPYTVYL00xSnljdWFsdis3elp2UnhxMjluN0lzV0tMZm4vdWQveUxjM0xsanl5WHlSNFYwNmxUMG1wbmRxZjhqMEN4cXRsUmFvcHIwNmk5WTMrT1BNVDBPZXJwL3dBU3A2Yk9tN2cvc2VydUhXbDdGR1hOZ3F1T1A4UjcwL2pqekt4WkZ6NHZjeW9QdXZWSFl5NWNENUp2MGl6VWVUQmZQaDlWUnVzdk9LOERmRmJTMjhqb3dkd2l1NXE0MUdXOFduNkYwT1JscS9Wc3diZTNUYitORy9wOG0xcGN6blEyTnpDYzNTTjdHN1pjVlE1bGlkOUtKNWFqT1czSXJsTFlzeU9rYW1YSjJOdHdWWnBmYzBYa3B1L1JGbWFSclRkMGF5cXRWUGlmRFRYZmthT1NNcFBaTTNtcVpWOFhFblNibS84QXRWblNWeDY1YUx4VGZRbEhTNXBPbEIvWTZXTE5qLzhBMFQvK2h2NHRWaVMrYU1vLytEUnZ0VDBqaC84QXArZEsrdmtpYTBlYUszVDM5enY0OCtueS9UT0w3V1dwUWZMWXozcC9ISG5QZ1pzZE9XS1hEMWJwRW9wcDdjU3IvdVIzWlFpM2Jvb25wNGMrR0s5bUoxclp3MGNXWGZobGE5VG9ZWGNMTlhKaVNkSjgraFpwbTQvSkpQZmRVR050T2czWTZHRG02UFArSy84QXlaSE1xMi9VNjNqTUt5OFYzc2N4OVdlam40ODNYMHJmMVI2ZjhJWVZLV1RJNCtYTkhtVzdtZTcvQUF4cHZoK0hSYjJjbmYzS1M3S1ZLa1dFVTZSSXRBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBWWFzeUFJVCtTTGI2SE56NjdOeFhpcU1PbHJtYlBpR1Q0ZWxiODJjeU9TR1dDbENTa3V4eDc2c3IwZUxpZFM2MFBIc3NkZG8wNXI0ZWJFbTFUMmtuNUhsM0hkYjlIL0FKK1o3UFU2YU9YRkpQeVBMNjdUL0F5OEN1bHVqT2VyYWVUaWMvc1crRDQrUFUzZkk5RWxaeFBBMG01Ti93Q2JuZFRzanY2cngvRVdxWFBtYTg0cTlxL2diTStXMWU1Qnc5SDZJaDBVbVU2SlRUU1JvNmpMUDZNZk45UUxwNnlLbndZMXh6N01weWFpVmZyY3FoL3Q1L2RtcG15clRZNmd0L3paSFM2RE5yUG4xRXBKU2YwblNSRjdxeWZpZWtqK3c1di9BTG5aaVBqYXFUaGhpNHBidXVScStKNlJZTlY4T01WR0tpdVJTLzBMSDRmaytKeHZXUElsQ25VWXgydHYzczZlc2NyM2NiNjhaeHVWVHdwTGsxUmZETHA5VWxTU09abWpwbjhONlNFNHhlTk9jWk82bjFwK1I2SHd6dzNGcS9Dc2JtcW1yNFpybWw2bVhtYWM5V3h6NWFCUWZGamRlaE9MbGpmelJiN28ySlk4dW0rWEo4MFYxUmRpaEhKaWRxMUpjMTBPZHVQUlBpbUh6TFkyOERvMWNNZmh6NGZNMm9jeUd5NDZHSXY2R3RpbDJMazZKNVZacUdXUGMwTXB1WlpiY2pReTh4OUdya1ZtcTNicEpzMjgwYmhTNms4ZW5yRnMwNVM3Y2tkSWh6bGhsbWRkUEx6TFhERHA0Zk8wcTZJc25jNVBIaHR2elJ2YWZ3ZUtqOFhOTDRra3JTYTJUS2sycDY2bk1jYVhpMkRFK0dPUDNEOGF5UWJTd3UxdjlMNWVaek02YmM1dnJKdjh6YXlZZkN2L0FGRFU0c1diTzlPc1VuaXlOcTVUU3RYdHliMk9zNGxjZXU3R3l2R05QbWFXWEJGeTg2TDhlU0VsZURPNDltN1RPTmh4L3JjU3BPNUxablIxZmhQd244VFRTY1crYVpsNTA5NjNWclhpbHc1MGt1azQ4amFXUlNWeDNPRGgxVTR5ZUhQRlB0UnVhVnZEUFp1VUg5UFk1Mk84dXVrbGZYOGtRVUtlMWV5TGNhdEV1RHlPYldJUjI4dllkU1NWY3lMZG9EaytOUitXTXZ5T00xYS96c2QzeGRYcC9jNFNWSnIwTy9IeDUrL3F6UllKYWpWUXh3VnR2a2ZRY1dYQm9NTU1MbGMwdm9qelg5RHluaEdqellzNnpSbHd1blZIY3g0VkZkZCtyM2JIWGVYOFZ6NHZhZnJxNmZYNHNzdUZweGsrU2ZVMjA3T0RrbEhHazVTU3ZsWjJOTE41dFBDVW52VzV2SGQ2cWZKNDV6K3J3QWRYRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTGRnYW5pTWVMU3YxUjUzRGVuMUUydVRlNlBSNi93RCtOLzVJNEUxV1daNWUvcjIvK2Y4QXEzb1M0NEpubmZ4REhoMUdOcnF1WHVkeU40L2M0MzRpZno0NWRtdnpIRnluaytKZUJxc1RrZFpLem1lRUt0TjduVWh5TTYrczQrSkpXeXhLekVJN0tOOGkzaDI1a3FhbVNGWHZaejgwTFRTNm5VeXczMktuaVZNRGlyUnFVcmt1UDFPam8xOE4wNVhYY2s4ZEVvZVIwblZMSllyOFcwSDZiaVdUQkw5YkJjbnRaNTZlSnhiamxoS00xMGFQU09Na3FWMzZzS2JiK2xjcjMzTjF6OUhIMFBoMlRWWkkwbkRIMW0xUjZwWjhHazAwY1dKY1NpcTJab1BKSzNzbjcyUjR1MjQ5cXFlT0o1dFRITXFuaGsvSjN5STRGUDQ3a3NiaEIvVmI1bHVOYjh2dWkzSms0WU5vbTdWVEkxOGtGeG93TjZ0aUt0b2hzamJ3dWtYZmYzS01YSXRqMXNLUXlPelR5OHpieUswYWt0MzZBVnRjVTBpZXFjM2lyR25YV256UkJ1dHpaeFpWSmNsN2hNdU5iQm14Nlp0ZkNhVXUxTyt4MGRQcnNVMGxKT04rWnJaMm4wTmYxaXZkYkhTV3hQck9xNS9qSGhydzZpV2JCOCtHZSt5NUhKK0ZqdTBqMURsMlgyRW1rMDNqaTIveU45a2VqbStFZUhQSm1XcHpwd2hENlU5bTJkWFU1SXVGS3ZWa1BpT1N0UmUzbHYwSEQ1ald6aWE1bWJTeHR1bnYrOGkzQmpVVWtianhwTWxERnNjN2RXWW9vMjBxS1lSM05tS3BHTlY4TzNNcHlKSkd4S1BMY3FueURITDhTVjZabkUwOE9QVlk0K2MwZC94SlhwcEhGMGpyWFluNVNzNjgvSEhyK3oxdUZLT05VdVpIUG5XT2tsYmZKRVhrYVJCd3VTY21jbm81YWs0U3k2bU01eWJ2cDBSNmZ3OWY5TlhrMmpqdUNqbGgzT3hvbmVKLzdqcDQ3bFIvNkp2TGJBQjZYaUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF4OVFib3cxUUdwNGgvb1JYbkwrVE9LbGVkcnVkeldxOEYrVE9QampXb216emVTWlh0OEYvNFNuRzQxZk00M2pzZjFlUC9jMStSMmNqcEhHOFRjOGlhbDA1RTgvVzl6WXY4SFZhUkx1em9ST2Y0US8xSEQ1SFNIWDFQSHhZblJOT2lwT3l3bDFDSFZsaVRhM0l0V0UyWXFlTk5ENFhreTF4YlFTcEJpbjRUOHg4Sitac3g2bVdyQTBKWW50dTM2bVVramFuanVqQ3hwTzJYSml0VmNQRHpJU3VUM05od3Q4eXJJN1JOdW1LMnJhTTlUTVZlNWd4cll4dXliVklxeHVrV0JVUXk4a2FrbGJOckk3TmJyWVpickRWaEpyZEZnakhudUdXYXlsYXNod2I3R3hGVnVnNHBsYXo0MTQ0dWU3WG9TK0cvUDhBSTJJeDRic21rbVpaaHJWVUd1VE1mRGZtYkx4MTBIQ3pFdFRnZDhpNkVkK1pZMVFUb0RIVW4wTU4wSmRETGNYSXhQa2E3NmxybHM5aXVUdG1zclQxcXZUeVhZNGVpVjY3R3U1MzlUL3BTOURoYVJjR3BXUi9zZFBNNno1WEh2OEF0SHBkK0lzeUw1RzF0UnI2WE44U0NYRFZtNGxjV2NuZVRHc244VFVRUjJ0Ri9wUy8zZnlPTHBvVm5iczd1a1ZhZUxmN1RiT25qbTF6L3dEUmYrWTJRQWVsNGdBQUFBQUFBQUFBQUFBQUFBQUFERFZtUUJqaTdFU1VpSUVNeXZETkx5T1BHUDZ4dStaMnppM3d5a24wZEhIeVI2ZkJmeXE5UkxzYzNOaVdYNVdibVoyeU9ISGJkK1J3ZWkvR2o0VkZ4NDR0VjUrcTVuVGJzNVhoK1g5ZFB2S1g4VHFGZGZYTG40c1RwbHFkbEVPWmVuWkxyTHF4S2pJWE5Fd0lHRTdMQjFBR0cydWFNZ014QWp3c253OXpMZElOa3hYTGthK1JXMGJMVm8xNTh3SzV1cUlRNW1KTzJXWTFUQXV4Y2lmUXdsU0p5NkFVVDhqWDZtemtkN212dzdjd0pwMGlmVTEwN0xjYnZjRFlqem9ud2VaQ0N0bHFkTUFFNk04UGNsN0wzRE1RU3NiazlpUEQzRFpNUTRSdExsc1Q0ZTQ0ZTRGYlZGY3ZsZnFXejVsV1hrZ0lOMlU4WFlzbDBLZ3lxdFQvQUtNam42UFQ4ZUtXUmMrSm8zTmJOTEM3WE14NEVsTFRUaS8zcis1ZWZqbDdmOUx0SkhnYVIwa3VKR21sVWpkd2NtUTdLTVVibTRycTZPNUZjTVVsMFZITDBHUDRtdHY5M2Y4QXo3blZPL2puNDh2bTYyNGtsUmtHRXFPemd5QUFBQUFBQUFBQUFBQUFBQUFBQkZxaVJBRExWR0FBQnljOGYxdVRmOXRuV09YcW8vOEFWWkZmTm5MeS9IYncvWE9uTGZrWFFmREJ5OGtWWjhNbEszMU1hZk91UGdsdGUxbkNYSHF2eHg4VWxEVnlsemp4T3Z1ZG1QTTVPcGh3NnZKVWxUZHF2dWRiRytLS2ZtVzRjM0ZrUzdHNzNLazdaT0twSE4ybFhSNms0OVNycVdKMEZKTjBHck1SNmtnTXAwWUFBakhxU0hRdzFZR09IdWFtbytVMnNqcEdsa2ZIT3dLOFVWS1RzdjZqR3VIa1c0MG05d1M2ekNOK2Z1aXhxa1c0TWFaYjhOVlY4eXNMY2MvSXJacS9TL1U2ZWZFb3BicCtocXpqME1zd2FVbFRMTU11d3l4MjVrWXJoa241R0RkeHEyV2tNYnRFK29FektkRmFkSW1BTXQyaUtkdXVRYXNDTW0zc2hLWExZRVo4Z0l5bHkyZnNWVDVGc3ZsWG1VdDJ3VzRxeUttUUpUNWtKQkRTMTgwb3RWME5ud0pWcHBjdDMwOVdhSGlFdUtXMzdKME5GTll2RDhiNU5yK2JPbitPY205TnFmMXQrWmZoZHBtakNYSHlOeVB5UWJaemQyNzRYR3A1cDkxRTZIRjJOTHd4VnBiZldUWnVKMGVyajQ4UGsvdFVnWVRzeVdnQUFBQUFBQUFBQUdKQktoeGRoRURJTUoyWkF3M1Fib3lZYm9ERGRtRExkbUFBQUFITThRaTNudmxhT21hbmlPUGl4S2E1d2QrM1VudWJIVHhXVHB5M2tsTDVKZGVwcVp0TyttNXR0TnRkaXpwUjVIdHRlZjFQRittT2NselNPbHAzY0lwOURVOFFTZXFpdDkxMTlpM1NPb3liYTh6cC9qejM2MzQ4aXpHclRLTUR1RnMyVTZWbk4xWlR0c3NUc3JTcGxpZGdaYnNrbmFJODAweVVWU0MyUU9vQUFGY200Z1U1Y3ZrVXhkYkVzanV6VWViaFZvRGJicmtSeFpHc3FPSnF2RTlRcDhPSmN1cklhVHhiTThxV2JFbkh6UzVGZXRacjFrTXFvc2psNW5MeDZtTGduWlA0NWt1TmJlZkpmTTBYa2JrVjZuVkxnZDdIR2w0dzRUYWpoYlhtMkpMVSswZDdvUjRkbnVjM1MrS2ZIZFNnNHZ1emVlYTBoWmlwZGJXbm50VGZJMmtyT2ZnZFNUT2hqZG93U2FzeHc3OHpMYlhTeUxkZ1RCR1BVdzNhQU4yaURkbVpkQ0NWTmhsSjh5bkwvRXRidDE1RmJkaEtpU3B2MUtwOGk2ZlUxTTg2ZzFSc21zdHh6dGZQaW1rdTUwTk91UEhEWjdSUzNPZm4rZlBGUGs1Sy93QWp2NG94V05VdlVycVl6eGZVc0tTVGJFNWZFZmtrUm1yb3YwOFBpNVk0K2pkdjBSTW0xMHRrbTExZExEZzAyTmRyTFFEMXlaSGd0MnBmVVpNY1hZeWF3QUFBQUFBQUFBQUVVNk1BQVNpSW1HN01BWlRvd1pTc3dBQUFBQUFERFZxbVpBSEZTdEdPaGJxOFR3Wkc2Znc1TzA2NWRpbzhuVzY5M05uVWNyeEZWbHh6ZmRKRU5MYWpUOHEvcVgrSnhyQXBiZkxKT3E1bWxwc2pTZm1WUGpuMStWMWNNdGs1YzN1YkNkbzBNV1RpeVNmZFJYZmEvd0NadlI2a1dZMlhGa1hleGIxS29kR1Roek1XbEYwelBEM01HWlhhb0tpWFV5MVJnQnJEVmtaUjVia20waUxkZ1VUaXFPYnFZT0R2ZHArUzVIV2FvMThzVkwyQTVtUEhqY1duRmZZWTlOanh5ZExudVkxYzhlRjhrbmZTVzVyNE5WOHpjbjZGUkhYVGJ5UStIOUxJUXp1dVRObU9YNUhhWHVaV09MaTV1blJ0bXNsMXI4SHhWOHlLcGFYSEthNUwxTHMrb1VGYVZyc2pUelpOclR0Tk4rbitibXM5bXo4SEV0NHBLdk5sbUtQRTc2R3JvcytPU1NuQk8rck92aGlwcE5OYitSRm1Pa3NxZUxGc2JXTjBpcUpjWTFKdWlKbmk3R0U2QVNkb1RWMXZRRGFZRkxqYmJ2bVRidEVrN2JLcGRQdUUyNndSbHpSbE8yOW1xOHlNNWJXR0tjcjRVMmMzVVRmSGZtbWpienpWY05jMy9jME03cWFhNjh2VXFJdDFYZ1gvQUZuRFYzVjdlaDNvY2prNlNQRnExSnRLbDFPdHdxdG1LcmliRS9xTi93QU14S01IbHQ4VGRlaHorYlNTYmI1SkhaMHVMNE9DTUcwMnVkRmVPYlUrYTV5dEFCNlhrWlRvejlSRWxFQkV5WVNveUFBQUFBQUFBQkFBQUFBQUFBQUFBQUFBQUFFWndVNHVNa21uMFp6OC9oamIvVVphOG95Vi9tZElFM21WVTZzK09CclBDcHgwdVRKbXpyNUl1U1VWMTl6aVJWUlY3N1gvQUNQWjYyQ3lhWEpHU3UwK2xuaXNhY1pOTnJpWE90MXpPZDVpK2VyVzVnZTkvd0RlNzl6ZXhTY25mb2MxdWtrdHJmRi9JMjhPVDlhbFhUcC9uK2JFT2pvSjJXOURWeDVPTnRMOW5yM0xwU3RiZGRqbXZsWVpqMUlwMnJKZlUvUUxseEloSjN1RzdLc2s2U3RNS1Q0aVB4WTNTM1pxNWN0cHhpMDMxYTVJMW5xRkZidHJuc21HYTM4bVJKZks3T2ZxTTlPcWV5NjhpdlBxRzRXMm5TMlNmWCtacFp1SnhmRks1Tlc5K1JzbXA2NmEycHkvRXVXNmowNldOTzkzSjcxMDdrZURqYTUxWGwxTjdCajRJV2szYjJzNjI0NFNKUm53UWxLVDY3Rmp6eWpnVFhWbWhtemNXVlFqdkhrbjVtenFIOHNJTFpLUDVrcXRWT1Z6YzJrNFNYSmxVL2xrcFJUNEpycDU4bi9JbmpYeGNiZzA0MCtmY3JsRjAwNHZieUtreE5xV0NveWJ1dmZtZFBCbWNKVnpUNWI4amtSdFNqdnlleHRRa21xZm85eWVwcXVianZReVhkdFh0ejZseWxhMk9IaDFLeHJhVnE5N051R3FmRmNkNjVvaXpIYWRhNlhyYTlUS2RHcERVeG5za3ZSbXhCcVN0RWVxcGRYRFlnblRyekpOV1VFblRLb3k0cjdNdGtyMktmTmhDTTVLTEtjazZWTmY1Um1jcW5UTlhMTnViajV2bjdNMlRXVzRxeXkrYU8zSjJhdTA0OEwydFdpK2J0eFZWdS81bXEzVWZhdnlMY3E2M2hQaG42VnA1NS9pUEhQanBYdXE3bzZFUEN0VXRwWmNmdGY5QzM4T3g0ZkNvUGhjZUp1VzUwenJPSllqK1RxZkd0cE5IRFRLMCtPZjd6TmtBdVNTT2R0dDJnQU5ZeWxZU3NKMEU2QWtBWVNvRElBQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUNHUmNVR2tlS3pwd3pUYmlveDQ3U1hsWjdkcTFSNVg4UjQvaGF0VGorMnQ3N05mMkk3K0s1djYxcStKR01YUzRVbDk2TmxPbmQ5TDNmc2pSaEpWR0QzdHQrdjhBWTJzYTQwbkp2YnArZGY1NW5GMmkrT1I4TVZHMTFiZmYrWnR3cmhpa3ZRNWtNMVcycnRyMy93QS9tYldETmVTSzdjekxOYTZFZXBLTHBJb2hsNG50eTZQeko5Q0hWakprcDdjLzRHbnFzNmlsYmZzWFpNbFBoUno5VDg4WEsxY3VTNXQ5MzI3R3lhZGRLWjVWSnBKdDlsdDdKRlU1VGswbjBkN2JMa1hZRndyaFZ0M3pmOWkzTGdxS2lwTjhYWC9PaFRtcWp6cC84bFVvUFBsazVTK1hxcTZHL3BjVGtycmhqVlJSbDZlRVpOdXQvd0MvOVROVjZ0UERpY29XMDdlMjM1bTFQU1NjWTh1UmZESmp4UDVJcCtkaU9kcHZibjNHcm5PT2JxdkRaUXpxVVVxWFQySVQwODhrSFNmRkYvVCs4ZHlHWEhsVlM1azFwNFJ0bEovampqYWJSNUtVMjNiZTZYWHlKWjlQeHpUU3EzVE9uUFU0NFVsYjlDbDVyZkplNU9uOGNjZGFSd200TnBiV20vdi9BRG9qa3c4SzRvdHJ0WjJrOGVTVnkyc2prMDNHMVh6YjNkOWhwZU1jYUNsMVRUNjE5eTJFZDBycVhSNzdHM0xUU2l1VjM4dCtSSDlIalNwTDFXdzF6Vi9GbEYxSnk5VjBmcWJ1RE0xdTFmZEd0bHdiV211WFJXbWlHSnlqSk9xa3VUaTN2L1UyelZTNDdXT1VaTGJlaTFPelF3WmQzYVczVmREYlRzaDB0MVkzUnF6bnU1THFqT1RKd3BYdW4xS01zdUZyL093VGJpRThsMDlrNmY4QW41ZnhLYlV0VDFTNGR0akxWcGROM1h1Um0wb3huU3VUditaY21PYXBOdHlYazNYMk5iSGplVE5EQ3BKY2NxdDlIL2lMSk51ZkZ1bmZYM05qd21DbDR2ajRseGNLazl2U3Y1bGMvVTlYSTliZ3hyRmlXT05KUjVKZEN3d2xTNmV5TW5lVEhBQUJvQUFBU1RzaVRBR0U3SWtrN0FKVVpNSlVaQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUFjYjhSNmZpMDBjaWpiVXF0TGRLanNsT3J4ckpwNXhhNlArQlBYeHN1VjRiVHp1YTI2ZjhtM0tUaW9wYzVQK3Y4QVkxc2tJNGRma3h4dEsya3BjeTZFMUpxMXZYODJ6alhhVk5ZNVBFNU5wTGk4OStmL0FBWFNxR1dTMitsTDJ0bUp5cjZmTk5lcERJMVBKcy9sajFiNW1LYitQSXJlMUpMWkpGM0UrRFkwTVVtcjJiYjZMb2JuMVIvbVJaaStWTWx4YnQ3SjhseWI3bEVzYnlOdVNhYmRKVWJVM2JTVktsc2wwS3N6K2xkRjl6RzJhakdLaXVpOUVWNWRianhOSnRLOXR5NTRYbGpVU1dIUTQ0VzVKU2srclFhMDM0bmlTcUwreUsvMHZKTytESE5tL3dEbzhMdFJTOURDeHBYUzVsNUZ5T2VzbW9hLzBIOXlhMUdTUDE0SnIwcG0rc2U1Tlk3VDJYdWFySE1sckk5VlhzMFpXdnh5ZTgveU9uOEdOUDVWOWlDMCtQNGt2bFhUcDNESk5jOWFwWDh1UEpMdW9tZmo1cGNzRWxUNnRIVGVHQ1gwcm41R0pSMjVKZWdKTmNsejFLYi9BRkQ1ZEpGa05Ya2g5ZU9jZTdYOURkY2QvY2h3dWdXWW8vOEFVb1Y5YSs1REZxb1NkS21iY2RPbTkwbjZvdGxwY2VSYkpSa3VUU0pxTE1WTi9GeDdKSXB5Umo4UGozanYwZkl0aEJ3aFVsdi9BQktuUGduUDVWdlQzSlRacTNUejQzVXF2bzEvSm01TEkwbWpReHJobFN0d2tyU1hRMlcyK2UzOVExbWNyYTM2MmFtYVh5T1ZjdjRrbmsyNTc4NHRNclU0dDhGSnhtK1JVUmJvMytxVWx2OEFLcTllWlZkd2pGK2RmNTlpUzNsR1BrcS9pVXlud3lwcGJPLzgreFNVSk80YmJXcnYzL3VkdjhNYWJoV1hVeitxVFVWOWt6Z1N2SldPUDdjdUZmYzloNFBwSmFiUktNK3J2a2RPWmxjdXIrT2dBRHE1Z0FBQUFBUytvaUFBTXBXWmtCa3czUkVsRURKaE96SUFBQUNBQUFBQUFBQUFBQUFBQUFBQUFBR0dyWE9qSUE4ditJOUo4SExIVkp2ZDF3OU9YOUVjaUV1R1VaTHkvbWV4OFQwc00yQ2JtMitHRHIxUEV2aXhaZmh5cmFTYTlFY3V2cnB6ZngwWi9MQytiNVgzRXZsVWE1Y3Y4KzVSSDVjUEZiMzMzOXl4elNjYmJVVjBPYnJLMk1YeThXNmJybS84OURjdzBsWGthRUw0SEo5V3krR1NvUm4rMDFzVFZTNHV5U1VZUy9OakZpNG4yTmViVGZCZkpHM2hhVWZxZHRmVDVFcWwxZkdFWXR0TG1xRFZNa1JrN3RvbjJkSkZiVkVrMCtoRnV5RHlVVVc0bTFSaTF0dWEvd0FkMjl1VzVSK2tUU2Q3YldHYTZEeVJYTmhaRS9zY09Xc25QSzFkYmx2eDN3L05iZFhzWEppUGQxdml4NlU5dzFmTTVXanpPVGFac3JQTk9yTXFwMXJiNEVTTlNHVnpYemVaYXNqYnJ5SmJxMXF5VU9SRnF5VU9WRSt6V1ZDN2QwK3V4b1o0Y01tNzVybDVIUmJUTlBPMHVyNTllaHN1cHN4WGl1TGJmM015eVdxWGwvVkZTbHd2R3VYREpKcjNveE5KMGwzWDVtc1ExRDRad2xYWm12QnluR0t0TnFWcHYxL3VYWlpwUWtxN0wxNWxhcUtqVy9Gdkh2YWY4Nk9qa3N6MnB3YTlQNS93VE5YUEswMzVyaGI3L3dERFJibXkzSysrMWVmL0FBYzdOTjJsMXQvME5pYlhUOEh3NU0rcmhseHBWamR5N005dWNqOE42YU9IdzJNbFRjOTMvbjMrNTF6dHpIS2dBS1NBQUFBQUFBQUFBQ1NkbVNCSnF3TWd3M1Fib0EzUmt3bFJrQ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUNHVlhpbDZOSGl2RmRPMm81SXQ4WFk5dFBrZWJ5UXZuWHVqajNNc2RPSnV1QThsNGVEZTR5NFhYc1h4ZTZWcmJiYmZkLzhtdnJJL291cmJTK1dXNlhjbENWdUs2MEZ1cGpkUVNXL1Y5aVVQcVVkbStuWW9ubC9WMm9wdTY1RXNWWTF4YnljdHR5RkxzN3BjVHBWM3EzNWtzR1JKZkxlUnZyMFpWbVZwT1RyclZiL1lwK1BLTFVZcVRmMis1bG1qcXJJNU9wVjdGbkdqUXdwdEp0Yi93TmxPNWIvWTUyYTdTclNNbzhTSng3azBySVUwNVkrRk0xY21KcEpOblRualZiTW9saWNZdVNyYlpXdVJjdXN4eXA0Nm54TmNydjhqRTFVLzhBeC9tamRsZ2xLTFZQZlpiWDl5SDZOTlQzMzVsNmk4WXBoR25Gcm9remNTV1RmcVY0c0RUV3pWSkxsWnVZY1VlRk5JaTNGU0lReGNMc3pIWnZzWDhQY2lsU2JWUDNJVXh4SWp4cU52b3lFNWIraFZ4YmJKTHpvMlRTM0Y2ZHA3cCtsR3JubTNLcFJ0VnpxeUdUSnRVZDc2ZVpUankvRVVrMDFUNW5TVFhPM0ZqbDhpVDZkZk15NWRhNlhYZnIvbnFWOGRxVVpiTjgxNWM5ekVQbWRTdE5iV3V2K2JGU1k1czVYUzQzMVNmdjFOWnphZzl2b2QvbWtUeTJvY05wMnR1OUdxcDFCWGYxTnR0ZWFOa1pUVVpLaHQ1My9uMi9NdThNd3JQT2VTY1cxSlVuMk5URmlscThpakcxanY2dnNkN1RZSTRjRVl4Ym9xMzhaSTlINGRENGVpeFJiYitWUGMyU3ZUcmh3WTE1Ulg4Q3c2OC9IRzNhQUFwZ0FBQUFBQUFBQUFCTGk3RVFCbHV6S2RrUUJMNlEzUmhxaVFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQTg2L3Jrdkp0SG9KZERnNVA5V2YrNS94T1hrL3gxOGYxei9FZFA4QXBHTGx1dGtjS011REs0dEoyNnZ6WGtlcG5IaWhKWFd4d3ZFZEl1RDRzS1ZkT1YzdWMrYnF1b2xISXQwOWx6NWwvd0FYaGh3cEpUbHo3SEp4WkhHUzQ5Nlpjczh1ZDgzZnY1bFdheVYwRk5jMjB2VWpqeUp5ZXkzNkpXelMrTDgvT1h1VzRadFpWd3VqTVZycHd6Y0Z4M2xrZms3SndrM0trbjgzVnMxOFNqaXg4Y3BPNWJXK3BKNXVKSkpMbi9sOWliTlhManFZOGtZdzNmdjVsa3NpakRpZjJPWENmeEY4c3BTZld1ditlcEw0bnhXcXZndW9wUG0vVW16RlM2MytKMm02NHZKUGtSbExaYmJjMTM3bExrM0JMejJTOC83Y3kzRTJwTktXNzJjcTMySnMxVXVMc2VIZm0yK3RtSXhWV3ZQK1pLTi9FZU5ONzg1WHVZNHJXeWUrKy9vYXFYVVhqU1cvbTFaQ0xjWkpiU1Q1T3l5ZVZSamE2dmJtVjU0MnBaSU4zSGN5elMzRm0xYzB2SnhLVzdmRHlseVpWa3l0dzRuRnJhMmFuNlE2YmQ4UzVQOEF6L1B1YWpycGZtbHh3YXR4eVJmUTE0emNuVXFhOHJyL0FCbVp5NDhmSFNmb2EzL2RjbHZXNVdJdDFacUVrK1Vtbit5MmEzMDAwM0tMM1hVdHl5akpLMTh5K2xyYWpUZVI3MCt1NVVpYlcxa2tveVR2bi9Eb1F5WmVKeGV5cDgwYStUSjh1NmROOURDbW93bGUvbjk5emNUYW5seThTU2V6aTkwVTVKUE8rREdtNU45UFlyeVRjcHRyZVRWUHVkVHczU3hoampON3lrcmU1WFg1RG45cS93QVAweXdZbGZ1YjVoS2pQVTQyN1hUK3Nlamh0Qkx5SkdFcU1ucmt4NUFBR2dBQUFBQUFBQUFBQUFBQUFES1ZoT2pBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU1OMGNESi9xNVA5ei9pZDZYUTRNLzllZjhBdWY4QUU1ZVQ0NitMNmowWnFaOERsQ21yMk4zaElaSVgxL0k0UzQ3OVBPYTdSY0tlU0d6VzdkR21zdFE0WkwwM1BSNTlQeEtyVC9NNCt2MFVvWE9OTHNkdWVwOGNiR3ZqblNiVDY3RnVMSktONzE1czBvNUd1cjlpL0ZQaWFmN3ZRM0V5dWg4WGJpZTc1ZTVOZk1sRjdPVDNiNm1uam53emp4Mm4zTmw1WXBYczdYWC9BRHVUWXR0NDU4YnVWUnhyZXJKWTVVb1NVYXBiUlM4LytmeU5SejJ1U1c3L0FMRnVmSlUzTGJrMi93Q0JLcFc5aWZ6UmtudW9xdDc1L3dEQ0pSek5aNXEyNHFxdDk3L29hbUYybmJYbi9uOFN1TXVMVXRyYmNuRlM0NjBjdkEzMTNmMk12TStLMDYzL0FLR2k1OFRudHM1TCtoV3NzcEp4V3pxdnpmOEFRWTMyYjN4T1NyYTdKS2JUbW12NzhpaHpUcmJuUXk1S2JoZlBiK1JMYldNc2xQRTA1VnQvWTU3bXJmRjAzOWkvV1QrSHUxeWRtc3VhbDVXaTVFZGRJd3lOTnZwMS9tVGI0cWR1cktzYjRZTlV0dlAySzV5VUc0dnpSV0lsU3lOSnVMcjJOT1VsRkxmWjdtTTJSdE52NnBWdVZKdFI4K3BjaWRXdVNjVmJiNys2L3FRK0xMNGJoRk83NkVKUzVLUFQvUDVIUzhPMFRUVTVVNU5iWDBOdGtqSk5xZmgraXFVWjVMY3E2blh4UTRVa24rUmpIajM4aTdHcVRPSFhWdGR1ZWZ4bmg3bUZ6WHFpWkJjMTZvbFhUMGlkbVRDVkdUMnZHQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ011aHdjbit0ay8zUCtKM3BkRGc1ZjlmTC92ZjhUbDVmanI0dnFRbkhlN013TXQwZWQ2VkVsZHJrYStmRXB3NFhmZWxadXlqM0s1UjVibXk0aXpIbXRkb2VCdHdwOWtjOXhsamUreDZyTXJlL29jcldhUGlYRkZYek8zUFg0NVhtT2ZISmRWL25Vc2prZGNseVMzOWlsNHBRWHpjL0lwK0pLTGRQbVZacUhVZVpOUnZvN3I4eVc4c2R2ZHZkdjMvdWFFYzNCR3VlekxzZWE0TmVSbGl0Ynl5VytGK1JzWTNUWHQvUTVieVhTdnFXdlVOVlcyeEdLbGJHUFVjVTZyb3V2b1dTbHNtdW14bzRKY0Z2MEpyTW1xNWI3L21NYnJmamtjbTIxMFRyN0dKNXJ5cU85OTJhVTlUVFRpK2xrSjZocDMxVGRqRFcxcXN0dEx1eW5GUGRSUzVyL3dEcWF6eXVWYmtmaTd5cjBSc2liY2JHVE5VVWw1Ykd0bW54UDBYOGluTm10b3JTdFhMa1ZpYmRUNHVLUHVRUzRuWG1TV055ZEpXZFBUYUpMR205bnpOdGtKTlIwT2k0SHhaSzR2SjlEc1lvSmNrbGZrWXhZcVhPdXlMb3gyZGVWbkhycTY2eVpFOGEzOGk3aDdrWVIzNWsraUlkSk1RSWYxTFo4aWlYTUZtdlN4NmtpdkhraE9IRmpsR2NYMWk3UllleVY0Z0FHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBTU4wUXlaNFlvOFdTVVlyemJvQ3c1bmlQanVoOE90WmNpbE5mc1IzZjlqamZpRDhRT0hGcGRITnJwUEpGL2tqeVVjZVRWWjFDTnVUOHcyVFhvdFIrS2RkcnM2d2FER3NYRTZUZTcvc2RQR3BLQ1U1Y1V1cjgzNW1qNGJvWWFQRmNkNXZuSm8zNE9yOVR6ZGRTMzhkdWVieXZTcEUrSHVZU3N5bFhJNXU2UFVqd2JOY2k1Syt4Z0RUbno1V2FtU0RXMnpYYzZMVlBia1V6aFRwcTdObHhGbU9YcWRQR1dOMnR1elp5OVJwWEdlMGZ0MDlUMFhBcmE4aXI0U1MyaXZzWE9xakhsM0JyN0VVcFFiU2ZQbWVneWVIeG5jazI3NTJhV1RRdUwrV3ZkbHpxSTlhNThwT3JmWCtpTThiczJjMm1uQkpVK1pXazBuYWQxYjI2RzZ5ekVYa2Qrci9BSmo0bSsxNytiSnZodjJIRXIvenlHbG1LbmtiYjJhMjZtT0psMXFtdHZkRUU2VXY4Nkd5c1V1VHAwakNUYmQ5UzZNSjVOb1JiTHNXaHk1V25KcU5ycUxaR3lXdE5RcCtaczRkTlBMdzdVdTV2WXZEbGpmRlRmcWpiaGhqdndwTHJzdVpONlZPYTF0UHBJNG54cC9OZmt6ZGhIYnk5Q3lFSmNrbDN2OEFzVzQ4YVQ2KzJ4enRxNURIQ2s3ZDl6WWpIYm1ZeHdwdmw3SXZoQ3Jwa0w1UmpIWjc4elAwazRWVFNEVkpCU21mTW9uMU5pU3Zjb3lLa0I1TEZxOVo0WHFaUnhaWjQ1UmU2VDJaN0R3TDhUUTFzbHA5VkZZOHorbVMyakwrak9WNGpvNGF2RzNLbE5jcEpIbjR4bnBzemkyMUtQVkhwblU2anlkY1dWOVlCNURRL2l1Y01NVnFjWHhLVmNVWlUyZGZRL2lQdy9WeVVGa2VPYi9aeUt2ejVGeW9kZ0dFN1ZveWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBR3RxOWRwOUhpK0pueVJoSHBiNWdiSnE2M3hEVDZIRjhUVVRVVTlrdXJmWTh2NGwrTE11Unl4NkhHc2EvZmx1MzdkRHorcTFXYlV6VTgrU1dTZm5KZ2QzeEw4VmFuTFBnMFg2aUg3ejNremlUejVjODNQTmtsa24rOUoyYXlkTDFKTjBCbVQ0anJlQmFkY0R5UGUyY1p1ejBmZ2Evd0NpajZuTHkvSFR4emE2UFJtY0t0REk2ZzJNQ3VOZVI1M282YlVPUmNWUjUyV3JwMkNnZERQRHZaWnc3Y3dOZHFrYStSVnQxNkc3a2hhNWxNNGJjZ05PVUxhdTFXMnpJY0M3K3JOaVN2YXpIRDNDTE1hN2hhcFcvYmtSeVJkV3ZtWG5ac3ZHcitWY3ZzekVvWFRWcC9ZSmF2NlBHZDJuM3NxbG9FMzh0TyticzM0cHAwbHhlai9reTV4Y25zbjdObzJYRityalM4THh1dGwzSy84QTBtTXVTVzFMYzc2d3AvVHQ1azFDbC84QTZ2OEFxYjdWbDVrY0ZlQ1J1NUpjSWo0UkNEcHdXL24vQUoyUFFTVnJsMlZJcW45VW5TMzNwOHg3Vk9SeDQ2UEhpU1VPaTVwazFDdVcvbmFOdWFsVHVsNzhpTGp1bnkvbU5GU1NmbjZVWldPN3B2N2xpVmNrL2N0VVd6TGRWSnFwSkpiRmtJNzh5WEJ0c1h3aHdOVnpNVkppQ2hiMzZGcVZtZUZrZzFIaDI1a0pGdlJsVFZnVnpOWFVHMVBrYXVYNWdJdFdrY0x4ckVvWklUUzh6dlJWcGVoeC9IRjhtTitiTDR2Nmp1YkhHVG9OMllCNm5rZHZ3YjhRYW5RdFk4amVYQXYySHpYb2U1MG1weGF2QkhOaGtwUWt0ajVXM1VrYm1pOFQxV2dkNmZLNE44MXpUOWdQcHdQSmFMOFg4b2F6RC81NC93Q2g2TFJlSmFYWFFVdFBsVW4xanlhQTJ3QUFBQUFBQUFBQUFBQUFBQUFBQkhpN0hOMS9qT2owUHk1TWlsay9jaTdZSFRibzF0YnJ0UG9zZkhxTWlndS9OK3g1THhEOFY2bkkydE1saWg1ODJ6Z1p0VG0xVTNMTmtsTitiZGdlaThTL0ZtV2NuRFJMNGNWLytScmQvd0JEenVvMU9iUFBqelpKWkpQcTJWY1BjY1BjREtWSnNOMFJrcUhNQ1VYYUJGT2tTQUhvUHcvUGl4VGg1TTgrZEh3VFA4SFhSVGZ5ejI5em41SnNYeGNyMDJaV2tSeEU4dkpERnpQTTliWmh5b3RLWWNpNENmV3lhVkZVT1JjQmhxMGlFNDdGaVZnRFVsQjBWY0w2UDJOdVN0Rk1vcWdLV3E1cGU1WWxhNS9uWk5Lekh3a3ZwMnNNczFWd0p2ZmN0Z210NHBQMVpscXU1bUhNRm1wOFBudjdHWGJXem94eERqOXdXYWkxeExwNnRGTTR4YnBOdnNrV1Nia1I0VjAyOUFtekZXUlcxMjh0eURqU3RYYjgyWHRVUmF0b045VVlScGMzN2lLdGt5V05XRkNWTFl0U3NRaTB5eHFnSVE1Ym1XNlJQaDdrR3JBaVZUNUZqVmxjdVlGVTViOGpYeU8xN0Y3Vm9xeUswQkNISmVod3ZIcDNteHdya216dlNmREJ0OUR5ZXR6ZkgxTThpZHB1bDZIVHh6L3B5OHR5TmNCdWlCNlhtU2x6UkVBQ2FkazRaWjQ1cVdPVGpKZFU5eWtseGRnUFJlR2ZpalZhZHFHcC9YWTExL2FYOVQxZWc4WDBuaUVid1pQbS9jbHRMN0h6TGk3Rm1QSkxITGlqSnA5R25UUUgxY0hoUERmeFJxOU5VY3orUEJmdmZWOXowbWcvRU9nMWRSNDNpbS8yWjdmbnlBNjRNSjJyVmV6TWdBQUFBQUF3blpCeVVWY21rdXJaeGZFUHhObzlKeFJ3L3dEVVNXM3l2YjdnZHppT1pyL0hORm9VMUxJc2sxK3pEbWVQMS9qK3QxdXptOGNQM1k3SEs0M04zSmdkM3hQOFM2blV1VU1MZUhIeXBQZjduQ25sbFBtQ0xkZ0c3TUFBREtNQUFBQUhWa3VMc1I1TkdVNkFrWmpKeGtwUmROTzBSNHV4a0QxK2sxQzFPbHg1RjdyeVpzNHV2WTh4NFByUGdaL2hUZnlUMlY5R2VxeHEwOXp5ZGM1WHE4ZlVzV3g1RjNRcjZGaExvekhxWEZDVmxpZEFXUk10WDFJbVlnWUtlSFl2K3BFSmN3S1VtalBXeVpCUXJrd0RWR0drMHVoWVI0UE5KZWdFVXFEVGZWK3hMaDdtTmdLL3UvVmt1SHUxNkZpU1JqaDdnVnVGOVExWEltQUt1SHVXd2p0ekJPSHlyMUFtbFFNZ0NFM1RTSXlkSWsxVEt3TU5XVnozSnk1RmJkdjBBaExvVXRXeTlxMFU1cHh4d2M1dEtLVnRzRGxlTjZqNE9tK0ZGL05rL2gxUE51UnM2L1V2VmFxV1ZwcFBhSzhrYXJkbnE0NXlQSjNkcEoyWUFMUUFBQUFBQUFBeXBORXZpZVpBQWRmUStOYXpRcExGbWJoKzVMZEhvdEIrS3RQbVNqcW9QREx6VzhUdzZkRTAyZ1BxbUhVWTgrTlpNVTR6ZytUVExUNWZwZGJuMHMrTFQ1WlkzMmY4ajBPaS9GdVNGUjFtSlpGKy9IWnIyQTllRFMwWGl1azFxWHdNc1hMOTNxdlkzRTdBK2JhL3huV2E5dFpNelVQM1k3STU3ZGM5d1FBeTNaZ0FBQUFBQUFBQUFBQUFBNjJBSmNYWWlBSmNYWTlQNEY0a3MwRmd5eXJJdVRmVTh4SHFUaE9XT2FsQnRTVHRORTljenFLNTZ2TmZRMHFNbkc4SDhZanFZTEZtcFpVdnVkbm9lV3l5dlZ6MU9vSldTVHN4TG9JOVRGSnBXV1JLaXhLMkJKcXlIUW1sUWFzQ3BxZ25SSnF5QUZoaHF5S2RJa0JqaDdqaDdrazZBVDdJSlgxb3gwWlBxUUNrR3FDVmt6RFZwQUlxa1dHUTFRQk9qRGREb1E2Z1lib2lHN0FFRzdaRnEyV0pVUUFqSlVqelhqL2lQRzNwY1QyVCtkcnEvSTNmRy9GRnBvUEJpbGVXUzNhL1ovdWVYNm5ieDhmN1hEeWVUL0lnd0FkM0FBQUFBQUFBQUFBQUFBQUFCTGk3R1l6NTB5QUEyTVdTVVpKeGJUWEpvN1BoMzRrMWVtcU9hWHg4ZmxMNmw3bm5pYXlOQVJaZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQk5PeUFBdGhOd21wUmJUWEpybWVtOEg4YitKV0hVdFJsMGs5a3p5NEo2NW5VVnoxZWEra0oyclFhczhoNFo0N2sweldQVU56eGNyNm85UnA4K1BVWTFQSEpOUGxSNXV1Ynk5SFBVeGVsUzNkazRlWlhIcVNKZEZ5ZG1Tdmk0WDZsdkYyQ2VrSmN5dHFpNlVlVzVXbFFiTHFCTGlFK1JFTldHT1M5Q0JMaTdBUjR1SldrL2RHVzdJdDBTU3NEQk5LbUVxTXAwQm5oN2lSbHVpRGRBUTZzZzNiSmtBSU4yMFRCaHVrQWJvNHZqSGl5MDBIaHcwOHpYLzFLdkZmRzQ0MUxEcFdwUzVTbjBYb2VhbEp5azVTYmJmTnZtenR4NC85cmgzNVA4ak01dWNuS1RiYmR0dm15RXVoa3hMb2QzQkVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETkdBQUFNOHdNRXlCS1BVQ1JzYVhXNTlKazQ4VTMzaStUTllHV1N0bHNldThQOGR3YWhLR1NzYy9KOWZjN01aeG1yVFBuSnZhTHhiVmFPbEdibkQ5MlRzNWRlTC80Nnp5YjllK1N0RmtlcHdkQitJZE5uU2hsazhVdis3bDl6dHduR2FUVFc1eTliL3EveFkxWkJxeXdnMVJsbUN0cXlCYzFaVkpVekhTVmdHV3FKaG5zckpSNm1Xck1wV0dXNnp3OXlRTXg2bTJKWWxIbHVSbHlKRldXV3hpcGNWeTZHRFcxR3J3NEk4ZWJKR0M3czRtdS9FU2E0ZEpGdi92bHQrUlU1dEwzSTd1cDFlTFRZblBMTlJqM1BMK0orTlpkUzNqeFhqeFA3czV1ZlVadFRQanpaSE45TDZGUjI1OGNqaDExY0FBZFhNSUV6RStRRVFaNUdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdlWmdBQ1hEM01jeHlBU1ZFaVBNSldCSUFBWk5uUytJNnJSdjlSbWxGZnU4MTlqVkJsa3JaYkhwTkwrTE0wYVdvd1JrdXJpNmYyWjF0UCtJdkQ4NlhGbGVLWGxrVlY3OGp3b0l2amxYUEpZK2xZOVRpeXg0c2NvelhuRnBvT1NaODNoT1VKY1VKT0w4MDZadFl2RmRkaGY2dlZaRXZKeXYrSk44YXA1STk4M1lUbzhURDhRZUl3Ly9OR1grNkNKL3dEdVBYLy9BTVgvQU5SNlU5NDlxbmFNcHBIaVAvY1BpSFNXTmVrQ3FYamZpRXVlb2t2OXFTL2tQU252SHZGTk0xOC9pV2wwOS9GelFnL0p5Myt4NEhKcTlSbC8xYzJXYThuTmxYRjJObkdsN3g2M1ZmaWZTd3VPQ0U4ejg2NFYrZjhBUTQyczhlMW1kdFFheFJmN3ZQN25LVG93WE9KSE85MnBUbktjdUtjbktUNXR1MlJBS1NBQUFBM1Fhc0NMZGprWmV4RUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUpjUGNmU1JBRXdSNWhPZ0pBeHhkaklBQWYwQUFBQUFBQUFBQUFBQUFBQWFzQUU3QmppN0FPSHVSQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETE1BQUFBQUFBQUFBQUFBQUFBQUFETEF3QUFBQmxLd01BbHc5eHc5d0ltVTZNQUNYRjJNSko5akFBbHc5eGI4aUpMaTdBT0xzT0xzTEhGMkFjWFl5RTdBQnVqSEYyTW1PTHNCbE8rakJqaTdHUURWOVg3QUdKZEFNaHVqSEQzSEQzQXczWmd5akFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJNaTFSbXpFbllHQUFBQU04Z01FdUh1WVpuaDdnT0h1UkpjUGNpQUFBQUdUQUdhRmhtQUFBQUV1THNZU3NrQmo2aFJsdWcxWUVVWUpnREhEM0hEM01nQWxRQUFOV0FBQmpoN21RQmpoN21VcUFBQUFBQUFNY1hZaVREVmdSUVJtaUlBR2FNQUFBQUFBQUFBQUFBQUFBQUFBQUFCbEt6UEQzQWlES1ZtZUh1QmhLelBEM01OVVo0ZTRHVEhEM01nQWxRQmpoN2dPSHVPSHVPSHVaU29ESEQzTXBVRTdBR09Mc1k1RWcxWUdPSHVPSHVLSEQzQWNQY2NQY1I2bVFNVVlTc2tBTWNQY2NQY3lBQWFzQUNNbFJJTldBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQmpoN21RQkhtWU1vd0FNc3dBQUFBQUFBQUFBQUFBQUFBQUFBQXpGV1o0ZTQ0ZTVsS2dNY1BjeUFBQUFBTldBQmlqS1ZBQUFBQUFBQUFBQUFBRFZnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFhc0FBMVlUc0FZb3lsUkZCZ1Nib2daWmdES01HVUVCZ0FBQUFBQUFBQUFBQUFBQUF5bFlTc2tsUUdLTUlrQU1SNm1RWTRlNEdRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJqaDdtSXVpUUFKMkFBTVM2RVNVdWhFRE5CQmdBakJsR0FBTW93QUJMaTdHRzdBd0RLTUFBREtBd1paZ3p5QXdTVzVnekhxQmxLZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQVlsMEhEM01wMkFJeVZHQ1gxRGg3Z1lZUWtxSkFZajFNZ0FSUTVHWmRCUUVRWmFvenc5d0hEM01OVVpveWxRR09IdVpBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFuWUFBQUFBQUFBQUFOV0FBQ2RnQnF3QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRC85az0iXQ~` +const exampleCredential = `eyJ0eXAiOiJkYytzZC1qd3QiLCJ4NWMiOlsiTUlJQzVEQ0NBb21nQXdJQkFnSVVUNkpsZTdjK0ljUjBCUjhDSWE0WXNNeGhERDB3Q2dZSUtvWkl6ajBFQXdJd1B6RUxNQWtHQTFVRUJoTUNSVlV4RlRBVEJnTlZCQW9NREhkM1YyRnNiR1YwTG05eVp6RVpNQmNHQTFVRUF3d1FkM2RYWVd4c1pYUWdVbTl2ZENCRFFUQWVGdzB5TmpBek1EVXhOVEF6TURkYUZ3MHlOekF6TURVeE5UQXpNRGRhTUVNeEN6QUpCZ05WQkFZVEFrVlZNUlV3RXdZRFZRUUtEQXgzZDFkaGJHeGxkQzV2Y21jeEhUQWJCZ05WQkFNTUZHVjRZVzF3YkdVdWQzZDNZV3hzWlhRdWIzSm5NRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVMMTEvUG9WMXFTZlB5cldNNWR2VjFLVDVnK1ZGZk12S3FiVHNQRlZ2ZGExWEg4RzdWdXpyVjBYaEJjWld1d2I4bHYzN3BPVDB1WmxJT09kdnZwcWh3Nk9DQVYwd2dnRlpNQjBHQTFVZERnUVdCQlNTOWVObjFPZ1NPUEJKQXZJTklnSzlDUjJNZHpBZkJnTlZIU01FR0RBV2dCU1I2dEk0ZlY2ZG5OZ1FuUTdkc09WRVNhNW4yREFPQmdOVkhROEJBZjhFQkFNQ0I0QXdPZ1lEVlIwU0JETXdNWUVSYVc1bWIwQjNkM2RoYkd4bGRDNXZjbWVHSEdoMGRIQnpPaTh2WlhoaGJYQnNaUzUzZDNkaGJHeGxkQzV2Y21jd0VnWURWUjBsQkFzd0NRWUhLSUdNWFFVQkJqQU1CZ05WSFJNQkFmOEVBakFBTUV3R0ExVWRId1JGTUVNd1FhQS9vRDJHTzJoMGRIQnpPaTh2WlhoaGJYQnNaUzUzZDNkaGJHeGxkQzV2Y21jdmFXRmpZUzlqY213dmQzZDNZV3hzWlhSZmIzSm5YMmxoWTJFdVkzSnNNRnNHQTFVZEVRUlVNRktDRkdWNFlXMXdiR1V1ZDNkM1lXeHNaWFF1YjNKbmdodGxlR0Z0Y0d4bExXbHpjM1ZsY2k1M2QzZGhiR3hsZEM1dmNtZUNIV1Y0WVcxd2JHVXRkbVZ5YVdacFpYSXVkM2QzWVd4c1pYUXViM0puTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFDVlZCZzVCNXV5c0hSTEttMnVINDhWcDB1SHZCTy8zT2VKeHh2UkZQamhLZ0loQU04L3lxRFhpNGFnd212eW1yaDU2Sk9kSnp1QXl0QURwUlRoVndzdWw5QTkiXSwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiI0REpIS2NMa0dkYy0wazhZSDRfWFNjci1jTnFyVEI4MWhrbGRXRFdfaXc4IiwidmN0IjoidXJuOmV1ZGk6cGlkOjEiLCJkYXRlX29mX2V4cGlyeSI6IjIwMzUtMDQtMjEiLCJpc3N1aW5nX2F1dGhvcml0eSI6IlBJRDowMDAwMSIsImlzc3VpbmdfY291bnRyeSI6IkdSIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iOiJHUi1GIiwicGxhY2Vfb2ZfYmlydGgiOnsiX3NkIjpbIlFoMzliNkc0RDNsWDlnUUpCWUJoaGxVY3B5WWZnX2F6bVc5dTRSU04tTE0iLCJUdGUzZ1NVOUpYQmFBWnozcHU1RGxHNm5mRURxZmxLekxDeWcwOEtZelVRIiwia0NJVUQwOXZGaDFsaW9Sc0VxNzZGd2swOXgxYzZxbWxNdGlVbVIxRzVDTSJdfSwiYWRkcmVzcyI6eyJfc2QiOlsiME5wUmdPaTNxb0EtN18tNWtyZ2N4aXZBSFl3MWZuZFZ4bWxHUUt1U1c0NCIsIjVTVEYyV0ZsdGV4ZW1qRHktcm9aYi1JdHVzUGM1OXlOSTZ1Z0JvS0NLT2ciLCJELVViZGxTbF83TDhMb0dHN3lIMWh4NmdmUjJtTEVhejNTUGVIUnZUUFFvIiwiRFhia2dmY2hhZTNNMG9STHJxb21fVGhRUnhKai1mX1lEWElISkdBbVR5YyIsIk9SYjE2aVBWenJKTHU2UWJKd0VubFBpWW8yejBic2dnZkUwdmtGd1dCY1UiLCJpRDlkNWRMYVNOamI3X3BPNGJCLUxqQ2xuekNDWUdrbEYwT1ktZHNPOU1jIiwicG8wRmp6YnlFNHFkSnRkdDFJVV9CQks0RFk3WTBfRzFmTUptaEoxaUJROCJdfSwiYWdlX2VxdWFsX29yX292ZXIiOnsiX3NkIjpbIjExREVkYzl2Q2d2dTM2cS1oYnhBNmprTFZiNWN4UWFpT0VKc0lTS29paTQiLCIzaWM0NDNCQUlOLTFEbXRfV0R2My1JeWwtWGUwUi1zVXhOU2RyTDQ0NVRNIiwiWHVsOUVDVzVZNmd2UEc1cVN2a1NJRFdGM0FpQ1lXT1E1YlFxdXlIT2gzMCIsImpkZUxkNHFGUWxPSjgtZlBYVXpZVGlWOGsxMndDOG9OYi0zV2pWdVFfQUUiLCJrVVI0SGluWG5oRy1hM3FMN2YtR3BlSDJjUTRGVUFSNUVZZlZTNTVDSm1JIl19LCJ0cnVzdF9hbmNob3IiOiJodHRwczovL2V4YW1wbGUuY29tL3RydXN0X2FuY2hvciIsImNuZiI6eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJleHQiOnRydWUsImtleV9vcHMiOltdLCJrdHkiOiJFQyIsIngiOiJKZV81U1JtUC1MZTgxNGYwUjN1TEVoU2owanBLdXFlTW9hUGNNcENad0VNIiwieSI6IkZpY0ZZYnVIR1RaREhzcFUzb3pWSWRoRm53NWRQa1ZNYVBZYXlWMEUwSzAifX0sImlhdCI6MTc3MjgwNTkyOCwiZXhwIjoxODA0MzQxOTI4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwMDMvb3BlbmlkIiwiX3NkIjpbIjRVN0Fmc2JhNTFHYm52NzN3VXFWWm94c3VCOHRDYkN5N3hHZzJwalUzbkkiLCI2UEhwQjVwMndBcDlad2psaWJKUXplRGVVbXk2cjFqeHJpN253VHlkNDJFIiwiOFgtdTA1aGFmVXVpZjlpVDNtWnN3Q2hITEVpdVpMSzRzc0RXM05oaVNsNCIsIkJOWDVZSF83TVBrT3JUZ0d1d1JoZmtsUnVXbmMzUGp1ZXVjTTdIbkZISkUiLCJEYlhVeFVXSjZsOEg5R2UtSWRBZWl5YW9JNzJZWmo5aU0ybkV1NzFZVm1vIiwiX2l2czFkM2lFcDZIODFHUHFpWlVSbHFzd3EyYmNsNlNhaUhvaXo2ZzcxWSIsImJGYVBJQ1VsRUZod0dBS0cwSTRncUJfeWNoQ3NoM3pWWHk5YlJsMUxKbmMiLCJiSTdXeE0tNDJUR0VCNUM2RkhCVWwzdFBEZllfbTl6TkVZMk5yUFowZlJJIiwiYm9qQ3ZXcC1rcS1yR1pTbmtZdWR5QnhuR09VRzk2aUFsV2lTeXVPM3pZcyIsImQtbkVaWTM3ZUdURFZCOE5NRlhLRWJONGk2Sk5MUEc0X25LTjIxY0xGVW8iLCJnbll6N1MwcHl6dlNKRFpnQkZKcGJIWERlVkdqTDBaN1ZtU1RpVHhBSVdjIiwia3YxZFRnRUE5MWJJcGJmZGNTTUtvSFBmdmNmRFpnU3RnTDcyTjlRQnM1byIsImx0SDk2RVpnMFNZNkFmdkNGczRWbTlTNGtUTkVGdjRGajk4MnRwU1RIUzAiLCJuem5hdC1fREJNdWgxWnlNOHpVWEF3VmxqZURucUl5TVowYTJScDdXSXo0IiwidDB2Q1hubW14cjhxcXVCa3YxR08xQzFpOC0zc0sxTTYxYTBZTmFXaDlZZyJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.46oCMb1XM1rayoRmm6pkloU9f_ES3PBRfpQ2eMdRqIJElB5MQ0rTBFlyTnw8yTCjBa6nJW8p0vTZp63rMRQrEA~WyJ1TWF6c3hQamNGeGR4dERGYlI4T0xnIiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyJzM0t2aERrRWtZdUw3Y3F0OHVLa09nIiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJOMW1BUlJ2YVFINzRGOHctek5rbExnIiwiY291bnRyeSIsIlVTIl0~WyI2TkgxY0lDN1dYNHBDbnYyRHVTUGhBIiwiaG91c2VfbnVtYmVyIiwiMyJd~WyIwNDU1MkJyeFd4dHBidXE5SW9JLVBnIiwiZm9ybWF0dGVkIiwiUmFuZG9tIHN0ci4gMywgMzQ3OTMgTWFuaGF0dGFuLCBOZXcgWW9yaywgVVMiXQ~WyJVUV9DRnN4WnpJOTc2N0Ftb2lKcmxRIiwicG9zdGFsX2NvZGUiLCIzNDc5MyJd~WyJyZlBUVExuRURtcllOeVFaUDVROXVBIiwic3RyZWV0X2FkZHJlc3MiLCJSYW5kb20gc3RyLiJd~WyJndTlwUTJraDJyZjF5WEs5aFpLNkVBIiwicmVnaW9uIiwiTmV3IFlvcmsiXQ~WyJyTHdyS0ZPSXdua1A0bVc1LXk1aF93IiwibG9jYWxpdHkiLCJNYW5oYXR0YW4iXQ~WyJLWUc5d3hqRHQwWVdzaXhsbzZFNXJ3IiwiY291bnRyeSIsIlVTIl0~WyJZT3h5MlNzNmZqQUFtb2dRVTdkLURnIiwiMTQiLHRydWVd~WyJKSUFvVVZBNjVFdGRwOE1tR3Z0ZDRnIiwiMTYiLHRydWVd~WyJWR1pZbThzZUxWZkpWVFU2bFBjYlBnIiwiMTgiLHRydWVd~WyJUaG83YW9CYlpjNTJncDFodHE1cnJRIiwiMjEiLHRydWVd~WyJjUXYxdVBSYUdPUHdHNkRscG05QW9BIiwiNjUiLGZhbHNlXQ~WyJCYjJQNTBUbFA5YVF3N2xpVXdfUk53IiwiZGF0ZV9vZl9pc3N1YW5jZSIsIiJd~WyJsRVZ3MDZKWjJWa2Zwand1aTVJTE93IiwiYWdlX2luX3llYXJzIiwzNV0~WyJsSGxhU1ZWZ3haVnpxVEMyZ0pMTVhnIiwiYWdlX2JpcnRoX3llYXIiLDE5OTBd~WyJSaE1CTm0wMjQwZlY1ckZoZW12QWh3IiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyIteG1DYWUxY0NTY2FrSkpxT0ZqN0hBIiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJEQkNIY2hKaW8yUWFPNi1SdnJQUkV3IiwiYmlydGhfZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJEU1FxeFpGOXNfRjNobXVvX1UzWWF3IiwiYmlydGhfZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJfbVRoTVkzMGxzQU5TTjB1eS1lU21BIiwic2V4IiwxXQ~WyJNMDlaWnJfQWRDSC16MTcyTGIzbWF3IiwiYmlydGhkYXRlIiwiMTk5MC0xMC0xNSJd~WyJlUGxvZHRmRkNhd1FIdE41dWVHa2t3IiwibmF0aW9uYWxpdGllcyIsWyJVUyIsIkdSIl1d~WyJVWGp0VlhGVXFmbzJMREFEUGMyQUt3IiwiZG9jdW1lbnRfbnVtYmVyIiwiMTIzMTMyMTMiXQ~WyJCZlVsLTN4UUVCTnc2c1ZKalNabllBIiwiZW1haWwiLCJqb2huQHNhbXBsZS5jb20iXQ~WyJaQV9jMHdxR0dPVVBDUTZUU2NmSkZRIiwicGhvbmVfbnVtYmVyIiwiKzMwODM4ODMzODM4MiJd~WyJVOEFIb0VNVUw1ZmhvNjc5bktsZHVRIiwicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIiwiMTIzNDU2Nzg5Il0~WyJBSllvb280RU42OFg0RXFZVHg2bjh3IiwicGljdHVyZSIsImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRRUFTQUJJQUFELzRnSFlTVU5EWDFCU1QwWkpURVVBQVFFQUFBSElBQUFBQUFRd0FBQnRiblJ5VWtkQ0lGaFpXaUFINEFBQkFBRUFBQUFBQUFCaFkzTndBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUUFBOXRZQUFRQUFBQURUTFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWxrWlhOakFBQUE4QUFBQUNSeVdGbGFBQUFCRkFBQUFCUm5XRmxhQUFBQktBQUFBQlJpV0ZsYUFBQUJQQUFBQUJSM2RIQjBBQUFCVUFBQUFCUnlWRkpEQUFBQlpBQUFBQ2huVkZKREFBQUJaQUFBQUNoaVZGSkRBQUFCWkFBQUFDaGpjSEowQUFBQmpBQUFBRHh0YkhWakFBQUFBQUFBQUFFQUFBQU1aVzVWVXdBQUFBZ0FBQUFjQUhNQVVnQkhBRUpZV1ZvZ0FBQUFBQUFBYjZJQUFEajFBQUFEa0ZoWldpQUFBQUFBQUFCaW1RQUF0NFVBQUJqYVdGbGFJQUFBQUFBQUFDU2dBQUFQaEFBQXRzOVlXVm9nQUFBQUFBQUE5dFlBQVFBQUFBRFRMWEJoY21FQUFBQUFBQVFBQUFBQ1ptWUFBUEtuQUFBTldRQUFFOUFBQUFwYkFBQUFBQUFBQUFCdGJIVmpBQUFBQUFBQUFBRUFBQUFNWlc1VlV3QUFBQ0FBQUFBY0FFY0Fid0J2QUdjQWJBQmxBQ0FBU1FCdUFHTUFMZ0FnQURJQU1BQXhBRGIvMndCREFCQUxEQTRNQ2hBT0RRNFNFUkFUR0NnYUdCWVdHREVqSlIwb09qTTlQRGt6T0RkQVNGeE9RRVJYUlRjNFVHMVJWMTlpWjJoblBrMXhlWEJrZUZ4bFoyUC8yd0JEQVJFU0VoZ1ZHQzhhR2k5alFqaENZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJOalkyTmpZMk5qWTJQL3dBQVJDQU1BQWdBREFTSUFBaEVCQXhFQi84UUFHd0FCQUFJREFRRUFBQUFBQUFBQUFBQUFBQUlEQVFRRkJnZi94QUJBRUFBQ0FnRUNCQVFFQWdrREF3UUNBd0FBQVFJUkF3UWhFakZCWVFWUmNZRVRJaktSQnFFVUkwSlNzY0hSNGZBemN2RWtZb0lWRmpTU1ExTmp3dEwveEFBWUFRRUJBUUVCQUFBQUFBQUFBQUFBQUFBQUFnRURCUC9FQUNFUkFRRUJBUUFEQVFBREFRRUJBQUFBQUFBQkVRSURFakVoRXpKQklnUlIvOW9BREFNQkFBSVJBeEVBUHdEMG9BQXluUmdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ0tkY3c4a1Z6YVhxWmJna0NLbW1SYzFmb1pvc0JyNU5aaHgvWEpSOVRWeWVNYVhDMDU1SXlpLzJvTy95R2pwQTBJK0xhUEt2MWVveDdiN3lvdmhxOE9YNkp4bFhSTlArQTBYcDJaS2ZqUTZUWGZjc1dSWFRhVEdpUUk4UmxPeWhrRG9BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBZEFBSThTSzhtYU1FM0pxbDFzNGZpUDRneHdnNFlxYzcyNGR5ZGJKcnUvRmpCL016UzFmakdsMGpYeEo4MXNsMVBHNnJ4WFZhaHJqeXlhVjByNUdqUEpPY25MSk50dnozTWJqMU9mOFV3NDJzV0RpajV1WDlqbGF2eDdVNTIwNXJISG9vbkZsbGFlem9pNXRocmRucjg4K2ViSS9XVFlldXpTVlR6emZyS3puY2JIRjJBMjVhbS9xYjkyWStPYXZGMk0rOWU0WkxyWTQ5NzRiWVdkeGR4azE3bXZmY0JyZXc2N1Bqdmh5elhwSm5VMC80ajFNSWNHVnJKWEp2bWVkVHNseGRnUFk2ZjhBRkdPN3k0cEo4dmwzczZ1RHh2UTVWY2RSSHZleDg2VW15YXl2dkgxQStwUXp3bXJnMUwwWk5QOEFNK1k2Ylg2alRQOEFWWkpSWGtuc2R2U2ZpYk5qcmpVY2xld1pqMm9PUG92SGRKcVpLUEh3T1hKUy9nZFpaRkpYSGMzV1dZa0NNZXBJcGdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWczUnJhdlVSMHVGNUp5akZMek10d1R6YXJGZ2c1WkpLS1hjODVyL3hROGJsRFRZcVhTVW51dlk0L2kvalV0WmxmQWtvK2ZVNUhGeE1sVWpmMVhpZXAxVnJMbGs0dC9UZTMyTkw0akk4WEN1U3Z1UVNrK3FYcUdwT2JaRnl2cVkyODIvY2kzZlJJQ1RkR09Mc1JCdGpOWmJzd1RUc0dGbW9FdUxzWmF2cDlrUmFvM1dXWXp4ZGpOcnpJYmRBTWJxemdVM3MxNkVkNDdFU1NuWFFXRXVzcDMySDhRM0dmWWRER3BjVzNJeWxUdUpBTjBCc3h5N2JMZnFkRFErTjZ2U2ZLc2psSHBiNWYyT1RHVjdwbG5DcEt3UGUrRStNWTljMHFxYmpiajVQa3pyOFI4dHc1c21teUp3azR0TzAwZXE4Si9FNmRZOWI4dTZxYTVlNXN1TXMxNmxPekpWaHl3eVE0b1NVbDVwbHBzdXBBQWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJodWdNbUc2TWNSNS94eng2R2tVc1dubGVXdWE2R1c0MlRYUThUOFowK2doODF5bStVZVRQRCtLZUo1OWZsYzhyWFpMa2thbW96enlaWE9jbktUNXR2Y29ic2xzbUpOVzBpVFZjMlpTcnJ1UmxMY05KUzViRlpOTGExMU1mUytyZm13SW1VcjVDVXVXeVhvUjR1d0dURXVobjJiQUVVckpiK2dHL2tBMzcvWXkxWFZHRExqWFFDRFZHQ1VxMkkvZjNLbFRaakNWRXVaRnVqSXNKY0NjWmI3a0RMZDh4WVNyT0h1T0haN2tPTHlNL0U3RXFSNUZ1UEkyeUcxTkl4R1hDd0w1Zk5HMmpFWk9McXRnblVyZkprcFZHVFZXQjFmQnZGcDZEUHZjc2JWT05udXROcWNlb3hMSkJxbjVuekNNclZleDFQQ2ZGSjZQS281Y3pXSGsrdEJsbXZvUEYyTW5PMG12MCtxVVZoelJrMjExTitENGsyamRTa0FDZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTXVSbHVqei80aThXL1JzVHc0Vy9pU1hONzBaYmpaR3QrSS9GNFFUMDJCOFU5K0oxc2VUbGtjbStiN3RtY3pjbnhTazVOdnE5MzZtdjEzSlV5MVJPTWQrWlgxOUdTM2s2QW5rZEwxSWNKYzhjcEpVakh3bXVld2JaaXR1dWhrTlVZVHZvL1lNWTRlNWhxaWFpMlo0SDVNQ3I3a28ybmZJcytHekxqZm12WUt4RDcrNkk4UGN1aml2OW94d3BMekJpcmg3bUdxTHV2Sis1bjRMdmV2Y01rMVR3OXlPeGU0SmxrTk8rdHIxUmxyZld0UGdZNEdiNjB6UzJWZTVCNFZ1a2pOUFd0UGhRNGU1c3ZEdXdzZEt1WlJqWCtFekhDYml3eXJlMTJId1NkTWFpVk1TaXVIWm0zOEZ0YklybmdwV2JLZXRhL0Z0VkUrTGJseUl1RlBtWStscnF1cHFXSE9ueUxWUGFpdjZXMDl6TFY4Z3kzRzNvdFprMHpieHphVGF0TGEwZlEvRGRaSFY2U0UwNDNYSlBkZXg4eDR1RjNUcnVkand6eHZOb25DT1M4a0k3SytpOGdmWDBST3pKei9BQTN4SEJyOEh4TVVxODArYU43aUtsU2tBRFFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFLTlMxREUzS1NSbHVEUzhXOFV4YVBDOTBwTmJkWDdIZzlUbmVmUFBMTGliYjV5NWx2aXV0bHE5Ukp0dDc3ZHR6UnlTcEx1U3RpYzNSWHhkakRkc0pXd0pKY2JOM0ZnNFlweTJ0Y2lXajB6bFVuSDBPbkRTMzlkT3lPcityNW40MHNXQ1R4cmIySVpNTlhYTHpPeDhIaWFnbSsvb2EyVERicnk2RTY3ZXJteHdLVGJlNkxscGJmeW82V0xSOFQrYmtiMzZQR0tXMjdNdlZqSnhIRFdoZGJYN2t2ME5kWXY3SGRocDQxeUpmbzhleUoxdnJIQldqYStsR1AwT2ZYK1ozWmFkT3FTL2dSZW5jdHR2Zit3MXM0bGNQOUVhNVY3c3JqcFhPMUg3MGQzOUUrSy9tZnlyb2xWbGtkSzhmSkpma05aNnh3NDZCcTkvNWtaNlZ4cTI5L003engwdW0vbXl1T2xiZHVMMzgwUGFucEhMeGFUYXVKK3hjOUh3TGVhZlpvNmkwa212NjdtZjBiaGZTMy8yOGhwNnh5RnBaUGtrdmN4TFJ6a3QydmJjN2Z3RXFyYWpNc05yYm1OUFY1eU9oYnR5ZCtoTmFLS1czWHNkNzlIY1ZTM0lMVHBjOXg3VTlJNDYwMUtvcDBTaHBrcmJpbGIycEhYK0F1aVM5RVplTklhcHlWcDBsYTJzcG5wTi9wT3c4ZFBseks1WXVRMWxtdlBadEpIeU5UTGdjV2VreVliWnJadElweGFTWHVkSjBqcm1QT3ROcjBJcDBkRFU2UndWcGJIUGFwbHl5dU5salBGczlqTVowUU1wcEYyYWlYSFQ4TTE4OUZxWXpqSnBYdnVmUU5EcUk2akJHVWVxdGR6NWVuVFo2WDhOZUwvRHlSMG1vYWNYOUVuMGZrUzJ6WHR3UWhLNEorWk1xWFVnQU5BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJISmtqalZ5YVhxZVovRTNpVlErRXIrWmMrUjZEVVM0SU9meXBKTzNMa2o1MzRycVphalVUbTVPVGJJVkduSjhXUzMxWlZtK3V2SXc1ZHVwbnY1aHJIRHR6TnpSNmI0bVZiV1VZWThjMG0wNzhqME9nMDN3NEoxMEp0eGZFMnJkTHBveGhGMXlOeU9MbVp4cTBYcFVjTGRlaFFzTkoxekl4MHpiM3BWMG8yaVNWR0NwWTBrVzhPeGtzQzVFZUh1U0FTc0NQQ1MrRXFKSlVaQWdvVjFNOEtMT0h1U0JiaW5nWGt2WWx3S0w5U3dCTnVvOFBjZ29wRnBocXczRmJTZk1pMVJZMVJnTlFJeTZFK0h1WWFvQ0RWRUdxTFdySUFWdFdSYW9zYW9pMVlUWmlocE5GY3NXNXNOV0dyREhQejZaU2p2YnZ6T0ZyZEs0dHRJOVJKVnVhR3J3S2NIMVowNXY2anViSG1KeDc4aUxWR3hxWU9FMm1xS1dyU084cnpXSUZ1R1ZTNGlycVpUcG0yYXlWNzc4TytKL3BXRlFuTDU0Skpwbm9FN1I4MThLMXN0SHJJWkxwV2svUStpYWZKeHdUMmRxOW5hTWx4dFhnQXBJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCR2JwYkVpbkxMaFM2OXVySm81ZjRnMUMwK2duSnJpazFzbnlYc2VCeXl1VGt6dWZpWFZ6eTZyaGswNHBiUlQyaWVleTgwWXNoOHo1YkltMWJva2x3WXVIcXpPQmNlVklEZThPMC96Y1ZuZnd3L1Zyb2FXZ3hiUk9ualZJNDlYOWVqaVpFNHg1N2t5TWVwWWxSeld5QVRTb0NLVmt5U1ZJeUZ5b0pXVE1wV1o0UUlrMHFDVkQzWDNBeUFsZlZleG5oWUNKbHF3bFJrTXFBTUdXN0RRdzFaa0FWZ3NJOFBjQ3ZoSXRXV0FDZ2kxUmExWkFDdHF5SlkxUmdJVnRXalZuSG51YlpUTlcwQndQRThDVnRLams5VDAydXhjVUd6em1XSEJrbzc4MzhlZnFmcWxxakhVbkpXeUxWSkhXVnlzeGJoZDJqMnY0WjFzNWFQNGNtM3d2elBEdytWK3A2TDhNYWxZOVRMSEs2a3VWa3FlNWp5SkZPTjNDK2hjVkxxQUFHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQWFQaWVYNE9tbk5TU3BQWjlUZGFzNC9qeXJ3L05LS2NwTmM1YktLSnJaTmVGMW1TZVhQS1dTVGxKdmRzMTQvVnYwUk9YTXJTdUw5VEZKSjIzNkcxb28vT2F4dmFHUEc0cS83azlmRmMvWGMwa2VLSGticWRvMXRNdUdQb2JYUTQxMzVXeE1rWThpeVBVbFNRQUNva25aSkt6QllEUXdaTXhEV0VyQk1nM1lCS3lTVkVVNkpnWWJveEhxU0FHR3JJa3lDVmdsMENWZ3p3OXdNQnFpU1ZHSkFZYW9yTEc3SU4yQkNYUXJhcGx4RFlDbWZJd1Nhc05XR1ZYUGtVdFdpNlhRcmJ0QkxXMUVlT0ZIbmRkamNadG5wbXJSeFBFY1cvRXVuUTZjWDljL0o4Y2RxaU0rVExuRlUxNWJvcGwwOURzODlTeHUxNkhSOElrMXJNYVMrbVZuTnhxMGIvaDgzaTFlUEpIbW1nMTlHMDMrbW5UWFptd2FlamZGaWkxYWkralhMME53MklBQVVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFNU09KK0k1dVdnbmpoN3ZpcWp0VFYwZWEvRk9XTU5NOGNIeGNUM2Q4aWFxUEc5V09qQ1c3N0l4S1ZHTlpoOHo5RHBlSGMwem1ZK1oxL0RWZE1ucjRybjY3ZUxrWGxPRGt5K1hRNDE2VTRjaTFPeXBPa1RUb2tUU3NKV0RNZW9YSmlSTktpQUFzQ1ZrRXI2a3dKN2Z2TDdrRTBnQUpKMlk0akFUb0NlM21DQkpPd01nRVpBa3dsVzEyUmJveVlib0RMZGdyQUdXN0l0MFpJeTZBT0xia1k2QUFRYW9pM1JPWFFpQlZJcTZscytSRGg3aEN2STZSeWRhcnM2dVhrY3ZWSzdpVkU5T0pOVklxYTRYNk9qWXlLc2p2ektKL250WjZIbU9IWmJsMkIxSlB5SzF5WHFTeDhnUG8zaExUMGNaS1V0MHJUNkhRU281Zmc4cFBTUmpOL1BGYk5xblQzVy9WSFVpYkVNZ0FvQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBUm1yUjViOFZ3VU1VYWZWdWtxM1BWU1BKL2lwTGdVdWFicE1tcWp5a2xTZmN3bGJaTXhCYk14cEZKTDFaMXRFcTRUbHJkcjFPcG9uYlM4aU8xOHgyc0RxRitaY1VZZVNYa1h0VWNiZGVoWWxiSnAwVjlTU2RtQzB6SHFSVHN5Qk1BeW5RVnFTVkdVckFTc0dpVmdtQTJYVUFUSUowQm5oN2lKbE8rbEdRVzRqRVNKR0c2QWlZYXNzSUFSNGU0NFNUVkFDc2czYkxaZENEVmdWeTZFU3dyQUdHNkhGMksrTHNCaHV3UmtSQ0ZlZFdjek85cjhqcVplUnpOVDFreW9ucHlOUzZtMitwVGxkWkdXNnI2MlZTZHY3ZnlQUTh5RWVUKzVPSzM5eUVPWlpqVis3UUh2L0FNT3R6OE14Y1ZmTHNtanJwVWM3d2VLV2toSkxoNDRweVM1WFhNNktkbXhESUFLQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRVpWVzU1ajhVcHJBcFUzY3F0K2g2aHF6aS9pZUNsNFpOOHFkazFVZUM0dm41Q0t0aHFtZ25VbVkxTEQ5ZG5aMEN0S3poNDN4VG85QjRaRFpiRWRyNCt1cENPMTJXTjJRaXJSSk96ZzlES2RNbVFKSlVCWVdGWlpDU29DWUlMSmZJT2RkQzVCY25aTktpbU11ZXhZblJvbUdxTU4wWlRvQUFETE5BQXpIcVRKcmJkWUNWaE9pVGRHNHhGcWdTYm9qS1Jvakl3Sk8yWWxMa0pNQnVtVnAyRzZLbTJ6S015NkVURGROQ2ZJbFdvU1ZvcjR1eGFVdFVEUnV6QkJ1ek1lb1NaRmFPVnJGVGE4enJtanI0WEJ0RlJQVHorZmVWZVJYUGt2VmZ3Slo5NU4rWlZkcCtwNkhtUS9hOXk3R3JLbTl2Umwyblc2WGNENlA0VlQwR0orY1V6Zk5MdzJEaHBZY1NwOEt0RzZiRUFBS0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdHNk9QK0k4Zkg0WmtTZFh6N25ZYXMwL0VzVHphV2VPSDFTVkltajVySlcyUjZ2dWkzS3YxODE2N2xjbFJpMk1LdktqMVdoaldGSG1kTEg5Y2xmVTlYcDQ4T0pFZHVuRTFmSHFSNkVvOVREVk5IQjNTU29tM1NLNXpVSTI3OWpTemFodExvL0t5NUdXNDJYbjN1MGw5eWF6eFR1K2h5WG5TVnAzdVUvcFVycTFmWm15YW05dTlEVVJ2Nmt2VWZwRVpiV2VlbHJIeE9LcE5mdk5GWDZibC9lZDlqY1RldGVvaHFJUlhNc3g2aE5PLzRua3YwN0xmMU4rNW1PdXlwcXBQL3dBbU1KM0k5Z3Mxa3ZqcStSNXZUYTJUdE5PdXFONkdvZlc2UkZkSmRkaUdmelg1bHZFam14elZ2M05uSGtUUktwTlg4VlB6c3NOZE8yV2hWbXBOMFk0aUhGMklOMGdTWXNsUGxaVkxLa3R1aFJreVhkR3JQS255ZC84QUFMY2RENHFwZGl1ZWRWdHVjek5xVWx0S242MGMvTHJYZHVUKzVjam5ianR5MU8vTDh6RDFhdW1xcnVlYnlhMmJmeXpmc1EvUzVYVnQrckt4enZXdlNTMUthMnAraktwNnVMbzRVTlhKOWVaWjhkdGI3b1l6WFlqcXViaTA2NTB6WWpsak5iSEN4NWs1cE50ZDdOekZuNFg1bVdLNTZiOHVnanlLOGVvV1R5MjgyVDYwUlpqb21VYW5IeDRwYmx6ZEdKOHZZd2VWMVMrZCtwcXI2Wkx1YnZpQ3JQa1htYUhuM1BSejhlYnI2ZEtOdlJxOHNMZGZNa2E2VmZZNlhnT0JaL0VNU2JyaGttL1FwTDZGcDFlR1BvWEZPSi9KWGtYR3hBQUNnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaVJUcUk4V0RJbFNiaTFiOUM2UlhPUEhGcnpKbytiZUlhZjRHcmxIbFROYWYwSitmOUR2L2kzRDhQVlk1cGJTWFB6T0N2b1M4a1l0Wm92L0FKRVY1czlYaVZZMGVZOE1qeDZ1Q1BVUlZJNGQvWGJ4VEluRG1ZeU9sZTRUb296TnRWdHQxWk1qcGJqVDFPYTVYL00xSnljdWFsdis3elp2UnhxMjluN0lzV0tMZm4vdWQveUxjM0xsanl5WHlSNFYwNmxUMG1wbmRxZjhqMEN4cXRsUmFvcHIwNmk5WTMrT1BNVDBPZXJwL3dBU3A2Yk9tN2cvc2VydUhXbDdGR1hOZ3F1T1A4UjcwL2pqekt4WkZ6NHZjeW9QdXZWSFl5NWNENUp2MGl6VWVUQmZQaDlWUnVzdk9LOERmRmJTMjhqb3dkd2l1NXE0MUdXOFduNkYwT1JscS9Wc3diZTNUYitORy9wOG0xcGN6blEyTnpDYzNTTjdHN1pjVlE1bGlkOUtKNWFqT1czSXJsTFlzeU9rYW1YSjJOdHdWWnBmYzBYa3B1L1JGbWFSclRkMGF5cXRWUGlmRFRYZmthT1NNcFBaTTNtcVpWOFhFblNibS84QXRWblNWeDY1YUx4VGZRbEhTNXBPbEIvWTZXTE5qLzhBMFQvK2h2NHRWaVMrYU1vLytEUnZ0VDBqaC84QXArZEsrdmtpYTBlYUszVDM5enY0OCtueS9UT0w3V1dwUWZMWXozcC9ISG5QZ1pzZE9XS1hEMWJwRW9wcDdjU3IvdVIzWlFpM2Jvb25wNGMrR0s5bUoxclp3MGNXWGZobGE5VG9ZWGNMTlhKaVNkSjgraFpwbTQvSkpQZmRVR050T2czWTZHRG02UFArSy84QXlaSE1xMi9VNjNqTUt5OFYzc2N4OVdlam40ODNYMHJmMVI2ZjhJWVZLV1RJNCtYTkhtVzdtZTcvQUF4cHZoK0hSYjJjbmYzS1M3S1ZLa1dFVTZSSXRBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBWWFzeUFJVCtTTGI2SE56NjdOeFhpcU1PbHJtYlBpR1Q0ZWxiODJjeU9TR1dDbENTa3V4eDc2c3IwZUxpZFM2MFBIc3NkZG8wNXI0ZWJFbTFUMmtuNUhsM0hkYjlIL0FKK1o3UFU2YU9YRkpQeVBMNjdUL0F5OEN1bHVqT2VyYWVUaWMvc1crRDQrUFUzZkk5RWxaeFBBMG01Ti93Q2JuZFRzanY2cngvRVdxWFBtYTg0cTlxL2diTStXMWU1Qnc5SDZJaDBVbVU2SlRUU1JvNmpMUDZNZk45UUxwNnlLbndZMXh6N01weWFpVmZyY3FoL3Q1L2RtcG15clRZNmd0L3paSFM2RE5yUG4xRXBKU2YwblNSRjdxeWZpZWtqK3c1di9BTG5aaVBqYXFUaGhpNHBidXVScStKNlJZTlY4T01WR0tpdVJTLzBMSDRmaytKeHZXUElsQ25VWXgydHYzczZlc2NyM2NiNjhaeHVWVHdwTGsxUmZETHA5VWxTU09abWpwbjhONlNFNHhlTk9jWk82bjFwK1I2SHd6dzNGcS9Dc2JtcW1yNFpybWw2bVhtYWM5V3h6NWFCUWZGamRlaE9MbGpmelJiN28ySlk4dW0rWEo4MFYxUmRpaEhKaWRxMUpjMTBPZHVQUlBpbUh6TFkyOERvMWNNZmh6NGZNMm9jeUd5NDZHSXY2R3RpbDJMazZKNVZacUdXUGMwTXB1WlpiY2pReTh4OUdya1ZtcTNicEpzMjgwYmhTNms4ZW5yRnMwNVM3Y2tkSWh6bGhsbWRkUEx6TFhERHA0Zk8wcTZJc25jNVBIaHR2elJ2YWZ3ZUtqOFhOTDRra3JTYTJUS2sycDY2bk1jYVhpMkRFK0dPUDNEOGF5UWJTd3UxdjlMNWVaek02YmM1dnJKdjh6YXlZZkN2L0FGRFU0c1diTzlPc1VuaXlOcTVUU3RYdHliMk9zNGxjZXU3R3l2R05QbWFXWEJGeTg2TDhlU0VsZURPNDltN1RPTmh4L3JjU3BPNUxablIxZmhQd244VFRTY1crYVpsNTA5NjNWclhpbHc1MGt1azQ4amFXUlNWeDNPRGgxVTR5ZUhQRlB0UnVhVnZEUFp1VUg5UFk1Mk84dXVrbGZYOGtRVUtlMWV5TGNhdEV1RHlPYldJUjI4dllkU1NWY3lMZG9EaytOUitXTXZ5T00xYS96c2QzeGRYcC9jNFNWSnIwTy9IeDUrL3F6UllKYWpWUXh3VnR2a2ZRY1dYQm9NTU1MbGMwdm9qelg5RHluaEdqellzNnpSbHd1blZIY3g0VkZkZCtyM2JIWGVYOFZ6NHZhZnJxNmZYNHNzdUZweGsrU2ZVMjA3T0RrbEhHazVTU3ZsWjJOTE41dFBDVW52VzV2SGQ2cWZKNDV6K3J3QWRYRUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDTGRnYW5pTWVMU3YxUjUzRGVuMUUydVRlNlBSNi93RCtOLzVJNEUxV1daNWUvcjIvK2Y4QXEzb1M0NEpubmZ4REhoMUdOcnF1WHVkeU40L2M0MzRpZno0NWRtdnpIRnluaytKZUJxc1RrZFpLem1lRUt0TjduVWh5TTYrczQrSkpXeXhLekVJN0tOOGkzaDI1a3FhbVNGWHZaejgwTFRTNm5VeXczMktuaVZNRGlyUnFVcmt1UDFPam8xOE4wNVhYY2s4ZEVvZVIwblZMSllyOFcwSDZiaVdUQkw5YkJjbnRaNTZlSnhiamxoS00xMGFQU09Na3FWMzZzS2JiK2xjcjMzTjF6OUhIMFBoMlRWWkkwbkRIMW0xUjZwWjhHazAwY1dKY1NpcTJab1BKSzNzbjcyUjR1MjQ5cXFlT0o1dFRITXFuaGsvSjN5STRGUDQ3a3NiaEIvVmI1bHVOYjh2dWkzSms0WU5vbTdWVEkxOGtGeG93TjZ0aUt0b2hzamJ3dWtYZmYzS01YSXRqMXNLUXlPelR5OHpieUswYWt0MzZBVnRjVTBpZXFjM2lyR25YV256UkJ1dHpaeFpWSmNsN2hNdU5iQm14Nlp0ZkNhVXUxTyt4MGRQcnNVMGxKT04rWnJaMm4wTmYxaXZkYkhTV3hQck9xNS9qSGhydzZpV2JCOCtHZSt5NUhKK0ZqdTBqMURsMlgyRW1rMDNqaTIveU45a2VqbStFZUhQSm1XcHpwd2hENlU5bTJkWFU1SXVGS3ZWa1BpT1N0UmUzbHYwSEQ1ald6aWE1bWJTeHR1bnYrOGkzQmpVVWtianhwTWxERnNjN2RXWW9vMjBxS1lSM05tS3BHTlY4TzNNcHlKSkd4S1BMY3FueURITDhTVjZabkUwOE9QVlk0K2MwZC94SlhwcEhGMGpyWFluNVNzNjgvSEhyK3oxdUZLT05VdVpIUG5XT2tsYmZKRVhrYVJCd3VTY21jbm81YWs0U3k2bU01eWJ2cDBSNmZ3OWY5TlhrMmpqdUNqbGgzT3hvbmVKLzdqcDQ3bFIvNkp2TGJBQjZYaUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF4OVFib3cxUUdwNGgvb1JYbkwrVE9LbGVkcnVkeldxOEYrVE9QampXb216emVTWlh0OEYvNFNuRzQxZk00M2pzZjFlUC9jMStSMmNqcEhHOFRjOGlhbDA1RTgvVzl6WXY4SFZhUkx1em9ST2Y0US8xSEQ1SFNIWDFQSHhZblJOT2lwT3l3bDFDSFZsaVRhM0l0V0UyWXFlTk5ENFhreTF4YlFTcEJpbjRUOHg4Sitac3g2bVdyQTBKWW50dTM2bVVramFuanVqQ3hwTzJYSml0VmNQRHpJU3VUM05od3Q4eXJJN1JOdW1LMnJhTTlUTVZlNWd4cll4dXliVklxeHVrV0JVUXk4a2FrbGJOckk3TmJyWVpickRWaEpyZEZnakhudUdXYXlsYXNod2I3R3hGVnVnNHBsYXo0MTQ0dWU3WG9TK0cvUDhBSTJJeDRic21rbVpaaHJWVUd1VE1mRGZtYkx4MTBIQ3pFdFRnZDhpNkVkK1pZMVFUb0RIVW4wTU4wSmRETGNYSXhQa2E3NmxybHM5aXVUdG1zclQxcXZUeVhZNGVpVjY3R3U1MzlUL3BTOURoYVJjR3BXUi9zZFBNNno1WEh2OEF0SHBkK0lzeUw1RzF0UnI2WE44U0NYRFZtNGxjV2NuZVRHc244VFVRUjJ0Ri9wUy8zZnlPTHBvVm5iczd1a1ZhZUxmN1RiT25qbTF6L3dEUmYrWTJRQWVsNGdBQUFBQUFBQUFBQUFBQUFBQUFERFZtUUJqaTdFU1VpSUVNeXZETkx5T1BHUDZ4dStaMnppM3d5a24wZEhIeVI2ZkJmeXE5UkxzYzNOaVdYNVdibVoyeU9ISGJkK1J3ZWkvR2o0VkZ4NDR0VjUrcTVuVGJzNVhoK1g5ZFB2S1g4VHFGZGZYTG40c1RwbHFkbEVPWmVuWkxyTHF4S2pJWE5Fd0lHRTdMQjFBR0cydWFNZ014QWp3c253OXpMZElOa3hYTGthK1JXMGJMVm8xNTh3SzV1cUlRNW1KTzJXWTFUQXV4Y2lmUXdsU0p5NkFVVDhqWDZtemtkN212dzdjd0pwMGlmVTEwN0xjYnZjRFlqem9ud2VaQ0N0bHFkTUFFNk04UGNsN0wzRE1RU3NiazlpUEQzRFpNUTRSdExsc1Q0ZTQ0ZTRGYlZGY3ZsZnFXejVsV1hrZ0lOMlU4WFlzbDBLZ3lxdFQvQUtNam42UFQ4ZUtXUmMrSm8zTmJOTEM3WE14NEVsTFRUaS8zcis1ZWZqbDdmOUx0SkhnYVIwa3VKR21sVWpkd2NtUTdLTVVibTRycTZPNUZjTVVsMFZITDBHUDRtdHY5M2Y4QXo3blZPL2puNDh2bTYyNGtsUmtHRXFPemd5QUFBQUFBQUFBQUFBQUFBQUFBQkZxaVJBRExWR0FBQnljOGYxdVRmOXRuV09YcW8vOEFWWkZmTm5MeS9IYncvWE9uTGZrWFFmREJ5OGtWWjhNbEszMU1hZk91UGdsdGUxbkNYSHF2eHg4VWxEVnlsemp4T3Z1ZG1QTTVPcGh3NnZKVWxUZHF2dWRiRytLS2ZtVzRjM0ZrUzdHNzNLazdaT0twSE4ybFhSNms0OVNycVdKMEZKTjBHck1SNmtnTXAwWUFBakhxU0hRdzFZR09IdWFtbytVMnNqcEdsa2ZIT3dLOFVWS1RzdjZqR3VIa1c0MG05d1M2ekNOK2Z1aXhxa1c0TWFaYjhOVlY4eXNMY2MvSXJacS9TL1U2ZWZFb3BicCtocXpqME1zd2FVbFRMTU11d3l4MjVrWXJoa241R0RkeHEyV2tNYnRFK29FektkRmFkSW1BTXQyaUtkdXVRYXNDTW0zc2hLWExZRVo4Z0l5bHkyZnNWVDVGc3ZsWG1VdDJ3VzRxeUttUUpUNWtKQkRTMTgwb3RWME5ud0pWcHBjdDMwOVdhSGlFdUtXMzdKME5GTll2RDhiNU5yK2JPbitPY205TnFmMXQrWmZoZHBtakNYSHlOeVB5UWJaemQyNzRYR3A1cDkxRTZIRjJOTHd4VnBiZldUWnVKMGVyajQ4UGsvdFVnWVRzeVdnQUFBQUFBQUFBQUdKQktoeGRoRURJTUoyWkF3M1Fib3lZYm9ERGRtRExkbUFBQUFITThRaTNudmxhT21hbmlPUGl4S2E1d2QrM1VudWJIVHhXVHB5M2tsTDVKZGVwcVp0TyttNXR0TnRkaXpwUjVIdHRlZjFQRittT2NselNPbHAzY0lwOURVOFFTZXFpdDkxMTlpM1NPb3liYTh6cC9qejM2MzQ4aXpHclRLTUR1RnMyVTZWbk4xWlR0c3NUc3JTcGxpZGdaYnNrbmFJODAweVVWU0MyUU9vQUFGY200Z1U1Y3ZrVXhkYkVzanV6VWViaFZvRGJicmtSeFpHc3FPSnF2RTlRcDhPSmN1cklhVHhiTThxV2JFbkh6UzVGZXRacjFrTXFvc2psNW5MeDZtTGduWlA0NWt1TmJlZkpmTTBYa2JrVjZuVkxnZDdIR2w0dzRUYWpoYlhtMkpMVSswZDdvUjRkbnVjM1MrS2ZIZFNnNHZ1emVlYTBoWmlwZGJXbm50VGZJMmtyT2ZnZFNUT2hqZG93U2FzeHc3OHpMYlhTeUxkZ1RCR1BVdzNhQU4yaURkbVpkQ0NWTmhsSjh5bkwvRXRidDE1RmJkaEtpU3B2MUtwOGk2ZlUxTTg2ZzFSc21zdHh6dGZQaW1rdTUwTk91UEhEWjdSUzNPZm4rZlBGUGs1Sy93QWp2NG94V05VdlVycVl6eGZVc0tTVGJFNWZFZmtrUm1yb3YwOFBpNVk0K2pkdjBSTW0xMHRrbTExZExEZzAyTmRyTFFEMXlaSGd0MnBmVVpNY1hZeWF3QUFBQUFBQUFBQUVVNk1BQVNpSW1HN01BWlRvd1pTc3dBQUFBQUFERFZxbVpBSEZTdEdPaGJxOFR3Wkc2Znc1TzA2NWRpbzhuVzY5M05uVWNyeEZWbHh6ZmRKRU5MYWpUOHEvcVgrSnhyQXBiZkxKT3E1bWxwc2pTZm1WUGpuMStWMWNNdGs1YzN1YkNkbzBNV1RpeVNmZFJYZmEvd0NadlI2a1dZMlhGa1hleGIxS29kR1Roek1XbEYwelBEM01HWlhhb0tpWFV5MVJnQnJEVmtaUjVia20waUxkZ1VUaXFPYnFZT0R2ZHArUzVIV2FvMThzVkwyQTVtUEhqY1duRmZZWTlOanh5ZExudVkxYzhlRjhrbmZTVzVyNE5WOHpjbjZGUkhYVGJ5UStIOUxJUXp1dVRObU9YNUhhWHVaV09MaTV1blJ0bXNsMXI4SHhWOHlLcGFYSEthNUwxTHMrb1VGYVZyc2pUelpOclR0Tk4rbitibXM5bXo4SEV0NHBLdk5sbUtQRTc2R3JvcytPU1NuQk8rck92aGlwcE5OYitSRm1Pa3NxZUxGc2JXTjBpcUpjWTFKdWlKbmk3R0U2QVNkb1RWMXZRRGFZRkxqYmJ2bVRidEVrN2JLcGRQdUUyNndSbHpSbE8yOW1xOHlNNWJXR0tjcjRVMmMzVVRmSGZtbWpienpWY05jMy9jME03cWFhNjh2VXFJdDFYZ1gvQUZuRFYzVjdlaDNvY2prNlNQRnExSnRLbDFPdHdxdG1LcmliRS9xTi93QU14S01IbHQ4VGRlaHorYlNTYmI1SkhaMHVMNE9DTUcwMnVkRmVPYlUrYTV5dEFCNlhrWlRvejlSRWxFQkV5WVNveUFBQUFBQUFBQkFBQUFBQUFBQUFBQUFBQUFFWndVNHVNa21uMFp6OC9oamIvVVphOG95Vi9tZElFM21WVTZzK09CclBDcHgwdVRKbXpyNUl1U1VWMTl6aVJWUlY3N1gvQUNQWjYyQ3lhWEpHU3UwK2xuaXNhY1pOTnJpWE90MXpPZDVpK2VyVzVnZTkvd0RlNzl6ZXhTY25mb2MxdWtrdHJmRi9JMjhPVDlhbFhUcC9uK2JFT2pvSjJXOURWeDVPTnRMOW5yM0xwU3RiZGRqbXZsWVpqMUlwMnJKZlUvUUxseEloSjN1RzdLc2s2U3RNS1Q0aVB4WTNTM1pxNWN0cHhpMDMxYTVJMW5xRkZidHJuc21HYTM4bVJKZks3T2ZxTTlPcWV5NjhpdlBxRzRXMm5TMlNmWCtacFp1SnhmRks1Tlc5K1JzbXA2NmEycHkvRXVXNmowNldOTzkzSjcxMDdrZURqYTUxWGwxTjdCajRJV2szYjJzNjI0NFNKUm53UWxLVDY3Rmp6eWpnVFhWbWhtemNXVlFqdkhrbjVtenFIOHNJTFpLUDVrcXRWT1Z6YzJrNFNYSmxVL2xrcFJUNEpycDU4bi9JbmpYeGNiZzA0MCtmY3JsRjAwNHZieUtreE5xV0NveWJ1dmZtZFBCbWNKVnpUNWI4amtSdFNqdnlleHRRa21xZm85eWVwcXVianZReVhkdFh0ejZseWxhMk9IaDFLeHJhVnE5N051R3FmRmNkNjVvaXpIYWRhNlhyYTlUS2RHcERVeG5za3ZSbXhCcVN0RWVxcGRYRFlnblRyekpOV1VFblRLb3k0cjdNdGtyMktmTmhDTTVLTEtjazZWTmY1Um1jcW5UTlhMTnViajV2bjdNMlRXVzRxeXkrYU8zSjJhdTA0OEwydFdpK2J0eFZWdS81bXEzVWZhdnlMY3E2M2hQaG42VnA1NS9pUEhQanBYdXE3bzZFUEN0VXRwWmNmdGY5QzM4T3g0ZkNvUGhjZUp1VzUwenJPSllqK1RxZkd0cE5IRFRLMCtPZjd6TmtBdVNTT2R0dDJnQU5ZeWxZU3NKMEU2QWtBWVNvRElBQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUNHUmNVR2tlS3pwd3pUYmlveDQ3U1hsWjdkcTFSNVg4UjQvaGF0VGorMnQ3N05mMkk3K0s1djYxcStKR01YUzRVbDk2TmxPbmQ5TDNmc2pSaEpWR0QzdHQrdjhBWTJzYTQwbkp2YnArZGY1NW5GMmkrT1I4TVZHMTFiZmYrWnR3cmhpa3ZRNWtNMVcycnRyMy93QS9tYldETmVTSzdjekxOYTZFZXBLTHBJb2hsNG50eTZQeko5Q0hWakprcDdjLzRHbnFzNmlsYmZzWFpNbFBoUno5VDg4WEsxY3VTNXQ5MzI3R3lhZGRLWjVWSnBKdDlsdDdKRlU1VGswbjBkN2JMa1hZRndyaFZ0M3pmOWkzTGdxS2lwTjhYWC9PaFRtcWp6cC84bFVvUFBsazVTK1hxcTZHL3BjVGtycmhqVlJSbDZlRVpOdXQvd0MvOVROVjZ0UERpY29XMDdlMjM1bTFQU1NjWTh1UmZESmp4UDVJcCtkaU9kcHZibjNHcm5PT2JxdkRaUXpxVVVxWFQySVQwODhrSFNmRkYvVCs4ZHlHWEhsVlM1azFwNFJ0bEovampqYWJSNUtVMjNiZTZYWHlKWjlQeHpUU3EzVE9uUFU0NFVsYjlDbDVyZkplNU9uOGNjZGFSd200TnBiV20vdi9BRG9qa3c4SzRvdHJ0WjJrOGVTVnkyc2prMDNHMVh6YjNkOWhwZU1jYUNsMVRUNjE5eTJFZDBycVhSNzdHM0xUU2l1VjM4dCtSSDlIalNwTDFXdzF6Vi9GbEYxSnk5VjBmcWJ1RE0xdTFmZEd0bHdiV211WFJXbWlHSnlqSk9xa3VUaTN2L1UyelZTNDdXT1VaTGJlaTFPelF3WmQzYVczVmREYlRzaDB0MVkzUnF6bnU1THFqT1RKd3BYdW4xS01zdUZyL093VGJpRThsMDlrNmY4QW41ZnhLYlV0VDFTNGR0akxWcGROM1h1Um0wb3huU3VUditaY21PYXBOdHlYazNYMk5iSGplVE5EQ3BKY2NxdDlIL2lMSk51ZkZ1bmZYM05qd21DbDR2ajRseGNLazl2U3Y1bGMvVTlYSTliZ3hyRmlXT05KUjVKZEN3d2xTNmV5TW5lVEhBQUJvQUFBU1RzaVRBR0U3SWtrN0FKVVpNSlVaQUFBQUFBSUFBQUFBQUFBQUFBQUFBQUFBQUFjYjhSNmZpMDBjaWpiVXF0TGRLanNsT3J4ckpwNXhhNlArQlBYeHN1VjRiVHp1YTI2ZjhtM0tUaW9wYzVQK3Y4QVkxc2tJNGRma3h4dEsya3BjeTZFMUpxMXZYODJ6alhhVk5ZNVBFNU5wTGk4OStmL0FBWFNxR1dTMitsTDJ0bUp5cjZmTk5lcERJMVBKcy9sajFiNW1LYitQSXJlMUpMWkpGM0UrRFkwTVVtcjJiYjZMb2JuMVIvbVJaaStWTWx4YnQ3SjhseWI3bEVzYnlOdVNhYmRKVWJVM2JTVktsc2wwS3N6K2xkRjl6RzJhakdLaXVpOUVWNWRianhOSnRLOXR5NTRYbGpVU1dIUTQ0VzVKU2srclFhMDM0bmlTcUwreUsvMHZKTytESE5tL3dEbzhMdFJTOURDeHBYUzVsNUZ5T2VzbW9hLzBIOXlhMUdTUDE0SnIwcG0rc2U1Tlk3VDJYdWFySE1sckk5VlhzMFpXdnh5ZTgveU9uOEdOUDVWOWlDMCtQNGt2bFhUcDNESk5jOWFwWDh1UEpMdW9tZmo1cGNzRWxUNnRIVGVHQ1gwcm41R0pSMjVKZWdKTmNsejFLYi9BRkQ1ZEpGa05Ya2g5ZU9jZTdYOURkY2QvY2h3dWdXWW8vOEFVb1Y5YSs1REZxb1NkS21iY2RPbTkwbjZvdGxwY2VSYkpSa3VUU0pxTE1WTi9GeDdKSXB5Umo4UGozanYwZkl0aEJ3aFVsdi9BQktuUGduUDVWdlQzSlRacTNUejQzVXF2bzEvSm01TEkwbWpReHJobFN0d2tyU1hRMlcyK2UzOVExbWNyYTM2MmFtYVh5T1ZjdjRrbmsyNTc4NHRNclU0dDhGSnhtK1JVUmJvMytxVWx2OEFLcTllWlZkd2pGK2RmNTlpUzNsR1BrcS9pVXlud3lwcGJPLzgreFNVSk80YmJXcnYzL3VkdjhNYWJoV1hVeitxVFVWOWt6Z1N2SldPUDdjdUZmYzloNFBwSmFiUktNK3J2a2RPWmxjdXIrT2dBRHE1Z0FBQUFBUytvaUFBTXBXWmtCa3czUkVsRURKaE96SUFBQUNBQUFBQUFBQUFBQUFBQUFBQUFBR0dyWE9qSUE4ditJOUo4SExIVkp2ZDF3OU9YOUVjaUV1R1VaTHkvbWV4OFQwc00yQ2JtMitHRHIxUEV2aXhaZmh5cmFTYTlFY3V2cnB6ZngwWi9MQytiNVgzRXZsVWE1Y3Y4KzVSSDVjUEZiMzMzOXl4elNjYmJVVjBPYnJLMk1YeThXNmJybS84OURjdzBsWGthRUw0SEo5V3krR1NvUm4rMDFzVFZTNHV5U1VZUy9OakZpNG4yTmViVGZCZkpHM2hhVWZxZHRmVDVFcWwxZkdFWXR0TG1xRFZNa1JrN3RvbjJkSkZiVkVrMCtoRnV5RHlVVVc0bTFSaTF0dWEvd0FkMjl1VzVSK2tUU2Q3YldHYTZEeVJYTmhaRS9zY09Xc25QSzFkYmx2eDN3L05iZFhzWEppUGQxdml4NlU5dzFmTTVXanpPVGFac3JQTk9yTXFwMXJiNEVTTlNHVnpYemVaYXNqYnJ5SmJxMXF5VU9SRnF5VU9WRSt6V1ZDN2QwK3V4b1o0Y01tNzVybDVIUmJUTlBPMHVyNTllaHN1cHN4WGl1TGJmM015eVdxWGwvVkZTbHd2R3VYREpKcjNveE5KMGwzWDVtc1ExRDRad2xYWm12QnluR0t0TnFWcHYxL3VYWlpwUWtxN0wxNWxhcUtqVy9Gdkh2YWY4Nk9qa3N6MnB3YTlQNS93VE5YUEswMzVyaGI3L3dERFJibXkzSysrMWVmL0FBYzdOTjJsMXQvME5pYlhUOEh3NU0rcmhseHBWamR5N005dWNqOE42YU9IdzJNbFRjOTMvbjMrNTF6dHpIS2dBS1NBQUFBQUFBQUFBQ1NkbVNCSnF3TWd3M1Fib0EzUmt3bFJrQ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUNHVlhpbDZOSGl2RmRPMm81SXQ4WFk5dFBrZWJ5UXZuWHVqajNNc2RPSnV1QThsNGVEZTR5NFhYc1h4ZTZWcmJiYmZkLzhtdnJJL291cmJTK1dXNlhjbENWdUs2MEZ1cGpkUVNXL1Y5aVVQcVVkbStuWW9ubC9WMm9wdTY1RXNWWTF4YnljdHR5RkxzN3BjVHBWM3EzNWtzR1JKZkxlUnZyMFpWbVZwT1RyclZiL1lwK1BLTFVZcVRmMis1bG1qcXJJNU9wVjdGbkdqUXdwdEp0Yi93TmxPNWIvWTUyYTdTclNNbzhTSng3azBySVUwNVkrRk0xY21KcEpOblRualZiTW9saWNZdVNyYlpXdVJjdXN4eXA0Nm54TmNydjhqRTFVLzhBeC9tamRsZ2xLTFZQZlpiWDl5SDZOTlQzMzVsNmk4WXBoR25Gcm9remNTV1RmcVY0c0RUV3pWSkxsWnVZY1VlRk5JaTNGU0lReGNMc3pIWnZzWDhQY2lsU2JWUDNJVXh4SWp4cU52b3lFNWIraFZ4YmJKTHpvMlRTM0Y2ZHA3cCtsR3JubTNLcFJ0VnpxeUdUSnRVZDc2ZVpUankvRVVrMDFUNW5TVFhPM0ZqbDhpVDZkZk15NWRhNlhYZnIvbnFWOGRxVVpiTjgxNWM5ekVQbWRTdE5iV3V2K2JGU1k1czVYUzQzMVNmdjFOWnphZzl2b2QvbWtUeTJvY05wMnR1OUdxcDFCWGYxTnR0ZWFOa1pUVVpLaHQ1My9uMi9NdThNd3JQT2VTY1cxSlVuMk5URmlscThpakcxanY2dnNkN1RZSTRjRVl4Ym9xMzhaSTlINGRENGVpeFJiYitWUGMyU3ZUcmh3WTE1Ulg4Q3c2OC9IRzNhQUFwZ0FBQUFBQUFBQUFCTGk3RVFCbHV6S2RrUUJMNlEzUmhxaVFFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQTg2L3Jrdkp0SG9KZERnNVA5V2YrNS94T1hrL3gxOGYxei9FZFA4QXBHTGx1dGtjS011REs0dEoyNnZ6WGtlcG5IaWhKWFd4d3ZFZEl1RDRzS1ZkT1YzdWMrYnF1b2xISXQwOWx6NWwvd0FYaGh3cEpUbHo3SEp4WkhHUzQ5Nlpjczh1ZDgzZnY1bFdheVYwRk5jMjB2VWpqeUp5ZXkzNkpXelMrTDgvT1h1VzRadFpWd3VqTVZycHd6Y0Z4M2xrZms3SndrM0trbjgzVnMxOFNqaXg4Y3BPNWJXK3BKNXVKSkpMbi9sOWliTlhManFZOGtZdzNmdjVsa3NpakRpZjJPWENmeEY4c3BTZld1ditlcEw0bnhXcXZndW9wUG0vVW16RlM2MytKMm02NHZKUGtSbExaYmJjMTM3bExrM0JMejJTOC83Y3kzRTJwTktXNzJjcTMySnMxVXVMc2VIZm0yK3RtSXhWV3ZQK1pLTi9FZU5ONzg1WHVZNHJXeWUrKy9vYXFYVVhqU1cvbTFaQ0xjWkpiU1Q1T3l5ZVZSamE2dmJtVjU0MnBaSU4zSGN5elMzRm0xYzB2SnhLVzdmRHlseVpWa3l0dzRuRnJhMmFuNlE2YmQ4UzVQOEF6L1B1YWpycGZtbHh3YXR4eVJmUTE0emNuVXFhOHJyL0FCbVp5NDhmSFNmb2EzL2RjbHZXNVdJdDFacUVrK1Vtbit5MmEzMDAwM0tMM1hVdHl5akpLMTh5K2xyYWpUZVI3MCt1NVVpYlcxa2tveVR2bi9Eb1F5WmVKeGV5cDgwYStUSjh1NmROOURDbW93bGUvbjk5emNUYW5seThTU2V6aTkwVTVKUE8rREdtNU45UFlyeVRjcHRyZVRWUHVkVHczU3hoampON3lrcmU1WFg1RG45cS93QVAweXdZbGZ1YjVoS2pQVTQyN1hUK3Nlamh0Qkx5SkdFcU1ucmt4NUFBR2dBQUFBQUFBQUFBQUFBQUFES1ZoT2pBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU1OMGNESi9xNVA5ei9pZDZYUTRNLzllZjhBdWY4QUU1ZVQ0NitMNmowWnFaOERsQ21yMk4zaElaSVgxL0k0UzQ3OVBPYTdSY0tlU0d6VzdkR21zdFE0WkwwM1BSNTlQeEtyVC9NNCt2MFVvWE9OTHNkdWVwOGNiR3ZqblNiVDY3RnVMSktONzE1czBvNUd1cjlpL0ZQaWFmN3ZRM0V5dWg4WGJpZTc1ZTVOZk1sRjdPVDNiNm1uam53emp4Mm4zTmw1WXBYczdYWC9BRHVUWXR0NDU4YnVWUnhyZXJKWTVVb1NVYXBiUlM4LytmeU5SejJ1U1c3L0FMRnVmSlUzTGJrMi93Q0JLcFc5aWZ6UmtudW9xdDc1L3dEQ0pSek5aNXEyNHFxdDk3L29hbUYybmJYbi9uOFN1TXVMVXRyYmNuRlM0NjBjdkEzMTNmMk12TStLMDYzL0FLR2k1OFRudHM1TCtoV3NzcEp4V3pxdnpmOEFRWTMyYjN4T1NyYTdKS2JUbW12NzhpaHpUcmJuUXk1S2JoZlBiK1JMYldNc2xQRTA1VnQvWTU3bXJmRjAzOWkvV1QrSHUxeWRtc3VhbDVXaTVFZGRJd3lOTnZwMS9tVGI0cWR1cktzYjRZTlV0dlAySzV5VUc0dnpSV0lsU3lOSnVMcjJOT1VsRkxmWjdtTTJSdE52NnBWdVZKdFI4K3BjaWRXdVNjVmJiNys2L3FRK0xMNGJoRk83NkVKUzVLUFQvUDVIUzhPMFRUVTVVNU5iWDBOdGtqSk5xZmgraXFVWjVMY3E2blh4UTRVa24rUmpIajM4aTdHcVRPSFhWdGR1ZWZ4bmg3bUZ6WHFpWkJjMTZvbFhUMGlkbVRDVkdUMnZHQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ011aHdjbit0ay8zUCtKM3BkRGc1ZjlmTC92ZjhUbDVmanI0dnFRbkhlN013TXQwZWQ2VkVsZHJrYStmRXB3NFhmZWxadXlqM0s1UjVibXk0aXpIbXRkb2VCdHdwOWtjOXhsamUreDZyTXJlL29jcldhUGlYRkZYek8zUFg0NVhtT2ZISmRWL25Vc2prZGNseVMzOWlsNHBRWHpjL0lwK0pLTGRQbVZacUhVZVpOUnZvN3I4eVc4c2R2ZHZkdjMvdWFFYzNCR3VlekxzZWE0TmVSbGl0Ynl5VytGK1JzWTNUWHQvUTVieVhTdnFXdlVOVlcyeEdLbGJHUFVjVTZyb3V2b1dTbHNtdW14bzRKY0Z2MEpyTW1xNWI3L21NYnJmamtjbTIxMFRyN0dKNXJ5cU85OTJhVTlUVFRpK2xrSjZocDMxVGRqRFcxcXN0dEx1eW5GUGRSUzVyL3dEcWF6eXVWYmtmaTd5cjBSc2liY2JHVE5VVWw1Ykd0bW54UDBYOGluTm10b3JTdFhMa1ZpYmRUNHVLUHVRUzRuWG1TV055ZEpXZFBUYUpMR205bnpOdGtKTlIwT2k0SHhaSzR2SjlEc1lvSmNrbGZrWXhZcVhPdXlMb3gyZGVWbkhycTY2eVpFOGEzOGk3aDdrWVIzNWsraUlkSk1RSWYxTFo4aWlYTUZtdlN4NmtpdkhraE9IRmpsR2NYMWk3UllleVY0Z0FHZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBTU4wUXlaNFlvOFdTVVlyemJvQ3c1bmlQanVoOE90WmNpbE5mc1IzZjlqamZpRDhRT0hGcGRITnJwUEpGL2tqeVVjZVRWWjFDTnVUOHcyVFhvdFIrS2RkcnM2d2FER3NYRTZUZTcvc2RQR3BLQ1U1Y1V1cjgzNW1qNGJvWWFQRmNkNXZuSm8zNE9yOVR6ZGRTMzhkdWVieXZTcEUrSHVZU3N5bFhJNXU2UFVqd2JOY2k1Syt4Z0RUbno1V2FtU0RXMnpYYzZMVlBia1V6aFRwcTdObHhGbU9YcWRQR1dOMnR1elp5OVJwWEdlMGZ0MDlUMFhBcmE4aXI0U1MyaXZzWE9xakhsM0JyN0VVcFFiU2ZQbWVneWVIeG5jazI3NTJhV1RRdUwrV3ZkbHpxSTlhNThwT3JmWCtpTThiczJjMm1uQkpVK1pXazBuYWQxYjI2RzZ5ekVYa2Qrci9BSmo0bSsxNytiSnZodjJIRXIvenlHbG1LbmtiYjJhMjZtT0psMXFtdHZkRUU2VXY4Nkd5c1V1VHAwakNUYmQ5UzZNSjVOb1JiTHNXaHk1V25KcU5ycUxaR3lXdE5RcCtaczRkTlBMdzdVdTV2WXZEbGpmRlRmcWpiaGhqdndwTHJzdVpONlZPYTF0UHBJNG54cC9OZmt6ZGhIYnk5Q3lFSmNrbDN2OEFzVzQ4YVQ2KzJ4enRxNURIQ2s3ZDl6WWpIYm1ZeHdwdmw3SXZoQ3Jwa0w1UmpIWjc4elAwazRWVFNEVkpCU21mTW9uMU5pU3Zjb3lLa0I1TEZxOVo0WHFaUnhaWjQ1UmU2VDJaN0R3TDhUUTFzbHA5VkZZOHorbVMyakwrak9WNGpvNGF2RzNLbE5jcEpIbjR4bnBzemkyMUtQVkhwblU2anlkY1dWOVlCNURRL2l1Y01NVnFjWHhLVmNVWlUyZGZRL2lQdy9WeVVGa2VPYi9aeUt2ejVGeW9kZ0dFN1ZveWFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBR3RxOWRwOUhpK0pueVJoSHBiNWdiSnE2M3hEVDZIRjhUVVRVVTlrdXJmWTh2NGwrTE11Unl4NkhHc2EvZmx1MzdkRHorcTFXYlV6VTgrU1dTZm5KZ2QzeEw4VmFuTFBnMFg2aUg3ejNremlUejVjODNQTmtsa24rOUoyYXlkTDFKTjBCbVQ0anJlQmFkY0R5UGUyY1p1ejBmZ2Evd0NpajZuTHkvSFR4emE2UFJtY0t0REk2ZzJNQ3VOZVI1M282YlVPUmNWUjUyV3JwMkNnZERQRHZaWnc3Y3dOZHFrYStSVnQxNkc3a2hhNWxNNGJjZ05PVUxhdTFXMnpJY0M3K3JOaVN2YXpIRDNDTE1hN2hhcFcvYmtSeVJkV3ZtWG5ac3ZHcitWY3ZzekVvWFRWcC9ZSmF2NlBHZDJuM3NxbG9FMzh0TyticzM0cHAwbHhlai9reTV4Y25zbjdObzJYRityalM4THh1dGwzSy84QTBtTXVTVzFMYzc2d3AvVHQ1azFDbC84QTZ2OEFxYjdWbDVrY0ZlQ1J1NUpjSWo0UkNEcHdXL24vQUoyUFFTVnJsMlZJcW45VW5TMzNwOHg3Vk9SeDQ2UEhpU1VPaTVwazFDdVcvbmFOdWFsVHVsNzhpTGp1bnkvbU5GU1NmbjZVWldPN3B2N2xpVmNrL2N0VVd6TGRWSnFwSkpiRmtJNzh5WEJ0c1h3aHdOVnpNVkppQ2hiMzZGcVZtZUZrZzFIaDI1a0pGdlJsVFZnVnpOWFVHMVBrYXVYNWdJdFdrY0x4ckVvWklUUzh6dlJWcGVoeC9IRjhtTitiTDR2Nmp1YkhHVG9OMllCNm5rZHZ3YjhRYW5RdFk4amVYQXYySHpYb2U1MG1weGF2QkhOaGtwUWt0ajVXM1VrYm1pOFQxV2dkNmZLNE44MXpUOWdQcHdQSmFMOFg4b2F6RC81NC93Q2g2TFJlSmFYWFFVdFBsVW4xanlhQTJ3QUFBQUFBQUFBQUFBQUFBQUFBQkhpN0hOMS9qT2owUHk1TWlsay9jaTdZSFRibzF0YnJ0UG9zZkhxTWlndS9OK3g1THhEOFY2bkkydE1saWg1ODJ6Z1p0VG0xVTNMTmtsTitiZGdlaThTL0ZtV2NuRFJMNGNWLytScmQvd0JEenVvMU9iUFBqelpKWkpQcTJWY1BjY1BjREtWSnNOMFJrcUhNQ1VYYUJGT2tTQUhvUHcvUGl4VGg1TTgrZEh3VFA4SFhSVGZ5ejI5em41SnNYeGNyMDJaV2tSeEU4dkpERnpQTTliWmh5b3RLWWNpNENmV3lhVkZVT1JjQmhxMGlFNDdGaVZnRFVsQjBWY0w2UDJOdVN0Rk1vcWdLV3E1cGU1WWxhNS9uWk5Lekh3a3ZwMnNNczFWd0p2ZmN0Z210NHBQMVpscXU1bUhNRm1wOFBudjdHWGJXem94eERqOXdXYWkxeExwNnRGTTR4YnBOdnNrV1Nia1I0VjAyOUFtekZXUlcxMjh0eURqU3RYYjgyWHRVUmF0b045VVlScGMzN2lLdGt5V05XRkNWTFl0U3NRaTB5eHFnSVE1Ym1XNlJQaDdrR3JBaVZUNUZqVmxjdVlGVTViOGpYeU8xN0Y3Vm9xeUswQkNISmVod3ZIcDNteHdya216dlNmREJ0OUR5ZXR6ZkgxTThpZHB1bDZIVHh6L3B5OHR5TmNCdWlCNlhtU2x6UkVBQ2FkazRaWjQ1cVdPVGpKZFU5eWtseGRnUFJlR2ZpalZhZHFHcC9YWTExL2FYOVQxZWc4WDBuaUVid1pQbS9jbHRMN0h6TGk3Rm1QSkxITGlqSnA5R25UUUgxY0hoUERmeFJxOU5VY3orUEJmdmZWOXowbWcvRU9nMWRSNDNpbS8yWjdmbnlBNjRNSjJyVmV6TWdBQUFBQUF3blpCeVVWY21rdXJaeGZFUHhObzlKeFJ3L3dEVVNXM3l2YjdnZHppT1pyL0hORm9VMUxJc2sxK3pEbWVQMS9qK3QxdXptOGNQM1k3SEs0M04zSmdkM3hQOFM2blV1VU1MZUhIeXBQZjduQ25sbFBtQ0xkZ0c3TUFBREtNQUFBQUhWa3VMc1I1TkdVNkFrWmpKeGtwUmROTzBSNHV4a0QxK2sxQzFPbHg1RjdyeVpzNHV2WTh4NFByUGdaL2hUZnlUMlY5R2VxeHEwOXp5ZGM1WHE4ZlVzV3g1RjNRcjZGaExvekhxWEZDVmxpZEFXUk10WDFJbVlnWUtlSFl2K3BFSmN3S1VtalBXeVpCUXJrd0RWR0drMHVoWVI0UE5KZWdFVXFEVGZWK3hMaDdtTmdLL3UvVmt1SHUxNkZpU1JqaDdnVnVGOVExWEltQUt1SHVXd2p0ekJPSHlyMUFtbFFNZ0NFM1RTSXlkSWsxVEt3TU5XVnozSnk1RmJkdjBBaExvVXRXeTlxMFU1cHh4d2M1dEtLVnRzRGxlTjZqNE9tK0ZGL05rL2gxUE51UnM2L1V2VmFxV1ZwcFBhSzhrYXJkbnE0NXlQSjNkcEoyWUFMUUFBQUFBQUFBeXBORXZpZVpBQWRmUStOYXpRcExGbWJoKzVMZEhvdEIrS3RQbVNqcW9QREx6VzhUdzZkRTAyZ1BxbUhVWTgrTlpNVTR6ZytUVExUNWZwZGJuMHMrTFQ1WlkzMmY4ajBPaS9GdVNGUjFtSlpGKy9IWnIyQTllRFMwWGl1azFxWHdNc1hMOTNxdlkzRTdBK2JhL3huV2E5dFpNelVQM1k3STU3ZGM5d1FBeTNaZ0FBQUFBQUFBQUFBQUFBNjJBSmNYWWlBSmNYWTlQNEY0a3MwRmd5eXJJdVRmVTh4SHFUaE9XT2FsQnRTVHRORTljenFLNTZ2TmZRMHFNbkc4SDhZanFZTEZtcFpVdnVkbm9lV3l5dlZ6MU9vSldTVHN4TG9JOVRGSnBXV1JLaXhLMkJKcXlIUW1sUWFzQ3BxZ25SSnF5QUZoaHF5S2RJa0JqaDdqaDdrazZBVDdJSlgxb3gwWlBxUUNrR3FDVmt6RFZwQUlxa1dHUTFRQk9qRGREb1E2Z1lib2lHN0FFRzdaRnEyV0pVUUFqSlVqelhqL2lQRzNwY1QyVCtkcnEvSTNmRy9GRnBvUEJpbGVXUzNhL1ovdWVYNm5ieDhmN1hEeWVUL0lnd0FkM0FBQUFBQUFBQUFBQUFBQUFCTGk3R1l6NTB5QUEyTVdTVVpKeGJUWEpvN1BoMzRrMWVtcU9hWHg4ZmxMNmw3bm5pYXlOQVJaZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQk5PeUFBdGhOd21wUmJUWEpybWVtOEg4YitKV0hVdFJsMGs5a3p5NEo2NW5VVnoxZWEra0oyclFhczhoNFo0N2sweldQVU56eGNyNm85UnA4K1BVWTFQSEpOUGxSNXV1Ynk5SFBVeGVsUzNkazRlWlhIcVNKZEZ5ZG1Tdmk0WDZsdkYyQ2VrSmN5dHFpNlVlVzVXbFFiTHFCTGlFK1JFTldHT1M5Q0JMaTdBUjR1SldrL2RHVzdJdDBTU3NEQk5LbUVxTXAwQm5oN2lSbHVpRGRBUTZzZzNiSmtBSU4yMFRCaHVrQWJvNHZqSGl5MDBIaHcwOHpYLzFLdkZmRzQ0MUxEcFdwUzVTbjBYb2VhbEp5azVTYmJmTnZtenR4NC85cmgzNVA4ak01dWNuS1RiYmR0dm15RXVoa3hMb2QzQkVBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETkdBQUFNOHdNRXlCS1BVQ1JzYVhXNTlKazQ4VTMzaStUTllHV1N0bHNldThQOGR3YWhLR1NzYy9KOWZjN01aeG1yVFBuSnZhTHhiVmFPbEdibkQ5MlRzNWRlTC80Nnp5YjllK1N0RmtlcHdkQitJZE5uU2hsazhVdis3bDl6dHduR2FUVFc1eTliL3EveFkxWkJxeXdnMVJsbUN0cXlCYzFaVkpVekhTVmdHV3FKaG5zckpSNm1Xck1wV0dXNnp3OXlRTXg2bTJKWWxIbHVSbHlKRldXV3hpcGNWeTZHRFcxR3J3NEk4ZWJKR0M3czRtdS9FU2E0ZEpGdi92bHQrUlU1dEwzSTd1cDFlTFRZblBMTlJqM1BMK0orTlpkUzNqeFhqeFA3czV1ZlVadFRQanpaSE45TDZGUjI1OGNqaDExY0FBZFhNSUV6RStRRVFaNUdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUdlWmdBQ1hEM01jeHlBU1ZFaVBNSldCSUFBWk5uUytJNnJSdjlSbWxGZnU4MTlqVkJsa3JaYkhwTkwrTE0wYVdvd1JrdXJpNmYyWjF0UCtJdkQ4NlhGbGVLWGxrVlY3OGp3b0l2amxYUEpZK2xZOVRpeXg0c2NvelhuRnBvT1NaODNoT1VKY1VKT0w4MDZadFl2RmRkaGY2dlZaRXZKeXYrSk44YXA1STk4M1lUbzhURDhRZUl3Ly9OR1grNkNKL3dEdVBYLy9BTVgvQU5SNlU5NDlxbmFNcHBIaVAvY1BpSFNXTmVrQ3FYamZpRXVlb2t2OXFTL2tQU252SHZGTk0xOC9pV2wwOS9GelFnL0p5Myt4NEhKcTlSbC8xYzJXYThuTmxYRjJObkdsN3g2M1ZmaWZTd3VPQ0U4ejg2NFYrZjhBUTQyczhlMW1kdFFheFJmN3ZQN25LVG93WE9KSE85MnBUbktjdUtjbktUNXR1MlJBS1NBQUFBM1Fhc0NMZGprWmV4RUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUpjUGNmU1JBRXdSNWhPZ0pBeHhkaklBQWYwQUFBQUFBQUFBQUFBQUFBQWFzQUU3QmppN0FPSHVSQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFETE1BQUFBQUFBQUFBQUFBQUFBQUFETEF3QUFBQmxLd01BbHc5eHc5d0ltVTZNQUNYRjJNSko5akFBbHc5eGI4aUpMaTdBT0xzT0xzTEhGMkFjWFl5RTdBQnVqSEYyTW1PTHNCbE8rakJqaTdHUURWOVg3QUdKZEFNaHVqSEQzSEQzQXczWmd5akFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJNaTFSbXpFbllHQUFBQU04Z01FdUh1WVpuaDdnT0h1UkpjUGNpQUFBQUdUQUdhRmhtQUFBQUV1THNZU3NrQmo2aFJsdWcxWUVVWUpnREhEM0hEM01nQWxRQUFOV0FBQmpoN21RQmpoN21VcUFBQUFBQUFNY1hZaVREVmdSUVJtaUlBR2FNQUFBQUFBQUFBQUFBQUFBQUFBQUFCbEt6UEQzQWlES1ZtZUh1QmhLelBEM01OVVo0ZTRHVEhEM01nQWxRQmpoN2dPSHVPSHVPSHVaU29ESEQzTXBVRTdBR09Mc1k1RWcxWUdPSHVPSHVLSEQzQWNQY2NQY1I2bVFNVVlTc2tBTWNQY2NQY3lBQWFzQUNNbFJJTldBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQmpoN21RQkhtWU1vd0FNc3dBQUFBQUFBQUFBQUFBQUFBQUFBQXpGV1o0ZTQ0ZTVsS2dNY1BjeUFBQUFBTldBQmlqS1ZBQUFBQUFBQUFBQUFBRFZnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFhc0FBMVlUc0FZb3lsUkZCZ1Nib2daWmdES01HVUVCZ0FBQUFBQUFBQUFBQUFBQUF5bFlTc2tsUUdLTUlrQU1SNm1RWTRlNEdRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUJqaDdtSXVpUUFKMkFBTVM2RVNVdWhFRE5CQmdBakJsR0FBTW93QUJMaTdHRzdBd0RLTUFBREtBd1paZ3p5QXdTVzVnekhxQmxLZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQVlsMEhEM01wMkFJeVZHQ1gxRGg3Z1lZUWtxSkFZajFNZ0FSUTVHWmRCUUVRWmFvenc5d0hEM01OVVpveWxRR09IdVpBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFuWUFBQUFBQUFBQUFOV0FBQ2RnQnF3QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBRC85az0iXQ~`; -const exampleCert =`-----BEGIN CERTIFICATE----- +const exampleCert = `-----BEGIN CERTIFICATE----- MIICQDCCAeegAwIBAgIUVXDyMaOF1bP8wVigPfoxfe7OHVgwCgYIKoZIzj0EAwIw PzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQ d3dXYWxsZXQgUm9vdCBDQTAeFw0yNjAzMDUxNTAzMDZaFw00MTAzMDExNTAzMDZa @@ -25,7 +25,6 @@ V5fodM47AMdoV5cnog0TJQEPAiBBWlMCZmci7eH0SJCrIDo2l6dA8eVL9yCFdROL Tf2qVA== -----END CERTIFICATE-----`; - /** * This certificate was used to sign the issuer's certificate */ @@ -38,35 +37,32 @@ const context: Context = { clockTolerance: 0, lang: 'en-US', subtle: crypto.subtle, - trustedCertificates: [ - exampleCert - ], + trustedCertificates: [exampleCert], }; -describe("The SDJWTVerifier", () => { +describe('The SDJWTVerifier', () => { const pkResolverEngine = PublicKeyResolverEngine(); - it("should handle the case where the input is not an SDJWT", async () => { - const result = await SDJWTVCVerifier({ context, pkResolverEngine, httpClient: axios }) - .verify({ - rawCredential: wrongFormatCredential, opts: {} - }); + it('should handle the case where the input is not an SDJWT', async () => { + const result = await SDJWTVCVerifier({ context, pkResolverEngine, httpClient: axios }).verify({ + rawCredential: wrongFormatCredential, + opts: {}, + }); assert(result.success === false); assert(result.error === CredentialVerificationError.InvalidFormat); }); - - it("should successfully verify SDJWT+KBJWT issued by Wallet Enterprise Issuer", async () => { - const result = await SDJWTVCVerifier({ context, pkResolverEngine, httpClient: axios }) - .verify({ - rawCredential: exampleCredential, opts: { - expectedNonce: "9a0a06d0-0547-4106-b4c6-511937eb047f", - expectedAudience: "wallet-enterprise-acme-verifier", - } - }); + it('should successfully verify SDJWT+KBJWT issued by Wallet Enterprise Issuer', async () => { + const result = await SDJWTVCVerifier({ context, pkResolverEngine, httpClient: axios }).verify({ + rawCredential: exampleCredential, + opts: { + expectedNonce: '9a0a06d0-0547-4106-b4c6-511937eb047f', + expectedAudience: 'wallet-enterprise-acme-verifier', + }, + }); console.log((result as any).error); assert(result.success === true); }); -}) +}); diff --git a/src/credential-verifiers/SDJWTVCVerifier.ts b/src/credential-verifiers/SDJWTVCVerifier.ts index 345a741..1c6aa31 100644 --- a/src/credential-verifiers/SDJWTVCVerifier.ts +++ b/src/credential-verifiers/SDJWTVCVerifier.ts @@ -1,17 +1,21 @@ -import { SDJwt } from "@sd-jwt/core"; -import type { HasherAndAlg } from "@sd-jwt/types"; -import { Context, CredentialVerifier, PublicKeyResolverEngineI, HttpClient } from "../interfaces"; -import { CredentialVerificationError } from "../error"; -import { CustomResult, DigestHashAlgorithm, HashAlgorithm } from "../types"; -import { exportJWK, importJWK, importX509, JWK, jwtVerify, KeyLike } from "jose"; -import { fromBase64Url, toBase64Url } from "../utils/util"; -import { verifyCertificate } from "../utils/verifyCertificate"; +import { SDJwt } from '@sd-jwt/core'; +import type { HasherAndAlg } from '@sd-jwt/types'; +import { Context, CredentialVerifier, PublicKeyResolverEngineI, HttpClient } from '../interfaces'; +import { CredentialVerificationError } from '../error'; +import { CustomResult, DigestHashAlgorithm, HashAlgorithm } from '../types'; +import { exportJWK, importJWK, importX509, JWK, jwtVerify, KeyLike } from 'jose'; +import { fromBase64Url, toBase64Url } from '../utils/util'; +import { verifyCertificate } from '../utils/verifyCertificate'; -export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: PublicKeyResolverEngineI, httpClient: HttpClient }): CredentialVerifier { - let errors: { error: CredentialVerificationError, message: string }[] = []; +export function SDJWTVCVerifier(args: { + context: Context; + pkResolverEngine: PublicKeyResolverEngineI; + httpClient: HttpClient; +}): CredentialVerifier { + let errors: { error: CredentialVerificationError; message: string }[] = []; const logError = (error: CredentialVerificationError, message: string): void => { errors.push({ error, message }); - } + }; const encoder = new TextEncoder(); const decoder = new TextDecoder(); @@ -19,8 +23,7 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ // Encoding the string into a Uint8Array const hasherAndAlgorithm: HasherAndAlg = { hasher: (data: string | ArrayBuffer, alg: string) => { - const encoded = - typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data); + const encoded = typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data); return args.context.subtle.digest(alg, encoded).then((v) => new Uint8Array(v)); }, @@ -30,84 +33,112 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ const parse = async (rawCredential: string) => { try { const credential = await SDJwt.fromEncode(rawCredential, hasherAndAlgorithm.hasher); - const parsedSdJwtWithPrettyClaims = await (await SDJwt.fromEncode(rawCredential, hasherAndAlgorithm.hasher)).getClaims(hasherAndAlgorithm.hasher); + const parsedSdJwtWithPrettyClaims = await ( + await SDJwt.fromEncode(rawCredential, hasherAndAlgorithm.hasher) + ).getClaims(hasherAndAlgorithm.hasher); return { credential, parsedSdJwtWithPrettyClaims }; - } - catch (err) { + } catch (err) { if (err instanceof Error) { - logError(CredentialVerificationError.InvalidFormat, "Invalid format. Error: " + err.name + ": " + err.message); + logError( + CredentialVerificationError.InvalidFormat, + 'Invalid format. Error: ' + err.name + ': ' + err.message, + ); } return CredentialVerificationError.InvalidFormat; } + }; - } - - const getHolderPublicKey = async (rawCredential: string): Promise> => { + const getHolderPublicKey = async ( + rawCredential: string, + ): Promise> => { const parseResult = await parse(rawCredential); if (parseResult === CredentialVerificationError.InvalidFormat) { return { success: false, error: CredentialVerificationError.InvalidFormat, - } + }; } const cnf = (parseResult.parsedSdJwtWithPrettyClaims as any).cnf as Record; - if (cnf.jwk && parseResult.credential.jwt && parseResult.credential.jwt.header && typeof parseResult.credential.jwt.header["alg"] === 'string') { + if ( + cnf.jwk && + parseResult.credential.jwt && + parseResult.credential.jwt.header && + typeof parseResult.credential.jwt.header['alg'] === 'string' + ) { try { - const holderPublicKey = await importJWK(cnf.jwk as JWK, parseResult.credential.jwt.header["alg"]); + const holderPublicKey = await importJWK( + cnf.jwk as JWK, + parseResult.credential.jwt.header['alg'], + ); return { success: true, value: holderPublicKey, - } - } - catch (err: any) { - logError(CredentialVerificationError.CannotImportHolderPublicKey, `Error on getHolderPublicKey(): Could not import holder's public key. Cause: ${err.message}`); + }; + } catch (err: any) { + logError( + CredentialVerificationError.CannotImportHolderPublicKey, + `Error on getHolderPublicKey(): Could not import holder's public key. Cause: ${err.message}`, + ); return { success: false, error: CredentialVerificationError.CannotImportHolderPublicKey, - } + }; } - } return { success: false, - error: CredentialVerificationError.CannotExtractHolderPublicKey - } - - } + error: CredentialVerificationError.CannotExtractHolderPublicKey, + }; + }; - const verifyIssuerSignature = async (rawCredential: string): Promise> => { + const verifyIssuerSignature = async ( + rawCredential: string, + ): Promise> => { const parsedSdJwt = await (async () => { try { return (await SDJwt.fromEncode(rawCredential, hasherAndAlgorithm.hasher)).jwt; - } - catch (err) { + } catch (err) { if (err instanceof Error) { - logError(CredentialVerificationError.InvalidFormat, "Invalid format. Error: " + err.name + ": " + err.message); + logError( + CredentialVerificationError.InvalidFormat, + 'Invalid format. Error: ' + err.name + ': ' + err.message, + ); } return CredentialVerificationError.InvalidFormat; } })(); if (parsedSdJwt === CredentialVerificationError.InvalidFormat) { - logError(CredentialVerificationError.InvalidFormat, "Invalid format"); + logError(CredentialVerificationError.InvalidFormat, 'Invalid format'); return { success: false, - error: CredentialVerificationError.InvalidFormat - } + error: CredentialVerificationError.InvalidFormat, + }; } - const getIssuerPublicKey = async (): Promise> => { - const x5c = (parsedSdJwt?.header?.x5c as string[]) ?? ""; - const alg = (parsedSdJwt?.header?.alg as string) ?? ""; - if (x5c && x5c instanceof Array && x5c.length > 0 && typeof alg === 'string') { // extract public key from certificate + const getIssuerPublicKey = async (): Promise< + CustomResult + > => { + const x5c = (parsedSdJwt?.header?.x5c as string[]) ?? ''; + const alg = (parsedSdJwt?.header?.alg as string) ?? ''; + if (x5c && x5c instanceof Array && x5c.length > 0 && typeof alg === 'string') { + // extract public key from certificate const lastCertificate: string = x5c[x5c.length - 1]; const lastCertificatePem = `-----BEGIN CERTIFICATE-----\n${lastCertificate}\n-----END CERTIFICATE-----`; - const certificateValidationResult = await verifyCertificate(lastCertificatePem, args.context.trustedCertificates); - const lastCertificateIsRootCa = args.context.trustedCertificates.map((c) => c.trim()).includes(lastCertificatePem); + const certificateValidationResult = await verifyCertificate( + lastCertificatePem, + args.context.trustedCertificates, + ); + const lastCertificateIsRootCa = args.context.trustedCertificates + .map((c) => c.trim()) + .includes(lastCertificatePem); const rootCertIsTrusted = certificateValidationResult === true || lastCertificateIsRootCa; if (!rootCertIsTrusted) { - logError(CredentialVerificationError.NotTrustedIssuer, "Error on getIssuerPublicKey(): Issuer is not trusted"); + logError( + CredentialVerificationError.NotTrustedIssuer, + 'Error on getIssuerPublicKey(): Issuer is not trusted', + ); return { success: false, error: CredentialVerificationError.NotTrustedIssuer, @@ -121,86 +152,116 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ success: true, value: issuerPublicKey, }; - } - catch (err) { - logError(CredentialVerificationError.CannotImportIssuerPublicKey, `Error on getIssuerPublicKey(): Importing key failed because: ${err}`); + } catch (err) { + logError( + CredentialVerificationError.CannotImportIssuerPublicKey, + `Error on getIssuerPublicKey(): Importing key failed because: ${err}`, + ); return { success: false, error: CredentialVerificationError.CannotImportIssuerPublicKey, - } + }; } } - if (parsedSdJwt && parsedSdJwt.payload && typeof parsedSdJwt.payload.iss === 'string' && typeof alg === 'string') { - const publicKeyResolutionResult = await args.pkResolverEngine.resolve({ identifier: parsedSdJwt.payload.iss }); + if ( + parsedSdJwt && + parsedSdJwt.payload && + typeof parsedSdJwt.payload.iss === 'string' && + typeof alg === 'string' + ) { + const publicKeyResolutionResult = await args.pkResolverEngine.resolve({ + identifier: parsedSdJwt.payload.iss, + }); if (!publicKeyResolutionResult.success) { - logError(CredentialVerificationError.CannotResolveIssuerPublicKey, "CannotResolveIssuerPublicKey"); + logError( + CredentialVerificationError.CannotResolveIssuerPublicKey, + 'CannotResolveIssuerPublicKey', + ); return { success: false, error: CredentialVerificationError.CannotResolveIssuerPublicKey, - } + }; } try { const publicKey = await importJWK(publicKeyResolutionResult.value.jwk, alg); return { success: true, value: publicKey, - } - } - catch (err: any) { - logError(CredentialVerificationError.CannotImportIssuerPublicKey, `Error on getIssuerPublicKey(): Cannot import issuer's public key after resolved from the resolver. Cause ${err.message}`) + }; + } catch (err: any) { + logError( + CredentialVerificationError.CannotImportIssuerPublicKey, + `Error on getIssuerPublicKey(): Cannot import issuer's public key after resolved from the resolver. Cause ${err.message}`, + ); return { success: false, error: CredentialVerificationError.CannotImportIssuerPublicKey, - } + }; } } - logError(CredentialVerificationError.CannotResolveIssuerPublicKey, "CannotResolveIssuerPublicKey"); + logError( + CredentialVerificationError.CannotResolveIssuerPublicKey, + 'CannotResolveIssuerPublicKey', + ); return { success: false, error: CredentialVerificationError.CannotResolveIssuerPublicKey, - } + }; }; const issuerPublicKeyResult = await getIssuerPublicKey(); if (!issuerPublicKeyResult.success) { - logError(CredentialVerificationError.CannotResolveIssuerPublicKey, "CannotResolveIssuerPublicKey"); + logError( + CredentialVerificationError.CannotResolveIssuerPublicKey, + 'CannotResolveIssuerPublicKey', + ); return { success: false, error: issuerPublicKeyResult.error, - } + }; } const publicKey = issuerPublicKeyResult.value; try { - await jwtVerify(rawCredential.split('~')[0], publicKey, { clockTolerance: args.context.clockTolerance }); - } - catch (err: unknown) { - if (err instanceof Error && err.name == "JWTExpired") { - logError(CredentialVerificationError.ExpiredCredential, `Error on verifyIssuerSignature(): Credential is expired. Cause: ${err}`); + await jwtVerify(rawCredential.split('~')[0], publicKey, { + clockTolerance: args.context.clockTolerance, + }); + } catch (err: unknown) { + if (err instanceof Error && err.name == 'JWTExpired') { + logError( + CredentialVerificationError.ExpiredCredential, + `Error on verifyIssuerSignature(): Credential is expired. Cause: ${err}`, + ); return { success: false, error: CredentialVerificationError.ExpiredCredential, - } + }; } - logError(CredentialVerificationError.InvalidSignature, `Error on verifyIssuerSignature(): Issuer signature verification failed. Cause: ${err}`); + logError( + CredentialVerificationError.InvalidSignature, + `Error on verifyIssuerSignature(): Issuer signature verification failed. Cause: ${err}`, + ); return { success: false, error: CredentialVerificationError.InvalidSignature, - } + }; } return { success: true, value: {}, - } - } + }; + }; - const verifyKbJwt = async (rawPresentation: string, opts: { - expectedNonce?: string; - expectedAudience?: string; - }): Promise> => { + const verifyKbJwt = async ( + rawPresentation: string, + opts: { + expectedNonce?: string; + expectedAudience?: string; + }, + ): Promise> => { const kbJwt = rawPresentation.split('~')[rawPresentation.split('~').length - 1]; let temp = rawPresentation.split('~'); temp = temp.slice(0, temp.length - 1); @@ -208,56 +269,79 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ const publicKeyResult = await getHolderPublicKey(rawCredentialWithoutKbJwt); if (!publicKeyResult.success) { - logError(CredentialVerificationError.CannotExtractHolderPublicKey, "CannotExtractHolderPublicKey"); + logError( + CredentialVerificationError.CannotExtractHolderPublicKey, + 'CannotExtractHolderPublicKey', + ); return { success: false, error: publicKeyResult.error, - } + }; } const holderPublicKey = publicKeyResult.value; - const kbJwtDecodedPayload: Record = JSON.parse(decoder.decode(fromBase64Url(kbJwt.split('.')[1]))); + const kbJwtDecodedPayload: Record = JSON.parse( + decoder.decode(fromBase64Url(kbJwt.split('.')[1])), + ); if (!kbJwtDecodedPayload.sd_hash || !kbJwtDecodedPayload.nonce || !kbJwtDecodedPayload.aud) { - logError(CredentialVerificationError.KbJwtVerificationFailedMissingParameters, "Error on verifyKbJwt(): Once of sd_hash, nonce and aud are missing from the kbjwt payload"); + logError( + CredentialVerificationError.KbJwtVerificationFailedMissingParameters, + 'Error on verifyKbJwt(): Once of sd_hash, nonce and aud are missing from the kbjwt payload', + ); return { success: false, error: CredentialVerificationError.KbJwtVerificationFailedMissingParameters, - } + }; } - const { sd_hash, nonce, aud } = kbJwtDecodedPayload as { sd_hash: string, nonce: string, aud: string }; + const { sd_hash, nonce, aud } = kbJwtDecodedPayload as { + sd_hash: string; + nonce: string; + aud: string; + }; const data = encoder.encode(rawCredentialWithoutKbJwt); const hashBuffer = await args.context.subtle.digest(DigestHashAlgorithm.SHA_256, data); const calculatedSdHash = toBase64Url(hashBuffer); if (calculatedSdHash !== sd_hash) { - logError(CredentialVerificationError.KbJwtVerificationFailedWrongSdHash, "Error on verifyKbJwt(): Invalid sd_hash"); + logError( + CredentialVerificationError.KbJwtVerificationFailedWrongSdHash, + 'Error on verifyKbJwt(): Invalid sd_hash', + ); return { success: false, error: CredentialVerificationError.KbJwtVerificationFailedWrongSdHash, - } + }; } if (opts.expectedAudience && opts.expectedAudience !== aud) { - logError(CredentialVerificationError.KbJwtVerificationFailedUnexpectedAudience, "Error on verifyKbJwt(): Invalid aud"); + logError( + CredentialVerificationError.KbJwtVerificationFailedUnexpectedAudience, + 'Error on verifyKbJwt(): Invalid aud', + ); return { success: false, error: CredentialVerificationError.KbJwtVerificationFailedUnexpectedAudience, - } + }; } if (opts.expectedNonce && opts.expectedNonce !== nonce) { - logError(CredentialVerificationError.KbJwtVerificationFailedUnexpectedNonce, "Error on verifyKbJwt(): Invalid nonce"); + logError( + CredentialVerificationError.KbJwtVerificationFailedUnexpectedNonce, + 'Error on verifyKbJwt(): Invalid nonce', + ); return { success: false, error: CredentialVerificationError.KbJwtVerificationFailedUnexpectedNonce, - } + }; } try { await jwtVerify(kbJwt, holderPublicKey, { clockTolerance: args.context.clockTolerance }); - } - catch (err: any) { - logError(CredentialVerificationError.KbJwtVerificationFailedSignatureValidation, "Error on verifyKbJwt(): Invalid KB-JWT signature"); + } catch (err: any) { + logError( + CredentialVerificationError.KbJwtVerificationFailedSignatureValidation, + 'Error on verifyKbJwt(): Invalid KB-JWT signature', + ); return { success: false, error: CredentialVerificationError.KbJwtVerificationFailedSignatureValidation, @@ -266,8 +350,8 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ return { success: true, value: {}, - } - } + }; + }; return { async verify({ rawCredential, opts }) { @@ -285,27 +369,31 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ return { success: false, error: errors.length > 0 ? errors[0].error : CredentialVerificationError.UnknownProblem, - } + }; } // KB-JWT validation - if (!rawCredential.endsWith('~')) { // contains kbjwt + if (!rawCredential.endsWith('~')) { + // contains kbjwt const verifyKbJwtResult = await verifyKbJwt(rawCredential, opts); if (!verifyKbJwtResult.success) { return { success: false, error: errors.length > 0 ? errors[0].error : CredentialVerificationError.UnknownProblem, - } + }; } } const publicKeyResult = await getHolderPublicKey(rawCredential); if (publicKeyResult.success === false) { - logError(CredentialVerificationError.CannotExtractHolderPublicKey, "Could not extract holder public key"); + logError( + CredentialVerificationError.CannotExtractHolderPublicKey, + 'Could not extract holder public key', + ); return { success: false, error: errors.length > 0 ? errors[0].error : CredentialVerificationError.UnknownProblem, - } + }; } return { @@ -314,7 +402,7 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ valid: true, holderPublicKey: await exportJWK(publicKeyResult.value), }, - } + }; }, - } + }; } diff --git a/src/defaultHttpClient.ts b/src/defaultHttpClient.ts index ac60f63..eecd8a0 100644 --- a/src/defaultHttpClient.ts +++ b/src/defaultHttpClient.ts @@ -1,11 +1,21 @@ -import axios from "axios"; -import { HttpClient } from "./interfaces"; +import axios from 'axios'; +import { HttpClient } from './interfaces'; export const defaultHttpClient: HttpClient = { async get(url, headers, opts) { - return axios.get(url, { ...opts, headers: headers as any }).then((res) => (res?.data ? { status: res.status, data: res.data, headers: res.headers } : null)).catch((err) => (err?.response?.data ? { ...err.response.data } : {})); + return axios + .get(url, { ...opts, headers: headers as any }) + .then((res) => + res?.data ? { status: res.status, data: res.data, headers: res.headers } : null, + ) + .catch((err) => (err?.response?.data ? { ...err.response.data } : {})); }, async post(url, data, headers, opts) { - return axios.post(url, data, { ...opts, headers: headers as any }).then((res) => (res?.data ? { status: res.status, data: res.data, headers: res.headers } : null)).catch((err) => (err?.response?.data ? { ...err.response.data } : {})); + return axios + .post(url, data, { ...opts, headers: headers as any }) + .then((res) => + res?.data ? { status: res.status, data: res.data, headers: res.headers } : null, + ) + .catch((err) => (err?.response?.data ? { ...err.response.data } : {})); }, -} +}; diff --git a/src/error.ts b/src/error.ts index b55b989..81c1878 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,21 +1,21 @@ export enum CredentialParsingError { - UnsupportedFormat = "UnsupportedFormat", - CouldNotParse = "CouldNotParse", - InvalidSdJwtVcPayload = "InvalidSdJwtVcPayload", - InvalidDatatype = "InvalidDatatype", - NotSupportedCredentialType = "NotSupportedCredentialType", - InfiniteRecursion = "InfiniteRecursion", - VctmDecodeFail = "VctmDecodeFail", - UnknownError = "UnknownError", - NotFound = "NotFound", - NotFoundExtends = "NotFoundExtends", - IntegrityMissing = "IntegrityMissing", - JwtVcIssuerMismatch = "JwtVcIssuerMismatch", - IntegrityFail = "IntegrityFail", - SchemaShapeFail = "SchemaShapeFail", - JwtVcIssuerFail = "JwtVcIssuerFail", - FailFetchIssuerMetadata = "FailFetchIssuerMetadata", - FailSchemaIssuerMetadata = "FailSchemaIssuerMetadata", + UnsupportedFormat = 'UnsupportedFormat', + CouldNotParse = 'CouldNotParse', + InvalidSdJwtVcPayload = 'InvalidSdJwtVcPayload', + InvalidDatatype = 'InvalidDatatype', + NotSupportedCredentialType = 'NotSupportedCredentialType', + InfiniteRecursion = 'InfiniteRecursion', + VctmDecodeFail = 'VctmDecodeFail', + UnknownError = 'UnknownError', + NotFound = 'NotFound', + NotFoundExtends = 'NotFoundExtends', + IntegrityMissing = 'IntegrityMissing', + JwtVcIssuerMismatch = 'JwtVcIssuerMismatch', + IntegrityFail = 'IntegrityFail', + SchemaShapeFail = 'SchemaShapeFail', + JwtVcIssuerFail = 'JwtVcIssuerFail', + FailFetchIssuerMetadata = 'FailFetchIssuerMetadata', + FailSchemaIssuerMetadata = 'FailSchemaIssuerMetadata', } export const CredentialParsingWarnings = new Set([ @@ -34,60 +34,57 @@ export function isCredentialParsingWarnings(code: CredentialParsingError): boole } export enum GetMatchingCredentialsError { - PresentationDefinitionParseError = "PresentationDefinitionParseError" + PresentationDefinitionParseError = 'PresentationDefinitionParseError', } export enum ValidatePresentationRequirementsError { - PresentationSubmissionParameterIsMissing = "PresentationSubmissionParameterIsMissing", - ConstraintsAreNotSatisfied = "ConstraintsAreNotSatisfied", - PresentationSubmissionParsingFailed = "PresentationSubmissionParsingFailed", - FailedToParseAtLeastOnePresentation = "FailedToParseAtLeastOnePresentation", - InvalidVpToken = "InvalidVpToken", - CredentialParsingError = "CredentialParsingError", - CouldNotFindAssociatedInputDescriptorBasedOnPresentationSubmission = "CouldNotFindAssociatedInputDescriptorBasedOnPresentationSubmission", - CouldNotVerifyParsedCredentialWithInputDescriptor = "CouldNotVerifyParsedCredentialWithInputDescriptor", - UnsupportedFormat = "UnsupportedFormat", - + PresentationSubmissionParameterIsMissing = 'PresentationSubmissionParameterIsMissing', + ConstraintsAreNotSatisfied = 'ConstraintsAreNotSatisfied', + PresentationSubmissionParsingFailed = 'PresentationSubmissionParsingFailed', + FailedToParseAtLeastOnePresentation = 'FailedToParseAtLeastOnePresentation', + InvalidVpToken = 'InvalidVpToken', + CredentialParsingError = 'CredentialParsingError', + CouldNotFindAssociatedInputDescriptorBasedOnPresentationSubmission = 'CouldNotFindAssociatedInputDescriptorBasedOnPresentationSubmission', + CouldNotVerifyParsedCredentialWithInputDescriptor = 'CouldNotVerifyParsedCredentialWithInputDescriptor', + UnsupportedFormat = 'UnsupportedFormat', } export enum CredentialVerificationError { - UnknownProblem = "UnknownProblem", - VerificationProcessNotStarted = "VerificationProcessNotStarted", // will be used when the verifier functions cannot start the verification process because of format - InvalidDatatype = "InvalidDatatype", - InvalidFormat = "InvalidFormat", - MissingOpts = "MissingOpts", - InvalidCertificateChain = "InvalidCertificateChain", - + UnknownProblem = 'UnknownProblem', + VerificationProcessNotStarted = 'VerificationProcessNotStarted', // will be used when the verifier functions cannot start the verification process because of format + InvalidDatatype = 'InvalidDatatype', + InvalidFormat = 'InvalidFormat', + MissingOpts = 'MissingOpts', + InvalidCertificateChain = 'InvalidCertificateChain', - InvalidSignature = "InvalidSignature", - CannotResolveIssuerPublicKey = "CannotResolveIssuerPublicKey", - CannotImportIssuerPublicKey = "CannotImportIssuerPublicKey", - NotTrustedIssuer = "NotTrustedIssuer", - VctRegistryNotConfigured = "VctRegistryNotConfigured", + InvalidSignature = 'InvalidSignature', + CannotResolveIssuerPublicKey = 'CannotResolveIssuerPublicKey', + CannotImportIssuerPublicKey = 'CannotImportIssuerPublicKey', + NotTrustedIssuer = 'NotTrustedIssuer', + VctRegistryNotConfigured = 'VctRegistryNotConfigured', - ExpiredCredential = "ExpiredCredential", + ExpiredCredential = 'ExpiredCredential', - CannotImportHolderPublicKey = "CannotImportHolderPublicKey", - CannotExtractHolderPublicKey = "CannotExtractHolderPublicKey", + CannotImportHolderPublicKey = 'CannotImportHolderPublicKey', + CannotExtractHolderPublicKey = 'CannotExtractHolderPublicKey', // KBJWT related - KbJwtVerificationFailedMissingParameters = "KbJwtVerificationFailedMissingParameters", - KbJwtVerificationFailedWrongSdHash = "KbJwtVerificationFailedWrongSdHash", - KbJwtVerificationFailedUnexpectedAudience = "KbJwtVerificationFailedUnexpectedAudience", - KbJwtVerificationFailedUnexpectedNonce = "KbJwtVerificationFailedUnexpectedNonce", - KbJwtVerificationFailedSignatureValidation = "KbJwtVerificationFailedSignatureValidation", - + KbJwtVerificationFailedMissingParameters = 'KbJwtVerificationFailedMissingParameters', + KbJwtVerificationFailedWrongSdHash = 'KbJwtVerificationFailedWrongSdHash', + KbJwtVerificationFailedUnexpectedAudience = 'KbJwtVerificationFailedUnexpectedAudience', + KbJwtVerificationFailedUnexpectedNonce = 'KbJwtVerificationFailedUnexpectedNonce', + KbJwtVerificationFailedSignatureValidation = 'KbJwtVerificationFailedSignatureValidation', // MSO MDOC related - MsoMdocMissingDeviceKeyInfo = "MsoMdocMissingDeviceKeyInfo", - MsoMdocInvalidDeviceSignature = "MsoMdocInvalidDeviceSignature", + MsoMdocMissingDeviceKeyInfo = 'MsoMdocMissingDeviceKeyInfo', + MsoMdocInvalidDeviceSignature = 'MsoMdocInvalidDeviceSignature', } export enum PublicKeyResolutionError { - CannotResolvePublicKey = "CannotResolvePublicKey", + CannotResolvePublicKey = 'CannotResolvePublicKey', } export enum CredentialRenderingError { - IntegrityCheckFailed = "IntegrityCheckFailed", - CouldNotFetchSvg = "CouldNotFetchSvg", + IntegrityCheckFailed = 'IntegrityCheckFailed', + CouldNotFetchSvg = 'CouldNotFetchSvg', } diff --git a/src/functions/CustomCredentialSvg.test.ts b/src/functions/CustomCredentialSvg.test.ts index 8060592..f8a98c1 100644 --- a/src/functions/CustomCredentialSvg.test.ts +++ b/src/functions/CustomCredentialSvg.test.ts @@ -1,16 +1,16 @@ -import { assert, describe, it } from "vitest"; -import { CustomCredentialSvg } from "./CustomCredentialSvg"; -import { HttpClient } from "../interfaces"; -import path from "path"; -import { convertDataUriToImage } from "./convertDataUriToImage"; +import { assert, describe, it } from 'vitest'; +import { CustomCredentialSvg } from './CustomCredentialSvg'; +import { HttpClient } from '../interfaces'; +import path from 'path'; +import { convertDataUriToImage } from './convertDataUriToImage'; const httpClient: HttpClient = { async get(_url) { const pngBase64 = `iVBORw0KGgoAAAANSUhEUgAAAz0AAAH4CAYAAABg0oNbAAAAAXNSR0IArs4c6QAAIABJREFUeF7sfWmOs72tpe0K0H96Zze7Tm8jdxvdwE25GuKkQ4qU5JoHfUDylu3n0UBxOJRI6vrPf/375SL/tT+ul8tFv2h/z//DpydPXq+Xy4t1Uz6I/a/7hmag/diGfm5P499xzjio7Dn6Peknm8yqz+od6gLWQp9LaSFjsbWa0DjOxz5n70zmWNIl4Z8+ZuaRXdpH2mQ8EemLdOP3lS+v8idTac3Xnp/j869Z15UEVTw/o9dMTvLf9uT0yD+v1pH/Li+oe478e/38kI0qFMGR/06YY/+P/T/2v7ZBM2wb8azX1cf+o12/NqdnDnY6weJzU1CegIcMdNZAdATL+Gz82wzQANqvBLkBBq9wqP2egVztNwLtlcKuAHMGrHvbkQYyF5pjh/HVmKIjF9yBdJ7mNJCfynR75L/ZWGYOTAWoKp6p1nPmqHiXnqmhvNHnOOeXGfAb5aivV6NlKS+Fo1nxeMWX1Vo9AlYjXyPNjvx3x/nIf64Vjvzj5sqR/+g0Z3Iz6pxuqaKuQxuW2rNj/1PBLPFSusnDm4XH/ncnNDoVGZGP/fdUibjju+D/wemJJw1XOFHIdv0yQDWCstzTnAHdHNTmTgCCyRlIRm9PT21m4+ffai95BPG5g7jaLZ05AwjGPeQaGQyNxzCv6zVRYmyUM+MRwUsc42rtHK0d3OeWH3GAMpCfQ67ONTivymnLhHJlpH1b+SlW2W44WRuVpDfpuAbYL48R+/bjqNZuRYcUYBz5hzPCXKl3uh35n8vqnD6ZToh6pNI7vDJH/qO12tnsq3QxUfTI/5H/xF4f+5875hnuMVod++8M6IqHxt/fCf9zeBvueEe3pw73mBugald07UREUIxOTWUEaSwZU7XvmtDKqcXayeHeKuO9Wqgc7OcCsuojA1MjcPX03FmTzBDuOjXdf+lO1C5Ncew5wBnBO3Jjd0hGZ6MCTAaEwg6grkjlCOj30THxvIhOYx9TbHu1ztkYcseplh0cL8K/cZ4j8Dzy7wIij/zPQvxMAI78q9ywjB35j3osbr7w78f+ZzYtsxHH/lfhpMf+V7hFZQ6lDDdys8iNP2f//+tf/35BEIxEq5TYDDRnwHZ0BMQRkbAz3LWuwGi6C52sfAZSq917VCoY5rTj2KSOUcizQYCcg1g/gUj7zGhwO4/tKM8coWpcFb2zMeaOSX6as7vWyAeZQ6N0WJ3o1M4Q036tJPpzXpGMxrtybioHqpK7lQxWRtM7ZCNvxe2M2H+tKHNaRbnyyrXeOFBT1mWuBoyRdnHNMh0VV+bI/ximkdEVddqR/1x+umwe+T/yH0D5sf8uHH61sTnDesf+5zp7toF67L/HnBk9iH6Y01MZwsqRqbzNDJggWF+DcG9Q1s/PRhIT2P0uwcy4Z87d6MDtJMivx8cGpJsRBfNRMeyC9ArQR2WSKZ7oOK5AZDthw/hfD9pzcIAUiWAZ+XA1vrhGOw5DNr/5Cr1ijYUm4/hlxJtFMebjGuVE+xtlJro8/ckdh3xHN0Q9MRouL3vZ3KJTn8nbo/oo48fKkY70y3RPRq/KgK/4ys/vyP+MhyIPrtYw0j4+P9MV2bsPrfGRf2fRjv2vNUHXJ0f+j/zvWAx95tj/mW+Q48grOz0IkDs5q52s9a4stjfsSqe5JfOFXu2gs3adV4hbESc6E/Wu9UiXDIRVzskKRKECjAUYVkY++/0REapB6PiLn0d3PaoTswxwrHYKVyBn1WalQG3sC8djBxRl68zgqPPJCiTP1q3i20wmdCwI3DOgls0L343j9+tw5H+VW4caIvu71i14OjYW1Djy78MQj/yzZCIdMjiU8VvUC0f+mXLrjc5j/5HnItWO/VeKHPuPOgYx16MFMt4b/9tJTxT4CqhlgGrmUFSALirqWcGAKGQjEUYGQ+dBQcrMsVmNswLQK6diRhs/L19JrBqrA6xJkulovOpS3RUgnjkjI53y3fsVT6Bxjs5NStOpsxzGMHk242ueb+Xky2jKndt5KfSZQ5M6dTD2mcMTwU1cs1ThAGEzZw0N/pF/DyiP/I8AG+WmlqtRmqNzfuQfwPaR/1T9V8CHvz/2PzvHXzlxx/77Taa4meV02rH/Jpc7G3kptgn4w7ezt5mQ+wIPyn9VsrqDovlgdBAZCBsdm244VwIZd6dmzoVzBADAouMzlibOxxIVASrVGRCMID4q48ywzwBxBUorgJDRegTNspbuVCwv0RzHltEy45HKOK2q5WUKuFrziq/i8yNfzLjI/1bPvz+3Wr+Kh+cGvB7HjEYRSFbjzxypine4vyP/mKSeOZe4Ygg+Mtpla1+tCerIXC/lvFjr1iP/uxrgyD9T6si/L1Jx5N/jhcgjx/53XHnsv99Kjo75+lR1T1s/iv9deFu2w1+BdwTfEfBjNYg4oMzAt/er74lsRJ1+weUjYLYE/jLozLh50OC9SAQf2dz672uwWDkwkbYVc8zpljNMRrs56PXAKgKqnfWcAe0eluhPa2btPmqMR2FTxf3YHU61k+PXekqTsGM0c4CyeVbyFNvJHKRHnDT/bM3L0dlSqIShmVFmMmNw5H+UV1zrHTnbMRFH/hnEMn8e+a9s0JF/xSTH/s/wyI5eOvYfHKFj/9O7Hz/T/l+1elsM71FnJzshKb354EiMRtgD2wja48QrIJcDLYTjncki+LM2kzCCdF4PJpxnTlTmTFYOS0aD3NEYgXZ0uNaAeqRTBopKMLXIo9I56vu1U+0dq3h8iv3TnCZ3R2Bf+fxzAF85E73vuVM2o9vagesGFsFYdBYyRyZbm6mRCTLan80ct+zy1llVNlDuKdMc+X9Ud2aOj+pmDMfw/Lemc8VbR/4rHj7ynzlIO7qm0qHH/o8Vuo79z08HHO8d+19Cskdwpd9M/0P2XwsZrMBTN4YjEHpE8UWnYLp68OMMLGRtzhwobnbMofGhKfXIEHRkDlgGVGIK7uo9ZN4VUFrNJYLl2F50SrLfmTYjmIrj7E/VZXIz+ut307jaTSCNj61oN+eTfg/PzOGo6LVjwFCZ4zqs+Tc+3cFaNdacL1d8Hts98n/k38s26t8j/16TZeFQ2cl9tAdH/r1eOva/6+HxpHyOZY79r2l37P/fs/9leJuRYrKbbwwDR3YoYDMHJ+4oxp3KeMLUmRNNBpqKevEyEIzApQKeCOjjM1HxZI5ffN/PQX+dj7sCzgguqr+jE6dzXt1JFNcmo0McdX9HFDDxjZ9jBEcI27GP2ZzjOxn1It9l86nWpuJZXLvsbqCsz10nGnlrJ/QrGyOuLdJo5EsfrrEc45H/4e6JzKk88q9g/8h/dGoq3sh06JF/lq5j/3OgnjnHx/57STr2X3VxljZy7L85PRX0XgHVFeiMAOE17U1BYQBl3sDkR3ZZydlsXFVYWjXnDAzN6BPdntxJGVuIAFzvyTGwW1QaqZyJyoFbOT8roIe/x79RUa/D/+aCGtdOw9865fZC2uZrtTcG5NU4xxn4icYMeWNNn+7moEM2c+B2nMfoiCJ9MnAW5XTGPzjf18iNjeXIP4V7tjKgR/7F2A+O+pH/7MRpR+5eY68HfQyK4zXtHfufg3pzDo/8u3I7x/7npf0jNvmr8j84PQhUHgFp2XvxOyRytVtdLcSqfTF3VBIh7npXAE2fm9270RLtFVBku+K7wC6eT6129Ga7Fdluz24YxQ6Y9s+EZF/NhSrKZVeOW+b0ZPxQAecMpEfaz/gVAfzo3ISdxTC3ymHDdrK+V04jRi9n652daRJ9yzyz3DHL5j5zWKOzNtLLSwxCymrM1Vrt0DbybA2quPcj/6gN87yBqGcjP/T1OvK/w6NH/keeO/a/a85j/7seivhlZp/w2UxnzTBEtFs1Pjj2PzpE8bQ6oyVishU+/0723zk9mSMSgV0GiDImjoycTbpqS4GdXmK0cpB2hAjHgwZdQRLOc6agKvAe38/Ae7bfmDlS1pYVW0Aw13taAdfMUcjBf1aG0l96h+PMQPAjp2LeFCCkzfuMtFTzGi9v3Vdy/skZHUfnNFmLSQ3/jFav/a4G+2ueyNc9p8OKr6JCPPLv6ZjJQly7KJspzY/8O8J6Gq0rr9Ug58h/poOO/VfLUtuhY/9z63XsP1vBWQTEa+1+hiWrvmY6b6UP/4r8Q/W2fEmis+LBU0/0joudenY7+QEyjArgrQDZa5ghAsKh76JaSMXEnnlyd2HFgDimbA1GR6CDcaRBpaQzx65yLqeOWXIj+PwUa3YvkHco4tjjiQLSP9Izo++K5rN3Im3GNXmsMMY4t9GZ2pmDPZOc/qzmu+sIRZnq7R75X4PHI/+Zzcgc53gf0pH/vepe1envkf+9zaBj/+twqEwGo86rsFpun4/9z/nt2P9qs7DCs5G/4kZihQ/ppIeZdjyHyICkDmAlDJnTg++u/h5BfV43P0KKzPnKQLvO2RImoYT1DMgM45oUcchoVSlY/b5y2oYxJQ7kzCGsaDBzkPid2pmKTJY5TRWwrkD/o45M+nyyliuHDse5o8TteXCIIy0rIZytU38nP91DPsG5z/i+ounonKvU8htH/jvlSqf6yL/tbh75n4fwZDYtyuaR/7wyYNdHHadEG5LZzgqnHPvfQ5RrfHTs/wyPEQ8d+5/esfmd8T+d9OTgPC/XWzlIETQi4MsIMNu1HwC+fDGCxdFRq97NQC0aoQywrk44ZqWi8/6wspG/bHXmlETwnIHyFUCNa1wB+z1AXl8mG9e6AtyzdYq/IRTf45twArE4qav4c7W+Gc9rUnk1zoq+IyDyxj3jAV7TCAL83Mf1GE+kjvwzdasNlHrN6tO9I/+jhK94f6b3Z/ZkfO/IP8q0B25H/me2UuU26oMsemFlH8ZNgGP/czrO0cCx/50+o4069n8XS6o8wknPeOngTPArcLsCzbs70ggaKhBcCVC5KztQxztN810heXlSLWpt1HMnLXuvGosqWg9U81OBrI1sh+siZ3269/PoGsaLbfvajfTNADbyUu97TC7EClXekPtdqzjv2Cc67vT34tSsomMHYtHFQTW9VuhRkcW55QCa283mEnPhMlnKaF7RbfU+zvBR3pnJzKNt6TiO/CNv+L+P/Ocywzqw/7eyBR6IHfmvZA+/n+m1TEse+R/l+Nj/zimI/3IMkeOCY/9ndOk8h/Ka2dRH5PlRfyHDHCtdsmv/y5LVEbbNj0B94l8Hg+MRalRuSsxIFG9UMuHPFcJQiS0NW4vOx8QZGU4J9h2XGRis6Lt6Jwq6jib7VxknMmdG68wZjWuVOZlxnar1rNb9EWMXmbrikUwY8/69s1gBnd0xZkY6giMtKY0yMptX9hx9N6nghgUeKlpUSigqltj/Y2DwyP9MESNfVbKVyXvGL0f+c2OdyW43qEf+ZxuHGX8e+T/2P2KUiPfiFsCu/ax0ZYUdfpv9r2xvasOP/SdyecyVyGaC/6clq7XRjKnHDvcqb0WPXBc039XxIXa7SjgThmG8xe5+BRJngpe5UJGBZ4pgtts9W9jZTtgcREWI1GdX7ZJHY8d0yqu+xTFXzsDqpA4dqMzpXivJ+iQvB0Oj0OwaeaRoJjcV+JoZhGotMt6agTvk6R1ei88f+R+pG3lzpnNQx2X6ZWdNuqx0Hj3yz+tS0+/I/84GF3J39vyR/yP/x/6PkVDZ+S7ilF0sucKWFc5ZbVwiDpnhp78m/+D0MFlolzi5p2QbOC0KAuTHs6Pxmu2a+wVMQIBzaPJ7Jh5hyM5c403RGUOumBEB0mwcM4OVKaFonHbb9v3slYKt+GGce1QDCVCZFB6IApkLb+3E2ftJwuFqXat1zJyx3OlYA9RKoa2+j2APx4RjyU4Kqg2GI//5Kq74JFuLHdlDec16PvLvqVJtQIy1pxJqHvk35/DIP0tWtemWgVB+/tj/FZA+9j9a34j2ahuzy48V5szOII/9H/F/mdODhH0URCHjx78zCBx3ySsAMDLFeBIUn8l2ZGcnDFEZVs5FrHaXAWFvXOoE0jonplMiM/irPnfAeQbsoxOEuT6jsciTuSse4Bll4unnmvFcbqznQHW2nplxy07kYr+PgtHpiZbbYIgOJ48m48FMma3k5jVOXFTZO0r0yD+vxJF/L+nZadXM2YtO4ZH/PFx0RpeV7kA+zdo58l9V5MtPEWeOuVWKFd3QbdNoDzObEzf8jv3PbT86Zl9p/0cbMB9vjJzJNngrvDbFGNLtsf9ik1vJ6jlYWgu3AlnNV4j77m5ByqTxEeBVwAEZeeZH7zhTro8Hy1ZnoDkbG8+/jyYqvzjO2oiN4WTU8rJ89Xifyswh2nGWULHMgHDtNGZcVyv/5ZiSC0IfBUkV31bKIvLnSqnojKtx4RzRIM5ydCojuxpzNAaVAxgd1Hodxqpxqfwd+efTdDBER/7RvfEaPfJbKdNH/pebI6h/jvx3jWc8dew/EWVpaycgeiavKNk5SP9N9v96ebleLtcXiZ6q/Z3hF6ZTe9m/v3J4djY4Ktz2l+y/O+mZMfwOgOyLEpg3CZdDo89LPHGuJpeaVrxU7ZSsBDoHnrGXeqzevRkVa8WY1XhnTIpCEP+OJxbjvMHJDMp+Bsqjscye9cptpBW28Yggz/RGpmxxLZDHrM8Q8pIBgWxd6LnhfhZ02mcXsO0n91cORwpeUMaKuwPwveqkqJKPI//IfUf+K15CfVXp9SP/TL317mzOZ0f+1wjy2P9Vha5j/+MdO6+x/4oDboIHiO+ut8vLy90wLX3XnJ/ry+V2uV7uwr5R/mkz7EWcJdMQL1y06P5CTtTteqFCk63fu1ScpHaO/XdKYSr/7Z6eCgDNDFv2G4LdaNh2Tl2YYWRVhyNgr+ii89Dn4E+MYpu9tn48WRp3GdSAj8apP1uB/urSqqrNDPyvVfssiZdXAHeVY9+P7gzoeEZh9VH1O4CoMvhznutrVgGnlVPr28/XfEX3zMHy/N0/4SkNOvpR5nQ+zmEMu9eVQz5zPGeyvXI4j/xX8nXkH2XkEUd5V8/lTvaR/7iZ9Yg9KTdwYDFLe1YoxcyGRDvxkE4+9n9lfqbgzr987H9FTObJd7D/xq9s6dXeasvcjzgu5K3cLi/3O/kouu1BV0zcbpfLy/3ycr1eruTg9LYUXVlbdATkcXKGyaITd+SfKTKc9ODuMhqo+U5vfiEiKr9o7CoAlzHpHJwhlOS3c8M6hnjtaZcR4GQO14w+DshKp84QTI7VK4Adx54xdAZ4jTbF6dmjRg+N7gx8RwEcaf86BZQB8xktsrWb0TKjPz4/S26NY1ut0QxIjuMenXt0nKZrn/Ggde75vWozM674rKdRffv3Dgg+8r8OOVnx1qBvj/yXVd9m+h11uZjQYWOp0ierNTryn1vkI/9H/ldYbWbXq98qzLYr/6RC6RSn2cw7OSLXO5/o8MlOvkVBmIEwnzhEilqlQeeq8rEOF9Ggdl8uLy+3y8utnRrRAZBz3fzma8fDs80ST4c/YP//+a//lqjD8bg9Z5YabGVKOwOl1XNpf8nOTwfQ487fYNypsxFQoyIdDVl+UesKAMZ2sufj+OIz2bg6I8+LBuyMr9r5rxTAbK1mILf9Zm1Oco5W/c4NHo/An6bkax13QjIl+ogSTOkCzivOXS8LrRT3igaz97os9KdmjgTyKNPEy/NaNo/8z/gkrlU0Qkf+R0M8k80j/zXcq+T8yH/cZDn239ljYamZo7Cyjyv89NvtP292aqBacz9eLpebnuJcLy/3VgUZEJKxoFC9eSstVk0JdSMYbqc/5NyoY9T+tXMkPk9qfXNL0jcs2JH/ufy7e3pmSnQHNGaAKbZJC1Xk+KTPhsW0cSRVr7D/3fHOAGf2G4KWKg+pAo5ekYwOW1Q0s/5RqVSOUnwmnollZyudbmNeygiYu8wi+F7xEc8rL64Q26nW0feRzSSPac6Ufw0r9Jd5mMDOWswUkfaidMkqtuHazXZzmqZVJ+tRZ2q1bms69SciuHfjP/LvnE3k+ZXuyIDKkf8j/8YXR/5JhI7977Y523gBOO7U+rH/mZVL7L9ErPHTEVlFjcyOiZavoXA3ec1qs0qeTgtx89wbrTj3VaYtHPlfyv815vQogKtArAf9I4Osdg/Q6Yk5J7HtFehHsNjh6XxMEfTGPiqwFsFIVBrqAO2A23zc3XBXNHwUxGZzzQzCbMwIxEdANtZAb2e2EbwjrbwxQnXs34tjr9Z65ZhGPo7GsAKRM0OhfJvNKzqGK3mJ3Irvr8a+w/8znpk5OUf+vTk78u85U2XcnbKGjSh13o/8r3IvO3cd+d+75DyTx8y2ZXZkhlGO/fcW6dj/EU9qUFrDCNeX++Wu+TeaugOvcJgbhL0YzFMk0uLhblzpANMctPQbtSVcCf/c6CSoOVP6ezbOud75q/bfnfTkZHvk2yT8ahI7PoC6ZAdYe0ewWgHdCqhRGxshVrsOy9h/fXyO4+/tv091rwiwUcHvjBHFpcpNyRzRfE7vS+PKGXgtH3TeqE/YsvXJuH/L0Qo7LtEgrxxYHMv8niRVbIEHkxK+GU13d0T3tMCRf6VTBl6P/K8rF1Z8lunmR+zAkf+RsisdtCfz+NSR/yP/2eaId2SjLdzhs28j/y2Ph9N3+n/i2NBX4uWYzwIOTwt5UyjKjo4gMKrC1kqzAYYST4miNlpnrd07V3ajSDjon+sj9MIG2kl7d0brvyr/qdMTgW4UZFvH4m4EBMX8d36KoZ4tLgz1HZyfCnhiy/GA0Z8h5PlKFUNEAYvMEXeDsnFUALMGzABaN+Zf0lQ6rhjaO0r5DdNxfitHwySZGu89zOjYx9edkL5mee5Sxkueu3wVOexD75Aa18r3rzNAFwJ5K/4d6YnzRx7IlPaMR1aGAdc3m2c8qcpkuDI2R/47pyE/IA3jNkdF7+yd/LTxyP+R/zEpeSWjnj/zu7Leooei7nf689h/I+2Rf8nq/2X2P/J/KzndCgqQoyFpOOYACRNQPo6GvGED9DvHBLWMHP5J/p/IJwUQ9GuJdHO2RowzF0GQ18kn0qC5fpn5zqZQha0zfkYMozg9OlYeD2nr3wv/U3jbCLD6lLOL9HYVcWx3BsRnwLICbDMgmYNCbmkGhiNIiX170IyAua4OtwN4d0oQqpDEMsgRpHo6Z05FtoJSHaSJEsWVNoGSWNSkQghqexVAm2dRdrR2+Px4Kj6JxrsCj1mZ6Ay89u8ihEXO5Vnt8G4uF6NTVRXWwPUdRyBLUglfuFQO1c2MbjlvHvmP6x2VOf/+y+QfLk3NeGalx47853o15ugd+a/1/cpuZ3ox2oGorzNZjrp2hVWO/K9Paf+O/Kt34/Z4OZdWS7pZfQG7YdQMOPFj+z89IVKvSU9m6NBGNn7ZQyKVMWAQ+40LG2CR6zne6fL3F+1/UrJ6JAgCKATY3gEYK55Vux8zUJv/1sFF1ScuXnQC+oy8x8lt+bZj+/pu1WampGfPYvurvpRx4zs7bcyA+mCaiQRdrbeKILxf0L+juvFuslJPBMMG4YQVndhoUCqarhyLymma0aPmhYn3UNTvX/F+ZThXYDHSY8VzceSRB1Fesa0+Pu/kzeiOfaE8r+RtRvcj/3WRjQywvbf8d/pnzj73NrvfK+cp/jby+oz3I48c+c9TmGfyUvFLpouO/B/77/nlyH+OAnK6sBzqb10q6cymlZPGggboqOiFos6wY0ycKE4KUWOPqMEx8pvI+blz+JrAtPZXLwQl4wAlscJJcdPlr9l/CW+b32GTGS5HqEm+TGZAK9BWgTdU4DNDmQGxykBX7VRtVIyUnzZ0AIDiU51mZSAVaRT7RtCCpQx35+qA7IvuEcB5KRHhernemvDJUe6t1aCXQ1maiDiL5Awx1fjAFnY7XEch5rS4TykDTeNOXsGvRf6Y47fwjPYnMx7C87I1i0AjAx6jw+/zuPya44mBh7gz5yEbR+SBCH4qJa/hf9WuaQai7Nkj/0bWqMO+u/wjD3nZGzeaMt7JdHYqMwWPZHyV6Xvf95F/lPOZzB7577Z4B0f8Nft/5L9rllk1XvZFRNu1pJrmktDnVlBAwsvM8RGJlDt2qHw1Md+GZwLAgZ7Wu340j4fyicT9ul4v93bRqTlL/sLSPdnnp/6S/b/+8//89wveIzKCrNHrzUG4B24eTM5PgVaKZmZYmWX9Dg6zXLarM5rt0Zmpdj/6u/6dPC8Ge8ocJhx39XsGeFfPVsYwjll3E0jQSYhYhjkyVKuC6A3B7asujRjHqdXqb21HQoWxOVJS0WTuxOzyVs4/42rm7elz6Iig41jlIa0csP77Hu/vrJ2os3D3UDfcCHByOfS8nwFVNHSuYkwIkYtyNOetPRpEIxudgsppQCA9KvMj/zUvZGEpazkZ13p02qNzY0baKjjmJz+dh4/8O1k0+ZsXulnr+AdsWLgY+9j/B2gnOCOeikad+9X2P9qxKrwS7eSRf7G5EuGCa0z6y67VYW+EcVOrMsBXRvDpD4Mq1ncLhycx1KhPKf3h/sL1D9Jwt4a/7nJR6gM8/Afl38LbEEig4qu+zwGRLq+lcdlyj0AlB3J2gpBUK49jiWA28o2B3LCw+FzmnEXYGIGZsvBMSYyKxo+uAnfYdwVwMyeQ31NB4xEqgEbo4U6GtMRi60gqi3AeT18bDnTTnQq+RZiOXpsMwiGrbWIorZuykPSgrIiA0rD35kFztkbotIw6Qn4tkmuRlrHtjM6ZoRocx5C0WfUxGpCM9zPgjiPt9Mn4rppT5O/sucq5m31/5L9TsuKfSucp72dOw2fIfxxXxSMRAEd9ktqJQdf2XcSoV/04jvxjlcZRlxz5P/Z/tBtedvfs/5H/rrurzaKu6yCmzJ2J9OprtAZal5qWQKQXUgZGvFJ8kwEP0akU7hZPzOn5hs8kJSGJmqtw0w7OjzLn24rO1c/A/8uS1TPw13/LDZtX3B7UReOOACtbjHq3ZF2LfAZGlbnH3Zq5t1yNpxKiGShFRprNE2FWPI4hXZPfAAAgAElEQVScjWcEX30n0SqNaAIeCRDTtPVxE2EiGmoYG5VQ1IohUMKEToraNgTXnW8t0amQlKG3QyRb7PlxbOQvW8didwLBZMVDTAsPIKrLctFZqQwFvott87v57vhK2WC/mXOROV/2ncuxYvpG52VbARcFHCIYxuPxCrRXJ68zWc90Qjb21+qGzi/Zae3Plf9I0xlYjJtMlb7P2hidPa/JPCCbc13Go0f+rbZTGXY7bBIe+XeMtrKnv9H+H/nPS2R3HbO2/+xgcBEBCl8jrmrbvE9cV6Cd4MipjrcUM7ux4saJjoSKvlyymupUQ2CaOme8MY0O0rH/nq52OekOWI9gLHsn8wzz8LNxgUcw5QsAr3ZGR3Df+1izm2fWmRGeAWoEfdp7Nu7ofVf0X4+7rqqC79p82lEsOSTyHyTZ0XdagURAO+4+sqMjrYoQ9oS6PksuVc/Ptf/nEvI9jI6Pi1iZsNh6h3jmpCLXxDVCkFWB75zOc4Ab+4zrqeC55r+uZFcOeOQfer64cyc6BchvlSzhTGfO9o4jceSfKR65J/vcHSxvkDNeyp6duQyVrsp18dr5qHg046myteTOtchvmbzs6Du0Q3GOOJ6oY5Gu2EaM5V/R+sh/5/no+OY8t6ffj/0f8cqOPBz5Z7q9r/1nnqX/h31dh3e0ZrVagVjGtgYEcyVc/UpgSjZtXzi/mu/okdSEtsHZcq8jIaS9Crfu6MkMA/1U+28nPZVwPQqeMmPyKKCPYH0XRESwkPPc6NzsLrqnhT/dQvCTtZdVzBhA6iQMLxr6OLe4TqWyFCtFtUYk3tQJA/0uUtO8ErcY4LgEQbJYNqpj3+NOWx+U39PmdudbvcgVoi4kBA6JFwQ+m8cOIF9plYzH4jvVM5lOGde8NvSVPOyArdmYKkd6puy8vI4jOPJfAZHvL/+oXzKjxd9lbnAHEZkDhPwff0cAFvmuGs8O39fj92/vgES0E/Hvqp8od3HMR/51Nxx5J9/syXTKiu4Zz+3qJr9Wf8f+H/mvNUtlRzngpUegUCSMFgogJ4MTaqzKNO9JWo7PbFNzhUmWv9uguX8aSSsuxTujLndIXba2wd0yFzItHzcsKl0/wxVoQcb2vqf8pzk9mVEYlfw4oczIVQTr7UWXRnB24gBUALgy3boIlZGuDCQa7qiMta+ZsdTfZgwxayeONwPJVdul4OiWhRV8D60Ozo3MQpcHrgC+XqWKm7xDIWzkzLBSsNwi8ZjYwcLqJSyFdPpDIXQtKM7ngWVGcGf9a6Uz8tlsbSMdMzCXv58b1aqvHQco51MvfzMnjn8bL3zN5EbHuQZ5R/5nAHvGL1G/VPK9oz+ydXrU8Fb6bmaEq34r3UXPawitbKr0fmeXEecnY8ine7rwyH+8c+/Iv3fUMp76Sfb/NXIcscoSeMsDv1L+ZU+3awp/x46f88vl1qqnkeMhRHlU8e4SG56zwyQpFKVOjdaj4qtFQAsDjl6tWeUEoU7Xofg76vhbbX+GX0eb+fn4P83pqcAlhgFUBn9thOfeXwY2Z55m9tsMLMSFRedjjwczsJnPCRkhgslOp/rdyEg29qTscpS3YX3IL2FwwZdo4cyB8Tg2zR/wgEz3QggaFwfPSzMGcOjEp530qHGBUbU/qQx2K/k4zwnbB/TIDfObiffWOuaL1Y5Dvb4+/HDHMKmT0lWJVyqRryr6oBKKcrILaP28Op8c+ccQn+8h/6nuDWFmEcThxtDcLfAcg++N4NnzScZrXlLHtlc8HfnSl+6vNwR25T7KM38+8j8vtjBW2Rzt67H/Xhe/n/0/8v96+2+2soXcixPDe7J6aXulOR/VKO/wvIwLq8M5x0RwF+MqSUmg/KPcya8w9zjj32H/0/C26KntLPfKu4vKr3as/BLE57J+0Ohm4A6BZgSCCiDd+IJ3TL8p09DxJn3BVc003hPKjWaGIXrLlfNVOY0VHTLwoQZax2GsapU95Bt1nnAwtHUxwh98lNwJqp3YRqUOVKPH/UIejhCTx9xS6p6hDHZ7hcPcWjPqiPFb4pQtKvfpmkUaRscPgYv+Tf1ZGcnOaxnIyuSgOyT8FxaUWPH0TEZmimclWxlYjbRYOUAZ342ApS4ashrjj5f/ycmzp21uGL5S/neMWqoH4UXl+51LS30Z9A7qop6uMkNWuu7IfwcvyHu4zkf+vW6fYQTEB2yGuo2w3478D9ECmRxG+mGhjW8v/4p7WjwYl6cVQUssu/FDZvWjxn3nz1SpoCXySLsI8IDIFppHG8sa7VHn40Us+Wvt/z//9e9oj4cVWoG5CC4zx8OQ8JBuNoYvVP1VwGmm+FcOD042tkNMQJPxkE7NuN7E2+MppTVNWQGe3AUeqEhW4pSBgxL4Q1ynzqtXWbSjGAtO7bTQGvSjg9Ce4ZLTvKOAWwn0PlcquPDlXKJItIKBhrjQcaxyDNeLo3ZdkuC4g5jOE07AoiGraJk9pzO1PopLT6t3UXlodbcKVD66xsSQwpP5+neQmQGf0fCsFfKfln8gz6M8NfBRaGvmpNc6dJTDjLdSXSayynUZvUbu/eUX1WF7jr+7yrOBZfOK72f9VzKCOjH+7bj3yL+rTHnkf8SFM7mqZYa57Mh/bYc9QhptyreXfx/J1gPzaaMYJMk5GLLxvTah7/uEErMDVOZN2ksGDpfNZT2fhqraLpJnNbjfZv8tpydTBpmhUwJlCmLG+BlAQwCWgrFQtQrbwL5WQG5nHrjw1o9cMkX1xRqBbrfL/Znd/9tTS8znUDE+tZAShlIgQI4xhpIiu2OJc60A0ophWVsLEEbfRm/xURpjFTdyROAGLCCwFmGj2FG3gLAiCj5kEuo3GiaRyWjNeRLUVuSAiiAIrekkSXbciMZtt4LLlMzAYsYLM4Xb6WfazL6aAbMKUGj/6bpAqBE/N/YZLwtN+TIxwNEoj/xTX6Jb8eQOr0Ya6Xgz+uB32XuDDvpi+c9ADq7vIzoo05szGu3Kf2aQsK+MH4d1nVxQh/wX12c9xoS/Qxn03oZ31vn7ea5PlLGVvGY268h/nTN15J+5A538I//jKaPantEu/yz593Emoo8sX5kyk+3wh2RDsd8WCHvHh1Qw7fQm2WW3HOweilTZir9m/4ecnspwZAYm2/GL4MsfpnWYsCJ0BvaydxB4zHa2HmI5cwCkEhlNqvV0v7y8PPH9nS8QsiUd3184uU0rk/XQJwHzMtgMLBjdw27lChjNQP44ZymrBkUJ8OSAnrfBTeBU8QyFvjBx7HBMy1U3J4pze8DzoklLBTfdlZBTNS6HwIqHlVFvFzdeslFGIFUBzjVo60U19L6baqdwBj6xdspUvsoQitx4VDIZx4J9Vk5UBpRXgPrXyj8QKdIu6qBsPVEnZfJZ6TZ69g3yH/ua82SlEdfOcZSnOJ8IDCOfzGVWwmvj/VJFbtKR/079mU3FNdldvyP/9ekO0jqTO5SJymaguX0P+3/kn6n+avsviqpFntxbpIlGqNB9OKIXW+M3ubJ9sgH7EN58w8M81zY2KSCVGvh+Uf0K80zxSbCLP9X+s9MzuQckwq2466F0wOeydyqFnK23AxqLpP0MlOwKvx+TAmxN9m8AhJPsb1SonR0gzkORUVMDmkLbgH4D9MKCkvZyc6cs4MLoRZ/SVNwFz3bFI5jIDJkXeg3P02/5Das13/wzEmyT9iSbRosa7EAoeUZzh+wVcWzayRinQ5ljyH+o6Dbfkmn+JKWu2SmDe33oz562jAY8rvtAi2LnuJ7ZHAAi7/PfnfMr5bIjB7tgI8pOfK9SYAjKaeX/sPz3NfRaa6DRwjAtgXzgj4xv8btH5D/yWtQLK17M+l3xKdLHy0EeHjfKoj8ER2BY8f/MYM94vdMyPzk68i+XSKfaf1wntCY/3f4f+e+KLZOvmSxGlchy9kPlvw1e71WnPW7Z+W5YhdK4JY8GFZld4LMwDh/xMyk8xkqkOxUmadSRLhz9qM+xlnXF3f6g/afLSaORRKX2aMW2zLitwNkMMDoeC0fNM0OJRjubX8aHJrTUcLuKVxLt24WexGA+5ILv6fSJ8ddb87lvl5uEhzFTaoUzAe+3l0tzhkjGekn4ScW0JPmsyDPBeZkrQYlv5OGIw1MnpL9VPgclyaja5kbJdeockKDyLgWf+Nwul/uzVXPjIgjYYt/NkDfE/RnngyBoZ05+3N6Fj6VelU9i7KJrI0mGVT6MgDIC7D2nZ3Swsjmv2q7o1L/PHbmRz3LjmdE+n1/dTwbAMxpmGwdxflnfM7qhjqmcA9Qvs7Vb6cEIOip+YWwxFuTI1job2wzIIA3RDujY4+/eVvBTsf0oFz5HMnd+kFb1GnQnJutzV++PYz7yf+Tfo5JqU3JHv1T2ZyYn2G6m/478Byy2UfxohgMajdt5jjkSsrnqkx+HmtY70OLDnlG+4LC7ho34qne0A7yl3b7hp3f15K+W/+b0aMhRr2o1lhDOjFkFaGYGElXJykjPQEY0aKliYHTAjBwdJgxhEzZpAFxPd/gFjJVUz5+relCLelcnacR+GsQf1dEQiG83WHGoG91NQ7fpcrvqQHExALgcC04ndgBVDmLl6IVOWVq/dV7MR0lorx/P4W28ASHVFaDOfdwt4vLafJx8o8mJkySXiHVApACoJ/pnoC0CyZVBGvkqnv7kzkflkGfGrOJzpIUa3R3QPvJAl4E4f26Pafbb5b9a+9maRP4Y9Eh4YCajmb7LnAivs2pgv6N/c32A2tMXNUCZycfGvc74kFpPTujRLni5Qiejt+9H2XMJorwoD9v5LysXG2ekU+ZEVet85D/P4ZjpTVzn/Dmvj1Aucc0rvZi1v2P/j/zjanSLEnVErTP6+79R/rmyK+fqUCi+YDHdpGWuHQs7VbLwsd+rE8OY1ZKNbFnb7zeagxZisM1l0N9/0f67nJ6VQUclNFNOlQKaCVMlRDPDiwY6Kk4n3sTM98v15canNWZj/ZWY7Xu2l3o6w0xOaFuOPsU78akvdAQq4NxqWLPD1LxvZU91aFo/z+bcCEDQMolqr63WessTuqWOyopm7DAIBe1UBaHXx4qltQ4DpWnbAY44fPJgy4eicDuqV9DG3bzLkAPUvoaiCyNPyuIWSfBTHre63N3pfATEVmAQqRwB9hogPLZGs/HmYDE3ZDvzjrIZ5T628RXy753F3IHI9FW2Tn7/n+kWHZkKqGV6rAIbyBO76zCMw3IL/YYPt71Ph4z7zFDK7HOZGjcH/B6kjiQ6UOMm1Q5NOy2P/O/YzMzx+432/8j/kf/ZBiTKyrXl6lBBJZGEQYfW4e6PWem3Po2SCpu9zhpFvaqb6f37zD7PNld/Iv5P5V9LVo/GNd99QyaJwMByRSZhaGxzO9jNgBObZd9/5UhF9hmAFxyctDLI7LWzE0TORKsWJmFsnKzOI2oHhlQ+mSbZnAdRHtSBGGaNn1Th0JMfaaE1TKc58o7tRMoJB9KLnLInrk7Gh0Ytl6gfSnK+0HgFXwTafh35k37XHKCvOOVJRVx5QHzK9szd3SYsRRfkZlMLi6PFkipupqSgh8C2O05ABaqYD3IlseLHnX5HkJEDvgqMVGC9AucR6MxBzi+R/8ka1oC+OzOv1UNeLkdapnK7KGKA48W1Vz5FJyzvURyNNIdr1LdVu7Nx6G/RTsw+x/awX8yTG/IFiju3svHtwIwKBKzkrHJod52PzgtH/gfnVRZupW+jDh9wQNADFT+McvU6HHLkv1N4Bq4z/Pbp8i8bzM+USyxRPHRxOmxqSX0qgn52f8+OVvngZ4y4UHMbv2sFt25PXB03XFD6Z+1/dHpU6eBSVQarcpTQGfJL3hVIZrTr99bAEwFqFxo5jHTIVXNb2v2aLVSNT0PoSQnxoTFjeAZOlL6/cwKZDtiKG3DVD6KX3T0jRzfiKNFBUAPt7Tm6NIpvumm5LFQEgZq/Xi5PPAs+rZH5yzFJc5Aa8OdcIU9huhqHvu4+Ls9NSi5+QWhbKfZt7BLex+Ru/8d0IMeG6NMPlB2ISLwKDjnkI101OsgXKQgh3tAiFHzvkLvJuECPGa8+YphzfmVKYdvx78pxqeR2ZnAypfe75H9icMqTkLWztzLUka76fJdI/gY/Z44qAqeE3bes6QrIV43we3FUXa5WgD6j0a7jg/Ou/p7xbkbnFQiO8ryi95H/LMSxO23I350XPtv+H/k/8p9vXHp9IOkFN95ots0VfYhBm2OmlX7YUs5vfYgGYYOEdAxJITBHhzf2DddIv3/V/g/hbVFZIQiyZYeTmhxUrnc2ozHL1r8vihzhCVDppaA7SHR+jQAK/Y7PR8S5sKhMnplgXuJpPVnRggXoUVhBgsUtvRr+hu8SeBBCNhBPd82ooyUVOJQeBLqFmd2oKTzvenl5ajsQHKdJ85K/fUG0GP8FxgZD3t4qdG99PyAROt26N4eSko9kR6WVCZdwQ40/JGLpsX13IO/XGwcTtmjEJCpOh6v5RdS98BRT6M7rQsdzfXLOwd2Y8xxgjYbfN1l4WcNtETC+xclqlOEMsCn//Sb5xznlIGxczBlIiLqu88++M5AZy8gvcS0qA5vq5onTjLOd8UB0rCuWx3HrM5F7o6ODbVWOXpQHLCQyE7/MiNd2ZiWHdRjISgUc+f8e9v/Iv99AO/KfaB8r0ta1DMuv/D+G40t2OOu0R7d9Vlrjlb+3YQr481Indy22HG4QhJnOzWzCT8b/lfx3p6eoBhYNCRr+aulnxnDFKiMA6LtH1bu4MAgFKRJKOKKB4FYxrd15adWDXO2+Xqig9cO4t5+0vJIl+TV1UBgl0Vd0WKMlB+WOGsonpzQWrfksLMrHD3YaRE4ZTUjqJ9iBib8PyMZMEwqe4Jsm9H4vo2PL+fTi0urmiiQWcvUDPglTh8dqh5OX1O5NElrL6V09Soh5lFC5LvCSi9VCAbXOXHPGINdK6y/oRvgMlCrPVmAX5Sk67l6+PEjj30bZqBRXb7uIS/518u/NEtKl0mmRX6p1jUB69RyzZV1tbcYj2NeKh3T8Mx7oc/QFP9BAdKOea1akU67rR4fCjT2csml7docXdbvuOxtHXJvx81hSN3ce55XhVnZsh0cynoz07P0c+X/M/h/5jxsOnn5H/js94CSnhYC1qJvbE0WMsN7mOxlnev5ViOhdGtQtfa2C2z2gK530cIoGF2fo15UMsvSH7P9wOelq8eI6vX7dsjsT1oli0RROjY/aCV8QzceV3Fp+SGPq9rD593l5ohVxqt8jylK+VCCkuTsiYFStTBwgPr4MAkfjfbq80NFUvzjInYiQQ8dhW1TdQ+4ZopONcJLx2mm963t0rxGHDDLmaQUN4MRKFp6C3WjOGuLHHmorE369NjdFTm+02oEhKgllJP+xHfdqxTIBpJiUjQpACkCYj6k3NGsIXY895FysJIcE6VQB3PieA6f0IQOBGRDiNyPLRYdqB7Rl6/uj5D+ZQAS4Nd1zFZDNv4P2kfartdiVIevX8SbUu09yz3Iwn48RHabOVTGjJu4c45O+XZPDpIoa8iL22/5eAdsMyGWOQmaX8rUe5+BnPacX/1o7eZW9quRoHDdqjMgtR/4f1WNH/o/8ez3QOIjLx1JkDVQ7o21Fubrk8vTE27G4Ub6rvHdw4cNtRU3BusBtg8ojGkFzB3z1qNxE+1B93p/GJ+D/wv7TPT24szczlNiGPhcNWAUiMjCYGdeZ0YsGD8fjxiG5OpIgQwUJ2J0R1qaH5diPfm0JNHwPjxYS+LTTyyZkjRlJwCSJjuRQ8ntchSRhFHIG2HtvJ1fsyWtZBKY0+znkSUgRBqRQqIq2z6kf8yTIr1225ZwLzqPS5Cb2M3op65b3RKd4FPrHZfba3K16HTgjVgxC83jajG6Xy+0uh2HKZLTjQ66UlCdXQisb8S6QVduOjrVxm48pZg4c/6ucoUzmPMyqY5YzEFk7LV1dap8zxfjd5B/HvOt4xlXAOWVKfeYg6bpmgH62hvjerI2V/ow80dfemcGB8TKQjVXWUh5YFBAwOm6cbsX5zxQMjjX9G06QIi1ju9kpqRtLMfZIj4xn4npnNnIcj1qnteN35L8+B0RaR2d4pdeO/DNn/Rn511zphiUIbzxRgSvcSOY7cFgz9A3jbEtlprkmv62MykohmjLQeH7GsYRbnIPGAMWiVKTdPpM/ZP+1kMHOkkWgE0HV0ihRJ+udtYwPHuYNiBDj+0dav3qkq+ZNkvsttyODozuUeeMz1G0vKcjhcJyk1irMvTxr6BaHNNFdOy2EqzF2Y/Dr7fJ8f+6Jx7ABTNW0rapcr3xmI35H+X01FdDp0d1eO/3iyD7Ot5JFbc8/icZqKT3PXPBAqHO5t/wnoSFex6VOpdINq+d5vnReGJ8q6SmgnsDJiREtm6xDZG3kpiy8KQI+nUGWw7BapkH2FhUSV+A8W8vvKv+RNlGKo0OZ0TLTXRWgjbpotjaOB5KKlFGHrgCz/j7Th9k6IeBTPovQcYcuON6KDhU9Knpmc17Z+ox/cTw8ztHWVLyCG38ol+75EAKS8Uyci9G9cBTHNvJohyP/4+ZRlAX8jOtw5L9To8vNWjYy+/Qb5Z9ThfkuwGbqCT9IJV2n4zLo+mrQAy+uhHvSh5V5oo1yKeQklYdJk7i2+cPMdqz0bkTw8SAi27z103t//P+o/YeTnjrpLTJ/ZaSo83ATfbZTmA0SiTUDAjlQ9EtloJGMVL9Mio2u9C5lqrO7I96Dj9/WhjCGEIXmo0TjyoNU9Y1iTSn5X8s9Q5ERaoIkQZy9ECr2BkHrc3uXRiahhH33gctQcM6SSbKr1wAwqCmtf9BlSBeqvQ8hc4yDZMdDK1fYhGQ+QS618omfdzXs/nLJ50MhkEQROIDlf98FW9kOc1WCWSWjkr0ZkHWK7gvk34Cl/FHRp1L2q+8juMqe94B+fg/OitaZfs0cBvxuJomRHmi4Mv08m28GeuJ4Vzyk487Wjd/tcr/ruAzGNuHDHWNfyewO/1d0G2zBkX8XSFHxZ2VDs+crvsx0we4ar56rx33kf4b7vo/898q4Fh1D+FAcILoUnQtIPZpj+Db8t/m2buy2IUvKQ0tl4NwdzX1W/KvXncyxxEp3x5EN8vUD7H96OSkyZQRO5jYUiU/gVrjSuxWxYl+ZwY/GJPvsDJqUlZY9eI50pAdgF80Asx4JbTLaZz4GGFxPgjghrYVa3alyW9uRaKCe03SEsdsXEgJnwkq5S1yVTBPbrBDAZ87p0b5YB9lBGAWvtRPoG+cr0YWmVK4NIJTVEm9L3i4ckzLYeuzVjrDpPy6OQFSjPoRGdCmqOvBc6pHrT5j35E8sxbnmy1RbrJwsnMQH8x1DzH9YTjs6+jYDuEdFlRC3mDtUu4AsguSo4FCGEJCmxv87y3/gMTf+ybhXrFkCqOQEANcNaRn1YwTr1ge0ma3v7lphf9nflZHrzz5m8JHHYn/6Oe9zLDDwyHqgTcjaj7yf2bXeXzXnxzYf4lrjuCrgzmrCh6b0sR75RzC949AMPHjkf6gDWm18RJuzksdMvjPb8Z3kv7k1DS5oNAxdIdLs9ZM6O7xBSlDBH5vskONjnwHiNueMUxmkS/hNC2YZQoSbTjJ5+vH4f2H/3UlPfmmoj/GsDPi4un23Lv6W7d6h0UrWzd+bUyRqUxvmafb7XRisChgl1OwTgD+WM9+hdeHkRpd29ErxmlKJgzx8Kz6iHo3k8VCFOMntUWEW5+g9HB6Tqwp170y9fJd/oP+PDqu6d1SUwbwgkHZQq+LgtJDAlr1FjT0zYXQDh2J16T/JgXJRzcps4IFKRWs6QbKiCJL0qKGJDuGwlrFY2+C85EZgTrwIjjMAkAHBmfP00+U/OgKlQ5Eoxb4GCGxH3Tc7GZmt2ExEopHJ2slAewQZK0doxiOZ/sVxVOPHsaNt2Blvxp+vpWFFs9SWmPyNTkQF0ioZXam+Fc2jk7ijMnXdaW5JGOsMaB75Hyns1+DI/+yUJlLvO8t/LUucFkDeDF1CKrPQTVErCKAhlRBrvyugn/GcyD5dZTJErWBQE89P85MaftTNcKcr0pD4n4P/d+3/EN5WM/zaQGQCkRsdg5hJvYB5BbfKSNj31qFez8lHlbLqslnfQ94+gzffp4++39dm0/w2BdLX+zOX4m7Hsk2OpQiA3mTDCF8uVCXPqS6TtDLSNBdYBD00Ve9Es6e25rxCDK4RXljuT+/RkV0YvXtJdmLEv2O/TqvVKWppfUrltl7tpGf3dC7Xu5Vaj5LEiDkCGm8oc+B8IX1blaTenqRbK1IxToaldIw0dwBro5RkBKwZ6PGmvIfm7ax3BZRWy7dqO3//jfJfMB4a5y5J/uG1AfB6KzP4yGa4jtT2Iq9jRc90asmJUMZPcSzaVqXvHQ+C8pzNOagGCSRGzpvpfc+TY//5u9HQIf0z24PPlw4sFER4LY8aj4U1R/uVjWWwbxu5QEf+56H5yJcZf0T6MbvzBmK2/hnPoWxGXav9H/n/evnnEfAK8aZBg0YNNLXUXcGKphgEF+im6HtWbivs1O7XnS8VCHZM1+21FLuiefZMYbR/KxttvDupUFmNedX2a3VrZhsGvRkG5ey/FjJQQilpIlFXE9iZeKZcohHNAQl/G41EZUDIYQdcbxdOKgZ9wxH3LlN+2HNxddtJg4Rp8QkGZ7+Yo0dEgxC+R5EVvm/cxjslGqrl1rU970LroMMp4lgMjFjAB3gZKcjT89XVblTGupHjidP66MTnZnfvuDFL26APzb3SfQ7Se1JMgXZKpHPSgxTOpuO3a2IN9HWZUmdbK8J1B9JYMgmB60osSovPK5rJB/JjbcizWHQve10J7nH4DDREMFIDkj351xHVGzceHM102owbowiudGP2PI419lV93nkOaZgZh1EHd9dgNY+8CM3IB8ilWCwkuj9VuGYc447K6s+M/Bp5H+mCMlMZ0xQkTGLXMzmMdi1K8opnlY7RkYtrFt1JwtYAACAASURBVPlqNqdRJxz5zzBK5PAj/3Pd/x3kfzZCLk0tGKkJFOUGSFSJCaIWilJbP9kp3jOF7/yUoALyzWX8UuCWOxKnnSq5CQIZ/SMbk2KMn4z/d+2/D2+Tt6JQV6uVPYffVcYqPpMZoQykzcaFv7U8D6rAQSeYt8u1cTgl+wtQt3Cmd+bDL2iOaEw5J9fLy3+eeYOKHACGlOrvyK2e01OeDL7YyY35TVxMoON8dUZZOXD98/His3TLLIjclHzKTFi8IKRjEYhqOU+0ayMvUDlwmTZ9Jfk/g3fduXUAf8NmNVwCSwpHwua0kltLfrS4/A5nuKIcD5oPm3hkevcQfWpKSi6o5ePojz2VzGT0p8l/BiAj8KzATAb0K+diBlQfBbEZGGWJNZPlRKZapwxMIzCeGQLtr2qjoiHSMs4D36n0ekXfHfWZvTujZbWrmdkXozwbDqedqj7qduazqWzgW2gzW5eZDY8O15F/du+jzjjy77mown9oLqOTjjqn0skzyXk3+QcHgGQbBw13H/Yk6B3t9DnPDLZA7nK0nGMLe+PrUPq9jOPWyp+0/+2eHlXoo5GL5tPvoqEScMwNiZgZ0xvjL5KheVwecWaLlLKaZKcxuCTfXoqZaWjU5zDoh/aiDoZOCS8qNcjEJxwdTgW1M7Fw6iPQHEBeOhfI7AjHa5J++9XfMWR1JKLVeIQ4uvDisHIZ8l7gAAvV8WD1wtknPtptRR8yS7YaAzCchblBkQN+XQLbtJiEVE/h6YpjI3O/UTU5HYs6QR6uXFusomww4bN+DecGaAaSOwf45PGfLP99vgN3Bg3SzytmgDgD/EjTTPdFVtpxGlKHI4SsabsZmPCcM57FxHfiHLL+oz3AeVX6twIjQXWYU4dziu3jOysa4vwfnUsl+pXjQd8H27Ya344DMa5J5MxxpCtePPLfaeYRxJH/iMkyXoqyFOWs0nVRH32W/K9kmWVXrHWLGOFqTh1hGnaC+2y+cY0rqawgeTovl9v9ernf5L5Cwr5yHQtd98HOD97RozPXiKDcFv4c/P+Q/UenR8HQI0YWmc0wZVJ9JmNKNAizv0f/1If1pAJIO3Ut7OkffImnVt7KGqsk5jt/T1zaqcY5eDy5dqkmJ7fpoQtXA7Ky17N5BQ1IbUjGf2ue6thTjkt78EkS+fU+L3FRW/5QczOlWAALm4xBikhU4GmH5HqBqbsdmX0L8II07Ew1nahfcjjy5Lydvu0Zifs3ZaprAfeoWmU4WSb2URsduHoclsLmStzthOjGl9VS5TddYy3oIHc5JQPNwGwEk5nPF0FZBPMVaMPnUF/wxMbqUx8p/wg8M90VDa/G68/og8ab6eah0wrsqi6NZiOOJSpr1MFxvfjdHBDP5GnXIanWqNorqPqcAf0V3fq7mbvWRzhrJ+s/Pp/RZOZUjvwwjmXFT9U6RBAa+5qN1Zf3HnNbKjpFman49Mi/7vcd+c90WoXNBp0rTP058s9rxQWKeqkyfy8fD4h9HVjbTL2+BaxkSvUdv2NcJdVpX570QsNeqEA2o3f13V+w/65kNSpbNPOlMVgk0WqJvAhEHvU7Bp6jAXklxDvjDdhziBP9Krkl4cl3ZLmvbopZlBwcCZfie2y0Oh3EqmKSfVI+YpiJ1WnkUDXyccSRpF7JA9JQMS79zBfidGDOeT/SsoWl6c3BPoTkEUpmAN8uMG20oOG1kx7x+nCLg1Wd7Ng+0mv+LPVlYX2dDC3E0uTpLk63hiG2HCO6Z0np1S8WE1VsROO5aslrSUy0ROs8DEPfQJ1fycDKaFXgiBmvV0HMjZl3LtGePEL5Uf57kYmqnQr4ZsZ4xxBHUBhtY8y5yMaV2c6Ml/ndccVy21vfCRLHkNMfTvtekes4M5LV3OI7LJH9v9zZm2904VxZ/sdE9Ip+sxPUrF07Zd5k4siLPI6+vpXsYB6r6RJ5d75DW+f6ZTwRv8PxHvkfaYnS2WVqXoBFeTzq2yP/3uJF/Zzx4iB2gjMEDcHGZy+zSpuJdD9f21j0Emfy2CJC1Aki2+wjXDbF/RMfu12utMHMIITmSKWrlcvaRfYXucg+13J/0v63QgZ+becuQlSCusIrYd5RtnOgwONKhSAC7vaQ5PEQI1ilLTStn8ib79xVN+pw3wwsRMf4YF4n1WjK4Sm4fmm5QlrmkWvBq4Op9e3Z1UAzzZqIx6Jcw1zCQ2nfvV6r8Jvy/+hcgZNg2TDSDZ+eCKe+2hPWIg4RqsFZOHWBeT9N0T7RnGko5KRKiJuwJJ1eUQieXMBKeZPqzMoEpBhDa8UCFgNCbIqdFCD1xGFytNlFJ1ycf3R/ae+PoIv54AfKP3BSpp/QYZk5XgM32mler8tX6bkZJ3sT652UaOQzAFo5K2rGQMpdxTTf7yht1Zgf/b4CdPh9bBP1OAL6HFDuK9BoHyJHR1riuCK96l6ZC8akX36jGkNFj0iLbEw4lsjj0UmsgIyXgyP/lazZ90f+HYl2dNyu/noX+ScW7qPiEPs7OTfs2Ij0p3cyoqfUsSVDxdfjkn1N9conh/GJpZcNVNroDxsjUT+jvRh19++3/2nJ6kiUbHkyI5Yp22ppO0v6HZJ9wwMt4w4lDUJBopo8MKvfmJ8fEoM257s4PVq5TIVczS6FozGIlgjPx7YuxEGgf/i+zl6j4H6/3CSEzk7YNHRNaDyIj54CYTECm/TjC2PlsqWj3p8M2JKJBPjLaZh3n9dU1/lZpWt9hdaAd5OUBe2AB1LHaGYyuNvtenm6XC//7/5MeWb/+4mLUGjNPfr3SS9JlQoyQpr/ub9cnl8ul//3LCdHvURDnwSU0mYZgxuZyRj0w7gKbP0E+X+VnhAqZaCxai8CWASOql30GWML6Ac10KzfCizsOqH4ftVPHEscLxrADJTw8/nJUmZYZ3PH8UbQXr1X2Zds7XBN7L2w8eM1TmLsHz75Gh0Jln0J7Q0OUaRZtoZr7TQ+kfHs6ru49rO79LJxP6q932r/j/zzumd0/xvyLwWEiJGkbBkyudy9QxKJIV63VuSKcQzhF6h+1hwm1XGvkbtPe0fmGfHI9eX58kIpB03nCH2EDkf+++qI0wOebmGwZws6U0BRKCshRQOcAw0PVcmQUWUKYFTaSeeLOU0l2GnFY3j/0xj4lR1F8EVAgZwQLVMH8arqCEZ0tlxUprnta2puDiXqq+ZouB+Ogim0kIsHGETSBH+SRi2sAJ5AezRqakQ+Ns6Ee+wrKy4NpyA9nrc0yrONDSuFLUqV6vkD9HIhlljiUuaOF5XS6YoU1bhdL/8hvr1d/lc7jLkyzzLJ9KSm9QNunDorkpP0Py+Xy/+l3C0NTOZTHW6DT7Su9/vlfrsZyY1CFJXYL++NeQFqTNMlSHjmK+Qfx5axNS5r5qisxM7xSwC/uwBvBkiG/gtA/mhfu/oWaUJ/D/fB1EUuuq5OdDJMrOvx3L5EJ2Z3rkg7fKergh52qbyc6UvmoXFs8Z0Vr0R5QX6MdI7jxbaVZ6MTWH3O5C72jbY0oxu38fPs/5F/z5WVDl7pIJQZH7b5A+Rf7thpoeJUtVWLKcUwCM0xpQgIgSB6bw0rPwnN/0EYERacN1p1M4orF7eIDo2oiXoW9duflP8xvI2FqQtLvnulSicatv7u2tCNguoRaKXUbRFpVSVp344kNaeDK3ZR7sRQM2jHjP2EZ/oqmcKSowZLrTHAtgsp4Dn4kw41WqnqViWECik/EYhviuZ2v1PRBA2rIx+oPUt8JBpIHC/6LA/SZgQpJA2/mnogpUaycD5J9aDIxpvkd7W1j45vhYB0yfHyUgvF4yNzCh8jB7NVRLlxFGX7qjkgTfHwkY3FuOgdUVw8gY/O+Y6AdnTGzs4/KARTLi4lZ0iLLTAYpfLb8rv680Sp6+XyPy11iZwzPenvDijrvhb323qTrJPmfNFYRrnOweT3lH9U1rZsxiFCSyv0wU8MQD8R8QqUrhynmeMVuXoFhHecgZU0R93pwe8oZ9m8EYRX/VWgeuU47I5/1s5sTDjDLN8qvuudhXHF0IbsOiWRvbI+0eFAHh2BiWwSFRdmZtYqk5FRVuanBbjxgjz11fb/yL/P56x4LVuzVb4gynSGwaKdiHyb8dhKFma/j3JOBrc7K4R5vMzaE7JxycWsGG+0TVouXvD6vOIvRYdtIliQ1wgkNNDIPdukHq1T1Pe1zfl99t8KGVRGzy/umKxXCkVRwWll7FBg9G9vwLryZz7XHXZBgYa8RRQJWUNcwe4AvpSrNzpnCe4Ti4KgVjOA8I2W7RESFZEGA0B0Qejz5X594gs/W2hdc3pU7bTPLQZLr+rRBgSAqpPDpxHSvDlmyf0+uwNWL0DHK0quv95PNljZ5T6UKxGtJ+h2ktXhKrGZlJ8mJ0L5kBNo7Dccvjk+8qXmOxHwsRw05mmiPXlQrNzJ6aQCCOwkNjLzHNipp1NsfslkAvxLdtbAEeQLzVSW6mTdbyn/wcBVYAxB+65RzQz+igXzd0bHogYDflc1gorKaYpsjOPQ31SE4xjx+2jwYv/x2cFgJmFgcSyrOYGqgZwYnnkFcPGdUqST0LKduVd2rTIf2fcrwIj8QGNahtN5Tqhs9j6gQe3oNwUibfnJb2L/j/zbws1w20zmd2Qg0yFRpzIM8Ser8b1K90YojptBPL5c/vn+Ok7Yv8JF6AiB+jgRtIP8gN/0htTilWn4+N+NAXhuTBveau5Zh34YM101X7vfIf+D0xMZMVs1ZySB4aOSXwlkZHLtq/Y6FVkDexMClIsxZdEJwum4COyNQvnx3PjRPcjdQxqmxDWPPZInMuGOxoOF50Frqk9Bp2dPVzpFIzKL9myXwLZbNVv0Gp0iPEm+ix4dmz/THAJ+/0WOpRn0Q66J3sHzCAl16qbX5A846JEqAqm3w8q7m3qu8qKlvnulBCu9TUURJKRMxgkHMv2SVooTDhxtROv8zIZDNq9g3yoD1HxChjCvHe1bcT0ohS1Xy7bTIyqDDZtjUNUFLzD69vIPPJGBysrQZ8r8EVCKxj7Ta5FVEZ7Gv/FZJ7EbuR856K4dLATWMydwtfs7E0WkOfaXA2d+oqZ97QBWba/4IPZX2ZlIq1U+VRzPW9tF2RsBININgdzejn/Gc5lzdOR/5PQdXHPk3/NhphNWPMi/r+UfK8T66wckfF7CzgmHUNg4JN2iMngEX3yzZx3cEQfOMBrfSmnbRXHolb5cWxFdIWjxs/H/e9j/Ft5W8UEm7Jlir4weQr0ZGBmFYVb+kUel8YpUky8c+eNOOGf8aF5KdKe+GSc/MBzGyDIfKoncNv1H5079fz4ByE83rNvyd14Pd2ikEiZxtQ1Us38lF9XIvTz9yIeROrUk/Vg1NbksjO8EgpO8+kCmtkyiOHWyWmFpeCHM1fMwZvtzPo9wXde3WKdaps2njhwC10/P4SRysb4aCkfN0WVi6LzLy7Lk/XheG+3PWtlKimFu6q87V+YqN7lpQW+tqpzlxfkBfk/5H0+lECBmOoenP6+alrH+TK/NjPpMzFYA3b/r3SUsU5wZMltmmG+mr1Xf6/OurZBbVBnMjJUjYJ45fitQNNqE/k1F3wzEV+2sVGE170ffA+k0zTSjXeTfymINc03WLT6DvMBqdiw5n9ELeTbjp8+1/0f+M56I+CreSRPfKeXv28s/VE8lGywRPlrMIBFc3U9k0JhsQi7s8rf+WUPZDI90/GEFpvSGB4AQf1n+y3t6HnEPZgo4M6xoHCvAooY5qyJDxlTyQTg+s4OaaBppbK+1VN+a2zusbzsZbae/h3m2CYuLYsLwVmGXUsoU3iaOSQPLWPOe8k+4WkoPy7pe7i3fRSuoqDcAQSvsmN4vtwbQh+2GxxePBVoWL1qDsKbsEMic6KhGQvNsHnKTsRXHgAaUuWjQUKIbj/t7hFp2J2bKYdMZr8ghE6fpN2e4hbS1ii5Uz78ZiafL9Sp5cJo7ZDRnYq26mAFhVKaVYX2r/OdA1jNOBvgqsJABzYxttIc5MB0TgFUHVqy4A2xnTkBcs2z9ZjRfrdNsfDi3KHKZ7o9OQGUfosM0U8VLcA+qIK7hYLuQ/5fhZnNZiTTHz+g8xPXSz/HfjAYrWfVry7Pv7wzK9sg/nLJXcnvknzkx6rNPkX9iWb5vh+9N99pJOZp5vOd2soP/Vgz03QAhUFyDeKRcN0m5hL536HLkn2Q6FjJQZhnvIIgEGxX+TJlmxgWNdWaURxbDZHJlYDRjXBGLILeGJ8mB6TwI4Lsx8wPjAbvFJyd2XMKnM5IDYlUGHmjaPYp5N7y9IuZBLwGU5MB2uvDcEvrlJKGtRYsCkwJoln8olVS4MADWsO5V2PTun1cNeQOwEE1aGJ7038p7qxPXnS8GCXyIJlXr4DJSOmVslVLMBPCPFFVpYMsSnLgYgpyCVWDyofkG1GMf4XSq5WBRWhJVcmuOEJfIpoucn1rBD1nOllfUHE8Sxng3zfeQ/25qPZVmTs7DDpCGmUFeYlyrGSCdAdHKweDZ1I4bOgvVWDLHaN5fBy+rKmaxnd12I7iPvI3ae+Z8ZQ7njCYreuE45mPwfD+u7VjCO3N2onuBAHHm4EVHKQJu4prAp5kThdwV13q0zVlZ8iP/lXztyCPyo65Fhm9Qv2V65NHvduX058g/82EaaIHKBv0bwwIzzfyQ1f3ih2UeFk7S836bLSeEQbBGoqBEwXQ9V+u0747/38P+m9ODgogGPRPQTJBSdkpAQ2Y8o9FBgxChgDcW8omAZ0x80+1+vSDzi/n0A7rXy7gUlfMpi3QktFenpwzz2hgXrVmTIro8s4dK6XFKj8ISkZFEfhI/uWSTnQIJ0KFTH/2Go3i5FFxE8Jq88riycm9ExKFzJq+Eq6NRENqVq5yp/0Klz0PpfnWG8CSJ34bydeQziGK5th2pJ1FEcvQsJ5Ux1WdjKdaPOOHVigfi8t9bHX++B4gc5BeO/LWTUtkN0wILXPFt3C74PvLvtcMa7O2Tj5ZwEg2KNECSZ2B2BYri+1nb5cgL5z4C2eh8pOC56CSbXwSA0U6sbIjSN7M761Xyu8wIKPu8utBn9I3rlK53Qtt9EMmj2uGhaONwrfK5jRSq+unfj/TAtrN1qOZ65D8/5YgyVznqmZ6qHO+l5fuL8k9YpO2i8h6lO+2ByPRXh8rvKKBv8gzxBzEPejbt72a9b7LH7LXxzE6izvqt8p9eTjoq2mgiR0+xK034LYkZzgV+lsOTGA4unWX5IbbwoboM7ctr3Oc3YdL3HgatDO3iv0BomayGzT29DfSxoYgEtFMCusTLAmUFFNMY2m8cHiYp9JSX0l4leE33KsnYsMoeHTtYdr7l9RAg1x2bpfbPphPahEeou3YxKBVUEKVBukIm2v1pzPMXawftGuPLAGl3pd/kSpxNFe7ag00R8R067H28alKPrZshKJ4QOzt9+eh+oDaqVoGPCjO0e57agkkukJS97J12BZoD24+Xf+6395OBxAosZsAkrkQFXhAYzhZhCkB3TiCTxqMBmvWh49R34ucIxqJTEGk39OXu9Mnv84iAOtIO57MG7F1SqrFmY8Y+43uRNtHYZ4A1o2ft0HonI+PRCvzuaIaMDtV3se8dGfBj+F72/8h/bTmiXOHaVzL3U+X/ppEtgkd4Hr3A03j32OtM53d9y+QYd3AorE2KFsl1GrNNt0oX/Gr5/69//dsOByIBKmUZwU5kikjkzKDgO96Y5Dt0aLh7nk8PJZKsDMkl4acZW/7Gym2B4tF6iidEBR2IRA3M9os63yTErS8rmKBbLbrCPauobz4w/S1orQEmO5aVMekhCR3XSVU4Cs/S4gvKEw9Wn5NhtQIJemlnL6wgPEIjg8tHM2RboTJ6tvMXHQxJHJ+Po+Xxc5EDGYztUkFw9JsWZuNly/eR6nvX5ug8kRNLI4Tqf60cdxsz52MxUWbAEXv/ePkf55rprq5JiuDWcCHnbJlnADXqtwpo7OjBmYHS95Vzdb955QjoeKr1y9YrUvg1AB1BVtZetC+ZTfBjwxX1LWaOWSxw4w05vz9b8w2JWp7kZO5C7DfSqRpTtQbRCUAMFP/eXUdvkz2tvt7+H/nHXOffKv+lXurgQu7Ne6ENzGa/bhRBgty7I8U/+BmxzZqvRLwgDMHRK5Lb2zY3rze6o0jD7jM95PwnIUulQyoqf3f7nxYyiEYyM+p9YpAsFoxIpjiztmcGdmmUMjsonm5P2dyoXPaD+X4YujvYAZZdEjO0VKAWDoni04pOfnFuJGfFLUtrh0pYy8Wbd8n5oUMRdgYQluq7FqsuoXXvFQ5m1ePwLqFwwvMYO/TTG1IoUHXNKuwJzVzhDbrnSE99dqDnY6NaPa3Vm3iZuX8yHOI0U0ywVKPj6n18B4A+bWBLLnC1dZOO31v+a/b1SqBygLAcqtImKvkZSKzb7aAQaR7pgbqvUzzPjcTfZ/oxGq7MkMX3M1CsznsEtCNX5o7HDkiejS06B3H+qIpGkNfHFNconSvwp7NjD4ClbDyVvGWgNH5XjVs1bO6s5KdsKxrM5BLnkLUzmoTPs/9H/rueqZzp3yr/cV50H40UsWKDpBEokpNKKuEPOT9xA0ejOTQUUIo+RO09Qx2/Vf4Hp2eFi6PCjGyVCWOl9CPwqBTuYEwgOd8V74AVpZwRvdCEqnKtIOBv+V1yb5pDIciB69Q3HRASVB6cMq8jnK4J8S0yTS/xbO3yEZOF1XJoV6si9ny5tlwSiqSSkDetxCfrJOUC5KROoPVb1jAwNeVCyeVmGob35kIPong7jYC4eoJFX0lYpsTcWhliLI7w4Lq8+nGJh3YFI+ywTYqG0JrLURydo3aJxwTqTJZXwO5t8s/jiGKdgcOMPpmDVqmIHHB2AJKNA410ToeqhHbtVOROVGbGsELXPDRsBLE6G9/GSjfjHPHZyqnUZ3r/PI+KZ3K7hO+Mjm+fCfeGT2RUnjm8MxnL7Ftc/8gjmeMR1wI/Vza0cuoqO54BmdXaxt9Rbr/O/h/5H7k6l90dPex1n5fFyPsZb32U/KOesDFiKLw8wBt4ELWBWOS3Yz9aLtVoshLun65JjvxL9bZcoUdz5Z9aKXpU+injQnM7Cro0OgTSFLbp4mqLdiPmzGb9mt+oYpjk3muFRtqs18UgnailG3vezZsIIAJnYD9rzOoRtNLacGeSOEaUZ0IVWfzRNOsxBdxSbOBNg/WF4trQ71RmW/jm3ZQjK6AOrFr7fJGpHkNz7pMA35Y/Q6dgev/UWye5/76WfifWoNhoLm7A8dDtHz0lldrb7nQP5EycoZlSfVTOq+fr2eUOw2tiuytQio5HNPTEPpOKb1EPzgCjf3aui1HXRpplgKdDoxE2PQL8V0DcqR3qqnZs9NdqLjvzyhyAmSMRbRT34aWW2xwdKuSDmbRpm6q9qoIyM3ua0Sb2OQOiesK5O+bOFbiCvsdvY//dsI7878jJT5T/UcY6B2oYF290S17uu9nyfVv6ZU9KiD1RZFAEIhNsnCiSY480PwT/v0L+w0lPrjSicUclHI2UjiEStq+FN3wRCHjGmSfJUt8C9Mm4GdLXqh507Txc2vJlbPl5HeudRXI3jlV4o6IOmn9SGIc3jlIOlEJVQV5DrVXQYqca4KeDG7mAk50evVy1DULXTLhInKE9YZ1Pgt0P2RHS6FYtqvDG+dvrhmb1QtZWH0FCAuWazKaA2qmJAmXxM1gpfYM0NK4Io6d64ihrAQZLEeM50VrimCHVawSib5P/GQCcGXzUSxEI47LPwGPV/ur9jK12HboKXD7KqtP+MLcp5Dmhfo9/Z58re8Dr5vW5f39ezOaR+fb19Q6M8s7M2ZytceZAoU7acbBwDKiFZ3+v6F45n7s832n7/e3/kf9HJKE/+/vkHxGl4IfrE9vZB8JUX0fNb/YW5EiTBEM6gFZipSJSy4CnvyH/0+ptlbJFxTMa/BzU6HM7oGK1W5GDDO2XzZDm2r+lVPM3Y+3t4dBmh1Tq0rwM3OWTM4Uxg34LiU0eyn6ykzgtVw27qZoXxJ6I7USQdGptAZl1zPvZJgY+CETgQgNYLOFVLU5fMnKwJpIyexCT1U6Zmi56FueIFk4mTs7E7q7MO409rh8yDXUh5WC01LeeVonT2jy4lkhKYUrqBQU9+lb5z0DjMMxAjh19gs9kf68AbdRvCKyzDaAZeMv0bvVdrgtHsIPAWH8dHVKfX4RzzuYT6cRz8o5GdAqqjYuKL1Z0GuZfVAyNNqvSYn6dIwgYnbZH1qpyYPn7bjeXPD7siozjxJ0naj+8UzlKGa/OHLvPtv9L2iSq8Mi/D3H9KfIfZauSWeLPZpqk+OjmUcY7Gc3v1IzsPMKF6EyzkXJ/Wf6Hy0lnxjU3crzo0ShWIGLGIjPMnf/Wt5T1955ngCb9OzHmB48Fp60XZ9JFra1m+51CyMwjjLu6swWwVQ5msbLkepRK1do4dI3PXvHyTi6VbAZZw73kYk1ORKxg0uN05AIMcvpH1lOqzb1vN35gIhha6pvLqGtIZhsR19NnzKPghderXYZKleUEEj0+41e+YfSQ4hRyImf3IkjJ+LaeXPLb762RzpVls+N2VBDFsPbkHwFeD1/1YHUO3isgHkF+dAr6+CLI7DpwdCTQJfNEWAH9TIdGPaukrIBdJHWcY3QG7PnJceOuwZyBlozW2Rx4fN0hQKdqBNxzfsdxR/C8ausR+laOw45NpGcK2lfyUbUb5xvXulLdu/1ge5Evd+aa8ebaKT7yr3SbbSjM1hB5/zvL/0x/uN/Mdvbw8XeEDa80op/3mltrE49x88mlFkPhg9fotszeIl9+a/nHktWRyfrnvd27lRKNv9dGzotl1W5u0Pm2Xqrm0cDYWxLgP49v368nM5h6a3F3+zPQxuFvwsKVpnxwdBrm1prjsDa+V4lPb1wqvJSzbnf8SEhi++NJHKOXO91xc7uTu8aHQVHaHhgbVyYTAGUXpGqDR3OTwAAAIABJREFU7zT5cjxKg/av0vvGOTM0r1ZfnGNuyWN40lMevS+Hg+HezwVcE06zklrqFZ/eiFwaqVQvwKLYVzLaVldDeAwqkrtwuLg9wZ8TpS1D3gWVGTjQ7zKlvOKAFbDLwERlHKo54Kpkeq/8TkMPXW7V6JzN5si/5XTf5bsVDWdAxr87P1VBOkTg9shvuzyykhbkjTiezE7N6Jm1lfNebpd31qBSpZlN3j29q+x0/H72XKfdkf+/Lf9iFqttT72OBHJRORy7MBIrAf7Jv0sRJEZ6/KGZA39PpVyMDoonI9VfkP+0ZPXaMOYckhEMn4zgZmYcdhT30J9s/EphLgbc4cLSn8zb22PX3CYiUAfKLsKq/aY1AvQyq/eIhZX8GOpVq8ZpwooCMsX9LJkkqrfL7fIsJ1F6GNPaYOgDUh0RwTZRhNtuL5eXZ6krYJXM8537h5pePizcCvG3/RJlPimRqDE5q9fqD3zaQyOURM1dALoc0uoBGlTrjXN62J8WaKzV5hRsw0W4PbcrgWpuoxZqq4MGVh98Nc9KR1R6iH22nswZlX72OewrG8VmwDoCShzPDACWADyw5zjvvBJc7DfTt37OsiEwOW2ITmO2BhVY23EKM5asQHIcy+xd/A3nvLJ1K5qtxpY5PCu7F/lgp48+J1hDaSjjg9UYKtlb0Qvfi6r6o+1/xQ+Ox3+I/Ds6J6GJfv1+t/yXct4Yii7SZs6ifNgdYVnZvR/3u6IH3pikgg6tSq5UsbIrOjb3jB+R8ahXf4L8j+FtSSJrNbEpuACFi0p8RtC38hotPWk4vl3eYYWP7PitA3/X92XWKgeIwLAfOVWwam/ynAHWB8fkwgrplk4OJbOLwiC5zjfddyC0VDUtlVV5oysy20U/vSJatKabY+Wk+3aBWTtZ0ktT+WCl3Sn6WccofAai5yjMmFbuASw0uxtt9loyrQ1U9l2/5A4CYSpxcPDkSYtR0DYD8RYuivCkhi9SrhKH+Tk25YXv9ytUvqi7h4r76eJdn254gB+5sF7+R/RcBIvRBmefI8jdZOc+Z6zeYy+PhQGwbwRN8Xv9rXre09tvrmZqdpfuGTivnM6Zsc1+i21ruwjE49/VXDKHYAUUog1EGmd/r8BExWcZ76xMn/v9h9r/3yz/1frZ979U/qebXjp5NS20IahGfMXxuxr2Jz3HV0z4wCb1ClnDYCGZUr/8Bfn/57/+TTYpM+zMdN54RgOG2CYzIEzcCr10wFIr/g6YOpKaoCE5LaBEaskloft6PhHUfr2oKLMLqtTUBwKrApqJue+X2/1yeXmSI5+IyDYnos6E3uFDhxkETJUj4H4gOd5oAsrQV8NY1FnjRP62W0FBbXYKdb28NL/HmpLB6j/CiKOiBHPYnJ7Wq5Qb0/GS05PU/t+c/useA1q7Y2iagLgTTZGThyaAnJw1VWB66vK67l/1FioJE8E2Vi1zreEF6lC28bNyIRHUkuoUyii7cuT8MC80P5SW1wo4yA4e+Xp6uW37QLfapqolA7c61wpUR1rMwESlJx8ByKpvVedl40Od/Jq2uxysda93LPhT5ajUdsLrcpobgDGdY3RisB+0Jfh8tT6rsZSGHX7w9qu6O2mUlnFN/LuR6uhkZbRF1Rv5IvJHyQ+hXHpGH992H+XvsP9jLh/ag98u/7n5/n3y76Wx4wCSKw0eoYcySXmV5fsZL5mSYdynV05Y4I8IQ2Yb/pL8U/U2VYS7LFIZxLew2cyARcNYKTKG+lIeuZ30SIznl4DaLxaTnqtTwYzuizb6Mmzlk4SwXZDqGPvSduZ1+13KGLPX4w9PIARKlRM7RlkQouTAaDlrOuFgLuFTBOYKcxikP2qXkTPlA/FjcFIiJyx3insVkC7A/CuWzELcMHRMUnw0Ltf4F0t7a/bFl5z4qFPbnTH2WO5SJCOB8epUUqSclO/WXB81T9fr5fbSXF0u6qD5ThzWp+dKXDhB89D01IwdoQ5hI8DJQOhMX2XvZ+A7A/Pxu+iI5+CE35rp1oSqS5aNYBrHhu1l3z+qz1d2Qe1MH3TtYM1olK3DuNmhkGcEfVnbO+sd6U/vLEp8z9Y0jmP1+dH1QDr1+Y3XQOi6/BT7j3yE8xoq1gVG6dqhn+pWc97hh9gefp7R9BH5X/GqTXFR/CIb23eW/5Wus/WRMGuOnGj3APKl54Rj/sp/Qoy++dx1Hu9zt2ga2YRK7kr7a/JvOT0zIa9+i0bOf85PiKLhqxS949cHL/wjQHR7SnaD57sev0VGbJbiANCpl8IpONHgC0FvUm2ASwXY3TGL0zm/PrrDLzehUnciaLTgcHGLOidycQ/7HeJsqVMjTpim8vC2hYY+yUa/OgatHzk5aCGNWuxAeVHqA0gfsv1BMXwyJncC+AX8gUJD6yQFC9r3/hIqsdStOMfzhf05cVWjRXsPRk5CyHKwKXKuS+yQgH7oXiWFwYmBpvBHO4Xte+Ecl4yXCogBE6fGYJs4RlaHQ0MWJUaTWoQcqoGlQyRdBJUzvbdrUksgOamOtlq+cVxyKR+RrC8A6lZsM5466FxmYCwF3Jt3+iCLz0Dm6BTxqHdtUwbyY9+Zgc/sWEUjdGL2wILXKbGvjK47gDuCwuyduX2u6RrX6DvZ/9zhQXfn8SjlHTl/ZE0yPhxkehFGNNMBO+NFvorm4XfIP1buZeTS7KWGra906O/6va0w3z6o8IqPvNQjFI7RTVOZ/KN8lOniKI/988fg//eQ/7JkNSr0qNxnCrYyrjlgGhWvPldVEYqG0caCllurhRHAEiBgCddy7Pe7uN7PRrSapsfL1V0MH6xuoZywtItCtdgBeQh8QrYVDUhKRgBqP5ihsUAJgjpVhsoy37miWqvUBglFvUytnBhJ6WtCQCTjLUwt7ozz2bauOYNhze3CGDEpV20ZfuxUlQD1s3jF1k0CjPRzy5HRsL5Gb7nXh3JiZKF2QXg5FZMfocJObo1RjF/mN7VqIO+20amOOLWtch8ZpvakMpg4JewnC0/SGvJpDruBvHtHJ3fSk7AA3c3AOJ9PhrhiY/ss9x3RbyoBvcQ1G4V+WS+XVdfWH8vvQdrvAnR0MKJOW7GbucZW2ELpL/OXNVAnr+fb+ZazMeTj39sM6Lp4vF03A/s6mtlv0dDiDHD8OMJMFlZAvtsd6GHDMZ3pjOq3SPfKkFd8ENeotIlJA9mYMtpom7g2kd64NvocdongOg6laquvQ1/RGZ/urOtsHNzfCNAqfTrrL/Lqamwrnp3pAVuXhEcz2mZt/WT5Jx1IVybAFRzKtGQw9nTWStf+hN/7PjbYT3Jw2IbydRm1bftr8p9Wb8sXGr2K3HhGJZi9sburWCnJORPa0YbcFM+323Ot3XaS0XI6pCxrQ0a/+giUYGE/7VLAOIRDNeFgR4dTcHp6+sygewUvx8l3uWzTLFfMpYrwpnOD9oUATZ0Xyhlp60V+Cys5rugmWq41I2FTtzbPu56W6JzA36NXBCS2ogZyn5Ar4LCa+IdpQlFU4gSS49iAvx5KaWUWKe+tOzl0WkIEeN3ALFSMyCIlzp0xVe9r3Ycr0iCv8RzYYeE8KnGqyenVcNSmmO/igOPaapk4OeGD050+RItxsDySe1P0z1IsQviaB8A7YuzvQolTPfaXzREPSKyGID1VOTlK/WoZIpBasdn4uzjnJMut4iEUjKC8KD25FaeDKhvxeMlPDuyRAjvIw6mA3w4Y6+C1byZEsD6zD6pfFMdoXugchI9AOQLRzK70PjwwWK0PrncGrCvQO3M86t+4h2pMu87Jih712n4f+7+iUVyX4XNwFCJfKj90mvaQwIpXkG7vIf/RUq7yorN1zeaF4/zu8u/n5GVbyzNbUSLZFNSrL1xBnLg4r1FsrzOtn/NWs683tqXGJ/Sn7Iq+C879PfJPOT0ZD8yEbvytG4uoFJBxeUlmuyoINbwBMu6ZGWV1eW2APUanX8wEVS42dvI+h2s/oBdaVKYnYTvzKmTXnNAQO4JELtSQ4zL0AyIZqsoUB4ty7gyTU+NHOxDdmZ3tVhCull1+SxOSCzBbB5oPAgifHTY53qXEeOEyzR9p7ZH/p45vLxQg5f6YABQQLHT7Kqe4EVYq62mxcS76wGqeloaOeJqi04WTQgK6u7NBcMuLoQYx/LBDBPYV+DeNNrNwsUkfWqKip1kJYNN4a2VNO33VhdYTAl5PLpPNuTocryyftUSNMCFHBYqjSxXi7BiJh08fRQ5cBKY6i+oRsBPBd9aCI7UMk5P7lYJDNAPFM+AbSWtOIy++iDVzx/3yLIENkuekBUToWfV6+gkvhxbu5aJssBE9UoEr7ziONkLtgYJ5tB1gvi0qsTsW6GKwskeAis5BdAa6VuTxxDHGuWQ5OyMAXTskGS1w3HHuSvvZewhDIiTJfsP1jPQdeE6+6NjAg04cL/J59T2unWSPDryTAftBThL7vyNLON84xmHNdxnfFmlf/mPTIycrX3p6Vzi9j33+/DD/jVy0XTJ8pPynOhSZO9j+vpGlYdfy8Fflvu4S8ZXPQaqs6EDlA16VTDZQZ89+93Lye+R/yOkZiRDVqV+dSmFk31cEjoy9VsA5hzBIkwpXgvI1rJEWcIi6kLm5Hd9Xct+3fC1T9Vy1jWcu/0+hYw1P3alqmlxwJDZI2kDrq+bKqmgp6H7nwzMBaJrjoe7yMBQTbyvHwDlFLV3p+Xppu/4aB8agVp2IXte/qwi9JXXFlR+94DxLm7MWOnhpF5rK9QQtJNBimJS/23uZE4PjZbisAkF1/cmn0lLSyfvml4hDqWjSmu3SPci+5vGQPycXBqs/ZYUNoMybbcP4TYubOHXKfr24gSI0Bh9ylpMH+utJkTmRLAMtVIJJyc6EwljjtXjJsTpsAPoRYKthiQALV2EFPHWetqPQ1of2F6TqIQ2uB6LqZoHcTMds0MICaRB66oozm4P+HeOYzQffQymKtKgcm5nkRRtSf/ZaIlsbBJKjvcpA5NgmgtaZRvBzHcuJxznbeADkI/3ifPa01VhhruzXJvNz7H+n/2ghmJ6Z5ehv7QNAfmeHf1fyr72v+Kgaf8X/q++Rf+LfmVyssFuU+az/95B/opcBOewFKIh4Tp+VDdM61n4mvd/zt4wnNOiDry/qzjjP4AG984vl36q3ofBVSzzgHNjZ3DX4M+Gpf8uTolA4FbAwmNOjPi11226jlBMAygBvO9d32uTXfBbLm26AWICPp8OO2MsbDzz6OeIkZwYNJEtFM1YRWvZXw5Z04Hr+05wISZyBCme080zVuvh4x6qGfPC8ece7V5jTcsgaOGW7PCTsfNcP8aWGxWkp3ubX3Vull/arDNqmLs4GbY4vKtl99OLJmBzsCJZWIWwD+s+36+Wm+T4CjXvxYVV7mk6juVJ8eqB19sYwP3YYaQwiO5qrY86xM0RxdwEnwTOhdVS6C405p4ZP2vS3xl6kuIXOtJ6U28MY3gK29KBGojlpd578AS553kvXd9DjyoRzwWw+EnqR4grWPjrGMhLZGKATxkXRBGQrzVlCV6MUGdig4ZMsmSRWqdPigxrFKvxO0q4LKdV7Gn9wfHe/YFYxoI0Bd3+rnWC3056DaLQTCOa6jVCu7SF3SIelGum+cC2BrvhN5sB40FoBMu0gA3HO/sBIVrYQfw/ibGBax1PRMhvXqi0FplnbP9n+Z2uD80G6xO/julfr7XdM584TMqUfm5eGyCfb5uQNRRCyPn6c/CsjkwDy6BHsq/3gzVLGMozzINx+qWS2V+PzHjRlQIZR0jbUrkvdYopQadiHMXA2zUwe/pL8u5LVpcCHEIbVKgsbljHImdJHPs6Ud1QkDI7k1EITejVxWkvhts+SokD7o6YsNFxG81cE6Lb2ZMfbNoWMa4B9su9WRPk2v0NVD8lz4vXQJO9FCQPL/eYcDDsWerUG3yeMD4Hji2i5W4XsDbBiCFAP46EZim/H4X56eapwXmvIbnZ+vtwI/OZgaX/E7/PkWNa6h/JplpoWpLuwf89FBCi0scejK5crvSgLRAE8AWUJEXNGpbtcVnmNmoe8KnukV4NgE1ODA8bkAn4b7UmP++RLPrZFiMYnHLbqcoBH+l95mMbeGpNTkOYkcP0E2+TjnB75gj0r2TNhnn6h0zS93ZrpSYGRZETUQerj4pNTj8R52OJYCi1IXohu4NwhqkXL05GIXrgAJ1cawM5rwIVAxPxTKGI/E+VmOOb7TuGdwhsWlYrlv9WA5iFrBtkmoKurgVp2Zvo9BbDCX5x7hgUo+KMr9JeI3CP9oQ3UFR5DBPIcj0wF4vK6zQvpCB2nym6W4x9CF6K8+c/UTpLPYvOUMc2A0o5G+xT7D6GJ0bEeaTofdQSBmTMb10DniJtKsd9UnIHGXrN5mUM+jONZmdp8rNxinCuuPbaLPDB8/9nyn4QNmx42Xd/XuI+dLyx1xQ5mymCHuT/1GRksLgbm55BDd7eS1Gz0eLM3y++t5Pqvyb/k9IzgZCZYU4Fw8dWj8dw5Yo6LgHzWsQCE2eBOKDku18ut3RlCl1FKiIdEbWlyC+/ic45AuwCSDacCf9mLFQeonY5Q7XfNmdBtBYFAfcMhY6tPlZJXdNbpaMAQqt8xSvLz6vkgrI7Jgfisqbt+lG9ll56L9PdABqvexztBlPAvMcDtnlNO25A5YI6Q3R8j7X/W3LLVi6jJnLrm8GsoFtxDJNfVsN7rDiy9pmvZ3rOQMwgh6wGPhiQtP8eStgQYW30BQ9sSW8rSa9FfTrVEzSFgnfxwKWUtbo0WHlHniJI09VRFwC+tJ1642+SRdvTkdErlX8lDa1yFL8rYSJdItXDKI2rzkbBPapsdDirwrjTQyZpTwz9wKNrL5dYu2iXWZMBOO4+Sk6X2yU5PzWdXx6UXCTFZNC9YRFPXHFGUgiut16KeoZykUV0Q0YmcyjbE/spCF98n4T2qKTL9XdmMCMQicGwhjURvGqOkL8M9TRaIaJcY5+BxJlrIoivAHO3iSjUkHD/kQJUgO8lfmSn41ViiJu9z+Zn2H+/lSjGCygA4SFvYYsLb6Ljapo0LnYubTBZEXrgcGb/yemTOC4NaKXCTbEav+E3pFB2Zki5jToCROvMfKtz4WvmvZIO2/Ux56oaPJ4jmgfKzbADNSTUdsiM1r4BV7/QK4l0k/MBlGr6sGLboP1+HPyb//5TLSSONIivw506cTLh22kC7jAok2uvZ7sYwNt3FJYQCFds0OskuqWyXJ7ad3+fL5fpkFct4N5y8I0v6p6gYLZfb/tIQkgYWnuSUQcJoCPDT7wD+7Viiw8Z3koN3bEZDfwQQx7LNWpwgsyiKH6XC2DsO6hVNdceFcb2cZmFL9INwFWHltkPCx9+cG8IFAvjEhHeHqAKYafEvDnWDuehmD3M7A2yo3Mxj1s0ezQGhkDJU+mIgcH4mhGpwVdIkNMAcEy5tzl6jGhoMQVU9sbENT/MCh8PAd88rwstIdaefQ9eYKAoCdCokrtRkr37XQ1bt+mKNkWO+kLBA+ptelv+kaIQeMKhvTE9Y0YN+PwR1K3S5tqvC1MHUMAwjOxOu3SylPoyqV9mPkfmxA9n+I0dJN5XUUSRXwJ+6WVSmy+Nhp7+9z6EP7PVwe0xrq5EQdlYzcLMS0ql9mAApFl8Mvex8AIvC6y6ya2uipffD4GA17bQPv9PHK4AFmmM17R6KmQDSqEZX7cYxRmhSOkvKuskO+XICxbvf2v4X/KqaBdd3BnEzPtmhV8XrploTmsbfTN1M1izKYd6+btzyr3HdYj/6uzoDoLm7Cgynam+hCQsuy/ce3bwDiGpZ9/RszL1eC4aBSH8gPdQ/bwhqNMTn7dquqWdrxqCkv2B6kzELZTCTGhcboY8a9lz3VT0R5eRXyX9Vva0LTE4WI0piwDKCqYJHIat22CoFkCosMXS8I+sdHkrMbj5Ow2iEz1qP7WLHxi7dKkkYZL/YSo4NqQ48hTkxsFJA/XJ57puutHEs+UKSlE3jdDsLmiMTDPhMA7+eXx9603bYUduJ8DgjK2Plf+SDOBJ8F8sbLOxDI54/TDpC7w6yHXhdD3XuZILtRFBmY/GvOpHmGNMFt8wo9M+32B1CJ1rvxOHIvDsBbJfOIZ47o3PWobz1b/rUChREyyanMFIym5dcDVB3n1gmxDtWmRJnsb3iToq8EkiP4EttYw6pNEKN95Me83MHRpTShRS62IoocM4f43zv5Q9FT/Rmb/yBrba6CkRs1hNyL5G4lu0JzUnipNKey6RORtMbHGsu9xeRIeN7hqgHuWyVSO90BSyy0zOip+hnvveKneEe7sDkYbhMJ9jPLR6S++phYjnQyNYmAjF8Bsz14GxEwN7bEXqo4wNFNmj9yMD78I3uNFJx+3AA0LVYbsh5xDrWlSORzS+qznpu/PbK7oFpCmfsfazZOPE9HecKWM7U9sw8fSf7rwB6ZYLcfAC34Npn/Dz7ruKHne+zdY4y4/v2cjnK15j7jLwZ+TzyRqff58h/xptbPOcUEUtCv+4AdhvIseLCP125aSIk20q90oc3xdhi9UDo3Bq9+7e4yK1x0fuqp1GDmeWhacgmu+54KkNA/nXkQ9UbmW6O89pai2+A/x+Vf3c5qVP8i2S5FWtUArVD7MeZStmCW2eM0u6e4ZMdLXDUfiXo98KGnpRA+/KJn5V9UHOeqDXdtaW7YcA60gQVUHHJZgbFcsmmOw3iUDpBpiJ/mowWTJW2C6cShhgeJ8wDb3CuQryWlCt7dZAh0+STBDoAUyBZmeEHhvAujwIg7IcQvCuvu8g2+JfmA9uOvxW2CCcEjEKFx2SXXFzgdxnxw40QI/dKZY0PCZ9LgQm75JPYF++qQVgApzCK0BUM28kpMDzXcQah0YeAEnR6polTYozEYbBQBDApqAs6DR7RLEGl22WdC4pKF/xPN4IK+i00TwS+uzga6iVHPFT4oL1/o8IeRmuSijufzMguHIfZykmixaFDopHStn31zLHoeDLJii2bF9Krm0c8DWSb32fBLrzW7tZS7c+WGItlyXcBPapGHSUa2JV20BH1e9R4A6vfPeHzsswJ1yIbGsooLgWfDvZRqVOMh8CINbKxbhn9sCTVO5HXo+ODttfZYWg/fr8af+SW2dgGR+wb2/+ILXLewhl57RL5M2gRc0qx3bg+M96u1nqUpbEQyNt4rp5znMsO/6nKyfhMaRbXAmkZabQzt4zHcRxZ+/07D+k5AgD0qOCztjlIxW3oRdsW4nw3NqSsbFem6GHDLeYTVbngPXK6aEByp52F9fIgeGywiWjhHLxZ1fJh21xi4aKMtyu94Gj/i+U/LWSQE+qxXQQ0gl3gFkKZ7r7k72SC0E8trpTawTf2aplXUHVkTCX/o+2GWrWvXvHJrrhoY3rmSxMbYzWBoU1jwjoCJBrYauE2/2hhI5xIpqdLuAtO7Ku7t3gUqzkCBmwk1EQEYkA78fuHhHMWbucdBi4UIaIgi2h5PwTg5GLJSSnEV+qFd3oNCCOg3XJd2k9PLcSxOak9n4t3f6KJUxpoxjx5CQG+r2DdO03JWRsFslB9TQSPFajyZw9fRODs2AYsshY/oK/gtIxDoKQgCCpusDWxwh4ZEJUXy9eRnuX7RbTTKwj3kEAM7bMM9zARd2GuXt6reW/qOEkRBneErBUOcW9f9Y6ykBg0iqKT3CGrhpgrURnvfI4R9JMx1Rw2O5nTu59aKGeraMiFGqgsAz3M+U8W4gmUir1no7HvhIeUSzOgpJwsW0t8PvVIkQ05U6QhapEG83R44B4S+WVHoKrnmIMjkHDiOO/+FoLF2H8GOiMo5ndGu4u2L9c6PuLfqYywlx1BZgWILBQocF+1lp9h/+O8srGD1befq3Up5w7hWBXtBZrCrmjHzFPZSM4WKtmKtEZ+wbHj/FbYK6Nh1k/NF/zLrvzH9UC5Q9rG/irrKhmSIt8K+qGQC93t1ilFf0k4SMcvqut4Jpz72jDkB15g3yYOoeemH3Qocu9cS8NgrCF0hqAKo6VdLK/zhqs7gJAZX/xF+beTnkw5RMarlAoy5EhYr7R7G6NxyASXv+NnIxQ1RkkkCe0d87DsxJI3IrucIq4NINxeni93CgFib1/BvZ5+0P0fEgKC3REotCSynpBNjjmFfUFiTJvAP9ghY7mDsDc9dSKgBEyuIVXiqBmWlTAO3pSoAJB8P8VHoq6tKpvT20B+fo67k11YOk2DvYVpPytu+uDf9RRDT+6UeXD9mqf7pLkqxU6PAfixEtIHzyBvnmgOEMqwqoZuYZa7GCd9B8KEhqXDfDULUhMJBBCQsR7ljjSnmKPIXJ4cH7IhX35XpoFxhSE6J4h0B9Na5zVEw5kOUGivMiaXsNLvQrP2t+Yg9niz17GWhdzxGrATCwiFHC/ZBGn9Q6n/pqBIO8kB36NAbNDNarSTAysblzg6dB8T8fS8yIY508rH4qxye3L1gO1YqpcJxWqk+QrkrThzRpPMnkb7hTl26AR1i9dD4SLAjXZ2NVYDTUZ/f8rwXe2/WKel/e/zX1fW03VAHl1YUEEKcfvRYxiGGb7IAK5llIlqDUcM1cV/vc6Rk/jdrE3kR32m4oP3kP+M7qjY4nh2lZ4cgkihGAnf1gqWVsyIFRmHs2FOUc91tbxPAlmyRQM7cpXjtTwRisTXvVMqMqrbPDx7qQ1K+eGXf0jqUXHIzxtbet9e4XkGIkY5yfTOLt2z9Rr57G34/yPk34W3oXDEvyMhKkMxKlf/5g7jl8yFhlMHQEzZwmpuBD6wfZqDgBL/vRpTSwGWHIfu7JCCspjPVompOSvPlzu557i0cncM7CNqWAvHoPd8aVp+rZREw9ZgDk3qk5MVqhYnicdWelgBC1eA4jGIoIBjJNsULjKGLijUcSCXi3D7h8216dO0JWz90Zbw5dJCcDR3hhIkvk+ifyW0tmqWgG5bwlzE4lkAUQN/vCUke628O0SfrPiXtPYa4PBIAAAgAElEQVT+xxS7Osc/p6eXVMK4s4eWXrakdS3eAOFUtpG0EDw75UNhsjDHrhFIYUv5b945a84xc9ldTsi4qzyk8nUE+OC3QORVhvv5GvfNIiBl8OV5ixVvAs3l3Pp/IMfXl6deRVLXAZNY32F6NCSV/4YBWvEG2mRRlSKDa88IE9HdSappoJCbYxUXCpGfMqzsyeNFNpQgzFV6N5MkUvGP5Iz2jSV1SvU0XjUdrR3s9Rj54fvodBhNkvPeNTDdW0wEFas2s9+rdyJYqWz5rM+vsv8jfvArhwggU2cIouIaRrrM1jhbwZxetTyY0hA5X60x9hlxjvLwjLPiO8jTrBvqSo1Ii6GvhfzP+t2no3fc3BhkQ1OvFOiuKYAvWFyjs11lALaLGpY741Sp2+aTcBQnkPvN5mLx7GtRvByKxhd0Uyt4P506Y2o7pH+qHCx7QM7BAsK+hneinos6+tfKfytkgEofFUWctH6O/yKxoqJetT1rqwuE90fBReghGyA9Fr1h38H7llCsyohnzP5239bU2975TVCV9GcP8SF0bMeL1oLE93MSHb+vdwKxMda7OmiTtz1hoTCcY0SnPayJGKjoSY9EVTnlT8UaWmlcLVzG4J0eVbA+KDSo8qLvCZkyB0nXSUNvqC8FRlpSdnHFz56p/4ynQhgf7PxQ6mNzep65OpiV83UWh0/vyCmmpH2fEP8ZM3B9EDPICokAM3/w7r2ZXZP09ptWr4m7lxNVBz95Za4OIn9rvxmTapI/9gUqV8VTBTda408nqHboaTFQpgtFT5QNhojtVucR+tnSo0TnSHy5hZPjfBeO6Jo02aKJAypHUm3FtGS13k3Fny3NRwMlMYis7NpsQAKiRhr2ky0OT5aQLgEaug/rvBLR2D3ElB0fcnBUV6rG1dMuAZbdhdML3DqBnU5dE3YQwQpkqw10KiQ4S/pbBkSqoSAt0e5m3BvpjnZ3ZaNjezObjeOIf2fYYtWWWmfcLcjmWo1RNWM1R0/bPIT/QVYQzevDKXNcNZ62PSruj9Iim8tsDcCyiG7XXJiaKiv5z+TBrV+hN6r3aPzyjr7K0cdQpYe+UDstWkCwV7SANmeyQwCOoMIlj1cBU7Jr0g2kbadocxx/q1/zZdhWGdnAl5684yVzXTs0XUkpF0lgzmyNMzn5c/KPTs8OsXBPrFKkmdLF75B5M4ErxQmFQfCCbYbaSYbEM8pFgnoqwre9e9Wje4DcX/Pu75d7O2UxqW0lAaUiEIS2WU6Ps5I8E8NvDdu06kzUroBkOnlqIPS5h2/YZPnEyrCrXpT5DAm9BLC14II+ykluFKcqyWwu5MzgivlQDGZEaOkkQEP67KZ7fVYRdJ8oOT3tjV6SqoMjPW41hfAac/HZ7+ixjyb9y54zfS15BY6GPRyoeUQc/qMlnUOy9WdPBfrjUYpKVL6nZVQXfk/a07gI3G5SmcScKVchThS6E73uBNleg5ODblhRxL6QnOuudaC6byIpg3QIaiEMfN+PHJ7IvWDKYxoHKF0N3sF6CNtPKEBQOYXq41zYgQdPmq/JdKtkSEmyCgfGnnZA9WgX+C0y+tQ4hGLST8irAlTM7je9J/TU/CoFJbIWtmFF7+iLo7OtAMKekjVrG0n45oq+FXiMbcyWdmYP8b2qjUpeMmfKma9icmM/htaGqJ4VHkDuiePZZfeRBn081fqU4wrg2sx+EEH8vqJZfLeWlNEZ2lnLyqF23yfzUeqo8xhPwkq6uxAwJsgO30YZz2ja5aHOV4tr2dsZwxdHmUEe5c1drjnFURC06UybzFLQwMJpeZasr7mKJGUt60kP7KZrnhAWWpHta36fbj7oI+uaDPWP5FTKqQ8FArQxEYHUgst8qSnGiDQFw7Ij/2c64Mh/5ygKb8sYula+XullRiFTANjHXMGZqcKcayiLK0kLbWdPHQMMQ9K7KCgJn9tiJuz3UKglY15GplEh8IKFOwGcrwBW1ZSBOghCXIrn6d/ZDfAUOcLOVIswpdvUe6Ca1H3iC1UvTxL8KStkCcYKwpsjphsBlAakJ1Ccit/mxmEf3JfeR9TI8iTPcx5TVOtSkUtOizjlQG65x1AYVYwasie97hqwFYj49N+1oAWe7N1a2JvUwmf/hgGahlLq1rLR8LvMPozjA4dlxQ6Ek4mjCdwTx+qBpW3W9ihqOQGxxCDhU5PJMf/i03li2WFNWNYv4uC5CzV7oyx5es9CZq6WA3j9A7Y7qgVL+g6kJdC/3C73to6SZ8QV6njOeCIc048yoJnZir6JJrkQ2dYlIjcIV9O4exmNbU0QF6n6lYFwUZaQmEz6k+esxTsUIelJl20SQLHDmuDRdnlAgjZVaZHRCQGK9hWsVDoEbMvbYAWX+Xh+hP1PZhytVoVF0DGIa4DvRPp5R2PtXHU7jlztZb1qxbG4C6v0eCtzfip+zDTTyINjzkWG32ZtVfwTaTv7XG2mV/Kh+MbHC/dPI8jHzRWQPmpIelHnx4pqhqQ/HYxWlRS9oYmS1GfDEPI+5XS3L/VeNBdCyCF0lntO7zSMertcnl4u1/9wWHhzzvhKA5ibHWD1Ddq4Bkf+WSpS/on39GSKOXs5Clpl+ldKPVNI2rZWIPKx8xCaJM4DwXqswKaOBOzUWXCZHXNCkouEatlYLFqJWUcZk5vtFc0wDj0UCoIIFj0Z0CRH0WitOoceT9qOgDjzZukkJr2V3da431YljmrPS5WRBqaeu5PDUSmoEtrfjCR4d0IdOvWWJJSu/X7jm+Pb67Sv2yrgkdAJEexYlqWOwr8U1vZubEciOzZ+PUL7vDeJekIHPLtza9weIAIAUEXhecQ6fd7UPr4nCIni6AIhJPGHnGbKdgadSFKxg+vlpZ1oWtlnuW1bQqzqQh0fP5136cFwizCFJQuqVg5QqFKm7zKY2Aj3TfzeNkJoo+XOZf5b+l4zuVIgQE9kqOQraRI9DUKL7OMt6qn4QhtaMEFPxXEvahArGrLess7WicNb+G8z+IZ8tGKl1nri+9dYrfHT1O8T69l2Qt9OurRSp4bUZ+TPAGIGQCIIq1RF9j3ro558rZBN+8HPlV2uQGt89zvZ/2wus7lu0yMUG4gYJNJXuV05q1LtszXNxr1D675uLKfxs9IILT7SoVpffC+b7wrP7aqoiu8qRzSj7U5fNc7sulU0XQ83k/1z2/y28NrOeSZ76HWIc8IqR6ISDP9ojnnfOCb9RJvcEPEjOqZHyEGlzOHEju9v40N43rznABO/XV3pJ3So8ZlMd+3w5KyfSv94PuVP9ZplPYwO7SP62PWvTg9ANzYC0GI2OP0ZzfXoXfuBRuVd9dEFmHfUe4gI7zIKC1iyuZVnjRLTGpK7Q9xPM2q73+QDAYLuvGjIHNNMLhRU0ECMrdXhdLx63tTdJLHbVpKU/QmWHMaJDCh65oWABMkhURpYTgkJxTNdOMinEHTXOwuHxQAqp7UyzSJk7TUKYZGd+Zfb5fl+vzz9Q4SMBK3feaE5PQ2I3p4baBXwIDurPaZWnaycgb/1t8AD+ierMFkNl3si+95E47C9/K0n+ZGDw9hpFCireiBOs5zGajKahlZxAKXooB9U7KAk6Y7Z/sj12GhbNop0S4S4nXYqWXM2XcJ1PnrxD3ZoGyCnX3kzRqpK8r1MChdHIweRInIm3SGlAQnTqd0gESVNKMX6iPHKAYpYM7UjeneHnsJbwRh5TvISza4hQE5O62YrG0GF+6whyW5XvwYCaJ8zEBOkDM9KDWCgXc/aQy75evvfR2N4QPNxJw4gjTuEZiGQQ4yi9MjAnwNJCNAcIEX0M5cxzye4guCkhyaWWmNxl8qG1LtHrD9sN+lD5UxxhZ0KJ+OPPOfxYo+oqbDjiteXc8xoZCCF8RtG5AhgkmZlI2UAjyEfMMGTvCktLwIDMsLTqyAo5o7lky6pZrtJG00UKUTxbr3yMG/vsI41TJbo1qBTZJasq2Vmf1n+y/C2JTMVDwzKt4gzxYXQphxvdYwpP9M25HCKQScSWGo19BdV5zjspWrxliiMCw2thnhoJSFOzNXL/3h2Ztr1CJX4XMbQtY7ckyElFtuuJl08yTuc3L5aeglJIY5uVd+a1HTAwXUWOoSkHU07uLlSGXh65j+80y5nQtQ6uU7tRIpOmtpbLbm/OVMcDstbwNwn/SwL2BXbvlF4Lb992nsWQytKRvMipSoXbxhDQYPM0n7aYL+go0SMCLzCCSyJqhbxIN9cnGnim6wKBqhoYSW7i+sLpvjru2Rl1o9q6M+2RpKDKPhArrtlxdF0DcW9y8k16d92BsSXnarvY/ZbTi3s0kB5ndSk8JAOYWSpla4unG2plsR6k8+meWp6eqKnO5xb1PI69aJZtVMMpnsuNPMC0yqC5gxEz4A3gpHeX2/b2cVkhxRDg3BcnV+9HkbQmoHNt/D5e9j/SKvZ574KmjvX4dzMccvmiP0gJhnmBGsQf8vWMvZVrefMZGScn/GZ9tV/G3NglMcyGninxEOfSJ9e7S3yKjozPXwuzmEmzdmzuCZOLuFwOdI25WXRa1xAQKv0QnEq3exJiS4KS8OWNZqIGlIM4EO6uRkN1e6FCQwa2haFhELLXXha8MpV1UvDbCPOmn/+6/IfSlYnIHVxFIxKEwU+MwSeIWeAGH5DO0ZGyt9UziEx/UTILFDUPm/R5NN3RTJAI3DNBDkp0eNPGk8PwaAZmpGXZGb7kgdPP+NlkFZIpIfY3V6a29cAR1f/3Pfz5d6KMDT6WHUmDl+ze3boJKjFv98ut6sk7dkmhlYP4XZIpgWNqCOnOzzNKVJnSg9EYKo/NcrNrboqin5PmJT7FUbXOF4ESx/Gct+8YWZrDENVTS0utSSVklNt8ot1RnqIgE1VqhtyiOFMd3xz4pTDm0GAz55T1+rtL84lRIWlZpxDLQgGSDEB2qahULEnyX1E0MXvUaVJKmPOGypSZpImyTovhBr0XzwhCtSnzrbagl6YhnvgypkyR0g27izL/OWmLDlBVpQi2wSeLFMGnPHxCvCa3tEqnEkfb+Wc/v53sf8YIRIRBhPAlr7c5BxPzaJDMECEcEKUrU8Gtn1ecH8rX3Ol8Y4O885E5nSovZk5FBVvIT0qmLzjRFQOSvZupEnEjEidOL6c9vJUcrqXtW2rYx2JbZKHNSimNdfsjJaNcvxmG84SFith3Ix51uuquI6dJL7MruUWyu0B/LU0Q89q7mGmFtNNkMfsxV+T/2us3qaLC3R3FIyMvKNwM0YfFI4yXTOwynkUvKiOgiSiqgHWxUbmeGytP/BpAHxosIOXzhulkr9ghFdFx5Smy6fanLWIiDStwLIXadD9S8myEbrZvX102iOGQBZRAU0L47dKRRp3Ijk7z9frpd0JTDiTN2/5ZK2FtzVDTP9rE2khcnzqhAnODCTkEi1N3PtAyn9J0+bhgZpW9Kbe0JcM7Os6HQyho1GPg2byQOgoMVAH1Vz9sOdfEOsSyu7GSnEPXhr6dTP/JT3DZpOuBl+gqnHplF7Lp8/tvh91AiTNrQXmUiIuHESrr6ohcHT3GSkIvUn9PWinmrFv0pD+lBNGc4gYaUhlTdFrdKGhuTXsgNmmn4SUoDOPxhKGnvlilZ3EZyvgGm2yikBqQ2UcOeAe6RvH+pX2P9ICMUh0fRBaiqU0VygD+fG7lZOglHJ4ZxFOVtG8mtfOOuoz0VXKXCelg21yGvaoHUCwWD3UfiM8aldSp/hw4mTivLGvHRmJa5etPT8jVAzMpJsuVuEaCkHRpglGfdiOil7Uns+4lH/qG9IeACPiuLM5Hfnvq5gVwsjkkb4bq7cFT7UUdL8LkTFmxqD4nBNcCR1gIK7HgZAg64xK84rF6FIVssIF3pXMD39OKIEEMTJ7dY7J8qjMIUTeQkbYmVBUwScznHskR/1EzOfL9frEzopUbDMVKEpHwz5Y0fAFireW1EtFE9puvOz2tuVo5WtvTxz+QUDnfrneKeOZVWw7GrYytHykqwWINbTkw8n9FR1MtftXDOh796lAYCh2YCgD7ilQI5NcJidcJxsDV85D0TjtApR+b8p8s9EFVOzxAVcYIp3zJJseooxbhcp2kXMrz8/5gq1ipRx2i025PXH1SdI/clGzSyZ9J1IoSOELcls+I4fltb7biROF5unGkgUSMLihSGHYdrV4ei0I4zeKNVA5GXmn3AwEV1PO1MvMkZptTs6cgiFU7rPsP5jwlSqd/R6duIye/n2Pd1bOEGIWtpdjIEM2hjnN/z9778Ki2/OsB807++iXMyKKKKIooiIqoqIoKioqKooQvKB4IYh3P8eJUUOMxkQTE885msQkJvH4Bc6eka6qp+rpWlXd653L3jN71ub//83M+65Ld3Vdnuq6dDwnA928jgzWd6CYkUU1boVNc/e2eewJC56QxzN8ymq5o+cZHsiwb0m7KfVknkgGx+Pb2FMb30YNj1xrGUdxKES4mdU685qBZ3jsu7mu+Kzij90afHX5n9LbdgK3I2aWiZ1yyrs1YovMidFdQWszyA+e0D/FuU8I5Me6ZMPq+LoR1lAceo4Q9stFmOArSox2NCIa5w+ZmI3UEk+d04NQhyODdtYCAOysIg2rugsqqXGeDmd5XqMt8cPvfXu4/YZ1k7P2bxqZ0uiTpLz4afSs8j7WipwezRkttbe2p1/3a164qL+wGrSp/oI0v+7EachTz1uwYk+rIXL+v5yft2UdsbDWbmXoiRQZUZnXc8lkQwa1llI4OPSINQKRBTItBP2We16/7ci9GBhAT1tSj8YvyCRABBE2xXOJHdpW0OYAC41GKFDu1AA+78FoTvEKvdkB804t1Z/PTti0AWmDWzlOGP8ZG79ShXtVehznXrWuwXoG3fvn9SB5RQd2IDLQnP8+1r3s6XIcddxTz78bAz8pj7ni0zVonh24CtxnJ4P5rMOgPC52HCDPIS38W+2QrhwtjFfGYd3RpByAoaZh03PJbErBPGemy47+OX3ykv8V7/MK1kbE09tWSvhe5dcxaDynEEorztdCfDsgRm6gYC05AnxexFvbxw/5vJLTLZIiGSdRRD+llIjwGsDA2TKCGaOFos5X215bjM02XMNh0ZTDSGXTkCxaZZtPc/uNh+fnsbNr5b1x7IeBIBN/js69RLt/yAVKgzpjWT/DPN5qjFkp2HOnZgfYNXWeQB+vOK/HNQLVt0n7dEpuQTsOz3dPTSjeakpf4jmV3rHz0bxax67R86vMwHv7cdUsEpkXfWBpZUhtoU6X705PbAZJ40qK5HiDBo2Sj7FK2q5vD9HIjNE00o2CZQ33AAuJ/rVzqkbbl9GJKRzxAGIVwKvUoX7W78jP9+gA+Z4dwGK6n41krDDBi+z/HVGTjk86U9KpYv4802hllvagfcY32Uno7q/oVjkEGSTnNcP3+b36+d4hBMhuVPZE/g6A5zWS66jOuaJ3xYf5OWcjGy/VJROvAJPmcsaCV8/AmG59+fNL/ueVO8ODmd8r3sNng3+mmp6V0K2Yu18o9nBnpT0JrjUBwvv1eWYoCLAoBNL876548KXM/jHvOyNK/chh+HQVjLrjhwCSaAkMR8eORfdiYqSlyQ6tXf+E+hwUUjw+PjxaegjWRXZPv49ObyjPMFWFXV1GAB+T8Neo3pkCkHGct+IdaixlE83enUul+B13GS+jGF46GqJezRqbYMPEO5JSHtJmbq+Tuncm3Ed6vOgSQCldAEkUA7h5Hocvo+3+DNW0xsaKggSHZQj6RhOtrKbpQB1AXCBtZqdhqJekUavpUu3yBjuEqKOnOoGDNGc7n4lagZzusyoCw4ARb8o/S9BZkDS/t3o2qNQB8Ffb/6aG5Ajmw6HL8F3HNncqq685RiK6Oc8ce3RWd+CqWpMaxJ1xkupreik549zE3byGWRIrR2MFNPOzKgeZQeiOjvfOsdPfMx46PrWSweNV5+m6k0mWq0v+j9Gw95J/q+k51y8d5i2HEsHUO2bvmFu8L4s1WJ+wuSjRHuw19p5ksRLHNzKav9JjGimUj+Gh8AEavqBqvCW2g/Q4i/yMk9pdSUoKiy+md0jyYmXrXY+GB78Saa+53E+Bg3Gy9taSkik75xpZ1DNfUOdnLEbnnEhrz+EAGfgMZWknWcPn1h6EsmlizcYI83JxxnkH6f5Z/xp3TGsH/WxZh9qdFTYF4d5odzZUjbRbgdPRoZT3JpUMBCf9JQBom23unFm0Rc7PsNokTR0GC9l5cuYkISquxwyIgtXNunyK9Yk5rsEYR3b6GpP1DvnPt/8MAPPvFYnWwHx2fvq5985GBuW1s3Ji8fySI427u9egvQfrlSPROaT8lOxYTy2S7cLOKZpoO9V/1TzVvXeFDfm7lZMuMmYR1zMrU81phWU7J/YsCn2pmrvk/z6ez5sfeX0ONT2d4ADLRq+lY/EdK6usJHaCjCZX3iZQbmBrai3EtBp16gp0hsGva96AArwmk/YytqKue+IiWU1R+ebxLN5WPas53mAa1yM+OAWmgxvBGFJ1Hp1u7DBfa7puHd6ARq2eTE/m9X1kFKFrlNO6HIZi02vtcgXt6129D07FnzI8T62VZaO1M30QByXTuRXv0b3g3tmPdbe26BLWkaFb7Jr0FA5rVbayNDI5nFWdoXGfPEY62lmCnHS2RGQozq6qbGRlf3dTWYE3tsnd7wzmfpr9Px3t0RFmpwfcloFpB9aZOzHnuPaYSjibp/N6oactPSMdC5Lnsl7/o4OXsVYF1jMvZOy23sjus3Z0rDG3FdBfOZJyn6eKBAV2zmc392q+HV0zb90DTXbz5Wdd8n/0IbK8HqM9r5f/2+/7zd/24zFXwnZczJ6xV0I3i4TZFk3z1van4E5/iL1HfvDvO1Nwff9uFDhrJaEAsa6rAb1Xesu7EeF68LtSINVfCBAVn0ajP7oRbx3CJMJTaB3PSSLFIo4NajFM2aiFjYIUa4svRuk8vnlXcnzGh0/ARZwK0/MV4vxJE1yBFDfJjE68sYy6RNoHAWlwFuGSstTRBEYPp9Z2Lk/WzY4282DrFjUC1fgqYFCRrwJVRzAbkaKfZv9p/of5nga+e0FtAXF6B9OtcprymzKQzSD5jLnswB2xiLsTea3POgIxl30kLJyE4I+1IxTQraJHB/C7Z7KK2OHJim/re9ZziTnPFN7Rt1qPjgYdr6zm2PFTN16M55L/4zoOWnrL6k64WGA7j3fFFNViVkwa6RCpi449AIWjFwj5Sejg7GtXCw61PUk+uKd4wRqRnB3Rdd1noUClSAQsW6G4p78h3W1kJ43t9dGoIwIL4hiNT4aTNFLfRjtl2toRXWMAXGCqpXSibUJ4OldI+X1ZZz5R5H3f9cZPR2qcOcvqgysDj5rGkcKrTnPUobrX5+euVQquBmYYfQUSV6BrFqm0Q9+A/QpwrlTxW9j/4+ocnbFuBc+A0tbhucPs6DOOzlVFm85x4tdVlq9yqPJ77zWLfH1Ph2P0plPHO6duBdIZqPP8w+kLCjD2ZJ7Mz4iztIK6PeA/Pp+xL+PQ7IjKeFIKXc0T9SruZEjpdsm/ruL7yb80Mqh2GTpB2Qnc2fvK6+wwGcnP9+J78c10i3AcGIP2QHxI3Bvbs+txb0+BiW/uYaK3H8r1xE9HAW+vZREedUZkD/352dqx2+nYIwo0GnWYztCddtUnes6vajvtrjUI4QezGFWszbtFmg5NU1hZVqjl09H2GvBLKeBqbOoOGHALrdUlpe/528PzIw7l1Q6lwzEaHf6/Pcu5z1OSHwO+ydFB17vgVnW00t+YU35OB3pXTsEKNO8crs5JOwfEZxDLzxJYVDht3U56N86KzkzPgF9Yn6o2Zwa5q2dWG8dnMVMGgwyS83rz35gD02C13ph/7hj4UjmJ581POMuL3dyq8ezm1c2hdZKa1MMsc3z/ThaZHjteOfDGJf+h614o/1PL6sqeHxQNcQ3bfBb7/HvFIFBYWFTdODNAohotds5MraOTk2v5SoO8RjKve38eBXaO0M8b2fXmn0oBZQz5L8ClKB7VPjPbmDM0HJ2hG57Q1c3ggXw97kC/a9tPkofYjrwfuovGCZYORx0Hs078qeRpXn6J0w9alQK16G6wZmMLr4DnLE4wHJ3hdMuRh55WqecbSXfMVLOS7XK1SVkBw9ZxsovZfgMY8z0Mlisg95b2P2MEBt4HWQcQbQDgCthWu/d8fQcpDrScAFdesZ73DvoqnbHHNK/WdDe3l3J96XiVUY30hlR/GdFyvS74Zo5grHiH9XoeF95ey8BiHYhXunNvKt7Xz3Y1THuqV/MQXmuAe0WfSg/kN1frWK5tURf3M/H/j5R/j/TcI2D7JS7az+28Mvn+SXsXWOGn7sSG8MTORt6dPTOi65qLAhcFPicFEoSHLhFdMXdjH5pnnI0C44tDjh04iVMjp2hqABk77FYrFKADJsDMj0WGVAfZXXZJUXbwrmSGEb460L0rmV/2cPIOHILBpkn0cNi4R0nLfPw2dvpGqqUeCYDGbjhDqAJ/7HwcHdsZWFYFadl5yc94K2f5ALQW9p+BZchf3SiJF6WnRV+zsqJfBr38rspZ6kDizkFk4J7ns3JoO4xWva+6tnM0QHP+/p65x7XQjjjsom4IcQ/PddfugPyZ/XDwXdkW44zDlzbdKtlifs7yXNGdx13x6iX/s0/ANAzareV/cnqCkdY7F6wcup2TvHh5d7RiSt0Vk8O6NSea0gZi1+z1XvfLrNl110WBiwIfiwK0nR7eiyoRP01blUp8bRorGrWpAzRSZ8emy9jVs534Z0k6epS2xpIy9zTOnnrUFtruMb0VTNxQVkLhttnjzpoWyYshdAV7daD76TxKvKiH70ZWtrjjwp7W6p8ORvV6ghHteXqa7B8Dpjy/DGan74s2vjsnY9e9423t/7k6o8jyAMF01g4CU2ZIBkAV3ujplp3HcFN8M8U+YlzTAe0aoNcYqwPzGTDz3VUEICd+4xcAACAASURBVK9RdX/FR/m53XuZvlXUPTtQmTaVg5lpuXOOqjkicpKfv3v2xEtGmLwW2YHp7gluOWqi1fquZPySf1rBQxbYnOo6883RX/BGBtmD5OWqBWwW2t1i5kVrFY6fZ2CbseM1qbOSNjX46abtGsBFgYsCP5MCd/obcvkBa6gi0TOBQku5fkH6hqdHoOZnXIwi9feNPHOaFAppI3fKxsyTMy9IhgxLnVHIz1y3X/zdB1to9WfWj8Pr0nRJrG2pBnu8xgwHvOZd6IrlS/BXALcV2T+M/W/Sbjr8cHQ6QsBXtFJarDdQe6eoSKulJtodFqpo/BIY06m9Huydp4k+Y65b6njj/Nh7OuN908ZUSvDcr+McFcx8vnO4piZaRet0ft4ZRwjXM90qBymPs5PjHba+5J/l8QSv/77f/K1z5rDdMaqLKFd4JAvRxJR+OCahEzkQbuTn6yGEmplybti/uH29pndR4GtRIFuSw+xN86wUkNyDB1l+nJ6/q+1TRPVoo2H7RLvA4WgZ+05BKg7ftNqN4WnkrdfXrJB1BENKnr9TTwDVyLh3QWZwkQZRbCxvSfSacV/3btyOgFo4PypYSZ1pPY8O50npAu4Abw+Q4n74wXct0Y+y/zQoBsQ7hw1bFxx1mOY51XRUT+s3cTuHi58vYy1S+DKtK4Ba4aFzQLcecwW2WRt0YLsD9/P1hSI5yUiZdzMtqjnvdFRlDjIynPCl14UeZWnv0OxlEOt9XOfaKPB1Z+Tykv8jswVNZoenlf/h9KwEsVqIivArLLJzgA4C6WO3VrUjtURecDk6J/XLddlFgS9IgYWm2VlPapIQ2OXJOsQZKQ9pbVYbNJ1DZTqLIO9L2ux7RAe70e5/xcGZeYeSF1zut4YNw1HS/nUWkcKZWD+6GOkLcuQ0ZcnMgmM6Tu7R5gWSzj1qemzDb3JdB4tZlqWwH7k9O5auwHp2DviaCvT9CPs/8W3LI+sdXNw202QN0BlNdFGLM9hl5yx0eCmPLsPilaMS7zzOsQKB1Rh3/JMBeQ/o60WrcOWK33gNOmegcpzw9qpBQXzHo6/rxVYOSODb+52+ypnLaDavNePpDlvv1u+S/3qtluf0rBYrFqnfIclK5bB4TR4u3qsG4skiO2oK1HirgbhS3L46irjmf1Hg7Sgg+klqZ0y32O8DlGIXPt6m2f36lWssLVTnUI+pR+wRSjRJT7Ts4d3t9vA09JuAYm3K4GcVIfYk+g8QVn/qOHCqq7Xudj1pz5SBXHU/b8c1L3uS8kPYTt7nU94Ag1i7dbg7nP5Nrz6C9tiVnoFfnZlxBoThHTOYfR/7n52tCTtsnPUODFZ4pIJFHfiugGmmyex87Z0uSDB3PcuOG7fNz2CY35/XOXNm5TDk+6s13j03g+vq72ksbf3VUS1W88u8MdP8ZfKYnzHzSmxDZAy7oyk7Yiunaksz2u7onNF55pf8V/wvvIP0tuzggLHuWeSK3SoFdMZDnQ4tsLRnPXvDzPvY8HzfVPrXSc9190WBiwKfkwLoj2Dq5tH9CG0mIDbbDLfWCD1qkwOJSFtzAUSFjAIjNVeSck2htnDIAYHBYneSRs2Rpviqj2WGWJowjGvtiQ6YDU6hY9joWOdAexSQqIa/6n5+HosOQDSiPcFf8FnDodWltnPqJL+SInbpbB+eyRkbm21+BYB/pP3PTskZMD07YAzP9Wmdk8DPrpwhxj9Hh7J3HPmd+Rk61hmMzs+uHchzTmanUc46Xkq7DlAv+alIfcz1Ut39mb9WY7hXUlfv3LWhrvlqHsGKJi/BvTs+zJgcf5/F3fm6ryr/U6QHjJqJmQWhUwLVrsdrlK8iBEu2d+usLWf1vA3d3ZzO77lXMq7rLwpcFLgo0FJg6BmtrxB/A14LmiKgBsidjqEFLS3O3Qz1niQSM4WnF9oRtY0SbdKiHbnaHDJ9nYHjsSkkylfB8XDI/E+ypOPzETd6sjKkqfjIWnhfjPCDKOCOtTrCUqMKfOpNCe2sOjOsYuvQp5x83MoeYxYVqOxnWB28eQTC72H/M6DrIiggUoVJOqCJ+fbj7lsrx73zbv+K5mc4qAOcGfTvaM3v6hwWfma+nukO9vOfVAeVr8Mza9oe6cmYcufcruZR8fWRx+v17Nalcowrp2clS7Ge4dTatpMfn8Bj784KWvHOykE8M7c9X34N+T+ktx0Ynyg1M+taCay8yDMKw88smCBEVBvH88+4Vfvlvq64KHBR4ANS4KeLNwZgkRfsHo/oynBGfC+5GKiGhB4eRuvr26PWb4gSHQBXCjkklc4dISm+QVhJ1yLePlppqwY2f8cWyxyqR02W0kAQ9ORN3zmulOfa39hAGuehoXmDpw6zlv+A/PDLDcl4wZxcaVwgUbnBG7bnZxE6ifFgDzBVuFb2dic6+fsM9KrvA+S+rf1fvbsCoBn8MltUGCbTJ1/Pf/u1J89qmcHszKD6rBUIp0gMve9+/HSMIO3WdzXuLGb3j6egQ9HogeNQK6c0J76tHBDWm9UcO35a8Tv07kr91DRar0vHl8zDJW+mgVzyHwTZyf/t9/3mb9OJFcpKOyUzMyezbSXw8Rk/14131yKQt3qkc5LmNsv/DC/slPovZx+vCV0UuCjwcylAEedsUEMv2kHLozaHinc8Qq05ceS8mCazB/jhlEC4U/l6mF+uA4gnWs1OaTUBwcwQT4X1OpvL5flJ7AXAm1Egek8UrYSrkZ6xifM1GSL8ePuPeRxHot8w3ujA9xHy9OvYOQMZXOYnMCTh388A4gq4VoC3A7tZNjsHIeMqJaBGDI+4rt7Zr+jAaXkdHbp1qp93xIWYY/X8/B3/XfL8slvf7Igw/61k6ozjt+fP+/TLtGYnGs9c8t/7I77OVcvqyovOAlMRd+d9Q/gyWIAp5pJgTza/PdtBpXp4oB86yM07c07pmZW/j/euqy8KXBT44hRwh8CMD0rMASc0RUk7uknSLVLNTB95N/6Jjqak3dLbM1DHaNEhreWxbX5KpdMyD7vZg0SW8iubRXY6uqfV6XPyo+JwU3XHvHboi6/5D51+ZhBhDY0E5qDg6gDRzjZXQLkDeZ2zsdsQXToAi51+tHs+OmQKb7fYItXvdOt2xuGZ8ElRdA+8EpsOAaKVxsfC9wysVw5Pdlxmmgeo6+bSwZ8zNKz4ijdCOodk57B0Tktep3D6juB1i0ubtupMz+DPmY7VNfy+A03vjAKu5Ix5o+OLnZO3c7bugcRbOh824XQGW/76KPL/1/3mb+mxFCcOZWKF1hKx8LCrBeH3dUKujs/Y6hpXW8hfDHZRx3PCC/6hBux62UWBiwK/LgWswcBwbHyDRtQUdJbBn+H4SBqZ1SHKx+jbpaeN6ZV80Kl1bPMObV7kEYqaKHvQr6j7wXCGhhc9Ot40/i95eQ8P326yofQkNUB2jdUDjaYJUWMygLc6Y9qo4fr3XhQIzjCoL2dCcZqbQgutzkJN6znQkR0KuBIf1f53zpvOvz/wMgP3DhB2uCTjnLzWqyjPDnzmZ917fdyfHa35yR3tcjSG58rAGk87Oniv4/wz4+J3Z2y4O1C2HJ07KEeaZaCecXCMN6JieEfFB9lRytfq30fHNdOlw+PdeLF2eT2ZHpf8m/nM3dv6RQ/ynVFGR2Y93g+BmiI8fpkxhqW8T8wEu/0N6fJxxoG3R3qdbF53XxS4KHBRYE8BbLb4Tz1IWfdoSHmNzm6jfqcBbBHoGa2mR6nPKFpPO1HtTlM1TPN8xAmzG+UjbBjpWKT5m3Sd0wYIMuzR3A3HAWHfmp2gy+vZ88WrrqBIwVgvcXz0wAbNdrBo3OYd9wLMyvk5+4ywz3PK1Or+tf3Pkzvu/EvWBzYGULu26Gg3A/n5+RmsZuegiuAcxaCOTlTisgK5NVCe9ztqVbByBPWpFZjuHK9O3aw+F/22jLjsHY8KqFdjPD++hi5FowbmER5HjnB1NFuLZD331aZDbQK6erqZ/87KbsUXndPF1+Z1Ytumv5d7dIkH1857vOPt5L9sWZ0n9hrbe1AmqVgPxIl3GhEkk0ONse50zapDizpHgkm0gbQmR1dm+qsM7nXzRYGLAndRYLJa6qxIS2s7VPnxSWsSxymTUtdjtYnSb0B8khG9fpIW/LdHiwjlLb27BlSbo+hPbW23x3sp/gMdHAbLvqX5aeODu7yvF4/8a99Indu8sx4Owx30txNxqVZjBeorWq6A2wpk3rP6L7H/HIHqQFYGakeQpXghEuPO1wZVeAf0y0BuhY0qEcZnPF7GQPt5xUreuw4dcF/N9+w4MzrTUcZszzhWZ/kxa7fOqcx0zfLxVjzeydb8/n1Huc4h5efwNfze2klfa9Cz9P7R+P+95f+W09teYmi6xcrMmBVgJ+wyBs9T9/6quoNqrT3lnbYV6oW/nqj3kllc91wUuChwUeAeCiTYITpLdRTaW4/DSh+loYFGUTw9A825dEdHdqw14mIpS/cgmrNDtp1N3Ym1FDc57lmjPFPahZwJZKEgPxQgDka7VO1Zor/yurEG1uFPEiClTsssJ1rvnXxFZqmDQ3LyOfmyd7P/1B4+xkrQfbGBepxK320uOyKA6/2ZOucPq+wAuq9Fk5afAWkHfHn/u4oYVGqkWq9qnNlpW4PfGEk1djy/A+f9GhxrRSo+Pjo+eyejY/cKp4L+lSOL5/Qqm93NE0LWdPHbv+e8kwMe53lVzuOJ0W6jhzzuFR0n+hX1P3rv6+VfnJ5qYnlXIAtFCGERdjpDKbqmYqTxmSp4Gwl1TULes3ZhVVIhpHptQt5J/OvyiwIXBd6EAqLHSE+5EaG0MnwthekUZdF7h5Pxen26moxpS9tUwiGl9l7RpZpOpSlUyNALAKHfDScJlUlWKTR32n4Ten75h5j9G3ZOz2myLlyPloZYWu6aap3D09H4I9j/I4jXTzKovmeXuwP8PWBdFGgbOGWHZAWks2TvHBYA05dGMmQsJ2udawdnHnHHQ/eA5Y7O8rnrzoDkHTbM/Lly5FZrjvWq3BLmv5WTUPPj0cmunMHMu8xLlZNZjTfTv6LxJf9KOaHDqOlZMUx2dlYMvmPQrBD4+mqhNJP5SU8il9x0Hbao/1Ec/HvPD8+P1L96pSW+vAW9CHBR4KLAD6NAbiYg3dys5f6Edrgjm2plrVN475HaIMaLJP1OexVgIw26V872cdVrB0Ojx8GTbT+NRyEd+Wp6/XYLZ8cz+NFNo8nE05M0nhjRQz4ot7KttePSg7EV8D9jWt/a/mNOKyenJHbquDY5JUWjpfkZe6DfAVWff3rHWbpUeGjpJHjNRIx5h6lWzKnv6uY/vyNfl52C3XuwJow98z3ZEdlhz5VzqP4f6hvPi+iKnp0T0/HrSr5WPH7Gicmy0j/vkv+ppiczIiu6neDeszAVy9XMNdh45J9rEbCcNC5GejCwnnoO902uFGNMnY7O8/Z15UWBiwIXBd6YAjgCLTqhiTMh+AGHkiJEQm2kf5TjkJS67KGbhyOaVdVv7BaPv8cmk3Q6iMCUbErZmHfA+Y0J/AUex00NdD1u30aY0LqZvoYCjXNQAaafY//PT64an9/t88yxFocPcWnxyuWzN8Xa8rhE58ppqpyAfF2OPKycteN3iua6tT3jUFQOio+paF6gY8jO0onWxs0a7MaIt+GdGVPy93zGGV7H86toz3h4d8+Oc2Md1hEhfmfHhy/WuV9U/t3p4UUCcStBxHUsgOsdgmMHh9XiBWOScIjxHbuMur8wOhtBmXiLTz/Ggk4433He9f1FgYsCFwXekgKFBRIDK+2e1YS5Q2FqTG7Bh1K/8ZYDOoEgLJIjkZ5kCMZB0CPNWKILUnukelmiDx5kp9y2UZdkNU0vm4WFnN45ze9lY/vRdzE0sqMa7Awo71q2XV611J1Nr2b04ez/kS2juVHRyCED0jzHM/NjHLICu9WzO8DccU+3Kz+rkhlx1ZvE8YYVEM7f5fdnDHbEevqe6h2n3ltEXLoxVI4L07GL7hw+b96Z14SjS8dI07qWK9MNz66eueLRnROTnbO1bF/yf1hj1PTwHkj+HQzOi1gRfueJ5++7xZ0XEedDjH41I5fcOthYuPLpaUSCRkck6/KGZPT3Bg4/2v5d77socFHgU1FA9RsdBgrDqx0C9FvTXyN6IpHs5HS8uwNESt2bhNlnGowaEfVHTc0b45ah82kyHLXyr885btMzeUdc3zNSueRsIIdY9y9/C/B2yOL+V73PHQc0OLoCPtt5SYiv9eTW29miz8OsQOXHsv8zwM64YwUe844/QOnO6TmC3RrgM/C/35GYwegMV+oWy7UTlMe2XusOFlXjrxh6CavaaM/RSVrRq3KomE/vEbTKoVzhTnbwat460veUo0eDnq7fOGMrXJ75uKLLJf/HDR/v3tYTLL7pFM763hDuzuPl52bFJPfgRtsFtTJObQkrTYiGQR5Oz7dlnvM9wnJde1HgosBFgddS4GAQyxqayjS/9s333j/GQClT8qeBawnpmBpGc5mhd61rMr4cTopvSsneVDRFmAEEn2VPZl2cKeklJxtZ1krBbq3gyMk5evcItRV6wCrCW3UmwofZMzPWQD2Cdm6zf4x6Eik6EJtt8D2A7afZ/0NUIWZRjb/7jKFE5hyAQ+XMGbPM4HLlrOz58eDDklO6czzu2TTO0LzDXnAwGHedkbSdk6fvqzuorRwRdm4yLuzWr8KPuhI2k01kiVete+e9Y0FK416+5pVaXZ/FPquC3d9MvzOy3K3x2TGCB842H8lrX4031tV0ObkHs42ZZZGf7d3bKiU/C3v3kFi0nQI6Y9rzO2OwGsuRg9pwwJ7XpQ3jOk4+/w1UAu+1z3XFRYGLAhcFfioFVCOeMSI/bJgWhZIqHcllG6eXflP9CiAhKj80tUAbOs1UAzhIU0tWiQ9s5fOKhrNj38lJRnBUUGbku1+LNnFCSKImlXeO4WkXtLkBgEBcmaM2dNANtg+0IjZmXX/LaiDa1+BdPz3Oot6lXoFJPL8C3Hrfj7L/ke5OcJYb2DopGLTvwHkPrIh+KYIxPfNEcXxgmJlWM23JQdhETM6sLYO8M7qjuj7jNX7OPc7z7v073Mj3Z7B+dtzM+R0vM1/x2qxwcDW3HufGwb1ZrnheFZ1Xcl7R5wyP4JovJ/84pycItxLMuOroKR4VKq7uTEjl2bUKzU4OH8ZWA1a6E+hnF1jWm1X9FP7fTvSu7z81BT4QTvnUdLwG/2MpcFSkP/b96W2ik0eanTggSCWGoyFKWJ2DEi0OG2CNuOVX7b4pDpBYWPZgRuqaVTjJq4bTYWcHyfV6uKvc458PnT+aKNjzJvQyO5BhIWjP2acEomsDBtgh72C3SAf74YsjtVN2Pg/6rJ7oYbBThz2Q+6D2P/vO9HcN8Hs8knHJSgQrjHLYID7Vurp2OCsH7YxK2M25cg7OOioVAI8564g7pyrzXecA5HdUjsh9kYI6JTBvcnSb+9XnZ/hkN7/a+TB1eAdK3cnzWaeI54Tf57n/+vJ/SG/Li5idkKwsKwHdOjmbtpFHgUI/1TgsT47UG7ltt0eJ/KBIWDoJrTj4h1utn/3Ce8XlZ4/3ev9FgV+cAp9BJKNP9ewCkAGQ6IlFFCKsQOAOnodZfnWoLBuZ65skDQ4NaNRx0jNp8AKkHNnn07ZtHGWgjlgcMCRklms1giNdQPFeafVJtUnjdzTbw8EIUmPFEaAfzJdGa631GnMYYPObJAB2Df46EAbaaGpfRDF2IPXD2H+a8E58qu93jkQF4rt75ucz2G4cm5OHqK6wFwNUva4B+SfjlCsaAaiX+xo2kG4MTMf8e/BdOE7siGVeY2nLuLMC+UGjGbgzv+dndrLQ4dyDBlhFAQt1UckbX5Y5aFJ1E+33jSQOa1wcevol5R+Rnp1SyB5i3jXQ72mJDo7Ny/I7J4bFH2LDLAXCWlg/j5oeSrH4webpet1FgYsCFwXeiAI7WPdGr9k+RlPBJFNtGExBQ9jlRRe6cc1QzN8FiN0GHv82/tYoDd+nPkmcleHAIjlY4zqNF6nCR3sHOYhaA0R2aLV+h3FNqWm283Wzow607hM2aIxhDBS1SrZpNs5+e3iyNGp5CTlG+k7PnpMmCwwLt8S8/wJxGiMaNY5tkE0+P9a2f+QZ4N+D0vVzezD8zvafhuVjP5EKxthlt9PPYDfm2acliUhsdux33+dndI5r5ZjdM7duVXcOUL6vG1/PNdm1iSs72lS8yc9f0WI/3sY5Tfx1xiHKspBQ8CFm3O3Hr+hQOW15/p3DnGmW58Tj7ZzDFd3ncX8O+W8jPbMTdNxV6JguPg9F4cRM+a/5Hbng6cC8shtnkRwYM3mhGid93hkVc7/9+dR3tCS5KPap1/Ua/EWB96BAhTgEXA6/QXWwOCCy+RSARtwghEsYQExduC3HzKIpESlCdzhEaqxTwvCiBtC3KAw0VhhoO6B6fKHej41pHGqtTgOCBOKAeUTJGi1w1h3uFb9MU/zkfebDwYFSp8sDSHaXKVn50ggoH23s0Q7tiE2TvqWeisfAabahIPoR1HXgOL8+P0///hj2X+dw3MVHClOG1h3AzLQ4sQRyS1w3j+EIdHv6V+JaAdbqut18KoDbOQDVnCH2GQjvxly9g52BvGbzd3H3SlKYL88C9dUcJ1qezDw6rvPs8J5HnkcnsKI906mW0+MZP1lGuvXf6YOMzfO5RmwiMt/puN8e/7+V/Ms5PZXQ8bKwCFfe79nFngi5CPlWDO7NdsQW6gGlw/CaPUJChO3OvQcSuJ55UeCiwEWBr0YB1u6LHVJ3iijfrTEM/rE7JeEkaLAe8Rtr9z1IPiIe5lBEUhqiPFaHJI0RrOZTehMMZ0EjOpIC7V25tP+nXCsGRB0jTSHjXX2CRhYZGs8ZZxZp8wa7RwattBn/xXmu9gr1e/hfRq+d52FOpDh81qkUTarP2NzaITqCzE9h/8FWi25NDMQqkmLmFYaRtbILKuxTOVSZblk6KueUwSaP5573r55baac1rxROpDxkjm5lDJiBLtM0vjuC+057VrTcAfZqDNU9Opv5X/e+LDMd7c7IX7WmXUe73VxWvNmplkv+Z13n8jacHt4pObuYK+FaMvCiL3lWNsd3WDseq2f1cyPM2CEd4cjinah9sc9fs7hfjFTXdC8KXBQoKCAqOBwQdRLQ9GwqilmrYfZ84DB5erJ+ObkfY6NL/BTUs1izBaTQDadg2i3THtuanocGCOMJoznDaK4ASILr7I1+NqqFclAPJOnU44DZ57nR2xileCWaGjeFhnLJtxk4oZikqkUUSoZFDRqkg508c0SyFFLq8Qyvt24HM1Dk+r+E9yuQVb2LO/9VwHBlpjL4ux/kdiBfnxTPN5pTmmU1rpeY1BIfpbTPlbtQfdc5aivgewZIZ2eBpMbkvnL18OQGdCYHs3JQ5/nEWjD/r+6LN9dlFWf4+ziLkL8VH/Y8cd4JVEG39OKmBu8lvMfr5zT4YvLv6W28iPd4u50SPqUAQfUUXsxKId4Bo4oDIpDSRiz8Uk44IwWf8Zqdu/8Z5/TKMQeLELMsPfVXvvC6/aLAr0aBpGdfpXblZq3RfLS8MXEkxFkZFn/Ua45ojVXcfHvUQ13FCbLcOQmxaKMCPS8onAOv+7GudOJEPGmt0ni1PGMcwDqegVCNtLF+friNhgfSsE4bHeCpEjeSelI5RXWueRotvr3ZwLhPa4cAqWswZQeyWi3UmMDz7bt2yxMnKeDKWVqfAelsHjC7j2b/j5ENdkiysxJ/V4B9J4Yr2q6Abgkmi5d15ni1DvyYs2t/GE9TWtCN+yx+yzQ5w3OZLAfsyZsB1CkuY83KZK/gTv3dMaLFNOnokGUl0zHGdkxB6/HtkZcPtCGHkelxlu75uq8o/4f0tmrBKyZdKZSs1HfKJy8sM5T8Lg+wHYWxQ4a2qThfjmOr1jF1p9yu778CBe4xEV+BHtccLwr8AAq8VOzsPnVBhj9h6csjwvIdLa7Ht9+stihSyYZ/MByl0URBnROrORLb4O0JHp7FuRnOzLjC2mlLhMWOpZQOb2GRpIHc02jT8CTpcnBcxCShF4Kf7zP8M3O8PJ1u+HPDQbLOczI0867ifFRZFDuW1eqi4ChhZAntbJZxBVr1u64eoO/shlfulvfN7X/RLOAMuMUBkcA0E1Bt6ji6uWVw2IHe+fPaOTs6ccfF7MAoX8mwhz9n+nfXZJDestMmCrCj1wo7dk5LvifHR3bYs4tO4bncTGV+V45axbeMUXdz6ngjR+N2mtjps6g52tG/o/FXlv/bX281PazQ8qJ1TklWgisvcmLExa4DC+NRsVG3HjlUbhxISikCKEDV/bs3SATYseUX+P6rkLGY5/RRZYW+wPJfU7wo8CMo4LIGj0c6NFtql0RZHh9uj9acQM7wGcf3DKdFHaARudFGCiMxTKNA6lzgc3ue1e2IjZP6H48DaTqJ9k3QZgQSXbI+cs/D8Xl+eHy03mk4jmhswA07hNQ0ee/Tw+1RU+HE97LIktwrXd9GemCkz+nGNllI6zynER6NgO02Du9dowyG2OZ/XPu/n+US8DvuqMHtCtgCl4A21UhKR4wBa5PCVmGvjIP4fStHhpsGrAB9BYY7MF85fO5ALBzSGR8Gzc+8u1vpHRyZ1zCczoq/mcYr/Fvh3MwHKyd8xbUxn3VEKI+1osOONjyOryz/t7/hD/628Imchp02kypmz+qCBVCvn69YMfhRyRyZNMYU5mk07dR7La1gpAKMnbgHyYTea8briosCZyggzHuPKjnz0OuaiwIXBbYUMOCvNTTDLjw+PHz/PU9bk7bY39UGPEg6mdXvqA+itsxS3CaHKgPf6KCtKWpyjo3W21j/OHsYdXITe2P9dIYPI5tvw/nSYxNQ4yM+GAwYnBfzXiRdbgRzntVmqeNjThhMmAy8332uwNjRHmP7T1/M9nG38/yx7H/A+cO4tQNUbAAAIABJREFUT3Tf2u3+g5ZM7e53AF4GrFENdGyIkAFrx/tnLE0FrjsAfBzn/OZ8X372EZ/tt5LPgH9/74l14zngd8apd0eyCsc3I8ZOLiosy/dmfuF1P957lOtuPTK/VOt0Dpdf8i889Df+wd9Gg013F8QBolOGK6+QGbBb3Eq4OwHdKSV07lHVl7xisUC05YAi0a1lvS64KLA2NTNg2lCrQwkXkS8KXBS4jwKBjKg4xxoGjACINByYLZP8LQ0BhonQeiA4QxVg4QGxf3EAboYo9Jo4f0gT5KixgHhbiMoM50cHI47G7VnqlWaAZU6WR5aoEGmRq7ACPV1ESO9ZRzgqgJmBbwZ5neN1D7DvNHD1ru79eRwd9mDnJjsjFcg/i2GO19Vtvnl9OnOx4tUV8J7XvnOWj+PKcsA06ta/4zMVuijAv5d+O+B/kM2TWmUn/wddsI1g7Z2W6pntcJsGX3kTI99fOTs9zS/5F/b8m/7b39Y6Tvs3fsWfVfTnJI8Vl+2ZJCuByXOWelGkO+jj9ew4u0v7elpS21XY8/J1uu68KHBR4KLAx6BA+D6jacCI7D9Krc83s1PazG1ETUb8/+Hh+fuwESNaY22qxZiZJVkAizzb1hZZdzfYHw0KIVyEw0xHmpw2WJDokZ2FiliLtN8ezpAjSnhVAXHyuRhnV2MHGtnOZ0C1dpjOjqC77pX2n/BJjDOeWa3XvYBb1pRuqiI9HAXApZUjg2fx9Xv61kX1PI9Y331Xss6pOmwaN8XxeQ5nOKB2Luqx5mshqbwO1RrwdTzGngfeRv6rNSXtMqWgTnoL5301BKw2JLqNAE5frK655F+JvJS1v/kP/Y5sRKlijuA3hJ9/wgmKx8b1S8/fF1uZr/O6u10MV84SgdLktuGpPQ9Dh7xpbJKxYes554z8XtdcFLiPArttqIsf76PndfVFAbIdsEVIYpZaHhzgJq2dv+nG2HCCEA0isMwAJQzjDJw74HUExHqfp8CxByEXW8tpMYxjnGa3GDmZ0dQGdIggzcD7JcCzAmezfZ4dEPe9Ftx2vCZs+QpkzI98nf3v1qZTuww8NW1x3aShA80dkHyJOl8BfaZVntPuXYfvN/VD3VLvgDT4APdXTuB5fphHUa1jx5vdtSsH9D3kf7UuHc5lp5P1SjV2nv9ubSr+qfXHF5f/v+UP/U6d3kYnYPPCsOMzFuHsoq+Ud15YFqjDost6qRGx3AW7HFtwZEl2muKCFBcFTlHgDkbqLr3381Pjui66KPDVKDBSZ6zrmjQFsJjP2Ay7fZeGAiJq3vbWLAila3egbAcqVkB0upfQtR9ganWoEvmRiJXZKaQCoZNbRpVpeTsAfg+IrWxxNzf+vKLbD7f/NtEOUK6AJrcM78AxY50Vn+TveNnW0Z0ZcPJa5LFXID07ASunIOOog18eMdBto4zjOs9RqYoe90TAunkEXx+d7FoWmvS9Hyj/Z3izc1CyHMezjhsGncN05tmdHlnpgV9F/m9/a+H0gAHFwTHnJzs+cmaCh+1CAHqlE0sRDH5UAN2iT4uhRyN4oero1aPFpxay8s44VwO3rwaJfs5873CKfs4Ar7deFPjcFDARk05mYkDsfB50TZN6nnHOz7P0N3Cnghr0ZNAHguycmQo8VsSsbJ8DYHwpgxj/0W4LdqRpCTrzuCpn5cxnB+1UdE9lQFMB6wzkJ5DftDXutOJr7X/lDOSuZTsQvR9bLy73OHqRaBMUq9aVebFai3rO1RjnyOUc4TteX82lc74qXtNr685j1ZjnuZ+P/GX6ZADOz3V5LaJdq/l28vYa+e+4qHI6mF4ZGfPflSzmdV7i8C8u/7e/7b/7P58lVcxW5/DTIz76jflB+rvdI51ozAnSj2iJDh06zud3MsP4QovRs6QC1PkguCNpDZpD3RVt9qrs+uaiwNtTIJQPqaHLR3p7Ql9P/DoUMPmRqIklmMl+l3VEgz0bdT25q9YMGPpddwZZ7CwxmGbgXwFV92+0L1DU8FhbN61HPffvLEhfAdZqThmY6t/rYne2/XVU4x3t/7SPOQP8DIR3lJ0cN+KULiKCtKQMTvk9R1Af1Dqj9uWaovZsxY+8Bh0Nzry7otfWwUv4bv2ePNJ44xn+XjklO+cOq/DD5d+mWDkxR3r3+qhzlFkfVXOs+CE7iMex/dryf/vbh9OTCtkQwWHlJtdQVzchdhMFYsdpx4xHwzF7/zWjxzLp82FVMGIzdi+V9J22vL6/KHBR4KLARYGfTgEHMbIR9u3hYZxSOmD7t0dtJiD/o7N4bMQZLFTANU/uLEg+T5QjCKzesbKhauK63f0jiNqB52wyM4jHyUZdqtgZOuZr+G/MJdv9vTPX0UCfvgXvxaLtgOY87gzge9p3/ME8Wf2O+3aY6swanJ1bdgArYN1dcxzv65zUim/yhkRFm2rtP6v81/J5yf89DR5uf8d/PyI9oRjA1ONn7t7GzgwcnnHONEI+7Dx1nd+y8sq7Gp0XmneUxt/So82KWTV3Wru5Pd1uI9PBDoyz8d2jKc5brevKiwIXBS4KXBT4SRQYUX1pBS36/fnh8Un1v7g5hgWQx3AWEDGwW4HDAFz9DjY7GeP3l4yhA3sVAK2AcTeHDsAyBsh2l7/DfLLN7uZZvq85qHznALBLsXcKZ4dkRaNq7hU/ZHZfOYtMp0ybelNXn55pfwTqsUFcXV+NsV+bI3CGQ41Ogis+yjw6r8lePlhOKp5bP3+vfDonp3OGV073kR/28+vWruO3LCuV3F3yP8tJxQWlXP6d/8P/JevhTopdlR2hLvoDwktKAZwnNzY6DHGGUgpdp0xZWFa7M77g0r9ghOPMDZLcbpxQJz1D5VTrbaXeXm6uKy4KvDMFdhz/zq+/Hn9R4DNQoESKfBLoTTbDkIo97IPtiE0bdGf2wQBWmCx5d7kDkr0070HStAxFzcxh8/AF63YY3zu+pwPlq3mstWGfgrNz8jCW1fMDpIZjkR2tzBs9P9U78eXcm65rKz6seDPP8SxNOiC9ctbhHM3Rv3p9eB4dPXtnr3byMuvXDs6+xXcHmvPnl/zfp2zyenQyXzoo7ateLv+3v8ucHgxMDMVwUCgHmRl1Ff1RI6NXV04TDq2qBJjnlgU0CwquZSKhpmgUsj7dHh/kxJ7UtvS+pbquvihwUeCigFFghRouIv1UCuhJBnogIo4kfcaBn0+W3EaHVyMrYDay/Rkp2UgzoKwcHjbyce/5Ym+2bztAm9/v77ujZXEebwWYMY7dLrxetwa8O2fzjP2vgW1Qawfyd5GitcMVlWLdJu2Z+xk8Z0xT8VAHFnfCN42liaxlZ6caT7f2FT6r5n+WJj6WpuC+5flUppF5djUnrtc68/wV/1zy/7Hl//Z3/+E/WzYymFpT2yqOH+EcqXvjfxM2YKfHn2NOFK7vnKe8B7baDZiFiKtFrZ01DiCSh+ho1QRe/y4KXBS4KHCSAktrvTPlJ99xXfZyCtgSeAE+Nu1c16veHyflSDq21cBUBeP6bb0zDzBWOToZNNbAZ64tyZxTchKfbt8UuK8cgAr882fs4HTz4oXpuL2ay+Q8bVoG5zlk+lXj7Ggc42X0oJ+ecRx29GR6rByBcGr0Kj5w9tTap3OFMg26tcvrW433zJr6NeOBtqlQOWpM13vn1a0J1mDiIRr0Tuuu+BSPueRfKbHbADjDK59K/v+e4fQU6WfCjDYTRG3cUTEqZYcnOzLe1Q2Hnhb35XN/KstXKe7DdX6RHmDq7pg5PHKSt+wE6jkP0vq0etn12ftQYKel3uet11MvCryMAhe/voxuP/0u7e4pGW0C1kaqsxqzJ2S5ecPoY90Ehr8Evtw3J511cspWbWiUn9H9nQHjS0h/r3NRAaCdqJx1lLrxr+a/m/NubB3eABi8//vjLreMYeGwZhxSOYDZfav+nsd6dPiOEPe8E5idHXZKOlrVDlC9oXDvOvH11b0zDY9pcR2NL/k/OkGdLjyr6z6a/N/+XnN6eLFRf5OdGvl7pIxZfkCZwpZCjGJv7OEH5wmf20+uG4JCrwSqN0hGXosqiTKRSlc71Gfs4UlvU9FC4vxcxT47s3F9f1HgogC2iKVN8qrV8L3W+yLtm1IgAKZaEM1HGAeXjj+HHXh4eH60RgejxXWK6sDerEDVyojz/Wy7KoCgnwUgq6IGva0LwMoAtAPHc9QhjqhgMLtj3cr5wOJ1z6mAr9xTOI6w+YjY8bOZSVY08dS6w1EZPZtlAF+tIb9zB5gZS71kY3W3DkGnfVe6an0yJao14nGv6a1YivqxY3m3m8r+3s1aVfSu5LRb4ZKePubZCbvkfy0nWc4/o/zf/r4//GelBwCfv8MOhzgt1Tk+xolVmtry3B97ePVcMHc+9ycr9UpoEduRHb4RWbJD7NDBR4ze8H/kRGz9Xr2xSuW9qR2+HnaWAme0/dlnXdddFHgLClQ8Cd0BIL01728xkOsZ5yigcE3OcpN10vCOwPzx1WhqM1p7SsRf29/swNIaFB531FdqbPvdAkBmS7Xafe/mdUbFsnOzcgDw3bl1ma/qnMDqmZX9z2Osxgnrnh0+/hyjYrpsQb7ddM91u5TJlbOaUUq17nz/WUdrx4sZ0PLa5PXrnrWi0e792fGq/q74aMWPeZ0v+T9S61eX/9vf/0f+nEJ/NDAQGui02QnB74ef1rzAFVM6u2d8vj38dHU4aro/L9EkOJbZFr6MWDm9xYDKNygsOdguzh6SXcALvLzEfr3ynjNm+JWvuG6/KPAGFJAozwDN45/ssiigVt9ngOzXwMA3GOD1COviGc7MWC1JaZZqHv1Noz66fHzcQgWAsqPB4JojM9XnE7g+EXm4TxPOO9R57PlZ9/5dsVIFYO8b87zFuHPMzjgV9wBnntNu3Lzu2aHgcXVj7MD1ufH2TTXYATL3flkrlK+fx9s318j8vKPXCpfFvecPpt/JVqb7OXrbSt4RmbrkP1b2V5H/2z/wR/6cJ2tEtAftPqP0TgPiyoq783u677ed32wk7FhNv5NzNBmUg4bWALnkcKdjesYM/prH28PT8yhqHYZQIz4+00Pa273ifiGPHQWyo3r5mjuKnTMpcpV1spJfcxrWxcr3EvqoWQQxp39CVyWuOEV6SNir33U94AUUEP09zu7Ruk1T77Ieo5UB2u9oUwP9B/AYvUfn7a/KYcDIqvS089La1H8U087clHe9Yx6RwMABSbaXDEJzpkMG9DtnCkPFM884KtWqrkBr0PpYg8LzzmMphNejezzebo67uVTqdHcP1uGwXpsznLrxVrxZO5PhSJ2JBO3mtnIGmO5n3tXJS+aJbq2ZP5i+uL57zrxW6hCdAfY890v+lfqfSv7/QTg97lDEoW7mEkhGQER/whGqDiAVx8k4Xb5PkR88aRX92d3Xtb5Wwmt0R7O1x1ye5cBStW7K2GOT9q99HI0NtK7nu53aPYDLuEfS6+x6ATEvkdxKs3/lz0jzgLYuLdHoPKn+QvVewH3LRQ7ijG+XNSjbp10XTEa8dXrg5xiDYntY6h+JaVdo6yL121BgpDaPFOZHMkZmeKS1NfRNo9o7wMe7z1he/snG/+zvPOGVastgbKcGM5vtrs/jxbim5zTF+P0cOEZC7pXV81Rgl4Eqg1mefzeXDG478I+R7ByGGdweWbMDyBUIz04qp7uVtLYP87wzfaqISE/XY2QnA9aKZmu6zs7UGT7L881jwBw7nsTnfN2RB5tzXDZdBHdyUNGro8+Zzy/5DwpUvF7x+2vl//YP/Y+a3oYcZ5zRg791kVV5qZMDj1jFY3yiD4jD4Jgp5Xuc31M4QMuoEDFobpqA97LzxAwUo3zUHW+J+gxzN9pZ64wUfI/vn3SM+NxO+I721liOe0T6qCS/9CeD9qOA2C3JSDOR05T0n7WZ1cicHS5oaflfmm6nJq98Kf81gkpNGyOyy3k/RclTFwmhkddmd9iGyWQU74q0XbrlFO3PXCT1nCOh7enh8TYSmumgUqugl5IfavJpKghZ0N7e4IyjUw2pAszVClfAmw1951xVYKkCWUvAyu2woYZp2ymD+rD+x33As9x79rpM0/zuDJCOTsW6yH/HRnnu3Rp3+6H1PI9OYHaa3RxCrdC64Lvunm5OZ2i+483MzxXf7jYCqjXT5wCt1d0UOx6u+JNlh+nFdG1pTs01us2MlQxkmczv39EYtuOS/yMnv6X83/5hcXqifgcOwniJOBRm4LFbPAApUt3EWZDx4X5Kh5N7rWGAp9pQHc3kMJnz1DVMoGyRexonSKzHwLaEq1xD2Q6gGz19gUx1pKZIi+0ByuEaWcRK/SWrD+IaoDNqZadmf+HvaYcw1L61lv026I36CMu3tyJjZS1rOIE8xV+YTK+e2mBDBnLWll1lOtT4soD7YuXzyyD05lS2ofOGs0nt8OVr1T1CWkYP5990XXmaAlrYadt0KhDQ7QboVXMz0JrBKMDHBKTpGDjvgVM4CAAsFYDZAbKY4hEE7kBbxVY5CsBjm59HusG3OLG9eUz1y3PcRRsqwJIBcgZ6TD9e+uyEho0ICpxxWFYAtXOoQjXuU8VW4PjM+BRm9GmyK6cjg+1prk2UbSdeK7PAYz3nFFScqSPo3tM7TOfqxPJ6z3x0yT9ncq02WipH87PJ/+0f+aN/3s/pYUXD3dzEBbDZyg/bIeJzfFjp6u8jMhSMPH3PnydHB9fl9DcKMukBcxY1yu8FwFMnDakMc6tqb1VXbdPIZ9biWqV5EkXNbplT4K4ucDuVacrbnEbw0OPzdwGJz4/fHh7G7xqOe7jhQI0BIB+14ZLFMTQaJx/cu9+1G+Mv8L204wWwxoGMlm46kJ45RIikoRBfxfVC4y/nANUz4v94EclgUaKp6yutN4QOVRVUmPoVynj5QL/EnU4609U4mNoorzQgnLMCWpWJYEnZ7QiD4NU7AkAcQWAGjx3YmMcSztIMfI+gjscFclScWI3DD4E9cG5+zzyeipYZjMa4Z8fPx0GgvaZJXZxfOktJGnZ8kL+v3p9Y60WZ8RngV8/M12TaVmPteLmypNkaVHxe8c2KJkHu4JN8/b0KqqLDPapz9f4VP7B88XVZXi75r+ukqnXudNnkhL2F/P+jf/TPG6S0Ek97qEdUnCtuD0/SoUhbfSLlDYrSHQyksnFEyIy63283yaO9XXZyrKKxjndZw1DkJ6XKldEfc9QGwbwmyYzgqNORXZTbszRtu1m0wZoxRe+6gbjtpSOqJd2brDmCBB6we8sWtN2ruFecf5XrDRCOdRgtw2ntdMfU1MRwfoyrrCxLcvJvsi0b4YtKwcw5KViwpjL0VyFrqTUihVAUhTB/BMs0XRP1aip8Kkukbu6xGL8yLe+ZW4pkqnIyXWEd34Tyg589dRbptrkO8Z4XX9d2FHDwYRbTO7bJF5P7I4+4B+B0YC/ie9z25zjCDpDuxjAB96YDFYOxsM1dY4YafGYduwK3+X1My9fMh8HjGdDPY5wpPjtPGYRWID27oOwsdKC2A7elvUqF3xOoW6UXtulXL3MudV61o3pcx7UzyTTvHYmjc7+T3042V+u20ord2Ha8WvFgRSN99yX/lSPNMnLQKUmfvaf83/4xcXoiRDj+iAhO7Ej6NdnZsMLeiMxE6BfRImW0yKt2iOXtrlUY9N3xToJi8h2cG7kWTsdoOmAde3JEKpwkRIYi3c6tHMJRSIkQL2i8YTg89ruEG1JuCjq++VkQ1gSBmqb6Qag+u91e10pcP+93CjisQYbRe6TaS5dw2fU2hD6iFN8NgOhx6nqALHVhEieIU4qm85iIOb9oBy2lGozg8Cq/WxcrraeSs3opCip1Ve70wKlnyTO+u5yhtQBKC/zh1ABGAG3rbYNvcVbMbYTjtM2efKEiYZDZ9c/X1BVvpeUgBzjzDd7/WAcJJJ9oIV0BOQY/Ydg53SmDyFVtyfHa/M7J0UlgOQMHCyBOJPTM1oVjdwAjVOtTif1LVQGPJa8zg9HOKSx5o6hLWj27ou9qPjGuo/O0A2Yrh6LirQkoEgiU99jLCs3s0aTKIcufVdcswSipf+Y34LWjwxaf6CZm3VCA58FSkH/He5xeAg9nJ281riyvPO78+0r3VOO95P8+bf1R5P/2j/9P/7c7PbkpQDgNOjk0JPBUM3yG2h1LVdP77ORyiuRUtT9TlAaYtajhAYNiTIf7zDrAcarT8YbTg+w16+zGfUvtu4OGGR1/xLmKg0/VCwK89IdqwwQZC+2iexK4jf6lVuM+Hvv5V/MO+G0UFlujWAN9UOaiHB8fH56/P0lN1VDwsiy354dHcZa0AYU+TtdB61L47G4DjeA3i8gpEYw5HEGgj3mxEJ98bRTsmaw+Pjw8Pj0/PI2fD9/MudEJauqV1qBgA0A3D5Sv5Sph66LF1Sen0bsJhmt1OJhWPYKGLNJJcqyO1kJGoxSkvPHeiiV0imXVq4/l4+82k0//YOdfNmIk/8+Pj97Vc6bs+oyUHUDMQLgCXfyMvCMaIDuW4MxnK+7IQDm7092Yq3His+yM1QzTdNA6yc0VYK/en0FvNZaKPnne1TbDTuq6tTlHn5Dq/J5qbJke94D23bX8/h2teG5M+x0tMj91SuYwlhwFSPV1mFs1Fn9HUctU0RMZS5nP8ljzXHe8+lKaVnPr5tttk13yP1vP2z9hTg8TUkzsqqnA+N7AkNsU+6W917jGHScDo3G9sYWlzumuKM7Q6Q84bWt/BrSwqMLY2bNaYmSrRVTJ0/BME2vdawBBA9ZP1hRBak4EiNMhQNNeizpW0fAAUbR5X0DJsVOpnxx72AGw2jXPwLicYzJUixUYW+MICaY9xZmPliFklFU6yX89MGT0JGWGRuVjATQAp0ypV1rpcqWduKWwa+asKj7yWtikiM91tOMDdSSxCTDtwz2bIzmcSjiRkDm50AqB2Fn8yGT4aWPDniYPwJqo2AaIrIakyH57eBqtlIccYPfo0Q7OFL1jdW1yg54l46VBtE4/baqf5MWuWctt5KR3m1SxDkTswBsDovh9dgB2IJPVkGw+IKJhmRXcnIE1FY+5eseZ956xSt01Hfird8WPzFSBydDhrtXcjmfnop7f+jykPGZeX7y7A735+0yX+W8dbbR7igybnZPEY8z0qOiT0QX2Y5y+L+R5rkWseGDHFxUdq7l3fLqmb81POzm2XdTp5tfKwHFNLvlXmq6bgPwI+b/9k//zX5D3iL01ZCS/EwvgO3zmP70rzjDO85k8SCtA9hjyHMdjUWODc7NR6qTv0RfHeKiznI0KrbPzeNrDT+3C4/e6DOjDNhw5OFuCPayfwYg2yO7sI3tEVmuCk9ij4t4pp4peUfkozpcifOymW6vs4UBJbRHSW3YW9ZOAjmmYQ8HCSRaS3x6+PT8/fB+pVrrxLU6kCsR3pQR1DgyOEOsfHqx8kQLotDOuzzRDaRE9JbcWFylbWM4XDpfEfvpnLOyXLsoWYbWmEMJ+0uBgAG0UpElllTo1VMgmv4oTKi6+O+/i4F91P/dLnqqNCNAMZ3w0UjbHcpAaEAj0Bp1hdJGSKGzN65Cf7aM7Y67vn0p1h7/px71yPXAnmsq92CBGi9bkg4P72ciu5nkAXNui2tdsax1rKNg08DjDoegjByvCsa1nH7F6B8Acf7dffreEprHr0ewALS8lzzk/rXpONe7dHHa8wd9XNCjn06TjdffLuO1Lnj/GvnOWjil2Pe1Bo249V2PM41nRVp/Tp71tnZSU5nmkwZrfsNeU90M6Gcl853J4yT8Q1KZhx8eS/9s/RU6PIaGp65rBQksr0q5l+Gz66Q0MkAaHp81RmnzvsYbHPEF0O+JDUx2mxo7JeB53iQNDjnEe0vBMqtW5ifEBRz+hLTLmoltssSMrtwxAiJO9yTvMKXAOOh8ldV8jXAom8dV0IrgAGkZIKzP1Cb4jSwpcPbO+Lob6KCO9Tf+SDXBFeE7ciO7YAub21ebo6B2mAlmzWd0PHG/UXqADnK6JFZ+7RqMaz5WF/UhL4aA4BqwNOLR25HlEdsTJG9E2O8BR5v1N0t1UtKXoR2uwjF/Bs74pAmfyMzqG771eYiHVTIaxtNx2c3pMBWgaoUSOGazaJohca3mej3aoDPE0nFsACBWkt9UfazCregzpppEGeYQse1D8DoviERFy7DtEL6+PL9kJqOpk2Ibl2U73FgmJO+DOtNo5AZWqwkyqMc7vnudbOU5nQWDlNMXzQuszOMWz82czgA0sEDITo5rfe1zcXM/EtKnmlmk/cwVxCDUUyPPYPTd/v5KNndzsnC7mDx7nWaem4r8dDcF3lTO2fe+mzk7mm9LUOocky8ZKZvXaS/53tFxBo4q/3Dalxg5ZX/S66n3k//ZP/7G/8MxNCHQAMNrhOETHNp1eW1PDzo/NBlhK0s2IsWOyOrlxnY9FhtA5QNj1jLF2URx/pq0o0uuw/er3Gc9jN1UbJRAQ5H518INcABWgTClwVtwjNRJefG9Y3qE5OUB2oJ7Dn53G6yzSB/wcUwGVRHFJJMHiW3KWEqIUyKrVSMPj7fHhSVK0nh9u39F7zICin31iu0YCxp8enr3r3rhOAT74ytMO6QwbVBtB+bkzugRJH43Qg8ralEAMDgJiQld11YUWOcQMuTCfG+dUyXU4Y0Z7MavBQZBI5JwOQJ1iw79+5uZq9V10wT+iJ5TLHiUmbs6+tWcX/54Nr1sfa+piXQ857VYukQ+kYMs3D5L9fj2T2is4PRjTElZwvZkDsFYH+foRnHtCYbHVR5RqQNrkCjjG4n101eK1lSpmwJlBU2XE8yTmZ86OQQsOi+5dM1yrSVXt2azMC6dCOY0S0M+qcUW/aVRFWlVofL3ySNv5c8wZ11Zjyc+Y2aOPovlYsflWOK7VWsqYoEvTPWfezePNoJDXKn/HY6m/6+uqcO+SF+gFWV7ymCse4LnzuuVxd/xzFgbl687M6ZL/Wl/88vJ3RaLmAAAgAElEQVT/zwynx+YuP6kdte+aZyfHHSMFPmUDBOquljNjWDjryFHk/UkUB9EePh/IxhApcqYYm3N/dGrmWJm2lEdPTh66x+kXEi2Ckh55WE9jt9xuRvtqz40zwD2lwPF+h5reejd9PFORi3fSEidoigWdM/4f5aoEQrTG5vbw8B2eYvwcq/JNnJvovhYKSYvuQTf9+f3h2+OAj2Eh8bqRPgieMoprDrX3y1YAKnwzIn7WtU9SDLETbw6rWl8rIvJN9LNq+CcshETMohsbzJ0emKnbCo/Du3x8lhQrOe8IFU+S/WbRBRk60Kw6Ot7PwDQiop26Nuq8K2Jh6f4JNPhIr5S0QqTJjtRWJeSg0KPwqXYmHL/LSgz9ojlv/vvz06i7CprKdQBXJkLD+Rlrp/FnWwhkbWItZTntOfwQyJC9V9cQ/7G0W9uQ8TOIpJW8DmKMd7hzUkdntOcNDT+YteiH8S5LxR6/KNTo/uibLTHJwxA66a64mp2JDDgZEIKaGdit3nXm2rh/dpq693X05nGc0W55bve8j80CxrMDvNX78lzuoSXfm8xUyQ88zi41iseYf9/Nr5/LuqmGiynSmkP6J8eRM16g2ec1OzqBFT9XznM39oquzK+5rqlzzMkSQZN5BN1VleudeTQ50nfGMb/kP0z5Sl8wH302+b/9s+z0mMHg2h4YwLn+Ro20OhHBJmUntsmxmO2um9aiaUL+DkLoApxbZ0cylI8Nu91+r63i5PyQjc91SXyf4HUByfoQTUkBcC9S4NAKeFzvtRRmg614PwR6Tg7VdLjxdsAYNd5co+J2+4wkvwu6uOOh5jhqAwhLrxrjRn6zE9UWQzq4KW/B6RwRn3HvqK8aftOgjOAuixAJ9NKgjuIciR6hBoui1xL1eZIIkjTjkDOajLYCzDRiF60PYp5nAMEdVHnzS8MgE9iTTQk9Yyr4dnSM0J7hMlPhR9pvHXQcztODHh6rN1qdlUV3hIYCeqkmC1b4oxPqzSm/eKDXWUXqGRLfhEzUrEV4zjZatJW4bnqw8dYaNDrll4t+pl10WwRqjCAjgMPfOCHimFkhkkaW5D9ai8iww6J+Hn2yZ2vKnrnMozYMTTTuRdYvWaNDirAqiBk6xkAY2IURj+2WDNwy+D7L5vyc0PkMLo7vzNPPz8igg6+vxnUvKN8B9xU45K2+2Tzx3jqPuP48swzumADyIiLT0UHranlLF/kV8ziqdavYMvMFsMtqjc5eg/dVvPgy0x/ScOb+s7y0Ws3+PR0/6NPynFc0Y1muNgyYd/g5edw5za3a2Oj5u1Zal/xnyc10+sHy/8/9sb844LStdaSZubDZ7iAMHqe+CUzqIivmFIAZeZMRTDedu0N06OqG5s9DeP3ZqAMiR0yAs38eL5FnOeCYa3zieRQZGkJoa6PYTlnZ64AcKNtGqWQEAWhGp6YB4qMiKB4Yu+lI8A+wKaAV0QcBPS9BBD/xHvMJ0UpZZm1T1xNL9OBGzMv3nfwEWAWMA4irYwpAB+RmQMvaWccldp29SzGhRT08iEZqnbWTn/OjH/pVlSb+iaT1V2Nc4twoLYPOs1JRtoezh9bUlrcml+rNQ7Y1ADCiEtYRz86i8UN6H8cBv+M7RHu4lm6T4nQWNX4E+r5wDNMUPWJivC5dPKyTmxa1WQRFnXR0blN/FU6HFztqXZaMy5qxWETUjTL4vrI5me1t3WXphRWsbblF/uXtFn2eouux7BYVtTaM5tDJpoUlL0u7bk9JpUImAjgvIvOYC3Y7eKPIjhVA0ZRcZvoGyuYMWOpAW763Uw07ByLPeSUW8R3oqneDD3gHvaJlvp+v8fGnuokDAC1T1Oa3rcBy1lmTExPBe6qyiDmeoSW/u6alUius/ry7ne9Z8QhoE2ugY614oR/LbNC7e/GOTAN+N0eiKr6Sa9M5TPzcvBYVf9QyGvy4esaKJ3MUjeWuk4kzJuR4TdjDS/5jRb6K/N/++T/2F+f0NlI6eppECLELODk64zMt1bF0DVPBuLOMqnCmhaeuHRtEjWdUrbMxDnFmivtxXygkc+a83ghKL/yH7l0gDhwuYGVo5HCeNCohefYUBRpO0TfZjbLddNpp5d10Bai0mw6g4ObM0o900u6weU5JrYk+xKciTKR5NNoz0nrUCVGCGf0A2mXt9dAjrV2wWgh3YMxkWdqartNwjIwn8Fzrwhd0MlU6brANc3m3nNuhzlXsAZJK9KhUmsyHoDCsrCHQp++CXMGPSv9wEHFQ46iP//5NI5FKdtrR550EEGpc43XhR9OMjodCYW/eYWcCfdUUOE4zE5KRJwLB4K56ro0BynRttHGBRe3AoUJjO0RZInC2ETRkZhzy+xiNU/BYxqvYSJGosq19vNUa+1lnRf3eUhnHNKi2S8XZUiNtjmhQA4fDEyfHBs645vGbpsWxgem8i6WMMaSxhiR6yBf1+g4jBhpVwNZfb19W4DNsSuwJl+As1a4yMGVQvJp+BW5Xz+kA3A4YVt+v6NPRJc9r9d7dmLolz+/ONKrWYvcu/r6ad6Z5Hlv3/H5s5x0EfgbzHsa0WgseZ35O/s43SlI9UrWmuLdybqr1CReDsqAp+aJzkqq553F2PC9j3EQBeV2nNbzkf0oh7OR6pzc+qvzf/oX/5S9pIlHiVo7MIIktC50w3CE1bU4mQE2ONiUAIJ2DFWDctjMcbNZ0+Om84Y+xeU0M6pBwMKAnOXBqnj4Y75fx8bvISZo+F2k5RrkEMKAzsGFjL/mxegp5joCYaNygdeJS9GI7oVGor8DEmiHYbvCnrfuZuk2ja5V5HkBjhxSdQbBR1zDQlwElQYF2Bg0lr4yIw8OjpV1ZEYpi+Tj7JIrI4fxghXRF9DBU69DnjoIyhaf4VAd2dlb6R3xuzCnlOjibylL7ut2yEHetNZGojre7NqkQHsdhu2pBlJRRYO9pgFKP8k0cR49wwrB5zjntdmRN+iPo9DPeQZrfOEwVl0Q2LdLDkQ5LsfQwHTZJQEtvKjFSFB8fnp6+Pzw+/oYecgXA4mlewyH67pEg2UAw3eWZuZITOqJ1Gn59/sbOi5TPaU3RGOr354fnb+OZAVNi1zi0p7DjSJ8kORniK3rM0kq17ifOgRIyqYEwYevDPw6GLFIv46OO7LHMHGJW6jj/lrzA8Gy2Ddkf830nW9/d7nYFTt3mFkATRMiRmw5oYO2XQNHHOu90R/vgmAW68nXAZQdInReJzhmgljRJYJjtMy+ZrAdvKDhfc8J9RFyO6iavdaZJYIG8tkGTfapYNUfMg2m4orOmwDL1ghL5Pv579TtQ7b37DN04mb5n5nUQPz6HijrvHuTObuQ1YRoz361oX5uCS/4hMb+y/N/+xT8+nB7UkEREBBZUAxfKPuz8yPemdHxXb2ovbUYDtRlWYCOAyFJnIBzsYLGSi3EdIzK106HvREG8P9/sZxU1guGxAI1NC/MNJw31QTy+0knz8p7bw7MBkYHFpWbCJNjB88nddAeZpC3V3o9xfoa6n6OqdPViwMVTsnCkD+oCUGUjADGcRYkk4KBTjxbdpMubrjucGkVTClKi9goOjqe+2HcSsUSakTkDGKLvMvmueK063+zTjCy6Bwt+luNe5UBeqXdCnQ6TIaMGB5iayvSIGj2ACTAsUjet8YNLo+gGi+QQzb2NsVssS7dDnRtWEg7UmxHsYz/Igr2C/fVsruFmRq0P9ChlKGoylkcN7Hq0E5cLladFpwynBk0/nvSzcIWsk6FsnNiGATaEjFfs+CpkN+omFZoUjNoce6Z2XVQZkxokky09DFj13qjlof4LtklkYilnm3HHS3N4hiPIxwFYKprTw/tlzPIsb9XdjcnhtjYMyhSmcHSENeA9aCmK1lTgUMQzpYJVwJ6B2JpD9S25yBu2Nush2KJuY4Nt1W4MK/DM75mB5LEAfiJ2muxKnWWAjluxVhnAxmbM7JxUYyWz6QH/FTDvgH0HovO8urnkNejew+Ml1mU2bn9fOQkVv/i7muQR5mcei9vC5LBn/mcWqGjONO14dEenvL7VOmW+nbfdVR9c8h+r1emDX0H+b//ScHqwc2tSMTk5xqnMzGoMlU3y5yFE+g0MA+qhNZPCdoop58IdFYA0eq+3G6bOXHhv5chErRA5cSjEFSMdwDhHscIBQ5oa2kzTeUC0RTfGkWuTmD4gEGVb2QYo0o02u+mmCQAgnBmRDmZOJJwGT5/J2mptbX/Qt6H2vCmDA8AByuDpUGc3T2+b1bmsrFymXdwEtqPbnsEGBwnyWjpXRDe1FbAYTPJuZlLEYuejePRp3G/VAMaD9+6QvRuB3eGx3Xpx+nAez7zTmcdQGijO98bxRTL928Pt9wbBpMtB2nmkLVchH6ijK4PuY7q7YkuBQ3pROyIbA0V1/VmL924EfuWDkwUWJ9E6itnpPQbIx1/qfHo9FfSnr4nKhfr6gPSmrWWt0BBb10NkRLoTmoSMnDdElOEHwGGScaozo1FnO8sJRwWJMteCH01Ls3b7tmGG+ySOPpwySzPlBhrqWVsKLxxeeeZv2HlFOifYH9QRaW2ZARNhEW22oLobsBjOX4JANq+Hh2/WRk+/h13S8QXgqQATg0X+vgOkcAjT9ssEqkITYryzI3aG7fkZ1bizfOdrSvmnABuPIY8n04Fn0YHQTv9MAPpEVCPPu1qHo9Q2wNYuzOvh86OJV85lt06r9au+6+jrvErOhUcZD1GxOcsGdOk02DznvBEQd81jm9+Bu7pNgf77cJhXGvYeHuR3HaOoR/nSDYtL/jOfdLqk439ev3tlhN8t7/0R8v8v//G/BP3vxoDTp8RBMCpwNISFcXwNBQ9Bmn56tMgMjtWw+jXmaejfHG1i5ySMFXeX42dU756cItLyyCaPZ4WxHW+KKI7eBCJFupo5fvZMpQEiZl1kSp8kswJn0W66GHkBftx81hPhgBZ9RxOxtyhn/kR1P7SL6v2VRGIA2MI59t1USilkQ4ltXICN2CvVbnsjZQ2AjnE1mkqgc7PuwNvSqATa2gdnyVh7+7DS36//Dlve4BEDjko2O1RUTyoyh27/ymxUGEB46pLRRFL/3GsBDxtALiw5kwnnIyn3K1T1zmVyYaQ6KdFJWPfT+ERXiHfnB/BmsOCRM+hcd2CN8v45IR4yFnD0obVENyACIk7N4GDzX6QjJPG5dLSyYh352JrASCt0rSdCa31ZP4/iqwRq22yNwEqkRy4f/DhSU1Wza9fAMaaRN6fpc+IIgVlk3a3Pt80rNsqs7bfJpkaesPT+gHDInsw9i4smPul4H/p+XpsjaGbQWAGFDFh7sMpS14FNGrqnbOtnGVjyuDrB2AEbABB0OSvE+7gzflIKOUrDt7DETwC2mOPhVUVHtmo48zxqwJvHNOkxo3bmnfmaYxzhJGkKmsaT5Z0s/+mhbBMrnqhoup6rfsu8wtKG3zMv6d9p3C0Bjs7P9D6KpE7y1n1uA4bIZ1mo1i3L6SX/tuap4cXRVkWN1ln+hmmvVPIPk/9/5U+MSE8wN794jpgoK2AvsHKADlETooQ8127SjWCticGmIkeXFPfa94atmDExxlNd3uj+8S45IJV2+gFg685wuszarMCH77OSc3zsL6ehyXrX1W5cD1rIT07XHcb/97RgmHfTddPVaP+L1P24ApdzPjT1hk+JMVxj4TWNsminXlOnA4B5hzVzd3wnGiDMokeWW+gK0LWqAVDeLU4JMorWIi1O/hxALrU8vUfoT117QCWmcsiyeUqPyZMOM4S5AipZoXfGOxtN4VMjuTqOFKm0tCexyAPoShtwK1Hz3CRdC8pEFDqOWhSt71CDLhE3dni6SdSW6RRpf95Fc3G9ngvFmo0hReiWClAdAEx5UdASTVL0NLEn7YRIPOyyMdLfvmsjDCAsrAd0sjrD2u7cN2jIcExNDSw9TyOpg2+0Fsl6hjzcRp3Qo0Zy0K1OZJ1y/LVkB3oC22Ke6zaBMhmTjUXSCQeNR/TTGC8b7l4eAhCbmna2mcAX78CnVB+9gReGOe/4eZbFw3sXssCAdxqok6kGoaflH46ybYAwp3aiuBJdJU3u7tjRSt82gWEbQJ73AZgV6YcVD+x0wm4uGVzz887cOzssR96reG733G5O1X3Z3HTzOZglWpcVDXfPq+49jNP5xehDg2EQ3fF05ln8XV0Pua3Gfcl/rNbMt/MqbvnzZ8n/v/q//iW3rhL9qJwMdhyKqI75EZP5HhM+pL8ZFYRp4DAYB8nmn/1+aGU9iDMKpY2m+lMbEuiQoy21M7JFl+Z7ovmBOD/scBTvP9xrD8d8EbHRuUbYF/NTpR4RIDxP6UwOk1m38T1206cdX7ve240pVre+B0rUqe7HXvQ5zvsxAsmhmrqammEWaTAAxNixhoILE4lSXKU1CvLDKYAnaqkxw4H01C1jSquP0EL8+Cff2n+QSuPDA9jfWczT3+tYQlmw2piLsCWJzc49kvGMHXTbUd+ForIir5QT0yBAAuhcRCrxkEET2fV/1CQkOyMozPjikF5pi60LI3IDH2ER8j5N2p99oTBm7GriuCpPRz05PpCZ17BaK4vJqI6zekITL2SmKZ8lnDnxRuFIBa909RzQybELqO9JxdjW3EJlWqPbcgDXb5gNwgFQ415pUGJnpFGXOU+jzCEURjNIp3OHpHY2RAW3fNbPlf1z3l3eOlcJjJ8CfWT/9Pm9k9DJeMUr5+X/OMrqXoMRZQQKIw6Nrc88OCv2qsqpqXg/gy8eV8WzzCKVw8jjybRkKtR6Mhw00GKeY71u9Zhnmp+V/+lZvG9nA/F1SDy/mus8kvPyf0a1ZT7ieRZqyPciq3X0z1I0qJMZn/Ml/75UHV/ntfyU8v+v/YljepsI6iLCAkH21DCznnA+9HslB4iXo0CuDDi1jd4pu5CHDmkRHRr3q3NE7ygOOcX7c52PZVFZ1MUaFiS7jDEcokAp8oOIFcaSU+qYFvpdM27qbgYHQNOtdHtNjarlzHMXLSgym6w/3VENdtU+7nk/QRFL7bN5Y8rgqKCDeX5AGtgmljSvgNj6m/JNJO5kQ2JpPQK2R0TiUXaftZuUAS6jLVwS5U3hQqphKbd6e51fWDDmDKnHoPozN8QjTcmioUC15zKk10Phbysg4LTnHRpbBnHwx7hkN99I4t8Z83aH9PrL2EpZSiIivnio/GSVfMakfoBrxpDtXB1t8W9encjzGgG7USYLk1knz7AC3RUVKnBRAc0M9DqQCo89g8YRyXE9aVMWepguRQdBOYRZOCjaqAvpfEc5YDN0gjbOsDeClihLisBPmY6VOSmDZwZVFTiHHXO2jADUBCCq9ZgBwwyEJ9Ca0rsOaXIluNurnbymp+W/2d3vAFAFlCpaZl7sAO06TTDe5nJjH+W/V/JQ0aaTKX5uRYNyvTxLIduihQP4ieW/ckrBA9W638NLs0ZQelafdTzX8X3WYZf8RzOuks9Pwp9ODn+Y/P/rI9JDB9/lqA2nvkHB4yeMTTgfOp35GTFFO2lFjA87SBpxAdyLBgL+HigtdpBM8dptntbj6Xe2d8QOC8Yr76dIEzdZ0PGHKcRv+Dk9z54zd5JTUfHok2k8pqM7PflgV0xGSoWNDtKdGeJnCWCvqPuRsUkK0fiFWsr1NvL9vik1m/EB2uvy4YikzSQKYCCacfDoYuZnAvk62up5eypF41I4bhcPp0FTq4CSiAesa3ggLxujSM4MVvYmrIIA4BkyeJKbM9pA65hQq6GpPgoDlS+etDxiRBGG05aWdKecluCqUWJZOc3PoCRFHSYqeJTfIOc410VkaJyv9GQpSEd36/CJO7qWYnfWQr4fJ++fLLSwjmVGA2wsypoimyw96QxQg55kQ98CRvsCMuN8tZhBJu/ZMa1AZUjX2FSwaOroDmiRXk1HQ+MDRtmQZQvBSrQTMh/Re5GZ3G9jGpBSy+eWUmfY1uG26KI3A9OKPivQ1bGrfr7SJ0eKdvJNqrKMolTO3Uqud2uZR53nz13Y8rMqB3oen61VszcgHGGDz93eVuNmucm/s/yAllnOPPOZXtI5Rt3zOznt3t+JaV67zyP/OtKZbkk2E/Gw3lXdznnddMl/x/8dL2Xeq/SF60pS2azXQu/H0360/N/+DXF6lO0U7+mfGukxcJd/HtLd9AmHLmbTdagHsnQvvMfmDmIETtW3M5Hw+xR5MeQg17ZnBtncrHNbFKhr0a2/2zS11x24wzKnopWtr2ke8kzX+vZuM+h+8CVqhNI7fL5mhFFPZD5Q20VrPGbU/UjWvKQJ6fphQ1m/Qk7cAMzK8uRnEU93ZrlTue//ORq7qaEB8cRVUZWJs2CMgTSfX50FXQp1EPRsFOFWrZtCZwMDThG1M01seULhTGRlSWL+kghES2p6jzs9KKwZ78ShlIYYhee4RiTS5Colxp8djY7OiQMQbkzsRjYuh+e3h/RaTY84ZqhRg0NnDttYE5dI8zYBgJGSmBHWx2PXRiBs4AUi9lS+JIXZiGCtVoaJdWa86j4wjffCYEF7Z76R8SAVMc16tyzz95T6Z47wSGm7mRDHnCKNONLRcDC0Ma3soq/ny/z7Ku3VzJ2fuVuryllV7bzuZoRrsAaWEOAAsuYDk+2UHFeB9Xvkn/m0oif4aLboxyuzXql00xl9luXE+fmFNT47Xt6txfS9DCb4s9KlfP189TGKUYHGrDfudaZ/pvw7758UzEmWyT7tHNCKt06+Mi675H/C6B3NP5T8/5v/2/8jUI2BvDpAykr6XYgAQDsAJX/PSjY3GRhPaA8fVe1O0RpTjVO6mQF049Qxpi4iE1EVzEKXgp04v2ZK44t3cPRnTtsLo1HNiZVVzBcAlg405eyWwmEraWkcJQXh6IFlB/MJCWVSOOzzWPfDKFavN8DpjqOdueJHh5xR9XericUNi/cVX/kJ9AZwBJiJtwObohPUmgEUdR9NhDqE4+DT0UpKClK0nmRcytiJgBR2yINvo902uM33r06SMYPAiOmYHFqDNt+f5hvwu4e55h2DPIRqSOUwKc+5ekY2HMKB4ZMKOeUcTl91Sx/0G+MbWSl5ibYknvb/ZB2fxtmzD8/cMtu927fkwzd+FhEu1zKs3nSgt30ADsb3oKB+Pic6TixSAD4GR/le1mWr3bhqDjw2FqE89nL+Htk2/T1FLzFbdfpjc4PnHccAsB6tQE4G9qfGN7vlbidtH8lLjfxZWLeiZiDTielxRm3ENXsnj9fwPeV/9x7wVcUXgTRSTViTUpf55z6akS3naDo9NMsYeAibhXkOO35bapaivmai5S8o/51dqnXb0WV2+mfa0HoyzQ96rEkN3fHRrHOphgu4jqyX88ol/4QCZmi1si8rHfkS+b8Npycr2tkBCkYbF+p3sTfFN1cNDbRDULTRHbe6w+TRJKu7oIjSnLZmTgO9DL9WP6eW1kaxGPecWne4P6W9eSTEo1/HttTufNlZQPx+KHiNnOlfqDGZUvqoj1PQOVZmfCa0tEMC5UnyQOqitar7MYssOz6SNvVNzgtBcb4rc19atOkxCu20wBvjxAlJTO/G1gq1uY2EL3NwjC7e6S00oDoy4XwCKLpLTDs34zvtUaWpcNpyylC9BM2IelRTIMON/6wpQ3ObpkkRTMQ+xvfRzDyrAl0ndEjjOodu6VhxL69JXwqLNCDBQUtqQiD3KCm1Vs8cVBdp6yQGlsZ2tbyajYVP2xoe6HKacP0MJj3J+Lp4uj4UYdX1RApggLBsBCowyOCx+/7k6A7i5vpgkeKIZ4Mf5B7q7lXB8LAc9chiBS1KbXwDeiDV19vMI7LrmQrzTjjTaJYYsyk2jG5cZHKmCErl4FTcl6VUIvZU91NJMTtenEKVnQmmf7bhWL/MIzzGd5V/Bn1N+lIecxWNyGPMc/b5FGCzlImpHjEV8W4cKx5Lxb3d9zutlO9bXZ+/2/39eeU/Rl7RdUfrzOcs3/rdJf9fVf5vv/9P/mWP9EBB4ieIcgDxxlHhmISxjihLfCbP8zQug5ekpPgeb7lLu2l+po5rMU1n4A5oPObK6cAYdE4BPBRURUQr5hzW0CM29iWuaSNXh8hZOI7S5hovpTmOMcOQayqbgaH0TjbCozOWO2W+Oxp1P6MQHoldSmNumasmVMExWyVrlGB0kpFPO8Q7FX6Pmn3ptTEGSemS0xrvaH1tqXAaWUAUaJxZMqJBWmOg7oWeLxL/THWaA6uHbhrzxA/9yJz9l84QbbK1j4K+V3s1aLGCOg1zNCfg3hgXn/WEnai54w6AUbei1ef4TFn4CBpK8MXj5N+FtkpgpHdp+2pa34k1zcGXYo1xk4YA5mGEMZsch5/NtlbDhE0gGF7ZeJDAVi7qsXa+yfvJhorXEL+rXJu4JwYEyVVdHGl1AAtF+saOlC4y6/4MvWjQvobrKNdHtEOYopC5xmIi3YJXD3NOqUeVA3q8J3Z7V2u0c/rO6ot4R8Pv9CC+lrcs31v+Id0r+rkK2DSAYDVcqZPdGtV0NRtISj47lmcdzdBk+qbuPozjON6szC/5Z+e/Wt+DrUnRnUv+lUKX/M/Sf/v9fzIiPUhdq2pWxm1aXzKnoYGqSLdyItu13t6Z8gjwfBh+AHwYa64rUkBEaWFm2SbnajgS2QB6al6MGYtf1+SY8ZjS3YxpDEEIzqo6xNln/nyj8RRxwmcUoeE211qLE6pd3gVnkTrU5XomGY/MX2+QJ4yC6d8b857P+3ERwFaj3APVoMUrAqqtAFijBtZFD1G4kVJiCJWjRC5dO+101qrffZ1N/qWtr6EwbQ20PkadBHEdjb6HLmkAaIaCJkB4Bh2W9NIb1QlDMhsOsrFGFKzOXLY4eYpNRk3MPLwKyOU7V2BvMkJN2oAigjjY0tgqcgnNCVe+tnTL4cA9P8lZLng/gyCtXTPU5KiSBeJuZnr7GyBrchgRewNoWBL73OHzRTtHnvcRHlF6xWbkO5aE/t4CVYKwku0AACAASURBVLzH5YaabZzYMV8D18YZs0EJuxuy9N+z/2/jkyelSe/mlsGW077g6bwu9zDOmbVg0GJTnmSgAunVeKtx/Uz5R878igZnvsv6wMWfJnx2jXbvA/27NV69J4DnmkPkOuezS/4rx6dbJ7YJoPIl/z2/fVX5v/1bf+ovC69EGpoalEjZOh7K2TlFAOoit+QcQHGjIw9fp9d6ctHUSAHjIvvKUE87nNk/TrmTpxXNFjqHZYCwyeFIjs8cOVIxRIoKRtDNffqe2g9jxkEbbpYwp1/h/CSNIMS7EQFj+ug47CDIVPfjW+IbayugS05gHwD7yc7JsMMjCUx4qKHSNvdY/ze8VqcmE/BoiDtnZlEOhkVyrvT6cYrM09M4vHGss4Lt2dFx70dGjd4Q6nEq8OamC+Trz7NcrEF8Fb8hGulGYLxEnF0cwGo7+5b+KNwktwcsqtygGQBmly52G3eGpjNOFVBwcMBOnfRo0CiWnlKqzjbcODohyBxza0IxVk2iPQQ5ZCksaud0YCn5gZ75GMuj9An02kgL1kmEUjZr/F92Z+IbXco+ssYiyMCv+xx6B+vD61uv9XFsK57YjTXznUf8il1y5uAuUsF8ltVRlza1cn52KqkDDCzvs56ZHdMqErdRy+WQlnKZvvxw8u+xxmN+f+Zb5lcmhMwpRyMpCyT0Zd9uN+to0iSufw56LKcp2k1cUplxS8hlL+cd313yrytwyf85GAHdwyHHS/6Vdrd/+0/9Fao2iV0x3fAGizURDqP/5EyQotUoj8FQ25Ibjg8rtPG7X4boBlwfuxDXoE4hNxaIKEeM18denIszRpVT047nAkUzB43CaGF1rsPJ3dUwN/6Z0+2ODQ6UkO7QmHcDQCSf+/kVcI6Cjoc0O9Opultua2BbowNY4gBIyZf6poX/WrjvN3ptiI5Lt1bVwESLWW3xLIObt3l3iOGtvy8tv/HdqdbXSiOtlwLfGz2EJOM/shWg3Ozv0888+onWuQxiLXImtD5YTjaLRwg2T8vSuOSQXnUM8h0K7p689Tafc9XD/NkAt8DLvpgBQaTN1aB1DeKP5ECKIup+opnBkAVtYmCRr+z0eXYYdIB1srODTiM0AGYvZvoS1LnjZSHBePAY4NPD85Oln5Kz4zT1ZhvHh7LOZLp1n++Gde/31XsmchEIxLXsfIUlmLsCVuO4axnSxdW782egX7UhUb9bZ9KNi5/P81zROMvR8b6QnWn8Jwqv4/r1uEMhxer+SPnfOZ0zjWo9dXAyi1qiyqlxHiC1nOfOf6++Yy3usllkAOC6PO8z/H7JfyNNl/wLYTKvKh9e8g/zy3uLt39nRHrsk7kBAKWFZcYyN0kEEdELPGN8ZG2AsZs1R3JiiaK5QNS56DMp8nPqwNHIW5xS5bhYzbQGlIf/zGfleEqZCtm4jrLOHLuO7+YDT23MTBuT013tD95j5BSM7U4nxk27VzImSn0L59TqgijVQ+iBFtpebA8Mb/Exqf2RhdPUIzfxI+4Bxwmtw0ba1UhxGzcMIBeNHcIxUuDpyv2MVr8Xhb3w+tz6WnheDJRGeQTa3KzJA/lyYwryPZjBJhc7jWSU7b6A7KudvTVx1GBrN7lHi+x4KMktIdLwsBOGCELUveVdHja8GbRl0h5GSB8wMOH78vMr5VOCBUYG1EVQYiW28SAO6ojOTul/BGMslKIRT9tFtm4Qwpe5xzIL+Av5an0bZGamipxPhHRdecCRT6APc6QPugLBn/n7eUeU1752fnH9IXmTdlfTM6kgfAanMyVW31U0q6WBN6D0rsqhirSgRbrfpg7twPupLonBZx4D3xvzmIFHNT/5jNLGq/nV/EXPvqNmCc9fyh+98OfI//moJttPlqBqfTr+8s1Za9N0oM3hxuDJPR/U+r/T/AcA2+wp+l7j4ftL/luresm/688vK///7v8+Ij3hvLBSdyzAaVVGqeo6TYfSCwKUk9MgnduA4s1R8ZO6xy47OhmRWiYHwOuDSCF3UQ4fg8WVtH4m/gWAsrGSswNnbVxdOSyMkeCAKAlV1KrUN/9+qk9iZ62rF6ImEDzG8RzZ9Qe9A86A9vKThHyc5TnShsTVkRQiu3l8Lt9ZGGPcYwd/ilMj2V/Wdo/Ln6lVE5ohmA/R7zKsMX5t2+/+dPGSArBrKo52sosjYZzpNQpkHh1AqPYvsLQl2mdR82b3SoqgvlCDaMyB905K12zUrjzJTxsVIkkQq2dL0SNgP0HtEzvFlTLMFGUwm0HU2Zn1hom2rojgGpgcfP/dOsCF0+11V1iL1KFwGiPOdHK09Jp16WZrQAdNLczbdpjKLbywW9DwB0BxVwfBo89rjZ2HDAZ5jWvQNjsapZORUtEOz4TDbvITWyFHeuc5YNlLZy+R3Ldpdh0FMV5nvKAKB6uDV1abFaG7mTYm7Z6mnVPrqujSa+QlyyHzeRnJ+gTy3+kFnuvBeceXJ0At63B9zuzsV5Eh3jhQ59rko6nzymuK9wTXnF11c8dSzfIk5/SoS/77DQ/nq0v+hWNYziqZg05ebR7wNec5Gph1bviR7Ud+3lvJ/+3fG07PhO8sfYqiP9n5EWeAcuUV1x0bHIR/Q/UqNnLyfTSugCQ7GQuggbbE4jNzsFh1zZAuwfgvG1H9O8aQsQ4SF3CPXG8D5M/82XSgqZ8Do/7EIfL1ZJ5a+Zyq7slWuuwYZ3UP8iw4g3CiqgYMxYGxM/1ip1DOUpGmWBrvUCJaSt9ILSIzbqE4Kb/A2Z5aX6I1GGpHuEA7FVjdKx2vvb6UFmUsrQcxfpEieuM56b5GFk26xA2u10gQ/MaZ43A+CBwUHbhQkeTpYLDPzk9upB1Qs86Cny2ad7Nj6FUk691Slg2ApEnBTeDxuPvPoCArvU557uY83xdg00EJmnUIacd/vqtjLo6lpoyNVMunwb8ahnRIPnUolMiPSfyo4/LIHXZrTHtMTsnZBcJ1TDOdmS7d0HOIqnKeZBwinA0RWyYGXkegXY+xWo/OmATNQmBWhq96TlA9jCqLH/Pbjifw/J1hZftU8bPovBSxyblqK4O6e7/bk5RaFdygv+1oWY1herddwJty3EByon2eLy3WZ5J/5slqHXgulUxk/j/SmBzfIoJ4r+R3vNLpN/BF24KGbrzkv9FxiejVBs8l/0G7Ly////6f/iuxv2gOhn5gEQGyKHPqGNLQlJhe22K0ZadGYIS18NXULXNAWInbNfLqhNX0/jkqwhEVNihY0MkpskYFQxisKiMaJsh4DfzbzT5eisoYSWpnihw2XCCtqZkWhwiPTt7HWTiNc/pcPK9Ml4OfwqmBZGoHhPe6JP9cU4TGuT2eUjiA4Ej1Qkc8yXgDLzw8PI7uaA/fDWhGJEljIcohiIqowzrqfmyFpuLPbI7uNS9vcb2tATkIMY84/0Xxr7XGJmdznD4ljpOxkPp5turShtjaX/tU1y2e2xnhfm/mM945GiwEbNTAjlFfoj/myZEFYIpX1O8+C/CWdr5THUe1+wrfYXp32pFfcYJ3zbNXTyDHnJ3RKCC8b/Aad3PDyLRDoSgT8T30Pj/7RfduDK1TUn62EsHOlhkMRaaPlEdAh45OinQ/nCxp0mDdEau0tgzKZ944ArUAT7OzwUCYn1lGAcI9c4iOtclrFCytEfoqIlONCfPonsvzXPEr389zXGuVKn1P39jdN/EbIgLFxZMjYgaMeT+sjL4v755W8pFpJeOEM3M4b4asIG0U5ohABs4dvfndP1X+7eU8bqefES3T8sh3sbnnmyhdGht4AfSlhZl5gXjGbC8lPkyJqtUa6GOTPt0thn9/yf8l/9ZkSITBd21dmUXGSjDVp7T/7yD/t//gT/9VkWU4LZxhMb6oIiosm/OBpFRknyMlufbHrIS8A0CxqIfJqWI6VlUjuE/HGcYrK+y6RTXfD6VozyUiQNFpdzuL5pjW5e9gkGDRxEmjOUfkKBQwDCVeNx1yWtQyTdEfB9/RAAEBGo66oZkEutv5wahhyR5GNEo2oHUxKFI2dqB1Z1r7F1j64UihElLpLrumesVutdRRjIJzCXGMnXdbrUBK2hXtQ7a+nruvea69ORVjKjjLB6lr00ExDKgN+EpIzFFRhaBZoorfJ8PLqXLGSyi3stS64bCOFDjpREcAC2Bh87bJsc9gA+jwXvDUvbMCmxlcdPfCaYg6HRgAoJD5TqEWPBLUA3FkDE7Pw+NoByEXO2DJW+os8LA53uyDDq6lls7s8Oi8w3Qrn5GBKnbq/Wt6JjsbO6OW6bhyECouPTi19sAq8sd8snMoAESPGnzHqXFHxY+YQ9m9jdOSmpQvtiMH4MpOR0OHHuzSOlN0aC0je4ct5ruOsu2oWskf857XbjbO4oqvzvDgSv4nHn9F/5x7xuh8cOJ9DMazvEBO5vnVjk+3KaHG2HDuYvPozPwu+Tf9ccn/JJa/uvzf/sM/81exKQl5UtBbAP/sAHmrZ5Me7fIVxluuJ5lWTB1RHiau/j6nyOXv50YLs4nUZ8+gAX/rT/1vWRdEOefH5+jkYm7xNH++zz/GNDtk62dkh43pfxiPTQXVPBgDH3qYo1zj72PHOI4yAZNb4buAZm9MpvjFzkjxxhXiBFrLLLtW/R8iBjW32BeU/+QUOBZ7sgb4Fali4FPx6MTza3brNKeNXPO3qOtB5Mz4CbuVANtPY91uD5GqqOuZRzgDxBhj58jsDGg29ODJ/Dx+Dl9TKVlILI8dv2tLdTP+2PAQ3WORR+pQKPVo3KHQd3JV34TSGNEKa9ohj5J2hpZaG6OF76p+vEq5DAUNE6xcLuq6CJhDP9our7qkKkPyBntNR2//PF3g9LdfDn0aErDOtO+cmRUId5WaDpWsxliu+xT1xZsysGf5sfzvPPdNowHmIyIxReOmru7llJk/ATht+b0jfOZ5B8ppANkRMi4k97fOc8f7dg7HSm6CysSTyHJIqXmVw7biB5dNssPvKv8Hj8LmZJ+XTnACt6B9xSN8P89b5gk5w1zJ+eBnZnplPbmjZ3d/Dkte8h+UqmxJa3fsYvR32uo7YxTvB7Xgp0v+Q49VfP9q+/8K+b/9R8Pp4agM1/LYIlc1NaqEdejMaOP3KOjXbyXqYVoR1yINar5evw3HBM+PGfr9ORIiaUaUUkYcnB2dlQMj76dZAYzIT9N4foirvQNzyFEynUtQR64zjTVHdYxOplDZYQKoPswBdOa0uSkFUFnt8P6JbkrXcSXS8bgMxOu2PJJmyE6cQERV7UwgAXvm9CoiVOB4uqCcuCgBm04ZvennpUWq637kkEwwBlm5SPPRvWXdhecaHBrxvUjTbzXJEUfT6qgonUWiEwNEVyjEnsFfMQ2FBJtCYFdWRq8JEBbTe81S5mcDcISRAiimKKPrLwOzwwGSCA93KBwDHdG34RGN6x6942TkD6ne8nchlc6aSWg7edQc6iyj7o20okWLniTdcfjJttlgzSgkCkh6Kzt5h/VJu+tsPLI8TA4a6aod6+U1Y9FQdq+7Q2HWE+sVTslxjvzJ20h1Nwc8nb+v+IznMo+oHutqHYgbKCGVn5rPw5rf0clQXhcoc+X3no6/jvwf66TcTO9SCIuNgI4uvhmRHPyOws5PG96HLF3yf8k/6k5Vpi/5Z50JOTva/5fJ/+0//jOa3qaAPHYv4acBpI+vjgBeh6MLRpEDG2VOfWMAnw2NOw6kSTwly8G81ZaQ4yCta7NjYQ/HO/znoS5Inzc5b5bGpDELZcFVLRO37HZnjyJceHeuwwEWPqbezXSMOehY9DyhJmLl5/IYHkOK3cHhrDrFRdc5S1aDDbWfSjyZh+a2uVkZRfQ43NN3wgRNzlBgKijH02VaVFBuW5k3PTDnA7S+Bg+EQ8B1P146M5xu50NriwzeNHKBHK9xBFRKLcImyCYaYyui0rOTlPKhPGcAX+fUdCkVPX6C02FRlwXQ2s2ZARw/5sx90mYdtHeSRIdCB4H+nT0V7Gld9oZcSUMI4UGNwoReM3PknfNMAuRQW9GOegaPl1KZY2BLIHNCsw9L62S9W5MOn4YD5sraU/Fm6DXtQp/Y2WYJ3dHapsDn3eZN5wMOzOt6Zp3ZNky80MyHgYJnkRYsPqXh2UDudQAxwUw3lrZYNZML8Ndk244RnY7+nBbFz65+Z73Mz/ul5f90iuCBPacPZmuFr46A3OmKOtwm0nOvbB1Hd8n/Jf9RxzfJ8yIyC7t/yX9IFNPi9gf+j5HepqzFzoMbZLMic/RBVXx/fXhgbMDG714HYU0FZNcwdTETDO3trQNY6f06MgZ1HnmpzvSpGiD4dUqKcG7I8UtF2vJue6tErlhDGZiJuVntC0fBvNFAikalQMAcPeP217FscMKmcdh4DzVYwHc2YMxDIxBo7MB0VV7AWmGe0oDCFmaqsxqo//c0Nejh0WpXfKhEREeeChDt1BvdLNdB2a774Af9HtFAaSeNQ0YZdewQy9rGverb43k/Yw5PD7dBg7SBkHy/F753VnnieI2PLGChh5X6YTTRUrV5WwssGwAx8XvaRe3AmtMhjSGW7QgoGLTloWelz0ATv/M7lRr0DtDL+E2jOBq3kLYQggwtcvlN2VQddXPKx/XqvTw8ShMCBIu0MYKshx++bBTDuVcZcUfHBJ8m5ncPWGJRO4YRyOktdr5ZlBjiBd3r9cnrncfLntDkANiDZ7HteKBm3PLdixQ35ovMj9m57vgpqxxfJ+ispnEA3pfV1EpeOuXANJ5MT3FDdoaq9/1K8j/R+c61WK1tTnti+VyZnm6tdor/kv8dhRwmTDqTU04rm1PJeacXWBt1cnvJf2yngl6dPGTcMNP9bez/vfIvTk9EdZSXAJyR6eLAN3ckoxmIvbcL9fcw+lwfpJGKcJj0KnJsiEr+XnoeE1GdrnBaxpMOERVyPDxFjKzAPO5owyLvkRa4OjocaaOpepjoAD44vJPLA9ixmecmd9pag048p/mzuFfnZutDESidv/7juUeRvTorfg0cN+MUHU9EKY5pd5b+ZmmKzKaDJtrAgNbAWlWjuL+KHkyKRdYC0TR0frNohYzLupQBJFZStNeVb39F4iF0rAOERn6JTu9t65XgDCLip1h8pLSNHuJwEJ8fbo/f5CynyUKQdqIpqNw3KR8V8Tiw0uTsdK+dHpeXkx0iTUPVy1dALo87j3c4zKO5w5igb3A8Pjw8aq8CbUwxvpGwnUhgs4+WFl0OSR3dDuEcubcbA5biuPHWR81ke3rSe3wkeXnOOwEYDdOQjdAq6jGvd5WuVp1d0hfTdyGf3dqUvDXFKGcDi+u756reXtfprMbENM3OWWjRY5QG9PTNG7OHnv+fOIrMwE58ltG0g8zaByUISRfv5H1JJ2M6ec/KAyj0Cq/5D5H/TPuSP2DD1hG4STMUBfBZFqFYyy6Wpb695D/rtQpYX/IPLDjza5XS/OXtf7bm/8lv/b9qJwwQUIaOQSlygigC4M4G8FxRuqAKQNnzEIGYnCt8bwDBNAcUCP88NFMwZyBSvoookDlrh+dNWJQjV0iji4gHZjI5E+YAwdBa8MTAld6B71gZoqMamj4A3HHkZr6ezW04V7ODF87PMWXuGLnBmmvZTawUHKDxyXHNdH3C2VRuAs/IcyT9TS2h1rhomtGhoBxF/qMTnO2aCyjV+vFDteiHaH1dIiIbtKX8SWTAUIAC6mzW39D/mupvLK9KaK8Hz0q8jDpX7wAjf59B2WxkjoC4dU42APTsmEiVlCCwM5TjPvgyHJ0LZExtEOUl5pTwDo48BKrMWpePUYz27ZZSaqU5gdCtHbVSys6+oo5fFStlwDQBrNlLSVnfSsXJ4dlj0dbJ1ffOGy6MbfPYoeMOwasULc/jw9rvdgsnvrSX53vy+lfjhX6pcHoGC0F77xMf2KHg6VI1JMcHS8iO0T38f+AHu5l9jiq9MQPEDjD+qvK/2jC5VxvfS0sWW8h3J6eX/K9Xg3HWJf9Bq0v+Y5syy1hlB27/6W/9rpxF7wrPfp1SvgwAsFC6A+G7p2F2vZbH7AUaB2QHKMNBBdo6EqRQOaim9Co3HnDWUENzAOp1FIjvn+qOTKMB8ANKaDtvZS13Bsi6+JitbRLXAMn43algIKFOSm4G4c8n+T9+FnVG1f1IPRxv8wiVPW+ir32Gzm/uRJlGwfrMtVnhDAm5pvqi4dxEFy35HvULVlD+dHu0bldyABBBKwWGOj7rwSxhNowiDp2MHWyLBH2Q1tduEMcvjrSboMG91jZfjy1WNDMYr0TXPY+QaO4bUgNZIQgvt+At7TYWYJ31BepYQgNAciDL898Z6M2AEZ0SjwSalDt93YNmcs78oqj/ic6S+qXPms9Dsho6Bap2BTuSBQoSelMqKCteGTZFVvMm+cRD05ehO7yT4AJ8ZxpDd7Wb8g0Sfg1AVpJFFPm0cyMlgnQOBdmFKuUo05f5qeLJ3ZyCtY4cl2Wo4mUfT/Gi1buzPTyuFfGAMpKlAM+ywu/g8X5p+X+hvoWOLMT8EH3G5q6UBNINeT2yTPDQLvk3u3TJfxFAveSf9S1+P2X/GS/8Z7/9u88Z9MJA4jo+eNQBp9eoxNOgVBnMSySBtDlHNfw9U/cprTMZcNidMXyPdxJiw3gULFstTVI4SGvRa6lxAb2XnzPPI85+mhoV2LQ5OsNRD6EBdWWKuc5RG06bY7pjDLlJAy86onO4r47wRGegYxc7j0XF4a/GQdUBqPn5MUYblaWjidNlReEjOQ2RHtksH8+HNyZ9rAeDmJVAjQQOm2SuxkY7WgN7+Yo1DeBt/IweXmjw3ua28xDr1PtmD2G6ZVKJ5nRZeZT7jvVokpODLK2Fv5aHUf2dwagMNstmE5E4AAU4aZhXky4E1SBsBxlFFIQKjskH0shsToGzFu3iiiM90RGQtbIQPlXnvfrXrfxMq6B9NecSELnDypsox/keQjl3OCBdmkQlWmx0siPdsSs/pzJaS5B5Z/SwGoN/Js/aOw95HSrezk7FZEcT753jF+WryDvQu8zUlQfCVnTjsX5l+Yc8VI5MRRf9LANNqLB1B0MsVDQ2iRW/5J9KAVKEmvFN3nJwfX7JvycqrPQk659sC2f98zXsv0fY//Pf/l34C1TtYQ6HaXEAeN7J4Kwddxh8R47Tw1AnExlLHm0w7RNg2hSMrSSUOzsP+IwXNB/qiR0wv9aaIqDVqk7LzqQhgOdOjXHIVAOEKI+f6UP1AYiEIeqFM20QiUIkDZEv5ziuBwrlGs4MUtas7TeBODwCUahDxMdT9yLdD3RWulh7aRrjtKY0X3UayeBOETWKOnknvDk9EGvltV3jYSMIgUQw8hwFfMJZFIuhVVVSbD4OOpW4pC6QK0UBuEarPIl2a7vEqZ/qQyUj5j2aKKCV9ohmPEmNCtpoc+0XaBdGHdAqnJId2eTeKIFzfJ1BcfWcKsI0AcQTzhYbR/yewTODw+PCzmDG6xfNWRpzE06jooxxEK+CGKszo4d24P6UESfDP69NntkRLJAok1YhByh5Ulxj4u9K3acm2SokIoPGPHd/JenxvWNUOB6UHlfRcXZcjikOTD1PnfX5zIY+eGXmi/yOPA7IECdVg5dZPqYoAOnSaRQTmGOYMu0VtDpqcroS7Sv5uuQf+Sw1SQ+AkuVk2Unw+LwVOGX5ueR/Vqq+l5lkxmWMMhYyHS/5n/UG2+bQx6FnGC//yvb/9l/8zu8+o9B9ajuNFtTJokV6lIH+FIXJYATX+5kvRs1waFLTA+Nm75QGR4prhuBkmIaYnARypLADi5qVueZGWSA6xVE6vnlxcsWhu5rOMJyAqNvR54czhdqXbHC8+YHXTaeObvR8HWOozPGb/J9pQNdX5/IA8AZNC4ePlIrwg0nFNE/7Q+lybH4Q5xdlJ83+fnx8uI1Ce3YOJZNNz5sZuXieKoUdWJmntURHrQxSjlwygb4/QOvrFfrNlq+FL/svHLCgbsjkUD5HK2tJH7RmHHa20FGZHXd58tt92LTmuVicAde90+yuz6SslXYaLT2sWwp+3+SAuSWgtt/jM+HLaEH9ZMLR7UQy6IVn3o6l6DzGO5ocseKZUtDpcEDmZOwTKMigitet47qz68O6f3JyaMc8wAqcnMx/M9g/8GIBNmVOzbpnWjBY2v3utLJnlw78Jns10y7TG3Ywp4iGNZrtzTSGNGcYphxdWKmk/J6KB76M/JMNzBsoWfayXLIecT1FfHPJf8g5bJd3Z6XyijlefeTGks6X/AcFi4jyJf9zs4fbf4lIDwm8Gy80NyDeYwAegXeLWHDqGOVhc4AeCuEYmVCg7S2sUUOTwb2tYDhf+kQKFkSHJopm6DURWZG/resSjF/sxqlpeULnK0Ru5JwaWDmLZkxO3wz25RmgKzku+jml8BVNIPz8H7tfegRQvsoYhtc9pbWT71L77qC3zs2o5lETT2c70JecW35nMeZjShzeBf6wdZIT6BVYcke6x2c780T4AKmKei6RMYfZaJj+z9/6ugOay89ljTTt6tEDqeo4evOIyCHUVZPObpobqNSbjVB2YpxJqH6lAnCVy8QAdAILTSpbvgaj8x0HQnqswPm6aqc9vj9LZY1wWx+O2NmYGkYkZFQ8WsZYeApr46MPAiCYYzrxkslhS6khO+fkDBXyGKf3nSjgz6A+5jS/vQL/wksnU1eUhY91P1gqzjjMPJPpkJ2ZisZZXqb9FvsjA+V6PY4S06250KjoEIbx54hb6PUGOhIIv+S/koauO2GxZgs+zevm69Wkch11ZL8Rdck/UeuS/0OKa6VXnWKX/KvZ+K9+5/9z2HQArQDq5hyAoJ4bSxGHSCkzoNtEIrh9tQJ3KlyeuqkRqE91OKrcLT3LBlV1h8NieyMCrwnS1Zdn8Dup8YLPdYrqRJc7mBVxtkxH5XvKGhs6ODKaRVjxPzuKiH5MZxihwxzGP0d9uPMaDCCaSExOJlJ3LDVKxmmG2+uvUr0TFYGXBQAAIABJREFUO1gxX3LyDg4v3KoAc+M3cQKthkIcuzEWzSUKAXYnE9sWcThjB9wUZH7C1tdnkGgFrOVAztFzeRwQ8/3hdvv28DwO1HweKYBUBSDIaHaYV7uYh1dxvZ07LWnXzpQp8z/v17d7+dOuVLSMFcU0DeTYTrYCst07ezDdAVDj2YQwjrvxiVpFt7IMePjveZ46lhpw79Ob2NjN61DQDoANuo+InR0Adlq6semconZwAvvEG9Wzj4Av9AXzKa831rOq26qA/2T0jei1QxKjyeDhEHmhCyZ5InugqZG17srrFbTpeXKifwW6yTmXawu5BM/t5s80g20Ifr3kv5QTApWZvtXfl/zP+rPSLwe5p/rMS/6T/bnkXwiSsUG2/7f/eqS3kSVmAJ8zXrn2ZgLRpKe1/sYcnyINC6/yts3m+MA4YCxsuDERfmcKnNBBqdEee0rXO6SFxaCR4jbe6el4AIs0EB8bjAnXunh9w4hehErs20fPKXKoSZk66RmxvPtddfiq0zg61ek86pS5MsIG0CKEtlQzO6PImcPrkWZHUd5FsrdugKB0GXPE+k11VLYA0dLbwBS1vn74/vDw8BuaCqftreFx2nlB+LNofS2+lTgLaVF3aCzplg/xp81TfpgDJCmC3pLMZHC0BBdyRSvt/fhnoOxKJDcCSJZ8R8YOaAUAPAK+CsR2gGIH5DKQra6fPzvu/GYjjPOoJpBoaKajR45m5JSNCUSnxWJZY2We9SU7gOeB7rE4uwIimX/8Gnb6Yr/CPVge0xnn6vDuvIAFI/sldHPFLzPgrJ2duK93RCpZYnmR96TJruSgc370HnOicoTvRErLTjbmeVzyr/S45P+Sf9aes55o9Ts7Zpf8s2ksOtJRQgVln7A+2tntg01uAI4/57+xSI9+EECJwYZgUSrgV2y8rkNR/KpPhRN0uI+sfaTN4R4dOaetYUyHCI07NHpFAIBFW2gjDEeePHXNI0LE5FPEBRGWeBuDDvwuzg9AuNGDHTFcJz89lTAcRszGn00OyXyezrF19LR+tPPP7zys4ZTOSF3uONrFu8MWMfJomWmBsZbZrxjjmc738WdyiputuUfgzAGbCqustfBoaiA05e5ZY3ZF62s5PFX6yGkHBUpr9N0io5ErsvtQQiNm7/GxDcwr0tHOzj53mRozGWfO2CaANIEI2WCFXQEqyF6VJgTeykq/w6S1UmoAXGSPKvEsvc61kzws7mUKT3Na1MrkVTkHQkkXpEgUy76MgaJjO0fQJ0laq6NjJH0GAC45LEecaIJ5rvyuKl2qen5Fr/+fvTPbuWTJrfP5q9//STzANjzPFiBYEGwJhgS5dWfJGq3BllqW+sKqMjLIRX5kMHLv6kmnT+e5OPXvvTMjIxgkY60gg/mOuYzXSG3bg2p6oM+3VL8poPzn0pGmL2V8/oxOvkb9b6kz4U8xCJUOP+lTGW+LmOqeieBWe5k3At7xLGxH9jPp1mP/PbKsdei97x/7fx2JPq0ZzBoqvrwYz2P/79j7nR967D9t+ePv/Y//rc12vCMnz3HwwHrFnkovM5Mvh9hjhcg0NAHjuzS0q6VayQ2kIqIqZg2RtuUzPVcvU99syu0eEYx0bB38r3LZQu1bxbUD4dtIYE3XKdGfIQImx9llwBQ4XiMFtwWfRC9T3ux6T/nym6ONjXAm414lzP3g+9wvvk/pLEfrWiXHEbUKQquzO3p+pi0uWVyKs6I6Xtb64jgruuHIU+/yeVH6OmAyUNHCzpjfqTypK/e8+v0onugndg92IS9BXQdRPts5Hytk4LouMO3zeQe2qFOvwHqApyGlq4MArl8V+J5y6LOX01mdTtjKYtnku/Wlk6EevRoITQfJxwV8ePYdEauX1ygLFyk+v8/fdN22+I1jfCX7O0XdUxHv5nxqadKJr9ONc/8K0Xkxv8WP3sz9u2aLjPDjLZXU1MvmM0078TG/P0eRlk74Q7qcH/uvMutEv+pgBfNnPzZP9dbW7Xm1x/4f+z+daXzsf1qDtQ5/9fr/93/n/3zZz9lk5CFDvOeoSTx8PH+SoFguJIDwSEBy5dH7gwR8stKaQHmFRCIAWeTYSU9EOhJgr15pFxxSEwNkmpnGZzwonxmEZIh6rSv9ewKCifzw9yQlDuxBkK4268tG69mNTooWaehkx0Hwimqx0APO+fSX1Qb5UHQGkTDJ3PTEx4ztS0XqNlLqMp/S4axoQ82ij7YRAl0lYb30tS30mW9oL+Qs8MdAQp7nj1Q+IzuOFPy80bqXE/Mu6vkpX5cLg5PAnufpen29HNO4j8jPTGP6wmx6i/NVL8ezp0TxljsQIbvibjdn7OWjXR9PO/ewjqZN6ZNCREU805gyusSXg7q4U1Uw4NMe/TuEcl0ThSkOoMqfNY1/3W5KvfwQS1XH9e1+LiK1zTqSSWc4l9P8057Z2tb3Q4Su+IN2uJ/P02sJ/D3RkU4ROg0F0yZH6e/Q9mnB1X3dTVCfI+qHdL+orrY2amSfTScbATvJt2sG/UPV/4MOTc957D+S9Iu/1Zk7PzsR+6KILvJQ3mP/dR2/8/Nh34/9B8Lp68TJ787r7XubWr+I6//HP/gdj/QwhQkeVsC97vzXalzd+ddoTp6xmRYJOwOUS3QHwXGPUq+waBnGzWkjCVGeg97qomvX1R+K5CBihHysIA8iBZGmJyJj7+vI8tDW3nSGyECklh/rqxUFqGdwKJseDasySfAluaUMfeHkWFw8KSuLykWk7K1zQjUKJGCs8wxWnIARHTNX9euc6tbbFU2xTptM8+W2Oi9mb7yvpa+vYgifVjE3lb52mX/55ptPn66qcH+7ZL6kp0XL3+6j1M2V6vatP/fTaIGjOy0oS3LX/1YJMqRa+W0d5Jukh7McfVdSegQPOzllWfId2XnfeZ8d9x3Q5ALaS8uegb1yo2rL8857Baj0UT0dMD1bCu7V+NsMl+IGZ5D7CtQ24nK761zbYn9PfV99lo4JBAI8Uy/U+ivyh2WodGjSLel6r97G2TyRQ+sbSG2azjEHnX2P/iDqOc/GQKbbPaG7oOnst6I7I1jxh57sMuagEbvH/n2DYKjS+Nj/OdpFHX/sP9fas1977P/k0+d17aez/n/8wyvSg1U7AWFdpAkWpOCCuTx308HI9dmu34mSXWvue103pGlF1wTgsCBFhMqrdvlj9tLOIC17e0lINI5OXtTLU//qu3F2IqWxJXGc0tHSVCayoPNGcfA/5CD5DW1K7tA0kZ3SZwHaUpjB08w4LzjPU9LWMOmab32V0bo8f2PysAFM7xXSvfZvptJEZTlPSVtV3y5YckU1Ql2v9C4rfX1FOvSi06u/Fx+ynW87a7VmPkDH9d1nC6Vd4aCfy3M/Ym1CNf6SUpV/F5BysU073aEqEwCA8UyRA/qIstvdKnsJfMH8a2QP4LkvrH23sADCYdd6uh/urkVAkqxP0YjiO7qXHlKjeMndrt1IJDb514hN70vvr+xhPRd2OwLiUJd8f9exvaGz5avt9yQTZd5blG4QZ341Cuj2DlOtDdz3Gemrlf1eZFce897uadWvTKuuNrGfg3hrmFP5dEQe9D6pU+qbNj1sM2CORax+PPZ/1INZa2oGdL/msX+zinGz4JUpv2UYeyOP/VeZSIyZgvuLaf+L9KSjD7HkQXSmgLXDmIoeKJGopzBxARHwz+/yoHw5K4R0LjqOeJaXM45OK+LCbCSfy/2ZWtD6ORerbKVIgsPhQOZKKbv6zpS7AOckA8rBI1noL1bVGJzk6dmZ+lYTExhRCqLp+jwVlFDkxUo4x4VGBGAHlZAkybkm38Zs+qD7NtIXnMPfOdSiZSZ/83RB1nrK3zV/YjMt3bCO2yNHGsCVprZkfeW3zaWvE9HaTSvNZ4VAvlilZ1WA83Gu9LgiM8///jad+7n6aOytIjR8cZ3p0Zxx5/trdi1z9uujpl2sng6Xz7lxqg1M4kTXuLv+7rqYHszUN1KJ0IBdk2T6tDP3aq3tv8snlRoTeM8Rx3DXNn+r16U8p/tlGgK2OfY9ilGB+Q7As6870KeMpxmeiCn71udyX4x7f2Y9ekcndE20oGOdmPRRpw+RljK2w5maO8JMs70DwndjS59e9XvTx9U/lBK/ie6d9OkdkPrYP1wxifZNmuBj//u6kmD8sf/H/l959/r7uCYd7O/jH/1uRnqEWTtQJnjcgH4AY1Ts8oU+20sTz/sFqAVIBY5pDPvLPqNvJW3MINpUKlvg7XSmRECF755hdCF+vxrCe3xWL0FeIndbWNQJ4vWySENxmebHMaz+tXf38Pd+Bui6fouOWCMlTZApb0wh5PzpvEd8NxBOphDWuUc6Gzqs80ApV989dhVgVHBdy+hSi/b1fuVjDNTb+5e8cp0z05jHqCBn1yzdiAXJJmcFfEh0PFBSAL9XzOO5H3PO/k2EBxPOjMWh71a5t+3bSN4ib6u9lhvjDMdAl878YEFuzzmB/Ttw7iKP9zqNkQNvYNrZdjMqPZGu9FS0aaeOAIttEVRr8cz5rtGG9fBjFOA+pB5kqZ/98PmVPHofNkA6gOo7ucdvsiPumg66RR8yzsOwIBQZov1ZdV8Tkbjvhe7r5+my7itrBGN+N9A0dunKqSv8/q67RxkNZxGkZ75U2MeD3hHwFT14Iw3xDBa5lu5O5rH/9I0pi8f+J3+x68pj/8VnPPZPd2fwuEXz6Tv7WqdUj7LJMvjKn8T6v0iPnLHAHv/Nv+vBbi8c7GlpBHwAWSxv7A4/gbP9RWArMUWHYuc/RXH9NhVeqN/Z9eWMT49yEG23fgTQxws3S181Lmd1V0rglu7lM7766+W+BfKzgEKN6EyFD67nXsTncsUqusxFfbWvQ+t9jB51WnJY+NjMtMgcaYfSg+xnnZ8gsRq3r+Zx5ggdy2ckcZWTyFQ96UoSZvZtq/xW5l7RDB/TIn39fNgVylGlN+mQUwKPEuUd2vn3qN/h3I8M2d6N0zX4HRixg493vulRGiNsqec6x7O+8UgPLDHSxzrwqs8+L2SnKFEHi+bsKskYpbJuzOe9wMXFV2zttQh0EA8fHD/z/EJ6LUjBO9Idtma6tpX3TaB5dPJ+YZCiISVPbU0yoe1z7qYxH2Xa32T+Ii1w0s/ej1Fe7+56vwD27+hG9HG4uMuT8yITniKCWcTnHQuFLgwAqOvsZs9fCRJIrsciFTd2eI74PvZP8NVVabJx+pDuGx77z0p5PRpIi7rLQOh+96UlPvYfa+VpHf5Ft/+Pf7xIjwM+LcjQNEZ/ykLXKrWJYmgx131S0qlK13KxfoPe55lnVuxp1/9rlEZAr38PgB4Pr8UC2E6AV+8gf6ukwKHjUInNjpOklbHvRQ7uDass877Vhood6FpEPey+3Lm/ZMVCCAZkDb4HDo9oW8qxQ/QghULCJH8qEuFoIIoI+Fa/dTlXd419L/Vt41z3t4JoObckYjWCpGvKnDio7gUedKey5cqLdi95XOTwauh7H998/L+r71eq2PeuH0zXVuaYCiAM537WUD5WO5+uf1exABFKlYldD3rrVaDQnB120tg2RAnPbkZkBOjK9PvsejLl/kvX+bQAwBXwTI5xAo93NI/XuwbDIZ9Tqr4K5GI3abpvEiP7MoEW+44bJ5mLHkDmsLjqzi4r2t7xLMUB/Hew1Z/Rx/AOoXoFJuyZEwjWTnjbEYe+bWPHAE46UftzJsPqVz3B+HpXcZs3deRwduWk16NdnKI3Y7QMGwJ389023M4bB4SB2fbpHULbvD/2X3xSSvOx/8f+aVvCgUOZ+Mf+d0G1dfnbtP5//BOP9Cxg6msNCxTkeZs9KqG0NwPlCS8CoIrQxG94/00B2ADPIjlwxkpjErhn4QPDJ9bxOOje2x4iOZwlkQWC+Ot3OzOSym6jdBBP8I/GEshbilE5g+OCSQCP4g467I2XePLZ1ke708gDnLKXWU75+LO9j7pnjanNUyUTVlUun5QFBzTuOhc58CQgPhc6N9Hf0xPzy7br34qNTYUjpjNM1Df72+R+UZnUzfxuXfH54xsVQvhYaWKa5zz3Y+++sUjRomWrlNxnI0rrX69/LUIaqZ7KaMTen5TsjiV0RKLtZ5/5VZgO55J8qJblVmI5Pi8NbJaULv/tCO5epDd1zH8C5tKQd3bzaupS6pZA/LaT2jpRuGGg3OqPB65SNgrsGXxSTUnqUyTXVw6QexMTwE8vWQ/1hpwOUZcuXwKSaS7UXhkJLnw176fnUayn527zVKIYqGD04oxJnTlz4qczWpq1pWeXoXgEu1TT6ymJ/oDUm/38y0GNtq5NekF9TD2BXvtN0cdBOSd9tRZAOg8X0eb687tN9QHt839vR4/958bx0n+c5es2022Tkj3N92P/j/1XC3zsn5t74f+1ft6t///0d/8i4iIBHkFSZgLkERjtci/e4SBc3lW4y611YT5/kj1HoN08P6Mg65tyyN8a2w/x79/ZdepL3Q+cALM9i5TNnjWemwkQ7U4NCKZHOFYbkA8XqhB4J2MaJdLnrJqe968VRFB07Prdihd4OWYSM71oVeSN87DmJknOmiP0QUaWehGBjY3M8RqRxx59Ku1gjuN76YqIElLMTmSnn9UyUjanG1obHsFxi7EiCjx3pZ2cS5if/GWf3qZphhVDuKI5H9+LZMF1zsbLZWdlJC8G57q7JO1F4uI8jkzBz4cV4B5eriuokdNP60FWunvNY26QpwWHTNtL+QZSMy3Oq0mBTay8HUwfq7n5PXGgGjtArrJj4YJ12xHxVWLeweX0mbpMwB9ju0nxOgPPXIZOIPLgezeKOoEaJmvqd+1jeAFCs9ZGBu4Aq/0GRakraWrcRkiqzNODHxqQL5nyskGC1E6MT7/dREH4xIm8xWI4kJzQ2xPhKpM9y+k0p+xX15lJP+7mibbRrwvZ39i2+hIvKb1JuaPsOR/x92P/L4urPPa/+4FlJ4/9h4/ezqy+ubH42H/XrR9z/f9nv/sX62Uo+646CYMesl/XIyGdMMh5B+jwXeksC51lUvXelxOAtjZ65TX7Nne6lZ5k1tbJixGImkIVu7SNAC04DIaifnFMU7lqZUw4nI4ZS2JkRGZ6JxEjSZbGBmDlfyap8HHrTINj40oiMGdIWUuS6bJo568yIpRV9uTEGBEszwKJk4yKDCR7kji/cEt/bC+UtcvO73ya0icrUapEOHXM09MunYgtV9P36wWfq3CA02iRY0vG+/TNFSG6KFDQd0fqS58+o1Kcb/1FOwPADscWpWRNGW0xVWkEROJWZt4lD1cK1xV7VCk352RY1iN7Sb16tevfXU48EpEoN0KPitXUtclplzYLaEck4CCnAH3QN5GzvrCE32nRhrrNMVcoIwAMIoXB0B8Usnq4hv3u/aoyHsB2R9F9UuQbDmSWLa75bsj9HIkr9DaeOoH6ZT5MyxqIBQF2IZ9lfubB3YlgAu4a5km/1deRON1E3KTrWjdeE69cpCnNbU7ahkDoyF1EbEqteex/U6Cqd92+Hvt/7L+qzMnXhM9fdndT9AIKZwGBmovxNf7qLkuiR3Of9f8+g2LJ/Z9fkR4HZWuRQDSmp8z01KiEUTVSwwkN0qDF0H+sYNk+8TsCivLbRkTyPQidIES0CKTuGipLUAeQ9x4kGL4hHN5X9jGiCG478bLSLtMS7TIYdI6m2S8c13qNDKIkQQLXc924JGM7KhQv41xydH9v7dqFRq4y1c70wK41gsX0M8C7nrrGc17lHhMKzz9FlTcCRJE2Rmvwt/XfiU8D+/o4Rej281qiKZTvl28+O1lZMrlKWse5HyuLrf/MhxlJWmWvP/ne+RKaSkrbO3IiFdMjF+sg/QKH2Ar3hlep6XXOyNuPy6xogr04VRXnPPdu2IOkw+4g803sPIIGaZj0QwSD+HlqP7574+GvFpsOMMexEgjenZsAUeDYZsjNb+sidjesLn+20hc+68NOeOSjmKpFnxhpX2/s5uu+rwH68aw20Im0hCP3H9V3uacjAdkiQwPxw3xlO7O8OrmgvF69iHQEEpi4Pt/rcysOIRuZ5gwetFRze613doU9v477ZPO5Kuznnt59XpWd98Ef2InbY/9Vqo/91w2Wx/6rHd4RmpN9PvafkvlR1v+Pf/E//zJgeKQFCGhj1y4hX08pS7c37cRf3SsRjgDx2mHPPO2IPgBgLtiIjWutEraYOmj1FbWmOlmPmepG8C5Hfl0l8G/f+X2Qpnb46fzL8/3ajArpYH6NKG3369lTOlYhgS1ND4t/jEMEJs6V2DjKCz39fApJlHY7g6SReIgk+UOWrAD4bQoyUpIAP2HwIlNY5a90stSlLGDw2QlCJVg7me4pkjl/SLn0iNAU6etpctYXRZBSZ9aUrrRBre4Znkoibw5MgKfGN65f/nZFhPI/o9x6r1VAmIiYiOV4qtoShnpgsrha+PTpe998vs4UHd4B051lB9cdqFRQcwabr0BSX+DpMwS++KztuwExTcA6tQuGMLEhAcTDSpuPqyRGfbwDzpKFm4NpkYD+TTrQJMNY+CbEeCP0kPeB7Mif9zTB+F7iawD2NO4TYDFfmKERu85lqr7JRzZriNt8k4WEoJxbYZt3UZjDuZ0yX+3+Yh83URW1ofnSuBnwHNM821oSQ+n9uEkFohpQTU5/T9fTbroN5efH/h/7j5WpnG587N/lgswEnjOcUvnWHY/90+UB/7XTsz+j9f/jXwbpsdkpJAHAm2CF6VUajYFmRGvcoZfqZsAoUgZC4ADj08s8hfiiwECaoOFuAdZ093LkEdFoURdd2VPUtIkWgFqsS+NbG/oO5t0t7OlZJoAE2bmcbyl3ihwBxUTfvZN8Uef11Zwax0hdArkgk7A+G1s6N2sTsvPuhgxAgHRfRP46Od7avXQjUw05L9I3ad8ltDgL5RXXxntLtIvJSsNzNhk6QdpS6JQ6KaLmZbHXfBvJSDN1AV2s/JMjNo/QKHpmJa1tcs02xFJS0AR9dg3QYUGAiQqXldLYiHCajUWNhnrkJ5xQALgT2JPu4xl9J3xJpXWb/oIgmhLMJk9Aq34P9axO9HB2o1+v502Ld/e3g//dpDxdk99l32HWQYy4FlYAmnYrWXWAOkx3fLWB2Rb1YvrfO2N871l7SmIfcydaU7uT7KhHVXcqUX0lo67nfYc1+huR9E7iTpLYCbOuvJMv5bFfNxdU6GT01D71/rH/fd4e+891/0Rk7uye+m02+dj//VqwS/Oxf8rkZ7z+/6vf+8vlI60qVC0dzAP02g2PRYgllnVWI7yy/bFSdXy3vO5uE3RWhVidEapeYNoN1ElPRqNwFqgVHlD6mkTJSI3131R0P8dUDwWXtKjQ6tzBVFRiS+EjifGVWpEUkpfp+Tb8Gg257lliiJ1kE0qSxHruRjIiGVxkAiEe4PG9iET0v0KBvTBDRlesf94PAWB4gtWXlg4nXerj9dGV1L7ru+jz1M7wglcbbhJ56Xk811UvSTyPjiexXAWoVSwiiO/HOtNzMcU8C1R1OdLVVr2BK23NXyzqHYgy7YgS2MS2EPiyJViQbEIqUYjJeaeWjrmDPsmkLIKcP2TkvVooXewRoe0pPuOzNBcvnvMKqL8Dro/Pb42Pz4riGkl/Y9ah/9OOP4H5HWA9y9f9qjvmHlmi9tFy4Trikt4XXlOtvu5UVg1Pf6nvX41rA+Qg4xx30dWDTpSIi3dgaT/y7fcIVI6gAA9/4Cj7gfhvcsDzu36VuUAUcLKL3m62tdt1zGGz/+rxMF5s2Tz2P9jvY/9afiJ5ctnDjdM9bR5M9tE1mO/W7o947L/b7WP/L1TRdPfOj8tH/+vfs/S2C1IRFOsB9m+tI9QjBJqeen8Sn7ge81bbdEBbwH6ddEtly7KQXDwJhld/fRf8GP0RgAX54Xjr2K0fGyn0QRUwDpdhwM+LKSiEgkiY2gyw0IoQqA81nUt9UcofInNeAS8LRNi1q38CyS7/Ii+lDqJAWPzunSuf47p63ijOBWEBrveZlvG7kXSSzEX/7b038sRj0QKXd57XqimGGe1r/WjvXxqJ2TrrY+dzrjM1i+isszlWWOB60vq/E/Pi8NeAjSDZ2NvmAiMses4iOL7WwGYM0Pm5HxlwWZNqlED94A5x/670dVjftkWNof1+M8CpjXr/L4l6/pbXvbfjQ/vcFlLorBZsVjqDWW6dGxfaN0DqDiCzVyQXgzje+uqtNrzz9yBkXjjHeRrnr46LetUJi+b/NN+F/BQpnEn7JKxoZzrQ3/zXSfdP8g2y0hbSE0CLBVdzMZC1CT+u79o9sS7cpfJt7ef5UMrqsf/qiybCJ5/SdTqI9ZvFJEzuj/0/9p9ccVpXaJ+P/dMzc2U4f0+fPa0lIXOkTUdr/+aK9ER6EkhFIyhpzFmKmVXftMOWLxnNaV0Ex9tb5ErQV2lsfih80asYjUV56mkWkSO2bX93crDAPtzPdFA/Iyc1LY87/1x89By1azB4jxjp1ZQjgBZZwnuIVl8ZzWkH+uWQr2soP91n6V/ptiXCc2RH8Fuyy1Q8IyatYlsnTyZxn8VDxAyRPvXMNqj5bKWP4bsgYSZlXb8KqTlpNL3AvPczUdKHY+lqpsPtUccyHyx8AcJ3ncyxO6+y1tfRG6Nb2r36xl6qE4oaKQBAgRa9yU2FAI5C6Wu+M5LU37AcjU9ISpExALbN+Q5A685Bn8CC2QWARUfRaLQ7q+lANmHDBKYrrODysUfIJkCfluI6dAD43DGnLznlblM+XY5lTOP5lR41CdqLGKf92UkcfdREAqPv6JT8dRYs9JSq1dH8O2+pLTOaMsq46ORMlurMvf40LXSn+WVr56iGrG8m6KUN6sga232VwahE6p2OvSDt3TS75LNejYlyCNsbbsKSUHfutTY+9u8r2TT/tTLXY/+P/ef6/Nh/99Y/T+v/x7910nMNYgLJFbDmUC3y4eCxlVYOkO4LxXj+ZLqnRXqT3hM8AAAgAElEQVRUqYuLel+IDP3WqEP0GXBhOwDvs6TdOQFywtQA1iBVer7wbDmThPCaIlO2KDWSAUCaz3Pw6wtqvy9JkdL6zAR7NEuA3X7za5gOBgQwz3c9ExM7XY3czSlp81mjUv6apCfO1JhU9wiOLTUck4hQEh+QrxtZRwqiCJxPZCFQnGc/g1RSHNc9CZRMBn4O6ON6UelVuADEHO/eWTMRmwt5Jseef0WCoNmrqMPFp6xqW+wgt0p/vKcDobCZgfTknL4B9nA/7WrjNUPFtAIUBmJx6rOek8/oUN5GdzeO6dkCgUw9k5xeAU36oE7EOj1ZvxfQ7/292flnf9U+fQOfEZsBw+5z6sr5mdC08mfvcu3TTlzurtf4i1ydsE0yP4/11FvaWSW7vKO3W3QY/Zn0meefss1dF8+kaleDKXLqrqHOxYuCCtM8yzuV8T/2D69tdvnY/2xTj/2nXLp9Pfaf68nJz2yFfF5kfvydrf//7vevMz3m8idyInDBqIgO6Ou+PNSvxd6As5YHpRbZgiMgyyFf7zuBwrW/eWbmqvJl0RUtKEk7SFgYNVrfXxvvzlSsvrrv8K8QQl1ABY60QNrnes4m0/MySmOpZTlKOZEgSIX8oFR1A9tlLrwRBQi4iOtvEZAgcC6/63MQDrUzRHH4vB5d0VQY8VBEqEVaCMb1bF1f96mjGlohaz6Q1I2ZKNo0OMHFGO126tVezIBgR1GxiGi18tp3Z4+ChJWIlYI60gpTKNOx6/9ZwW1F61b9aSuDrV7nNV6U3Opir3Q2W6k9BQbj3uv/z+8NqJZmDUxAfXJC1r++I37e5SZQFsB4F6BR13g4ltGNAmJDp5lQcgLAOYY1/qlAwyGVKLUrbWCSYVkMVt8y5ZEV1AJcRNQmfcEGvjVXBxDM+YHbjD/5rKijARslwZr+7m1yfieieNah3b9XIjf13r5bROETIlHwLVNJaM7DK90/PxXPLgOtlDTk2xoqduC/TS/bPd1f3nek+/EMySR9iI16Os/zSga9rzupf+w/ZIQJ6z70sf/EBdPGEP179593dvjYf67ZIUP3iY/958ZS93N1fYIP+/d/8ANugseuepbh1eLtAA5GT5JkTeZj+nkNW7wyVcfA3wmc2gq/AX0AXu63BSCXRiBtTsUG4t8GwqeIhb6jq7dnJPJY8DbAejXZWmktQbquF4gKmXAxO5EF3KzJjSgEDlfnb4lseb2dd3Ki184RxRhBYvIcjo032hr6WZ6zxiQwd0iXc1Rs7UqYGbmRnMazP2Mxg9o/ifXuxaUiqiTjfZx9no5nivw9P1bYIIscXC8qtRftuE5fZ4JWFMfJzLrPHZuHCRQBWq9GLchu2Gl2Qb0CgNQ7f0wQ/h3AVqBzBGewp4lIGWr1Uuc3pILXncB0B+XGK7J4Ro988W30vWrZ0k6RpuE8CIlaBfodAFany9mp8DgXrk4club7l0z3o8w3+bc0N3NNjdi5Aaj91R8KtxG/2zmO/lVf1/vYwUxvs4/9RwVC7MUUaZlA1CT3TaeQRltJ2flFhJi+8tjTbrFcudJ1J30/tclIc9GvKernvekANNRA+n9I78wtyjFwGWOdwEaZ98f+Q1aTj3zsv5KlOz802vWLqCh9zGmNCt/w2D+8hlDRd8v+P/7D7/+AWDxALRWlgH335FtaFaq/mRP0/xsyiRSvqHhmSBJX1pd0rrz15SwrCFa/ppdQxjt84IXL4BApKIsmCA2jUvOZGClCpl1xMdWwFoHwQ+trzHo/TbxzKE17fE4AMvujR8hW+xqnolYCOT1N0K9TG/lOHgDqLxltE4iwMdSoSxI979f6xyIS6g66hReS6uWn9QxQ3tMidr4S2O8sStAq1bnA1deut5HihwmfSEtNb/RIJd4P1eWQGr5HSP82VjE9NPvv9RDMJvxdO5cjztpsLge3mztA1DFsXxA68Jh2+mWASRgqVCegmRacivLSX3ab4HPYL4lqIyza4X/zXUScd8qlyABtdvvv4PedsYZstI/grIIei9HXTldfgV3KPscxz8/tmAFqC5kbUn1WH7vQsAzegY6Yc88d0PnOPs6uU5s+ILpYAAsiY1xfeqU8+q99i8B5Xz8bNRDAkgpVwNDQqvx12LJ7ribLJGiIAspvMx3EhVTtMj3rO3b52P8O1iaC3PXvZJfd1z32bxLZ7O2x/zzx/Nh/mk0jlIGhGmkt68hPY/3/j3/wA8MdDrL62YcOJgT+zO2n8+dBcw7GAHamj6XbroA6ga0b0pYGx118E4va6mdm1m+IZmSL7s7iHTsDcThEoAQEOug32TH1DePyFUvAbskOtFJkskSMWvqW5L8CASjLHN8X4thlKpnZuJV8VcfikSh6cIxJymF9h/6WqI/9kjrUrm0yMh3SrBSbGM8oKVWs6+IpghP6pzNNceYr9WY/q5P96elt1tdMHCF5zGdVPZBtaKyhqzET0NGL8nio54unvd2BcAKe5lO99bI8x4ew1sHJXBdZlIEjmnd4CBzuwFc4r1cRntbdaUzqVd19/7odwt6fUXaHqM9pV/40xq1t2Usr0TxdN5a9axG1PsN3/dC1MbvT4jOMe9ai87fHeRsXtVw7at9PNIVXzX2gf9qiG61zXdO7vfUoDW8/RXCooycprXaUmo1qnqeozZ19d1vo/rH34bH/Oa2Xcuq+TZsAj/2/9gaP/d9HRMI/PPZ/q0w/7fX/4z+J9LhHNIeej3W8Z2kw/q6VzFVHBOcAirWBloEywSpUaisRBSMzqzvucQgY5ZTiDJBC5/7DToBAlrzhQuywUlZgqghHPXuzFBeAPxcaG49S9rhE68B7pgvYErdAO1Y7O38jglJBuAxGAJzRn/gNUZmSzre9hLOSkjzjkvAoCEVs41TQf/VU462pfjYG/U4gYt/11EW7Qtfb39ZGVMEDVQx9PFSPq7qSbUvemc5mTzKyvr/DKPoaer1XmouxtGhYjofRKzOw1AWbf8qHoGXabdTYexpbB1vTwh1tH8BfJxVll1taWdJU9upG/Z7QpsGLadyvdlXz1tywmEhBbS+rUPbDld3bnhxsMczNRe/AvO8gc/czrao2RB9xksO5fyxw4b5iSM3w42Alrc3aPJGOuY9yTeyzzXeVRR/TpLMTaa4ywpmx27NMr4FYtynKs4zFL4x5OOhsnSe3adwrQnR9NZKSofpef9Rpzh/716Kzz/tj/0PExZ360lko7mP/ia26J3/sPwMUPYJe09R/ftf/j1/6QzvTcw2wpln5UWKvj9qBviIp07mN/O4c4Vn2SNDfq4O5X9sjT3uJ5ACZzbDjGSBkNVUvl0RFKViwIIA4iIneR0MQwEgEj6YbyCXpIuBPhEKAHWNZXcuUMH2vMQW43wosCADpLFF/LwyBdqZxVeLTzuBspZ934rL3R5EMJ3Go1EagbzqXxPLqPSvj5bUqPpHfTBG+sVLgKdK0iLyeb3KjDYxpcI30xrilr+1ZtY0KkurYh/dkvQB8CR73VJnVHaTsrFE6mqL81+yMKAugmFGAdq3NbgXQIj8EIhsYvok2aFFemuo3dlJQQU4OYRsbUG+kW2GnrYPiOxjNtssYm3wmorUBW4yfz5zGCfdTYnE7Uar6RazT56L8pv6nuIey2IAH0KM70roRQabZ+aBv72/Xv3rzezm3xTH5QzY5QignokH9qNiRrSFjYHjZapIVn5+e/tPOvIStPPb/2P9mAxWwV3/12H9Y5WP/z/pP46CP/aU/+EGcba/RGO3sewWpAhATSOvcyqn8MaMAfQERqFGBKlvRPW2tlVkm4BAZsSpsCZoF7BKIOjhnBGSLSGUGftABLob+d7TZfiNx0+JmaWQO9mMvn+AfxCSAqQhI3jdF1oz0mCdcUQP1zz+X9yAVspfOMubBZRypiRhrUiMvyRzPzHZUjMKIXZ5F4Vyt6FWk9li40N5xg3H4B6aEJQHJ/dI17xEJamTSwXfOk0hfav7pvUXC/TtBrVE90zvpW6az5YaBjetIlhohWtGsL1nQ2/qxn1magKGIAWN2HSwRyMVvww50/20H/rmY5mzkVbw/KNBAau4IRfNPPpvzHWeAet9P+p/jOFrjCVjf7EuvCtfS2noflIrLXbUpyjaPOSNaAvxThKmC9UPK4piCuFfpozxO8xAuElENBIxj13kjISTp3vgdKWqudZwg9kWMsfe7zzHtaCyA8WYa6KZvhzFhSSljOH0vfzW942rp9SF3njp/5xse+6++jfbYZbPZwGP/aZaP/RvO0Zr72P9bEODO71GeX+undP3HL//RX33pZ1J40H2tUATYsQrZCzGV3SBAev083b/KRcPrJjjt5z8sBcie6++kUXUv5PUozUvPjQXGGyYQC0C+Vn9EivyiTBljX25S90oaGavMKTKDCJdYiRO0ICmYsTUG/RDAXofb9ypxJHpmUcOYSkTChGLPsXYJtedCChnNYf80b2pv/auqfFGFDf0ZCjfkeSvp1pyCWJ6FiFlNA2znqVyOEbkDUYKIz8QEOqHnbyRmqhwnEhekrBa6IABK3Z+IWaKWOf0NO+5vuRC7iI7kDmQlyEzyve2e+XO3Bb/1pz9nXc+FcADaMUd9xxwPW+0K2CHFijZ/B/R7ClKPdJ3G9c73Yx+kU4dd24kIxYyhCIztCdUXnKWMq16UvvaUqi7LUwTmjX5PMqHdvorkaJ6iHezIxSK1rXQ72aPqUT/udL/3nXO3lWnHC1s5X3zuVAb9uEhDtl0Gp37Zsx77f+w/Leyx/5K5l2tdO6PYLefV5t1j/9/d9f/jl//wB6vSwOksTBIQB09BSPphdN/R90XLdpvmdkv6WwBlU8sA9CQBWHAKoWrvxTEA7sDAYV7ZvccKtZ7T0rYUkVhxGpzzUFqF/Z5LUoLXRGGKesRi6xeViJT3Mc+3VCcWKXgoXLAXS7B+RCRNBFGAFItq3Bt98d0HVCdTfzOSkX1S6pvci+ZgIkt7utqQtuV9tDNNjID5Tr2nVK7nrGhIsmsRbT0n+l1KREg3KyES71ZRiCW/KLLB8zfWwRxfptZ10tqLHqQcEY0LsFLP8dRUy3x+pvflGajoT9PvAsCGdJhYFF8A2J6KZF2u70aJZ52eg7SdvqusezsI7kAzY6SEk/Vv9vUOuGqh490CTLGD3xrou+T28/xeIt46gX/OTV9kOzDfZZvg9vScKrsawTn2px2iPe3+n6U//5LeArDcO6HoQ0SjJrI77Iau2xPV5Y7pGJV6s1+FLO/568WeOhk8pJvdyaqT+OnaPo9FX/szH/uPSJZ8LX0KZfnY/6Btj/3HvhELbPVzsnA7S4gnf/3KTz72X4Ma34b1/+M//9FffSkljDGLAer5XQGqpg7iQUxh0JvqsxTvXOVMW9DrWb4C6++MfuzneKJv/vBCRrTbr9BiA/caTkQ7XPtLRCgiIw68Y6/c7h4jNgKWeL6u5fgMUCI9rZMvRGVcwhmd8UWvk9SdFDENLicwSZKPw3/ieKpshygMx88zRXzJZ6s0d7U5EoReLU/vrCkRohaBK+9IMqHn3NUolhwWo3mFLEZBifldQmX+WDwB0TnayUZ6I+JmV9n/9XLdSrTUxyUrzUuJAop81cjgLei6A4gAUBV45bucNP5T9aJ+nmcE0cPh7VOfCZ51zRQt6KB+AvnbmJoN9z7Y9U70GpJyHo4tDxF0e3HmOgvonQ+64n9QdhM5eDW+GJu/B2oqZJG6ZaPqL0OVe9V1Eb0jsPcHiaScC2b42JkqiQmY5kKCq3Oyn8Viv2QwLJbAKF3MXxCDmZyG4Q2CrqDkPooyAaG7uduu5zmugXy9aquns93ZvX4b56L8OMylquB4h/pGQH3uTs77HK/NQo/cvurzY/+5Lm82+tj/XE7/sf+jWT32rzUm/dQiPR3kRuWvHj3giuk7Bjz7YXEQHEpvVc4UhTntcHPmBJLjjA9+jDLYY5EArtxK6UrAaUMSUM4zIhHNEQHxZuTw665/umYEpGoUyG9cV5Zolh/UD4KnvqR6ihzEU6C5PbqSZCWrxyXQtxauyIamPEDD9e5MgMAyNw10q/+6RvGuBPjmnhm54uI1HuZXGkubw7hvRdp8AYi5oKxAIKJgRE6a3frxzWdtFfN5sfWc5CIjVHpmS5sDCXZYGRSIkRmCz06AbF4FMrLQQyGwESlNnU2ShncdmcDD3qZITQddxDq0tR6FaGZuPb45VK22ejtdf6cdWR+GZ9Heg85Tak+ALNhJJ1/M75vGw8XhbqE4yaGTCY2rvDC0yTCec7syVdmf5vQdMDmB6tCJmyhGjFnm9SJNr8si+ny435yHZ+69iOLsotp1Rj53eYA2aPqljWBBiNN5GfMo+d9p2jivE9jP+waSBj/VyXKfP41TGY+vdqsf+7cJfOx/9xa3fg3HGBY2fOxfLqv4A9rXstUD2X/sv+mQC+T1ps6MD756/f8VJz1ynEwv66V8RYamh5AUBPnxXXyCOut2HtrXItQjFQLRKgjgUNR3yR2QYieKi5kW3R4pSjLHSI2lsokIiQjM7eVyvvUbfalTOke4CAivvyVTpnJdT1NJ5RIlwXanIjfsL//eSBIQSaQuMpVrSHmThav6VY/YkERa8zaaKfqU8+ozKiDUCXKJ5lwy9AIXveKa3rmEA+PrRbBMm1sLnUdXnAQZYEgSwuhPzO0UzUF/1/y0AyH9HM6a21KUw8cd40jyNhVAqBE9pQrWJLB+n8gBF7KRFL2ZrrOG3A/ouofvwK8/p9uRmUlGkgIQSy/1M6MIsA8u1+GH2rWtqbLC3y3YRV6xsDswxVmTI7kroHkHBwLN0875ea78aZv8h4hUm5Mi2yZDuZCJCEVfsOnF5aaMf0D+66vhe+ueSH9+6meqNvBQZnA+qN8uyY/ox2l+awLG3BLXvCQXOQfTcEfb+HS9lNjyQEcSA12WtHqPjqKNC13GPNcwpaQ+9l9JbDsHIl+1vO1j/9u5UAH7rsiP/fsa3zdJLv167P/vfv3/lT+6qrfliz4DMCiSw1AG05fgiUkA5MrrdznOIB5x/iZz0AQQs2m8IwfoyUhCok8D43vhAaYuqM0AiG0xJIAyPHwB51ReLsT2e/52XSsZln5ti/UePdA5IqW7xTha4YMo6sAoTBtgPzc0nz/Ks0yaawEgTrWRLx+j8oALORpIjUe0TDQCN9YI5zbSKV2QPfpBoFz0aOmfyEuiuHVNizza89R/+0P9Wr997O+JinLlDvb0LqhyzqmdUytRQ0X3pkIHoS9MaTMZXRiIEdM9mtfPG6VdJOERkcgzVCPoysuG3PiEthuw+soUlWjJx32XkrUD74TVGvkEzt8BgwT3nWzUzynTd541QeNJ3ssSYEdfJ4d98aykEgRiBGxIHyuRlLxvAt4Tqcvr6vsZZFfrniGSRTmxXdm12p0JSd8+mqR+/q779LiSspBdQhCnsZzkEnp0Fy3zh590626n/RTh7GuSomX8ns/r/dxl/ti/Vq6v07TdTokRHvs3+Tz2v290hK3ebkCkXZ7WHQEcri/P+p9HPOjTP371j//v9orEGrUxMUdKmavvftheABOERDPk7wCKFLKe4uPX9XK9up4gWH2RGU1neRLgAmyz8ls5J6Hx1cJIAlsLkPvqUNLB1GesHCRC5vSy5wHeS6pZgnEBd5JCjWP1ZYuEOIgXqmOEhS/zBNjuZ1m6nOio2d8+jkhjw+rK81BBoho5XJoBsigZGYnN5SbOHan0t9u8iFMnRdYmy5cr1Y46mfNhl9dy55yvHHtAMrw7KN1OJ9BJ0DLVrxR9UGRJ57l8AkSuSwqlW6UcVxCwIfq00vhcsJLRNT72LxbetpM85SpMoG90tiA0BLEdTMENbI/LWa9P6KBYujmNYwKjetCawZYy1KMNqw8by8v+nH4qoLqRjg5Aex94b8p7JxQwsehQAc7eEPs4AeiwmXaWhDb/o5w9SindL8xl/iaB4ruQjf8R4x1228s8I5pV5AHVYi+73lADb9ShKKr89ia7EwGEzXSbOs3hRNwnewxdgdy6HRWd1QKQbu6Y/vXY/y6Bx/4pk8f+T/ogM1vSeuw/AoPTGt/Xu2nThm6rr6MhYq4dbf3/+NU//qtYDzk5HZyyM9OZHHWO1bUsn50H6q0nAvFb9TQhoNi1zyFf/VlhZu60t2pq1n8bji0Uw+F0bne10s12vx9Kdg3N9+2kqK+mkxTVfTjrZ17LIhFF+UmKkF6nMWgx1eTcnYPKa2q/JqIUKYqISpR594nO+U95xnMyOFeiKev3LnNXvlKJDdGq/myeC8rzXHjJa482ihxcESBHj2vOSJTGwgeojKY2ajVgzKPIswknSJnrd4/MSP8kr352qshRWw4up6VbrNqHpAKmzjHtjQa89E/jUCnxEmmrLzK1x9bCBam954XstDNdCA8dD+b8DDZvdpoL5rDrCBLLzwDH+v5rAW7ItI0hn/N69y2w5CEKIz2JzDW/oZAabBzcpUTBdW4Ebg3hRaRuWjy4uFC+m9zR9u1CpuDxbbSxzGRJqVm/oKLgcf6HzrJffayFVLSUlNqbMym9k8/2vCkds51pintO58DwQMrhFDGj3pqP5c77Y/+bzUlgj/2PaZh944WFXuSLqu343rH/OEVHJ3ve/Mlj/7fz8dg/NjmhgFz/P/7L//q/7T09KETAogQrfcyOxNuJgkyJK2c3AHrpN2yBTzrA3yJYsbSepCOzrRE0qVEBpqGp/bLK1DQsAkT1yR5bSysb+JwX4ONZFUi2kp0seazqUAIUubjlqsc0NQEjpb9FKpj3rQBe726A6hIFmqIfTt1c7vW+SlbzOfn+mTKHenYhU+YalSpn8rQRL/mKlSGVr48ndEYpditqmPNi87SnDdoZoHSZHJs9e29D0R90c+kAtVZk2sYle8i2siCC3+UTXM9BaTPAekg9q+9PsnZTH2gP1j4jSZjuaHfSD5K0HazN5xTunGkxtwYW9vYh+0MFK7UnbTktoqdHxYI6AFjKKDZQ3jrb4HNWvI+irZ62ADDf+z7JSHp2SnkL2VEgiGb08edlSQqX7FqExJXKjlZ5RxWpKLLGc1sXimO03zoR3Qlsb6M8238sO3vT/PU8eRGI1ngnuduz9bwyMXnKvejgG6S16DnG0nW5BFXWjzUte9OFIJT0Z3OazL4RwTl5Yyfe59HStWtq4WP/j/13Ylj9z2P/IY/H/se8ZLparUEswnJcI283ovZHvVr/F+nJyUrvPhcWyKf3w+vrPAR211ebJSpTh7QeOpQ47ud11blIsfJ388T3ZfkVsRIhs2de/6/3JxRMcoKxxfW5dNbd/FzKMhWrjTdkwWszJS16gHLVHwLvJfJjVyq9ScrC8bMPObJ9zEVmCPlJhPUMS45HQDH/zbk02Qqc1zkVKKdzzPnEPRGRMDp9Jsd2t9rIqmmQMdLC8rl6gaz0gVXQ2tme4f7PQFATCaJmK1K1pb6p057qucYBuYUcfWVRZIxpg7XNJPNlzq92/aC0oj72rFb5rUR/ci76Dl6c6wrzAHhKsW878t3BTdGLu/MtmrtpoT1GmZovWD0lsbo+v1E6d8nTB2DPJ1StecK4NJ5exs7oBnb41Wpc24mX/3A31gKgI2M+7SNkN5CerY8nRLP5VxG8DnLOZ3pqP3ORsj7s5MgeuYN09vmOHNEebBnKkuJL5yRbzSyKoKx2GSkcKld1jegEi/6Oo+h9Zgok3ENKHPZFv7fZUo8cHQTViQvHMekxQ6nZlcf+tQ52vR7n/bH/4j1pK4/947ynK89p/X3sH27xsEm5+f3T+v9fnfTkDQmOBJYocIK1/fxJRg7sYL+DTGxlC9xdi49S4QxIAtBiweeau+6NFUsH8j31CI6/RxTSGenu7Fvf9TbAo76YCmY6UxX8JpdDNCsIS1svinym1KoSNbFnK9IwEQNWfktwrvsgXwcVmvNKmkAClJ54GleJsNy858Y7ngULGlFysfb+aKHVy0TX76zKht3lPYo0RxZreWmb36IDql43vWeIxQ9ivoyoGbpuaXCtoIHGF4RGZ3sAUkzHa0Qn5WIwpc+XzvRcozFZuc5ucrVuho57ARPrT41+0e62xbxEaGrKj7V9duYBvPDOCWK7hvOiGyMojF8TgOvZHfcdcGAha73vKYMqnVMfO0i02ar/hZ99kbY0yWR67vqupa6VaFaLDPU27PNOYPr8vyKaIzkFqQjyTN04TGoBRk3XXLlDsGzipEcBUnHBKwIw6f9ExOy6e+uZZEfgXIi5om8vdjf7ubTVg6JTlUxqPCfClBkc88i11vE59rzH/ouTcgGf/NVj/7WwS5gP1O6x//QqZ2vEmodoNytL1hyVuc10ia9Td2MN/Xlf/3/tIj0txE6AEEYqUNRTiWDdiv6U8xRBICpglWKX9jegmb8KrC2fIsLkGhFgXxGDdVsFh/2MRwA5f0TvR8oko0a7LGpfAjyrkw1Q637t3itFsKZh6fxKwpEFzL3Dtuj0tCgSwHxfj4hRkC6fWIE7kyNTFa2SWFnggmheJjREYRAxum7FjI2H/5OsgVD62NSvIif0hwuGPcu+yeu9794Qz2N1krjuVwUBRD3iGe3sUES5PCVvydSVeNedloJXyKvrpYOUbQx9vDyXIzKTnspl7MSLBL0Qp0rcQ14ojhE6xck/e9ztl9WlIUwbud7tzELY3/SMBqbojxK07USndx2u6fY9Q+z3BOTYVxVX4cJMHSS4jH53oI+bzyCUYfsdWN6SjEAS9dxTl6PsLRZKXHCOiOnJ1HpE1LAA89wI5cIp74C/qwNU/S1tnK7nd/H3oeH+9en5lKWc0CpH6xsIZYwbKDmTTOrsZor+0BMBPZqu7LINZiJexfm/JXGsF4/9h8Qm4L7mpxnhY/9ZQGqK/D/2j42zx/6xifijr/8fv/YnV3qbh/+v1A8Coeb0DCgCvfi26piOhDUx78ulL77DMxKMparv92K3ut1ruRK2LdkAACAASURBVMhOUgI8AhDCgnp6XpwvmUC0e6sYUium0EFPjEMH6BGdmK4lNKkH+evuu4EUb72k+dl3KVPL4VlEEdfb7XltvScXL91CWMO5ybiAFQ/guZuSsogCAixaETqE/mwRjJb6GCQU6YCKaBjGQ7oXnEOMEefQyvkaz8lc4/Pog53tsf/28tHW4pJ1IT3SM8h39cNJzvXuoNZPLoJaDD8jNY26sp4HfZ8qx0UKW4sWpQzsh3y3kKK6GifO6ZW5AbApq9C5fLGeafx/BoPNveT4SJA0l37xCBIibDyD7ziv4m2xDcm0gxTvduvi7Gh1EQHN9MziOrsjWA9siXRDf6NDSNlat36ynSDx+DVGyK6MOUjYsLsH4NrBNae+qIF3aiYw9wD/NBchHuwqBomaSGSJjOyUlLbTf81J1i+u7+lq1xevxj/p80ROu75M/fHAcUnH1PPLv22ObY1IY7HnY1xDVDAqInJpP0Qpa5XDx/4554/9TwTmsf/uFx77nyUSPvqnvf7/upOechQch8YrAK6pbzl5eXdZGAqBSk/cgf20/hNk10iUufExLU17/wKbbYfdwGouQzp0Po1j9QnAW8tG7PgHsO4gt5IPpuNx4c3x5epiwBZyUiSD4wmA4S/gLGOsh+JjDMojZJQoBm3PL33zD4pIRQQP98ecOUiyI/2NkIp4ISJjEbu9EAYja1EevaSHtShTtJ0GRF0ViU2Z2rxrthS16nNi0R8SmFoOWk9jiWgRNrWfbZJw57eVSNU00NLfC8eiaoP9ho2HKUIF27Anqlqbk0LX29J/nGGI9lfbVebr1l4Z7aYIAH1Bw2IFkAuWVVe4L5ZhGa0EZQHnBO2eulVn87QE7d8XX9aTmDhu+stDWpJAbGRCuhuK3XaA15RHl4GDzMKoqu2eQDZw8P4+nW0OtWlSXw39juRoT6dd7JNcp7Lh7HdzWYWE8LeQH+bFfq+kZkvpkp/1VaLrr3T4TsZ8gorWRMEMv/G2PHpPDfUzIYy8Ud/Vx6mvXV6n+etkvI4vR/TYP2ztsf9RnR77556DigJlxdTlhW6Mtdv2iis4FpVdPvafG84shGAuPkG7iTk3ZiT2j1//k79eelpIT90s2qqY1TMQDiYdoPJdPASeGWGvbjojETX0Ww7uC+j7qsO+spJXRDeG5bBGODIatAYfAESQ2MGiYDLI2/WVzk1oHTUB2rgStO7nViiPmKAWfREhyN14G3yCXftA8MvnC01SRotoRETGQbYWeEV+RJ4ka6yEIj/ZDx8vz/x4p6xfrnspFo8GZWqXpsgiGyAk6AfnrEefeqENk7vLKrbc9rSuQuC8E5Jl3u9SLJXi9mqFER2MKJPGkUlrCRtdP7zgByONNjalKGb0RWMOuXvkbBury++SZVQhxPkiRhIrWZO8XE4uPw82RUSI0V1VlyMAOoFbmYWVrudymDo9AcgzkAMJAEDV6ivSzYhJB6qxYChSctrRDj2e+852cw0DQLxJ5/saMpDI4twPuJ/t0HDcP0bbsiKYZMddfzWmJ5d/D2Q35LsRDjna9LHbM0FLKCNihDtwn6SmjitlyIVkL2rR25bGlvkqF7k+vns+q6V/FbKB6FtuzaDneK71h4S42gWydoPmnYjarMco1nGwk8f+M/L32H/q6WP/NSJc1p/H/osrPvnyn/r6/9/+9K8DWyW4IfhHRhWhC3fxPQf/7p00mvwEQr30MM6iMC2q+f1Y4AXQHGxr83MCvrbbbTTqi184AdwKpBOIBzFba82++2194mKe4D5esOoPJPQjeF3V71hRTYTVtwUy6uIDL2dOUn0iihaRl+xXpP9tZ4JAGAA8KCMjlJqjnLs+HsliGye+WONuEaoE9j2FMglbibKRCPq8XP+sd9mE1SgaJlLfiwA4+PZteBGUEonxtvYy2XUeOB6OXREwvlw0C2X48ysWq2e2UFa4kE+B/lYsIQhxsyGS6JC/ZIjnmw0kvM1y46ljS8Ybkty/4DUJul6TCPqI1YY31NNw+nX9TJGpmEctLkLoTfXc8cnJlt20lk71Kve8RJ5CLASp0rt6qDfGt4H/O8JTf5ufDVCCaN1ELAjE74CyUkHld49E7kSORoJTSYSbpfttFoU4yLLtiNqoXT5MgXSdWn2+HWTdwKHS21zxnVfZp94sF/dpl3YypQ4I7PM98YWplCt7ShpJlQhUxqQlkLTeV+Dk5Aoe+6dkHvunNGZg+9i/ZPTYf26c/8TX/4v0dGUUcM1FA6BRFKn5XwGpPFNjbnSCRwmm69QKYGklMsBTna++K4kXPFciAKfbWGWreOFWyjpA9Mc3H1GlSy+8zIiBXrja0+u2YgEF5AOIY6FcJMfPIXEH3tIhsmBBEjpnRZ/yvI529tsmAtL/shCDFkVN3XoGomJHIgJCJ53QfZGKFsTPySJITYoCpKNXSfNOcYpCT3xOt/M1IJtELwTlqS/S8g+PYCTwlFwW4AcIikIIi0ghIrXk1gtlJBnOghYkyFr0ZBEZOVqEV3rrY1qfUbo8Ykd+oa4XmRHpjjRDygwleaVXOrdUyNohynY1VUhfyMn11AlWBHRchgXsCV+uL1NHBB6XJvoNMQVoIADwVN2K192loYk/bVGYVijgBVCfFqYgWBiHTGvNfI8GDM8wXd0r3xFE8zlhz0NUZZT9gE53kjSAM79P4yhnj1p/C5EacrMjQtHnGn2b5BCEFXaSWQXnlJFC4gb0jmWCR2E2SbEdm6OdgHSZf2rxzfU70kM5Juo++9Q7cprX2Oi5S6+EXUrPOsm9fbbmrJ0L0jL92H+CdobYHvunfVYZTXsOj/0TeVcP8Ng/0wfLYrA+SD6BI6co/G/86d8syFXAZiMamfvMCAbTiTptclDjAC5ymmMRyAUjo0N+tmR69ga6HbAyKtErv4FMBGlogJHgJdPTAEBXIjUjUE5Stnf/WEsdGHbQWmVsy7/Hn3LX0BGOEZIKzG1SU+3XvGkh88ntEYkYYxCcVJQE2tamfVb1OC9HOsj+uk6OSX3StAl083vDuYf3ygQ50pzO6V06f7XGr7ag4XUsJsTVz4FQlHkPsutz2GIYBibyPEx/F5U9o6bzrb4M76CSUa5/W3U4E71mIPW7p/HF/Pby2C3ayIifNd2IVTwO8nZCTr0rZAuEg4RZ16sIQ4xzA8R1J6+6c9p0BZQDVg3vtv02XFyBXNtNBBljEYBQrQM5WWbnF90BxWmM/bvsckKASJ1MjpgqondoekMTcAh1ate6arqlWwP9/gLwO4ls169rr6plK7Uy5zDJ20wOKIOt/12ubU773JC4bcQZD4pxwV5JMu/m6naORwXNNOIaGXR5MPf8QLJf6463Nejh3qV9HmqKXD5tHI7/fPcb+6v1oUYvH/s/+7xqiY/9A9w+9n9wBY/9/yjr/8dvXGd6lO/9kbvgC5DH9pGApmTfSVJdfQWAgUl9wbYD7Nfu9ArV9/eU1MBOPaOB+c1eVEcdC304aOuBiMJ2psav0878GKUS8VmNJ5kT5uEOtZ6vBbL30+TiEbCN3AEwYIHpoFfPjWfovE7Zp5yfYY9n+pI/Eyt6Ps/HGqQkR5Wg2NPHvFMbQPYLr39iDoZxr9+jX3VO61msCt5Nb9V2ErF6HirHa8/RmKtOq+/1PT5oe6kSyPznzyVVMmWShCe+g8vqZ2p4TRDaXhZehAZV5ZRqGOQKJEol3JmWSbK0fuc5NaSqimxTh+UGJOsewS0EnX3V4SBGOt6s5FYA1CmaQI/XQ51tmSAwPgGP2P32HXmygXwUwOOAAEfC4MI8poJpfGUHvR3AxG8F5MP10t+eiNBh9Tx+fZJb/T5jFha5Tl+mfvS+9XbfAdpbGy1FbmzDv5TdS01WH0k8OvlrEZmTgAjuw7WtpS31pPerE7NOxk66f9eH9TQ8iBRnI7V1ufa1uQpgSqWz5p24DG1E/1qhEa2LkvnSka9QxK7vce9j/23V56m2rxDwzaWP/d9nwD72PyjPt9z+P37zT/9GyRGZEhUrQz3rwTQcAvp438yykAoqYzfUfyIQWwrTzshwkaznC/REpmtlZS0DcdrCy2iAgLFGIhzWAek7aV4cixYAEbgCEtt5iiAS2PIjWBeR0eLAvthvfPfODvLj+v5umSWPSmxEMOLAu18Q/fc5tHli1RGTtVIY4owKvKI5AGtpPQdKoufamar9mpgPIAeLQDai6XFJI88icUuRIlXwuqu++DYNU3Oxzz/lxGifgYExguY6Z2OrYy764B/03any29WDz0AvkmfVLX8OZBjybs9Z7XVZg1wyalUjQVmlUbrXdXS17QZRr/G5jZS6jPAR6BRywHShEbnW9DBeYu3sADOvyd0S8zczMND1/fFTdzQf7wC3cTj4kv3shMG7i9guDpi775RPpt/kMpR0pC5OpzG8Gn+R0/qQILjM7yHNinqk8dW15IDAbsky+uB9oizZ4plskSa4L+AYVM6+Rd7k8l6dE7sj3UWmh5f29jGUIhNDCseSrTc8PZvz3/+edGYjVN72Sb8YBT21p02Aki742H+sn4/9Q+sf+w9hTLZI/xP2/Nh/boshxf/jN8uZHgeYBFTYtauO18FNqw8Tu/1+sUBJITvcdovr1B7AJ7ylJlpgXH3pZ2sCAKKzUgLea+k/WYBAUZ4ecYizHEifK/d6tCEX81amEOBfIHYBdCCEJEC+eMcufIL+DhZ6BOhqO/uai/aXtfKlTPNTguckQOZmA/L7xUV+Iqn4t5xJKZEX6BPIV4kIKYDGyANkxmijjTEnNstkK81Pz/OIUNOfjPhJmiDN3mw8T1FI6lFJObTWojsOVoJIiNvB2C7ZfvaCGtYDVRFsbQVprDamsXMO+zkngbCYM5DmknInnRBhc2RkemXeMtuwv7KqWwpFeldksd4FlOGd67epn0dA/pXpPmZXeRZmT9sZ3keD8TPCI6ARNtN2tztxqyfGc1fQJHgPpAvYpXOVXt2kYZ3Au/zkLFv/tkTF9pKeaR01R9rGXq+3b0A6T1E5f+aEaQm6mxjGj2zjgJHXfbft3t2IyGTM0U2a42lMJ1J86v9dO69kRJ/Aue86dvsM/zH2XRbB23W4T8rx2dRfEEiOZezPY/+RPl6JIfzudFYB0b7H/h/7v/MDtGH6uNgUdCP9Ltr/x2/92d986SlEwishmKgk1nfvq8vaCI94FryigSO+p8V/LOk29sACpAr4dDKgMtl6AaTv+vJwdix+Aer4fPXYwbJyfhAx4f1XlGKLVPniqoUihzoXSuiAQuSll/pGwCNTsgKo5cnlifx0hWZqmfrHhSrIUkQsMtIR28wDKbHd1DrO0j7IWwHRQThbJJGpUS2SYaDb07Km+SG5BIAXDB1LNet5TrxIKmJsSAdc+tjlUMhCJwOe6lbIWhZAkPVEOhr7Q1BeImep+wR2IiUZBxSptYZiXni2R0CUhQ78uXo/UNqg9ba3sz7j/nL2Cvq0rvEVXGWvJ8e7o1wPzfiLNwOQ4UWeR6A8HLjOOE/KpSwOBH4d0PuDBGY7EK2fX4NF9XsCfuGDD5GpV/eeZKL56juDIQOQk6uNQvIwfi6UPcpBH9f/jn4LtAUB8w2flrLWx5Gk7rV8t/nBQh5rkKda8T048cw2MTHmLYrlfdFSdtDNdb/WECYluKD5uJynOs5J5zrAHfYUwwfIfmLO/KFdByMS2yKp23xAEVMPQIxlT4fnvNbjx/5Pm0Mn2zr6w7aZ0mUv/eQm5mP/Kc3H/nfNChzZNh3jUMG30P4/futKb4td+HR9MRgnKTnc9pJI7dQ3CyKYj2IFgkxygAOA7EZW+gGQHaBAIM1Tr+z6PETa7+dnVpGSAyHYKOceIv5hPRwjTKdxBTGqoFE55ddthXgW4JGRF0Y5cjfeRtSjAGWcDazrt4mkihImOPH2Mb+8P1IbfVk1UAIdwQq8E7QDiSQwL5ESjhPErIB6EPNtnrIQR5EZz9CAQFOlMxpn39qc4W/4g5oqprnNSoBTqhzn09rFvHvbKfc97ZT6W6Mq1J9O8lPvIlVQ55bGyKZ1hFUWe7pgyOaSj4eGeqohSXbp62H3UnPFsyJyJRVwAyDyTAYQXURIkC4RixmjFATlPo4ODPclAJGRmmkbP7CNHq2R7KZoUidmfQeuguGdEJTneseDvGlhehUd6QN+cY6q+NKBQG1gjueWWh9piypN3ueDG0Ud3KesZ7J0Igo55EbKuq5Chl1MJ73Zo3IiC2mzhWC/ioDIHw2ka5OVp7+5uwbVdJ+mDcTyOu/0d9SdU2rfbh9Vhl3fT9HLx/5rSq7J6Y4MP/b/2H+1vsf+4df++59dZ3rqTrD9bG6tgucUXQXq9v1id2XnH8bnu7xKnokFkWCR84QVjPDW+mPtGnhMdEFQqLdh9zNDXDyzDw1AF+CeMXrtUAvo2/0ae0qLMos+BcFzuYLBsN/Rv/7+Ff9BC6X6vuZBhSECjHuLrcpZENGSkpdzWl48WQhDKzrhRFflV20aoBvqe9s1TllVPYq5jA1TUYCafkZZiqhLB6QXPWqZ9xDwfykvmE35s7R4DimKG4h0D3OziH28nDZTDXtkMKOQIE5bJTfvR4nwJOCQpjENLecgbbYQc1bPEziSTuEMml68mxbBsu2mazHVVxqbInKdLDmArQQQEVxF+1bOnbe7o6T4hk57dOCxsbADsw6u5N+0GzWBKhKPcqimP0edaWld4aXi+0JbrOJfr3rmbRHonhdvCutcclu63TeTOuFJj18ngcA80gYHwB9pOC8iNdm60oDrOLYV5xXQx8H6MmfwlyTL/ZqTLlFH1MOTTqw2vCGSp6ntXL1aFA2LhmR5mrM6Q3fgt81lXS6j4MTJ7EhCcqndU0X7OGd7g11mokI+emoEHXvs3+U32sNj/yf16Tr82H/Wb5ki9PQF31X7//j+n/1we9VgjSiYkLIcLUA7dvom4G7OD2AToJZAw3bR6f+qCsdvL4iCnG2N0LQoykbkZsJnBQpqRRye9wms06IEGkWPBPHeJU9jKzZuAMYYAxbt+M7lF+/G0UKLSBHJ6ikVT89dMxNkVIuSoPR96lmdr+qQk3h4tv96r4tHdXDmR+lSEXFa46mk0MRkA+V4lvxCnzyNTHMBuei+3l/2MUeu9DU8zyM/1CmRNytwYC0rwqXeMgpkvyeN8KnP+fe51pjSHmoaZ5TOhm7YmS0HkCqB7ruAtOPUx1rNsJL2WvhAekdZGWlz3UDxhhqpbFGwAIQ1ZdXG6f2PMbmc2iqWHyt5OAG2V0DuFGXRfZ146Hvp0avFk9fvbfGb+xFMu9+mcHh5W9unp4Q0h/nd63cSTVGPSV5lgYw+uS5+gpJobtlPzO9G8kAi0raqnCaCEzKHbOjD7+YM7qSdUpV3kGflC0mzT5JPb4fy17xtpcvgr0bi1uWBtPEgdCW1u+pHbTNJUiESJ3J5E81LnahFZEioJwIX69lhoyD8GTr+2H/q4WP/dcOge9DNFl15Hvu/JPXY/8dv//kP1zreU4/omCL9Lb40NQvwjRJhnfwkQKxwoaZnsUpYBUx1H4uH9b29VrZYDoH9uNrIlByb+Ey/64QrnQtBNhdfgtKoItaBeUnxqrkd0bft3UJYRAEUCKAMkCD61KIP2zmbZemSb0snE+gsu/QJjxzON0IqwOsAFmyivDMIZEWxqO0lnBJqixKQoOiS7EuSIvsuNYTnSSQmqexIeuK5dlXOi5OoSAsM1O7nUjT29KYJ4K+WrE9qjxUDq51lul3YCd73FH1uJaVNH9pLVhGByIhdssJOLgpZY6QXerzZMsi1ZF/S065+ei7b9bvsR2eWci5NbhE18sghCVhGxHLmRuDdQFm9pnoPe2r3KG7vJBHDIkmA2BdZ+oY7ME47nmjP+q6leB1BMPwD34O2NONQ6cj5aiCG6pGr7wkpHaI67GeXRwcd/Vo+dyJ1M0n083y5L5LHDTcC2OcYnzGRE7k7jaWAhQbWRay4c8oImR55Rxx1vTya/BY/67tdPup1JWTx3IE05R2TNh90AZe+RTpv7Ch0ANXq7ntSKzjGtY/95zkxF8pj/4/9J4ozvKZINP1K9y3jurYlvgp9nK01/P+31P4/vu/pbfsQUMULC6wt8BUClLQpXTtEZbjo59+CszYFQTQcrC/wqHB4S9/KxbP2NXfZHeaUnWkBLutBITYBLh3tIiUo5eOAdlWo+vLNp+vdRkTYHcw1R6RLe/U2U0bvW64IW+EEgndLqRJpq7LTFOX48jzRHoVSpESdTeLJlDfqSCfJG0DewIXP8/p+Jp1FNteclyhai8JQJ1usklENVSzLtKy9SEYH2BxLkEj/UmB+fXThl/LdXv5k6ayiIl4MYZFv2sfwglK1XyJ2iiQ1clIjOUmslx59udLSMoHLvtsJ/hjFceWpY029v4ZwyTh1sVZ6S/vbI1xyunGNiJLGpuiZ9MTlpSIWijLNoDzntqdbnR36TIKKrqdZmM/AHsYYFRkWirIIHXbVuTh1oCzb4LiQ2VtSlSbAfTce84Osfue+0UmitMimJKOKmywoF8hAabBHotdkyv7Q32m8XEd0vqf3pU5S+rc5paOlbCEtd/n3/uLVuofl60iTH8+UocOu1tXR+voUOjIQX87/9n6hUMjU5bpC7/Pbf+9rM/X1ZDuScbdF6m5Ug/IJ6qmG0Y9JXnhw1//H/rH3eXO+bJrngBeU72P/GYB97H+Mdm9riCvXz5v9J+npYewhdU0Liy1KrMDGfe10l4oEmXP097wAuKmdJUydBcr9uwAYhTnG/Q6i7WZfeEZzjjMITEsiYAzgJ6LVXw7pHcidaJttnaVhQlgSIPRJL5UczoJwbKu9IAQCkv6ssvob8DqB9b6AZRSi9Wl4CWbci3MWOh+lcsxMK2N0J1K7vBHK2MgH5wdkdiCXWao6dYtA2QB8AnmKR3+bjEDo0ActotJpyXIvrexgH/ImeShls6WbTnLqfJpQsogBT5QMaWU+CEZoRP45vzaOGlWix1oyYBRLLHCoRBjy8uv75oG1VZ8nWZP4J7FLN8mzP2FvPdKJMuEkTkLk133RdsJG75PN83kXPMlKAqicW7odpuRs4LEB+wIIXRgBrE/gFRN4d95k+cQFuJ3MtQPqp8hB2ELvD+yygv9Whrqnh01jpsG92a8dPO80KADzRCzad3HtHYmcEHv7brUzoMNZvqkzEe2ALa3vhrbe6MbLSyibXPXqbfHoaTyDTHl3rEX9OvlQH5ugAVPrZHiMAFF+9PwS0SKxHuXd7BYRoGme60qSMn/sHxtbj/1vmaSTkT32b1L5RbH/RXqYHhE7eXwPSwM6ElBRoAIo0/VlulUeziZoi78RPTJwbe9e+dCO9dJMpMEFKPQJiwUdq3Fsi3JXWhOcq0JGAXwpORzEv37VQW9z3Ol6y9+O2IgL6rUEjjXNpwBWnH8xoJY76pRhRsf891b9bF1bogo4N+E/XrJm1MsA8H7mR+OO6dK5Dp9ySbVGk7ysNVKnIuygKByiGVwU937tQFV9XXEvENQq/zyHovanaFW/R+QqyJPk5XNfIzpNF0uExlpW+yKiJi9GSoYzaBGhkRZddrDUIRpcMuBLcRuR1Et0o1z0ur++KHaSC/ubOu8FJhpxquWqfc5hC+qjSSl/N93O8YTuQGdXv3EGrm5A1AgWgRftZPpek8KSxX2TRZWSjli2/cD77V7OvPzPHllRX3ufO2DkdfnoahdbRAM76REhYjQGWJ3D6cB1lO1wvug0Fp7XKM9xO1BkP4A1fJcAco/4SLokHBOQOZFEkZRpfjvg5ufVtWQ++SplkN3SNwhll7HPH1Pnpt33vqsvgiKBvZEeOerPQbkn3evz5lNUprzft2S/znnRw+Yt/fEhZ79Ec1tKi+MmtvrY/+BbHvsfo9nhmhFtneyjEwKp8WP/Ws2rx/822/+e3nZzcHFayJZTHxZ9Ltx2zbVjWVOxJueZIGt3t3XHOqtQENDbImMEq+S6D+9HWM8X2A8A59EQbNfF2QQ44AXgHK3ZOADsfWAB5rjYbWUjtDHYzmjAi69nkTC4IOnoU24iONYJRUPi8LkgGFKdMsWpwrO+8Giu7VkE4NqyA6gPEpWV33iOI6IgOx4s5buLPrQ54jktrqVxD8Z6jb+WR87+U2c0VTUNLSNk8ZxW4KCTWsrU9MDQSuoNIlU4s2NnxJTPmWmAFFOSNetXRI/Uzlj5D+W61wLoKXCg7uUZkp3IbyFsSJvzceUZIR8nS14X3VWaYiV3m/56Z3p6pZFF11M8+5KEom7TooUpMO2Vf1BJ3hdnCzpgDt/l92mzgpGb6znipT2nOqjQ8TB397b2OXza4Kc7cKQPfud5MykAmeoRH39g310/yop+cEuFA4P3uZd/nsYxSWddf/2vVcUToY65gF/o53AYedhXIBiFQBJSYYKMBTn3OWudvZunTBlLuRcQzxRV/NCB/tu6ILK7BMEVZQ9YUR5sP+YbX3bZCSP4fqbrcj6RT+dYynNAjCd7ZqT3pe4/9o8tW/nTaSPmsf+NRAPfsXz+Y//p1L/N9n8407M7wMMSbABicJbmlKrrJTDU78XNDik3kcaEQKVVrDJIUXa326IakYohLYehj9Waa3Y546O0OeygT0RjAa5EwwnKEA2gEy5/R1TJx2MI2dc+A8A1LayC5YwSXNcZqNbseVOokDWUZMY67ljDnh9CsdLgPZWhnJvxuV6AXoNboCDn3/qSGkCQW5daOWAnUkMRhrzX2lerAYhZRKKRxR7N0pglq/i8VvIE6JwzRohCxQF0ehpkP0PF1D2+qFMqxJS2sCM/WxB6ynlTtC3GavOgKGstJGCth8yvs2luTzYWRH8Q8azE+Ir0GMWo9rCX2rY5qXDJ+pZ96JGdqhudROXYtnNPIGhQvfAKPqUWQaav8AdOhEX6MO0eM2qxXTfuuGdZ/2ULLY2st8E5Kql7regC0/G6n1n9xjkc5w82NwAAIABJREFU+ocOQO/GCvd2m+9NcAsRrz9nUtWv2j/TxnLjrM5hvSvPN645vf6Hl9tu/fKB76mG71W6I/9cZch11tKFfQLx20hLhCjXYOrBrqO4DkBeS1oHH5vOoo91fuqGWSW2Mz6gPid5q8QG+zkR9ClRyVwyfCGoQKra4Q1O6e1I/65/H/tPnDD4vsnXPfZ/OPcoXyw5PvZvqz39iju6pVe5n/t3Zv8f37+qtxHxhSduDqXsLNIFwjcdKpblopUAxu6ydgQiexWiAD8EM9xxjghLWq4RHVdQRkgEAnV/jAegWYShofACWpn2V9LI2IdMJ7M9cR8nx1GAuRAsowG5XNlflVomsE75S4vsekWeEGVwz6XfpZkB4oV3O+CHKiQg1W6/aTdTBMl1lXKmVKwgKA6dKOpaECLHQLIkvYmS4pJpJ8yMCIR4JUlEC8IQ7exERCxgB4XkoC3KUSYUpbmb7knP5Q/0byHmLjhFsPLMVhLdrWy1T29qiObCbdTLzdf3Z9Xxa74WGRmiMhKFkZXUy4jC+OD1lmNet9uwvfdnyRTRWF2XZ3/S0PPdQWZXWT7f7DyJ4yFNUKR9AEIEngR9vLSAwRZhOV43PusAFhH1oKzDy5aUpgSjSXZyN/Z+lzyjyhGrgx+UVpzISc68jCByq3KyyrjreHl/kRsWxfx+Ly6wdA83bsTVweyrtu13yOxF1GyaY/qtWhHw3Q3DDdOXjIkOPFcfWtqc+ZR8UeW6hmdhWhRxquCk3vJf+qhKdtJa5AZ6GqU2FvphihMZvjPJ02+bHtKIE2/Ft6HPj/3HZupj/4/9C4HLLE5+U/5g87e+n7StoWoYP3yb7P9cyEAONFZhunk52nr4qefrBuBpXipIjnaK3SuRsHAiilAxAwkK7eoF2LjmMG1ogeJcaGI3roFlgg4O/WrWCjO03e1CJGqqX4DJAIr+fKYocNedhM4JlUVyrOMLTCKSIyAtWrV+57Ni/HUJzXlpKWqurDHTWwQhYzfUBsMi3sdIvctJ38/O2ChKlIMGEuMQWfRRS9YfBjNU0Ws9yYeYc5wm3IlMpvzVcydJRERaU+7aIS3jRj85RwQN6/qQT+pOL5hg1+l5NpitHLRNcLzzKIteUK8ZYUtiqijg1u661dPooCaa06gyCNvp44v0T0QgKinXi4QVpUkj1twt8gLdla0pShWRJi8SsVpg5bdWCY++oXouunop3YGMgCzl7vD7xKX7rU5I4vfh7AbcXBY3OZzn6ItWWbz8w/TsE6CN+WVDSh1raJy+YkkGxANmSVGYL7shou2xuWt4qPrI57SlZnuuvrBnOPDBbu0e7Wlp0hJOP5+CHcw+76ll6cu5E1r7VKOwW4EACYcR2UKaW1GKF/PWyVX0pc+9/1B0C88NHYSxqYudGE120edNfnPNrXz7MJZNV7xxbqDu13Qbfux/Itk9Wj4W+4DyPvY/V8B87P/ohjc8yzVJCSI/afu3QgYA1ufu/Wi/cBe5uxbicxIec3S5685FYQHIkIxDzRK1sVUpfO9wdrLsZrtXPaU9BYvCDndJ24otr+upDla9xKn6sKf59ZSfCvgMFFSEM0cb5OHt30I+eKZpi8A5mSDZ9PyZmpqFNCT0Z2FvLYIH0hfkA5PM+3KWKqkr6WqY+Ez5qozRogY2S/HCUgcmK/oU/czoG/VpAsczsSGhrWmV5fqSwpeFN3JuROTSnvp5mD6XiyB2OU6pgjBR40ccf42CbDJYX9i4KMeyUbAM2AqMLDuE/lQZeESm2Izr6JAqWlNDs6pft7OYS+kfSLnzwZLzZBEhH7dHhF6diTEpvP5vIhq5uOX9Am8MqEwAYwIUMUct0hMRmUNfm+uIziT4kyfewV4HiH2cblquLQPAlr1dR2u4UdMaPoFV6T5T9jieaWyn8dYdsBIkOkzwvTymfmh9o0Q1ho2wnNSqy4Zpj/htmptX+rqRicwaHnszXc/zaoX0HEh4l7t0xt2H6Y7O92qfsqWcWeeQYqeBegcncqr5CV907J86QK/V0vBeuIDH/m3TgPke1MVLPo/9S4vfVCb5zsf+XRI/Hfu/eU/PsMPFrRsylpcYIZf5kn7mjq+Qn3JKIMF5dU/YDfNnr51hFhOIPskwcZAbC3OkeIXC8UnYmXPPrbSqBOckEA0YNxkFMIMDt6ftEReeg2CP+vmQklpW6B77Xg/EE7hcEYCUgYNEvU2zlFjOSd7JSz2UzkU/QfMgV4FW5W2g/3Z1Pa9DSSl9jyl9XIgENhkBIglSHwmkI43OV9QgKiLawbfM1St1cTqzs/pSoos1PVHFBwLYI4IjSTPaYWPbCxtEhMkemEUgSnSpppWaHlbyZbLx0sjFPpL4xlm6VVHRLiqbBf5MzrQ9ayCfroS69hqr5lzzrDksc9QiYJn6lnDMilAkeWJ7JHGh0R1N0p/hrJbkP+1cM3Ih2Rx30dW+P/fVbjv2eOqud5snH3Yey5qApD+T4JZ/y+Y6iG8iceuUDrQzfyXtoezdzSLHmaMmGj62nAmqU3YmcdmAXVPGHfJvqXSU6xCF64C3dLJ8SBJ1JmYuwxOBPURq5L8C/BMoYVMgdJW59HxW031+5DjNG+/zvY19mMt6TT1vNT3PXcO2+SB/UAnPJGOS1wrMg3jRNh77hz3v8upzXP1FPesSe8Dh1B7777Y6+YuTCq7vDz+OmzDMBKLveuw/okpGespJzHNZSbOM4Xd8pxKtJc1K913OGIBaylAXVDOpNKxMQRKQFKDhdYxQrLQFpLZZ+wCETMHQrjUc+oJ/Ar7eyQLkfHvPwM3HNx/rfEJq2AKQuIGgTe5YO4SZ+uR91JkKP/FVAWRqf8gXFc22FKvSh8yHX4B9i8A0YhoLfwULNRLUgHPb+ecC08kPZWK/2dj4vfRCZGVVNlNYp4FpLcmFRMQYPEKBZbTMUanshj54p68+dWK+KLbOl0hfgwhUUm6g33XS9d8IlQAPzrdId1rJ6UjvCtuAPqOdlGEWowiyV9IVpW8me0t/q0Sh2BcIjiKa1++r0EYxYEu/M7MB2UEq0ZIn7qJ8u17Yb/28lQpyDDIAQT2Wvl7Pn/poA9EOZWqlzxMWn3qOo6adzuCwAusK4MzGCNX6Olc/p012wlQWwlgw2TIna97dXm1E+lYFlUtrykP94L50X4uud3gEmWWktT+hc1CqaVd/v4vGzmwA76/3ZyMfEGxECtB3d//7y1972hXWj9XfQpbn3lLO9sgOOPd5rnoj/3GOTlJvpnup413naP9m0P6cYa6pE5q6Sj1qpC3WP9fR9WzoVdfYqW+xprf7mjuKqJLa7DaT68xj/6Hvj/0fSX4ndsXGmBJtS+wxc+Cxf7O3tef9s7T/RXpe/NcLDEyXv3NNv0/KEkCnebYxLSzOutSdbTpogSQBpnzufoajRALWe4H8rMCwNbedTfGOhwD9gHamC/qiVc46tNQ1ESh1ciNrudNeQZEJq5OfPpmKVijqsMBmpAgm8JP8KhnTQnwPUpPIZUISD/SXuQlwgP4j1UFklQsRIzRSEZZpLmdmGvkM8OHEZREVCKm0DWDEbWXpkf0LLdaBfAD6qdBAjKWUK0fK2kCW7DFTNTSX20COw46QjK0ISslxByBLMlXPvNniR9KSAEskdZFnVlKM66UH3uZGKkWuNJZ8Fs8pMX2OkdWr9RLZJTnF9DBtrpS+lo1fFb2kj+3fyce9s1u/gCFBbzjzhFzmn1yeL86T1H4AFANZqmxqAXslcpBplkHIsCN4BwJPvwkc9UWdPr2DzH0XuEt5J2dBRN7prz98Jp2+wLYy/vQzImhMraPvmmTRSWbMP4bW9WYG8L7wwweZjiSpXFN+k7JVwJc//9TnOzCmMdu9dU5IDDfy6862pCZuEaxhjp3wyd/5sN0DEjRmugvtZ+umP4KJKZM9l7m/hIfKW4/9v04HFVagjj32P5zruUuzpJ9AVCd9xmP/qV8/Gftv6W27Q9qN/51r7lzM+Tfu/sjBk1DUqIh2KWtKDhcp4tOMbDjQRyxpI0cot8wd9hrF6mdRBGJy6RFwDQwaYHNII2qpU+aw2y46o1VIiStgpwHrSCcsi3BLJWrPKXJbAm1pfqrW5sLuwJFzcP1tAJWRjewMiwyILAaxKATQmJHKK3PB6kStpJ0JXCoi15yMgbeUB+dbnS7Eoad06bOv9EuXCrFUCliNYlEfSbysP65L7BuAtPQ1z721KJK3kfNYyZPmR3LL5yGCUqKVNfo1EYXS5yVzb8u9d9gY3pcVKXJlzEnqjLDDrlBEZM1ZKwiSVe/sHr1rKWQKfSJ5Lfc1Ujx7qwYEGzTsYF/yHknAUHFM/RX4O5EF8J6atjWkOAQYiQXYeqPdxisCP0ZUgKSnfkxEsIIhyGqIfnAMBYRq3jfQPM/I6oe5qvQ12Gy67mLhAgP9LgM02UmC2uXm2LoX0czethW7SZ8XJGzQk2k0KfKZcFBO9uzMwFj3Oto/6k0HYfjcSaYUq5d5HmchUtsaRkB/RkLp508ZHy/kSlGHW7ZYe3QimmV8A0J/rxDHY/+hg1P6Z9l8eOz/sX/Huy0FN3Diz9r+f/vPf+j4yyuTEQ3M64t9W+LZhMlcQYQ4D7/r0gCMvlBwoW0efrU0NFvSteRkW/8J8rQLxCzvkpLHalAtqdLAFiFlPmgCkloURUBKqh0WwohsxVknyk1kzVSFQJ8le9MZ7ec4KEoC3TgLxVXyy5cAjdeTWbmO50EE8DNCo+WeMgn6t740YtB/b2eQ/Mp13XampqWGDXoah/H9wH0Uv2jnVqI/WA1LhKFVBXPpu/yHNEG0Y+e/etqYQL0h/YiIwQb2KBaImev+ypLz+zX8qSAAI2ckECQu+b21tKVJrq5S//ZzOprhnFeLAsX8NXK4nhNEzq4LEuNIU23l+SfvX3nxafZYNrEKGAwkm+NMonmYH39G2FnRsVzINxzmuloA9Rs7fZUoDAp9+OqEA+3y/awKwWD529vvoD/kCIBYd/gb4KZdD4RlIjkxWXj4PK4kaoxURJsh+/d2qY24vFibJJf2igES2NLX9SF/HckPooHLPiF76hsLDPXn2ezKXvcUmkmfVteGtZVzPsn9qGMtLUW2PhGbqdx2n3cRcHsx3Jx6KfVin+76vP+WZzFTS6Dc7QF3eijbKDbz2H94gNO8UHdPFz/2n+muklfX0sf+b/zfMTPg45sa6RET4AK75WGw8srNguGeTxGLNXEsD+3PGtPitGNEwNm2bjL1DSCbEYu2E5eAx/8aXoQaB8W5Fg6gNwDb+qNGYwisBDglQgOl2Ip8FWHRogYxJ/nILwMsFmDJM0b1wLmcdee3V4tKTNL5Dq4BtkjlmQ39ZhgTgMQ/klhpQQwQ1dJM9HuNeigtx8y7FwxQhMZ254ZCFS6iMve+C6u0N041+2Bj83TI0xktzd9Q9nzJsh3OzzMmltpGc9vP7KRBc56SSNlc2CtCParh+hXvwYEdFx1p54ZsqLVCXKSPxRhNHvkupT36Y3K+5ixpuMqpl7M/0CFGvFjCvD4HaWiY0zhftiYx4aXsrOgyyK7mRlCxvG+oRxdF0rHRUQBX370aVmt6ybJQCSDh3Sp0vW5uoScdTJo9up70Fy42wnFaMLfn6T6MYwYgeSfb7qRpGnt/ZiZ036wn2031izInh7FTnv3vkKWD7Wl3NnwFusmoDrJKC55m9OBIMJqQe8RlH1/62/WXd05/T89Rt2Nsw84r09nkD7c2N4XIDatNRocS5ozkaPU4HX6IfoykmDPltoBlllrSx/9CpfznXCEmPe/zVHTAW3jsX6vpQeJLsO9vQEytPPZva8Fj/1lM7LQW3VZve8spTESp3fj2eZ+btvqOawGyDrYMA9gv6Wjqbry6pvd/6LMAqqIeasGMqZYejjamBO4CdgUCfecKnj4Boke31rtn6s64Lcx1mzhIDwmTO40836MUqddpbH3xuayGEYPog2Tqc8RoREZG/CIRxQCQPTPcoz04nxXzOSwUqz9tjHm9wepIk3NN7/BpHNOB0F5d6CWiBZBDv9zLrq65EPmvFmrD2nYx+xTkrpyv8UVbMigRjXNxjCSiX1ahkPUclGqeIze5TDDFUN/W+bUOpc2wrDRSNdtuOEnv0qMxauYhnq0gR0ZglgyvcWluSxGIdDZRAS4KNkiekv9QACHIzJDCh7NmnbCeFt5wPt5XLeXrY4tEmHfIOSdgFMHRNfRDPQ2tRgdqNSX2p7mSEaPf+fwOLKJv7Ye+T7Zb/77H3uUWftn/6BGK7XO4nh08bRj9MMjTdafoGPu45hGVy1Zb1/9enBNxtTjh/bvpKBEiXljAfRtU+KCe5niIynWSLnBKsE893mTS9LsP6ARWQ+aZkhHEzkUdhsNqiXO/Zp24098+5+thh1TUrqs0rMf+6cFy9jXvRT0HAwxdxm9hW1jrU86P/T/236PEVSdAevgDTZVQ7cdj49Xh9bbunqmlYU9GKGdutpLVZhVxxgLRpg6K9QQ7ZOz39TQ23JSgW6uyg5qBdImKCbh8/qL9+eoEuiM47f6HNPxcQ6T7tdSt1V6QGI0pBxHgGKEEkqwArWsFSAbd0/Mksy6TBL2STRuvLhDgHaJvpY1yjimBd4mYiCj6KsXd/kiZaGdBelQrtS3JWUSytPrpfTZY1JMwVO26PgmY1vHYzSQd1EOlkHYylUS1Js/0M2Ak9tYHRiVV/tr6yopsvK6cd4qxMvJi1T9sXPu5pdAJFkXQdpTbpt9qbawPSvsL67U+uj36JWtKttS3mP96Do2Rrk3eIvJAL+p3P+8T5KsRK83btrs05LzbY8zfaSxbyljuHxS3WcCYWvHOBlF6cUZD8p68efZ/KPUL+ZTsZrhA2s6000ZwonRfAmvuI90Boiq3eV0iYOKZlE7+ioDHD7VC3TTX4bs5F/7l6h2+Pz1P64PSl+90YiOwmvNOvF4PLhiofNRp3t5pCiriy9JetZBnjhi9jPmSn7k5e6Twp2QWqeOQ+Tv9LTrWbpj0pK7RVe+mts669tj/Y/9V4R77z5VR631ZZ+gPfM3+Uew/S1Zj4SpnW3xxjukp0Rgz3Hwfyb76lpcBxiJdQWEtg52OxFIHbMUozxjSiSSk7OcesbBr1EcDHhyrAcPc8rJL7Xr7f3VUbRTzTr7u065EIRg1rNvTcuzB+RQ+j3+rUEAFnd5vBUl8DJJn3O+rDuGwv9PRwbiXZhZqjMPkh8gDVs4N6PtD9eyUrABqErNQZgeGTIPSfIjQCCTUdm2uddg/o41ZcSyGBNmUqM2a8jyEp6nsUUHTG+qJaeH0fZnDSMlKMt+JS/QHpEUgKsbrX5RS0q0/cS30Owo0tPcxUY59XDkmmyumpCWh02bDXoxj3S95oSz3itbR1hQF7KW7kxdZO56nWPqMtSSiPz55Jl8TglIF9V4fpTxy3nRdkZ/mYg3TfYJIkF84AdYOwmDavtFCi0gdIinogLS3uT2DO/iHd6hobFPbUBd4QOHk9NXrurYQxYtsQ0r9nRN1o8sAYmuzDYj2w7t5mfpsvq6+QyimTAaKRnjfVbacW1NVRrWKEGWkZ8J1lJLJ78xXaBX7znnsQsB6+DWE5TTv/Xt+jg2XITJEkfa/Ne51/1WYQC8OViXD4TzWHeko842Jm+3uTE5O5LHrzvQ8tlqKZGhZfzHZj/0/9j/568f+fVWdimRMEemvtP+b6m1JaAx7m+ub3sOzkR7NZBAkxYVtFIbR4LmZDxFakPfcpceVBQ6AKKMf3qA/jrAiogBBqur5HHUlzuIEXEqQrt0KDicBkvfuGjCqsdFZxiKJij/5vEzXs77slepibk7RqYHYdYCgBaoTwHWdD8GAoH1hXw0vPPVoUB8fgYBFVfIAAhfR9YxVRQptt5SpXDyRuqf0p9C7c3pfvsDTwSoOzGq4AX47kfGzFxIK9cf0rZ4rUlrb9UtEXW7LQN+nZ9ocuHbhDI7kG3rXKvtNEcOpnRh/RFXOZM70zlWS6YBb0YksE0+stsYS43F7WlPSyA/Q0nr/z1XBT9sL/ndUsVv9SdssKYoljS0LgRgJqmRb86gxplxtfsLWGWlyRdYzrxaZIhg24cDeCHmmeU2g6tNpc6dFeJR2o/5Gu3KO8h1lW8aemJeYzX3ydCyIvb7nBAhY5DueP6Qnd5AM9eWQX/7dgXjte96OPRcbn+Qds+x277eQJOQz9gIQ4Vrc7nTf8lWeehnyuObnkqNXxJsA9AToNx2I9dZXLZm+LwUxR+6PY+vkug7zOBLlVoSgTEDRm5dTgwtaVEcEh4ujX91lQjLkZlyK9fTI4kTuepSQn6f3C2666Ur1asNiF08lVbQdmd5j/wlkl/p+5e7NY/+IFj/2v6UDb/6k2zLWph//TM/X+MSvvXb2JS9bIUhJB5SuqlKbdtYCDjraESkCSMsFxxckFzKBnRxejyjYpUkerk96s3yQtdZQgC05C/+dfaQn6REDAjmBgQ5C7fta9U3gggus7i+yDdKF1CctcEQ6kRZUlw6TkX2nPqg/q59of+vTUE3PAHU9TcCxxdgBpB1zxyt6VhsKew3nfBawbcJY98S47Y/UkFosoJAknO0R6bru7alYknn9PoHc/Gy7S8QrSBBkLl3tZ5n42cZiCrifecr0M81h2qGTCgkD58ZCXkxdC/KcBCjFLP1EH9wmrraiAAPn4fPnrJInssU5Gs4aKa1N5dpNn3ImpV+0i2VzTn4nGXFfU/elSLBjgOhigIMNaaVznDDq9N3SK3RCGxcdgOjeTiAwfey+/c3cOr9w7W1gsSHQz/6l7mbXhpSoeGId93o0xiUbFgGxsWVkZj+Xged7pwiG7e6048BqjAD7l/2FtWWOve07rEeZJBXfRa15LP1aN7eCAm/ulPYnxPy3XdWl/Sp+APKu6Zd+qL0c60lXa3+DgU/63zop39JtqvQRmwMnHV/NNmNh23c63+V0R2RPMn3s/7F/riFmM+dNF+lrSdd1H/jYv2OtVqCl2+UbpIfMY3L/AR8H76wlg9d4e4r4LKtvLqyn0GkFOzwh10MBh714gZyhLSYWaaiAzoEZkHKCtnwwd+8ZvQnFbR5TQ+tSKlGAeMFjyktEyRYUHOoWMcAD6VC58OwpYYcoDUDfdP/qez9v087F1JiUVycrBIBkKhc7k4tkn+AiZBspLzVdTP2kXDnHJhMUOBgWTV0fY3Y9W4DWI3OlnLen3qyXcgIAs3S0tZlnNVZ0wj2V5lQ6uJ7Tokn2nd+C6GDXr9DNrYJcI1atGlkhlo2grd96Ohn6skpBi3xqVB4pUeRChTZYOILRkTW+SBP1sxKIkKVMtBi26BlSpVg5kZsGAusqgMECCkVvSLQgd9uQSMi2nQFSOeuQn/c1UtxMaNLtFZlqwCrUUd9P5AD3pOP2BRE79VM0JUHeBComR3peaKN970+r4F+GslQo3uZez7LMIuD6kvY/kgPKY0pzUAR6eEeO5qO3q0hQblVoOFzrXk/feWlK+V8pc+nvhnVzja8RAdhoesodqHN8bHkbbycymtNRQE0nhkgex835lW8laT+966eQ4VNKH+wzEkPSTW7iL33pZ4NCBlmgqESFIkKYzcqnaA60vqTe5JowEaze/mP/tLNqCydXSR86XaM5kffu14z3tJTanPHH/jfy9R2x/4/v//kP/Q112Keihce+NZQgvI7UCNs/octA0vRShcCgzmo05QYgAVPQnSihXbssU6OYIhVNEKiX3V29T8UXXUYmhnLFJTUva65l2hes00CgucqrgEFWh8sFPgCtwDFceJAjpGFxp55npmq/eKYlV7Ql3QboF2FxsSdc2yNg1TWRiGWbQWAc0FfChkgSQPP6U2D7lPqGh+vPPZplgxPI1r+9D0saIhSR/tIWOEXfOBfqs8/THtGoFyvmdfU3TCrSs9Le7Pe0IUvjsrv1dY/snMhLj8BcqndxlZAZdFPfRdtAz+z7ktfQxrrGd9n5u13vpc194Fn9LIHwGh9Sy3rZ7GizkT+7D1G8VymDiMBQbkXu6jPmuG9YSN+q/oGcDQT2ak4vSTUg6PP6LmDDdRPwChD2xq6+oi55VmUnHPa4nQBNu9iSA4F1/24Dv82euFO/PcMnmUtDj3zxeZKPlhgFn/gZj08HvDGsWS5liaoZ29sqGR4XEdztMQ2F9fGTbHYgzXGwGfroeJ4iYQD7JHkTGFT/4eo8LR1npFokpfeDc7v19/DQ+evcVPClokTjA1K06mohz2i0zqt9jbZdYFO/6W+6vmvPspcmX/dgQLSFyXYko2k+Ocaw+eYbbqNZvnnK1xzUPAjO0GP/+yYI1hyfoO5nNC9lHh/7H1PR7jbO9g2vn6z935zpoUXBICa219eIG0bI4gTbAvTuF9O5oOle7p7CQfvRmMhb6BERc/jigvvZlbIgCCS5h2UEp6fRqeBABaACGdadAg7bmAi2ovMoCx3GhlLQc181wjRTAjLHPVFqWW3wHMj6rr1nyL4TljDPsAH/UV7eD9f2Dtz1Ykw6EyMJiIq0HVEtMOuw+iJUIhbePwEq9ZfzFwe+/TwKIM164SaQxZjmhXmjXBV5ijMoWL2u64peTNXB+FwlzbVxcL5Xm9HZPVUsSIvmZJBhnWPMJ56b3bI9Nt4jIKp+xDkXPavpr11v8xU6Bi/IwgQQhxFEKaDbvcgG9bKn+F2/les6+Slkb47MZT+122sdJlFNgmi7F0pvjTEMpIX6XoBWXCtwsoM57iyDy24LkPSFLdizROasF/tCJDuv6V+8luBtPFPhA7wlTaNcVE2tLTxcd8rDcZ4AUSjqhQX/mxzvog5DxGTb2yvXJJDc5zxl3eWcROieiMHluN3kw09zZxfWKP5ErkgmZ53c+5bi3yvfbX3FBob6Kv8t5eP3ATaz+2XMBKP5rCFl8hAthVvO1MZpU+Huu1aY4WQXj/1X3yJ5xHw/9h+b5ndE7LH/3Fh+Zf9x+wX5AAAgAElEQVRGetoCkc6Q5t/F+uN8nh34Wy1OfX0RdpvatWYydSZ2UibwrJSy4b0ya90oqSmC+RpjAkXBB0Zk8q30tZfbWYq2cimNSAt3TmKm5pTncGccj9pAbUS5cFDdz8bYAfLsCAmOfjGgnQ9I4oc0JWT7BNBvkYGaOkhZ1qjJ9aTs1xe9zDsIhNb1AsRjsa9RB31tMiWkzsPxJLIGtDnfCbxzXP4dvTjJIHaCLLLRoo4+wWrPX1buIN++LTK/IcEZheNBfpuriIA0XddMJhmvqX0CJyIdoYcgkZ2c7KTW++DTvIgT5uh8fkj3ecSpEZ8aqakRFsnaullTAq9n7+NNIkOHabLvbZs18DfJUXJaegtCWjdINCe5F9tB37TIrWv8QkYNJMue8sY2qrbTftUkQLtfPAXwldoW/mC6NtzIDJY/bdFzA+ZyLNMSIN/agcEJVG5kwhsN9+fCXNkDLtcpEkOy1Mes6VjfH4pWyH9XWVaCdJr7qz+Lw/l0pVxer685BUoxbToHYl1kykzNomuVuIkoiXxzziinmNYWOYP5l1VHY63LoVUYlR3abymDSV/0a5/TLfox3Fy/ujl/AZ2aI3VVZptNH8jgRGJ9D/Cx/7LyQ3N7imPX78f+A0tU0tB8yXfA/i29TZ7XQ6A9TaoYY0ltq4tRN9qs1OZLUktB0/mAItZAOzrL0lD08mdaALkcTH3B7y1Njqlwvd8GRrJUM1PSzLHqPfN1iS3gDkijR5IKocHCIUdPgFSAifcrgTZ+9cVPz4qzKA7KUmTtvBOjNeXcToK8Y3+w2K7nYjUwwEcQlstVLNKx4mQqVx2vwLgtYoq+1WIP1k+2mfNQCaf1Kc+tpIxiGQ4CYdPn1/oDNO8s06xnd3I3pb5xx1Ty0ZIZETEoUe+vIiCKGjBiucTviE2H6TMalhOxtcnITJAORk0yta3Op81tB/wSWx+PEZkcnEV86gaBjgxJlSppcXIHnbNn2dhI2tYYQ2n7OSUbZI84lftFRCN1z11kKKc9r5CWkmKXYEb9yBTC1HCVytY4lXYWugF0Q6AVf7dzdSt9LZxp2ltKI88wrKbbQu9HHaODaRUZKZlKSlOvl4561TL3+mXewyN79zoR4zhl2NPLJ7e1RuPxqmnmMaSj32xV6WLD63DeYwfDxb1tW0jT9b2PZWyIMkhOmqd1HfxADATR3R8r3U02hAO/Y2TQO5xq2EF6J1fyxTwRJ18yV+za5hvpZhwjtbmvkwJpofveYRW4UAr4SBZgBzQdkVTp0fZ8GEfvp9qZnse1lLYQz8NJgalddNcjxqllj/171UJ37o/9V3za9VE+y5fiqIMV339H7X9FesIpKK3H38VCl0ZD7rvgU9itlKamRY+r1T15ipQ4AplTO8tL9fNJ2X4fa3dCXGt20N7OuQDExaYt+qX71WYnPwFK+z38jF34HH4CvXi/CSaIfRnBOM7w6DYCnAST9sQOPnPRAeBcf6LymkctuEAZmK3L3ALNULQify3ODdza7PZ3xOSCu0U0SuoY8rijrLBLSSAQoCBk4R40iMW6Jp9ZdCXuR/EGFggg2MSKHaQumwYJS7kZKHYgHm1hLlABTwQpdJB9KyW2AdT94iDXPrdbxAXyGiNpEYVy+Trztv47IHUF0Rmr0BeeFQog4O9Yaql/KY+0z/VdSa7UOakkBqrQJqeve0Lnt6IOIFBT+qHLdo9kGV22sfm8MUKH82Uaf9gBzSX+dpJ3SL8iAC+EBPaUILcC1xHst8VvuyYAfFkl9pLDGtSABjUHM9DbD9RHBCa5W/iq1YvG/2pEIXXdTK2C+eqh0v8JRKV+4N1EIaO6amrm+4ZHX1s17zNQbnNU4u217yWNEPYGl4LV5fWfXRYau+RpMqkbaXNUK8fQZaF3Ii2d1HxiX3MjEa1T8XHo7PpqGgR8fPS3XFdlXiR1XdfSJPV7t42bR7s/yDFfJeqvYjGF0D72H5taYRvtfWNFRNKhx/7zHYOunLNvufcB30X7D9IT4E5uJ2z+xvhDmD2Faxfk6V072nHTHXfv5Hntor/+iul5BBwC5HKMndwUUtHPuLQzJ5JxtD+8h0OLf4A/ApVw1IlCAjw6eWD51ngOwFaAKv+ROMRAH/6LziA1raUN6HyCIhululeTx2qfu4stiBe77RGxcDNF+pBxhDwkv8BkiVZlZCJIiSZtqkyG6CPPYHAnOCNLAK9bGXODFYzClfGgAt81hnreJGdhgTW8i4a7LiY/v3ZF9hxIdznHZwdEOsiMOdcTKxFNbdZcxWLeomk8T6V+La2ETvM9NesavZDQlGbJi5XN4plXZUW+46SdE9rOBYkEL2G5friJSKeLzKVfAdicIOkeN6qMnKGwRCPQlQTXVEPOl+Rd0+bqubi0P0s/jDniJJwIQwHCNb+5AOppFcMFp5/Zxrrm7kKXq+yyUqGaQNSH807EhACT0anwXTKRIaWsd9s+v17jpilgW/Sjw+oHalY8rF0KXTWraPN3ePkon8lWi7wHYgx3GP649/luHjTurgerDyMRr4TnFKWKdtUG/dZU8wjP45goU+rKXXRsGv8W+cTaVeZoKxRRsxyk41z7OpE7kaXy/cHmJt3rmx1x62P/SNCX6T32/4tk/2+UrP56IrHuYBocU8u+urmTQr6vqF/9SBtAowoGUHrUiQut/W1e5Z1zO0lY0nub80YbPkyV3aWztUv9WsRQFngKT+iEVO0ElHLALCC9PiaIVi8ij57FI+IlnUxfu0n1837mwtyKQyS3CQkS3F9dnkgCF3qlT1ErrgIIXKCtG05FPJpZ5okRFORKMIVLqpwL9Jw+pWlc/3IOXRk3sBxELiMgqxS2rvcJiTHr7E4A/cRPcQ9QiI2hkiTJKvQl5qGmnRV96tEbfYaR1edndI9jznSyCvyld3vamv0iMhBlr6EE19m69aLI1hduTCg1MfuYVpuWly8W5XU2l9du7OcsjuHzm+SRqZZ72mWPauY4G/GSri5Fs4lhCe60Vp/3lhaT9h9p/iGVintoMdWnlutgSOVdP7Wx4l3KBgeuG3jb0UVP4Ls9stx79xsvZLsngM+2+t9w9evPAmLbxdrIiM2TTlDb50kY9GMBovt9ODyvpSEiMjj7ozUELr/F37MH43OhayIGXe72OddQznn6TlPtvpJPcpcr60Qkxik7xFwoclRQbhNu6QuMatKjSloy2qr2T2Rzenl7l4GZeU8N3TVBmOGx/9llPPaf/p4bplu6pkfETj5J0v0u2v9cvc09ZY+ClM/tbE8ads3d1RmKQOGdDMlruZSjHfeS5qBBNmI151KD8z/RnrkgkZRwrChrrYmdo0s4WdkiIL0LPeWppv85iCU462PyjggAqV+s5LYRKR+npXFkWlkB8eEBWEUtt6uYapOLeBKSS2YqmGCLTroUpql1gkfiZrdVILq+m2QaxMyuSMC5J1CqP5E97vrCcxYCybb7byEUzR0jRJiaeg7JO9rllG3YTCmFzN6x4d8xmlCiA4ZMShU47OTG/RJS3Ju9XJGOIqt8puTKku2aujJnTLOibjKqhshYAbBxCL8R5yBEJMMDCdmiGEz/Mj0L/djSJCXjPJcjQJTDyIgb56PMs6JHgSAZPQzP4FFFG5juj+gU9LiQab7Hx81N5wTXuOBANM66AbK/lNYiiCKtOpsWQQJ5z/aGoRkY9G81rtX6m6ihA9PqjXPHv4C7OHVvT8zntspaW8WmGTzTJQWQn9D1elqH1lUKfWc8fvVO9oj/Ttw0hhviGL7BujORv0n8MymrMunzsYthr15WpHI86O3zRPTjAD0ief6wdQmiHmz/SNSGeVnjvc5mqYiE5AaSJynLlEZQh36lRb+2iWIPuLx/f2cq8klT6twxCoNxmuw8wtx0ZWs7+nhTVOFm2I/975Hxx/5rafq+e6zXHywb+Dmz/znSA9JjCwvgxJCS9dqNtCs8atDX2AT9NENClfvFi++suRxyOb8TKyTcn6+USXpAlOJ+7yWiVRU85dhSTrnkBOiaIkBtNevkKYFBwtUEcXUdJ7hjUvDagVY0oE1Dv0fXCeAt0jNMbo7JiZB79nhOAL0KNbY+Dud+NE0G8S5yIMDpQKkd3iZ4EsEmeawpZjhLIjAzRbdIUpDSQLJL8CrDL2lXvrKRMDGVUwvXage5QvV9PCb8TW5XNAPbiop+2HzZUrxHsIycRfluzGsvrBGkzqNPtEvrC0g0CYzrc+mv/EdUSJvvvVrtqV8auQjDwgAuEKURyoQke5sX88QRRWkEVKQl+oldcImlE3URzaJbrWpb6m6is1Vim/1Byut6Bs85qe+Y83I+6LpXKYJIYQwdw+7dBtA2IuH8xpHb9c86U1B2dM6ePTwc8usTjCXwP3lyqG/ZaAgP/8Z5Ja4fpgfNKLgY98XmNDSXR+hBiabshEb97devZiBbjmv9PTEeb8RuU+c1qXXtYze5jJS/1ffrYR4BfSvFa8jMyD65H8b47kS5R1tmAtvEHk3q+2mM8RvmiITi1KacKonsWdP7L41sQu8mctr7wM0jm2Fvr+28j/qkL9v8FBl5k4/9p93EHNwqRDG+Tf/si8f+tSbTv/282b+RnvtNsLf8wY+TwfazPsfz1oDevEgLPcOEQqlYHsoB/hU90aaqogOnd96s9UouLLxeEJm1lgfwrFZdSYDdm4C05kYssKjd/2gm34exQGbrRoxd+8tOIvUcAZEdgCeiyf5k41q42A6nI/pCAIkLsl8+3nbOI2TWzxe5Y1uyaINNgNrkiP3qAlBFALASjhGvtTOq6I+NWOMP8sGxsRiECCFBvY+VqYohY6SbdGLIqdW7drw3TnJ83MM5LZFkRpIsQkjdlEydyLYIYMrcoIBklX3X8/1fVI4rugH5WJt1vqx4gbEctV3IVtiAWy/yg1afGunubcQ4WtQv5tSVOu0to4NTAQQjmNq4yJQafbee395rZL7ozqnPv0lWEzAOYNXA7ogjSppOgsToVQA8Uh/NIrWozh15Qn/uBDjl8Fj9LarLqTNyMmicY02Pm33JM0BJ4KeRyM9wJ5TtjbLzC/gbbbM/J/qqi/yCPocdbHd59edpg6Zzs1HOeUTPPWgNGJKI5Cy7h8KDJ3ls8wxjpxZ3Es3xaz3INdf8gjiy2RAib8MG51QBrthEBEkr4Zf8Yk0r5Lz6tHVnO7tES+7631V4Ecy46LH/x/7TWB77RwryIj3x31ewn36pexDgXmKR+e+T15+6wWyz8CSHR0Qnusv260/D/IrhF6DVukHQGv4TYHRLfxMZYZoTkT+8GwkTd/SXKFvhBAKrWGzQrnbLNWw65rVgu/hEnnimoks+wGlYl2I1iDRNgJGAHaSMi2P2y9rMEyenMtYAiTcluUWeIjXiOjsE8GBngjJiKHyEIbpfNUVmpM4hczBLA8v5rdpS2JhgOmDfwubZNhfZJLRwbPEnwbnf779VYmbd49zZ31i4Wypaj0Yyec30zYnGEAVR9MVASKawCZRwczvG3chR0Qv/INkGT43y7Q4qQH5C7uxrPx8V0SLIBn22VqF7Pkd7BMnTUxTRGcjc1RIjhEHKdH6pFKVQWijSNH2qVInu6pf5iBpR62Ao1NzTka4KWjzzdQTNjMC0v3m2YfMPp0PgARZzruDuoNHIVa/7NfkoglUfILOpP3nK1OS7pyUl8WPbXfG1SFHwDmz5eb23yF+uNZHKjUS0NXFfIs+LlF1rv89La6Y/nZbePm+yl438DHMA13kskFDa905c/yjKaM/Zx8i2tbxHhb4WFXSnHO9GYoskR5uO8smHyCiJhWTYewtAFaM52cP/Z+9slyS5cXPd3RM+jnP/F7d7EWf90w7bmp4TJPACD0BkVs1IGkm7pbB3qqsymSSIrxcAkRO9Q8cNc+i8e8t7Yb9PMPSS/1nfcO9e8p80+meS//e//8d6OSlViosVIwb8PQCFi3wBGPjO61G3wmH5SNGM+G1y8ukNhWbZKtKLh939CJt0ocrLvW37Yv5m9qlzj+yV3xq3HIWOH0cr4aKkQEc5fkGm8DJ47sSZrm0Po/RmlGTkquMdyhMm8Db7g8nGVAOQ8UwBVAPOfVD5b2NeThike6xt7abZHGYbe+9k67aW60GjBoEC//EEHum47nHLHphHn6ViOs+UfKTL96z62RsZbf7LNcDo0HnQOSk5p3G4Huxb6I+zSOIX7cBaj16uqdt1Xkm8LECla5lx2jTv++4bmb852LRN3TfUUjSzzsqEaK0J88yhXrdO5Xs8h2XPxL743KLETfzukw6eYTlbAZe5pzwTk3MUkEjvu4Ao30M5uEGr3mSCIErgBgIR9+cmeSlizb7GvjZZYCDFKGQTE9iJUj7sJ/djOkAS7NYPUKPkDonMzE6HrJ39aqoTly+NrHqw1YFD6daI5KOzPvVpopH0cwE8C3t4lspkuZ+pwWY1ue6OMq1M6Ch8ednZDnS9cu4lR93JoP4g7x7OSAOXpvN05rW+P0eKdltUYTMMCFYdzx8dc8INB2huIClM2kXzBWMJO9jv25VWbChHjWlzc/g56NB4xryJqgMFfvz+A1ik4IQ9YTZv2jvars6fdUb2V1nGHfi5K7FR6WmjQ5/L8W6tl/xbMBD7f8VWL/lHMOovIv/nmZ4JxIRUtsYA6QFcqbRJnmUyLS7VshPbPUK5xqAC/P5QRcM4cP9cG9GJnCaV8+gaItSz3UYLuH9ijRTNhGZu8wxSIdqbLz2tz5BT2suDOG86nXRM5OgkYLA5TbG/AEA+p15myAg0lUACBcb5zUjVTJSvG1kOI5/ThAAHXGFk9fMOBaicxklrp7OYzjYcQxBP45eyoNY4gbTXrtpxiuVE547WrnLJmttxFkhAVmCNu9oxZwlSzUqRV9ZjNlBRiRNf8xhKGUDX6Uz5Mbr4PoE9NXalmzI1yTMCVLTEtfzN+Zxld/68yvPaO80oS982GNL+I/jRwWBmy8A/vQ25OzfKMyogoHK2fPri1WQK0kG0iRJBBBaC98GjJfvlNE5g2c7J6NyMe/f2LC/ng245sj8EM36PdlZOLQElM4SSy4vwf1OHKmk9tWRoxljjeTalacsS8a+Apo/PkIl9rmP5d80B7Nrat8XLfDhmOpIhBg8i6abv8yAZtfsBNq6yT22ZJduzlUE9s2OBicqX39NSW/qD8zsyTI/2Tybthj6ke89WST3aOuoeaGVXtOxjcT3TPYyVTgDwBLcnX5dvGkNJtkJ73WQsc5za2e0O5OzHseV1y1aqBI9ZcAKs4n5096Tv3yQsBzle8l9I8pL/UWD+yvL//vd//GeGb+tuN1ddP85K7IEqSW+wuP99TL1YoZtOqsrnnjSgkwc3nkY3bmD90zhKjwVKVef6jEmy5KSWoKS6DucrHOasP1bEPqgBjd/gR5YZYVl0QtcYWUoDIwsg0oFizA1aOp4rcImzP5FZ8OEFFnIe6bwmyKh7n86on3spUXwbWPOkUczyKM8aYN9KtitAsdzh06GW3Sb95KzasNlRLBxmnrOQx61sQBgnm3E/67Ofw4YhbhTDuXZ2kXNhXeM8QwiwqD0uEnuUGGIO3mAkO405TSZA48TY620y0dfT39dj5PDnoowr1qy1T13tSpmgnOMzw2m0UQbKJigQZXTJclBbQ30Xj5YU161rpgYSZS/IQ16+563CBYDJqzXrio5zPjc9O/ejZkJDhiVfLLnzTV8zopxTv3QfCBi95LC7Mxky136wP+s5F7LG6Bz7BdSgh+PrEy2GViBDv/m/3cGsY82Zo06HnPN1N6zg+YtzFHxu+YwsQs+GpF6/bnRwPdfTMFGTdtqlZcKeXQDK1DMtqotBRHeBjH4PdXPJhHHfaCeUiWtR0XuAlfZAJvsWcBQAA4FpmaSJ/zu1p305v7sHgOQ7atUJ2JW9fRAI6Py3SgkjsFrsYsbiyn6drHU0HzEuesl/B8cwQ5s6Qde7gMKTwYZhW8pXL/l3FNDomeVtRbm4cPLdLJufkZbpn8MSiv3hCrHO4NipYu7SBIYWteeGunAJzp+H0IvmNmm+0MacyD2Qu2y0cJda1oy70kahAB0gkpcxzohYj40OcjczCl7X1c+RlG2KeKo57aICzxwdDi0Nk291iXTTWW+dqmpXOc2MgK8/jYCGVZhq0c0ytzwob8olRZ5Rb2thbp7/8e6fixIpRqiBX1zxr3k5UGq8vZ1pWJzD4Q36ubEuBijpo2ImG882KgGknl1h7762aVxmH4QUwwkeSjwSnNQSrD1s+AjDizQDbNSSxgJOW4MOW9fZej0bDziI8ODB7ooW9Kod0vJ722sFGBbdFpDQ/qMCN/ZJNO5jMOBAAJP0c4ClObH8bTjLFnvBskCd+0MXtdxvyotoMYC91tUt5s39knMCAEmDTDbuEjkCIL+Zap4RaumWPYVBZ/IZkya+Al3ZnbOWA5W1NM9T70Lp9VodHCXHt8h3zP+caXlU72qXlg1lWqkgZZZosg4Pxi/vGRQ6Uj2z0R3m7ojr916Sl/PJdR5gVLag13l8e3sr56fofWUsomnLB89xGWKWIwCNXI6QvWwIIJ2TALOCm1CRu506NYoNRu8Ewxv7QK77vKCaeh3l5U896LCfj/K/g7/8Bc5QOTH2IYuge+eBY120Q8Muta/czgylrrJVTseX/Dd+YlYPOnTcH9j9l/w38Pid8r/L26oehwnqoKdzexcIdCI7gQKMxAGeoDWheKLwOrxOn9stiLqZZAFD3dSS6xAG7sM1oz0CIrdcdilgJt43FMBiv7MlQwA8d0FtyTKdkgXojrUMUfHUCQJYcsZodxU1Oex6zxGBkMzBmaGAddNhctSfkeIanwo3PxeCFOeWpXoFbIWJOteq8w7rkiwLbIAJBjym7N5Wfw5Z1NaUZyzCyfAmCM5VdpUToALReuCcY9V7YZDZLQlnkjZNkUHptI1zH+LLoZ5eq6GTL5qLQ67O4PQzTxXwVQBke5HCRfAg1u2/J9DTfZkdEa2spbNpQQEm7p/GkGwV8FSajSS4tLkpm2ZU6PO1SzyCWs4wnus23rc1dPBTgdiQEfPYU2R/GsCMefkD9Hdp6Q2dJjkssukbfRfhDm0BtVEczIsIvS6Pf71ktOcL93yoOofysTpWdpo0T7Xq99gFjXkbsBpK4VokO2kDZ71lceTE7nmGLrGL1jmSfcaIezGu93y/Tt+XqrlTv3dHPwCOm5s9B9HfeV9jB/Ue7EExj00vuRh2C5p5bmwRfZC79YkvXNxi7M5XfGiU6Lb46Oavj/WyYZdwFZoMM6YuJY2OuU5ResoI+LrvWz7WeMp+T9B9fX2dMOWwy1F9hrS9fRvresn/kelO2vfs70v+KYSb35wkf1b59/I2CI0mzIj9jRII9+M7r39iyN/nkjNA95s+5wBBkpbhufunrhB9NunwpVrlGRE5TK5Di5DmbwketnPjrlpOpdYeMxKfSr6G2OTwagzzuWqJlpakA/sCbPneHTiC4bSlsEjdp0r20iN7vygc+9y6cPZEEJ0r6U5ZGPd0ijst+be8lQ7wwqn2KRSnsZXc0WBGdBBR23BIw1rpvEs1c3sPw5lLB3uP30rG9vz89BRpEwAwHHxkcVDHF/dof4woxmdeuhfgzUu49s40fiZ4YsYtaBKgDcCRBt/5tgNNGXPNgeVqAg58nrs1pYPeAUJ97rl20sZmnOupTS82j/uNhRcQ+OiBA5PJWp5JPus8zfK8PQ+A8lifT4KNE4yy4gf7d9/PIMzF/K+V42nsNw9Gg4B+NiDBfdgMyc5QIjY5ePyu/344d/0Q/PAyUMajzOvD6WXXldM8ijz7H/FOnpvSlFBNdDB1/29khY41DeNqTf3aO/pyGPHlM+CrgDonQIIx6O8buj16pQXnk0EnI3IFKHP3OtGB9CA/lfHbGggkO40IIsY1T2NtPkzAf7efBcRC6exnKWWIkGHOD3Rosrd+6WB8Zs2X/FPPv+T/JttOH+lCzu/4/DeXfzvT0/+jh95jPxNqeBJJ3F529ePwfRHo9vuDCJ4Zt1bLc3zRL3hyfcVwTuay03IxijVzePv2uT0GOTLmYGXZmTlyLOkBoAHHsFa3OK6wAMx8RKmOK+BgBh++Zg740ku7oDhmcGLSMUDECmzGbMfAgFir0YzR+sigIeIukTMnso8ogJPSl+tKZ37vmFsntna25yfY6mvT793olSxfy2ysa1VqlQ0tbAQ60FP2pGffupNObrXfVA6lEO9UFqVne65pKMmKfWigx1kneIHn1SoQyDI03VNoy7NLLuObnyMCa/JwZN6gVPf12H4DFvVcl8BG7KPO3ShLdnRm8/uDNwjQQTd/bpFT7xp2ZPD8mdvJcN7qDSg6ADJHrIFDVzMVXFZdQWnIMdFgwX39oO1U5umDTM6bUSBLTqViQx6okxVDOS5KOjLDU5V1PmNrhSd0/Qha7pBM0VG1HKU7xZKzD5xjSp66PpeT+qNG1/Vy1T69CYwoe5FWIOk3We+JVtN6cvnV5m3Q3Jss+MT2lcjYjXSis+P71te5xtlgfXCMdO2jrQsA07NuHmDke3BcNZZ0Wzwn1lbpUAASeXiYWNLhsf9A+kk3ylsAS0YUxmxVljtfeWmbp6gTFxBEgDqfcc6x8N1L/mtTFriRL/n/i8l/gh5j+oKqek4+BK06lcc94XVAFI+SNowBEDOjujulMYAeeVIyjPwbGiScDNfaciZNGSIXHu7cDMC2st5r/nh7f/98+/a5gIyd9bBWoDaWWmPKGd4uXAyZLyLqLrudGUkVFE5uwW/VoQwHlQ5hGIKe86kOixyow3n2daSTnZqwO6JGE384zqCYQncgwyjU0Q7az0iIl6IzHDrR+W8HvYRp2+/m1InkGl/uKEql5LyKV+LcRXYWtEsIQpkFgAMnJ710JDTHNaKSMd8c0cbPHUin1vhAPCRHNWjgbBKlbD5LNdHY47hYkm6ijaxq6bAHRuK9YesAACAASURBVGAW7mhbjVhCjme0UGtlsbFAZKqKE9AUcC4+9g3k+Oc6tDu5Dwm+kn6Uo1ARBAADWKJc2TujemGWsjf+HN//eBYybODE8L80D85NIJgZVcpg2dPGuzUDhRI91+37XoG8OGcl+bTFMcN065AV76z+kdor92R06mKfp7bWZzSR84G6CRUuE2DaOUvKNB86scVcDOV0XNF+VnPOu8N9Qw4Tg/0/Z7csrkM6ghbgCnxVjdHOD8AOhFWuldeZHBBRpb6xUXq2AErdQ5tl34ekA/QZtRtN7QXdRa/+8k7aKDfjpjnl3JfueNgR+Byk5xARheUFEUVstyk7pohyud0swLOncn3CnWq8E7w4rL2ALCKbO+HBMuva0q25403pIIFwu/bcu0djdHnhlLs+6GNNPH6le0g/bEsEv7TVL/mv+rPQuMlgc81ie/4Z5H8ob6sH+m4ZuwOZlJZWUz2PUiIOQ3lcgJDwjKCFoQ1iHGqX6fc2jUdpsyLsAIAZkfVEsGvkBU62YLeCbUWaNZ4UN95bt51gq7E2k/G+/zBNWpzjcGq1mAFA8C3uUFd0Dm2rfDB3dthdLhxr1yLpNKb6KxkVnOfqTqjYQtmqoAP3VUaCdqVpxnD0WzR/jyvW8EjyVErVsyaiYAIK+6Zmt4wAHdRJAadjLifX9ivqyDFe0ryXlOm5/m9kAarDSXLR6bXubXmvyoxifSX7pYhtrmmzWXPe5Qg7N2b2zC80Z9/XrIkhgivWErjJuVRaBg8JFAqQtQP5onfwks+X+0IgKKHZjkfQZqBzPK//5hm30BkzKFOmdtMDgMM+Z3DBfrcLYj7iW2Vv4LXpGssCYW9bcIAyuA5nMtMVz1TzDnQKLGOiJXqeXYqFb51k/nltv10Ag9MxluB8wsxQsIm/JJTX3gKgBwCEfD469OP9NaBg2fZ6YXcYkyL3n8IJC1CRYOCx4ze/26gWatXnH/PsZ/VAoPD1tVSsmWLMJ4Ru1ZfMHuCl4bnXXZukbN2tv9vjnJqXYw3PtZGdvo3eIWu+gOssZa7WTXmQu/A4qsaCXeADNfYZFz1dMwEbu66vq/Jo+AUD+BZVvIzkYNhxri3W+whXveR/1gMv+f9zy/8Jem70uZzi6hzfGYB58WH5Q6ryuueAyLPmJxXi9R03c/SbzvWqSt5K0FZ2Zzue8lBKQwd5LFlgLEd5AZv19M99+8oifJjn5O9e1ZmYtFnf3t6/fbx92yFCd4bCKTrBjylFHB65evcNDR8drFCC6JYWD5ZOH7qqFWKboxQUY0e3xgjhiCnCDoeUxtr9Sb+boBBnlDAHRrunxgwCPXqGxo8SPDiz65rIYDndatmhMlm+R74H5Rq3FtXxNF7aINdRXJYApkeWmZbsmqZfJ+dY3y1yZNmZ+Qm7BbOLCK+bnezacIGOUM86NRbBOZgzk1WAAAHV0a0wI6fJJ+lX7GcCVJT1+D7tgAJBROGRzHjk+rM03vYv+Ut0y0yIl5F4GWkBQEWmlB3CGSA7qGbgumSCPGsU99sHll6u2/b/U4addvyOba/3LoAvbUzbNVt70ro0VvArOrijbNrdJ6AoThaj7NiDvA+MAM/r8rxkwyt6VmTG2rkF5rmNb3Kf83FX5x6GaHlZXC6IX0+XaH+It7ojqfbCJrCWps77TqeYY6bdYNanH8T2qy5KBac1bPCLw/8BFqUPUUqmOQjgPgIeel4CucHxp30pfF9Ba+wlmr2I9QIcu7Jaf6+mBiWIcHPO6NHekvZbJtDNL35zRXPQpDFL0GTKBGn+BaH4HpM2D4A/dfYjsFPI3zMEL/kP8jzikZf8k5PSlldd8dvLv7WspuYP7qdbI8UIC6F7TKIzSsbwQ6ypA4vHQCNvvc88hTJBu9vr9Vw8NxR+C1sVT2IpxVV///72fhgfOlz2jG3AF5DxczkRF1dIkYd3lrMRnfI8yKRnOzBKJbphUlFhdEJ0ctO+S/X1+e0zzquULA6cpnD6NSAcoCiP8onkM8+XyZoTrMh3BWPuW0WWIOeb/FaaJSDDVsqyBBzo0ILfahbKFlkdYWd7599yfTiWVf3LWVLFRDrHWSrnJHMWZPkbGg/4jQS15rjmDPd5Lvp+eI9huRZZLu6JOa4mr0aizFRo5XJwJfLMukwlTQI3m5Z+1oblagyVF9q4RAT4RDOJkokRvzXwsp6lg/iM6vaGBbbcLIvcdAp+reWSRsP6rh6jQ21IUHgmAg1GUY3PfVOTgL11cfbJFbrzqmEvHwNrLetxvj+yQwW0nc0T+vol66Fu0KqdfBR0crqwSOmgiQdPct0AxIq7e6mPGbCz5Cmd2jR2vVysOFcwQV2L0ymU1uvji8dP53uyCQkwUu9Sm55ZoX5dzKmZ0XC2vcwWKvowWzam3+E6lTQ56MNyNWj/cL48YnOca7ka9InvY+zp2sGFyMsAviIzdf1izGmtOKZ6Pv1iYuQV8sIdv5RnT1kVZyGYqvqC2WFfpucZA4DR8eC4vlfXFFudesb0D6ktRegBr4vs6ZUsbU5kRUXwJjVkdQMXiHzJf93Pl/yLJ/9A+a+NDNxhv+rE1kMSJSwFCS3ZBVrBARx1zUPpgjLJMiKaFzcLnC8BjKzooBhitiVMmQJczveAb9MJ8SYDPrYdSP54+3i394KEmxmlR3ZKYsEVO9uzrllH4BRXXU7ut7eVNNo9xN8+3z7evrx9W1/4VVnEJIbxd8TIiZSjJP22l4OIZTlw61EtaN9waqEfSffuusiZEcmr+kvHLg9fy91OR8OowhKkaq10eDYc+tAaRhaeKzFgk7OwbUiHjMBHM6jg6OxCl2OmBaET2LlRjjBdKdGwOLo45xTs6byynfzoiAfQ6N6KOc1mIfe/vuT+7NwfRTBxbok8UrJvaXV1/34KeKuvxzIHCQQQlPYsRKILGz0zJrvDHLv/xZ4hkMCsD8BA0iENufYr9z6BX80qOV2ZFSFIUrZRQQGpsQgSpHsguu+tcPJt0Kh9wXkuJ1NmZwagpUSJZX18bQJH8EoyK5Tn3HLPAPK1xi7XpWHBmSkL2V9y5pF9Vym1lM/HVSYt/CHPfofZwHmykKZWikUHs3w+rqMOcWhwk61gNsLm43qnO7HhDD4XLZf8ddNInu5Aq+hJ38+m1o5SO4h5cTf7fd0kX5ls6i0bA3q4zWnaE/GldqE4czDz8ZyWTaOWp6MNP71vf8GHer6pptTxnE/QIqyxyWQ8oz0sNV8+muCkjndSZfr9KNnDYgMkDQC1WkHMB3qINiZcHTCK+Hxj3fZOn3P/65rFEuN+SK7FNiSFGpVsx8aqX17yX7PDL/mv6u2ny//cve3S9Fz+IJBgCr67v8NtFwbq+598f8ejc0Nx98q0NKe5KIZtdLPAbzV2XKVmC5xEFNpD84ajMmq1sz1ekhbPc+fOjLErY0WGl9OrDI+cPMc+6xzQ/B+cOFggOZm5FqrKoYvXMHhkJKJM4CgQKU4ch7CMAQADrVV0XyNUwXEwgRrRCrwVWZBuuFpZlBmiGone82Pmx61Vj77TMWJ74HDa4TjlgeQJONXsA/lqTwXWNsBdA6O6xErgbIT8Tn/VMxeZOahGs8tndcQBevw2Zg/0pJBwdRWEQ3sCRS/ZQ9S/gEI9x/8tZWh+YayV+gUgUSvMMi7jO3NKbWeNjVZpjguTO2cbKDQ+Ke8IioP+kJesSNuPLu8uat3fGEwgONScbIqV7gFetH7viWqXMXtn92ndsT9dDsRn2i/+rc8tgxPz66DJRToBGbI5yLgfdqA7Qcm2VS12HdQ8wG4kexmbSFkPYqfMUGVUR/cEUuXRtFlPgLUfMebKzAUug2xcqv51DeYmPs8sbyXoI+d9MAH4CuCoZQsmgHBucXHVx0clzfN80zO0DL3KTb1o1tBpWeaOOO2UTSKwZHz3im4usl5pkb5R8ul8Z6x5KLO7o0db/h68gEEHgbX2gVfBXvDZlF/wf6fHJf+85P8gTVNtZrN2ZUMVrvO6NtRL/tOGPJD//XLSk/nv1d4dU9eWGSngcc9vDnZcnH/NuFd6GHW4W7DXkZuvlqpZPoi9G2QdyPl4+/bt047jrM9eIqNGBRuoOCnovJWXQC9AFdflG1a8NUI4ZTkUBnXAZikke8L6x4yfPxpSkw7S+nLdBKdLDnUDBXtURNnXZwO4tSRIAIO4l1wQ66enj3MMx+++AF8VwGQDbAH0+P4SRcnl8FiZVC2zag4Bo24sQxJKgqMs9KG57UvCmupQObIkEMb18SgPgiMd4EegsTme5kxkBzhmh5hJuaR9aX5goyXtM7tg3wnAexOEdbXL215H4Zk0x2U84zI/r2Q0sTNGyGb4VhD4sUSDL9gUz5aGEYgmyyEnrxvNYdCRtSnZpgIQAC4J2pCdiiFb2ZrJTMofZb+WrWWpnGR3/auOd8kLyPowS4NFKchAOeo8wEwd52TPpMOTYNF2z+WIoG7gWQHATW+/hy2Pg0P8A0TXuU3yavnybp/ooGpeiPOYmkI0WnGC4uxDFu+sHeca+hkec8wF+jUloJXE6UGQHWUqDhtc7FmlAR7VQkqVt+no5i8XYzFCPxCk09zFuS4wUheuES/OxBz7h1K/Z7Zl399kK8CJ1oHf7wBO5x8unXTmdRMt6rUZ8JzAFGXpFshyMjTk/jDpOOsU15Qb7o25QVaffq6CRRj+yFCB4aWvI8brD3rJPzbkJf9n+SfluQVTujr6tfJvZ3pMYlqSVdaqHoQ/+v1LGMN7afcdgotC+lDsV2dpcC0NRkRiaM6dajKzDMFIO8QlNddezAAciKSIolvrG282gDavkTLfjtznblP9bWeBPMISmZx0uvZX2yGyEjdzWrEeCIaUmmahhgdeYLefszpwrevYtWpilvjucN5VBpdnP9gVj1FbOj82Hu9FFPo4jF5Ne4xZHPCkgbatAgBRVec3jHXpYGmNkbkJpxxjO8szg5Nd+WwERejFHxvghcTlc20oN/Sx6/UsQ16DdtMCqD7h4pD7QzuHKwOQ2SafaxilsDLRuGDzR3tW0qg6s9IE8btoq8mJltp3jKsyOPuqqhWBcD43ALp7MBUICBSabojSKb9W2UfL0tiZOEW29YztwON4xj5v41FONgMI+gjUgwgBPNoebX4b9i9kwzio8GUpAWR2yHWo3lFSM3SZVc29d353nUseoaqzOfosBhlZv098EffgPVjjdbw/ssBVxicANDrQeM/PxiwH1OnarFqswlcqIW5ARJrjEpTE9VdPb4BhOKsxlTMdTqLvS5T90F/d7H6+80h8K+p2a813hfD8JZ3bEGEA8TKHTvdmqknjtc51trXoJ8kbx7kCPsMeH+CkZ5KeySytBbU21YXfng2O+sICa7Q920tVtuN4ZgU9olvng76HBaT6PNU0YgLcZqOSKAfIhf7bPom8POrF8JNO+bJvnpFE3lvPZYlOpTph5In6Dq5nnkr6veSfJZ5Zxv6S/1pauIP1q7xtKk2LMy30xKgwmsZOh3F41w/HgHwcTQiK7ABsSbqpNbyIVYChgjE7BL4bDvQxw6FxkSFYIwhzT3pftTtqmQJYGZ0FLrZDpZKY9WLRnQayweNlk16SQnBSJwSjsbvAOfjZXZx2PsJL6KQlvDHC+vOLLySWaOwdJPL3BhRDFZHtlu8eIuBSeOFoQQtVJytLt8QDFs9vrZEBPPQOmK6ojrKrIJZOM7WzI+5YCgCYccuoVzjGYXXsA506sYOBzsyQ5dzoOeVaOfdgM57TCX4BtXh2BU6zlcb5iKi/PoGCzaWARbR2DocojFr2ojZ2Nc+37B++V4le/100C9EJdx5d/XDT5lz3FqIMUY0PBraNsyMNSEwO/AYE3nVODkmCYt9fJ2UBCTD0KXXGlOtv+UkGPFyHBa9UsMtsCPnnin6ch0ruDsBU1p6n93RvZrRszmcWyDbA9rkK6/5leAdPlNCB0NrGaGShxgfuOXbwo+uv1hjj+f193cFbWbUZUt8/zGDp8vJoVKFnpMmie5h0cw1a4FaQ5oHzfjULznmav8bvDj9U2zh0aCWB/Yv5UWZz1ckf1XG07+s8KwBrqrRdy6l6EKJ74q6jj8yW5NPBhDKkQZcDrJxr2PNeK2i8pPVMTvLJfz6uGf7Mf8NduOa4G168iV6727AnP/KBOLLGaoNF3BUqjVkiFu36/uMjM+uxh8PZ7YNG0Jt0+TQGdbTi5gQs+qwuhN8tAyDpS/6TGC/5/3H5t5bVz8BqMt/WBzj9/CNa4OaeK+V09xi9L6PUr/zwM+RcJuAxhbo0iJWv7f+cbhHogcZyvPO28BA1WZLaUUlE00I7ueHRm3vSlbGX99jc7OWnNo0sk1t1d3bGKBN3giBVYIoxDg3mpUo+bjo0jqbifR21DCYMh7Rvj4K38w95vT24nKVRtB1hySPLNDjumWtxRzbuz+xa+OUDOImovJgP/wZYwJmunlE7gARBgKwxS+vAm1GCJWAIAm0nVmMdDQdai9XNk/09ONlwvbxw1Pn3BFa+J63rW+4TytHEJz5By5pN58QyaGBLA3gsa0p+UCzioGvwVjpokBDnp87rOcqmp/9stGVnOK39vEYeiWWWPIPk49QW0hmssewUzjOB18mvRwZH9HQlw/mur65K9mxtNfpeZFjPb6VxQQe8h6fTiTKbnf2G82uYnz07W6uLh4x+9byVxmfcaXYAq7GiQ6td7362iUXONcZthqZmaZy/RmcVnYcgx1CjeYainWOAKKeKhi3dv/dzFKrxF6O3eODx3AaAOo0qfeA83JajnU4C1NqYdlu/L2dbMuPqISg2O7F4TncEgm9ck7Xfy3zavoQGmNYoecZ8FQHrU8AWRIHMbnWtSEgpWmm0xfupOL3Yn10eX0twL1FQMHvecIC7NXkfsINvVy3j8NOayWMHyztN12p30KjEVB84l3gY6XANAF/yH9ULTUGGy/KS/4lF3bi/WaYnBbmiJzFvH+FOKAYbYA0AwnPzhgDFsOddk8EKBYSUehclgYzp+YeiaomOY34S3HauZeMXlR/IdYIVi0YIHmYxd9PUgNEsuTRdofW9v3snxqoFCkokWfvrr4ZMPr543s5AznY4d8LJnrF08PoYdNqZJLkhVTGFQvPnu20pHZr2GsJyuNPnUbuI/iISlaVGiQztfp8DnkUtyUekc6zn8Vc4puDgXGF1yKQoyloJMsIhVToUTQEiA2SjR/nJjirajxYEsJeJFuWdK0aGJR0qu9YP1wvwYZL7iQQybY/6AXYap8gSIGNRSxHT9dbeisJlH3zz13d1f0KPGI9HNif5vJeR2RjJKPvvpgvSQUlw1PlCO2H76mVkA39yPQVsoZMhQaWdM8pzX3H/McfWHANlgMo21udleYm+t6zbOXc+Uy8clSTrHU55dkccnyCw7CEAvmQ4yNQAZ9+LDsZCB6g8ar9jrGY/411GkXXKMsUxuzXsP2U0qm9Gw5DafussKJIrB1iANThUmRLofHqCGrf/220K/Y+cS8rHnn4rhzNdMuIFzQaZlwq0ruYjYxNlWVkkEMG3CWxID8kuJf1O8Dfd38v6qIfCNmvSA8hjADF3VZ9mACq5DRvnm9B8QTd2KH/tZXtrXg1t9D3knCQ/eg6vnYHcucflngasL/mnXzcAuOCLm8zf1juRVcpVdGB0ZnZOADPtu2RMlTAZ1kwZsHk2UNid+Gmfxs0lfVMP7kuHMQugesm/2+5s+BS6yn0P7p9sQOeNv5L8v/9tlbe582bvn8kIXHwvxRzRX3d+5PCFbsrzCrveV2CnlcKFoygnCqAoBL4dzqvi5m5JqytWeVWcMCkJj1yb5qbdrspLbaCzZeme086srCu96fRCIuvczvrKuYSGyOTNv7nToNCmW/fGXYt+nlny0is5rNYW28DSx8fn2+ennTPa26EGA0uprczQ7lQF6+pdFRaAksPYjQyV1lEKFNkepP4XaCOobWuSc1cPi9eMQJTluUWx9fnTI/vTwU8aQzpKckjkiGv9e2gcdN1Onr7TPga4ax29AIC1nSoxkmYNZ3Jy4sUKQVx3gmG5D+ChyamhRimPQ+Q6XphKBzjBhOiR80vQGt+NGZrcSFtrPTdjS5GRyfMr0Xwjsg4mYTUjEvD3BFIsM4QeEQCQLhApk26+/pYJ3M/2pRhJK5gSmVMefL4uv70scPMNkJJouAEM9jnKONEVLeldSyklZ7q/NBTQ3IOvpKPZRCLp3/nI9r+de4p5+m8OvjsIO4GP851vQshQA8QMhNg19kACQb6Hqnx/er34ploC0XPyhaiP7fecNF9EWR1VhOqD7nbrhb8VczP9XQMDV0vp8z7nanc+aTrKY+5oMul6mucA7BctjjkhqKekgU/4jla8b9MegIN7oWoSlqJcA7akuziE/4Z9uAEYOfbJY+WbYY0E033PqK9YIDOWi/mmR3kaeLBfP/HMBKxL5rBNDmrdArpRao3gnWjW78VcO30P/m6A/5EsXauAl/zfqkfon2do3OXpX0L+F+iBOQjDcEUwMjMNuMe/S8pejoEhbtX4ZnjRWooiBCaj3jSDskRb+TnPb4e4gSWtQ8YimaMKir7v6HR3ZFtJFDm+3oDAoperRfW7vbXZDUJRdPkCHqmpsTDiEcOGqdMZH8dZwi9W4eY5JJ9rvMVSjvunnwlSulw5pg3cFlBS1sazRJipIrUBBOHE0eyHE6MzTCjjoQKMz5CuCSAlFqiHQOlQymk6DguD4unQipJ5vsi8luRsRp67M7pOqdp3DVg18DOudd01dFsTX9JhTl6tZUCdbj1rkJAwsxISmyxxqmvlenIuWmHLXPja61mNWioYjroGk2i3zJhEXPSPzExWeBqtXUzPcyNpbfen3hJa+++bWHnmPKe1Li+NELRWZSGR1bSxPP5NsNTKCLOMLcME63LJCXXSCP58idmhLjnSvkt+lNNo+2x7TOCX65/2y/eZnSmd8HzprtYd8ynyCxDDTFUvG23NE9aY+/8RFNBa1hQWXQJL7mvBk0PGouhSAPLQXU707kCG/i/vLXusmfcWebkaTFHEq9lMgKN1J7g/6fx9tldXY1J3RVn1YH2m5xQtd5wZKZTMSInMNpRfMeUsqXK55rPjvOdRy5UrFF9cOfoHDaM0zWUFRAl3wuddQNFVSd+QheMawl61+7lzHVA83nePxxAEksablvmELFzOkuMOziLjl7HZPPfbMykNZK/16iwOpjGC/+48G60aH4OAkvNpf2UKYFYyS3olTC/5Nyvm9JnI9JJ/4+Kd6Xms7p+9wpj8scp+djyfZImefcfoF5dej1A5Jg2wItzqn65WRYwKZmYoOO/7lnkf1xPY2xmwTzsrtPJCH4a29tPNKufn3Vt7/V6KxPNPX+5thXpRVCzKU7QchmprPs+m7H/hdMOImOMjlZYqzpyi5B86QLw6PssJ9SnQmdqP1zb5DZ3RA3y0BgN8lowbT0rQUUsHtG62OdUuDW0hsX4wYj8nEQr/am7hUGfZVInCYrHFyW7ZGknYVMqUdEBWTvsLAJDld3JScfh5cPwPIBVBBptNB22RQQk9QFctPf7kqxptF18JNIs0Zf+DR4zhT2Bi39eMjACHStQ0spWb7vHh+Cco9XXevf/HwUHMVeyFLNhRLrbfByYaGl1s7S6TkhP/N3kO95TrK9AqYKV07nLgHXKY43H+J4DO8af23MwOnbKbTVu6mmUwSr9JjfV/43epSIDBSX3fZRp4fbnukSOCMuFSFDE43qEXIlaYQRnaNcIV6cJx7LAB4slES7pvynL1zEZqcbNB+4wLQMZIS19Mz36kFF1nusIsoVSLPOLxytIaN+bAB0ImphL53MfqNQSguQltkgcmnuReZqFjzVgV/lQLtlD0uWKY6YPUpJVls7HHdzcOHgkvv7p1BDTt4kmOaPNCae3VnB7b1f17jMbTRWauZOol/2Z7yRsXtOw0przadv255X8GPYdSCI+7HOyorDhACQ8z5HDMcXYzNqnFmm4dr9Dg/u/UiS7u81T6Mr6lc5w7GNGcId7xYA7417f3ty+W6wnvIc7R+FirVTXP7Myr+bFvNx+KXJ6GWqVrggf7/I4AR4Q1q3qK3dnngbzH9bufRYl4tL1stRoHqYz8Wk5ULwfKqHYFR1YWaN9pHmX3S/akvUsHJAsHjt8hasyzFOV9PGHYCF3cYY16N3doh9KocNxE43B+M8sSuhlRd1MOyfPMcImqgYdakwJFQ8te0BH2ceuZnmL2j6h6GNAWbSdXBsiDVbf3AZkTE1+HM298JoeWWbikmz1hKnHSmFS26SjXrEkwj7eG90HtXFXscQVB5F7DIOn2OiYp94q3M5OpPfQ1NCDBjm3kbT2Lp/O013vs7az1d01l+WCVMWZW8gxT30/uvh+12SW5+n7svBZlu9nEhHxawLg/sAPC9XV+Z8+rIDozODaXDpKwzzC0SS8BtwRxjHJ3rbp3eHJsx6j+E0G04rDdNzG40vDFAW6OcnIk89CuM9ucbZwE35p9SsA5A44vmZG+WvcdmfOLUiSNExQDjeMZZb4XrbcfONrjfpYvH+xZAKETqIwA7sIRnmoL6dDXfautmic+mJbdgVEkcpDRueIp+37mx25f95UjkwAeDzJz/+wMqDDzuZ9Vzg1BObMJB/kHxCzg3BdyuDU3PPSS/7prL/mX7TZBtzM9dBrCC2vszvy1froSkuDQyuwFgvtz9Ow83NtyrkXLIP+u7MU0Due3uw9YC7W1+UrXsrWxGQJXHktaIyRrCkFNAUz+vr19+1xlbm6+rUHT44LvR9rjO3438nr7aiRU3ldnuXd7T5BeOr+GtWx5ZlDWWaDtQGqfGnCwqawzS7WQfS9T2RNpVSjS7tSp5Scd+CqA7D5Gx5XgpNF37NxWy4j2LoGPzQnLPbLfBHpsT/N//XvQRFtjjqjOhvH8BsfucpOHvWOcYWzeRSCT86plQeV7/6NSDRkgiaHvmbaObM2g4gAAIABJREFUGRSOp8+LEjxbwvfI2JDp4JJGvRQvnuclkJvCpQSuNZ0oTrOVavYywwBiPtY+UL8/Y1/JAyWr4lkQZ7mjBTWATZxDE+0y7gGeSWCUoDEjtvO5mNRJfS/PDA7GwrkhA07iizyLSTClsQ38MHPjPA+eiCwW9tbQGVqQB5DvZ5IG/hxKQU0W/dkd9F11pvOzmZuO5tHsh0d4hrLtjBhggJnNo3yrZhLW2Lu178PzLHAUUyXEYOKBDDSlw1kdZZssr4eKid+6s/oMgGpaqPjv3Vw9Zb78ogh6FKWoBj/IKsp0tGz7o+xPmbfOX0phlGLRtnetFItuzB39ena8Xpu2QC8aP8bqgGkgZuw5ACvh2JFhGpjkdo9kxyO84aOL9x+U8N3xiqvXyBgX3IR5SiyvxpLvkIGrDKCRlfIcl/saDaxRrsPGv+Q/yH7FJ/z+X13+A/Sw1EOF1REB+oEIQAEzckYaoHpm/KuuEOHgDCAt77HsggEduhYIq0uq9S88u36LHIDstla0cpf33/dvNwRW1rZn9PbVcykb1OwGB15X6+2tF/jYSmNrWYViFDP1PJXO/axmCe7W7qzSQecGDvbAyqBBjaE97vpWZV/KDqTRJwhxkMZDlTpbQwUnb9ofl868ucjT4XtygZI89l0OZh2yVJ5RD19zU2N83r9LjLKsyYx8ay3N0qALJ91olfhbc1SkV+voDrLR04hku1yJZE5n5t3OjIaZFW530BUOMp21wJDK/sArOwGQaFHnqI54OVsv1QIAKXtkL+I6Src23ZrkjQC48AzOU7WMm0rd+gFfAXrR0oarwEPlZcFz3t0vp5dApAQMlElsZ2JE89yPurvqEylg0K9LwJAg6qqskrKkvC0BXfBfyxqW+4IP7VujVf4X2aFoxDEDRZODBMa9xFCgsht2i4vBvQQAOoPedEN9juv69qLLQ6lfeBkEBzoHFL4pYmQEMJuDptbafOit92sXFtAVDi/OSsJRN671+64yHseiz7KzJ6ZVRhEfhPXJL4an3X2FfXtyErps0761dO77IXoecVwfxM2enfV1Bmd8mPzXAaPRfeA7D8Pd/S6KHEse7GO4byWGWbOFMd5wzqdTv/C2REUtnsS/oI90RQF6CHhQFqUHGRjrtNd8OuVe8v+S//HAWWPgHzjTc3a3OVXShQEZAIpp6d8gUxKazNIRu0iqfKcHrccNJm8r3XpOJQDCahywA56fKxSY4SwVDve3oX2n2v41lwtcLudlzfd9NzH49PKOdTbh0xW7C8Ru/pB5js/dGWGV5l2f+9lk3MScexMVtzo84Myf0JE1I+Lf+DZUB7vuTT0HUMslaLzYCYoGIU9AYj5lwp4R0f7LcUVEW2kJOnTJTbl7fH+KfassRSqjLK9yQDJEw3UvIUvJTpUOejaTdJpzPumQO5AJIzs926ip7A8lGFvqpUv+zF6uaIzSSpuYHXOsXTJrpL/NS8CuACYYSTnLO4PINuzOEKRFATwtW70uL3T1RSsj0mla6ODAsbGSrz0BvErZFl0sG5XeAAGTzSVVoa7s9Mg5S4/ZeOeZGEEV8Z5Kw3JnrezU6d/AC/nb5iLdaCvuoFh8PXfoYxaovUfK2VX7RMAqmkgO8m+7Sa9Ay7I9X5t4pdsV8aegF0BRd3YPk/TARokPAkC4Or0DMcE7vZzswbNMs+jlz2fGo5f2TY58ZE5Jg2aG5/u0YemvO1lrhKZdFjp5yDp0J7osnx47s5wNrIX5wnuRjj1JEx+aMAFiggDxd2Z1ZmDyyG6P9BOQtk3M/xogD9v2yDWSztN2ZKwuZPv0dox/pOcMYGSDJu7VmqOK3gXwRLzScrovB/yZddGwTX1dUzncROBDNqDPNGZU6nSG9nW+5D+EV7ZWOrYD0J7ZjC0Bg/7l5H83MlCbY5x1iRKvwngXgCeciRAXKMDHCqNkc45UbVMMrpGO8rRyGaMu7/vdKW4mvIztlKaILHh4Pd3EFfpdoOBjA4gFejZACE39rLQ+UpG/8vcd+DaWXY5gwJP9MlU/jbS/X7OXd7fo4ldGY7wFigw4rWsV0b6fnUoDkdEQWZAMY1aCSrdnQtw2HeA0HNQo6THL4RA3HObMY8ihPi1tzZCIR/sBdpwvcg6i0xnzhHVLJ5pOopzLPBcjw1HO5LgnSUeaxk8h3ABOykro/TiXpX/MnCE7ksvO8r92VkcOQM8ZaY4JTIxD8vujejJ/c2YKQMuzNsy0OF1rtgjNBPTAFZBX2s4F+dIJHwIvxZlm2Z3oOcxpy1nb9wMkwe0QeAn6cO6Y0+nYGwew/HeP0UrD8tnI0rHpAQR4Yy/3YDbQcp1bSuWiHDABjjlJ0ozqxplAtvDq+K4jW/ReY/CAzUWATwB+KtErgGraGwdulB9miWjAJzwkEh1dp2LNdsXTjoEPyBLJVT7HDGAp2zzUVNJYjqnunRzZrqNl9Qwen079roBoJWMaI+lD+306mAksup13Prk4H8S5zs8yHjHrRKCeeuXcp9xB46kaRagZmNmiEajstfnkCGbHc0Fg/sJnbf2MLXawh8e1yVXaLn6KEswDBKQeFkg5M4ynT3YxjO2Bv7T4iucuwfFNhdC+h8LUJsA/7+ZWcSNehAudnee3k6wv+fejHv+C8v/+t//4r1rLAkVtDo+ptctzP2Ls/m/wFzwrSbws9CEUEMY2XgE5uC8Z2hzW7dTLWZK6lOKJtso0i7L/6+WdqGbYIMBEY4Gdj11A5g6IN1NNYPiMCZqV7G/zbbr9ZiYsFGvNDlY9fvg4FptfmSCUT+U7iPINK3uc9+VKfiBj5mNx37hP/lwazvhKGhh8oSh4zI+OkD7z+nCCUSPvoTo50ConKE6VK9d88aRIlCZ7KgObyt8UGTHHDe2wsZEFCDhr7Pk5MLWoeRqnHjW3/XNnkJkxlK5ksMrWkK2Lk7f3HJtF6iDF7vUxIBZBPzqofX8JVko2wAi+uwy2Dn7MdGk48mfMpWdhkHmLPSgdxABwe2mfnwlBgC+zUYqwgqcreMmzW6JzLylMXkv9sK5leeJdJsRksd6be6rSO/2eezrNIzNbU0MB8JwTn5rQWkQLxFS+ID+IPSkzdBCN71o2McpedCZuOAME+Tf5ooxUvmbjhNKgoZ1xIo1iBJ7tSRUACc6Pm+rwkCROE4jYzi6vzWTI0fqXDxPtVGp13aShOqt4VNBKVyxHVeeenrFOHAvqp9HkPDS/6BFOcfVADdw5fRkXXZeNZeuNdnp4rBqleskdyI76/eHsu96fNrbQ7gKYdZqMDNK+zHsaUJnar8e9Rriam03eD9P54F1RrkJKuaJIQDbvn0f+8IUEb7KJTcuyxf3D/sURaa21ETXtadbfdJHUNdrzrS+HDFgZWn8Mmb9wQyXbj/jkJf++e/8c8m/lbZN0u9K8PVNzqMQaTXpGSfzoNUfHGQmBW6nlbFGVCMCYQKUBDVnc7+B5e/v2dWnA9fuHlXRtCdNZGBzu30OcEZMfXc9veZ8Bm2VwliJZfee+ruLlPKey1rPO7LAl3H7Zqp1/2urXNVl0pINm3NRBpPlu7sZaLcpYtiBVnKLhaQtS0xbnBcatHkLXjstg5NkaZrz344siS36onFFfALp+YzmUjdPKfroR1EHe1uJXRk6ZqokrNZf4l5MWUGnPq120aie8WBsO9YsWB/ARH/SSK3gxXLsZR5aHpmepzEIFE1UKk7andNpvvqekdzl/46Lq9LDrXdrjIHwllp7ZtYFYI7JwxZG2XwXYraxU56cwx8Np5/xRynZkbNL1KQDQtU1maYz39hqwrGlNchDU/CGDN3mj+Hr99hlAVTOQ/J5AinyrjLpla5pc41zb9ZyTrtLTkVnC+aYuu3YtGjW0ktHCp765EP8b9fWcfk+5sk26AhkZf7geV2zbKjHz3SquT4O34bDHOVo/h7RL4IZibq19okGXhTCXWFRZH4MFmUCMeGAvAtG9jMUUtfbImW28HgAngkt2QY/L6bbyXFjwO2BowC4d7T5G+E/J8s4D9Z1zmhcZTmNT188gp2X9CtcmP936aw+OIHeufE5GnBOHLOX2P0hYDHjNg1nCXvakTWbvgWigvW9gbP1sIeCUA/La3Z47F42+YlcYL/mXtkZwnV36QMU/hfz39/ScZWMuCjepysoEsazfCRScBmNU3s6JiU3tKus+1qIKW4Dy4HQttzMMtB2H9uiSGboxnT/jp1lBrYXZGaTlvngOyDIOVrXXqhgBeBwYWX57AcDsNmXlb9PJqL5SEszHLk5pUf/+h0ebW/32mA1pY20a7JKNC3Bi+i8AT3ektwPHB0UGhZ3aKqXTMfX1wWuweWQ5Ro4vcCb3Ey2hocjrGSG/5wJkyVDGiP6yx1Te2VaYokwHlI5vZLmKM2EOuRmz2uChON5qlKBGFA4azKHT/qaF2lmBdk6pZDHgqPZMBvdscqbF++XMV2R+amw1yu3cO9msECBPc8y9y7IzZDYUamkvUNW1hU7KjvmXff4VdGXmZOJhldodmQ3RLuZT249v+l1kVWL5ZR8zA8Q10WnLDFCCGD0nnud03XuvHLqX0die9nllVpXPkrgsP5/PZZOEeEY4pWe3wEc6mo7uo2vL73T04gWa5AK7Ws7YhY9orhsBMrMTW8235jxtknyitDLP/mw13wE0M2ItULTnGbx1Pj/oJd72hYVFGM72iA6mw2FfmnHTn7vMy7uSpp7Lleo6+7c2cnBTEFRqj7A98f9hBi/0ud+g/Sp7Q+B2MXd+HSqGjuK+oDXUGU8je2aW9PVzWqLhtLakdWMUZb47+OReX6AF0cuYFSHhxkfl2cPkjoD2Ba90OSw0fQCev1eev/f6ibE2wFXDiy6fjZ8gXnHlS/5dV/re/qj8P2xkENGj5gyWPWNY6hIcscYKnmeJU07X+JP2uPosq4hXum9Bywssy5EZBjmFNj13XPYRHWvHusrivu5D/+6owhN4/+KKpcTwPu3sy5gm+y6z+DtfLJUPB2QtP5Skd7bbBHIU5ORdPRvk/Fr2KJ3ATd3ejL+E9ZphRxlcOGbVKoX178ozsyGpbS16K1CRtbxRNobH20c2QUiA0t2Pup8XXcx8xyoQs8VoXlLsQsrl2nLOCWVEiJSiKqyU+YhZSoZJhor0NHxRUlqbpQvdkEEZHB5TvEYh7YnoFf8WL6CWJOk+XptZEf/WQdDluD5nRuj0Wc5s0gSgNtYKkEXHX9lMdYGbzjL5JgaPlDNT6WLLQQ86iT8IJgiCtHRIvhx/I2eeLeOW9vfsiMdMVSUoIPAv+4YyP2b2tD7rrMjyYPEHsz/5Pp+cT3ZtTB6rwJ4gOsD1VaYKwYeDLyZ+GLJIR2a4AzvsLZ+xZ82S3Tu7l37/qMPj1n4UFg7h7D+mfpLZK9fBuyvgog8WWeYsB67OFINSuYRLB49eJXm3nM/IxdJcx+U4i6UyI83pAF3KarnO0hicdU4J37Z5ch6PHOp9K+Q83I7Bca+07PTzPURmTrLIMcVuEwPFMob1XOAO09UdJIgPfMKHu+YPH9jnMmtJXRcgmnx5MWboaiyYAJrznwIBt3RqNow+H++74u+Y/kv+53c6/ZPI/wl6HmR07svdoHi8OYIxMcPd4UJU/kU5XdxDAzR+VuHGeq73/QmF1ZRgePnf9Nqefcd63c63/T/mHFkae70Dh1kSd9W+2TmXPRU/6yPuuFNCo0X83b88OTSjZ34+aTk5a8ErxleMu7lE1rXFPi2AE5FBj/zauwv0glNpOUMil40mXOPuHdO7g3yq5IxeRkbEa46V7b0N1xoQ7IGyxE8vRZ1KyU7gYvfS2SQPMxMUjjiMSveRIoo9ztGDpVF+lXuWLrVHvRuDmQFVGaL/uDKSXlJQaEkLpRhqeW9NXn04mb4g+z4dWRkngsSYU+PtvV/+nYBK6pHemCBBCrM/dMgLAAoc3rI2mqA/u8ytl5RpbghgsASs7LkjbZ5DI68abyYgz3WnJ9IP5K/HszQspl7aNHv5Hiw2y9uk7cR/RvMs7+yyVfe5l5VllqWXMPI5cY4Ma16PVAc27rmV2zpgah6T3rHEOU182L87z0GZHtBeWxOWzN7amSWV3hkvM0PG8fMcTwMgQ9SZuqLDiHAIEThgCRwdMI3TswZURiXrECrXsxjBx7XEzugh4CNFMofrBLojS9DO0aSGcn0AQLI+WiS7KJw9q7LO4QyNjZvZmNk8VpvOOCjnpX3MlRo9gneRBaj75VE0j8bztwMQXICgvta+DtF3KrFHbsRo1rIV8nKeAyeVb+/cDdHeylKdd2RSekYR39+VcqYOI+9l8EzmpMiHL/iS1ppLI0ynE9e6eXJvPrpGOoOozfieQ6vIeMm/Fxb/E8r/dabn2XK2q+u69u9S9+z4N9IazN4isFQextA1I7QVuh/yN2NgZW/R9nkffsvMUTg4n6t/I5olwFkyQe3ieqdq/ojf3HX1dFYqi2ytnNkrlFN4g4cvofC8Pm51hfPMmYEP92p+gAxh9FCkYoqT7/7JGYcjippDOnyMLBeHcKwCyTKJLIOqaynlV+0APRV8zsHmWkp9gIjoKBXHWN3o8H4jc9TqfqRT7s9BlL47h+I0fS8Esh3CIJQAWAWQdF9Y/kXA19tUy3j2DmMxli9ezpFzJRoq0DGzzxONumvFRhXXoMWoQWe4j6MsmwCaQDC7bgUtA3Qpy5T7LhqR7ll2ZUiXGR6OaXOqTRR4fs3mrwyPAw1Y//zd1zvwbJ61Epe6o4Q1FbpHOVGen5Futb1sGSqXtQBOWhNURfCmP0iA8VxrFusk6M1W3WscvqC28CpolXuffLBx7KfRwJaBbnntPT169q2Kg22jjermgeBdjhevMV+tdqS6shpXjl+CDdeCg4rea25ARGc2CVaMNL2kTPyFbJImg9bhvQiAf3enk7R9el1DhkO8S8f6zuoarVMb9LXzXvLBuoVtna+ecYAu6UEAsnCb5IQ/Ku1ahBzeJdXBtJSegKJ1Zs2yYpPeRFq9lGq/tFelhG1TOv9gGANu9PWajiLf3e3N+dtwqL54YOmA6pF3+7nn3Gk90PYl/399+QfoMSZ52Lig5GYfvbOnIp/LsUMoXN0d8N9ZnnlZOdhr1sA0eaupPPmZm3/3eX41ukR51JY8y3YYn9sR0HB2vJTLXgK6fv/cJXG76u1KK3+fBP+Uq+tU92tbK8TY22kUXA7ELm/b0bc6vW34LP+TbswqEaSzULxi3M+9Dkuf+3ucqeGzA3Sp7GqBVXWnqxmCPWIDOARKUtTp1Ok8UOWbdMpMI9IJpEKP7+MGRLWLg9rOVAwNIegY8wyL8XPybZTyuZVROZv7n2XfuE7RJkRIbcYiGu1rfQDuSjYKJUZJguS4KesQoMA3I/YkvB6VXtoLeGWVMuuTe5yOdXZe2KAgQpcCTu09MRBgAZBwezr/CCS2cqrgbrea5WyQmhxw32IfbX2lhFAyhTKgsg5kUtI9k/OS7mLMoZS0yUFFaZ5PXg5/Zq3wwla/xt7bPN9rP6HhwXCd5ruf5XS3FyXXCHCCPewVm1YUOp9ATe3LJZ+R/dFaG83JhwdYapqZjnTue3pb5gDnucSyttbkoJuO+ne1nd1AyEHTPevqaFjRAIzuzVACw3N8TgKjCtA6DEnnmDxoV/l+bJPq1sS7uPVSPTZauDOj3Rm1a3PfxUByD0irg8YDuFvX82wQ7wmHWSaqb8Tw961zjdeDHLfeECH4zukaLauRIdv2zgfdHfUEhnoWo25fyXJMYH7zNN99tTJhn2pS0I4QtNAv5YWAjvwVKro9vAO3Ao4JUEg39y+6nHIf+zy6XNzy4sQHuOEl/38B+Z9bVt8o3JahYZlKTdm6CozORxZBKyike9P0xkIjJEtGydSKym3lIQfHuQ7GrPjT2yJYNkPvJtgAbzn9W7Is/LWftIQ7QJS5AnZw36NhG2TtmjjMdmqg+IR2/KMu6VJ9YFdTZEar1d5aZSIskXCarbKVBXj2n0bDgIslvOfm8WGGL40ZubCAy8jspTbPOLAR1RQmDyzbosOp8wyRlGMmPOq5HI2lrWKGpbDvAF70u2UWmUFJZ86YKH/rkhdtj8NRNMfiqvxt059NAeAg2hpU4pMrssg2VggLIWfYZMOi6YqIliyWm9s9TKvbJ/gUoOtjJK18/zhedEsTb5hO6KVX/uioC0lnNkGc3D2VAZaMQqEVHPgyFwAtJ9kxDxpBp2uAPg8sbLriTIv2TXwSTi10moIw3THHToK/s3FF0IUZD5fXWrpXM2rBEo1nOHfylJ5je8lsUAUFmeFSR6rKz+vuc1/yO+OxDEIkPSpIEh9qHQGmBLKiVfgMiK30zaJqVn54/nfr4IogbsKurhXvM6b3lAMviOG6LkNBkqEEN+Qnjh1zusoAwYl95Dh2R5PU6vcW3Y4Lr969E/dfZD66g+zqqrgbpjSwINfZ4WvDNh1nfwZnvkPB8Dn8B/eAvFrB90Tzf2gHsYcg7F7nblqRUt1pOzIqlk5eiBK1O0+/ka2T8erWztedH7RHl3J1NOdIrnkYnJdufnLfjjk8oEfo3EeZuGYLRrn2/X3JfwYz+n78lvJ/38jgScGMCU7Xl8wQl3IDrC6kNhkdIOcYpnpcphv80D2BC0qx7GtHOktIImKZhXLj0jLP4YELqsEr1fNn+j7pGCrWo3Mr27VBooNBGf1dCgiHbdfLuoNj0SB3CqVsHvHQo9+hvKhoIlIEAJQvsWvgGoBB5XKKWseLeRF9TnNSjzq5L5dOPbaSjm/e7056M1rdfYpIfzjItbpbTp7xKEBFZB6m73AtAw/i9QANaVwT+HGGNTNC2hyfffOZMYlrohQrwQTPttgsAMhK6QXKjTyzV9//ozIwd6L9oSxTs6+8rHVHnp3qYYXWYf0qm2s5B8jd/oYxOUvostAsz5Bw7do3OdIC5aV5AADrpkbrZkbwKOBBfgjnvgAllfLVkNQaP1/o6pkJgbAGWstLS1VyB7nMuXjJXpw1S95KIJTZN9G7ZOncwQpgExH9BLr7eU0vEAiWfVSJXbvnKNWELt/7Jhq6/eIa970ACY+Az+aW5kTln6kjZqfn/F3zsyCgUUJmUBZosq50jvdNzWG7O+Tf701Lh4hZA062hy7ZzTRe0WzzfGQRKka59kOvfYn6HAVNUs73tNbAOMeT9AWTDfM3Gc2xoOYDW5U509wy49T2QTb0WW+i02UCgNpvzXmNHVVx649mh7mWqhVnWleLkXLfex3FPHxxEx9M+0wAFbGIRn9tpeh2zS+YXyzuXNfxviwfULQLc/wA+Lzkv8rJIV9gsJ8h/xegZ2DsJ53TKiC/1185PxMAE7kQPDHn/sKyFYp5hdpDmZVlCbrZkFHQ6KuzWTR9dsd/sz+CR8+qqd+LLr9iXJfMMG7r/NL6Py358+Pt/eOrB5hk6IxmYXzRvML2wxsNSGMVGsMaxu8039droYLl0Ipa2xo8yxNbkmCt8ArsWpbuuINVwoKpKPW1OUY4d4QpsyytgBooe5tHqmZzEuSM+/O4WMy1gKzhkKam4iJQ4tSkU6cFo4DKbppw5bubKs01z5p1Y6RfDy/7BlpFaQlLMijPAKSb3liNHKvyclb3RHoGgOuxcUwrzOWEBAs8V+bfD2fPYn1Do4QtKU44u048llwgY1Cua2Npv2woW0GAr+llrY3OUX4k+rqjqvHM4UIDAP/d5uaUj2v8+avc19dTgWzNkNRzOrV5Qu5j6gWBZ8lFzdLlHEV3OYtTl7vjXVEXZXoRtNF5Kx+cvKtGDbFWZ6Rw4NrfupfWgTIUTprfN/0dY8MOd+e2jN8i3NQHzzqFkwYOOuDMCdV6X2OUtD0CRFi76dTTUYr5POOLaA/aIphJSs17/7y+rxySe3uVfQid2jJzvTqhA+MJOXHOg3kC/MUsVfI10HQGWO4ldVAcOrn5hy0AMPHDJANhezxTuaa5ZGp38UOZmunOmR/SSa5zqs/L8z9lz1OFlmpO2sApM9Pl4rj+Jf9yGQxLe9BHZ+O579sSwvXWx99T/t9XeVuwCxVPcUSbAGHWIejOpPflbmK5NGxM0+Yb2HCdMgvyCMSeO4Qgi6RDeT6xUovGglaVubCmGWsrGgVekW2h/+9y5PfJV2uGcEWnyWL8Bb6TIl5oZ69tRd2iw9vq8e3cshWdl8Bt0pi5222s92dw+2ikfJzjN4JPqq7KM3R8adCDTTqY0P7t6flZIL8xDDmMgy0GZw92hNtNeh4vSRZse2uGu5+RG8rSCvtVVR0ZE3hc+ijQY+sVb9qkgyddm8iJCDo161rAQaGT3aHIGk+5FZDR6ChDWudGIAnRjXtVqjpkliTmBewQNPCsU5ZMkS8KjegQIZsjKY9StA68pF4AVnWt2J3ZmHW5uoONZ2GOfT3P0cR+k8Zw2KuD7zQh+IlD+q4jASBjveCfksWKLKrzAeihMlFmrlJLAiA1R2nzovOUXX/Oea8Z7cXXS5T1bjD7rZ7jZNbOsjDpIeXzbGWa95g109xA6wKCCp1AE9gAzS/0hwIweoltf2GjngUQscS2luaiTE2attsqPKepogDGrqEbV59X2zeIzqTaKxk2ApyQtXZm5nuBGGczLPGcbLmoOeO6upex9VGGB2kfSyYHrgQdcW3/vhZBiv1uc2Xj8AyCoJiK6NbmYn8Sps77pdvoLLLpBB1zFAxEtq9aHu2/6SO9W2bMBG6AMvAn+Dq4CfboyspLhwR9wYrTyqd192BAz2je8xV4iL4J928IKGi+Afh83to/BdbNJxjY+CX/B1F+L/kfMj0Xm36lG7ec1zMK+lt1+yWPXqSvpVXFZGQ2fufGcPOTdPJnZnjMl3OzvTGRHGvFNx+1xLxZ5LAl2Qr5e+77i1zLSJ7eArI6dq/6dqfzwj+LwvnGd1fQy9HaP7iAf/vYbcHzNUo165K2KRsFCE0U3kL9R3cKmH0xmw2QUqLyqXHMORLJcV7LAAAgAElEQVS4TecrDHjwWwXV9oJOrwiQg8CSnnDSbaQDoAyZmU1SVeUlriz37hzH4IjS4ZDRSJAe1G3gyOfmFsEMd4WSchi3k+kZvVhPM2r7+6EzXjipuj7yEipl8/fLEKQ6cXtGSs7usV5db9R2misjI5k/m1Docq0zNZ+Vuslh2Y45HV6gOrs31fN2uqWbgkZ5/mTP0A9qVf7zkirxpIABy7McRJ9AKmmZFDiBpX6j86/5Wwt+NXJB+Rj3omV9dBaMXKMzaFP3tctufg5+VDZW1tDkp5bkmTDIYRO/eDP70v3O1pwcrr+TlxwUDUA36dUBW8p3zKHfj5a5OlhegICzTjhN0gNOhIeR5owVlMC/6ZMqkI+diBk0uLoPvi5OpJ7f1t3nnY6oPaPPZY7s9mur/e73aJ5SyVKoDMLECgHOVMZ0AZky4fLE+Q0CAPHW+K+lNS6ir6EsA1A83rvbp41JoVgvQeHuXFsdc9HZTXqJzE/zCiDiU0qAkxmXkDvPAkg3POT3+2XGrwIbtAdwH+w6F0TJYy+tkh4i5CyyS/AD3djlWy2zX/Ivov+x8n95pucqVfvsAbInefPHLxOTyauiFCn6EF1jzOF5pq1kTkhmjDGJOl1e8eML+ZPe2TM5e7F+7gkZkEWdTaH93XqfT4NAqynezhb1dwH9tus+ojvKUHjCiQ5PgHI5ehexT+3vVn7h/KU3wiyfsWGGvA5wEtHgfmj7PDBvz0uHSlbmKrtSAKBbnO4IKgMUawp/CA67QI/npJVpYhQr5sXMZ0TQfc7Y2nQu8zlxPoydzSLKrpcKV4eSDkk4oW7FEGjPzIpKv3BOJZwghToxbwOyueP13I4c5gT5VDe9lPHY+20Fbf10oI9W367L8v036dQXXpRPhGyIPSHhx5H9w1mc2BPJcQtane+zUZYEkexewuee5R7b8zdxZid9uMy0YL/F77ZGDz743mi8ADetg2Jk2AL8kAaVH7Xu5OtstrCuNKekZRrxd67HZtpL5vZ3vhhTl+ZARqAemeTzzezp4K+1lhbBHpkJCbrIjl9VWdAx7ecUit7UVGHyulMrEZEDSX+AjuYEYph5mLR/fxZlrFvhNf6mkTvo276HPqC0ZBnrHo8g0UGMBTdS6XIeNbiFjIZfRAdXQ/Tt2Zk72BgX81LYcoDEIZtQR3nCfg7nzkiZyZm/3YOWMZt4YwNtZOSfmKVty1R65zdfgY6O5PYYaH4Qe+eLLlm7kb4IoIcu6hTT6yNyZZsOjRixniJ8XoWA7FmO8pL/nyX/u7zt6El8y6kDUx9KWMarKp9+WM6caHh4l8+tz1QGKUpLKGR45CaiOrGpJOlZKfyB66427QeG+hPdosSsO+KWs7dt28ecTLPuKNCmtZ8FkgNm1igB574QGsc1kbEBtBM92aCG88HIb5kqaVznStV2RxkLgSB1pguntGlK7anGjKhhixrbFLM7nHhPDjAVN+d3ACdfK6OTCTSqotW9dCai21uANDpyefqNxryT2uYH57HNSfeytEpnqI70vi/2oB+BXcZSSyMMK3GqxqA7pd0pkUOx/k2gmhm9mEeUVdniKmAgnVOqEwg5dahrdAZEfACdtHkADMBMEOeouceM4CTfZ0nkzCW3EsRtWgToGpo+yE2JNWREVo69nEXRVY5ilkbarCPDo0YUECCbB+ciJsj37eT+ZXCg7KmrjqVCaimev2vML471X52JgoblGmz+0hXaidRNBDqSkErr5Bdl5HjO6VHQsDtuNASTY5izzblS18T7dnyg7qRPhubZ5zDjQ7XpMZMsyrrJkJygaTZ9OSf6Auk/aNsYzS9OruZwk/0q5seDpOLZCroeO/bJBbxTAR3XsCNdMpuVYyRwk114tI/r3mgzTV4HH3SaFRAx2VifEHXuCEgw8dH5b1vMdRZ+bzxrY32nI9fn4rFJs9YAxBeTUJBg/zytq69lKFOEChz35JDxo2udy/aFHE18Ir3wkv9Gu+W7/u0f/2m5zK7pwqDrAwR91CaIQ3gZxiY8JatrxonDS+4c1nhzKdL1zPTs8z0+mPwbbxuSLvuZWp3V6/d+eyeyj1TT9z7rj7iemm6912edZVrrsrXtLfWymH34ULXrvo9LaWyc4zxR8hyd71yj5zYnwLJnBVMm8AomawAaYxdgUZywqf2y1KFKWeyZ1XQ1h7psSzu3M7znSAqJa4qnoNNaLLc4bpXfMlGcsYsQhcaacZ4BTj5Bnwwqz/ho8fpOdMBO4FxTauWY+3C4XuQSIIy/CRScSPU5vtrSitzuJo42JzMzaGwOcTwzynMcELZOd9WJdwXasiY5/1Sw6RDrV7ZP9+t2CeZ5foNAL8Cv1tTP6ziBtnMG+dh/p0o8y8BchxO4RhnmLtGTfNsoNj7WH2Of14VIYg7R/rmMQxmrGU+VJksuci3IfhVa+DuPnKZ5JCUFPoFQZu40175fxicpQCyrO2gWYC4VgfisgELvbqkywjttLrN5xAWRNSiH8otD5FLq0z+6UIEvHlkoGyJt/2ntTgDQr5mc5GntWrNlwYzrIqADe/9cVqRG7csske0Rb1dQM+8MTEfEandQdXjlAMFAWpSUoWMNF5mOcV7I4Ih/bTzskwDCkXUxH2plRIo9DQCQ3PLsvpFaV4D5itfJK5Vv7o8icC/kMkbFA0vPlc1zQ6aWK6FXfWKXcnAy/LwUZssflEE+Q6OX/P9+8n+WtxWEb923unDUaG7rrM8dpZN6o+EVNdwi24FRWv1r0FMyPTqwa+2W7Z06RR/c2ZrXb48sMTM9/v6ij+UZ7jc2q5reVYh6GUixALMKOK3GB8xYmLAPYGeqGWCjgLAOOC+E73gmQ4ZOS2V5kv3mjh49bgd06fgZ2It33BQ5cUcw+LKXzJiBIgixZdfymnTclWWjUyXH3h1R3MtyNxp7PY+tnCXfXJfbh/1PnLOI9rg2YnGo5UTB2FhHtqTDnm07j6R9OMrD3NGKSL5PjtsRTv7gnIuFylkbByrGd74uzRv/mrpJQM/zKjJER9YEzn3wVBt7ESzf9cJ3NnkWyMtJWYa4gwWgLUPnAkvKJkQ5G3noKEHLDKv2u3SuM8qgtJKZGecz3zQdl9z7oMwI3jtUZKuAVKMtsx9HtstDJ3aUqp9j6+Ao50X+EK2DT8MW4WxX44OdzdIzuX/iW+gvvQur6xIBbukRguyyzuG9Xn0sOs5mG687mok3WV40ZZVEj0dgJzVN/fSsD9jXUrJCGPJqHs88R/Q411mBWFkzzqwctLgo30ranlSRe6J19LOmYYJ8QQdwGlyTA+gsfYQyyZM2JhMq31+znMBSv++KbqYChwOa2tQHm9bXGLwA+1CHOIEzKR3jNf7XXXe8Mv12t5/9uaLlNB+S41I2hwk8lGN/2Ev+r7SQ63yWpxascS//9+/p6c9sgOhHajfvl/Edv16e6bHOXLvjz+bY9ffPnekzCvs7VvrnudQ911jfBj2eK1h6+evb23//7y9v//5/Pt6+vH/xZgI2/d3p7n296DTzy9tZUpvrcDbhuvyuhKzAJAAInJ58KaunD+UI0btCNNiUaU5aDhQjrV02P1eJoJxsOFo9at/LQODDeSatWqR01ocMA19e6nO2JSUgkIONl3rvK8rSo2G73RvlpsdZEY6Lz+Ds7TzDKI7ZOWYv3HJFw4EDMMqxTvOaJWzae3Oqpda4tn4Qn9HcuM4/7LnD+TeS2o+6lqCSe7OuKQBgUdF1dtzb6FT2gWeWIlqbYGlnuY4zOzbg4aDHc8AzTleB2AATLq+9vG2PK5loIIgZxNgLyPh6lDLCHcxJtERrowFANTbPztUYOycNnepRMnmCNTuDswUSma3cRwIYrlNX6PdOE/F25S/wR2ts8igLkPqXBxPANM0p7U5WnGls0elNzkedzhov8lEdADR1uEt7+A6eEJDu3eEZdx/p2iQbnQ60fpvmp/HvHNE+h7t1kh5T6R7nsvUOhLnve4qjZ2cGOZaPo2x2lrQZHbrtsSKN2qyAetdMst0bcx1BUNJZa+iFGywNS+tSqQnxjy6hIbdTxuYiWKBR++53evPpV6D5ESiZMN96zm61PTBst9WVf2oF0vTs/t1L/o3IPyz/q7ytH+TbAwZz+dCSSErCuMW4PqRJ73JBbYM9JPO9V+d7jpbVhW13aE6Mlt3b3F3d50tq2c/EsE/q2B+4jCL9A7f/SW/Zq1oKafcm8C5pG1muNtdf3375ajvyb/+2GhsgE9jO+xjfZYFVeCrUnkQJ5MHGexMPj+QDz80KyvbMfTqU9FQhq+8jMcslJ6wLpBl/jQtnB7IkZ06mKq7yiWQGtDprm2ZjtoMgpB/SN8pw/dVRS6MXjgusVjh3aMO7r2ulHrqX2RLRNmmizEqWLWnfwiHw9zcwc7Su4cv16HBKr0RLDXd06WQYvX1PlDH0bwhQYi57MvXgO2lTz5fM18Z5kebkenSmdBuLzJDzq/bq+BdeU75tzK66biSgVfl7dGp1sPHG1t0saQO/4IyS0Z06NuVHPJalkeoy54AkgGHyKtc3gQfybYIQ7UsF1JKpncFB1jUybmjIUmRhFfHq8E40Qkm53ePCU9RzCNQjQ+ekPunkMsj3BbW97pF4mNPSQUsZqgAzeiYxUS8Pk4JjCRkUZlqu5pT5NTrfcOmkQV+Merg9q+jMVqqXchuWPrUXHWOAfvFJAWWgB0cqz+ZksR+By1q2gYCh+xa3zvNQfnbl3B6gtZXzUkeJVs/7OVXXy1pLBwYQc4Y45jiV5TlxS1MB8iL2adjR2IFWQxTfH3Iw8fvAdMzyF5rFi2kbkGtz5pCFDqJNy7BRp2hkOuo9YNBd7Jf8m2T+1vI/n+kRFDXzd7Sk3l+7QvgRZ7OcIZo4mN8V5vXuF8ou+PzMnvGQ9sfb+9tXw96by2Sw5JY+rxIeKeznfk8T8tz1f5Wr6rr0ksmP94+3Xz4/3/73f77u2uGV+dkKzGufd6trgGo1PYuoVODmrO2ubMASNpxHO96LY3fZs9wMtrR91IwrWt80FcFKKdfSWYw9rDJBrTtXs4hnGV2a5nDwfO0EUKmgK3wIBxHbcJTMqVQP0zRxcVkCYeXMSaanbEv42K36IUp3wgC5zPkkg6xXmTR5HvC0jyxCAXegdcxF9KnOryL3cn61hnSwc9MLTX1OynLwN7IJz/0IrKixQ123ESefa5uibmlipQBBAPx7b1yX7c9B59xA8Tjn1te6ro6W1z4ZG89oxxiD+EH6nvtR9psZHQdQnEM8s8278FI8G311tr7I01qaD9cecr31P0vfROv5DFBk04KmaRMIhrO8MEtPD/r63LUvAozGb3WvpgygnlfeudRKE+NA8kWJzyOLkSpCIMacCbbRhRqZh/MLtP4x4v3gPEMMzIe1MzapFX0P/YHhOAYC8TDZ/tvDRZ7FuGodLZ7rDiYX3H2aK7roe/4bMagGmMZSwyvAwPfeCNTpWtKKD15GdWiBTRNU1uh/lGzMtHebSbLSYVpHyape7P9MQwAu3oczSw958obxv+feO4AaStv5bo8bczzPHvXnFnkpP/r6+x5Dt4vnX/KPks1fKf/vf/t//7neMBnWbmRqw1uIltFpfaRu+72pKk0vYMcfD3VcYf6mcVKcxfh8f/tY74XZLYnl7eE5TzfT/4EJPX3L94jk04P+tAtT33p8eRmbleFZZYUb1NjLTf/7f75uJ+v//vu/oYzLXIm9/9uncQOsd1rIUVhd4XbOuLtQD5bZgM3ZNTCN5NVI+UR2skr11ZL/ERm3qKu0o11/zB6R5/5bcaj8DwIgK8eqZ0L0jG7gFF0WWLNDnJkZ3fc5YDud3bCKXgKWJU8mVzlTfZpocryzBgTpZ27ynFPdlUqTdD6jHE+ZBoCfyMo0Am910PSZqYiURwMAaZDCCXP7L6AiNrNH5PX2jJ7DlI6qZySTdvjey4AS/PC1sJ61cEcv595K6RQL8LXxHJeoWzM0WkMCoNgqn2R9EWqWQhmf5X9ZRpjVnhk8aJlKLynb9EbmZX3ez+tjM/vELInWqcjguq9lpFJOJJcOjjegzAeV80oloPHN9VWCmb0uZx2ZmngOnJcAjJFxAchsAOeQrZuSnhZXiVLXUIHuQFNnhAXuerI5W8knkrR6rqg4c6GzlXhujYMuyubSjsznUGQoSGf4PHnMDZON7MSk3PsDfd5qd63AXcj8QJOnLfcA5mJKF7R/xoDr+QzYxX3D5EK1+W+KA/es0FUGRUZsyUUp4Wp72ucTpL4CwtiLzUveaTc2dSBGX96UcSEt8D5x5HpDZG1poAv3/W6fO++LRpw7mwQFe7ZndT47+O5BEOEl/0bZsD+Pgi7r99297Wkpds4MQ/AEVz6S4u96NjSbc7MMFE2ufd5hrLdvHyvLsKhitdphCX92sudOKz2i0Z/+d1n9xUp6V485/++f72+/fH59+/rL59v/+fcvb+/vX3YW7tPbXJumS1HnAcp4t6x+D7Cq+Md38t8PGBo6c6EgQwwkOsgs+HmA7dq1m8PB3Yf8dw61QCI5z5FhqdgpFPSmtlt2nYGg4Ec0GZqZznGChaT7kVVpYI2Zsl4KVzJC7vCnm5RNA7ZRbBBwTzHZpzSRKDXxfe9UViWw4axQwZsNnADk6uWuCJhoAGYiUQaXyjVfQmpgzZlCGRO+94fOvC+XEX+ySWRQ/MtNa3TsOgAGoo89i2D86gBOno7T6bY1s3tDEURC3KHsvdSpaN//JbjXeiCyHUSFfLS9DN6NUjNjmi0/7cySXQu+RhZGZkE0ZBZXeii6zLkntMcLUKyyPDeywYMNcHbZwRhcYwXaOQZBT5yPir1sDpvTiuZsMqmSpTsnUdqoOnQJXjQux4jvMA/eH3Npk5rmSDmY1tOdU6iOtK5weur1GQU4A7stWn9jJ/q8j7+ZkUHGRrbjzDA1cHhn6/Ew2oBuaLgni45XgWyCHJ4u2EHLUkI32NthA6c91XKgVs8VtiDhccENON/nxJY9nTZGKlmCrwiK6xGCZz2zjPOEv1DkqU2cU+Kznr3nyi/nuFVWbQIv+Xc6OCGM9pD/DXriv+m9OTcOZnD0VCyL+8Jq9xztxQ6dXG+ujQw30p+xw27obJ22zM99KNViE/a/vtSS6bkT1TsN9PotKdC1cSu0fn97++WXVSf/+fbly5e3L19sM1c2brWm3dkLnJdQ4nE79ptX398+3nez7Lf3nRpaZYsIAe/L3HFliLM52FmeJtVgwuAxemxoNbk0xtz14gw5z/HsgD3lbCWdhiBzODs75JaBZzPIz3xezsnP60Tpl56agm/lGjVqfmZZ6vG6Ch6w0/Akynyax6L7wwAf7YXzzEinr/7Okr8sZ+xBDmW+tKOK1Nrc6pbGGRc1L5DTjMvU1phZK9KanDN3IINjTLHwz4pYW8yGYLmdn+H14UQnp8pwsuHCzuQJrKFjmrYswSly46BRKeXa02NGrwJDu63y3pz9a1k0D5gRvCmzdpR+ObGj3Cw0uNOBZ5Fa1oemqTxr0zXXRV6LTowOqPZzWxmn9r+X+pn82lNr57jauTFBrWd6fKI2x2SYCRBShq9kRlZXGmyy3j9u8Z7wBbRnLNHyNRat+iAie5xnoIsxlPipocmQVw/pDmezObPx/SJMnO9w/dksBNewl5qVzfkcrLepIA5qu32xGZLZFbKtGfSirBhbehi71lxUeitwkyOuxkS2qEseyUFoCo+1SxYi60afbbhzAgJp+5KKBE+ivR/dvqSF1tLPhFKXjyDbea5XOWn6DBLINWWWsdNw4pWRiO1L0uEKMHFb+NyX/CcxRbv3v/3jv7a6PTbWjUMokVDqZmnoSGWO2UkcsJb5ynB/4lk9/bfH3G9YVuQlz+lQCmOue6fPSNQyGAvkmHH79BPWNq6F7aBBn+G63/iaP/jxv/FqpuGoNOVg2XW/fP3cb9Le5QQ7DGUAaO/ifrvp2jYVyKyyN+eBdc2yEGs791GtxbN7l7Nr2Bou3vEwWd8WhnOn5sh7K8++Ll/jiSdxocAMxSIcpIjsG3+acYTibnaLyvfw1L3u3pSpnMwzml3KgC66dZXnMHpfCFBL3g7nKsXYkqmxLl/nQKOy/gBnCQ8lw5pfLRtL4Bji2ybFKclgbxK3siGbB5Wgl8r5D6by/KzQZ2Zyzr1DYwiAi75Oe5ZK5ZQxsOdzCWNGhTwSIVmpr5pF1FgJDsybiu5m/V1JTrBemiUeyswYMkUIGm0V6mvLtdQugTknX2/MIfmEwFjjjNkq0IvnqzKgUNe6+anIILvCpfrfQMNtVQVhmLPrp13+FnKsNWQWU9sVtIksmO8uQI1d4+P5jeywlwAos8G1m2C+YFaZ3j5emOALU9fEGNwG+Sj3ZqQUcY9iS8USvbysP4v2r9pCRGMjO+blcrrQlUP8qYYmDY0czuG6DzI9ObnSD33+B3EGA861E7DR6Sxqy8cIkCoA7xPr4CqUxrROMw3535ChWI9TVmSbveEFmLEsrG+Eucf6/aoo3fRSNemJMPWZ6e8godNPPpyCgcFDBfDW2dme23fFJgR54Cv6ZsimjPSWDdn2n1zQqeLP7EA+9KwXGfkQhffEFO0cl/ZTdIqCGE2jBElz+zHcFcuU7wdWjoVeyeZfXf7tTM+0h13SNwWSuY97YjOkxeXQDvq0cVgBMZO27jvJR/hvKTRbvFFkYtrTDrnuQM7+QsZz1PZ/0Jd3DPgHTelXPtb34n29M0kS//H2yy+/bOCzz/3INdsKI7VG+Hk7kmbtru29SwNfkWdoAOKZLVw7avKWrbwbU+lxzxjSaaU3C+gWiqZkguIA+LnzigjneLYYLSn+hfI+2ktTNqTAB/AVxliG14lvczCCTuVvms9ZppPmPR1bvggWjhVKoPZ4TvcS0UNpj93pTmeZp7xMnPkBINGWE6MVvOZrP7vyZZxEs65R/tSLMgaHM4/F2EcvyQjQKGdvOIh/8XJXlmYltb16FyIiHoy5N2dSDrZxIMGcjwXvsaxbOhisu2jXaRA8og9Ropb8rHtMTyeA2bdM7/7xZ/czP4dsuOvTx1jXKbvMDnd5vy1K97EsM9eXRmmkCwHSAwC0lrNpR5pqjVqD/9rBWWaOUMnQtXbs0eNSqlMTQSAHz/DKZhF05OPnw/bkzcn5vDD/j20TJvdrbOsBoHwwjVlsFRYQz2yFD6lHZBM99vcwFpuGi5URxYl2cMNAjZ63+RgOfMzbZfMOAAQNprXIzrTsHk31vqRlxYJ+0Fd9Le6uVVAHXXKwergQ1Vf9/v2/cBIe8FS5C4xb13r/4lXTe9GUtdj94pYMWcZRKF7yX/IcBnr6fw9rGe+8xj6crr26x50GOGN1Op4t8mFLFMVLn7pbIsdiRecsF2DelKJ1Zj9oAL5fJB5r3NcVTnhzp1Ri5S/R2EVquwufN5tYjSfipOQuZNtNKAykrv37eHv7+NylbkUx7vcwnSyMesi0KLElN7z4I9um7AUQj5T8XB4jBwfObnNu0zDWInXj1OxcllU1fr7BWNtIJmMUXmWWAIbTHA65chIyjPmMJHhmmphZCuq3aHasAZHtsQmDn8uQZiqZFRzIL+cbYApsefMbxnUZwWZmCHyGzg7b8YxSySzzIEuok3E6qNmwoDq/CfL2XoiNoyTLW70PWZ/Yrn0P9kFjIIK6BhZdVP6kgFRmO3wMOc4Yx/jEI6PR5VD8U8u7uOb4zLHkDKLsbDyUr3fhFMDL8rkJ3DMzmO8YKgEHFapaQjiFQOBXctqyeJkRM9k5QGsAxTBCRrNQIwAsw56tuwhO1iU82xa/A2jZd9KgCeTtK5bMGdH5jE5znpMUL3bnUnJyl+WgnIvHdH13as1pO/XsASAgXNdWuDqJ8SwtoperQeDoyFfHU0mqdI5lo77XOhzX+4PORgMELcajKq7+LrOD9+xclV5913i6mI0JhsYTe+9WsHK9BxFBkg6WGMQ4frtY8yaZ9EcHfxNjPHLZSkYw6b7mtpNcABadnzo4nGTmkr4XzSvE9xq7Z25KkCDVjGls8dMAhmIeeC5JY6axBn076SjX/wry//73f/yX6dbiOEKMSYW45oFaYEaoc0doSVffKHdJ2TsdmDIMNlElceaYIcq7/9R7epbT/bkP1VtplHcNu3qb1A9pjB+/ySiRb7DPkR5J9o8/8+femYZ5m2xlSvx4jpnxLGPa5W77HUBmFLY75AZuA6NmTINKgQBCjH2ZPXecq8/yOWqa+vk4CDqyv1aQj6S4MBti4gaHshibVoKlqHQ0SBBdGq8XJynnkODEI+ihHCVjRliWCsXZhgBP2r+TH6cot6h7OF+lAySzRy4BpqFtPpdnNOpcejZq3wyD1zkhaO+TjMg9/w49Yl8WkNTK2Ww8XaeVn1H7bWSZgUHd/GrqEZlzNAcoWcELmtAonhkon5ecCd/7LF9rznr8bqvO1bh2GrqlBdhkKWF/ju67ABRX+621ZbbKeGM8S+VTNjoj49RMW8+MVPohfIZyoxEE+Zfa1xrcyKDG/p3XxmfIXXPA9LzjXA8aJ+QOpRyJv6YXyOp6gp2tG1yXdTMff4t3HmYhUqfaLa4PPWZTdehZkp53l4o5F+fzPWMRC1PmARmI9dvYuIEPweei1S4CvnJKKcORGZVt4piYl5zs8njofI6ZLxdN2Z0c0UvwGDrUZOAqexPSzZiaM8ltxge0Lbx0rP0xdAwgIG2DuVP36HPf84vtPL4OOjAz8oCvRZ/9zIMn0katn1fVSujenqHB5sY+yj71crg8JxK2ogKwRtNpXu5XGBHq9Yf1/heVfzvTA8DDg7AJM/vGN1Dk99cszAPgcsGxh4OJ64qDuqeQjmYKoDuDKIJmryyr9/ZuHwdbPCtGP+e608X8Oc/9KU/ZAocGyt6bYJf+7P4THq71DFFExFwxqhRuy7hn8ZIHHivcc43WNnvr/RIAoMqdXOiZWjL8eyxPN9KJy3Ms4LQCr+kAACAASURBVFw9dwgERLmbP86cKTEw55URc1tLGs8SFJDC3M86y9doFPfnViFIR05D6R5bJ0sVfaJ+ziJnW41HnhHq3+eB+dgbzQd71lt7S+9zO7kuW5N9k3uT+9mdbV2ZdO/ZiTyPGJFQoc7p7E9rOmDZaN8v7bMmhqYB14AQZVsELeIBBwSxXnS2K5kHOQTxr30ISpXxkl616xjkooCprLgXn2xerpfHmaG+L3ZtakbSQm/0UWc68lncp7kg6GD8nZlSymnNAqVNO9p3L70V9Tsty9qaSqxH2f1pvyg7AcT9w567y9NeL7OFoQ8SZKg1vXWiq7KUbz3KOSojw6xBU0VFyWnmOxvvOyeNe4CRAyzd62at/d6h9v0a3ksj3tb9Pduy5q7zvl3PE0gQMEzvSDEaVGBnRCIlyKn1+w4CqwTYyHZeqwZLNovFOdN7S51Scl4nftuz8j/GKP8EAqEfNL+gkT+K+2dVG/1cjKgFHerUm0r3uJbYJ/9y2uMr/iElDhq0bnu8tstG+ApX5YSNRuTLzYM3bejTfLpOuplXX8/VuvX9S/5Xy+qpvK3IyI2SupOqZ3fjRm6T0R47sabMqhNiAmkH3r/trl921f7Oy6JobNPdeEZk7hXO69dHFKDK8cjfUow7amJ/70Rc2ApvcuBG3wx1eoYBoIZM0KOZjBb97qZn+Z7RfbAwO5AF//nvJXMRy7OrDuATfpNncdyJjy419PY7aMH6EkgY7fvpgOKQ4o/qqMlymuEvYCK3ydbRywAdUfXzPFmuJYf7PAAez+pgAbyhbl3agk22KKnzIIlbpaR/gsoAjs3Zzpee1syClhegCQZQWcp0elVqkZ5H0M4JkjKgMzv1BZ4VuGE9LQvFtbEhgAzy+vdoKc4Oady5WBNKNbXPhW9rmSRpac78DDgEgCNij7GZNSUPSkb2uOk5OEiYs29aO0EF6WFztMVusBEguwG1kgVrgQfyjWehYt5oKS5As58vfQCQ18syo4QP2a8ct85v7yvAZ7eo/e/umBkF8qqiAnfw6gwWdTVZQAXKmbBVoRnWeFaWk/qCQOR7LLTxQj0j0dV7L7t9ND7ppXUezveDZ5IeVFfKHPQ5Qt1H8mHM0AGo2DPa7kLm9I5GWuNYj8vRj9C973Vx8odMB3mFc4lo28WGiE91vm8N3bN8tF8Wf8SrE/Dg6RHxc2fmK//g4n1Ux16muo+fzoC/6xF/doXN5zEnG4h7Xfe9lEK29fwryX8FPROqF5R3d2UkrMIFF6lh7Op1Gd3ERGU8i8SHMSvXp6MWLSDXoT3vFPZtpQ52jtmd6d3VbcEfvbvHmKVWjV9x9c/5/lkZ+zmz+T2f4sU2Fv70PfbzV3t7FkhdZ3wSQegUBdPFC9Pul1JvQKvUCtWdr2HkUam74XoqEWZHwhL0+gqe5TjHiwAvnLg8g3O+0JKGLj73yHiznhnBrAeXM0Kc+0nny+ZhtDDndJuULMXTnFuUOkdrL03tXeQQrVZZlMDrntv+3UaLd7AEnWZHX7MEe+xZ13VpTbkfvIbcrWVzLqIH7w5nvZVb9flobK5P5EsAoDVnhqyDxj1ftNrnvQW0+mRz/R74UVe6KKvr3daMu7bjHda/ZbK0Nx5h1x7tR8JJ17tzcu02G1uDA3SVEFqv4QD07EYWQAR8V2jXgIE9BZ0/1QTBBaeDV4KIoJfaVfvFlGDJj60De4a10Xm2F662d0PhLJDalifIPRt5lFI18LVlcXy96310AghBE7Nmwc9dJlzGftTmpX2yB95VaIiG6bTV8rZHgIQydGQjkKFL7WV39DntOWviBAb+ALH9VeaANpmfY35tnEffS68U4IKbIssTD4OfI3NYnGwECxFw6+VZtCkECakH63OoH+PzRAAuOHw1MynFwqLsrp8wmIBgoaMz05SV4r3hxLd9Zovw/qyrJT3zfadptykxH5YT3oDiGO/IlIrLqZnyaeTxDmT6nKSUxwzfuOntOa5v/2ryf2Z6boGLAw86fCROdwqpeCYAA+fpksZ+31Olc3zGNjYf5kh/2e8ptXe8qFzq25e3bx/rnI84b7GICgMe7PhP+vlOr/ykKfz8x0yBCm9YsWPGiygfdtj+i9UpIFr86al/i+rN+IYpj0EtXPJpL/H0v2mRHSAlr7bxERGlUxqPDIf/VC6lNa1H7uR4beOpSKtrsMgKhXMGL40Oqj8q5+MODCLcGr8fmk3nzzm1nNlIta1333Sl28W/HErn2SMVVmEJuT770rozZiPKbhIig6Ex5DDuzGFa4RgjIyzFwbUS2/ndS0FD92a4x7IPLGsqZYVT+WCARgZk9FkZlNydY14aU2BTe4320QZCzqYLe+64rzvHtetZUrsAoQZy15CWYdpq2f7zUq9oztHK8kjTzu8bzrWzNyVTepxBSlplU4xcmfZGGcbN357hIG/aM+0XXWN/Om+sRgldrnyxmUkjt848JflYuo7lpeXZD85LEeilvNrkWKLH+ZpoOHjsFuAZo+QZscDOzkorg5EZRhaj0kPPz1dAilO4ns7s8hlfV0e86KWLczB0kOPzzaF1xcr2wf/9zsCzVE1nggLcNDLEvLTIwRfCckqUn7Rb892ZFujRbSbQEEFBXzrnAitB48GgFro0BZ/O9GMQldbC1YJ/ATURAJ4gqrPn1d8x/gA6tIarTN3EhwffYb7zUfEha1P48AxQdjByBdLq9zDuFMBU50GiCDhK1zegvHRpGyJrLe8I/yeX/yfK27C6O0B0GUHv7kftxibDB1v3LB/P1+26eH83j/I3mw8M1OR/Vj+/Zd8ZQkbm2OhfN6PX3U9RwCSFB2sX5+z3+ex3+Fjbldir3RHONs6UVRydyaYHTz33+iIpuyvje3x/E1rJjEaVh1SeyJJIb/m7QdwMwJGq1vaoTceSwlFsrYLlRakskA6lHKyYaXNeLcgsp00lV2l+7T53Cpum1hkM1nnrOybs07GrB3KL4+t/7OcR7ClbhQPtmpGRxqPgYVGvO7/RWIqsBFBBIzUgQATebImeVT2tcL58NgEA2cEreDuzidWZzgyJnkUg1Ut2yHkxr9gqOkWefcXcIvvj5+cIWLczDbrGWRU8kPOesliKZxjvEWKdpZLxPDRMkHDkXJDtAy/GPFrHupzfWQYXZ2PaWZr9LHhr9mzXVx0YiXG9Q55BTf3nn/dYOv9Qs6a69gRmyOhcnM2zeVbYWt/7U4MmoU+K89KcMrjYV+cTsGRXB7ND1p25qpFTqT4DgngvdV8BYJh7t/VVhftfQzbpcECHMjLqjjsHdh4rVzKNA7Y7HFONl7blGUOYK+d9x974j03NHS2WTR+5WnZUp4CEaN7101bjyEhq1lcd7gg+SYSJn8KWl7RTlfUEn5UWmpeZmeumGgLTKdVnsVlaxtRPetp+3/rR9lvUSqt6ZEYPZrgG/P0ZnM8hX/+k8j+AnhvPLVjy4poh05PihntKRqaFOOXAFC9siLJzt+IhqaSWk/yxOoB9/fb27Yu9YFLRDmOjPFy34dB+oWmeHXhGTfzMa75Pgf3Mmf1+zyqZBQc/4iKLQliWx6JWBmztxabrFFdGVkKRHHwnJkr1Wivhp7XdycegtmXtyM83AYJ0yvzZsMqH04r2yh34MDIsh5j3Rxkbj4XrggKQUC4ES8bn+emrPeF+VsQobNwr54tGYXJSwhShhbKi7xWQ5bi7pApGyZbiDigeom3Yc1kNTQIgZelspROdZ1lkPTfPSdRIfD6QIC8c3XaOQxAynivHwtmz7LvvQXmZpZxteDu7XXVksQFKyhkdm2cFIcZwpdSw3bPnwzItkcV5lTQu4Lc5NJUfPNsBZ2mNE8764GzYPHgOqWZMKN1yGeyZLH80Nsk554s/D3AWWZXarakCNWbgfOzjmcmrBcSIrs66mqu4KRpFDMApr2X2q84zdIsPGJlBX3/vttjloGjDq85PRzlObTc9ndMttg28H84gXOH980WGxebn4KxlqugIi2/21eDZDkzUqIFuZwVJzTlm2RaaV+Q6TqcZKvXotrbmyXPKvLbvRTr9vpkNzR1+MWTRzGfK31huuIjDxhEAgh04FrBL3esC2elMe7DnIln/TqdHAYhjPr6+x7a9JzLubb14UbQn2NSaOJer+REaiTeLK80Ag/bpIiPZgVfhE2ZyogzYbYBfqPnelauagbjo/PgXkP/s3lYcMbVQTS+o+G6bF7pKrG1Xj99RLtI3ov6djozASUn/IrprBx2tH8WOWPtn132ZDv1mZWtuS6zsaTnHn+Ycm9lc7cP4HpjvlLj7Rb1+/V4KuEbZin+n4a2szUrcLLMjgCOoqmvl7ESaWQZwAiBlXpOSo8nC+R1ZzKLpXCZOYUGt3c14HoWSs8UDmaFYIz7szmkACnhy/ojtCDW6p2OdmRqjl7uFeVSgRLGL4wWBrKVh+bDisEWXtFx7ntdweYc+YYTWaKEyInk7rqjlIGFbBHk/N8PUDFBx+uJelfM1Z3eTVzR2Jx9Or4xLgm+nYKF9uhoyePZvdgqjwY/5ac2MzhfHXOuHK8OW0dFIwnSy6FlKJMEb5BO71qgYwNZpYTRNmuy1e9mM8FWuU/t2goj9BG8k0zuMkV8DnADACfzaO4iqwbbr0yPu1wrIaY6lvLBlQ2zoBM0FLOMZpVMc+FHrYEaFdQYmHy5zFq8Jj2t/ixI5o1fdxzzgamvW+6P2tQXUIZoNs517nlnPvBJgyTNPXX1L7Zn1ZI4294TnJ+L+Bwe9H1ld8kdadH2bga57xzqBAR3RvaZ99jefkiNjXRe2jMAqL1E29qRR6Cf6NE/ayUKHiaadkCgDlft2ONZjMA4Tgt7qjQlWgMVoLp2TpVETCBppJflxYHuksHwqfU9qmV7x7GPTegZlz1SOe8swyfGHdr2ayogx+tp6WfiTW1yyZzNvwd4O54PiHvACs2t9nYeMD3Id9sqf95eUf3ZvMyfLjJ75Cw/aTrsQxH0EQ5QoaeKb3e6BBHu+H97trtvgm27iL87eL7n0MoNtlPMlgOkvrus+397We3tUkBDOXpZ59IjBs8z6uu63pIBUnCDMOmj9/vbx+WFljAVD2B/RCHu3wU6JL3bgkYKH01PCgcmYvkifQEXmMwFunmnOo0otadLMkChLQBHbV3kJXGnXTMdvMOChuFzU5Xrt5/s6IkPRdQA6xKXj1LMFbmt25s0DhPIlAhToqUMmyTWQqCDgo4h6Vbw1QtkpJ0d/3XO8KJOOfXE84YyKD3g+BJmagkGjpElZo7C5wQ+xpnB6jSD5Mk04wACjAn77WiwyaaSMRc697rOVjlr2x37hONL6tdSwlZk1R13jF7ru5WBP9kUaB9k+OUiNrmF9CogTP6VY7V6caOkLNt9bojmxrXKAIlm4cATnlu0JdMAPTnDy2XoW9zVK9VDCtjtSHoDYZdv5aY8S9EuqCYQqm3nyUFjseGKe10krlvuErFoQHC+53d5SA1lNJvdtk6PlOkQvsFTWYPDBi+rWztKJyvGrwSdQMXVcSyDp1GN56bj2yQRb1RK++VDorNovvx3ON+S1fF579pOPOQAA7nvGaec++rbH3i/dXc+5DHMcaDmBHOrs9XnqUCcbaCqjavIJkFYSpXBFfH3c/GvC8okjYL/dk8EpRWBJUqh/p0ABhx9ZtH2pP0W3fgaHl5drowNu2ime+eJeMXjQ+emvKv/fd6bnSUH8vssMdpx1m0bi7sRsgfHo0sf7hxs3FXevl1rmIWO3H3Huw4yGojze6GAf4gV7hNadWOb7Vva6+tdTgLtAY7b6uW3u2FJpQPdzlyt5qt4tYwqt12w9CG9UoB9vYnCv6yb0gaU+juzkJLK8iuZr4vqcgtmEpgFVkqa8s7Ir0Fq1JM3HaxYqr2H+zI0dIshM6ed8kFlxg8PStm740ibpqpR3OtIc3yiTZUyKLkbmCA6oMCapmdkLOUoZ5CBdw7FEEXTJFDBTEpHaPGdWwEi4/TXzsq/pDrMTSRkE+zPnaNt+BqM2aHIrdGTHlLGIduTpCK/h9v9fgGMedu++iO7NAsY8h7LXVpx8E9XueFPH7zkca8hMRI6XvF/fSVN9JYmIDcuudFqzZCpbjnfAFvt47FUtpQs+By9E9rTJa15bSycLDyMKTVpKZnrp3fpePJB7mjoj5fBswpFZV/KB72UDahPgnrT81btZjOdRVhaBnrOl9ORGFqvcztFEQOhBtDKctZ1tdEiJjNZkIujgTQ7p5OhTo6euY8bHVrjHGwal3hp/T/M3ppHCSQ165/PWfHrpnIms6yjw+6XJxJz79M9ny964RrvYo0XH8WWubFTT3DXtzUTHq32bmvJIZYWd8g8JWKqt6Hw/rbnzgHTovMY5kMD5xF5cdH6LOcTcZzCW5uIebJ/AJseb5EBz/TPL/9Gy2hQHxU193sG0oV36tveyOFHePbICYdxZ9Q4icl5TKRpJizLxDM7H+6cp+E9rivC+27N9fXv7sv5cLY6/vn1bbYtdQ0faVYeK39al72+/fPt8+7J/RCQdPHK3qZOi/yO++yvM8Tehi2u0VZCYTpbldYxnHLwqU6GI48FzsrrrwBf4vGRhrhVFrGXM2jxxX2gFhFIjNOXzKeVxlCG5LueZBObZi/j2BgbD2YiuVLfTFINQ0deyrOC9odQoxnTPMx1ANzdeb2DPOtU6HcBuhKSw+zkahU/yHIg5WBo+/h2AHjWeStY0/v5NKkxlgC0YWe73CWfkrbaC5v7s9fskOU+eJ4iOb2CvUipFwMISPM3RAbCNn47PBgP6bgBfE73IG9y2fu1uoex8ZO2U7QpbL7NSfl2ASG9cES9ntN8jm+JMV/lG48pmOLfpWjzbAGNmUQowciHqgGJf796GSK3VJA3dPhJEulz3ttPKWJYXn4I2pGXyFbJnrMByr6RnMftZoTrvlAmVF26ZgVyIRnUucpBUbko9lDMNvdAUv76/+t0uBxB4YDj0xDtH8HiWp561n5bZpvT6PraOZ+NUhoXskQaHVKLbD+/H977/paXyVYxtImAnhsYb9mBF9fe+B2iUZCYP752436jr8itWWje52dmj9lLbTls9VrRiN7RneOgK6MS9rYPbXqti4S02GjzpWXJmPkKhBYDrvFv9gQBnKKMsJG70nvjbMektO266eUbfppZymxJrn64AbQGSJZva1jTw6J9d/mfQU2QACuGqPIelPQcHo+Cexs+jLNqEiSElgjaDzXU+ugGVVcq2D6/vHbL3iajOXGU/Hve3d1x4y2p794i1r973rpK4bQwlodvMegbqgeb9qT8/0EI/dS4//2Hu0sS+WFQKkWCPXu2zWTuOtfZT57mQU9G+awmdr4sGvDjHI025ZaUazcgWjePqoV0uWsiTjvlklKGIGLWik6L6akv+pApN56aBAliL7njVyFg/74G0hy/PyvHa2RXoCZNVZS3SU5hK2TpICPVUnDSsD8DEAif2W0LG1Dqag22jrsv1lHMO6gbnr4GKLMVltiZbaFNakrZZnUkO2rTDF3G9aAvLH+d0Lu4RrbJsTcauZyrSOAZQdEcg5qLsIQ89lznVMqPuSqbjbb9sMBDgSNoegNvlZ10tvW7ryHMDexwhFwAnZgM5j6TXWZJpz6HVqdlA7lsC4tRK8iKWvyHQ3kvSYj+OwALO4nRw6yCNfJClcs4MrckCTXg5dwRRrUCp7Z3rAtEeLFfPDAXISyDZeT18Qr/2stvbhZMvcuz1E1A0cxiA4iYDYregrL0989rC1oYMk/UrTi0WrWfS4dT+JOjJTd/jmDgCWMwONRsQsFrmah3xvdim0bQ7u2EiW4n1aP1TBcfPel4pQcT6TjqcI4dZmhYV380AIzXsOOOnvhTfJeCovHDNM65LuJ+ZADd4f5GxKTLUQFoHXuSXc0HXQQRt19Ri+9hKlk1QoKX/L2S302b9/YfLP8/0VIJdZG0u2aQy3VgPCyNGcBxYZBvuLFsywrvjIB/Rn79fWvn51d7LsjWzawlE9vcRn/XTLqD0lseOm/SeBgNMYgz2csoIpEZ+SkJeF/3OFEgjv19C6qBnc8rCseurcCjMOzVBY2TDwJDiHKWkbQL2V2C/r/SJe6N15nFWZo44HsT0Zxzn7VjXTg+FouHEkEI7XpwIBbactogIIrzUy99U8iVCU1nGc3xcHqTnb/nZZS632IdVpz6eMekuhJxGk+cAT+XZdGrdICHeNZV4ST3woH6UCuk8FfBrV0M1IyNuTI1C+sm4ih5HxgH7s0dSaU4DIRNg4pO3ulS3Ozn6OHNlTQx0R7bFJnDSrznHdN8CtNF5J0CVfDJDSCcxQGRrVy1HvLVfDnqJPmAu0T/mWZx5W4VdnvreMm8eTGkxDdtfzCu6GBnTkraS3TBPksU+ttPJmhHkGVTyQa6xlWMy3oBx+36vv1laWAGPA0mMJdD2uV5wpzWLb+DJGA/6/yNokHNvzQHkA9w4e4ejpIyEou3ebEAsysT4/kw/I/talMh2cncGAZQWVnMO6t6Y040taB5QCaXXNeWVaYmOuw+H+CibRgODxyXVhyW5/oKNEYYmCXstZR/PuUs/eVzS9DhdSs/4jEBQ+t+DmNGGCnYhHP+DWZr3fTFPAilbT9oGja2MyZHdeYaU4LvCRz3LhBbV07AEoXJtGZM97qFsMpOD7FLIs8vrtHs9UNFxjuT7EXDb82dzkD+B/P+6Mz2d4Z5ghnqLkU4Zloi/KK+6O3axC0i+OHH7rBvROLdu8GOOrL+zEh0WHdS4U2xJIzMu60BdZgPG0tgnVvW65OdRoEq1VTF662EvXVlAdn2fvo/xiCmvdG6GgIXrgwcNPGKxxlcPG3504jwDop65ZtSSHiHEbz1SbxT0skCBRtf8cg6jcxzHkSUr7VhRGhpOnd2knYIPWiPgw9kUOktFGukgX3U1g4VIAytnFOc2JkfW1ymntawfRljkUHJBjpfWG7CsOMbIaOg5PTvh4boCWCJ6f1bgpBPstAZQIFtY+ZJtRqFtlOsp4yFnOrtNZdYjv9M4MmaZsSOQQikWaG3ZP5PBE2DaJLeLjfmqZJFni2Sk5ZhzLDvXMmX1jCr9jJL2rfzbGlIIQE8ZuIOmzGCqbJHr0dkiv5H3TyV0AhUmegnoKVNW5f1umbOS5TRiHq2oMQ4zYll253sUwaMENpnNqnLVZTxncgYnkj8rgJscK+01o8NdryTcdjnxgQz/mOJA9/ZLU1V8Ezj7/L5nQvpvV+VHdxH9uzGpbybnu4xL04jSNWZJOQb3TPsaDvAFKC3dv8JGuX81nB1iVq0TvtOkVxP0UizdLz7h/bJpd9ymNdp9EMoWEM0QQI5mrkMt040Tv07I3ZwBLxIutG6FHcU+HrSOFYYNPTJlRVg6bJlB6MlLed2mXxGASzGpP/R7SKMOeI4h/xj5f//7P/7LbUajfEii7yjDKZx8cc4unMB9TVgjz6xYeZkMFBk+EHa8RaOyv1376Z1F1uFhQ0D725iuHVI0Q7re2bMyQ/ajHBu9l0fTu3KCn9z+dtmPcNCPPelf9i5J6ipX80zPt4/3tw91Q7Kqxyx59C2JfQYIOuu6iyk1V611Nwwl7LLBkha/Ifm+bBKVEj5DTmYgldf2hgsVfAnY1fKqcNBbUmly1KPYt7QuVi34kE1h6RM99zGBlbJRndkziq3wYDqeRsjMkLRsgNM59gKZDN2XRs+cYzqWYRg4DqPpbbx1mUD3BgBuTOXcZybB5w0+CKejvWuG51/CEXVDXbI//XxWd6SjoK81GIAzq6xFcVo5x2NM8ZbLlfSp6FXoVsGPdG8FYJm923oZ4rAf3QFbc8Sj8UHbdwKVWq6X5336mnsmUo6UfV9NWAAQYyrADWY9uH7nbWf9DmbEL3n2DZlLJ8LqTrrmIRsXvIDMxgY5CzqW70TIBKa0g3w2sLiBpbDR4vN8xXcA/3ZGSnSVnd3P4nw2DR44Zr6fqr0oISyeWbgJDq3HqHSn61ObgtsN6SsnbkyvyaqAV3G26UDfGOK857QrYUfQsMS+8wJt8B/vZqvmO28jfrug1f9n74y2JLlxJJuZ+v/fm96P6HncmZdV1R4CMOACJD2y1CW1Wso+M6rICHc6CYKAGQDS9TuDY/acV6VX0GXBQ/lIEocUy+ikAicWkByHtaz2eule2c8T6bGtCmmLX1w75kn6qjGTuMpn5vgO/dRaOpZtHWREMWx6eYThQrznoPyPIM2iUyWEuv+0JkcpKB7G51KGYRI9wyMSSFL1b17/256e8+buEAaJT8NqVQJR4d1cSj13aLIuEXkpAcHcysL48Z4Wi853QcR96X3cIa7s+0c7sc33efh+Hfei7qCW+fvV94OoLMQmpb8F98Fu/YSffkQ9f8Lj/hZNOExwg2PQP4t9VvRzZQr5slLL7i11kwPYHEHlm90IKpPj+tf26+TqRjU1HWj8nmSJnuQUVCBZCMtLQxybYHz9hJHnatLa9fIM91pbxgZp9wTr1iLWJLJi9q2V3ECZ5nHCgX4K7JSesyBNQ+Zxwt5N76dAJ8GnRFwkTFmPGONp71B0NfszyqG0md1G3ax13+fisnWB5fAHmKRYdnmWXjQ5DBK1njOj8UhTVoYG6EHEzUxcGN1WfpbOFqQCDljybGSSZWXQiJyDQXAya5rzfy4/lIxFVCWz2ouD/V2au7ipygO981u2aG7qX9eEZ28ZGi0U7VOCOm+yx/ywjzZPo38+ln7ktc+J/6+RLwGmcTBCBUv4olWRp2hHcxxglHpXZLtITRISjNPvcWXOcWCtNELNMbW9XBpTnRZn60ljjbHNQEL+Ttmob9RLmeb47RSIBBeTKSx2HO3P+4BGaEpqRQXY5CZwn1k2eDjpCi5dH3kIidZOQqdor7IAl71CESVnIG2OIf9uxOvcHgkMgbZ5y8g+Jgorrpuycn+yn7AH9XKFHYJ/JE6ypZd2t+YO7VOF5vMnOTrpQK4jtH3Ucgcy9gAAIABJREFUuX1oWpjnMR/e93RqV7aDWaETQdfSpR6d22sz0v84CIBwJbFCCCUhklVRydKdm9S3p2Atp23CI41L+9dszaEK4XdZ//c9PbI6PVT7VMrT90aUdUtAk45AbYeFjFcsO1MP0hKD9+8U82D4QZPgB1i7UfWbCg75favcySJNBt7kceLktwcd+frpP0MCNu0w1NVrkGnpXrx7wA3kKQ3xHIG0ZjLcxvuf77uum1MfHpo6thNtkOw8zVwCMjn0tFgVpa7IG8qd0iBFB0kGoo1JWLTPQR5Cr4lhH8rZogyssSyBSDeBys7IOPql3kodSb+XULnjnlkq7ukYBCGaFXBNccmezAMMBN6aJwahCWPuqnfeOJ63hoBEumbkvpHUtKs9ayLbmfqaJpljrgh+AtNRvtgyHbKzmm8oWgJt6cWQRwfVVYpUgLjIun03lJg603RY40fWTns1kwhin5frDwhaeuUoEYvGeQIay3oS0Ncy2Ihptg+C4+rX50gqKd3qBy3UAs3sTjxJ4zd/mXqqbJR0vIIZlKfWgS8bH0S1HyuV+ocj4v3rnh1tbeO+TR9EmmNtzz6pvIYgKR7XgOWrDMS8/wQi67jmbmwnOfBAhqwXTH+uuZ0IbP0Hb+Jv7fOM8Ef7DfSRCM02x96b1bbGWOqN8qz4UqTHnpPBAo3p4IhOg5OaXlA4+0J9l/r57329f2aObyRMere6kyNgqnb0dz5rH6J8yyXTgrnTvTcxlQ8pvXk11rt4z0Ah28s5rkwrTSqfW/39BP6RwWlHA4UZiiwsSVz6tGHPE0ZJl6MTv/f6t0zPE5Aa/WwRFajUeTjZe5GbSplmtIYMEg7C10+UBMyVX0nrqjlIA16Ogt5AXMrIz69vb++/HBDbPtivb/5TJLD06COgerwc0Q46aGFBRRFiD9BpbABA3Qx/1hh8RmAPh4RM4/yZ5n7rNQKCGXErc5PgJoosOsiO3ALAcRo2kE8HV/4fb5nZmQASqHUOvOSr3hGZ05hGEMoBTQeq+7XuDVQxixSd1HX6va5nC9gLATBQBtxHlE5Op4rlaAHqk8Cx70HgQm+vhGBmqRASs/6P8GaWXW337ZkDXxqI0sfgsqwpCU4RRS4nl18pn3+sbFlFDkFmx4tDawxlyjkLnCO+0G99r6OgafEFeDbdkEMl6cjvQhebPHdgL1EXcZkZHq2amlvKxDIADfBLhw7vzokBuHzQv1xUCgCEHMzmYS6SmGvbq/qm9uLZ6I9UK58XwsxMYmhbDwJ4OzmumcVKklalq9KZObebGbOmvaRPMr/g6lwFmVU3Rb0ZxgrkXNt7ldUYSOjoSkK/5DnsYJhvVSpp3+carFJkgsEn034s2Ys1mGGiEVxZREgHA8h2tWeMvUwS7ElOvP8EoKWqzGpNizX7MNx122cynzf3T81MxWl/VdlrkA5kcQqX7qTkKeCfMjwKtZcKcgmXDpQ/bOV1h/LJcBnIJvfCleYSCnoPVep4homJfd52LSxZ3HHRJHO6Zz7rukx/p/X//o///t8qch4lPyzL8ckps1pRZaSj9Htrx9W8sn3laTJI1cpH1oN0gptHIVW6ZC5VwYoQiL2nx6rgCm3lJnbYPes5NezVK3GfrM2P/nZZCD/azNf1dwmYiNd/ImT0fZ3itnb4WjiooKrHv1SmUA71sKxb7ElrgREMrofr/afyuRnD5npJBHeqMQCsme0y28NQF1FpdHKNRY85jYGgRDULZ3CtUe97cUQ21hVbuRrBMixePVfjrM31igQKKDET5fjQ57nIkkZYYJCEgPOVMthI3GmPUQHQzCzBy1SJG0Ampm3P0JRxyEMHEBFn9s7GPkioOygA2JhbZgy4R+hKCkjWQnQJfvWMQZB6WyxtW3IbIHfuI4vG/RyaklVUX8Vcxrhisk7zl31EO70MrsfFZtZOvdZYpIPHAyDSB2r9dDKYS5cZDewFksvRPCogU+V1dwJa/e6ZIl8Ppc08AQ5fp8XZDjVQdhQL4pSN6vfNo8m5Xg8ldunyve8Efavvsi35jMMBJ1yvN/B5c7Mpn3FSVsonbryBrzu5uO176UBw9usJDvC3CRpP9+m7428HF3IPVJddyw368hcnt3sZxOxzet4E72WrKW/OUUK18VwUkbdfnuQ5u95K+ybBO2XPQFLVL2vzWCUSMtRY4ZPy8sNpbbV6fG1sWa+w+QzCag0ldEhX7IbWt4c8BGpFhk4HdmBsT3rVfWht09MhQpz7TkC9X/3gCr/6M8TyX13/bU/PrhCDAnOys7iQplXlZSRIJbY0djlBYjtwInH6Wr1dJaZaVW/aTK6SOHPEUXe/rpFnkXLlW8gDFRuhipK5K8U8rfK/6nc/YjL+5DIIi5HcNjMLsbE3VeAj3u3k5VCbGmiDa4C9XBcwBr2UMxAdLdsMtxxEd2o3LzuWvUVPw7O0QMRtPcpxjazOK8Odb14/kBIHdwA6GVUdJAEe0Ne+QGx31Q6Eor1DArZZmHZKWbU3I4RbSVqYWW069zHsEIDPaiVQbZPxLJ2bNs7LaStq6K02kgAA3Z6jKYa+9JKnXe4NvMV9tffFg0ikJC5vXXgodZNNHTI73ZNZADn4EKyurSyBQ4Y9o8VywwANLdxQG/dZmtjGIJIx1lhlgvqx25r7FEFMDolP63+6qX6yXdcVVya9FJBEhH3t5Lyyb8ymSCtZcqb7JMPqn0jEKGkzldQ+Rx/pJFf2HQIwuQ5rKbrebvNROkgy1I5nD/0+zlOecOdrggEVLz2Uz5+naDpYmjGdUyYhIQZIVlTybaVypplYd9MfbHt0AIuoclyHM46F5WYf7aSvQ0Bq2qXKzDwA2KH3/ufz9dPr8++0U/Hl5h/nzcfnu+BDDdsVJqdb27DLW7Pbc89jPBEvu/ITVRT0CRp3B9n3PVgOJIQF4F9m6eLD2HPMyNBM32b60zLq9Sze3/bBHnTWMi7tJaZ7RmorkZR9T3x93hO/y7HLrROeWNeHPvYp/znr/35k9QOTvQm2lPSkjBp0ERS3t2vj6Xd7z6g557XvRgTHali1pwdizPNo44nxvgyVMzlhWtPlKDfv9LM9DxpyW7U/6/vPWomf9by/czvueuw9TmG9F+RzLrw0QrqpMNjK9hC+/GTZ/eDUZxbm4hB/rHdchz/mNOmYzM4NK7aVuKisJcFWZdi8rSCY46jkLEnIgZWLdTJQAixQ4TaiitDla/o8JniC0HRFRRZrH4l8MVshUckshAjLxnUjypbArO9RyfZDNDY2Zd3wUDOF0cH2vuR8bp2xylIrF3M2XtmSQSSt/SmT8Kx8dpIU7UWiCZZOWDveWrY7MveSNWW/PX+MzduqIoRwFQmkiiZfZKwRJvD3zpN0NW1BeaLaLhLk92pvkx9/PI6Chvw0Dy7LEnTKFtk4rmcekhBWLKRQ82U6rUZHZjLlK2CS5WgeV9X/RCzCUF6P8a51W1iudIylfZWR9baLgJXOe+/8SPEi2tXnyALFF6UruA9ZzmdgXiT/Ftf0LhZAtbWNqmN7/19E+UvDLxVzZaIezPPrkrqan/NzTmCefdMiZDXC1qHoay5llBeLE6Rcho1IWYbcVhvtxLK2r6heIKz7Tv2/jik6OOdPejE9m3SZc9bijhLUCOKJCGNrXNoyu5R6cSK7qwFtVA15TdmWGH/MB6c+cM/QpewSpnkL4h5JoA4OcsGl3zitl67efQxqe33byo/Zz7E+hHG4X87HWm3fllQeUzUG3Pv9vP4/8Z6eOVH4+0V0OWvyU41E4ys7Q2Ysxc2aflM4mev7dHQB1WlbVD5Lr9kXdfWnbNWDGft9fvpz9ur3Gevv0SqsZb7DydfTygLqbe52stn6cpyy8nv06NjmpTytFv5YZz9Egj5rXF9dV6cVEcDJOZL4uH9gQAGRzMwyuSSsrSEUB1jMICmCLU9TIIu9JjBL59raLoMuJ8jDFApc7YcL0OnIKdZ3rmdexidQWRvCJQ/tY6qItpPvnomhTHrmhZkpPyUL5U0SWpxYZn3hvikCnJiaCZhnfXUCfRAvQTAjWIV8Yp8FsvXm/8sbpWxTaDXvfNkrj83u/QuwjWNP25zEhBIgl15FVDBpQwFz7qtBCKSUUyVfMR5z6OCS60K93NrmWQSVei15h8aXfheB4DrYMmCIeFN3MgOrdZTk2CcmSbBIHMoK+/Mqw1Jr2wMT0t1t/kL4+QyehPcwXn9uBDvVRsotTtuLtS/12cbpg8tT52RHZFDWGlTkOsH7ycbEd0QT3AvikGZE87cA8LCbn3DZDJ74swd4TEDdbe6NtNXQehZXrfI+AdPZzQmG23W0HRZEDNIffS9NGULm2GamBx3Q/rb24ko2RWyJ+24kySR6eaWEdGSWXen7yRKabzqRjMf5fuVTfZCyKXPf0dP4TpLWOljtqRS6ET/ZkTGfbKs/c++/dFd6q7lPS09SBs0meX4i7PZEtMFA6PxN/T7N0WfXf7ynZ0KQGF7WI+v3ywbsa1aI1wdIkNFJxYm282hpBwWsb3+98M/qkAszU0cVIb0p0Nf3/5kSKKgFHmOqZZt7nPQwomHHWa/j0fnGYBTcZgQs9JNG+FWaPNZDK1/L1cq15s/zhd2/9ybmugyDeTLuXIO30rjp/XCkdZ/18/rP3tAJ4dABPlbQPIeQGY0aE48WToMtdJgAksSiR85rD0/1vvqI5yDC3+/pNklbiSn2BH44Ot+JTo90K33s1+8b0+WcRBRZbma/Ncdef3j0f2bHa3N6yi0aMdozjmVugDqT3TH2E2CHMrDMsfoOeY8DEzR9mltF8yRHlliVTCIYFTetMaeMkS25ZWm4SjLLkPeBJCYBEuzwWTsdWe1i5yzV0b4aPcmToExl3vwbjreVkh2yb11H/P7Vv0lEU8YaRiMcID4gYU6c2ObU/fhtHFuuwGX2RcrKbBWJVdgYErXsv4iNiNUoVc2xBqnUOpylf5Jxt44F1vIT1lS3b+OveL2F4YVDyQ+vpgktLeJ6uJ/mRh/VMU0R9NMLIofH2IaSoBTjZflQPjeBZTwvfqiT7KrpCp74zFOUXazIlg3wKp05AVfN7S0rtX63eCTIv8u7Mq6Ir2R5okZwJnSQ80Uh2tis0mgvAdPcdf2DDoyM0G3e2xgPJXBX2aD9HOdF19fXKxtntgiZS850+S4cpvFy7dR6e7r0NF9HYi6ypAIRCdnWZveWkvtpDcEkXiu6KtPDF3XNl3ZZS/vxvrcNRfqev8tQr1IifU7Et75ZUfiP9VqVsUfn0VqVEW+RjVMakisw3clvpVOf6NT1kpfa9K80/ve+N0RbereAyjcrdfSMoS83+2+8xdz97+fnZIsmXQn/vinvtl5s0i6kZSNOByJ0bXe0uZGr2++fXP8EUUTt9n2SNmZ8pJ5jTwyWYWaPeH/LII2SobCg7MsWqxrAXr/zWY7lFlmJX9PjXEqiuBdh9RUlDrOUiGVxuS+FRCe8N8u5RM4EN1avtj0qBLWbDEdGDVFXAmnus3IxFTCQmdRhA5m1yk2oPI77cEQ4Ts5rAKhlR7zjOX/waBOo+xxVKHHPemnMlfGqzEc8I+TUdBewzmXjv7qfKmjDewhkdInmp8B6+Ce2FwGT2fe5lgT2KBt1p90bS2p7JvqfZD7HFkdzY65d73sGtoiLri9/W8GDCC0ejuJuhI9yTFLuUjxludQX9eE4viRYtS9sW/uSzw0THDzmBMzMzJ4Qw7yecpzXJ+Ab5ZF1uM7ujV56pxcX2M9xDfu6ZW/y/WyY5wlCQ16UM9vcZHHY1zOvz8DfhSzoJawiQdOWJN4cc3kWC8D6KBtzN3zZs+NO2v0aycPR/08tvJCtIFWyUa4rnUSShMs6c/wH9U00c8xgstB19L0sXZVXkuQ/oeXTGrj1TeNYz9te6JqT1v3Q47Ohk59Z/9fyNo8gzl3FhyYluAfwVw5Eq0/W1d8uaidtrYvWkcNR2MwJOAnvx76ji5r0iKr0Y61+Xf3nlYCro4e1Fx5dJ6L4C2ujRMdUYv1d74Vqo3mhz6M64TcK4rZEH5r7rJF1hnNpSGvgvso+u/4nWM2EGMCGEG1GbJlhEgDCNHgJomr8WbLFmGO5tC2rlBHyDoZURkWhpIiCZBU1LsdfBr02xCsLoMyC2ThHyRmd99rluVfhUAKYjlYbzwt0q029sc3BMMY+XuzaS9DmyXk4UU3z01QAIDT0jOBbciMZ0Yjzu0FCXS4xQ2hzfd9K3NqchewHEG+kgxOnZ4xTv+p6z7w5cez/y/GtuFvu7cMeFRKWRoSi1HC4lgbSDzK056HvmmnJr5Efle+F/Pgolx8PgdgPWaCebKe2WR/itDiR2LCNy24y0ybdXndoXxP1orRVBxOo1MwPkMns2yixLNsR44CubL/FF7WvyslWBjBw7wb2arnk5E9EkJfEhxti6CX7aq7b8QSKM3p/IV69ewOUv4Qnfj3lMBHO+nsGxqZI+Pe1TOggx1PA7XQql27drkebd5nLHuxZtN3BnefCrhv9n973MLwWjNx/7wSJv5/akg0yXsMysPAdt8wO7W7610PZHdsvuQx53Igl+jNl2sbCMoITE7ngpkmKVptGeOQEUPB+kx0zf+vyLI3Umg2ZvFr/LdNzB0kH7ETp28/PAE5GLGNJ9oXR5rf3j4+3b/bvIkG9vu83osmH246q/fMf89Xiv1cCKuOJyMzHL29v33+1wm87uMC0IMvEKrJx7fQrEmTq/PlsaL52+EJOztmbscba8/jbfS1+Kiv0mZkb699X1f30tgJOZQkyyitywJ8QF7GvA6TJYRTp8M5GAB0npjFq7b3rBMUf1hzVcMCZCcJVc69BOaS4WaJHiZk/t4RaUWsN2H/UX+tfljZSLBID+WxGCufGdq4BtKnTqCymFeWVGlftKID8Di9j5Xxy7hshGsshxzeyApKzSEkBOM3byEBAP103nAhW+xUwzfmJDyqbbvJDBqTNg7KNh/dR+fhLYTTHNXc6Kl2nje17ilqWDaW3pR/e6dKL0Fk7zCB0F0GDyjaOQxbU1ySFnbCRlJgkcSiJHz0e/UgCOO8PeScdP2dHtY6TeMZzRBQZYzU55nNrPhk4UVKW8tHLiSe5lb7KTmyhz5jKjDzHQQdbGapA4yvwd7GhT+hDujzB7wSMJxue7Y4HpDl6cQz4sV/tYIJLPYSIveQ1s0UxORYAinWvOZBZzRDcbW+I9ACLmTJKF3oC4bonAp46hKRbFNmQQ8GF/NoY12dc5OZgOI6Hjf7TZml8qbuH7NlTf6aPo+zoW+ZzzK+ONThF3Nrm9XFf3h94KznGASudiFFbp5JZkCWRHvmfbU6HUNT+lun5FNhiYxv5OZ+1rQXtedYQ5veoz7STUor0tDdSfVq7PnPhk8n5zP1f1/xnSMBXh6+rZSF+8Riv9qh9e3/7WNnFOKFnG5MW5EG3/doDqbiRoitZqv1u97K3GgNLRt2BS5dptjASjoEI7xD168v5dJTqQ0DjIKPZs+whlt8G7BCmYoaZo8vINfY7lcEL4BvPQDB9B6c2hUE0AFyVDihbhf0U6Mgeqe+RuwYS08MHcIxN8h5JF3gtwcwsjqsb9w9FO+3Qhz0DZToyVNWBpD8rs2Ohz+wP9z3V3CGrAADTCEcDVs9lS6nD7Sh19S/K1GKMOlTe+18JzDZP6Zx7QMPIUAxiytYkMcqMVjMbKLe2+z4tb7KUrZeQnUnXJKdqoenL6fkkV0lghhw22WiepS8khfgtM5zzupk5GsR8EOG+TklYSlIGenPd9bl2fYj5DbqzzXVmcwoKce9vHfEd40OG0fRb2cU4vIZogKVeGos9ZQB/2crM5gxvkJbycsrWvJ+2l3bzit8RqKh77yVZBJL+ue+RRIzGNZpCCcB/ApNeCYDFmH5R+/F2Pzmazu5/htSl3EaGRF1IN/TQ53pgrc8kAIfO8atr3wcxWO21bR2XvTqySSTh+zP6oRZGyGMNkKBQD3odwBmnaC3AgnXRzI7cBk/lnZ+Be2Rnj0kV/1EW1lSNkGIGKZpsYwDHQAb6rLWU6/8f//wf2qs9Wn0AbWeFj1GP6xVNTyCjEgKbPT+iOt6c4+lq6+xvkfLTDHz99neRQGlOlEXF3h0792c5u1/9bAM7wCCIkWQzyUf7+0pedsneScy/MAuPz7+QkrAebiD7Mr/2ZD7nX1z/p6d2wiMA1l1+lkQN3xlmIxCaZyrKmPZMUwP3aL4D/4pAeqNljcIS62sv7UnCAEOdG7PLBrZMDU7SEklolcMi48E25Ni8O+MUvHGSWc9YMC6NEqVjeZTbXgcNNQecBX5fh4C4rhWpDEIWzqpv7i9I1SL0ISbpRieR0ScoaGWPNL4C4HvGxj2ht3Io5wtHSTIk5z/LH30eqiPUvcwsZQW4ZFg3Zb/n/rTI6ggMcL73gx5GBkvkRifM+QQWGUSZpiQxgayTg+pvZXriWaEELauZBGKVuHVIbv1PQtIty+M+tEFK1KvUizFX/L7sA3Uh5EDdaSWCKNmPIWgOOiDrWQmOaMlyvWPH5ukA0G6+RN8XwD/b7NWf+Q6Wee8kXDWbPfhyjcpffEmu/RB0JxK3tmscnbzQPo5KigHvRMYmuZpkaAYbu8co2+vgtk799UJI/9/xnvFbyntmOgqfb1UNWVIFZTkR10ZC0X5fNbe/+n4f9lODSxJXiuilqriYLy499+fppe0hw7FHlPJ1y+v/S65Av6V144uPlmwLKMt21dsUawZVhmnV8GvgeAZlzzVlfdpIz0vpY7Hm4jl9V0qYBdWRlo+S/Tj+9Lu/OqdpY9XrvuzO1wVfEniQgKuo6yfdzEeUVC4lfHpRXDadEXugoPoRxRiHzjze+yPTd3KUF8Izm50ZmR8gcb2p37b+KbV02rHmWSbkjqmudrsQDpQINE5ZYiIFdwHw4sjqGZhsmY8a5QbCD046+5m/VeTTN/7XfoNGcDA+ZgfcUejUyknExuZ6ECEC2injkiUOh1GkUJHadPgBgoNEntpal/KoZgHFBlKTAFZJX4H6nj3w/tX/8iWs26b4vWzSe8sT1bQfap9Hf0YnqSTVE0zLUGzfg/Aa6YkTIdt+mchm+N7Bqu/zMrF+Ct+JCEom9uxRticSpzln8GCWbmrMmdEzHxt/BSI4lWrytL32HJQqai+Rk98qfzP9UAnT0mVmbGwOcsGjZA9ZoQNx0vkguX9J+no40cl5SCe8NBnWH5HUp+ONkQ2XDpjIYAf0MT2LGTXvXP3mMkhAOMq3SiuffcCr/Ti6e5IOZW/ZOq/h3geV9W2vQMQaDdPgq+mE6E/DELC9lIdpHSuQMttlBi6bH/a4jW/M05Rxy+g9zEd7BOed+0aQdbkNfT7/1nXuo1Jb6WlH1mTtT272+YE82fPw0OMeLJ1YqHmN5xXhiC0nLUPacccc1zHwy3HEAIysKESligb9Nt6VNWWcSy5sk+O48irZp7lu//Hf/4uwnaaIIuUUeIMt07OBp5swok1Lz4qxeifdbMZ9MUu1GJ4Nwm/99WHd/NYmv+7700hA+rvMQ2ym/bbq2TzNs/RtRSt/WWqoDfSr3o0eUnqe34V+Ut9vWRHo957S5fo4tEmP0jb815ppG1di3eSatCXK9duPmU9jdO3jjURVLOdH1n9TCYB1PqXAFezAKJUqU4as1WXjOk1JK+OiA4bjwFMLGMIUyhYpYtgcjswaLHDu4cksQAeGbpcFQD3NozDP+k2ka+5ZKHCD6H8X8AZsBfJY2uWPJ7Uk8PQBWT+ybZaqxbG+ckzK+oCY+k8+wwXMUR4H4JkUF2fmZOaBcor2DKgLXIbiKGvjBHhkxhRQiy6VfLHnJmVeSI77XASk168iZtveIOzhOuoq5DEBi/7mHJ0PHkh4MMoTNZ89OFDtVhmlKi9qjv0qZn/y2dsLWP0uP2iEpZnRLxCglDOBjDKacWhEZg9Xq1anXwcfsO9JWO09PaUAk7hSrnpfVCN90ulWZtfXgZQ/TIVr8sXOGGALBbflP975dsIYtDVlUX2h5XvTN0IxbHJ2jiPuWYzVXp2MFWsxlk4bz6Fc7rlS4eIfTgMbeGBdwuOxGfxS8GoOfc/29Ebdlu4kjG57tZkZegT/rMvNnvR29vkrm9bIWVx4yppUG2e5tXtelETKtW/EcBzjPTN0oV1p0dUn9eizlSlzLnL+Ykra3GFtnEbOezd1lzyz7iIqLBBYSCcVNsXbGNzjdtjDLdPzWUGUX4RFG8pOB2ibyFfR9C9xklagznWK1vraWSy3IX42pLA99OuLv7EE3C94uaQirOuPdXy1fxkAQavn4+PtYwDBo/geMyS+6Dx6IX8YK2+GTU6NPxEqLOiNuNzm+UbKPqEXv8f6Pz3W7VtFROXECKr6OyMEk/esAds3cRuPLcvHjd5JLDZT2SPPmkgBPZ/XAhvTabUsVTrb0kEeZ622WHyRYE/AJgx9kYcaZV0rIlLIo4ikX8+sgjYT1XuS3EIT7K57PEqNjeyK3OvBhz1JGtOp3INjmHIiTyeUS/BsPm1E8kiUQiyUSeChPOVCv7W9H3CwWrOn58/vNAtZAqhXMYTzFRFTH1hCNtcWT7FrJB1lcGlPBtmoAzrOe2KQXJGli70zPqLMNsYA9T5nkRuuk5ZpSnkjs5K6ir0H2IfA+21eVV4ZMstAAPVV621EgXOemdk5nBK4ulTZnyDzGqvGEIuF+4Nqfl9nNW5holsbmW3AgRHBI7fyq1eg/7S/9CXQDvLBLNTdNovQuqH8FBproLUlyOIxnYRpjbDtBooP72qhbGe/bH194ymt6nfNVFrKw+EJWmuZhOhofuRZTpLrWcHpl1a7PEwm576B9E8C+MuEnI5ZL126ayyzf5rspzkvW/a5fWVuxhFEA7N81q0qudvJaPeJRnLlK0iU4sZe3nZ6P4fU9OH0jytAMtlWF83J2pvn3OC5Uik31b3W08Auavb19ZcEIIGlX+tUwLUAvr1H+V8HAAAgAElEQVR9//5hhDprfY1d+zVrca8g4wrU+f/O2veKCPB3GoOejP5tk3QlOp8pU6ONkyUnilOXfpf1j/He+irbArE7CPYvDCAp7xCbcXV0bYG+emP4DaB6i2qzkHuSGJTwlCbE9Yf3kCQhEGIf9yt7Q/JQz1IJGwhy6F8BzQIHrbRGoILgLTwawbXGsINqKToyRgC9EHWBsIwT6DSyWCklxiBNNR6bucNBAZ3AIQPE8rFj+RLmDHmoxxP14LfWG7s6ACldkLrJHS1VbdkCu5QHRujI6JJDZmrG4QBF4HuZojIFBeCrd9ozYr8RJMBCtbkOoKdVM9tU9tFIQJ6ipQMyGADqWcBTO7YeoXvMUCXZa+VwCSJy/1iWMuZpcaXUynCuuxT+3PYdIaPXSi7DamQ5nOr8t/5yfofcD7B2jdf28uAdLfQS02P4cjyDy92PPNGm2puX1gvlR4zqa7HewOO5j2ePp2vTlsb4bR4+Ac6eydozTQRkrNLAtnDPRPQT3cpWTr7aAz+1rb2yT/3wh5ZZevC/qQMIXM245hhWs1BTSpyTAa3rviEE+kLTi+NBGKUDCQUGTLFmtUQVKHzQ8ROKYtzXmg9h8AWm9dhdR3JoLzDPTQ/STfdMz3jQq/KdUiH/lDOhAcXQ20yH4Y9DDD5U7mbmzZfryvp4tueZ+415+frzSwLNgnsZ1toz5vX1ZYi1LPz4ard0vzgRN9RDUl4Ou1QcpqStkb5+FG3wS2h+GO2Ie7aFfN60eDoWmwbEbYkiKTfnctuo+Pus/zMZpLL25zoADcoQCeTa91NAlQayRaSjuS7xUA14Y9b/+yNrh+Up89AJC4jYPKpbFr/tTtKGTZEC3wNQ4HGQnwT93M8y9ubkm0oUwQ4CEfLLk6eCeEjK9cxyowSxUsUkAACBLtMq9PO/azNqlp8dsjB6s/YRkEcb6Zhi7a62Weol0e7kTscoC7Uo1KC+jn1ROMJc2QCNA+KLsq/SNAfh/SS3nO744ESll3/ZTyyBI0mN5lX27frvbZg8sRenE8aKKeoeyrbWRMEAb06zGH2Em+37k2LNpP2qDE6Nue9V4rppbcU+N8pKfREYyn8VmGBjkAXrQHK8oW9bRhMZzBClDwp2hW1Qv6ae0mJtAA4/6r4MLRwi+Lf7aQlJHCob7kpkJ482n3K39U9Bt0lONmCNTPiGxtKNBimggFhq9AS0ow223fvwTJDaI8cfV+IlNhNr+dQG+6Aywa3M8VSOVsh8o84lrpi7WpbVk61NH7/Wd3Y9lGyWumkso+mjmJ6ukeyky7nnC6jcSKL+vjR2/prHAalUNmzSQR8e5zjI2LqNL6+dlQbSovWvZ3pmhFcM7JbduZChUwTDJ2kBy4hYGQitGK6VtLXKOL1colzrF/l5mvav324ScLgTVcSps9xB9r10L1av6XCk0zMSMfeXkNxvDy8jXWVuMh1x8Snck1ZtDzM6eaNXhcWjZacnzXBZ7MEbpMsdf+0T8iH1du8Z3JHSuNqLIFeGJOhG8GyOC/3xW2rU+UknoSX0HqTA5OGo5nh/thskw8J2gUgVoU+0EnZq7NdwYFTmvAgZiVCMObwS94i0qQzZFMCSk+sQo4AhN2NX5E7Dpp6szyzvk0zbNfFH5Xyw14VBXZUXRZupk1UpCoonAjLIPvTYpxbzC5Ln8ulkpY8rfoPua6w+K/M9NeFQXekHiejyTpmNQwtYJuZdD4ldsjAJPlReu9pbJXAp79ICeybWCHtLQlA68kC6WKY4yLj27bCkzdqc5SYSEwnSqZRwEPPsX465SqOq7+u70m0RTv2eWTthAukBiJokl9kc9iMWAvUh1CkyxqFdobelh64z2/oZJPFneduyHiHsthayIvNTYd8sZSq+n5TjOWzs6/S0z6b5BwQ12G9a9P6cfiKXZHomgTUfBeAJUd2GTHDPNum2vLURRJtEogtf6pFV73x6Xpr7EAuNTjrmfxdJ8fEq8MoYPhzMQaFOL3eVeVhymOVhR+ITHZeZJSG463D5TLVJYqO2Ts+TKW5EqLvA7bHKdDO9Madmn6oxt9Fq15huW6UR+nYrb+ubpNXioe7jB1d/dt5Oiljl9oqzqrvrio/YWN5LKJ4X7g925Ovyv5cEQr1sUeZ2Hr2hfZkXf2Fp7vsJJ/vx/vH23TafyZTpxIMfXxNPZXGPJXMv0rhPxsvgxu3+V9/fMrw/Q3NkMU8k7LH9nuWgbxNI9H+DzAwCR9Clx8iIy0c6AOyERn+1yFeUCJl8U786YJLzzT0a8TCVxK1beTzxlqlK0lYkfStTQ9hPY9lAWyIT74n/KaJ4lqkB0SSPlY0yx2sy0pu0C7D4/gwQUDrduK9I6Pm9QkYkkiT609q8oU2qjwDujO6RZ1tLuSfIW6Ys1F69vLAomZeDFZgRAEl5HICxZO3Xluv255KoMysEeRIE60HIuKyvag/KIRs2iUfOQT2P/ddnlb5Zthsyox5Tx1r5WSwsJzJ6Ti9fVKbM2y8CpDnM/WbjZa1GGBHV3fQ8BlClpPGSVfUJOqg9NTXmwsnSixZkAImWjTmtdsaf0sYcSoA6kNtBnMBy5SoLaJ+eIVtToeQ6qZTY6Xbvvc00GBnikewI2z+Dz/wZMpaVabNvzDdfMCZTGxDcU5+tjyhDbG4FWSfpU9rwsWcpTWcoyi2r4iYxgkUg55xHznn7zD1F8cNJnkTJp9+7TpUdeerzEzk84RKuOfmQJ7KR/URShfPWyJuWIIh7ZpdGySHnU35Pc3gix1NeID2Tt/4MhDO7F4/PR61TtJbW+ylb76vW7VvXvIr3fWZp/ew+f7X3nyWBZqbysIJFduzY6nwvSZyY8atnQdwWr0hs7CAxhxylbzrwAKpLY/FIWk6W6CrQ2/rD948k6F9dv//q/T+oKSmbSxnfpTka3gQWuHaC4QIChRxJMGz+su4fhx40a6qjmuNBLfMn+FP2Se0XZSbRQEnSeO/PLIFsgB+AssBqB+IiHQIYReQ7gHeSHxHUsY/GEyFldQu8KbNR2SXh8rm3xPoX3q2Vpg1cI4Cq/R0qaTS4PVBCIxHx4Mmb66WiKmd1CQ6rEBv5BQwKlDcgHag9wW0QJrao3+b7bCrTWATawPQAbasHth8FS49zngQEcXvNTGVtNENFYKl3Ps8xVsgthpeHGvDAillO2IlNBBVijmc7mWEdGTD2oel1yyAVGa6xQ3djMvVurjoFdr13rUj9fJlvkjAeepDrCfvOcOgB11ARIBE1CXXsN/ykCUx9HIBXpEBgfCthg0xlbWbU32J4APbMGuzPDfsR/eC1DCDwHSlHl2Zfdjv+aYKlbPIDvCPgrxPp+rrebo/+nPvR7eZnkOUkUqvdtcfL1jDeg3MjNCgMeK0lw04kkH/Yc/uy/SsWIW0p67bkbON6iWHKeJ2wULUe7ckHVLww5dF6IiMxiSjXDJNnWysw/EPiRXpOEdgXEd9WdsPjde0hrhDeBN1JoUffFOfGyjM/ApvdO76U+2s1+rribyoBAwnvH/nSW8eysbzSQayszv97+1j7erQpQQQoyiz8eFVFpByumI7/1szFicCwvItr5ukZjIQp4uSD3Gcc7VfcIa77o9Z/Gu69j91ono1pu4YnOsFiTuKzfjrtC0lAPyQlwGVzjpP+XHXCMSSQ7IDSnU8vR7ySGRh2IWLOWkWTz6V60uUOzN1aKlPT2lOfZatBTkhcXF499KZsRc9IRanYoQw6nxv74/QiaqqmfQ530ECxveOoHHCCTsyTu44q3eRJdFvpGcJ/NrcxNvWxZTdSj5ANU/RWcxtIKtsSgN7+jbkwxUGGB0Bfq0AZBvrukol/O3W4SC6J957hyJV2IKJbqdvIIGX/TG5lK8xX50LTgQiVm+DR4SlnWKW2xvR9YAHrU37W+tr/Zbt5dDTmwMlO6dHcs6VcXzuG/EDYqctJrFXy2mRaRqhyg6W0J3DNrFMgJuyY02m2NfbPAPQbXvocjtqDUFqzz1mDyNjg3VXt+gO5KX9aa5LAd3NgEaBiqVaaqfR983zMIke9vfIvUGvXlplZaA+JVsLm5Xy8qso4CD+DIPHbPrfdB2b26sWzVnM/8i6fVk53KOML88Vq+OJYY1w3susgf+D6UY4pW2NyCFvbvOtpDxV9gqZmU5z6wvrnBxmUcNv1BxA203i6PsEIQVhovbWOiZUx+kCN87dv7yYTpqdXrelCqwViPrdsH8b89dPfTgKuM9LPXOBxWMb7ej+P1VpE/ZsKWbD4V9RDv6+SNzv22kpxIjMk5JZHZCfMMGnns3n6kyyJX5DhWK6vYxZprMm+7ghxx5qmoYzP1f4fv/4tvHHbMxgykRNwR+QWz22JxllXcORHYC2wK4Blt7KGeZz+lsC03kvmjw0gG4deqOxGXoDP5ksc2x6jkfEguJ0j8nFhHwpAXfxi75ya5GH9luVPciDjBDpmglT6Nct2GmBNL00nomCVBxc6GEZpV5QN5niwOVfgM/8VyTjYKs4zywN1qTIq2W94Ye5vShKn08OqwqkD5dSDOlxHLzsOyxIHFAwyFR0weeDo700+odo1LpCjEbpIYoo1QHks4bf32EB+JExVJlmz7f1yU5QnxxWiyxDK6kPbdxMKfyJPGnsGAg6HczSyS1mESfJSwIA+oX+tBC0DEXt5kcxGEh/sP1sLRjKpLJw/lJm7usaFweBArY14QpbsDcV9gC386Qnd7Ps8zjabpavHwBe6JlvllQ49bO5l3+tUU99Ps/VtmGDtz6A+nvamqJ3N9h8G72arbJ9IofvrQ5j/KsD9BDyRAuLSIzRKv+lCyvEJgK8v4lAJ+quJeW+ZGK252fX5N4+gFilLMTBwpIKWAzvubdZ45vRTs3rGDxKSXG4yf0UCc037h+xDbTN+gVT7vrHTxdsKkdr8tPf0oOMveuspdVOataD8rc4eVf1mUXh9XuDSHbPcxquWv37/ksDZ4SwNsg2btkhrpboRc00zp6YlmCtmvIwXv8uuPIL3w4RcKIa3/EQETm1drr+1c9skOZv+0X7QcP0WHTzJpMf7P99qgSTNKE+ICfCS7qui1Kc+ZBZgeP56p00h0wJBPYwkMC+iTBDoQCpekgv9c3JNR1uArCXlWhlRIkUfXVwoskauKMAjCWXWJICdAw4Fq/heBa0dZhc6GOTG+Bxfeuoird7HekZmaAJe+FrtxJRZsz7Prh8OiPthBQ4OvN8qRakXbNa6r+CayphoJ9xjco5rbKWb63eSWP1i3zewViWG5JKaF79eI+rwtZ7R98vUHaFTIugYWLUrecVdKu2KIbMfPs7aE0VibJkPkJHMPoVilbyU+/DnHg8NGGSXcryRn9RffTASVLLmeCsz2MtVPQgb8ohgVuoKDvBQv/GoXiqJHE32/QQKsd6pHwL1XVJnu7fa9zfQq+e+mPLe+Vz9fQhkIy5ww+atE6chpRqsXqx+aU8NLvbnHDJJcc0Bp1t/stQq56gfK61Oi2jIdtVgbgC5LH4bEzOx0anZN8rsPEPjW3Rqk7cP0tLruowkh6REMsxM4iX7Yfd8gng8P+dMdhFjs0GuZ+Hs5XbwxEX9FaO7soZGvOYeSIk2Mn/aD+bPKh+Ih7TJyPK2YlrFirWgfgsA8qd0t63v3Gh/w6khsa/i27e3b5G7tNpNN49Ogg5Rxk8p20++6Lbgf/Jjvpr7HSRQdsdBsBtJh0nrLT7++yLeYTCW5n3/9rY0VeWXWWe9yHmk8lVK9K90WXXTM9K+t1nmoLJXZZxqC+uP9eaPXP9ui3tPc1Q6OW9EuV+NpjmGeW8AupalxqZ2BzFc2QM4R3sETQnWG1kO4CQPiWyhgLeInAAwIUsrvwlkVb8PcGPeL4DOALUCZUVyqpCaUXKSJ4JTpY2KrLUYp42YEXrE6SoSOqPiuiE3/IqgIGJr2deMy7dQrj+zZmke78z+s2RIYvLytyC4CeCrjoUHFZAckjCpD0Umo+SLujRKLk9EU0CcpYLSXxvHiVyZavn4qQmUi7vcCYRFgEI30Xi9iDRIHshLuVsedhHPD/DajqSOBaq+tXccIaNa8xT9Obz/qkiIr8PMxNjYq6RyljGqz3Z/rEFpzCTLe8amgh8maekI9E42KOegZXYkG7cjOU72pREUjd/VvIPqCHaESTqRAa3x9pseerjhBNLzu5FNIbD2VSPd2cumpHMox6k70kZ1623DCh2iv5tkII+KPhyQwrm4kSX2nT2gTbB7UXonL6B/Cb6PPugCCB1L3LdpnG7LZ5PIsHIqOkD7JkVjtu6zhGw9j3ukmozGvItQHrN+T84ZZYCSiQhx62cKpOwN/ZM+61HbXraM8XUk0L2m391Pb9NEScmjZlpGgA86EyF/4FbOA6FI0N6mznIJovX9/e3byv5EWdtHOMHMrzZz/yTpr9++JHCSgHRzxDcsbBZZoNgDZLHARWzsjI0gSd88ouG83MvhbInayrKiNzuTQ+8jOfUgIxEZhe9lXq/KR7mkj1Fwla4FODLDKycuo6n1fYgE6Xqu+Z+5/k1SY+zsn8oqWgTzqRwvnUJ4L2bLMD6CkGC7FRkFaM2XJsoiH46xVR1QL5eaLiMAmiLPIHmdYDHzEWCH9pJWuwFeB4TplBOk9YxESYVZG0XnonH9k0GyAm4ZGOCcac+Pq73rV/xnglrNrQNF7/EJtLdjkylK7Z2QzgZCmW2oKzXPBacagAfw5IZ/VROsXvI9MN5/EI3Vn4jIUrabnFUeJCIRz+39djnf9puZzDhFeLbNS5QdESw5kQE5FDtP/CW5hF3IufMH+TwV6F9/eMlczPPIiuS84V1Q7Whs7OnleOpZoWsH8rOuqfIyn+EkMWZ3d10SSdU4PIAlfY4xYhL0USQtn5n9rozcSUd7/+JlqtQXyNOfVSTOltQlGm84aV6LOWgJmxNARQx9koLUlxZWqEVHfSIh8z71bGe1XfRA93uL8D8Pm9K1ztw3dKMn21LAt19zBvn1QlHpWva1d5DWJgKe1fOyHQqSxm/RmJBE+uVj5cVL6tT78DJDs2fLegPnipGUk2wK5uNGlD6T8EhXedBD9Uu4JglPTArJzK43sDvRkILDCvNawDpO6zNtawBpm1pbU+//57//13UqrOsEPbyt6it3ctOAgDoolkdFiJGtFrzmPE5ssx5r2Par1Yh/Wxkhiw7Kaj9p7Jz6n//3fPq/tzc/f3x/3Ra5gNxilcGWi6mN6hn4N7C68jwr4/P29svbL3HmUrQRG9vXYvTN305ibvx8BgR0LSNeWYIWWY/s+TCGaXDbqT7c39Gdh/WYazH2yPyh63/YhtS3kJmyQN7VQ9ZZZG7+C+vZSz7UjltZSYSGWnNdkXdmqMtp2/QOp1bgSnCstuCn9MNI+PkZndj0Gdqj+LmhWxeKZNAbI1TZSFsIV310m1tO2/em9CxXj6qXNWib2OEhjRDEezTsOXKo7bSyGLML0EH5aEMj99tLRjlfwzCl3MN3sfZcl/ZMip/OaN9lBu5wQMT2/p0OtJ0EFjkQlZv+Lw+CGEePy1n7PGVl2C43EJD+TJcI9VjlJlNW7Uh03SFdRMmfxkB56Rnybwnudb8uiH0f9ucIsLCP5r5jrlx1qwTR57I8qa7LPqCvW4YUwfRVE1LZ2MgS4cWuSYgQQs5nYb9llrQiUys9FwyhrNs4Yyxanvx3ZpdyvTyRAdj8Ft2WHT047JJkyDlOp3wCsFqO9D09eyPNpU2oc3wwfWcI0Tq1XyLbWn5TAZJ4vQSY26mp+d2U1QkL27zFjSSGbKt9brbTxz6DlHV9kS59N9u1uGmSp26bHQHPDGAFl56yIKcJsPltoKeX/D1Nz0bZ2iEOz4TupHObDx1zoEyW5sSvT6t+UZ76+qWe3/b0lOvp9elquphteVb1S53NflZMNS+2cZpB0m6KKGOzl5d+eDj97dfaNL7NyotVdF56v+u3f74e/a7D/Y9vvMNUxrCW7v3y9vb9V39RqQ43+Fi66RuaI6/jMlgTH3XM6/0+y1ytzeVrv9q5cAELdItr7b+1DOu4nsac66+tPQCMH5m0U9u/ff3vPHC2fzKf/G4jbQ+D0bU2PYjun4CDfhZXret3UpDtHWRa4Iea5d/6f2s0BngSkyjbF+qE/lYZ3X4iW14WzZZfkIfzh+hZU1wO7rTfqX5tfAogv8aBfp6AbpT0sCS5SkD3U9HozujbmGFw8WFPDiLx7X7KTqVFsQwFcpgt8PmsZZwkBnpTc+e/khy7TCq032SNeRFRSnsQBwZIIySrOshH2RbXmyMhaQQO48DG89p71o+DztmGfrQ5CqHUYRx9LxZ1pMZW1FXExuSBOWFGxWXFg4oiexUnLST50PqZ5Adn/PU+IDuD0qhanzFn2K+T8xCC4d4vyaURGFx3ygBJ16TD7J/WUelhl615kE+cVOXWaUdbCbJ1FPVn9n3mPqOyUR08wnZpb87MyISAGb0/mej0U21vU9ja0JUiAxGs4Ht3BJLbfsadIKS9wvWTyLF/N7Cc8ZkRdKR3nz6EByXcCMos0RKJur1jiPtxETPKIXjgtYIp53kosjP15BU2yPFegC7xR3WqohIn/65xHJuE3SYp3Qj2vHnO01hLVt52KoE5CeCJQT3zvR4loLO2yIzOILTtFL/6KU12zLCZxMj47EcRvpqkr9+/JEAJNCMVRLz2L1RqVOWVXhrhV/zqFXCJjrRH4G1tODdiBAgfJRUq5XEX3JZtRF3KICTYQ4fnepvubabWdespQ/KkCX/E+n+liT96aEEjQ6Nu2PFx3wDfQV5EEefeolnGlMhOJ3P1OWxR5xMJghcQqK0SL4C9Q2IwAZpUjn2BMOk0COBrv0MRH902dY37Fwq/k/AVoE4gUUvBfxwbvrMvIbJOLEAUME88eSz7mm1Xni6d9SxBQsbLn+8jpSxNb7THa5wMZtf1MvzMTqgsqe3JamBcpqHW+gbaY1ACtHVKGPfNFDcWKThlpkQmFAUtQB/ZTBGulH8QXGy238B6kools9J1ZeZcPgVO9bf/HskbkMg8PVB7qYbe5vh0DzJfTjpA1E7zqdK7kZEpPS/yw3nT/NucwpD48fTMovmg9oxZL0mU7DXeJDg4plwkSPp4Wt/8bnqM1+DwCYH1YAv9iIfohneKVzP0TGlhuBtYPgHxGehJLzmrGF44CLV9JSYhWD/cIf7QRnfZ1wS/h4MNuK/nRDgxIY18PJQRPg2Jsppze5bjvbUfvT5bauQA+oMKkDy9L8bfPSD7tOvfhRvh8TwgxwV5b7+P/0ag9sM9mO30u5L0uJ7wpCploFPFA9b5Bm4tiBO4YIpZdtJfbr+n1GRefeJUp6tqdb1I5ZX4XkGqr9+/JPAsgdQwGYIIt9n35ujdI3s0xUsu1wEH0l8/bt1RkL1k93scjKAY/wDFJCYEaHM92VrSqTxKRbQot7vKRlxgtHSyEq0JI6+69/dd/7NMLQ4uiSk52ZJTrXsvJQgDBhBGh+onpQUVDSfYySDtW9cNkoIC6/oWEUn7SHLQwSsNs0CqP8l/iZmTFOxfRngnMBJg8usKls7DK/xZUlmCR3+U/3bY8I75SJCoduLhDnRl/12+ygb4Z/VSGR2VZAh08/5YPWM/h+myHY1cmah8TshP0mtzBalu2ZFZYr2uFTFIshRZsXJ5Ia8gdQG8VX6huUxyg30zTpJU+sfjkWu627uFQo/mHhHOuchp3wOEjf6TsJlTVaav5K/+VtlIgH2TUUFgyngnra5IraQtQGaNywWZenw4slr6aLJCSWRfdxyjD8jDoa6MBOf+3XxX0dj7Q3JHkmM6IfS7E32p9nWPU/Z/PxSBa5myrILb5U9CXrl2Ofvax1G2Q+vkBHqfgPBEUw2ADwLEpaCAgYDlj4Lt6WPTPsYPJ0JDqwvTEoYM5XUw4XN8ribyBbQYRXDdiqdByGCnmvU2e8kay9NkTw0pNKKMXNyBTK12t3fqzMwEAjdlXUFCMeD0gUMIHLXm0VznQVjbta1GQVYvY10R8Og0hM3aGEdWTtLGtKVvesoUNpsFTNXIEnzWiURtmR5fSHDJbb9AdDWk1tUnQMiYIIgowFtsE1UJxDfbD17e5sOj5rad1B7AOPlRnafcvv7+ksC/IAGRDCxiWPf8GNkcN4UeXbIT3n7x9eNbfQp4qwSOhxw4iGd6Ye82F7kWcF93WmGXvTxhGEiKHKvvmRAFK372+q+MFME+LUN8xh6ezFRcZnIjOHEdx3ZyoozSuRjCkTUiIrtTkhZQkW9M4BKBIu2lmZvSE7ao1Mq+4FHD6b8bgQq8ilOoYoDYcyIvkRvvCZYIwNveKD0/xjiyIeyvALOb+wBkY2OygAh1cn0msd5/w2QJwKLvmpN5X/Wtyy/7EAuE0XvucbJHhFyy7fhAApSRV/128Gkco19WJG/9NU8v2+QKubdyuUZGA+xrDYxSxSafaK+ySJqvsEMAAtkXqVR8YWMKKkH5kIwcS+1gH/u4C1S6TKjrIxMXWZsCjBVMKAKv+zup4XjWtUVKOCfaS9Yzf5QhSwGtjYQb6wjmBVSCvLaSTswRTPlWViiIo7WR86w2Z5llBRO4lsp+RRDuw+8vUNmD0jSfT+hpEhjKJTNuC7yOY7JtrcaeIQJ/PrfD4dIBAtKdQN1BdLbNAV0G1wgdspEnMKwmZHt19HZ/qeZdvhdXJZexZZwziJcbLYdsGqiH78Q4GvE6dMDHdJqB8/7UxBiDeGkNJtEGEWrZwts8zGxiSKWKnTsmkX+dpYFtjjCJhr8OFRNmT6Sf0Yft9LaniZu/eRlQd3C6pjJnJQU5p2xn3fvt29vHxy9uVLJ+aP3w64ED/0jvvq79ksCPSyAXlaITa6GYM/N9PDoxaZW1af+O3xMHHDhy8GP3I4DAyAVtQgHu6qecDUH9zJw+GtdD5uPHpfC5Oz63/l+3dcpurbuYfVcwJiBcmZC2qXKP3FVRT++HjLe+pZOfPShMwtkAACAASURBVO7gsnpQASJ9V/tuyGXXXG4ngtktoQ0JxHsvEkynB0C2JMAT9bVKSZBlQZO5kR8DzHwRWJ1uUfdSRiI+KCfqIBi2HsQunTtLlLAQPPNQjtlv5b6M2OuBjtnHQeb8Z/wXmY8+h8gSJBQB4Ehy0Xf9+ZR5SywJUqarwGnPcvF79ZClb5JvETT3zu6rQw6598UFl/tBIOcaIzIjyqBwzmFkKrtQp0hK77I8LVmgN8J7SuKjLEwnj8UFPl8h41FCxn6vLLr9T/u3pAcBXKiTDmZGViXGmSWbChJk0KGCPY41a0+bme5AvLkcsDa51lIVc3w986T5MXkd5qiInF/pf9/H0p43DdT4m9N18jGbfYuBrZOtck/ICaS2NXvOskgflDlx34W1FStUJG01eXwPj64LUqel7YdakZD2jEfPEgRB8YXbho2h9O9JKOKXU0KE9x8pxYssBAl1Dw0UqZoBOnVU80u/eSKtJvboqI0exIC6fD22eh60AKJzIo1ntQyy1kjULjH5yBvp6m0/hX+PySupj5mgI+lhl5wl7pkfRkgzRTs2ELlJaVzOJZ9vs18fI68Ti95TbvFivpFuf7HW/zQ/3xbUn6aDXx35IQnIMLux9RIQZU7Cbbpj+3h/+1iHcMRbvT8+1qf3t4+P70ulK+aSC6wb4lunGilA4euxLEyGepQv0UwkCB2OQM//vdZ/RW36Md1z3JStG58qL3JDLs+E4pAWfRX878a1jWvKZyv32l3OnC05kgaodSzzdmpUH6VAWfrj6FyNSK4Q+74skgUvptIllSWP47Xl82YWoki0EuwxAlUTS6lH6Y/3tUA3bbtAW542N+omErS145RdJvVbyYggL0kDCO6ZRA7gTdITsrODSBARZEAhDxGYBtxc1sraDhAxsNQc4xwbgXD/fNh/NACmt10d4P4R04p4OMvNCH4dWKuHArf9cIyuk8ymdaCZz4rpyrYB9HZZzHJHBQJCn6LRdh8QrEjiuizXW66ZSbhUJnoif1HqRiLCDNqwAyIniyAVztmzMlrHlRXzzrXju0VAA+tkhk9lgqukM7xEza/WW62VOmThyY31I5tfOTzN6am0SNFywbv1b2ZjX2RPXj339nu405zr2/NO/eby1fzZbMyysUvJFe257hNhfwX2p2/tpqS2d+gZSf5G9qbtb0EjsgD6alYz5KW851DKLLm3/umPYXtOOlGXIFh1zOZghrGPa5pY9mfiECMqyhTH46YussKCc3R6ziXTc+Su+/s1Dk42O7yBilkjy92HXuNmT33/Zqe30fn/1oXzdd+XBH5UAjve0f4yLl4gNdNzL21bpGdFH395e3/79c3/NaCkY9nXnrWwnmZIIlycUGaWnJllDHiEPK99jQADDW0ShgSXvewtgxUPhrDAVZdeK5EDwDnJmBFG9/EuJ7+tIByj9Wn4QGoE9QggffjRnvL7AE3TEdAmOcoe1uUUrIksnTLZmdbXuIeixFS1o3f9Ub6vghmRLC0kaIsx0/J2oBrxohQIskqNOMBl52lnA3CGMAt4B8Bd758pZXTdU/cHOclMQ8GStpld86X7tQej9grtmQXpOoFvHXARxGN4xB1ce6ChZXy47wMekXtBuEHf+z7awHqyeXFFavNKItaJmo9MJLEB9xhPEa7STdcpv9P+i5Io1w2/OWUQbVEmc93M+5gR0Z7b1ib24cgaSddhwjxbEfpuIPlEYtJQ+CTUNToopNYJxzTHmbrVjkV3ebQ5zT4VGisSEs+SzEJRV8HJPE6bQQMdsLDeJ8jUlfqrrI7tScvxSjaaLx5iUhlNtjHLcqQzWN35Piba166Z56g3zWtmTkZfSRI6WfB1oX7sJKBjR96rz01voqEJrGleBXjV1/6b74WqcAAGYlMcdjJuOpGWYcprUcXFHAMJYCxPK/mTPqt9ttnk19Ums2Cj19c+nLBCu/c4GCQf4nfqwMyOfUaH9v7WvJ/8b83PqELQS+LR4JzLI7EFgT0PWUV9ZXf1iPf/88//kQ2z79w/e4gsFTtSzDNFmNfD2FbZSA2ugI72CwWsinecGG4xgPj9bb2QtE59OarC15dfEvi3SCAXV2bXfe+Z6bwz9gRDVt5mWUxkhWIzpSo5BXRyMZJL0WDHBbm2sB5bFqiV+6i35SZ5IIIAFY3077f+tW+mbHmOmSGbkQVIonGxS09KMLNjWXyyyai30mSswwKmPRxAM829SmjgWMwxznIMIpfcn4wToQ5A03tZ+qW/2x6iyGAYx85My77/JcE3cgcElg4y2MkCTxN0OVCuMGpFrz1tpHcBScrtfgOte4m0HJI2xLv4KhRKQjH7mSeikVA2YhZHuoo0WCCiPNlGAiAH/1j9mESFYN9FAtmf9upEU3WKW4BjkSnpTfa11k/KCOQnelelb5yX+Cx4uO53IF12gjrQ5kmZsdRrELOG7tA/jN2eJYwwyZD20sUFIsfquk/NeT+OdGo/WMA75dk5ZEoHcfcSwkQ5IBH1El0SVOlJZnPM5ONkxxFM0cE2bUwxSSWTkOVhv1ZlVf0mr+arPUQ2PsltZC1O4P6V05wEZcnnufSpMheygSVN2atOSPwZRZyqnz3IMPufIPVywlfq69yLcijJYl8JsDPAQ31P35MxDjcB2ts0yqtPB/BQ7p1AhozywQEjHrJnjVAcgnXtWXODCPrKZZu+mPsSkZNg+eEkS2k9htxPbeYzZ+nf6EzTIfst6s0iYLrLsGv26feycqO87cQib1FTt0WdHOV3zWLJkEtp0rpl8Zu98P7b+9v39R4Uu7dHC14t1q/fvyTw75GAVnrb5OBdidLMdfS6b/T0hWsQcZ3cserd7J9V/mab25AFqpN6qiSl1tvp5K3b+JtRfyGk32v9C4z9yBx10uKRd3eEjJmfW5xZprts+ubzWw11GXCU1sSXyjiR2ABH+csS82jcAtiqdbdrCYiF0UQAI6PV907iNDxlY2IPSHN6Cfzd5iYwYBnSiJgJaLkJH1kqgpVx/kaB8HAvyOiJ2Ay30IHwLCsaQL/w/8iEzJKvEEAD7cpAgDQKkEpeBkIjes9sgbKoBJipgznG0EyEJFWOlcHDVn4pTa6YedeZkb3h6Xbw6Do0QGPwzEIASpAIZSSc6JwJSwfYfW9LEhaNV+YJJ9QVqCjCUZmjccJaI/XenyJFPprKJLoitGs0x/HQmi+U683STLThoajSkDxggaQF6ylfjG7jLivk8qwVRwKU9oDZpoA21C/dz0Av26Vc8jPLGZMgV3DFiZ7/fQKAT5gu10Mt/c189jY7Vru3vWO6m53W94WFEcEINvQEfGcfiKlv970C0pPM5LQDAkjfGUi0nqMDh1f3PbpFu5X3j71yYZJdC8AU532y/5PkBVRhiCxwShFTv6YCD6dyt+oALS+H1ueQc0IbLHkdn8FBpFOugKr1EYcWXP2+ZXoO0c8ZQU4neGkpFXj0Np2ohx/DscjKBcyI4x+oLI+a8PXjlwT+rRIIx54kxumMQeiwcAl0o2TIdbufozIjXtNYC1BXZVsZFK43RaBp0HiSjt81nBMCV3/E+nfjPMrKYg5JtqadOcsE0UPYrswIpKQLVO4kChK5RMxapgmlRaf5OKkjI/iqCdf4NCdFLALsjbcSCqhyP0dtCB/yTBAIgqOIOzMl8XmWAtH5MKJXhK68aoEx0VHtR4jjtAdZM6BpNwVplRMdmlnj3YEwa1h6VoKRee9ZrpRG8JR5CVkjA5cRfYGqbKUfqKC22/iT0MT40qPXxnSpWGV/yoPnSWHlFlG6psYQ1U8g3dBQZG4SDbQXFZpcXfm28i0Snuwnjl/WrFVWIfY2Dj1qco+sp3izPz/s5rADmvOyUwXY89kgc3YdMFT1v+yynqZ5pfWTLIqYentFCGveSo9UPofMU4g61DqImfYfsi/d9qlNlgA2fY4/KkvlWWDCyZInM3YeHCKxbXPSjNSZSLS9K7CLrwJC0/51UFulRqX1n3PgLAlNuzuyN1LpGeBLIoPOSIbgBxUoqILF9Jjrg9mtuKH5WCynz41GTndHutKhJDF4Hpdt2mhk/fpYek8m1ezzgn1O8h1H2VZGj8+3ZYihBNeOS/o9T9kXv6FIFsfD/k8dLDPrZGfqh7e676Pa9vT0h+yRzTKph6hnSGFGp2lc7F08uRKjYjuOlbP3m6yMz/thH8Wnterrwi8J/BESoPkID68zExHe8Vr8UPhvH57ksVMNPvy9VHYmnACJJzlVUrBHzaYJC3MRJUZ+ylzV/t+ibopeezfpSjs1Oj+NJmo3sDMLdSItbsS9nwqDqd/WIo5lnn08j6l6Oh1f2isEY/RsZWFIIni9zfAAG5Ld6uM2toP9a0ClizqBlvYMuEb19+fsQGeEl9tRyftmcQdzzI/5CGVjZZvToSDj0jMJO2FQX9lHOSwenVxy6uV5HkGP/oTfS/IBLzmzE3Ku5UdGZiQmsUhdrVX1VUA7tRmlE3JP0iUP7nsbLIcqSo0xcC2PzM8O1LsPFWD1+dEg5tjcZmgNSV9zrhK8g6Dm6XijnyLEAtkptzMJsJ9BVqVHHuep0nWR5KnuKftH0kOZzIMI1P+az40MCLxB59s1WA/cZ0e9WE9JQsIkvsdt2/9cX2M+gta5brkt9b0/cQ1sbZMFoujSNP+3E91e5ucDzdK5GHc7OCHK+4po+1Of7HqG/Mc7aVId19bVcWx1CmQLHvldBWeH8C5//pZszATzalpyztUA/TiB5Am+1e4r8nCTKfs1y4pv0jiOJRlFraplV9MexdciTbFPheGfetxBBvRlN4KScznK724yCsVPwqjr3FwUUWnPw+CVIaPurXE9kbxT301ORoqCAP3jn/8T56ftC+I2kfbQUY7ACUzgoWifojw2oHhOGAvrhqE1lGxc6gM/t2S+rvqSwL9TAlG+ZlsaDpH4WAKrpHORHRkuz/rIqdfCnmtwRi5ua7FngrS2ZzmXoBz238FM/qz1776Q7iePc3h5YMljhmZ4B5ZLnOwTx3Ma2yuSd3KkMuDMMj05M5aPVckazbjAjGBP6MREkIz2b7aY81zAMUF8DiT2PIw30RN4CQRVJLrXw9WekB2c81xnAUFpHPc52POWF8IeIA6XANG7XuRpXtf6KeeuddVkiIh4lm1VWRBLoBgXyGO/Y73qJC3JzB6RzxkExRer9YZ7kQRK5/c11vmyTUfiKZdotmdVuKlfoLwlerIfDTylXsVAGslR1LSONq5374TehpLJ8snypCXEKWjMbnC9pq60triXpYC7ZLAdnBDd3/Rs6II/a5ZM+kXcHyYyrsxs63vGIapsrvS9bPosn0ydgW1Uf3ioBIMPPh5koppdrRPx+Py2fvTekiDWzfIEUYttdsV/hkFLGHdEnwcrqr2OpVKt7TTjWq8b/ottFNF0ucr+rDNZ6qfYDZexBa40VCOsHDdutKeGUNWXzS8f8fHZw9yCk6YDyGBIX0rsWWd6cznH8W0ZMJDVG+G8PgA/6F7KeMpbZf5bGRr7MDJuNSe1vk6ql3bsxh+iM/5yUkWWDo5Vp+d0NxqnUsXMW/KSdOww4XIGyvS44hzezL4unK+o/YzEv675ksCfSAJVJuKmap1NaGvAjq9+t2yO7+NxZV8bI1n8K2NhDgjr8lbu1Ycu7+AIRoTqlIEVDLtto/tZ6z8NF8idSSYspduDKFsbEcN0FQ9laKyhn2pwGj+vYSZCcVuawqP8SA65mT8adptam6jlsOxngnF4BQJm0mUXEbMMAFLKmKUjDvAZDxLoTFlH/1K/8Ld3ze8w0ISIXH4G2rYrsa8lM2iENJeofsl/P83HAmDw5wRt2Y+xL0RRe/WpkwK1sJf+cF4YEXf59ExDu9bGPiLx9t2hvEh6bYOO8R72RHGcfuncC1NowIF6B4MuG2X2Skc093mIQfjdLNuJG63NtpcMusDDVlNRg/xAX0qH4lStmRmivmeGqfYGs/+bPPh+ImVV2mEMevqQWyhbP/Sik6hNDwR6tVYj7JwlepGFoZ3QZ5dr7YHIhGbKsA4/aCWmyMxozSvbk7JIm1mlbKvv/n4djbuOJPcsVz98Q2uDa2naTP39CIJBZgj/5rt0XgLpucGedVJi8mHfmGWCKmX353ctgzFKtzjGWJrXLMLpWb6uUOJ1BO5FumhzU16heCwTS2sVF+V9Y/+lpnXrx20yZ38Pfz/cumcIMW/bfSEL9b2yLYcnPPj1DCrl4RE5a9t+tdtczCe28jaWbujC/t1gquhsRqp5nn17WqlNRqRin0+8/fHtbb3NfjHbjOQ9TcHXb18S+HNLoK0W5zjt5JrV+/WC0+/rRbzryPYMIUXZz8d7bmJMnBGgNI15rMHaLRSgSmszf/fIlZEvhqogwt93/fuDTlEtOd/T6ZD7PZBqeAoRpmP7eDdAbUbFW7Xjd4KeZvsQ0lPmbpGZ9TkGVGR1BH7mePrYC/z28jpF5bFPpW1IPgD3wyEA8ggE8rKrjLrnuJOfwU6DBHGlrStKH+PzOIBhekiCNjp8lTAoU7aTOz+anOBSfZ6lma1fqpyLG1tGKssnK2JLgK11wOxki/BnBsKlkmB3O1a6NpJb34h64s4c2yB5DUyD4ClzoVUgR28v6hzBS8mDmaTSB+95lhiCn/O+LQskeR6yMN4iNtFDaRRUcTl4SmGf1xjDgRjmfSiWmvd7vyMLdpij09iTdIRS6kjq6mcnUNa8wKhOXBvAPPUwyxCjVC2UhWsh50xyCZnVHEjH6lhvjjvHNEvhMptY9uQoL5QSP5U63zztavNU7nYjOvy+LE3GgaoYDuY1n33IfEybzzanzVIsxrCq7PdhYLf9S9cxITMx3YIq3tu4RyaiVzNENjVfN4BIxqGvpz75/sk4pfKQ9XAZ+XPmi2Flz1xrzpmpdb8R28uhAdNWzxzGuVXYUpTPsRfyGymvMdlb+eAMOka7dmR16+RNIcTc6PALHex5cyxeLbv0EFFbF6HvdXRbIDKVubm8Tyms2+L7+v5LAn8uCRDhOCWxV+6mlZJVVzYGy5uRrmkhDuuzMiFAYEdjDsMCMvR7rv/TnFzT+bhYY3p57SGbMp1aa+Nk30AQlW066tJTZCtEf0pS34x8B6699FBlcLVHK+ZOgFHeKdSGRrwAfEX+W1kZnIVdqxsOQNR/5+Zs6BAihWrHYTRPx1Ipml9s12U4P8rr5CuCFfSiUIDOJAbRJtsDaCzQOdwSwSrkKJCwgaRsU8dqIxN2kFnO5zgFztvFgQggmHkMBIiA+sFx5Hcxdw5g+X6bM4ETueD9SQ7CzmT2guTSdN1fJFaEY2SfQgbZnsbZTkHrGcosNxtZLGbmavz+AOl2n9f4DTijZfvie703hyVs3mbtUdtLLbUkUJ5I/Yo5aCWK6Ic40MrjWyZ/lM+J/Pq46hnrr3b8dmvTHzrJd+pBKm+fr/l7I0x4dson7WMB56Z7YW/SPo9Fk0c5y65ozXpEz+eT5UtxqukkXGy2PULA9bK9QnbIiVg8jftH6qu9L9A1j6cdLPcA2lyrR5ncBqK5ZfZm7pW5lGndQD8J0Cu/OUlY4e0+Zm9He3D3+B4zODsBi7Y2n/tAeyKo8Fnsf2rJ+hGVM1PXLKmyytsu85jlDVq0jNBJJebJT/18qq7gLpSKxBgQ/Pb+tg4wsCh4lvjAo7xQmq+fvyTw55UAiI8FC3gauwwCMkB5Qe4LDhjRR7gt5EAF26pBtDONCIH7SEFPOdpzDte056i9bLeboZttyYEpBJxET8DYo1EEDNOpPLaNwdyvw16mUZowXR5J2FOUUE497SOAy6cMuZx5AFDvlmCpsnTe6BbBx7M0R5mS0f5KlKMVhFd78e9QhAl6NUbBNX8W92SCtohoxMkGt/KpfGQSH5bOjSwJKkHrCOHqdCsZiq9V/uNyw34YhGZ5X8ovwUmt5XincNtTI6ArAuBXR7gPe9rYrj4XqK+VJXCr/qbM8/A1yVg67BrawTznEwSpjZ9ZtJrRBdYbwA6omkRLc34gXkWiRCx2gqJvUneQqdiImrI3I6YjQC8SrZe4zjlef5P8SFN8rkTGFZol5e5Z1cqqcL9UybgCFfouRpkZIcm65qxkDIIVZZOecazJYn/bfiOQc63V1C3IjP3LjBh/j3ZOOuoGx0L8j1mSz9jkvOYSwDvtx5y+TaDVtP5CDo59SZJFS40h7Ts12nHNM3Pj+uPzbUQJ/pWVE5NkeGXUtDLd8N5kWZaoaJnkoExB+iu7uDwnbTbxfD8WG+XNuUYqEUHCQPn7GO/+f45nzilHs3RVGcTb/I7RdzINUc773//rv/8Xrweu8hMnuv0koVn77peEsZDlZt2kKfW3t+92ZFVoB+c55RNlAL+8v33/daXcPOIhpzF88N/yz5PCfApE/S2l9SccNElBWiiVm2lvT2V90iAYUbq7khnRqdLQOCwA65NrOg3ecDxq7+etf70gMHczRPzMNyvxtDLakukS/G9+i/2Ah30I6r+TJpSzhWrk7w3USm8OBbYjG5RXhnwdO9UBDSIU6rfBF4KGW6ZN3lPIPCOlBLBFhAStHRCFHZXtVmRVG5Q1VqFCnv7kbtuxzSDRGmsCMPkF6RZSRYSMFdF2/RUYVnRb8moALfqq7hQorbnRM+ByclYF+lQq0O/n/qFCKsyqzYzDCQBmf+H9GxmI0jofX0TeY35omQrEBsGTDgVQ8efEfw9ETXvG/IoC7Zhe+6UdNhCdp8zVJxGDXQc82rumu9o6AP8wU5K544cTwQxQBeFahgMBll3GoUOHPWIlJZy6BkG7TtT6KUJ5en8Q5ajPKJsbwQYdMlAkYmSQgOwoC85LZtNyrt1m+QFPHRpzTWneK0sTY8Q+p1wPyVWwT00BFcybt1XIgqfYKS7lZkzvR6lyrCx3umTEX8N8ro5a752Wjxem1jJOiHkqEdpJx+lZpSMhem9KHW+Eb/cp9xbrl5MMRI7smYqeMCM2Ao9pdlgG9lAyp/UxExJ7r7x3RBuICYVv8LsqQVEya1UOF8jCr8knFNxruONQzjgJ12Q6ppuTTkq/49/3/1rv6UGtfXFCKMAllZgAaURJCzhNIcaubEyuHdkbm+2UCuZhB3/HIreLvnxmTX1d86eVgFslGdO1eD+s8mGcVJDVtmsgffky68I1m+4hQagOPwgDBpCqfSm3tn7/9S/HjKO1YeATI8DQ54kvYYFbH0E6MiN9sVdPqpFuvmWsIlONSL084NGBgxglqcQGY5bd1TgLkA1Kt++9SuPdLQSwY5YiyQbztwSzLVBVwE4Wv+45H5/dAWmhgty3kYMHXNn2rHgr7sRqRjfCRYcVE1jE6nQIAiFSlLhgf088tcVYmTEzMCpkP0uwhp5WSc5+mhxBtqMEAu/KgGzzM6L2VStxODRhQBRmPlgulGBbekwiMvt2yCQ5aPr+9i1PO0HJIkjALCHL06+U6VC2iKQae8EM/McrLUrHatU2PQfjNWCPiLRUSsc3F+EusjV1OAkESrC0XvIAiEaeKtOmHpIg1J4lV+AkxaFT0sMqH6ws0PqUfVeGdj0bB2dY+yN7mjpXnCVJp8kHct9tho9CGdH+DiFFIvbDVJ5salmpcYKaADpK23zOapEQbPM36/fynciC6bQ59WXPDARBSYPTdQriyh/URtrptD3uAdb/u/jLaj/5hNeYbt79miKqj2brY3/NHPvxua2KoKqNU6ZjUvkcVl58Gmo1Gw/Z3zJ0LPljKaSDnp6Wm9myy76v1I3VhEjPaQBtg9XM+oBNiUFOA95UK06MyWvgzD7Wnp54eV2y0eOcv1adT0/E14VfEviDJbC0dwWLwm7jQ3yJ8jYBJV/nXl//boce3FfZbTjcmAk8etmmWK38vPXvbb4y47cNpOrR7f4929VlTKdGJyZrcpPoJIBt3g7CPvW/Ra4QXKp53d/3w/G2Z+JQBmUnVIs9j7WVxC3bgE2xOdZhz9meqxhzNj57s9ypXkta8cHUTxENEQXZewDHekYQL0U5JygPgajtE6ETsHc+N09dG+VEoY0tKxX9irxgW6Msa5pOP8HkqHjw73WyU/RHIHVkCzJzBZ3S/Vo0JHq1N6pXGqmf85QyzaYAI3VK+nA6QrsTA5HjRb5qj9NcE6d9LrqmshRcnw7wtdY6GI9RB5Cx37bqqjoJLU96wt406bLIxWpiHm/N76x9vCurygVrBryPXA8olmmkBhmtGKDv2Rllc6nfJRebf9nMKIs2Hdd7fwQkSWRCwDqoRm0wW+TZvAo2kKgmaeZBEkGuLKsocjkIp30/oNmJSBxMZvuK9rju71Z/2no+9gnwq3/WGvZ8WKYcJcShYm39q9CCYyTmPY2VpW2SG8fkdtkFastJBFDkhf6ykqB+D37T+K9ygYDmNRS+Y5PQaUyEnqf3rs1qr9+EyJUyNvZ4LpecujCfQz+e4xhttTFpja1Hak8PBUmx0vHfwMFNmZlOYyeLxa+3ePthvq00RGf0zpX0atX8rX4P4xku/u+YEfvPnO4oB1AEI4yY7W1bet8WHEq4LkEHuoRGbpgpgaAmMO/39yg5C8peEZbTXNz297nm9o37v6V9PVNtfYY0sea62aSLMk35yIGZvZz7mR4yTOksDkb+ZGNdLbqMqovoFbwBQUuWRY2yJwGU1YIAmGw/X3Dp17mEaIYT76AL3HydYNAFFOAhDqiJKH7e2va4jTIeRfBikhLASQjj1KksI2NQoGVX/Kn62QH0nskyuYRMRRglV5Ziqj90ztm1EVUXsBGKzT7YEcVAtwG4jdQEgpOOZjZKhBBgt8ZR2Qcfh3xEhwjKOsTsVrZNRyZjD02Ske0Fp6UbWQ5Feav/7GcSy4KB1hfs3cvxYj0eN/dDL5quH965UzLsVL5/7w+kvGdfbM4pU9Pf0HGSDwCslrlsGa/aW+YAeb6LSYS2H4ChPm4EJZ7ZCG+WF+5ZwhoLxtxITWXzWD4n3VUWL4Mlvh+vhgAAIABJREFUhyBJK0s7bNjXWqNJZOJ+mmReXxrkV+nwFC6ndv8A9CeicgLwJFLBF71yNl94GXoTp9itv+a7u/SsScpgypLMzDFbn+w/3UPWX4Un2r2nwcDvZl/S1tU+Ymv7kIU5N/nguWNic/wga9NX63nJMVi61+6bEqrgAE+tSz93mOj3f/z3/35n9NhTjBFRSY+n1CNic3AYc9iqzU+PqUj12hxZiejofTlGW2bf1zWVQpzxxn3IX998SeA/RQKql3WH4nYs4Of3X97eV8YzrH5AtDTofkyyIp0slUsz4deGo/O263mOKzzq78u6f5YEf7/1r/0Nvb9ZLjb69SqjtUetyoyex0hIk6PNKN/mYOF96dhbSS/kuNvQs+Wq0l931T3bUbLRPLLoJcHtKTqWBIJAd7f4/X0eKt+ryDNtuchOEQW+TiDGB6dpvYe3ZNlYEYgi9jz6V7OjTMssB0rwPSaqxcXG0c4EFd5+uW3vK4hZ2xfm1yYBioba/YGA1M4JrE+wTUJVnE7zFeBJhCOmzsBpRvxrv0iNpzREFNkAMcoqueI2oA+USELYMgDx/CpX8316pfMzCyif7nJ0lZiHAhRVYRZGMiZZSIBuzSJrh1K0nM8kLj7SOSZHrMrAhOwxl40UD/La5ljrLa7Z18rQt2g4CXUjUDH3LwhblcIpW1P7b1JusvskzbHHx/vYs6BN3sgI21zjvUGV2ap5zHuZuYznGvkamU4SEulEl5L3z4dQq422vtatyIDWzagkGKA915CCjQhazd9IbqhXtqVnnDY3+5YW95LBoE3qBE97pCLzw3WPPtNPv/KR+axZKD8zIpJVmce0siQSrBho47CT8qIIdLQ1A4PdRdyIXBn5sn0JjdLFdGIWbckfRRMncvtY3nYEAtKCLfI8IrcbaAjDZ2+YXdeucp13q59eOxdMGNHDTPQ8kMjZt6+/qeKnqf6S0J9BAsuUW27T+f3bu95EXzUH3s2cQpU/Ve8b8ObJXnhPyKnky4xEGKjfIoskROHNGtCfmZvb+h97XNJYhhOa5UMkMOwzTcMpbd9ldKMWz1Ko8fYIsU/P2d5NR1T9ZLwLc4l2OI45vnIy9QJX3x8mKAYQerCbrTwqCECCUbe++r/sHAGyYmHutEisOrCkPy1QVMzI2xTwBByPvW1FIgZBAcGcJVQCqrlmTv0TiRDwi5uUZaD1DGlIKv5vdhUb1Uc4tJeVjcwV5moCZInEwX3sF+CcVBxy27siSpQlckBwJDttTASmOdsCsxVirfkPAoHlIiKS8xm/VXapLrZr40+CbK0hjbvLnSVjvYyvDlxgCBX9v2Q3OcfUbX8uytfix0kINOZNP7L8bPRZY1aGJ3XOfwheEOVmXcb6jXOY11t/62S3PRMWvzHbT/1re4y4nouCyI7NjI6/NDj6mnIqcntaO93K7saJgSBXXxyOAPKTQaJD1qg/w/+afoGYnqSL944lvTebez5jDuMKuOsklrT4wUXTr5/PJjoB3gqM5eEKZj6DvPL52KtDXZ/PgomQ6anARBwgJjp927pylVPYV1q/J2pzakc+wOcwDUcepKIT9Nq8zXmQIgIKJ+k+7elpjByNcQHmNS82Db/kLWsEv7qlX5us18ePj/WkeIe9Ra0OVPek5V/ffUngTy6BUmXftJsGKetcoOtzh+ZY2BPUkOq25wxDpvtOZMEMzYgy8TuJ96etf8zX0eSP2oeWMTjMtRnpdvDA3eXOqBydwZRBIyTMSgHQneQ05XVTz1d2MsmfGfM58xRir5HOMeFEJ8bYAx5EZsDBlD9CwMZbMCfITdXYQNqAGbqiblbMtiTM57QSL8kz9/UIjFa/ci4OBw1U3yObmvkdHDYQ3aAYCYLtM/TuND4SrRwnXnrKfRQF+Gvu6iTC6Ffc266VvwcyEDT2SHh/mZ2D4gLPp3I86lm9iJNZyQLTRQQCeKXhmBkUZKADRtU4RARmmVbP/EhtMsPQ2jmXZ+VSWN0b+zLW8ytbBMZrH8epbZIz7pHOlw77RRpX2+iPearT+i4lZdrT055VGIeEWGOY+pelj7lGKgBTGTof8zwVjzIrfUGmjkGNS+bJ+wV5HHR3ZgplZwrCVwZz2sRXtlDX07adyuLUTi4f4Eio8maSJ9z04JcOl4iRIBBxs+lP3/fzQTHDYyP+7MskCTfy9kqm2S6IsWtMl36XsUrp6hoFaSrgpJjW/p6n1ifa4Fie7fmvMmUbJYg+RbvHShBVu8zT29ze66idXvOsTm8AgBGMY816OU1GcsNW5dtj3RhBNIlKsLP1t2jYn/6eqdp/+g7/CTtIGd5M2r9fzt4DO7Pw7dv797fF7x30wdhI7wfpmetuyyoc9v1UOcg+ZXMPkNr7I9d/7vvI4547uKheu3xO+wR3A18goGSm+wu43EjfSblr31AvCaOLuH1OuwnQ5NirlwgREJzKAuiSZgbsDBQKzKCyy7pTJXvIFplyonzoQK4S8NgDpc3W4gYmnzadq0bBAZyPzIBSq+X2kw3tmQm0ag3bLxnd7ECWwIhjzaU19iBkYA37Lry0IyLGjQhW/XuCe8kCZaMLGNOdSUrygT6SHrGdgHz1l3uvVhtJVuLgE25unwC55lp6m0m97T1PNZaQ3unUu9CR7IdVa1RZ7g52g6y2TGLtF5FMeF99B73K+eqldBshgMT9eKRLOZcLJpeF3r3UT03z1v2/+74b6djpYITSWa0R7dlJGJmZHq98iZKuWACN2IBMc0mqVM7nAmsoex3tWma+x0rscpSD6hUhKkdU/7mu67s65U1ry3S0lfzJjqME77AeTrY2127YgmnbCPw/a8M3RBDToLb0jHqWC0wueCIH2in3SaEpF4jBr4vwlH4l1IVAsm8oDU3fjOskA5/TTKSDvlxoZMxXHmCQ2L8ydycymc9hJgZrZBsLBs+5c1PSNl72iKukGpOX1Qb0Ecj6nXThOG9rT48MY0Xy5olCdcLF5my5t+ewX4DOpxhNARhbEt/fHACu0pv/tySxRvL97f3b+9v3X8zql1JV3c/Tevn6bZPANv1/2fQZDZxjEb7zyY3wycj8EUpTaeMiPXLOaThJeqIEbsKnNNK5/vp7A2bJgMZ2Wr//tvWPUoE0gLLbo2yPcyOQ4G6+mwaBAreltW+pgez23p5u21rJXoCRqk2v6PHx0IRbdOoH3zr+WUe+yYTjnZHkaUKBFgMvJRtUhD+dcxAT8ROCH+nOPBRA+w8yqxTP47MyEj88KzMWzX/EhFeUuWxakafIVh3e5yJnbfA15ko16+Gzq+QIyR6JKgEnQUgCTnjiPD2skJWXBcVogEEIXDvwJwVWKRT3VNW+B5uDWAg5rdrHkcC/l0Iy+CigW7ahl3v5HNdiU5ZJlrQAelgpgOmegQExTXkEKRLYH3orHGc6N05505yTCPh13tkkgwBP0j+/R4PqhwjwmUXsZbnHgQOz34fDMfQoyZmVzKbr3E8m8oPF7etB3iGCAFxPCKbU2kD5nP0eMsnPQXqE2WKStbY137UWRV4PJXCwJSJPdUocnnvYc0dU4p8VcI/5bhkVt0ieDUTp03Dc6nv38ePY7IF+nmwuhpfPTc2JH2XCbsGx3sVLiOpWNTWyP9TcbJeCPACZIyGYMjz4r97shTxN33J4/pxnES2SIDPvOHDiVOhCUppY6nIgzXomCZ3s2LanR4av19bjUawTxeBSwUB89rEX2eHr6b+9v739sohP7m/QKg5RZVTxIM2vr/6GEjis8DQ+9d4bdzZcqFWPX7HGP5b+yAH6psgItVgXES0i6YnPvi7be4TbvCtiQgNNYsSLmdE5ZYvs2gYaf7/1b84YRzHPPs/+3WyKGUyVYY2a8E8tkATBh0wQfuMLFGmkWxYOcIqnxclZzf7cHO7p+0laX43tlOYn/NVc60WSkn8CWoDQJJuHshehMpZXFaRUL4cOjyDflE+VJ50yIh3EN2CSmaH61tddvSi3l0AK0E0QvJMMG4nK/Fr0nGVhVSVSuQSQlOEjC9BmOsvFiYDGGq2Oje3AVmSriFwdEFGhEgU0/Vl1zDPXV0rrBE5HsYWD4y6faLlKn0KcBM6pHzDh1pYRGmUmXF9m9meeLmiZNBySQOLtbYb0dXCBnpOkNMag+aTNw9zyNL9NR1s/95K9NreplxWQUR+368YaEzjsWaX+3hyfE/9Ptedrrr+ziCRIGlGWZO5Nay+3HQdJSBf73p9zdiz7lGmScSDGKCk9ZgGim8OrtyBm6XERI9pB2iV6/yT2zB5EUMMC8qFvo6p0M8F6fvoyNxooIWZCAzbx3UF6C0gAAqfu2XuKao67b48xvygP6xa5CObNn0ivRO4mWQmzklHI4+9jP9TTs9q84MKERiFkzctsK+f4QI7r5aSIFhyjmKPVmtC+ofeVE57CVk7UhPTh7yLRuf2ynlYMZJP8Vy9z+6z0vq47SUBOU9X8csICKh4DRslPAGV7GWHLBv0+8oWfd+wMB9U2G55ITwL6s7Gb6+q+hhV46O3Qifyx679knX34pMHmLIEyjqrk8x6lee8EzeamhgMiIXtNxu46RFmfxuAbHCfx9Cvnva/bAomDjddzCQAUSee68d+7jecRw7m2DpkVa0dR94RhQTAQtk/ZjxKcvL+B/yI73AtUALk8U0bWGUHkPqAE5TX7Myes8fupd5VxQY4xkgE+Lh1Xm0BegBupEkbde8bFZyWzEAHG0+8p8BGTR/KTcyYXCQNjWSbNRQIP7gPZbUHbW5ProAO0mtvo96ihzHfS4N0jOb62767vt8ojsNlnkG/aTdA6l12QsSabLA8L4KrlJVmDBKmNVmevzB72ma351wECwPCZUalyOZAKHHggEqqMSCMqUVaV36H8Teu2ExH3bCyb1Nqxp3MvXupHZR4zU4fSQJLGlAlIW/uOmT3IXxnj0/6ouQZWH0W2WHF0BM8MKsGAnojNtMLSHTOxLzIblgXOXY53ez5taa3h2hooksLx7GNDcFE2l1meh0qC9XLSWYqmLLZum7/Ltqkq4iZr2v/TNXzOURYjg3MijfJDFXSDvwPxU5+VcrNqjuvU9OoXXWak5xalJGOlks/o4TFSmRMUICsX3g4vfCDhbt7f3r79ukrdvr9ZqbAx7HiHCTX2tQ5+XSHn+JctZOMUc9noRZ5KoLilF9xfB2a8ryMD19fLGi09i9p5EqYOF/9VdVLpx/vbx8d6m7avqPf3X97ePr4F0R/hG1vNbZnXu2Fg+OeK2oCxgM7YQ5LxOJaoHtoloXAf2vf6/avrn5K9EYu2/wh6nYbsOMYwnKPsNjNdAQh6lgKO51A6mI7yItNTCOhWEszvTzJNByIEN2qudU+bbzpGfL5FzkijpEeMeMq5ZACKzF37cAbY7YDIjY9KDTP6Pk6QsrE2QFYPEijrpUZyigDvCaQr69KO+cY7YdTHGquywACpIHwJbNHPBrijodpH4v1LMhYTYGPB3gdttjge391q16OYY40Rb1+3h4gstvHX860fQaIpjyRmB7IwLauPpQin9CaG0yK8CWhDb1OvtG7lx7G+JPVsL8elIFWQ5eiIk5si1T4taqWItWnRMvGNdIZsMuMlWTEDgolGOaPkUHtkvNxKvoXBkNznEnZmBgDUJcmVZK4ykXOv2irbq4DIRk4A2kSMNJfc9yM9UKZdJYtdjqUFZRtKxjNLta7eTlYcZLWNMfUOa+UQ+KAu1ufClXwvTtlMn3cSKBtzkAOooExPzxa92Csy+yRrNf899/35W5LoJCzyyzDka2wrYGtfXZD/U9kedVnmIZuKAMtTT2kTZxnZtB/n0+p66099pY8zGTc/dGHBl85HpqcUSP6LTsodfbSQShlfRL16KdsTBNM2ZL+GTt+yOQZCv719/7YyOwFDV9nb2/e3X4J1G2POUqDfolL/qffMWX49jh+/43Wbf9Yr3LmpVGrtEXt7e/+2TgB0l6T676gbeVtcw4gHwttykJ4NMsaNQvx/beRcFVbVZnt1aMIDqIUFi5hqOzFr60F7d0/CEL8M+0hOoDsNHurac50PkPpHrH83tnXgietuvP0dR3FLBnQGLGtJIqD9ODXQNGCKbFVbVTje7F5ckKaPDhl2r2V92v6dA4GKUj5FP2UH2UYPQkEmID+t7wAWmus+v8/OZc/k9OsFytyvCnzq/LHU1Fwr0wM0cJoMuihDAarpY/zvJD0DQM3Ibj3XK/9VjufRwIryu7O+H3zg5KvKxgSWEl08nNJGguYSKjTSSrtiqHXccJ2MxAwVx19te/+m3FjJyzkgMDY5rMMh4oJG3EiasMn/2i7L4GYpWbZvI4jVLJIS8xq/5BhTt6qqo43RlmlkNEztQHjjMSQ+svk5ViCn0kllh4I8SvUv+2gm+Gr6iUMwcn0HfnERREbrULp23Ps0MlCSJMv+Nh1Im3XQjybvtCBOVEJNFzGamZ+mS4F0SdRkPxqxvJ38lkS4lwLmfAyCue8VrAqJWwZj2tQjDko7LanK1tQhJTBVIFB12Eq3kgUVsgQsVr/skGXituO2ZT8vvOWhJIwEq2az77+Tvs4XnFIm6/PC25M8tmDaCIYmSQoh8TnCUbXyvXdG0mL+GxM4yET7ddQ3BYse0jrlPGC7j5kpHlnNcg53njFJWCxzovfih7riUxHhETm2qMyaBItmfTj4NMPhWZ+PrMtWfGXv0V/6m78Tk/nkRJZL9XRmnm5kta/LyX8LohwpYFtkHxEliciZjo+OVaItkwmcIhvki65PQj2foNDdXkbSW7nJ+9t6D+lxAaf1WG1FOuqFHM7laA9Fara0ArxuWQTVwf/71r9Iag+RdCGkQ8LXr6JaYXpbsVYeaREvQXAHH0GgBBC91G06g+lkmYEBhsrSNDetz2XBp2ec1IB2mo6Pp+LN+2jnt98IjEp77TJps8CNqbTCyaidEIGbwLuRCTzYVbBAmv2FpZPtrLWt97wNb5ZR9ABwTl79ZdhbPwDCq4RaZUB+dScvrhMFqPvSnX1NApsZRh+sATvFWRqBnlkUjV/7AAogapxqTz7aQPOYzMqa1B6evD703MkdsyZsZ558xrxgjCnmrjIHvROzDKsCUL6CW2aglY8VuLVHZB/LLknHb+9HEpC+kZ6SoZ7lM6z/z+CIystiUxXL7JN0yFbEF0lcMS+MWZQuhL5J5wXYUjZPp895Bo8kKHU390f5Qipi4wG9Ir2ypDidLowP77G5ypR4rISWqeski7o4T7dr2cFGCnUKHbJ2gaC3MY5N6uJtcrM3X7CTjpNVrfWa78YJHdy96mctdX/O6S4fqr+EPIO0RBuRqcrysDy5kmuxH/Aw5bDBx5d4ssomTQTS57FX5nO+1/sp8tOgTtijtD6BUcy/rGqvUb7HLJMcUyOpsBkiaPKV7//45/8aJ88401a2ca6Lm6rCaHKe+gMz3KsuFMGdilBlCU5+/MWla2GvUjd7iWlMvJUITSt/19+/3C+vdPXV7yUQXvn5u/5UAo1uezQXXCHq8Hu9sLs6r0M1el3kJNoRiLGsYpZYhrLZ+vjWjk6OpVyoMCNngu0RWbYF7FasgJKTeyNIAYZTto3z9Ll5te9mZb2UES0QvgPt2vzvVqBlSqKTMzNymvufsv5H2ZyMbAKltLpPC/9ISbawjcvb6wxZFbwbb8n99do4zcnzPFVfz07joBMpfD/2nLb7OC9ZFuE1VZTc7NvVcXFetEYCMQYmif0EIMrmgQqsq+0EiZlIQVnZofxyIxRjBJmZyyzQ1PGKpLaSIZYujL04kmOVpjFAMN8Dhecl4C3QFL43NpJPYtL31dDv534YOTnI00Sv4F90NkFskr4eWY3psqs1Lq4UA6Lri0AOIqIK3STQH+VHmRUAmkzSG5PHvVcJgKUIaG8SCO8rsj6JkipL54cMCO7U2KjnRf4GmBYwyrK9KqVrpWitfLATxLnm6sWpfT9bGxsyQvZ9yyqF7sAXzQwh2/L7mTWlTnkjteZEVop8Sx9qnrvupvzDa+aThq0m8dSBGwqOtP6Gos65ZpbIj3r3K/y/KKeObIH03YKaCs6XGuymsAxVbbTBVTfrnvYH1xaI9pC/9vvpkiMNCgD82ouMrn/iBi09VWQc5UEALp+AtkkGbz6Lz6k13knWyQflRI5Az3W6Rl+vbW7h534lx5Rzs0jl+T09Wnh4IR3eg5Z2+FB/WS6mD4mgaA5ijzz6XocFSj0tt04fcaDa2OEnFOJJYH+93/5uAvHxClBFUMCOo137dsqvSmvCkAbgsnflmM+MjKbhYCcLMmxZ0mJf6EBOmDc7AEHrJU6Os/vdcOfGaHbBio1jnxGdR90em6YVcPD2Hag7sfNsQd+HIMSS6XVU6JQrqxVE40zAW/f/+9Y/Y+mMLpXxUmWiEuHyZcPlhAH1MQmU+x5BArvcpV6X2eU+t93pVrQl1hueUWVqu0nvBUz1u+bVAUh1gPNoRB2RVcrhVdaoXZvPeLZ+hS/7/i3rY/xHOCIzq/Ru43NCmMNZNOu3Cd4kefsNR3Hr7+yHhqG1kOSHGq/VmAs1S41oJQrY9+hmK6/LJjooQ8QjBWuhxBGZKwDasz6ae9kMZpMLIMZ+IEydfkvdCbUqEKpAYtkOguBGRieh0VhbtowHwVTJYB4tx/AC9gAJPLZ/cW0SMZXAcP9QloUNwjEIk6uAg/xGWlqWrWxa5LTtV+mggfzQtwbWxUyskiAEcyNsVsbUQ+EiD963eT9efHkou+R4pPdb6V40mfOOcqV++pxIkSvKtgcnTJr6KRspL+cAH6WiqSNhXXX/qbxNZA9rlYcvkGApO6VVJpvDjKLQjkgWTYGccsOLkn2dE+MVD6GUFmiP/XJHQJ5jq+AAqxFO90gfc9IboMcx2oBudk/2A7ZL10QsLP2ioEBUQ1U2uDT82drXr+hGu6XBl1GdJZ0kGTQv9rDPdvbnRKiajcf+xhUiXmv2enT5jTSpP/+13tOTq7Dea5HEBgdT5hugVQLajCRmBHU7PcMzHBijfSopaaDOX3xmQPSbq5cHZ9fZfmE5VvVbfzH1Z+f3r3HdTUt/cHQ/qZkffOrPudz77kRHb6BO6Gu1u+udT2ufz8fb+/uvcTCGG+mVQfST21bUXEdBIs5kYcqPiKnLiIjKCESPXC8BX5SJ+lfRqzxVG0fompXwa2jM4gkHwKtRF+in6XKD42ajggoHs9wMxJ9s/QO8XO2Y7FPYInEblgGZXHIvTZTKQDaFS8cqaIGekLf2O9lU+UMl695HttXbvQaypB4xTeYItPkeS0X3VzthC0NxpHLCVqUDPMG9xlORZqAx+fW2vyv0KaPH0v8uU2+Fg/DPLPvayADk6CVXvKegC8uGeoQ8+m7ACsQ3a+IDDI/N6z4il0XJy4Wd34O46JoCXJDZBME+gTF2/VtzRYK8vpXj12dmphoAwBgcKOJ/86Sy/KnICnkY7/XnzkBKtc/DFgiCZjmjBZxaMMdrYuoFmOs58ULTzHogiBP9MBlHtr3krllB2Rf2gYQJ9f2aT/u24nE2ZmUVSoVSajrFTsEmScO+z7l1bZjlYNbyaDNlDACuOdR63srKnIkFMasSbu8kSV7N1UZ6UZ7pmZT4X2T4GhlFFpH91zWNKIUi1GEO8ELqM5aIDYXr5Lr3Z6y/MZepSykXSeNehio/YPoaC/hEVGitk+Bt+0646DjmOkCBe2SaNzjBBaz/U8vdg9QVsHRY6bEd5EWb+3P21hp3f3rf3K2DNE21ZNDX3S4R/8gV0DO1R+GPz8jYVsx//fP/elw7xqvYtD+kTOJt34CWXbsyHCUjzx1YwGJAKHTiXvenzd62LdXL26I26PvHh0fzLwp0Vsn/5G+nVlENYMGOG0X+k8f9qu8uFwd68Qb3+HcpkAHTyOa8vf3iBGc5YCPTyprgOKXc27EO0/DvvZgt3kQQJxBQ77Q5OBeRPzBDgjlTsCnruzzxpDBdLrla+NMQzXLTw835xgvGn4Go2SGV6v0J1z/BWY+kUyZdPlXK53qTbxJJEB36dBJbU7VaXy1wk2UX1YDDjgLNpwKrAk18SEVNRUz3vTg3txZ6P0hRxnEVPEpIp3UQckEWkP1VaXIfQ/WBWXn/tpMFAncnJYynh90fZGC1w7Ign7gA3yRL9EgC+6m3GTNoZc82iwTyMZk+u35zAb6ysWlC4ih7ka3yc+GKYjoBI0MXQiesQkEkoCyErq/ymLGxWwwDpIlx221czVtXRkQjMrA9AIjND20SCFMvmdJa2r292lSWrP4WfvABTGKkX3ONt70pPbN6KsercqiSfAPuMV6XEwgQMkm5D6odQBBjjI653KAXCMTYL2PPTr+2M1LOQe1R6SBd6lpEvPRIIZCct5hPIzEh+JzjIBZJYqWnWpNY/+rXnCPOpfe3bJ7dE21Jh9N2HfbRkUzPOelHf9dmdz6jZX5R+tZJWWl4krBVUR7v2KFG1oz2Q0261taCyXUkvPmJF06b2clMUgU1mgfIqo2KMnmm8ezxkqRlVrNZpGya49OX67t2ylpklFRRsJc3d7vv+ykViTg/t7nQwx+/Fa+nXzngfbkTx3PFCU4ysNW9SE+Z4gEgeJpSRyCMpb0ap7uXrV5fLnNGqhBitCi1GyEzcrE6bT+Pxd6j/C0iE6kQn+rRX/EiWT7fnP+3kkdjIUFmwoOsY6ntZGo7Cn05GT8h0AAc983E6rEIjZEbt+xWCoUyujpzf2mhzvL/pcNP1CX78dgKLETpDOcH2aATxPUsRUUktV5P6WPN+ckk8XoCkT/3+o8SgMQuQoHKLpgpDzJ7KPeLOdRqnzK77mOJG1rZXwLHz9nJV8/K/VSwjb0/k9x5pyapmpZsKxeW/lhM4EBCEtQNEjYIR5NhrK3qr+8vEmjza2s+KmjWNZRuxe4mCI/nZ2m0gJvWdQ5cJVyaNP93HtlbQDN710SXIA+hd74zmBmoIktejdD2laDVWWZlAZfE6vgsAAAgAElEQVR6fzJKsQB6NM4EmtVggUcQKPN/Be4nYWm2gPtXOL8gj40cMStAIhFkQCRZpEf7Jx20jfI+EFll6aqUzEvC/NkgSoHItsMKksgKPwQ5iDEFz0lgnie+IoNIUC8iMTPEyroQ4JeelA3yKhgQsJxD6dq9NJF6mcQN98eqyr2H2/Uoz4tV19ah+qsyVGVJ6uTAOtKb2dhZ+qa2tTy01tOizKPBVUKMqBHL71YJl9Z8azNsx/qu3ndUW3H0vMyIxXUts3a1qUUmalXtXvclOM8MclSMdNOTf7EOg8SJ5XfNCEn/Y00Tw1lw6JAt0bwUJd3H6M8+jLN9X79PwlAF+8CVSpihk3Zf3MzS2QWBSERFtKRDVUoOWwdlzuuD9ORWBjMXvS53G+sYjGd6BiG5gyORFz5H54SrvqRAStt/IAd2cNnOa+qkGjfaIkXrZaUr1r6+c9FZTZ8iLGZr4kSSmNZdif5O30T47m/DeEKj47Qm2/+lhbf24KyMYFqFRR5+9XfzLFKz9MpOCFRKcemRO68kM+Ex8vRqP/sgo6ampWa8399+sShIHIVNEBKg0ulWRFvCsBPlaWE79/IG5LAS+IVX9HUuiKDN1XR5rvMTAJ9I1Z96/R9t07CGJoaQRSuLgluDk2I2JZcJMgCzDnsjRsPI6imcoz5feUXY51aA1QJCNj/R/rmNmFeBIh2dragr7egkLaPfJF1yPtIrz16K9x8p9NDQxlecmmlpxnoskNm9kOAqo/5+fz23AeIE6Lk6BJNqpSn8hyh+AnTsS+AeARIzZm4MkOX58ojMD7F0sgBAHBFfAup1rfZl2HOVWeBeRO3LaKU9QQhiwrTXApYCZYS1f6SAuiA0NjZgM3ZFzotEKGNnspqZt5BlHrxA4qv4RDw8QZ90N8dQ60MlQSJxpTN+TWZAJC+49l7StWeX+HwdfV3zj1ACMpQtu8J+I3PGPpqejljF+s7rBED4IRM3X4V5tP6TNCprA9nOFenzUo9QkLiVkh36zzWml8Jn5mW0mXHncXBCWt9yxem3qOdzfSiIYPtoIJ5OqkSMKgO0LiWBy3G3jN3InEJPkLhrfpyXaEyNdKCMUjfKpDqGrRYmaeoWr/S90xNICLbayE40kBqEPS6mbpH5IWYYXUoRb3YqeANJ1azsyrWDDNMTxBR6JxEl0UlJYVH25PakcHN2ZA9C7Al7O8LxijEeVZTlbbvoP/NIgQNGD12y+wZO/5rEKJdKf4EUmKcWv3EcvVPl4xcjPhZcW5rXjnE7q9ZZXH/Bb234pUW5if4vONR9SJz7cOh25J+fjLbIjKJxyUxia5gZgaWb79/e3r8pc8O4iB97beTaLnXrasdeKxP07cNeqOvOdP3mLxytdHKBFSV22vJ8QVK36H2W9I3CGliWjKJes7Q0Tc0lo5BhZGIh+D9q/TcMwQgewkM2Vjj1ab9uxM6yaAByj0TjIXqYgSJ5moyCzWDOKes9CUMMZLzzp5WhjVCZdNixuaDhXqRWzrx2C8kJFk/vG/lL94ZDUXIBADDtPMqAoks2qHS4IO2K1GkG2TNXXWMN/ZQqEiLM+8Q27W8MNKPMWvvQgewjy78ymFiy/YYXbZGwFXHoIEhetpVADXJGV56AERHaAiwglUJ9DRyHvRmZGs6BwDb3qwg8L/XSRuF2j/o79pLYNTDB1k6WIVVftvk57OnQNfuBAf4Abxt7xEhAlMnEnsYoMMxH7+SoMoyVWYisp3QrSVatjQYeW5/K+iwZkMB1wNltt7J4yjqtf/WC09w/FHL2dlweWRqKaoAkDthqIJI9SUXO7yz9Q3VeynvsW5MOscxVY5wEWuuF7qjPY+2LyoCJ7NnYu6byRpMv1n+XL/SEh2GkDpcBE0nZcW/tOZn2UTrPUrSLJU/d65UDsS8NCe4rDAAJcgwS5fNxg/qW5tIgkLDMREv7KF2Tyr530nMmElJB95kM5PXr8+lhH9jH1GIG5GRHYE/SviixI7eg5AfK3k7VEmwq9vT0+kGKKLs/OmCybgCkt9GAig4Fji8rie1fUFHlAJMgyaAtpf9YuzI+3n5dhu3bN/u8DjTwbejOhplOnFP9l/2bc5OfIdVW6zYm8q8mFJWOSDe5vhVxjoVShERLHlpr9XAB0iJSt8iOv/80Tn1LEWf8kbt/3BBYkwKfH7FPKKL52nGyADuPYL9kEo7rEl+2DG1Y8cwGIZNhSxfrdzeB1eifZf3T4VSGixm3gsoEaT7MIh5JjloAhiej+di37I5/m/CKJEdqQKfh39GJDPs4M1gH4pCA/8UadVt5ivE1y7qNqTvxzRKPESBoNfp6l1dTzoxm94wOTsvCHgGfQ1+X9t8TeB9O1OIN8qi516hmZ5a3tWcQieXcdNKYQCwVrKLQDBm38qMQgQIQ7fSrNq/70bcNNEYYVNFxbmz3Zg6b5jMqe95To2GwpKki/32viUChgVS9rwY2hqUsmgK+Q7w0MU4qExZQZiRRvV/ZwGt8kQBXtiz3dQmYBiFpe5Ncj87tjecYyS2CZlFiZCFIFlaXtpI7kq8DKSvyUwio6YW5imIVJGfS1QT4h6zG6eAGXm9tWCUCiQLK7kZJos011qTWY5Es/yafAULGcqjU45xrt40sr6vgRmVoqjS0Z03nOHO9pR30FVGliTHeCGL4Ou46o6V4tvtaYRF/qdh9pncKd54CQxcSIIM1mA7Xkn5KHoC9WG53o28D9LPEVWNb41Zp/smluFjQYMxny9CE7Dy1dHdMT3LkXTnWUCI9K++/FC6lRFnBcenORLzv//jn/0Smd3eQbn3kePrvreMFBTZ3WIO/v+9HpRTcgdoip5pdbthTfboBem1k9yg8/dd9Wv66v3DGTC/Hpti/5MgdGbkuKP29WPI65W/9uxgMIxKwQ36rsyWjz6tEbm3aU53293fL4pihXAcgvMVx6qGXy+DZY2LTcx6hmQDMjXz6M2cczYJmictHZR5uhoNRmBY5QsiJWZ6ZqmY2w8fYo6Y+LBq/510kO7QvDfvp6x/K20B7gAyWBTrfvGdyvG8TYu2EZ0aOAobX/IWd9HVX88rSkeZU0672lbhFAVFeNomb5mg+IwlQ2MeZdW9zD4U0p50OZM8CZf9Rnpw6gmFkCZeJQcdI7L7DxV4b28BVOnHh8cEZbW98cge0W392cLvPekWFDezKiQwZJfgAIFCpjeC6g1sdqhJkd5DcBIsbmSvQp+XMAWY2YAPVrndenltZF3+pH8rSGAxiWV3IrHwngPk4LS5gVnu/leRZIJvkRR4JBCQESQKs6H0SXBAQTWkv+eIzNOZRFqlx4V8dB6wsyQwyr0u91Ksi8Tk+DZAZFMhHS7vLQfZTh37M7E+MY2Qz0kyESUmSIHCf5E6660pprYXsTvty+nfeOF8i61y2l5MlSWl7wjCvhs36nkrF/lsWKAQj/a9sYC2oyvKAnA19+f/svduWHMmNJUpmzZz5/5+TPqJ1HrsfZonkLDNgXwCDeSRLpSqSSq2ZrmSEh7tdcNkbgMH3722u8XeOp2DW1hjBbEM5X2SgEc+HlzZvrXbdOZ6O+xEciMdUO4pr4bfclpDc5Pqs7XOq1DNR+G1AibOyyn0J5sNy5up2+C8T7fJsvxx2eiJb5UaNjHWc0kvOpiFhjtJ9KXHjZn6kcpyd78vnv////0ON06Rzs5gSlNPqTl3RTfuN10zbEDwi2Q9VnW6xgo+6uZG4W/BzKezbbmEdNei7E+b1uNdlt3+Zj3u8IQtGEGJK0A+F/GWmbQ4tyEMQHMkq6j4NvA9RipNASCpdA6KBhm6wSZa9d2dLp7cy3uMD+EjPliGXhCp0sBxDemI3fAX40iPWrI32tmoUwW7+DvokQyUHQ+P4g+r/UxSpA3eX8el3Tu5o/4ZzRLFsIMJ2nqqVvPG6F5kcOVTJpO8vDb3Z0iILF2cXH1ePg+zW0/x6KeRErPv48BuPKh5rDDm2TGOX8XCkOAguz1CDV7BtBXXWKgGzAwQuDQA5yEpeVkru8I8CWkfwHQ/roB0+bgOqbGDCkR9RfCN9vWX3vnuNRkfplYAmBgAAeYAD3iO+IWjet6gljCBKuBeBmRNNW19kFgWOYckiFhDrJwK2bLKDcehJDMWi+80p0YoNDRV8/T0r6PUePbsHsBzzVzaxZG0yuBUmPnSJ2QrLZvDXXjpl+1PKyQohEAnahIM9JkEaDNDbmtc9QZkf4VuMk9cPHd1SnuC6RC5wRibJk+1BIRe4P+ZyrEsrNbPAQeyDfOb6CrJWiLxn5bA/jYDhtrXJhAbd76uMUayJe7qeteIZL8+cXM6hV9JimVEjLloCVBtk2RlenN7kHfb7CJhtmVwVIWbbfZ+mrmYpvVtHs/NaJWX14dTD4V6hCXWOJQOU3wfusU51ZvNivfIel2eAVLFZVA4RthoV7bgXJJ4E6Xie7BCUmOVtMlA0V56xT1WOEZjbic9r8wSuZHGSpda23+Hc+YnByulnLdAKr2cJ0vpuPy/bOniN/yBXv/BHygeuVc4TLVn2x3hCbuKv1+JtY423qEd/W2Vlu11SsWAS4qx7rcUPzRA0g+fKB2WItpRIqGV8K0vaSC7yzEmlIvmO0oGATcZvjPanPkI3qAM7AVojQOUaQ5Tx+Jrx6QCbRuUv1n+EoACWAJ6wa5MtcltSwPy0dm1d+n7xOe08kUuNr93N0Jgmyl62PPmN3M2fx1O5x+1eucN81iGHWIvLecxpfgHwE25eoo3n/jjw0SjgjB2IqXZDPmc5VbyFPcbUwLsDAWZDzM7ZOR2PDPfINr4LHc/IvDdX6KSBYFia0oE45uOAxvcBQDAPmQnAmvI5+NZmyr6d0fa6c/p93NTL7WpTB8t88RxNAmysO+Bjy1BsgJsBNieuAuQwwW55k1xYtB7TzpcF1JeGdhJhD/LrY6gCWhxDoiugnZ0nMFiy/9yvPFAGSWSul3Tph9rzdr+8xJ9fshypf/v7knm3LF1uZZcZEdb6zBTdWobnBNO65fm4SbbteXhmyRIV8hO7tf6vy6DmW2XHz1l1EoMMyR5/mzN0J57TSxd13gt2xwkPzq6VjFB2nEOgcj/b1991jxT4tOqxFLL8bs+eSspu/gF7Z8UbJcvUCQfu48/1a04sMJTuWrmcP/9pjP26J9+UKndkZW5kyzEHxuC+dc91cGy9c3Eheus30bK6/y8dKBibgQNeORj9al7PkhhGm1PKJ1KEOfigOtgLx/j50+f9rpVlm7Kr2+rEtSzeb+vsxdm14dXm/czfdwEB+IulstNeybURNXzV7GASvB95nY5IRkbtUPbGYFPKUC18ed/MtjwunVh9En7LCAyaJewSN09NS02fouAOhPy6DuJ7qVrXOY9j3WfzCpb/2PofOKan9LPElUC3zt4JHxxiucLK4K5k8SHi50Z5Ardln4bMQdg9EU8nFD7fMRj0DqLS7fZoZwei1J8NewJ5pW1+2Ug7RtCzPBP5K1H4NEBoEALoFB/3g8A1CsrslXlm+pVSprMvyCWyTmr7k4hQrzuXkrciZyFd6AgWifX8XZre7bOsHAkWogDYBhhpZwzQw/chiwAg7pQB12RLp+FcRtBjliLm6AU27VxJrSS09+0YeWV2A5H0WpqD1e2AdalBlFXJy6hLno/RwK9C52qckEy5lkrVrIcyNnEV/EQtY2yZkpQIXA8d8n/7/nl2f39u4LmQ+SuYb+eouC85rk4uU64PGTJjQyLtc7HfOSmJMce+ovubE2FpifbZNQdZtaKb6Wf3d71pBcuLO4kUcVobtQp5fO09swzx2dmyvB9ITS9f22uRukmChnbftohRKGf+5Qge5nmkFkvtQHr929+L435lLwswCNF7rEN5lw6s0tCFzNek+LfUEezDUzbETCOffXQ7a34BY8R1HizgOBq+OuZlZ3GibiuEu5K5Wh7IdZG5SAudvnPM+8cqQU7W/fly0r3NnpfCATYCZheYVnpGJy4VAMGJJyoaSGvDHYMbiw8q4Lt1gVsHybPECNZkG5j8bAHPndKLMz4weGYL/jP+5DmTtQJvceYkNTOgOWJia4886/Mzr1k67B1eQSNVexkPDahath4A0yHQeOARpKAekIdQOaUg0YdqcJnff/bsAOHt7EX41yx88SiVlRaVdHljUl7ORv38ofV/tgs3pXbST8fuIWhzLL3MCzZpOhh6IyAybd0NaoQhhvN5qifjdM3+5I+guXhy2Vtck6VU8AYAi/Xei0TG3ZzghDn3jJLKmiMjstrC67StW5L7alhJno0RS7TBijlEvHPFy1Q0b492NxJ0WaMpgr9JTILMHniAA92g1rNJBv6B4wsgpe3VqgTAogCWNt0wGdRdlmTFNwS0CHfiXIMBzS5LMV48L+z+/swAtqLeInw+D4B7gMq6r2eZD+dhWACyvx68fDmbvXgw1QA4baoB0FoWhYklAc0l1nwp5SH2uQh8d0hbF2QRKP9WzYL5MqPhvzUZU5ahNZLwObTrHbjzOXZN7nzRS6yv251CwG18ymI0co8MUCpaLwnjM9o61Mxilima2pUSvzaPIt8kLDqPVvUyyWrKEG2n6R+zdTlGIMtKYDJDlPLhzSLW357hhW67zwDcPG20nyYN255hkCdznpn5zFbbOZ4InCKYoltM4UqQBuKYFBzPEk12wLikxx1amMN+afaQ8tCIys0/vZpBXSub5W7caRVceEEq/URWmmFAntQfMkFbvP+2zvSAaSJFN3TK6WQkBDJW1zM4vXzExiIaZx6s1y52IFEZLONp+177/T1oe7Ul9LPOVrwtArTaWyOs8yh7v9SXDjRyg+xkqyRhgRqy+tLs4LjDz7M+aHDBFzKuuVhrNA8t598HKMZsE8BdwVqzQLgPjC1046jPtdV8IjRT1mY2eorQOSDtm+ZT53cMdATy6QRQS9HA71+o/7d1LTbKD9y3SNW4hhZ58v0+MxkPDVl6xsXQsu7znFucxvYkRwfBfig3c3noRPcm45M8THI0ypYFsWjHEdd/mZ0qM2M2wAui3PE6DRMRSfPPbLdrpoErOtB+d0p/upH83g+xe6MFA9MOjsQBBbwFrhrpYZ2sPoc1dgCPbJii2VtKLBPjNjz+JpD1yPJeEpwlydaXJEJCOQSUWCvLlmAfRBS0bnsW7fC7Z6ugFwXgg7AQgGMf6/6xrMuyPwpO1HMzyjJ4BYo1GECTJAD2CUzHErPRRqKfkA2Wx6FZgXAk9g17zjWpnFxlYSZHZW339dpXyjkqD/ZLtVFaCOijzBb1JWOcIBVOTPx5qIog+S8d0aRbIUJGSkCiEfvI+VB+D9IXcxKJjoWhTLSsbJRP6vmpnDzQ32WpvKsIvy1niuq9kOmJaYkIUYaMmO+RU157pkIW16/Dp9+HsioRiHKS81wJ9Qlk5B0PKZbictbGfQe1uwQg7nP331o8LD5OknKQJZ3SmB59fHabpn9OHzyXt3E8Z8LoHYs4LpAZy+7Yu+N+BIleRpF97HfK6tvnKHX7kq902MLorWgLr33XQv7cF3mULkuuMkW6IqXQGRg2vOD1ZRuMH3xRth3Y57zC9L295ctHqalNEPOfN7Dp0w0JOqMv5ZrpbEMD3ec970D4KXLyVOr2R2zTuCY/kv7bJF/t3xQ1+941Kg0eWqboRkbhgG827omASWSrzN32/dUabOk1+fye6zsF2YAAOfQi83El14r2+nwandCLMfX5VkLh51mAAxqwJSiWInpr4O6UMS8dMMdsdY7AMxX61vSYB7edSIF4eNYhCQnMUit18XXuIDd7p5Qszb7ehG5IaO4neWlbzNPiYhYn5NmLPcyUQyuz4lp5Ki5/7zPvYxKgreeR4n46a7QCm/pfEgpUa2MeKVpYnxhqXkthaaA8B1Ai+zmH0tSAZY2SX4F3ZPrCK+Bgfc1QdABvzQpaZhBr4vI0dV/z58e1udLDvpzEocY/8SztVT1DtJ+VQhX3aiV4BviLHkwZo0Ik00dzHyxrSnsQ3TW98QLGyXm1DnuU7QbIdX1Ik8uff1dL8PSOnijPo8E7OsdRRgf/uD5apVnYS0iSEKnJlpfM7dL8CFLjtv5fq18prmw/r2EO6JUCHHE2kmckm6EYppG7j6xwyHzHJ/yd3eC7sj/5u+m+2DRksOK18/ni34msDaRw3351bzuyLY1UxmyDVU7ArzslbupRC6m9cQWnc+/XX6OBcqx70tmdbKnjHkvuuirbsqMXD3R8L9z5+a/fFMhKFWPPkNx3v5KsgSL+k819e58Mv6yzXquFOaIJ5hTCKU5SeM63g91JiW8Zm3AT87NqhjR0zHWxG0cf7asIfID8mivan3jRbHFMBi1+Iv3v8PFd0urG0OwawNKtlK2sf3Mqj+S0neE5suYJBgZakGZXdtczBNhPycx7aMy8Qi6L0xWSRZWzeQbjPU++XfO9n/v4ClAkgNJoEfphlJ2ZmfCuU4CNwMKymXiOyIEDotOv1bIfAS3pZY0i4w4bZFr5IQAnDuM7QPN1i4x9Ky1zMlDCNRkCsEwKbSFAbA4oztxUS8Ra/vwKeOmYM4lHe6fN5aB+zfokwcpxxLqYRWtB7v09F1HZDpbAGaiL6yqg7WAeKDMxZ5YxAvSiREqAlGtgwumyqZbQMVAsq5eRcQzD2R0Abq1BO4he9hrZtarJkSFxmR8IDLM0eUTA2CrIlZdbIrun+adeYd/6OR6SKIDnWCXfE+hAyDzWq98XZE+twpwQFbtAstWzR/CP6h6osRgJS1+q8reUH1urqr95oCJZDebgp0ee/FSLH1S9pgjagftyMxMEsSpeAdsmnTbb1UvVio8za9NeA1Eerwe0UeWLUiF/tw5x9oJcuul8tMHXZpE8Aue50FoWjQGxkQGc3t70lGD/LD6H/TvBYhgsBIPgYv61mnUa8YGxScj2aZVoWf3PFGKz0ez0cw75Se5+se/S9Wekbpu6r0kIdtYHxMBb8mrDezeMH3VxQv6S9USHC7M+Ij9WIXCdikehHQRP0WkqE0qkWHtfX3rZwS71zIzLBACPoAKIC0ALlfOczo2g6ZBzuhsjQz+T/qNBBYDiCWJ1dmvabIdz27wZQZnIieGqRnvOLApILO/zUC7pwaS9nf3clZ91cBg6jLfIrtnzcNV3sn/a++HdPWnnCVPauPYzHoJXN3JVM1C1NMn3zSP48Dk4MO664xHbmLMfigUauB+8L/sM8EL/F986kSqkyM89HGC7BzfUytbPFwB8kbTlfchBPFptC+QEOIhAjiyEO0uolOlxUrUtZ8uM+NoHwIXw4SyQEDrWoC1TEoYErdtmxU1Y5mVrSfwxnHvxLLsHpjcYz2oPZj36GZwcFOeLDIEh0grcbYzpCkcgbUgMYB1viFh3YHOD1EM0xYi9STkycl3WpenRWaLXSEsvfUMmyWXHbIcTrjoWl29kNzOb5TiwENEkqbnOkiOQ+7aencSCWLAcMgbqL8JNT9XOsqEZBpofxHPQnr3sWa5DNHMN+WVJnSI48XlZJ+BKZF5AkhRy8xLB0JHs0mFZyYGDUL2EnePBOldnVWyt2W73T9GW+kQQ0McRC+RZYAsZ3DFR1qKh/fW1bTPkesg4uV31+Gt/6C2IaNs0FyXhDFBm5qCevvZH9zZEkGF0aeMuS0HQcMnK9O/pkEHOCBAVawEAmCLx14hkGq9FgaLJwVrx3/K40loJK+J8kr7rlv/8X6TfC3O/DwfH7nxePZ5BdN3Ab0aPZgA/7qJBqQHkQ4aV9o80brzVaXvHzP3uA3KQm5dlP82YtAwBpGNfZeDWf7Xjqxnl7Jmaiez4Pas+Kgp6Mw6CdYkxGkS/Pe+n1f9cLJVVwXGfRIaNVcw4v0e7RZAqceg2DTbT98zt6JPN9GdAVs6x4WXMPe8TUSe+3LH90GWuk70ivwNxOZxSOvZ+hswDVfsZhk4D607ZlSqNt8qB+HmSBA/udXBIz+oRdcnDtJ4ArP07YGKUhah0KeAUv+/EiC+4lB0KDKoLHfjue6Uh48FkgObMSjiA47N7qZfZmFqKW0mGLRHnQZvRzoU50JBM4uwK/ovynzyUDd0C2ciFAnD1HLQD7o0V/RUDhRzElRWQxgO2JnAC8aoCkjTbVBAHJ89YS0tqZPlS3LBmNqRzU3cwnPnh/ZPQ4R71RZ2EoySnyJxgMpRLly/rduU2ossTPDfmwHUejJ3k34hxCXQ7eUEWJIkJSJthOWZd8qF49i5WLGfGRSBiHy3DgjljHOVlpClB1ogjPrFzbfb73EgGP/qe97XBesyfV4KFcz+hvy1rZPI/gXzYj7CNsTHy6ZOXts8IfLShvF9e5iYHc0ap93qOZ4jxuYuHJBQZlNC18mgc/bM57O9hz2xNut/pqNLvi3XAnNJ90JCDV7IluAXgdN9zDT///R//7UGTtui5Cfm057M2HAtTT1gDCI47sz6UXnHiAapBRzNYpO5ZW5FwcG6/mwXvu4s31ey2h2YXp3v+2p9pF/gS12yvTKD0bZ1/+ZI1tG+bG+zyMOjjj9zhjVq2Bh2d6tZrnFAYtFuh2kH9xAalVhb7/0RAACQmoNvl/UmeOuk5dUWxl9tZkmsAwB5cSVcarldnKKay1GupqdkIA6KStnMe0zr9IfrfQLGM/BnHCqN4Zj1enf25EZayzj0rwwqM525/fa/SGJduadUpaqM9wIe1pzxbJQR1uZW5eObGicwYHbyUYb6yny9c+OPP59++KlEVIIcsiLTIX4UkZM19c5wOJDspkOR7eC5GSrBEj+3Zq5ol4thsBUCA7tkHwQdmnPKpLOkqpLM+P55ZM10+jsPXM8OX4DejzuU9P1FzkTUlaXvtXTDrE57QYQxSqJhya/JaQDIzVUrj+P5gqb2kkVkXO1MBHRDBSDKai4510Xo0QmeAcvmR6BIrG+Plf0ycDcQVwFot7qudAiBl1oIDtywRibcX+tr5IpCI9OFx7qgC82InCdJ7d0CdFTaJHwEAACAASURBVMOaq3RQEr/X1AgaMiAoJ3Tb1Lv/xXokkUo58jNl5aWqTo5Alspvkzy1VvLa+3oW0Eka9SkzTZJLW7c8Tw5xx7kbBFFBvmgHJutmTlLYWBlp7YsErkOwPR9jDswY2aZCL3DdU7k9bD8CC8d5SmTAmC3zV86ek3S7raDOeZ2P20ymXjBfWMXD74cGCEEo9QqbaFltZTle2hILIMdSHCAi3YOTwENuJIn3aVHJ0bEdAEJdaMKsxq/WuZ7F+FY8/+1zmNYdMNq7F+/xQYL21btpXjnvn/V7kRe0H1d9/m5Pu7rfLQu+lyyOicV7vEMKaM32F7zbX74cMZIsxdlvJQ3j+bajfei2lcNEinhwoH0inVQQ9CRIQvS3R7tDx86I9i0r4/W1fhan6xHLeVABbaVNAGAV5g1b08qmfKw9Xf4z6f+WgG0K5FZFTK3M1ub/VOr1WqjD7nQS9EyYn+9a93fuEtdl6Ea27rI2j+E27i4T4eRjjY+xWIb1CA40H3MGvebzNU8rVjKnpXxNv3IH6vcSGI1PRS7s302/APKmtXIAGNYxrRJuPJxBgRXFmtb7WumSIUXRKWi6yAtF27M/ZqoLyM/FQHR634X8Q7bdCZRH6HEFzqqEQGjtCngdzk4VwC/eUzIe2C9vs6yyXKeWWYJlBtrB9X5W+izKg5EByjRK9LEWmM9BHrV4IAD9Hsg64PMAp4qSo7SsZydiu2xvTYBZuscywyTY+/3bKG2Kvwl035ZPj9VQx7M88+M+6mwExsP6fc2C6CHCHM8KebCyMiOQCC65Lvr7mbwUFfu2v8+qiLKX+TnmFARO12L9ODzsM9Yg5dTHQllkVrGXXiKzgyyYZZMaeY97CR0h23Y+w6xRCAefXmyeVPEwhZAp93nFxpnuGy+a71M+1VhqouKscGC2k0cL5BMd10xzooyannmlCX9jXQBpE+yzEqj0RhAlqJm6spaapMcs/0SCvH7vKM3xyHErP8AgK5iby0PCcDbta2VEd2KUKe0t3NGZIgyKSrc2mzTjnCZa1voQh1/0g7VHu/4zF2SniL9FxCrX7ivet5Hlb6itRUvMJyX681cNVi+2cu981sXCoMbZrujot830KunLMz+u4tvlNEJEe2SgLuZviuSlaynHkLdSV79zsWHGpWcFq+ddT0kf9bIt9gGC02HfMlMcx8+u/7kODsjN5guol/IfWaczF4T9wV0gVLl3/T4WBDpLvoZM0xBoKjIxu0Du9r9C2B7P9SRy7aXFEyF+apn6ZAPGM2pW8jJlNov8oh093YVg0b4O72fzgJz5KFy994mfKxMCnAEAA2bL3/WAj0XPtUEenKsAHaoGYgSd9QyDr58Da3f6/Hv/IaALPwrCou5sObNy3sV22swoprgBnLfltqACgC7BeCGPQiXMtudvC4i16D5Kkk3jYmb5A2DDQrT83Izd3/fYC+eZeRjO/CiTEDMrRTz2YlkvVYvxaUbea077LPgX5CSzEPhZjqWOGSGVtB1Wrp0hSLa4BmF1OQlAaVkdxCyxlil0m1AaQZeM55jbnlEnnNjns/Zv91zkF0HumElJv9mJDsbbZb0+r2aUXE583CAdlJujcYYaPfBFwsCdzR647Lk8eJbK5dm77bmcHntTMlWhyYddHgKU0O2jRJvn6UWgHgNfw71LlghpIcp2wyNQxiXP+5iAr4Ks13uDb7gOtpeeeb9vM4lUdtRnGSsRWMwZfvfJ9+x19kyPL3wBTwbu+g2fynRYI1gigJcoOJSBhQEpCLY5sc7PEcGlcN8+f/309gVnNz5/+vb29dNb1n4CwM5b9Gq5fqXvY4fX//2SRHFnyzZHDDCw/n9kz7I+ExK59wTm/cehP11hHIB8/vZ1ZwP3/qcQTLLryjY1MJgkABkhOG1fkUnOCoAcwG1Z0Zbp5PgeDqSPBjR/WExXA+q/gv4fJbQmFAch9Nry/LJTzf4bN+4RQKlnGOreWpa87bNHtG62qEfZwkSeL7V1MtxBmOS5EusYuiKXU7TQyYfXgbKQho5mnufVZ+e6HVUFQ9mldKvTMHOslyyP6385s2iH6AM41ai1Doc4WXUgZzFMJ1OIth/vuQuNFmiKHS+Ajhkh828sKZOEOLhjqZ4JqV/ZzzfoWCvAMEZR56mSJCs3akIK0IrgjxMfjGuXmVlZXCEfeVGAQXVdJQDP3/qZA5EC7f0ERDmr4501Z/DBz/64rpMwtmoXJ4PrOZtQJgDUGgzlY575yKAjbUcafIB16HCQiJQdko/MAg3ND4I4xNoQZKf9Q5avyE8uY8hhygT/jpv4tsf9ldmJtbCsx3S/pgsal90nq3Lct0K+jueXZhvWiMB+7HsXa5G6m8HdkYSDiDqQNqIokiOHUsrzQFy5/iHVIKQor0IOiPUz31aVUpLFXaSibGD44/o/rAdltXVBgww4Z4m/Jd37cfbMgER1H/ncCVThWnt2IUxtzP7Pgt/zPrfW2/02GAploj3f1KX8FD5ky9T6DV9O2p6wCUu+yMnBYScdWRV1sNT93pd1ZgSCs1Y+36bqg+f3E9O9fWaVVuf6rs1bLy0N0B5p0JTIt8w37UPt+f6WHwezP4jKv+erEjHaFiw6uq0St7Usa+3Crr19+m3t31rGvafx9166SSn+PcN9edcylK3j2NwgaMj+hHJUZ1JvXqFvRI91IKyAQayRP+7lSLOvxih76yB6rDP+97ui+nu4uWd0gxpY0cshSvPz6v87Ft8uof16CKbwmpQn7M22cRswD1YoG2bE0rrrNtnbX8V3ngmv5Y4JPpbtKkDnO+bZvMWW5zKmJu+oi8Z/L+Qo1qX/FnY//4uSobzslkHyEoV3yfvV7jQG8A77XoBV2S+URcZau0Z62VePcrpTzs21SKgTIJGKEAVNCvcguejg0uZfy3s0Yb8H7N8mHgDQ9gZ4e3SJ2joQd3PPe+e48dToOhhXYin3fxltR+nWCbLWddO6+rrjzuXeetpRpogxRyBvnTmqQQMQFfrCphk+T9RR1axF37fYmD6PshcHUXBwrPIokk8jH7AWXT76M7FOvYvaeJ4HEAkEdGM3yHtrCHEhW+5CuMZZXSHZBtGvck6y2M95lTK/mDFCrWcGJnWUpX81yAPZIuFJ0+ukmdeYzag6lIQE5WvZoA3zqzohxkK5thew7rkYU3ACJQvmf9VyVxKZfY+GXlK+nIisMeyGTtaDncToQq720xupgq+iDly+Z0Zz+N7dEWZYpmBkE00KsLYYEyt66DvjCkpWT5bAEC+bh5eTAsjSSdOKtbRbfl7IijE/bEB14tiU9zmko/yhl1zgEFVueODDcLKc+dKO34BEV0vLAOvLKOdxy+Jk9MN3eMnvwBs/y6VU+K/fPn3JSMAij7+9/fbp7e3zzgZFRODt0+c8M7WjUDyX9WOclGLpAKMZqz33ymDF+R7wXxkNldMKwLoqIgoZxmXKNFIXXDN94w2gFHmwzw99WY/LtDH1wa0FDBXKNJq+7nnynJPKGBdyLjWwOaBfSv/33rdOeQSVsXC3YE1wGLMlhnZFFrSCIMT8LnrnYlUVLkW5ZYZPIX+3/EWxgayXlvQgOt7tC0vr/HkMHiHgp/p/lhQ1TxTi5BkcI0o8xBqLs8eKiLi1VeXcmpzvz22d915luI9ZpAQ8CQdHmXUHrCBbki38HmDbVKDqYJ6dcrBr4/XqDgEtnnIkwBUw9z0C8FLGA/PxteXftgfbHud2edvjUrLUNl8ATVXiFbQNmQ6LASPjtX9DVyowgcexuRpAqpW/ibjlDSxgxDVycLv1RatC8H5kaVxT/HrNifuTuMAzXagpE8BODWJpWc4zF933s6y/Z0VyobBentm6kR6sIYmqkV0HktgCHwdIUJlXaiD8N+/vWQYj7JSHvBAEuJ/N4pzIYbX+5R7MYKn0bI+de6zsEHwv5ct8F6LwAfX0Ik//jQcXoB94Vvg7mFtlWep7lqq1XZUfJD3pP5kR84YMtmnHc1tIC98zsIB1tvbY0HcFK4Q+e9nfNpX21gUQlVuwed2T3cwkDIUYmXmjBWHQQ2pbvsNzPVjcsZATpKeKLOwT54InoWpiyF51VO6hNvI+v8iIU896ff77P/7H+quE5OxBDZFmALM9Rkh+OpVSipGRxAOswfnaCgNgRNQUaDtWgb8n0dI1cLTI2jEbtfwnOjXgnntSOMeR2QqQIJeqjiB+6X+H6Mf/XVqVjR7Wfn759un//vPrXrL/8/+9ffr25fOnf379sq/+7bf//entt8zgkUFYvOQvfKmP3HNIasgWZplGGWWOu7eFAE/ItAPWI0Avw9GtBg2FDkBSKQvYBdq2bj9+r8kapSYUuOLjJABNMkOnXcEoSBB1NwfoUf/I7vz8+h+2IWzJlDUQScUimGfaH0kOWLoCy2ooRERnslVwZCZTT3ud33kki2bYMjNO1sJ0WRg4AYNnChHB5mdmt+E4QRhgc8O8x4WezeJ1dg+SDZaYmQ4cGSVD8gxQiiVVWGud7kgU494KEMRIPSu5ydT03NxijjcBVyWX2qucPVcC+8Ltt8yFYuJhdVjSYmIFQECHj3icobaehRDgky5HZk0h2AIEtwz1Bgiy80yDMALvzQuqszs7ZYm4eaYIUSR4AHkCBZk43g6+N/JohMewhalayUuyFCuzDmsP1T7azwBJUK+ZF5v2Bqz5E8gZSVJe52d24nqNsqg3MyKxX9jb3CKSAjzeu+Dhjp5lw733M2GkEdGnnOFZirWEqISyhU2sLaKx3AD7GE/NpCgVgHHGesPZpOZa9QTnwDXNvfbyONMPqkHeVTJWy4eV6Ykn+J5B9n0upZQN5DrhINcy0YKX1Bmi0frlA1lAkmuLuaKoaA1qNzAYygM1Xsh+7E8065AwQrbwvjbXBdfUKRNTTPyUabHzPxNcc1l7SgGs64i1TXdKFgYCnxp83NsyMiBKJ3FzimOBZ1c4mkQPK1Wbhn99/vt/ZctqO/jtlz4xu+0Uh7KQeOwAIC3Cun9reTkuRu6816kDwCzJiDUyEajrUWeJmnOLNi2X8HVF/n9LOLA7n7B0dl6lX/5Tr70OMd9d3DKzsdb9//7zy2588L//129739bfsQ3revTshBEc8qV/1hrmEKhLVAyFS3a2L2tp1fAiBzhkAVTedE4idLoBNIBFzzzq9gImDtCaVXPSBOtOsJcDOouU+ic2XtzQ9UUcLPZ66LL0c+t/eEVN2Tv5+Ys3Y53CmT2854ZWUx4K/Kg7JV9uAoliWHNsY1nd3LmNP282r7mE+bSXke+dWdnzrVnMPfteOmxZdc5psvlWxkaAzzMuSidENiFnYhksgsz9NUBUXFfKCLvXnFzB4BMisJP7bGjkyHo2HwXh2Y8hxoORaeAS0zKZ63Lhwwc4Q5MV+bXTngAAB/kSPQyCl/NKv+jPpKu0iD0WopAsZjykLzTvBbgqmLTHOwC7WCplw2Lt6kr0MdZ10fr2pgXbN0nADMRLubBX5RkYg7WuLgTVMgVrz/1FsSoxTJCd9+ILhI18gICh4mB7R+6LCFDIUj2fvD7a/58NNkSnnQC5PfGMQp8vKhswdSd+fuBehDX2ieAdJJVyDzvJkIhKJY3Qup7EmM5zP1AnEdiahfG1wLWcR5MvygSIJnTCMpFHBqmVl4nwpCCYPsRzPdMVgBFyjZLOHrToBJn38SxSJgCwB1ODBD77UoK9xoFzMU6CaLNC1MZSNcgMyY1VmEDO9jGRnckXRnCyhOd45ubIAhHvd4voTrGP8UJ2hsOiPahk5h5GX5Uzf/uv/15UolayDuxLyMFybZ6N4QpB+s1xt0H6GaFQiTPtnu6ufpP3KbitCEJFdADmiWWy/j67duX5lT1zlFqUeFLdjF/+X1vYnQDuAzyrzUGcjUpiiDKWsGURUd+toenYwLQZY/4Tlw512wBOacD2BmdkJQnPVuE9JzFeOBBX2Kc07X1iFUqF/ClKfT7HYLkTKODDQDrmJLvGZIMGRPVerPhxLu8X1X9YlVd76KRVzStsjS+EUCZPpXTP9uxBYhChT59w7lH+9ko4ksTlwWp3LU/zB6kNYGHF6tTnZ2F6IkIh9uqqM7k7gnYLdlVyIU8gm1O7ru0QW1sXPfcMBCADvPYauglA4mPm31gbLEUrOSHpgP9IQMvLDcTGOpM/WaQaBhY+OpCmbIXePC8iFk9WW36zEVz3ejA6fpB7XcrSz3a8Nl15xgw6xuh0xkGZhBy/bTZBtREfJzm8uZlrymNsOjMjbvl6xsfJnNY+AEaso0iHg3GAOtddXN4JlM+lR/zRdEDnaAzId+IA/dpzrtkMfzb/zvJRtqMmSo3fr1J0J6CSyVy/XBDnnp4dw29BurR9kAthNNwjAhhDG+cBz4k0OAHCuxWDQJTGAPB3nXBoG1Mm7IyW2QCRpqZPaVxE+izjYv4z1iHtunH2g2SCLOd+ct3TBqzrby+JdVuAAAis3UiC8NLgrNBBFtJEoRhr4uxm1mFHa3MX2aSrxR/IRreXTrowv02W3D7beCBLXj7HIANk1mLod66AfVZMDbbOcisx9V3eZpowAQA6Rn9TM8iKOauSgckB35x3yIQbdSl+f15fWI8++3fYZLWvi77xFIresoKGJ9pamz69QwIm0Th481V+frgv9uSD+ezDbrsZxGpsgPKwdZYnokDrv+tk1DYcK8mzzk4hSoxF3AqCdyP9+bPtpZExPpOHrT0gZrndTR5B8GoMyuR2nWdiNCzPSbCsITOZKP8sZAbXKmKECC5CACRKLULqILWEbghk5HGoXxdj1fVq0iXoaRiQ1JGMcmnMc8Z3vL8bsn+3/pfsA9hE7kshkXA1t+CL5DdAupeUtbqElvmLZTvL7Po+oszHNWUiK8U+N5JLAG8yeNO8p/u4U7wRJicruuZ0Sb3sOWToTtLvc4YLpWOx7EENGjiop2Nn6U3eJ4msH07v/uim//sOZHta4QJ4VCBIveHZlW13DER6dB8+yQ5xV2dUD5Z7RgXXKTOhLmJ9Z5g5sC8AolSyphCx5mZBIitZgQahlOoe3TfyYWkoZjdwz3zgMaa025AG41aqAEm9j2uAsHTOBDtWyFhRYwF9RLiJIjwLBGA7ND9QtkZZEbehIn3+feymA2uNVRUuIGIZt8NtS8avkBauaQfxcZWIVZVJ5Ot6BrI8Nzdi71NGl0GcYPsSRJRmHtpXkHAnVbEmjsh4T/oMk+3cO7zIllkYkhXcL59B842XztoZJPMLToYrAYrdK/NEgsgOi6DEDetVCGfKpZNT18m4FsFkNHAQEpkyoFUXovSsY5fYcLEIz5Ds6xvekJw2MgE/nuag220+wp71FHyLy8yOD/UKjFtYZdb6XbxRMl98PFVt+ftA198r0wNwIuCvA163ghnaHlr/071O0TcpQprJhhniN7EzTm6O2PYl1Xe43Twku9lmitEG7jFrnV3KH4aT9sTkyaNtH1Nj7fRupU43zPHDfo64R7zsNefORV1qHE0VM00S3h9d/hLMUNFQjpgL/ue0Ogjitsf4NaX3bUXBVre+deArLF7MDJCsgqYSBBiiVn3zXkUfUtIkGe+U3XN0Z+S8kyNhsaq5jPokKFdGA3poQQcanOlEDOCNXnB7zdMO0fc/Xf8bCSlZNsuKxLrVsrJjX2EjcOAyBWEK0sCmdidyNfwOwo1YOTkJEF6zBCzbckLq4TN7aeER3ErHW+zsUyYpJlVI3EhSoCBZ41Ng38P9Dz3J502yirXoJUL0ZUYIYcEdFOBZ6Wmo5VOQrv8O0yuuj2cmhAY2gB3OIAW4SQu0s+g1OBTuD5BPOljAbhuUwBPZAjPxKidUVHs/0/Yn7EOeRaCnzFVCU4HefjiFsYNTrk/KITICJRPB79ya5guGrVOmJWjUeQpLvNdJ5X2xl/qfZxfiOwP5TjrxkJbFQoYovR0VT/sD31c74pF0Q34TfG+gb+cpcvh7wP6SRwfCXEuJVclceZcsZttybi5HdW2UOdOqOIlQOdO6B8oJ13artDB+KeAf8owW4P48zsfTACg9e9iTQgh8X/PmWE/PMu7xrI6qScDws50ha2Cf8mDyTuKRmgs91ByQ1T+DFtxPquAahwyDnudnx+I+yFr256Fle2SLQM5iIlu+DT9jevu/zc5WzBqrwusHorAJX3Zbdg0tfyPo24hSWI2ZOfTP+7jc3+255wX+CJSOpnW6JiumOe/fRHmbNtBBlGdOuiF7Ym1amIzo9fcW5GLFJlptvUXDscNw83JczxCzj2vB33+uEqxPX3dN37Kn8X5IUCB0eoonbfeTDyNAsk5CrsxoA5AiyGnv1piDIFyF54f7YtG+EM84v4OIWTjGvXToAsYotp2Z+fT26bdPX7mWkb7/897rA2EPYg6rIIe/54R3LAylnCXiKzh0nidr+zZJpkf0J1nHLah3sGwXwzEFEuDcJjGadPvQ5V9Y/ztpuK2RwHPSHzuU+6Se2DfYMr+Wzx6Is//Of9tJ7Dzek9CSBERqttgfEHxF/Rq5e0HCnRS5o+nOrcu/iDYoZSVslP0hC3qzn9N+3kjNbd/KvvyB+r/3EYMpoLpAjIwSi9R0MEYf44fhkTEuYGIGFnA9IFzHIflG2mnTbZgCf4j3tzImEp5YZWYLPPsT6Cu3Ic+K2KYcBMUA4n4qLqA/zmclgUGeHkHSiKjby8iN+ILgATyiABosCaCzkse5dEsmOoEoZ1iseZY1CdBSplPefd9lA4AqhMlK2Vc+PJ6cJBFyAt/s5/JILrBPGnNkL7LhhOUEeO9GTCdwTl/SSGRpUGG21O8hoI99HYA91tYyKCJqIAxVLrCWvYkB1jteY6JsB8oRSZJBmHZpmuS7kiTLHJp8MoNV9kCZJuq2r1d+mC/WYCkn1vYkmmc2UARQ5PVm/7hnVj7vZ9HcWk0xfATaXJ77mT1/9lrt8i4es7nOg/t49+8uvokZqimzkxNA91vPZu1iJjYy8NSSncJzBgqnNkf3ZIBvhMijzZqgQIZHMvv3W0b7AuShq30tGWHNEDEy+fm3T99WB7JdsvU133mxt3r1JMuD+UiFy9oW0Ij6zR2J8m1Mqd9EICPGMCJ/XYXXK5m/fI8oQhhE+vCk3HsPVpMD1Lvuy3LxwRpXU3B/IWzGhMS8DRn8gZkxkU9LlFJoV9Qgm5Xv8WY3v2AMYQMZxc6IdljP8h2uw1cT7HgZEMh0a944I+cWRS+gRBJI2WqgFhupzJDp4gC4/XqP9Pk6uD7hFOTPqP+eOebeJUi4EhYLESECPpYJ5EK+x969konje4ui3bMPLag0nJ1cUc9l/G/ZEsjgzWldiVsbX+hDPVtDeUHGYyCSuv9M5DC+YjGszqESrRcNIEpR2Wn+/mX9N/vhgJ7lQE5imHnwsu7wHR4vQ0CDXdscyLcpELD7w2ErYK89C5i/J6gGFjzORtC5hqVs+1jJCxBHzgOAMA0Kyt+CVKnZgaxwDIokRAyO9TVO6lhGl0A19FRBx2NsxXZK5nzt9rgY6QbATIJh60+il38o44UAW+uO5+c/8j5lfNjP/JAAOr2wMle5Rs22k1CAGHsZVyjSsa7eHUxnw4IQbLnYW19/h3FsfeF6TGuZHhlNj9p9uOeFXLkdGLIZhpIxjnMfLOiT79IJaGKG3V6I6jKJrFHJCrb38ZzvOpKsx3veM8NTiE2sJWREe1uJG9aE19nakLSZgUA2GVlUybHl13MvS9YElQUlN4rM9Gkb68rp+/K5Kkmlq3nsQUCyBr7kIyTTBXNb8Je64kQn/2YyA/apH1dxfsPytgs4qkRFTmWOuNmiwQE7q0vFI7jKtSsOwhJjPUoO0HKUaRy5L91x28Ll9Pc5lVXxFIfdt2DihWWpSEyUrzG3l1GmaKbRf2PaLd7InHBzA4H1nAWus3wA6PgPBPenSP7xnwSbD9WMrFi862aXE+TayGXFvq+s2srvrPM+oYR+LihPVyXfoCj88UOPOwqhc7xLnvZI4VSy/E4xtZqWncrL+nCdCMBZP2UXprSvX0+H30i+A98JrJfvD53z5bBzdC3DU/Uwl7ERsPjnmZ2NJf/x9H+yGSEfATb3/nm0/4EkXu81yHC3XZMtw8+OTMqlBGxfb0AG44nPKy3z+uhxXy/Zp7sM+PsizkBA2f+euXmxplBXRN9H39JLJR/G338PffHI5ArUhMNuZMv+/Xv1v4pDO0fY3tcRW4pVt5p6O5fi95tAJr8HN1EQW5BmdyllXa9K2wxgM/uRsR7EPuEnjzI8llTWch2MxzMZngnqMuaEo8qyZy1tnzKKDtsb+ypa7JktfBw+TGTKu5fhfAb8gPMs/EYv4RVJLeMuZUZ1P5M3RFe0FHYGk538IKBo9TuFFAEAu2+zsq19LWQg/+Huts5LlQ/Qi8TrONJl5NZlFFk9yyiUzmbuTVs5VpF1nYvzMbr/w3TSvBXCoD2rZ52cLHe5KmvRggc+dzzXyTUzV9QXPwslDV7HAij/abDBHRn8KGQINj1ujHF4KRxku2f8ZAtyzVMv9vOMHCJTNGfPa9gIPqUnIG5lafGY8x7VBgp/+Np4IAs224at8ru00zskdMn8uM3BcKZ23BsDgvT0Qfrk8bcEssXXEPlJ6XT2NjlRPOvVUk0TfLr3UyR6reDmOuvdLG8r1f5tH+/4ut50TowcMH6XdpkD2Yu9gd5S1LcNorXPceFOq799+/TbPj7y9VN0fF7ZINz8aSWm1f+rP0tyuJoXfF2xsxU9+5IAcbX8ztrZhA2sDdx8cq/GXpNFgt7Q15fRBRgqF/s/Yr5yfvxrczfEeewM196O8/nXqH0DWa+UbwS51TeHaJgSH+CsLUkNQFRD0r+j2MVDYqbmCOP724m9FFsfW4nu/nz6T6JIUFBbVhseqbEvI319r+JWSfReGONDum/10D1b8w5ygoiruvFoTOFJDVZ/7zhTRmX74eQmolWjeD7nd2V0pmw+nt9LpLtuypVcpgAAIABJREFUDLI8BiReEP1/l/47IN1raRmHLUM52JotEIglyDPQVbKx+9IWI8WaNZnaz45T33dg6xHW2ljOovtlh63Nsz4XMEvSgU6fikq1tsenkZyi3l2fsL4eSd/repRFVfLjJVZevoTov/HRUnbEcqKin2qA4FjJo/o8iwFYYOeIEIiJLc5xIkhXTG6s0VH2NoFpylUSFyNFgNlT8wQRhrxBknAPraiELeediLUTD8aky1hC/g7C4+tp73Gi+0pCHr+T1Y5n1Pshmxjf5FlEy05RNlv53dqHfZ4FJCSMKOpBynuP/B5V1uOIQM3uGEFL6AGSE3F5YMmYlzcrwDpN2b408WcWqZybqmVvjkYHWHK4q6rphhvSjzErSH/h1mhG+lzfHAwrYROzTNUHN84AX8x9gs3aBT21HDDO9LyIonna3xermNgXB1S7AyxOdHDsVPtmsCH8DhDPsh6NLMBgLAVrgPcH66C+0tC7BGR3fYzWj2uhnLWvKNdbntWJVtiR10CqP2Elndn+dpXRgVJ5NupRpH6kL9duo4wvtTTf3bNbZG7NXB/EPHeKcZfSxGexum+fvn7hQapYw+1sGbJ2z/7HTR6aTFHIPctsFduZW4QOMucy7nJYSz2RF+xXawokxkPZC0G4RZxcVuMuKF9ykwtgeTsoKODZy1Dq4lYdKdnTC8HrBudn03+s5y1T3OfjDmvvdstgTMJ6W6P4HBYofkmXbc0Bctvj+7QZLitnRqdGVlFv7b8PH1I783wPgUPWqI/X9eXIrgylbF3+fE0P/9DAamqDQY+U89RfkP6psUHfp7RkwRmYURHU/Hfpf+xJjbUQuHF/SNUyRmEwE6Axy2M3qMJNcU4kyUrk1e1eCNr5GHKiAqkK0GE19tNtzColYrIo5NTAtO8BfkoQbTHA8AMZ3XaFaPanALSWVVj7B/BfA5WhP57dYDQ5BcJXyG37fp6tZyE+OU7OZyDjfp4FWahCFnytOJZYmAKc6ZsSp5RnaVP4m3ao3cu+EBghEQjhT1JY7wXboTnCXgFJsiZGQNvLsDCnsZzTs4LKNq2f6LxVlYmQE0zOSuryI2E7rJP8ZaxNjt8CCxRZlq7FGuzrU6dK6R/tcegcs6YDsYh1rTb+RmCO7A+JI+aw7hU4E/f0NltOlGpJaOADH4d0WQTIdesM+NTX07gdpV5npgeEZc4mzd1dkdUZ+Hw12ShhWzuUcrurNIzMMIA78IXD/kemp7pqjxx66cWN/PjvPeKsSHJSglZPW911DK0/Dwo4Zn2aY/XI9Y0R5kOSCO3qXx5WW2KymxCk+KMeHkExRMb2dbv9cQL+zCRsgL9/m+Vzn7+u00Ir1/Hp8yIB1X/0vfix/r0XIchhZLiQLYj1WudjFrEJTrSyX6uULUvivq7v1lpGqVt0AYm1RYnEzr5k1A8g5N+xALGXSXyz+cJ6HrreTM+M8Zyk4gwODNcYhevgKe5qYBfXTgAxyTrKyaAbiGhV0KoykDHD1CZ5Znj+s/TfbYzsQe4Wy97sXNQDgJ/IhGfU9sp6KV3uxaN9GoSy27YO7kF2HLyl5DNKWYn22bBFjwVFEzB6Irwh1/byz7KUgM+5njjPlhHXfd/L+3Ik83mP/A2Yg5da7rEDZPVs2RGQe179P0X/LYMYQ8/aFJ7DN6ZhQGw6T6Ougwa0GDXOhemBFycyXLsDa8SyWsCuAGH4cwssxvUJcvDbISBaiEw+HxyOHckaSSxA0rIKTqhKmhZM7Cg9U0YGZW+cZ+q6l79V0iUOCGK5yZXZCEhXn6PHPZn9wYF3jwFum6G9EBGS79C9ciMNW/kBfu2XlLLMDSBy4SDjFuV3tn9BCMKRCcjHjDe+AQ9vMlWzLpX8a352LshUlEQkl4TleEYs6lrrxenM3rltAGnJuXv2pGQVLSsEuea+kOwkubbAkq+djz3WLgkjCBvkOIMLOwuLhxEriMCU7BbWI6+rZYq6ErgTWwd7Uxo9GBbx+zBwUs1Ruk11+YO0gsgMLqx81H3wun0/Ja89Tfky3+nPi8EktrLmDLiGoR/YwL//1/+8KByoZTC3iCGIEmql4bBgTO7ZJEm3g18X4pHwtEhpj4Byws25cOVNqcJmZ40/oh97AG+7FC5q/7OW9+3zpze8cGrXy62vrVtBTiL6Jfy2S8I2B1r8YF+Olq+vxOKv/z7kPFtAQ0rWf9ca7DK/L9nmNyAZ2ivuvViT/fot5ryN6SI/XxY/+vS2zzx9U6aNFlyRyX999mHBF29T1BObkyCqORon7P58EglztJ28IBJTaJB1KXSnSqDXysc8Ypp6nP5lzip5dH2P19oW83ltIb0cK36jqNhtzQvgvmQMfjr97+vioGXaFwvIpEDnkj/XGU+2C3sLJ/SqzLDIYhhWEoVuJykDzYndxtH33DUwMIGdAbNMl5yO3jkFv/jSdl/WN/Q1YceLMrynNbvpcXeCk1Oc/Mi/Xf/XXO0MaciFUEYB9UD1G2ACYcYVfl1/YanvF65zcgewosyEJKNGkAX6Be7iWtTQB4iVdJcIc4JjJwZhDy06b8BZ9DueJqBN70TAXcvDFOEPsj+cwTDg6nKOd7JgBc4yMitjs3v4GsfsI2B4nMUo/s7OtrQsFu8HaWggHM9AKd7eQyPIVW5yj47v4bGMcFiGhnsHYJ32J85EJ25KyTvJSZdJL6fE/qXsOmHKVswm6mqmkER6/8rsoJf7VV1QNinkMs5yx/8CKUOmPBvnmZly/sszgP2FqpRj0538Ux3YsgSvtVpHkwUSMGt6sVcq96TvaSE0qcQ+3j0Pmon+ji+TCSuF7UEO+YQaLIrqnoXx5vbW+B4xqFK+Ju7sieTqjuzsHgxcP6czcDHawtB7RTxwbbasrlFNRd4satM682B0Hfy5w+M1T2UhQ9DNyymeY3J1jY4Ibousa8zPZxnqXc9/0eGuyb7lQX9kEdZGZbekTQRW+VeufZytiHfdcI1fPewv/x7JY5FfFgXuLFAlElmxYGULSTCSx+8zVVuh9cYfHaACebyJ8vsXY2+NKVZkp/Kc0nr6+tLyxI8gygm2/e2jAWEPoAgyoSt61vU9ci2CIgdenumgpwHhfv+n502kxoGCk6uevv4p9f8FoJbduq/7LY3P/XlYcMgKrnVp70Rjknjd+j1SdNrIUjZ3kefDdqcRg916tPMP96wZSjldGkkPh1/U/SBw9yqMmll92PcfRf+dSFhFDx35GaIQaEW0XXIV8sHMiwVt1jV1D2susUR7zZ4BOPrW+H3AdepnkvD+++O3OTCuw/Krq6oiy6FpXx8AphMozhNDyBsn1LZ1HTqb4WEIjhl49EoA3zNmCljMaq23E3QrW+fyH16VmVoUclpgDmVevjdxB5T45SRR8pmbRMDdyp322lAmakkYZagExc6SqbK+ToxMvvqLNCkDCDTFQIx8YF1cbrIU0tfDxu6/yNuJ6JbmIZ2ICSQe2bck2SToAzHQWSEQd79/SlnZwxypEVweo2C5Xm3OkFuMAgKTWZUE1mxOL2sL3oOXt+71T8sIfdWLVK30DeJ0lN6eFVnfU9ZWbMfQclqZYnUPhs1/RIZQRHewww/0ctKji01GdLKWUPepbP3dEcRbdM9WANHinVm5RDN9wV5dUwBZj1DTq0MIYT5SaIliT2ARgrLAe5SsBfBncXlkPVYj7F3OZS8k2/Ypxc3z1xfn/mN8fEoNfcEidLuSbanTb6Gbq6RtJXn+GS8qW33WVyOEPW+0TV3Zsy/ZJnx/njPdXv5f7/Et2c+xo6wwt3LLjZ8pGkqabms/Zh2tvOMpsq9p1vMVFMUhODAR+T62CfrGOMw1d9bVbtJBH6M7v6D+F9LRSte4f2w0QLfOMxIVLKbdKFFHuGB9R4BBUZ9l4JXOe6buJfHqezyAft9nZjmgDw9lfa/GOcno45moXpJm9Qq+3jeax+ALgFzLUoVtsvOdUwbrRWMP3OMg/n+w/mPtJjnbnNCiwJLO+AJRfxzF6fdSkFvgpmbG5iwi7zOUlZUsSZKRXBJmcLI4uhCt0mUtH4ASq/VPVA7sdchAWZQHOWWBQlm2yPxJKV0CiEN2pvW1EeFQpq0SjLAcuOea937nC2TJDAvW2ffQ5+ufizThXtIelPp14rDHkSVt4Y+D+PSMneQDNimv2b9PGYBQHWWAsfAiKZ6RE6HupbYB4lMecyrMRqV8YFy9lArEQ2uitSApybm7jMee5P7sx2dTqtwI3g/jcQKaBKRmNJPg7etNV+wFrUW2OM9THkEsgmzkPnhms3yGNdf6+RqpvC61wOyYZK7+tjzX9iP0CjJQ5cOJeSHbA4lYH22ch26oOUnGZJGpUgU01+GJn+z74t2ZRtLKO3+03KGbZn/w743O3d7/7R//EyL6EGHrjqbGhJ6yJvHLHinuZMQNagVbtR9N7JF14bAxP8Y8vY4cBzq5MVa+0SL6UPgSEyMZSrVeY+AuyKnyQ240nAlIY5bL5f0KEJOe/wB/hZTH+GyvQSAWwVnn7NLo7IYQ+8zT26dv68wPXhmcjhoC+GU3hlD52SqXg9KwgGJQsFcLQn8HuUuFW1QqeulZC9n8t8so7r/neysjaq2ZlRk9D4tvxXM5tRwvprd//wKQlnv0AMVQTx7zOFvMx8dWyolA14f+H1kBOVVBlBsJ7ca7y+mrcqyQIbLyMOBPWY+DxJ7pjk5o3AkIF1bLw2f20swY0KF+R+am2efyfQ88Wenx7bqn31NX7ZnTupEQtUzHoQvNn/zl+u+AJDeTcN+B9iICWWmA74Fjb+QH9iH2O88E5h4DOMBUeP1JlOF5uW3+fckiVSAGy95+H9GZIl4ELwC4JEHwxj1wBX3BvWOG5YxGA50o+wmy3/Qghct1kOtSggHxvBDl+RwFcyjMGIk8QSchyyQYSTY0AyMfmEeeud2/yeH3LJCTL8dI4XMi4k+w/9UKvKxJgI+N4wVx9MMRps9uKVa5oGfAjntwrT2PmaQKZueQjywTCze3haKSCh+BZajKPtWuXiInIA0iDwgIiShEcLYSJV/PieD42ZyUz0FvcNRi/7cQUcsAYc7lDJATNZfHlLc8t1X2M8v/aQ9ahnETzTQRcf4NO5lkqeNb2uLDVbz8oGMg7EckQ86sD4i+QfK96Ai4pHq4mu+/s7wNBmIuzaFzMaPhM0AEzUVWv/GFkiAU4pPWdYqgAVCe7lZQ3Gsxb2Ptzrnf75k0XSbOOAjaIaQBhFa/4SVpITXrLNCXzH5AVzdh+HEZDyM2CCGtsaJULNI8QWxWFmf3NsgIBgRVL0fMUsmtyCtDFi2+SabSCGsfhqyPGM2oQOJISdRoEKrc3bTPCfV01sVB6QhImxDdskIHCfKWwnAA+d9HubxMZP8m24OTkD219LVnTqV5/+n6f8vehdmqNcO+Vj0C+tLqc89rIEnkpQLDKZi0b+FkAhp8Ke3qJO6wy830FQeD8U7Pa3LMdfGImy3IaY9F4lztC5TB2g+k/xU5ne75M+h/X6ea+UmQliQCxfwE47a5uA+o9toKP1bkLbRVzRCObT+FYEnOq0TtGfn1jJJlbxCBN+HmOabhbAsAUIDAAdCaD1U2qxIhDy4VUA5/rWINZXOQPbAghK8dPEvsA0B0TEoNkISvIPK4x9EwgaVQDvOyUQK60eVcsV8aj4N1kUNMD+dzOHcsVJLYkTDRvLGovWUrqlXb2aYMk3pmZo+B2Y0IPzL7Q1DtROGci8C3rSdkJc8Ocy1ahgcB270zQ7lZf5dNuU9WOziB5hq27Fg8Ns8KLRKZZkz7PJDXTsazk12cy4Mcibj4OlBPMTFkO8IRsIMbDin0MjbovetVzC2Cw8JBbdxm80e/JjAWe53ctPhSM8LJ/fy4bPkdn5G/Ab6EXBFDt+fiebj/ntl7GhlMDqtO9PdAMxL08r6Qf7m2+uLcx40ZPvyemaxrdyqXJWurjV7edJOZle3ACT2UgMW/v73hXTa5i92bvXfAf9J1+wzOFvRVXx0ecid1bL3X+aXVBjz8BBr0whGrXzqUaqvRltYgTjAYW1V3a+xQviBS3qjxZIlwxOF7UopynHsfdkbOw0V1p1+R4j2SqUTyhfKX81857O+RMepe/jGJyVOjgSfxIOFs1f2339zB/++Z0Y+r/5j/K5V8basKhS+r/LRiTixu153kJKNhQ9mW72e/3/lvj8Jrj8o9WoYGIXr3N/i7wtOzKuApw5nab+/jiEDXFOR6jxmsOt6IZSNjo579QPrfx7f/7eAxa4b3ehkJKsB2IOwA7wEmrHQL9sdLhS0M7cA79qiSkw6ET/BZJQW/92xEmnNZwkIyapZg+11LTm7QN4DXuFmOF+mCVECuKede10NzVqZBB6dr1F3POcfFNW8tpfYa5L766uQdSimPA/n1k/CW6XutmIilgdjHEkM/M2O4i4B2zWbRVqYhKWuSMsB5mAxJdGKx89Xlea4H444fOGHoZNJLr4ArXBIoh11WbpnJYa8xJ5YnpmDtNffyMqw37s29y6yUbSLWRLrZyJiX6/VsC7NVIEJ+Bkgt+LAeXg7qmT/ouM+B+039re+D0u7r81ChuVybem8VVq+D/KfXO7JAuZalhC7JNlqfL7xaAiSocPGXk+JRr8rXAovea9Lr78XyHmu6X3gtj9yViFwzrtgAnAuS4Xv9NlcMYQZ3Xj4SK1Ugzc54/JbvrIk7IR24lfDzer1npES3gu+T9HH6Z9vnAshfLMZf9TVBe7Q8WcQN+bat58nv9t+75C8jFfu7eCksjfPWTft91lfvNdtvjc2ASTqq3d66RPiU6cMexzPDeESnPL2EL4iQIklbhGHgQmj4bZG169kCS7lmPvUVGJNczUpNB5LzpJxbpGSv2LXs7q6XB2F7KqczYDmBzP9E/d/Sk86G52peZVFM3l5li1BmxOQFfytPOe07gFyVbDcQrwkprBlKdMK8n13bYFtXKaunp1kixSSTQzRaVZYAPZmvx3WyslIPqUz38/26PQ9ZOgdLmDsAAe7Tu7q5DKCe4s/WfwCUrqMiK+mH8jwliED8Dsg+9lL1+DTeDEJB7rlOSaI8oxIOL1kGMm92cB721QEwgHQpnzPRKS2h2yYSXCcrKADPiBmetysUmljGT/1MjrJSTiDFDXEDL9eKe/itNbbaxp2lQu582tmlni0rJXO5xjEnkTgtfTx5HEsjQHX9RHbcB3Utju0Nn73+XyUjeg0EMjqSj1pGhmegs5iO8ao0EXtDGTACpTmG5cLzXK6pAxlYraWHOQf6f60bTNsaWy9tZObEyHQ800hNI3+98xs7+VHuUkcxFpwvav/u+oMA8brMO79RFgBuONYqG3t+iXrUWc4aI/D5opBOmCQ/sh8Xq5+tu+4equ5Gt9a1tA37478Zf28f9u/ZyADG3lPAiNikaO3RvCQcGHM7R6OPk50C2FHwVCpC01LKFjT5aXFzcHGAbXDYhaZYhKsvsc/RxwwFoBM0srUY5Zd8F030JMv8vx19BgHa70DNLMnKEu2W2KQOZlTcE0yD/LM/g3PJl5OyDwAdXLyglIktXL9fAptONcvN8j2w8erSLO/Lt/9k3CkP4ZviUgbzHFAYsXVvtPzccUCu5H5n6lpfa/56lZshizPKRQhHcXIBnMSgeula1ad839HQlvepHOdp3JDXbexuZ4MmfZhIj52NiLI4lWvCrX/ovwOZ1N1GQqdmA049boQiUvbNo1703PdBJYwpin0fKbMlTFPKUO52EFBCpE/XznmXI7jQMyRPAbN3ZuqLviCCN2RiXunOZONvphVk30Huj6D/GG+a3CDnQD/7S5VKifggSqxanwMse6lyRrKc4LIcLZ/QD+YAeAo2ReDJX6joa12AeCljU3iqAy/Imj4nelDDg22fbb4leIG1Sgvnkfti122k+6H1OT3TUpaflzuox82nNsLx3VFuZlip2vpGwlqXtnGfEocQCvEMj0lLZjJALPpewQcra5PAGuudradXifv+xggBUM8105E31Vmlk8xJFpN0YNGT4Eo2Epi3ioY1Bs9g1TXX5jtZpYyaCOgzeklqBOWydX7D3V0Xypok1uik1+UcOl4bOChDW0snXV6TqOUguuwepX5H0wU7I8VYh4IHe11dXd6RRW9xtNEEP4Xvnn4PX4k1L2d6uqLWSLiXo2BSOCidjtFIyqPjeFGiMIEGd07d6fQMwAxYRd3gqDw+M8GBms2ZZ1TH2lfQfyNB2eRgv5gzOpqtw//R73yVYa2MkVnb20L+FZ8T3IVXXYQN3Xb2mR4AtixNy6Zt29/s6/yQGV0xakdDllgagRejpvqw9jylmy7MpJ1GrnXlObt9VPVBDohNZ6wBQN1BKXM8/x1EfNinU3nVcGCW3TNKcgDLZmj6fQDYAJCLUbKLp89lLsXvYKA9Cnojbn0NsW53G3GWav0V+l8Lvc7RFhl4VeP8O/T1zHpk6MZI1su1vJCIJwcSkj2Xkbm96xHuPcU0XdP9Xz3TcBFXK2cs6c5umT2rcmZe7nmXM4OavsEQoGe9KHueufth9b+usl446Z3BKqAjaDVQ6uJayFDa8CjrxoKAVFl7ZlQSJ4jwZpkAcvvn5lMChOppAOsJpS03j6SSvsF4CUDNh8boznKzAGexFvQdeSONw/VAfwNwiiLg/EPewM96mI+K33XyE7/R2LX6Pp/996DPWjcjdmxjffoOzVvPFfj38dfVYdMDEw4UnXsGKKoEW0lXky0AYxI7gzwxH2kdwXs7P4NaDScGWEeuW36ANWL3s9T1CvBt/XI8vicqE9PhL2a1SkbFZK2dH6rZPgVQESAACdI8UsZzIPN4fB9jDn4Y4CA/KUPQCu2h9EHZ4lrSyexcCgz2GZWh+9mePU3NAhZkeY1cBWPGrokQB6rxrt6xQID5mTVj7/oo8axZIkrUOtPDSEzLgPRoohvCs1ymHrIN/fQSuMN9hdsYSnVKJPQFmIOjzJvRaMKI1RIGn0H9m8+8ZIFKhLtsqi+xl8CEgVpZnd3RbP8mJvP509unL5++RpPnz58+fVnZjp21SOexPYRZgfuw/9xvUrrWe4jYZnprZ6oPWLKNCmB7y9hOv8Qa6KxTdJGJ2vPIFq3KvzX9fWYIafnFBVejBFy3CbYcOR4dB2xXiwVFNyEj7rypAA9EnTKUF/vvBS0qEC06UsrE7uld6IFKLVR6FytAE1B0RvXj8VQnNprfqZdNYvmE434tY/Ch/3oR5wQ+ekmVbMrdiL0uT7Pdah2MplKwyZ5WmYx/TbrgeuLQebKhrhsunSPZMUL4PfMtcnocYJfMay4ePKgjuRGuJ6L+s+t/uKlKXgN0x46JVOAcZoI+Ix7FrzbAXUH69ByvGsYztasOlHvgknvKjmdOiOrYl94dDQGajMe83ZbaQXoj+QLLkioldWrrYlwBoLrlBQTHMILh9xyDn9+QlCW8PcjP3icDz2noz0xQ2dfQZoDsru8gAdGJz+YqHqtPjbBgLEUunLCRuIJAyIP5HCQ76gZYyAtIh8/bz14ZMapySDS7xx/zTJljNktVDPuZCR58TDi84IC6E0OWqhk+AAlSdiEWTxma0An8e2qg4NAP9+trV/bPDLAyR7LznunjmAGYyjpLErQXtUOd9MPub80rfE1oX6ALaXvKfq3TDEMlCuMEmNsF0Nz8yfR7BsbWmZ5XAI50BecuEmzi7dnuVAHgWA7GMxmJd3O3i8NGeYK3Am5Gq5QRQGIuZROMUk4kDnb4KAW6R9v3/Acy1OcQRk+QYv25iM0X8ylb1rK+awnF5kMr0/Ml401gE93x9EX+k/9N2Vt/8OxM5l/WpFyJYGNK9FskhSWIGYFDxHh3K3lbnd0i8hbd4b5kk4Ncq+xwsI3ROg+1Hx0UB0NbrH+f6emWPix1kSwAOhiiCUiWFs+Q35SJrgOFsN/ORdj69JK0DsKwEjHse/SaekdQo1K8/l0PZnzof8jFFN2nxPwO/Xdpwz5uKg6biJMVI/GO8eB/kJPY3hBsz1gP6lek/Ykg6SlVRye5P2w9whLDHCpZGvQ/PdM+I5T/Iz6h7tzoymkAXXdumaq7/s+vQsiFPsumfzL9j9WqZcYeSzEQEJZ0C1Rr6Yv9IjBWV7Etl6u0e5+9zBoJBPrEs3j+gnYt5Zk60cjtAbIMEcoaerMG2X0A3pzKTCY6KKYcepYjdG4CfJDODlSl+zpgdLvHfuTU1tkAn/uyAPPx5VluGBPoJDCutjNLJZhm5GAXcQjyH4SorL9hOi8LNF2exhfyIXIS5Ezl2WXsBOU5X44uyRrvZbbSbCSeFeapzd/3FCWhJqsh0+Z3G0FgpslJX26UNz3AGGC79z0LkYyx41lbTyFtGcTFFZDpFJmCcZQRi+MBsVVLH+0M1v5M+hL7kHPM8mp04MWelABJO3OUP1Vmz8wyCFuMIzxUP+PklScGneX7mt/NKcX3yFjZEdP1LGSESHxyTIX02B6kjb83KbA5vetPLzvoGDSWoUYaOnODIwPg6//uY9//bhHR+CwjDwcSPsGOgwmfZHe/HeAqklTPPjHbs2Xra2zK15h38IeMdqB9tVcOeFjgXSv+x19EIBKnaLIRA94clVSjpH5iEwKnj8wjd359v6h+vs8nZWEdc1pNEuIcVL5fB4q6pVouE5GSvZpfF2lqbPm6HFlIdyv/GT73KDrktoLJCrVq1P25hG2SsylqP5Kyhy0f74HVN4Dhsv29z3iSuP90/S/7eiHBYZvqm9th1xykT/bzhXaNHQdvv1GRT+ZXW8le4IZABw6GboQqHOKrEcYK9XvgWTV4pns9yZXW7sxoQc4V7Jh93RNJdL/1M+o/ZJKAZ29UBaHxkSx/nBNqGYdmahnwY6Cznn/0zMNkM3pJmZekdcAOEwagBBBN8m/gziXQQWwlWnXGJBQGhkOeFeBjKZIHpNo6KpvmSKfqu0qJYlVAOVzaewmXqAkISz3LTHBMG98ye3nJAZyAAAAgAElEQVRzPKOsUc6RwJub5ZkjYJapqUn8oADcnFcQqszMoVoj5Wif/ckNxPilX+c5krCZ631NtYmSZzYE3MMqsBzLyG5fg3INbZjWD1aFsuDvujlImAgM9GNac2Zo2tkflzm2lGdTjqSnRt5K9iltdWAkkJuQr5LlBRkkCFAFDTKF+L2T2DnTFbMr739KHwB7w5LG3APjRE9QIu+b/sKalRQf6wTUQtwWP4z39HSn43NHBP+PBUXpNkhO8ijWw0HU9wKxIy6YGx/6VSPlxbFeytq0oAmSd1BmlXfhgIqpWIngpmDlgfpQJJVyrWdH84NA6RhfvM04wAXf/sNQlbsp38aXsvIvXRA2MP7veurX7Aa0FSGLKetojl0ws3kCkSB/2bwAWaRcg6xV223Yom999gTfdWyLLK102lu+HBVSPj3/PiYMziPTr+5AYmzRzD7JCcgJRtzOTFyAGrKhDTirJO4OLsPcz8/zuRNgMvojB/+h/79P/w/wnmur9Zz+cqsDIHSqcAm6IEv+QKw8W9TlpmSNxrLGqhFP2vRkq19pIUGrTVd6Kb/RiZd07xJseWnfayOdX1//z/OIIAQbIbXAYSVA+FfNBPmaEdjxDFDy5ezKGcBICCVAiD4Ms5qdTo+yuiQN5ffVt8fz5ZUcaEFWAFpFnMBo9N99hqEF0iqQZqPRjDyrbM4BKwkdwVqMjWQm534A8FwYkAWC0Abu3DpgfAeRIZHNZyPQUqvvmXUBDiEJyGUp9yVwxZrFpgQ8mssO1xU6u2IZNHRMswB1AGLP0NQMzymnuravmcYd8+d7aMzoaP1NxpP8U17yIsB7xHWcLPWskhN+3Nl1xEQ5x4ZPfD7pC1JgY5+RRYnz1ZRtk4+eOcNLT2FDSybKgh0hejqcJ7nCzKVjXeaY5UmlZrOFjcGTrOVga0lcfIgtSYvPAIzBbohn6lFpKHr9bMlS6d6GBWN02JwfFLRf48rmrHybtHdE+/33mCz27pXzvLXAfuVYx1RQG0i/x8t7YqMu5yCUaYqU476fKQ86nCUFiu/zIunkUvgo6eIamWHva/nH/VuH2SLiloK5a/eKbzkeOWbNulygnfWaS4Yzlq7tDNluivD26Wv2tURwKNbgxa7UhdKaVwRP73uQFKaBZyCF2/B37wVWac+4v9cs5Jy2lCy5+zl32/X4IPyXDFfYuVrqMz3vKRJO9PKh/9yUvV70SF1yU5IymLKDKmFw4zcmV/tzq3tT5C3uPslwt92HnfeIPe5tGRrKOQgbxjMQ8GNmQ2Zo8guvZPqQyXSNFeqmXTrWp2X6XU9zfBF4qlkf1+swxVXffg39F4FYf/mBYACXgzy0JgVpzrD4tRkBUQoyRyhjqCU2Ke6nEeP7SjK4aGWPfe/H8eYdy8FsfGZP8+8Dvisr4H/nJAXCWCNo8lFAevh5+HMnX6JkthbWSjjOm6iT6c5qoCx/DySzw0P76D7fnv3Aegu+1kxK2VO01U4btu+1z+VqAROHJ2Zpgbvehrmse84D+pzyEmQQo1b5mwuIQL+TKGS+wkxObcolJxindKBn1Pa1Oc+T7IkYYZO5DkkeECoDKaALKGRxW26Szf6c9S0qWtyPe9MDZeXqWum5nqnC/bLSJrF6IU0464SmFOYXfN0xtjLPVrVwkhonsbGGvOdo12WXzbSnBkgiTBzLd/3zz3/LRganI5xalELlp5K0NBUJ1IOA2mAfOl3RoZlTdQczk58EBqX9pMQFpRoYB2zT4bjaZnarC6YJbhFC/QQ2hahvkDwAkDGeTC/CMAawifWMWt+vn9Zbfmig+MogxDJi1PGL/N3pPoZPXlxbolLZ4+Pzyqq4oYJMiIT0rEKRgyObp4hjdPiIdPV+yema034T6oq2oXwtwR1roIcd81ZB/DppUi4yzdVDGdvehoGQPM2vjsbf7TSVXZ4SsuWWH8sl+X2fggHlOoJZk0mbT9wHkpP7CMLDaO8p6/f55zmUqSX3h/5LEl8GRmYAHqbHbNwhu7WbJm16CHI5CzTZQQKqwb4hyCDLVT/Bv8IK3PzDVMZ2Zhz2cG3MNWAU9veuAyojde2qAQCVbj2T9/88/ad7c6DnQJxepp5V2b8rZdnpk0B6THZBKsLyuKzo7x5V1zlQWTgCVHNjiHy7fFfA38pIjyxSni1Z40oBgpcFsMTYNknM4TgBQ5ajl/F4fPKYX8u6YJaYGrQe4NA/x2eTLO/xkUjIb/P3rUGCQL+dk0LJVtoFWqCVyfHOePiC65YyAPJikIfzAcxBWaD9G57pnGv+2vYObmz/NzEhyuN722vYBY1B5JHP5FglXDXL5iVgsidAYMEKMX8RfhJGJTUZ3I5z4/U3JQvjuuRZQiegdl5qykxVUid9iwojZWB6EAA6pLN/lqGTiG2S6ailZ5/W7EpZXCsH3LKcMou/Xd9MnAvUumFtXrRuto+VfFuZnujeFj4mLJ3XUccHvql6lpMVpMT9eneE8avzE8VUTreKJ+EExxTV013zCQOguIHu7hSvJRNlees/+mLrWfWbiQpFNiO7V+y/v+5OFoisbRGEIuyXn2ZpV+rhFll7108IS3gePA8kaB+so4H2JxjGzvI1CtbqI7CqyLxsF1G+7K4mCNbWhdHA+ONJKDXWJG7MsCgEPSZR+g9zCDdAhCxRGEZE06tsv4o4+yzfSz6qdnrdyG1VbrLDCTbiXUmNl6m5XgoIFMkXNfrQ/3JW5UHt+dW/ov/9/q9KEJ/Gc//tJGPP59m+9zlPxKEHtKZ7uxqnIzqzKhbhd38F54zDt/BjndTRvxkhn3zbh/7L/wNs0B/Y+RDso7fG5ZpnUA+kYJJAAB/3lzzLgq5zw0s/4cPW86NSrpKYIABeZdKyDowqC5ohCImMDOdkuEd+LjGSPTcyCrWDaS2nA6BkUUEA872wBtIz1hkgXasG4LvXMwk/weS+TPfHLbFn3CffTDvL6evF7I0ToWG98AxlmdOHWgbQq1gwFa5hfolKxypnsXdcz1ygqPRQYNUzIz7nOv9Y3AKe0/dzfzybbrLjhBoVGX5OJZ5jwD8nsT9Z8p/nk4i5GuFjiZt9zj2zLnNnmVxmRXJjncSIxMWX2FsnfWXvekbSjPPOKnJ+lplp8MUxBcoFSTrRaGPLes2ia2w69yeCV7vdcV5FKwSBwmYbEhVP5Yz8o89/X2d6HiKPO/on3YzzLCkcnTRMjuZw7E/Oq0X48NsD8L6jjMifK8d7RiBLbXsrI8EyetYo7hszpwG2cg9YMm5Bfme2qSzJymxEJ+eg6/H+njRieN8M34e4IisZ6dwWOlnJfgaaAeT48twNOpwB/CIJQjmBAdyGLiP1GOznqBNNqkVFQhKq7+205sf+m3i+izS0vdY/n2iUnnq/am4scCtx4cFpK3nrGb9DTgZ5mkCgAy3cAzPYypr3oTwyG1Mj+E/7cdPD8K9z56ojA7v964f+/5H67/bkun+/o0xw0gDf666rsIOUuwQmkE0EhKZ7jLTqUj753T6j6ZDLKvTvFniRpZ5W9kP/4dtf6T+AEsu8LOIMrOGAp5TDZTc33wEmEq1JQIxFADXD9SqhMnKgkF1BaZbNrH7ewSDFicG7PDzfgD7m7HY4XC+AdJImHO3toC4bP+xzSS0z2RsVlPUo2C3j63Zom2QHZX92LlqIxACmBRBx/jbIVi2ddWCpeTK8kL5fcw5gG6tD4pSLxfMbCRSChPraqVObE6FCflpm0PdDe5BkK5xkCZTgfTJIuOTIc7xZ4ZDkEsTBM90gSrlUbP0uMlbnzlI/lIOlX1Ug3YjplN0wISCRSYCJZ/IMEjFUloYd56eUpSslcHgGSVXMweVTmdJg4EFEQPRjF0BOQCRAR6UrvUvgKSe+vhjDEhI0XcC6QxbLfgzmvGeDYPv5njKmEj59+vz3f/zPN2y2ZzqeSpLOZ6q9qhOfG4CcXFDNAs1QtY+JitAcdPyzusLvmY8cvZi8yo78s1pvnw2cTcHv5RwUEHuR5pr1OiITh/rj7M6+GQ5SbiKzDEaQnW1QN9/58untc5S/rfYHuxvafgsy7pDas/9jGscCSYzcTYNiTVs+8GKob9GW9GkPuZfvybq5dOOm+2yDZWAA+uzaSjuhfj4uxlhyFVFGk/99ByiL1fcyz0sHtoGEV9k/5fmejZrPFUQ0MgkvghCXOUDhQWIVkYv1KSTL9uhD/0MA/0z9x/NKcOlhX7W3Cf1eXXuQ9OdggQeIAFSK7XxoiAFynvH342yYz5V/f4f+wwFiDTbQwe+P4EggalXdf+j/VNHB/c6KgqnsN66Zqz3oalOs3DvWUhqhjglg14yP7HharK2X5VnpKzoIpYyYPOB3LHMrZVF4FoCdnVcxUM/gKAaShA1kTWANAaQgBZwrI6T2GYgH3Zd8djwGIz6j5Fgvl39fn1h7B6v5d2Klfm+OM8dCQGvvYGHJE51/PMMx3wanBNd2viTnz3twbYE5FPF3bKFx1TqfeE5c6V7fxwhcu/ZGkFBEIYhSPq2XyhWCi+cI8CeoiEYUe7GS1GSpWUckLOnCFzZwDKF3Q8MllKNCcDF/3+e4uWcZ6/rB9oMU1XNce00bsS9ykUEJyaZkapE+J41xjelU7lffT/47J4sxrF/X0ric29COvQfiIiZTjxaYyAbp8RTrSCxAKsJTlclN4O32mTstF2z87WCsOPcXkW033IoYxXL27/BZaEtVpPfOxccLAetQwstNbmOgcWDb5nXnRXmis8jmOmsa64/9ktO3EOiMtMXLTN8+ff3ybbd4/m3XIePgY7Z83gKgomwrG1UZXUZi2JHOU+too72F5N4Pes8/F0H7aB6ik9DhjMdJkufjUyfHOAWc9zICFRxP8guL6Y7eZcKj3LPLD0nwZ0GuKGcXGZsCAv3ZTlZE8tzES4sKeDHHU55TMpLSjw/9b/toxulGTsu+t7R7SJmdfnlB/rst7Hv9ZD8qaJ0KgBE/72Gg+lS3X0585jumHrH8Vi8YxNmLNK9eI1AeyMzpQ+b33YEBnr+Qbsz6Lyftuv2h/yqxeo//R6m0Zyi03y5nXv5jZxpRRpuwyEvk4DFkm0NsAtopE6SsU5VjgjYE9AjyXScToJp7QqA0rSIen+YcwFzP6tkqAf8J6DEcqnNCDhIdi4AogSQYCMU6IAvma+VgEo1OFPxdN6sEhOMHuSoEwtYHzz9IQV3DAl5zmXwvREako4UUcmkTxJNsSZ48awHJ6sQnyA1aX9dD/VwvEnjIU2AbJwu4thM6BHToM3N9Yq6hR8ju7XvYPsbQknTt+TJSQHmDpDnRQZy6jikDPrluQezEqEQu14nw1CFmHIFmTpmQpPvaeambk/nagENlidkZrx2yiDGdGUKz2uqol3IZc64BCTR1EEHzjGSuAZ6Vq1z8G1pWxx5oROUii3ZPzrGaHWUI+ufHdVa200kDrkWUCVPfUxqi3jH8s0xnuu92eFybSo5uY3ZC1pklxFdreIPICWc4ThcAtW3+9Fu0HdnvMEV6cW8iSuCy4Gy9gPPLly0Sq0xu/W4LyCp1y45n60qsQaxdiNGiVm5EMqexmybgXBC59SZlR/KsLdW8gzVTEj8pjnW47RPYCeugbnKTTLmhgli74aBswRggum/GEGeheDjXvMRIjh28vWgPXWXbD2VfqI2VSMLw+bwhj7E06WCtbjtMBhnp/d0pH/p/NVn/bv0vD8Z+D1mcycZxd/ny6GcbXObimT7ID0Fp3uexcUDq9EXmfwz9n6PIiUfCFZRMB+B1vgPEo9fNfrktgfN+79mscy9/Rv0Pi0TgAr8qM328QDMuidVHhiRdW43aw9ZbhNmfFgAXgEgaBOCrAG1oCAAn7KEDZwGvikW7L9lAy5QVQDf8UgzHCZCDWPyMn4XhZhCZZDIV2gF9jaJrzgC3eEl2ybKkn+TYEvQk4old82yU+UP4Evc3gNQA+BzTdPajWVIB1HxRJhZrB25t7+wgPiwLBom19nt5ZtHlad++dCCLu2HuW2bN3/O3iXUhehibkwiV0qGRQ83q7XuhbXeKqPaSIRZWBDno3+PKmizQmNseISi6rzOsT2KxoWDePTEzdABZcJcFZEjw3INQlvcB2XrSduI8na1HGlm8YwjETLpnmSp/WWzRJSenQ1ll+rCum9xwEM8sRWV5m8voAepyUqXPijlkd6JV1isQnpxBOJtciktEFApYo5r32Pt7o7NNL0tZCxTE3VCPgnWD2NfQDYcDz2Jg80fRtSzIyve1bU4LmeocBDdG/Za32ldkfe3XVQ/3Ftw5qtRScfOI0Jdvi2whAjG047EsUF+/Yz0R573Iikd7M0RyZhHhg9CI4ZARyNhrWZv2u6dkd4MDP+zcCNaZUXG3UEnttD6MMLcDuJxFA7pPQQZF5OWI9/X2HilmIi1b6s5rJHAdbH/of8Sbmhz/ofqPzOdjudoZWJgDMCjlmrM7r+xjz4BLjq0zmq/FJR4yyXQljxrn+/Rfpaaaw6n/HdTe7NRIID/0/+yM90L/fX0P8mPOrkSi3f5V9lmaiXTQCTCKZyoDpJdZFxBtyMcJwe082Jg5yHvYKV6WVU4ECADZyyuB8cv4y8H/HsgT2uL9LJtQnuHVBM1EnIRC61TAsbG3g6hxnFY6VcgtngIylnfIjwsYTTBLbFTOddWMHoiI4IgFL/zsTMsEgJSccqDyrfE7+zC60kWofZIJkgcjUpKvkE7wjSMrZfBK66ASUjafRQklSJBlLcd93U+t5W2BTmKz9m+G9uHxuSp1SKLMrpNoIiAGckrZg7zmf6dzS3vePATH8s0j65jz6O8XAtLy+dR9iFXRuhOA7ufiiMZuZECRNQJSobSAtF97RtRrfWeA7VqiVIFDLna5DqK2SIDCEMruOMhUytqfoyqejKJAGLDoaTG707v9m0bdHCI+e8wutaorELxSi95KY7Z44i3DODvzzrbN6/5bUFZDhDVAvOjz89dPi08FoP/06etiQ/vMEF4itl64s3727dPn3blAwP8GGKbPaZzsS4Ics/YvMzkw7ogBDZHvuO/5FvvYastYTtFyRqVPGu5EwlOxJ9mx+IxHxls5Wwzn1AtfP8xlzEbRyJxU+d3gbojCx5iYrM9lqxHxQrjSBXzo/1wSW+wanMXv0P9Rr0BkLxkHPrvpyS0YdcrjXQ+e9N+xKuRchEkBAFrs9DQf+l9X9ZfTf4C6nKYAhuatKDsuCkdIYsESpZO4A4A58a3kJ2zu1C4XJR6REXegV/eEQJCO3s9QAB3Fl70sCvKuTAium6+N62sGwO/bD+l7VB8EJXKyCgTHGuHTnp1SkLmWjGmc1FlbFgJf2zLNNX9bfHY+N/eygtt57SJThnuZvBCAW2oqP3MwD4/m5Y8kzrGomRyM9SrAussrszRJGqasWG6GewWsfS3lEvEoJXM7A7NwjIhjkMTq770JRSXBurI+t7XhrstW20bbfnKt8o9o0+5k18i5nYnyRiahd3ZuyALHmpuCVZBh2IT4t0jUbY+cmCpzJ1l3ecU9dyLPy9t656hJ8HussUc/0wq0LYtV7VHrKaJ4xjL1u+7EGeVMYe7OtEQVGek+a+1jcHLhW2FcQwfyFj9JQ3Mp/xiduxiZ1sQ75GUaMviZvdXMVlTRXaZrWqgf663UIXkA6ggMiK8yujZdM3WXP5N0OI/sBLdueFioeU3yuxYxnmQBozn22oAm7W7uK4ximUlF9HNIHHKR+3slWU22+4rd5jy+TshJUtYH00EPW9H1r2d2XE6pb1YG5bf80P+weH+1/mNPpr0s+2lAQF0Zz2AA7EWYyTT/TsSniFEO4krY0fK93LPZkUHHPvQfC5vVyk86/YvoP+Fb4CeW4kgeAdi9FDsvTt/uEHCDxBag7aDfMysBwHoWQfePaLExNUABBt9ik2ppji6qoLsRDNvfMzuAwHctvSsgvZTLWXlWfo4SJ4CJo4wKEEKOU9F+Zg58Lg7ONfiy/vhdJyjC7UN2xEB4Jw/8twP4cFJaMyd1yiZEADkbPOVwCXat7Aq7F3Jwn1f/zgmYMjaS15ALw2pHpsWwJI4rbDxRS+KSJ5QyNWVG69qBiDgeid/rWUE8KnUKwqFEA67AmvAsUoqDr72T8k7yMf++dgo6iABVAlOzm2VsOQaQ8pvuuE5Sb1y+QLhyj7Z0lzM9zQCTiw1nUIAHgwheItnbMSoaf2QCwhs/Iu6JyDghKd9fGjYUYDcQlVtEFHN0QpB6BCt4uKwjy+TTa6zgAKxTqH9wiuWjEbyr9fV+GdNal3Xd+j9fgx+vOW2yY8zk3bXoCQ3vTCFG6PzuIFU57tgOyc/r/c7wy9AKte4z4GtuFQaQygSyNi/fnCk6SN2rLE5l+ebt3Ty8zgR1EeBtG3nph+cDLwDkSs0es2T2sA/9nzLVJvV1G5vM1y5O5ctc4+/Rf+3Fc+Y8bLwVIucYmU0AMLDPn8iVAi8SjLh/Q4SDnfrQf4viKOrUQjv/OfoPESHIQvlOCRrqcHR6Eb53p0TII4JGqXMg1jNA8j318Hfiv9wPATMgT6NCImqm8wEikyQwQJm5JL5rpkaeu9qcZxykSAVEEohyFQNsI6OD783mx/zONdId7FkkQPCXietykXyuPFObkwHgjqBaWCq0QcZ8CUitDF/rb2tkWQF+7yTUzE5fe3fxE/G5Xa+xWWbK7Jl/38vANL+W2cDeWEBU5CL3rZ/FMcvAdwMZtqY8hMiNZXdlTbOzXCfrtN2WZYqz4HElZCp8ViwEWz/v9zaKSWNM5/u61BnOs3cuk4XIoMwxZWg9Vt3sUsfouyJb1zOCCAAEQTMCCFyd9mKTHhiJa1SvgbtbdNn9ntmjNAtg5wnITaP1fOFCArgGLL53rLFxsUKHE27lIN1vT1msGUTWiM0EKglELvN2Y9jneAOpz/uFSAKOjEKco8PbWznEh/MKrjL39AX29iQyJokkNajbR6mhXj7E9H8aOifI9eiQZMbXJtbMQD11tAFOI1h9zDPhjR9UQo08iDrhgfCHbNk5CuwxCdb7MochqhlFsnuqYiDGFTYnnwejaMIt116f2wkm5P17depD/ysZOgMd2qeyxi2o4Xbve/Tfn8donxNss9eQp56l5B6aDXR9giz2jDfMKYHTYM/oLI2AIXsWjQ0EFH0cH/r/6+s/8BLPXQQCK82J0qLXsz0J9IRf1F1qAyR0EizR7F4kFChO4DcwHV+SaaAqwEKMrWaB/MwCBtWzSOGXqBqmm3x/DJ5lLhfRdETcezZgX8roh2UYDLgUAJ/jVxZsmI+Pw0H3dpQ1owLwK92vpWKwHw2yKRNSSvD8zFISxdxH+lUCXwPkIEDMgFkAZiIINj8HxZzDeAYmvqXtw0F5P3t+kK8F0jE4gXQQjPpuJmX2sCO+R0cmpRCCkOEFAuL9jute+ZpeKZe9xDZ2A6LgZHBLaG5WIS0kYzGfODKRj7WMifusSYdcFnntznYJ4+xrJgKDqPRAwjAczoWldCCTyGap0glrusk43tNjenOENz2yV0DiQSY0me409zeMQCaA7GVhQyjdAX8HoaB6Tmb8wNZxoKhMUv9wMBi2TiQJWYgRGFr5gd+6gMs5PSAStvf8nuno9y2gPRaVkSpeSyLQQC9N+HqnTzdNeS9rbw1Bh6WNqVQCAo3y81dl7/mOnVT7fOwEumAg8HsahMtQMT7oh4hHGEKdWapyWeSCdsrewbM3UCnzTpYP4nXL0PGlsrG2heSVjT2Cv8Nh4mYduuxxkOEZ458BJvx/fQwjcW4y+6H/IsDVBlXdxTr/mfqvksQzonuO9bmM1fcZZqXaw/oMly13oB/6n5Lwof8KSl38PwFZLllibYIxN/2Cb+g8Gj9yue0gyt1jISIArcU81nNAcf7D0wyKZm2Ckskmj4LDBvRx9xLl8v2l5ErlVHHX7dMaQAxYF10G9zUHWfGIuBEZRPYTQ2B8y6J9Xed9SSAQQksAmcB64YBepHNG+7GNyt5VImFj5viNzNnal6yAuKZ1yzPbZITFgbfHhab7BWyKm/OFnU4AWhMAXN1/h03y9yRFxjzYC8bEczBGrOM7ZUnyn0miW7MAI15enUmZaOvAtQCBwVi8xXfp0CYpnQiMZ86gd5jfJiRWKh7lqPFZGUcGHSKDVrNNe30F7vbvo7TPgwpT4wbP2CXBNDU+yts8it+zE3NEs0bbmQ9zpmyoq0b2zvIbj35SThronUAnBXoCyAX2laz4/SyLWRcnXp6BOKLyPb9aIq4OfGtmqICEY6zPh+ChFDOHsTKtfMfPlQi05/o/XSbq57VeldHc3oHME0BmTLdoDhFqGe+zvIjPTyHWM2dAV/YuFvoZ+ZUJ5vAG+ZPpOsuYCuhtso+53bIlfXgWcLRM5VS+JGN5dMUTe6W8j2QnlyfGaGVSJKpTRvND/w8yazJdszlVVt6XMfuD9J8AAmCtBQIebCS+mrKkAjEigF22XKYdTH3of0Kc0hWwly8qSlv8Lwy+6SbA0s+o/wCQLoaUt8Y7RH7iarXD1a8BurwErsemnLgA7zpQba4po+UeLlRkH6BWRMidhoN+NFmoToVkz8jYmWmo86Ne2qLt0Vn2f4N7cbWjfTjXJCdrlwa4HAgEMN4mOQagl3yyc1Zr6NNhWdw3buBAP6YyAFksbGYe2KjA507XnoMqJVInwMHc4upKNJBRIzkyAta7igHwO/kBISB5ygUQmZozSbF3WhXJRSX6TnKVRTKJbedaYowXgoDnOflGdsvX10lHkTlUFYGQxJdHCdpEGvnMXH9mA7U+IkoxP8qLt7hOaY31apk0zC/BVClvwzze44w557TEt2i433MP+B2k5BRP+yT3tWcKUldeZk36OFMWDAyehATX7I3E214vpM7HPmaH8gInlNInz9rUw2tOsHztLadBHH7D9VozUujWhlflhSOJsrGj/nMkRJmFoFHumSc/oHs5D/ZubjLc60Y6SlIslclcu8gAACAASURBVEPZQndgjxKYftRqzy+kvOtT33PXhWm+J3i0jODb592Dv+sqvNBUltRndZPPD/0/z1n0YFDYgmdj9qPqf8haZjMHwP1K9zyQ8KH/r7O0RQ4SZY7BjXfake2PflX9T4HaumVgRWXMGTFuID0CDwBfsnRYUpAB3jMvqUveI81nllyEwjMoCvWKPJ1ZmW0zGktwG8Kx5h881+HnchjpjruRgFhpU2SotpScXcAAMlEJgFbGOTYRJAUHRXxQQYCSpAS56VhJGEvpfCv1M3U5CWIGYhInuovWPqqCoZaBATQrjup7O5O3BOdaRhKOShRzvl/VQOMA1pAn7J3tDfY9RBpzxLOxhgqj4tl7zKhZyaqNsZMZ1rSU/J1YGySfuKzsRSxCnPjWWpJ0kfBo/CT6GXWINc55QfIuTQXkP5PMlUyfyUG5Zy2Fw+pgjCCLMX7JXdErdG+rIOcMhPdInYOnEuX0khv83cMrDXnx3j2sEjqbHDyn51I8ZAhuIOSIkCHTmPerTtzqWC1Ts4S8URIRLBK/Fj0tEd8zYldKSKB4bY5j6duRhWoArBNDsjZvAe7jGaLJfg+wPaH4UJcb5rOGEiNZK8TPc7QmHEXoIhJDdXoABwB08d+20esu22faG7Jzbu8B+oWEtDU+fk93hHGIaBYVuJRIxpbVMj3+js9Ox2bgG+tNozLJAnUrtvFD/zOCNGa3dED1Q/9DAp28N3OeFyhD+qH/H/r/HEC7B9rcJbq7F8iBI1AJ1nQ2AvTAwR6cl1yJ+RcDsPx+D0DnRwKQ49+KP6O8niPSEHeQisc+QpOs1EfgF+P1OceLS+s5GOihZwYA+Z186VhBelArywM0EJjupVWGDSzbgjH2kkHNqWKKAPDm60v5nJc8xa7rE5V0wacJxKpkDGe6vvJlnEALRiY8UDUEWx3g83yVZ14KYfRMhJ7lsuYEAIbxLO+qDRD23BL7YM3AZ1E+GLhG+M0JIT537MPyy1z/jCdQfnXeRaclII26d81Wui5BBmLnJD8+Vy/Zc/iGzJeaFkhG4zorNcwFZRllrkGUZorouGwquxWEbt/x6N42kA8nBTmOwOs3wHQB5T3y2SPZcqgxbDzD5KCUQx1lE7mpEzgtZVQTwcpd9CxBj2JOzn6MdJaytnqWoxCwS6S4X+PAoc75dBh17rV7nuqjHJRIyLD+EGgYmSAKaojgxsGv9d8j8oZU9VMmw+UC2cAeTVaGzbsBymCgmQAoTf23k6YaKTpkxbXZJtflwmUf39H10dHZ+lt9v3OxGgWuBLDuu4C5r/kpo2EosHf12nZ2LPWl7OGH/rO84j9D/6tcOWl2/T/Kli82tNuqD/1Xxp6w2mIxH/pfI1gkEs3/A8jQZDX587sQXOMMcX4JENlF14nNSSBEWo/o/x6MDLsDN4VILYgsXqWyMZaihd2u2YvQuuiWFWVPhaztcxDy5QKooIvp23tWehu21WGNx0wuncAS+gbDq5lteyllnO0xdoesUQ7XgXN8JJAKG3PsH397lr/1fSig/0JsSJQtc+JnzOM9OGRmAY+ZdTSCk9fo/BJIWvhukruW0XJavK9J7IzW0ie4TxBvmb2+Rkg4QD7O59vYPJOXc1BJZt75KAuT3IHYLP1k97YlmxAiy6y4rq6/qRcplpg/rmP2c3hvEgiaYFnVEe59kgWoSErukVnbJMu7t8WNo7uOG+j9EbpFsVNCpj8tyzFlWmawi02fskqKp3rKugMzJ0T4bgQpB7GI+x9ETjJ+kDm/fyjP/VwI72s738leB60VQLczIsgwXQhSPWMEwAujl0JvqX/NZQbRoeuKaImAac8cK5elMG8yZg8ugPqpTOhhqedtoA+az09wXGWskkOBvk6utKGdrD+Nket5eZeTfw87K99WF2yW2Xec+SjKk1o+nrP60H/I9hyQQZDhQ/9hjj70//UxwSf7/6H/1TjBxo1kGWDNflIpk0paWCGR0eAeCRaISrAK8JBflEwDIusAba2AoLeFxn47GPcsTYBTsBXRGAdsbn94H+MyCjzGh37w3v2zNxsAaSrgeQM9K9UmePXaioDs9EsJDomF1mswHCgYAC5nZVhWG4sc4DfhaQNvJA9GrgBklQmqZze4p0YS/QQWwHYlioEFe6ah3MsycjFurQXXsjSYEO7qe9ezP9xzrmkGLI00rWf2sjbdBytZz/yo/C7HGpHrs5FANtMkcWrP8q5sPSDA/WvNNUL+LHvHfUbziySHJoi9CQb3H/pY3u1kawT5od6mPtFWKOgUsvb5JD0e0d73yR/TuRkIL5mPhvxegdWEXiFCIprlzE8o/hqDzTiFo0bNIqU2HaT3yOOA/fiR5tek7RXpsJvKuVUQ3UH9U7ZndJClXOwkigTLto63aD+GO4G66btOgIpBzR8gE+SbV2xY6WLka3Ou09O4osSw1qoe8jpx0seodBoood1xgScSJwIlb9yJyUmyosQOhgE6BBL/mkApuxe3Gcirv69q0Mu+h31uHnH80H91mnyyH6GDU1OLk5R+6H9a/4F4f+j/bN9lmz/0n4i5uWoBTJ11TJjDF30rS5DAycBHP1/jxAdgV2AYzs/3617u5pH+efyLSb2x/XDvHOcZII4zDUnMW8BoA04jSKhEDyDZSoGQzXBSY7fzSHtSNSvJswwQyhdapgV+zscDQKs4rgW6AV4NzOLWAN3Cyun7XrWd9lK6DcazbgXlUXgmO/K1g/Bm+L2Uan3s76wpBJMNF0BqvQse/H/+l8+vUrbOXYpYDOWCjQRjf/3YWNwR0nfp/mZ4JC0zs4vIHu254ma9g6AHBex5HqcXiQ1RhVz5mSXqr8tAzz7xO62dv5PH7wGSbNwqEhq7ZXU7n+HO25V8jN4/Jz5OnOAg7F0H2nvmwyTE7pVE1jOUCsFtxVaBlkTLQxZwxHOGgBPpALoc2rdoCL1USMEZwaqlRr2sK4xFrZ+P6QrRTiBr26uje1otsSvXlDVUhieuOUsYfUOx5rCCE2CfSYmNp58NauvbSevTM0K9q3E45jDcQNkdGCMzq3AKqcF9zTtJ9fn6s6VHOnCrQ2I1a4r9gcPARpzPrvvTp1ZUjYC8r089w+ayAXJWZJHrMJPXU+HbJx/6f7Qud6IsO/Gh/4ceXHwNdO5D/yth+o/QfwDVdFi9PGw6eqqzBmpcsK3UspFfs12zgyvABDNlBFQAnvlfRu4NMMRnepnqNEbgSbeWXvaD74/fgmTQT+kOPLieGMWzBY6B6O2QCUBEmRkkEe2aLRI56LAI65Dghw0C+rkkzbu2wNY8dWcQgMjMpK/GBmcJ/rg+3Evs2t7smo3gnEUQ6H+9VC3vtS5HaZpIZ81gsHxsaHtNPHCUwRlBIfGzs18pS2qI4Lks6T/bYRt+ESFAoFfleD0jR6JsDRSwE8ziJADc993vvff1rfPAGrlMrp/VMz21K63Isc+xNVnA3ibCn/YfZ3kQIP78t3/8d56tsxSnKbIAYQOUl+i56ToobnzE1Iyp9dDlazLUumeJbecpE23g7cCkZ6Q6qODY/HzSQGwE7Gtk3TMhuFdpo4xRDmCxdUZIu9vPXbSXYbpVTILDBHnZtzQKNi8H6AAIHSgIQHidbm5h6fQkecGuKAJlktjKBsvwPSMBGen17qnkJ/mZIugiU55BxDNJSNwTPmSBMJ9O7vZQk0jLeCn80bM7Jwl1QJvJehLc+DczdWbUUSdOMtyIWJe/co6Ni5Clqoxw4kkf+n9m5czDpccYM3kpox/6j0X60P8APAp4eRahBGICflmA60P/lXxpNj4BnOL7niFAt7fmIHs2yMGyn330CE8r8dnqbbftJVMAONzz5Dg+ErYV3qa9nkvYADoLWuJZ/rLw9OMcgex0ZE/ODICi8wLL9IcexBuqdEEsSttrPBtrV2LFwoUC1aoQEqESIIadRfanrG0iaFwNWWD5HMvisCtJltqacx6JK3gfM00gJdvF8sWjymjsdw9mWoLd8HoWDbLBdFS+S8Y62Dl5RYKhlnMJH2Mce/6UVcgA9j6zlF4eiHlpyDgNBAuT5W2S5UKCPKPSGjYQ47SsaNm/Xjrn42H2RxqxyI6WDAS0Zih9zzjWfCjlw+SBa5bHM0p21JRxfjlpCgpKbk6gOpwjYHa1nQdpoB+A8TjH0QCwHEMMpmejOKYW3p4yAacZTDPlBuP2w++8f8+SFYA6Eb8hM3OMt51TgZFl4vTa9vlODG5rcv18yMDAaISczO/cKUSotfs+sjD+cBLi+p4BtlOwc0pTScxBngswDWtfMzGpRoX82nFUz7EPi+QZnse1nUhWB0dPYKnED7w+qHrmAuDpHyoxO4zZsf5Dqc2t29yH/u/V+9B/CVHRS5PpUMWqf7M/qAEmnC19z+8/9L+e04g4yn+W/hcQTjCo0ibvWOa2MJaqEpNiK7mO66WgDtmHMyYo5eGZzgwzosmCnZGuOuBZqCQWRoYc0DEC3xwPQGA5n7SvkU/d5EAcJb/VjbyUrp/rqfcHFU28ZuVpcD/7G7zw1d5Fg+8FbCGqDdyT42pvUHJFYmFNFXxn9AJZkTRvO00iloPZ6yJOjVXjuaaTqKkrRMkkZtlgrJX2MX7fSEx/7wyJhNJQwFlRHmafNwIkAhB7iT4NKMfrhBly4GWYIDWbFBp2ZUmalzJmu2ucG/Kgrb+8t+jkRNosg+b3QDlclxX+29fueN8Q1iBHvsrb4ESq0x7KYKbIcuqHZ1PiPidgKuU5VgBZMxBwiC3jAdrH57XzIEeJXlzYftYi9HN0m+vgVNvBamnNrXMmcyYF4/C3unu22+dZx+xAuoIpleB5OdcZhQ7F8v2VOTv/uka6W6nbVEaCNXMTqoz+KUtezkeD0Nb4yNwdhCE+MB/EMIY7LWTjeB5oIB4TAbsRmSuxLuOv549iPEsvVFVdZPMyd5epAIrnfmrfQOZ6hFSESNmuUI5bBu3MTr3Sx97q/UP/ld6uNu0I6FgW2G3Wh/43G/Wh/x/6/3v8f8+GOAGyrmjIxnS9DXxqrZzl2Hh2Zlv1dQYD2YGdvRH6apzzcG0d1BUmks9mEJrA3oICw2FyAEJokYB3Au52PtZfJplIJN/nlQmPvBHPtWxfptI34QyRFX5m5zgYJMW6ooQK753Dc3L99PLNdO+5mEHIahvvDStz4k5M4qMgeyJXreQQvtWMMObnBAj3caJUngUAniUi8V09+iCSEeNxwuvEAD4f48YeFtLF9TLSnU7G94dlaf0ccCP4+94479TIhCwyTnhliZpnc6xRR4z7JJq7HA6T8rbgzJzZ7zg/SKXWq66VKJv2GOurkqPPf1ukJ6P0/k4YgNiqKLEYIdL1/AcXgx5dZTRTJB7XC5DmhDyinvfqEUNvWKBlgKKFVAMwTGVO3o2O48CcLmvh7jdkOamvIdfyzL1CXqjUysVaQwgZjP7eFIsGnAyuIEsnnh2wP+3BsXcs37IMTq7PBJRjsXFmSsrdyYaTsk6wTpDdzhNdsh81elmJsO+ZG1+VKoKiVeLp2bmZ4IQSuPF2IEuDZC1BVZ1Qu+IcrLxFGa/7lgZaXUAs0+NZl0Nm/DqT4SGDg/2r3UU+9P9D/xvx/tB/yxpbKdSH/h/n17pN/tP9f3PbjGaPJWmOScJuBt5u50EsU6KKlGiLhYg+4BjMMfxFBa862xnXnaBeZl/fsTMdwDrbYNczSwXsG1DGnvC8RQIodx3X80XplwPEZ2CXpMPHP5Th1eKEXF/7jWcYMF4SHs9JWFYufm5EQl2UCknCdf5fG8/cLS1bOJPCKPjsmRMRq9pQI6YQAhhnWYRXIjuk/XIwj1hPJUfCu9inKJkT4QRhArnIpePaQDYps9a1t5IytK72TmhC3sBWVS8qGQWRihbXsgJ6/5T2bCZ00j8nUb5O0JiprI3kb61QZHqspriDpFayMXyde9nONCBUcWRoLCX38KxOimZwqCgDoiBTJLwSlnpYaotJEb7Pn94sRWqttvaFJUrRf5wPGoHqEMl3UNkJQTF4LgU9i1bIE4x0y1efnobzOCP9IhshRK1z3vFuJhC7F13V0HmtZPiesgEwZMjmKCtXX9hqdKsQ0KEEs6/D9O+Hbn3TvipzorNXXRa7UQjbxBM6JOmuDhMJ9KzePJW2VuP87ENrsOFE7xSXZtwv6wYdJfm2vXZ9xt9HSdNQ6jkSL+rZh/67TSo8eMhmctseAy4f+n/Y+Ae7/qH/XRmt+c6PrP/t3doM7pb34aAVNMAkMgWGlC074ARqr8oG7YHcVQaXwNbJgr3OAPZOQfCTLPA5DRfUMjZ86YRA0flMQtAkeEQe/qrdvmSneH2rPAC4BcFwEQCW6KAWZfKsZEhHCOANUgecGr9fd3srnoiZjJLlqGVniFaSAO1yu7xNaYucWKqVcHEOBtxFEEAK6prrfT+a+Vmaxldnak+Y9RARFHE1cpf1Y7X0zebUzuuUeD0yLrx8kDcvgfTyQZBSvC+qZdE42+n9P3iJKZ6bFzuJAR6K9dWZPci59O1sOU6ZWr/F+iAE8fd//Hd+31lrp+FKDyHa7/WelZTMHlcAOy2COxO8jCy/mgGmCEsFvheA2w/KgzBkFkZkrzn6/k6VKdyfIN5noncDqJSp57FpbLpFSSCscNKw/kcGptYQpilOTH1miHpyIB6pNpuIUd5KnnoXMT5vPHNkGSsTB9/XmRzWcypLsN8uZ5Y8w6TzRYqY5EKolnDIIvY6zGN8jJ3UTN2VlITHODoHwgk6+HfQjzU/ZTJ3xeRFWSWLuDtZewF4fXx+Lz9nMTdBUJi0Zl97LeuH/ocINGRl9ueob/nQ/1ANt7UpVh/6X33Bh/7XZiz3bDiy8cLG8L9+NtR9uEAlWjsLcKaA5vnV0085cQJxoEyzDEzUxMnLEd1OHxJXv22fEtcowo9OGeeY9Y1K9hAgsmwCnhEDNwLhrZIVYRdYzjUxcrJGFe90sQyEkYnyvhelzPRUvNiTY1LpGcyoVk7BLgHjM5iOkjQH0vFoL2tLctPP04gz5NrbvGyluGqF4MZ6eivl9Ulkj7JKJEvAIsODqqCkc0YAgGv0+xwvyBhJp+ZBoulla05QSGKshXWSP2UYY41XA4leeqf1NN1oBAh8gPuTRqt3vCskct/OCKPNcROfzBJVYiRZVAnimYmFhG+Ms15OKiDWIu+0riYBCZIVzWbOlz8WGLZwRntIdF4WiMKgUDqyDrxJx88DQrE++nybhgN7VcPkeu0EodYm4LkAbjgnkS0K82VOBIVoBYwlwvewT1hdKwEBI488LFKmqyZYTt9Theun26hkZvAw8p2UWa0Vo5bOePJ7ptgXYG4GLPCHlGKMfvI+ek8SjP36Kgi25kh1te4gnDMNYWjk+t2KffCcqL+jZ4uN3ZedV6o84NlLcfdPTKYYPZpeGlrW6iwHC8mogBZbl/UPhfhAGc0HuPiQ5+6ofSFM1bljT7guJXvYwlXjd2ath0zswe0/9J9yBnnePgvGw4w0V/9D/9mk4EP/a1T/Q//l335I/z/HGa21cfq5ZE71haAIGjfA5SWO1QV9+lqaIKC8R1UaAtRWXgTMRNwRUrV9Qt5f2AaZnUaY6Adr+ZtTHz//obsAWAt4C94nUUo/6xSKgNN8kpfLoazuKBt0AmTEwEkC+Fq5nwColbm1MScBGUlHDpggmu/jUZBR8zMyRFyD1EfuackW4chABMa5aVv2tEBoyX3bk14uCTiEEsMoIxuyiv5Kj3KezUvtnEBZu+y9H7qO7ygiUZnJBquW/EWzeS/Mz8vcSPCMPlzJXyNbpbQN5BHYFjqzSI+/oRZ4Egp0dCQA1lybxBKddr6h4cTxXg14i3goso2xHFHRJBAEmAXQtrIoki0Rt8nginRV8Ng4Cw4LieAZYXBIGYTNDGGpttQzHHwjcxHZDeZdauYg7xuw/xv71TvHouoYOYg/ffPqaGMvBeRpgHJ+Hvfisz7vGJRFa8RFaJsR+QG58JfIupz4cIztY6VKANhrlrH3dkEB74iENALlmUnuMWXFa6uViJbc+LmxMFZL2QrpLqQDpQ5J9FsmyLNU4CJRGpFxPRsgbrv3A85Lom1kKmXPvlvjY2tUd0D+8tvc3A/9VzqdKkdmK/Ty/9o7l1xJbh2IPu9/fd6Fxx62jRQZEYdMdcMwYLxJzvp2VaUkipQi+MtjN2sfrMqf/cfz+9l/2/N01rA1sM9fXfaf/StkMS+r//r+hwOQA7PbWxEBAcI6oAVib8eBvdMLyA9gqrMa530iGfFou6C0T/sZ7WiC04BanvudIiaHJutItNZ49p+0PADgRQpFOAjM9W+mrPm5uIvmb0LK3lGFp/bleWmrZExqIBQUgRWBIu0LjtqZSc+3tnwLt01gfxywl01122lFbQjkVcyvNfuZRRzkwD5zkDD60q3VoAwD83w+Oe2eoZjRraSlnCiNnLN2EIdYawzuwyG6t7RP7HvJbI6fPW9dOdi170/rT8tVbO7SHbHGThROMpcO591E0UmvfeHQIrMipMnSOZrx+x9//nXaCLYQZQxyQhjoCajFMozPzqLtAc0qSS7sGTeIrgedfW5wbi93C9akp0ElowZMxdkeNOyx51V45e3ZEPbTM6h/IiESjjAPdLsOu/7xeD8P2RJzdmH4Xju6Pes89zwYvdrRMU4axrPX5GMCJEgea0ch+rOR4qRozzP/NW+haxIGAUAb5PB6vyMzUiUSsvHbNnbJ6cRrLheSooNHnzpMyyii1i8iZ7ENebQXD4FLkepjPinZ8XHDg+eMLbCiswd/S9ltV1L+XhP3bJCd4WToKIMF9wZQlQbxW8/lyVcuUE7CVleCvED9+Wf/Y1+tk3AEfPYvn93bG102OnXJUTEZB8g+beez/9k37LP/Ain/9/sfdx7B4QySJwoDRINUNJKVBmt6LgjQICW6iwUcB9KKk3EShAkGNiimp3xkvAjM9OFmsNp44nnqAPwCq/Kww6nZtGS4dwMXp5wCVls+haTwW5GctV5HL9brJPqCFdB/PlX3PKPbp7bqRty8HxirhRQ5zkheVeAEVNdWwVFmx2zvOaINtVK8k2bXEjXu39GPIhyVKBii89Y/Ra9ct9RzJVUhuannLt3cun8+X+T0J+lzB1r4u5SpxqFDscld76tSADXSJGcl85JL6+Wt8xwIp/Rh+Jl/f9LbGnEJePlvYUwQGoNiAScY8NUbbyiRjmpSjtehJrX3RIQkg35r8jPPsJz5aZl7jwosbSegh4KeRyH96hBPmKPJE9s9Is0rhhs6MGs3LsX7DYANRj2HDG4y6rlMI4SYESQl66gfVu0I/VRpEGCSbeAvo1ZkY5GOXxb9y9j7KqAuIcQaAvLrYnnv+djcIgE6fgYBo0AA6I9oV+cUkaZIBnu0u5qtaJnrn0aUrPVxe4fWs7wXKAESPjz6vH6/9ZCgUoBcbqotk6Ef0oElsM/+E1k7olnnouX/kp9SYF87VOcS9vGVQgiC/Nn/7Gi0ddbATfLs4+2z/z4D4cT57F/3zrzfbjqVu+XX9n+5UhDdWY5jd6kKSAvQfz+pgGdFjw6gpsOMtck4e/Z9Z4Dem/9DhTSXu6ScnXeSUfzhXcshXiJvPFOJWtodBVAn16R36WR8/0ZQGlEzp10pFSxYIiQkaWINl0yUHXEjloPIm05VO+5LxKEwQiIlIaRvwql74ozJKN4THVINClIcRxSqweaNdEwMqhlPXEKiwqDCjsSctQwHbHS1ZMXGCwlCCCsNkgVip70wKV4NE4RJ1KKBe1ei+et/P/QezxZ6kZSSpV3tjR1r1mgd3+BxEDg4xWlvlNWp6SGQ33UmRJT2lKO1HflJ2UrqOwZoU11FGzcwxQ2eVhGww7e53QZQBI8x8dlAUfNZYTsPuoGoIjfMxb10uSp7ajJgpMo5L7AP4G0FVfRp9cvX3DozSx3Fdeb0mXQBxbeC/71BzSMV1n6lL3qeIBNefzxwLBpL165plNKdayv03qd54ZC4NgVBXRCNpsBjRTSkZ3XnzwYOQ2c3CaESwkvNOZkosm5IOnwhfY467Y48l9oh6uzM2Mjepq4KBE/2dyNaUh568LCnjl5J/nY0ALDr8NbzBf5XutZpaLJ8CZ/9R291LFDtvJ+tzDzP9kH42X+6iqaQm+fqZ/9y+cbx11r02X/uSUTqX2Dj39z/vjcagMU/kqzpvpfqLpngXaBaYD1HNkDHOo7V/Q08pcFhVlSAMV50jb3H4d85m+QIJejEwvqcL0Bq9GNMcsb60WAaDk4ROZE6he+YaiaQzruEALct3iX/u5205WfVJ1KAfIgBBaQNkp+zZBbRbznN6MNyjosoKaQgeTWR5foLpAfAp+X4T6Jh/X07iLkX13HTRa50LfIQwRrksXfxRR6ku5KrBOKOb3IT1xdMMiRnyKLqf1Ij/nx/vCi1jWKQyPHMZLA47a0HDaFrmeoFuM8YaJGt+Z339MTv3//NaIo92/FmRmEn4tkXOAHn2WZIeoNdiS1GVQZnh4QPGl16F3J18aQnrAvvQ3v6gRnHm9Q9T1jhz8Cdd3qdpgV+F1B9dYVL9OUm0xch0LyR7lfru+3DVHZ+5+ZtHtM3yJUzCG6Ci0xukbV3NA4kjU0JXvsqh8k/I3V7nNIZxGw20TH5nvqY3/nkHFsr3XZIe+2lZSAd9HuLQMo2KeW+IVpYkSjoN+qglprNblf6cO0f5x7/SY7Qz/4TPR628dl/O4M/+//s/7v/X1BaZzYcpQXgwVhEfpSWA284z5q4S1PCKyBZV5i+AcD8vAcIWQI3qJ8UKf5+ESUyKYxzHA2rDDjA1l6byowZJK8+y2MTbeNa/KwNzDPVuoPt2Exal/Gk0t1ybTf1K/BIUhcn+gLpfGXL+ajuRmGyU7vTcyKcGClcS/hcW/YxoLxSwPJc7dMhA0Aeh6R4bT2JBsUc0p3hRHwt/03kSjdPaY0ggKNaLZfRye7RMRUXUuo1wwAAADBJREFURi5jfUzPazyqKE3qcgrTcNwQv1lzI2EzJTNEsb47dEfkB/jHBI7NE5pq/g1+lqjwK4ZQogAAAABJRU5ErkJggg==`; return { status: 200, - data: Buffer.from(pngBase64, "base64"), - headers: { "content-type": "image/png" }, + data: Buffer.from(pngBase64, 'base64'), + headers: { 'content-type': 'image/png' }, }; }, async post() { @@ -18,38 +18,37 @@ const httpClient: HttpClient = { }, }; -describe("The CustomCredentialSvg", () => { - - it("can render credential in svg format", async () => { +describe('The CustomCredentialSvg', () => { + it('can render credential in svg format', async () => { const renderer = CustomCredentialSvg({ httpClient }); const result = await renderer.renderCustomSvgTemplate({ signedClaims: { - "family_name": "Doe", - "given_name": "John", - "birth_date": "1990-10-15", - "issuing_authority": "PID:00001", - "issuing_country": "GR", - "document_number": "12313213", - "issuance_date": "2025-03-02", - "expiry_date": "2035-04-21", - "age_over_21": true + family_name: 'Doe', + given_name: 'John', + birth_date: '1990-10-15', + issuing_authority: 'PID:00001', + issuing_country: 'GR', + document_number: '12313213', + issuance_date: '2025-03-02', + expiry_date: '2035-04-21', + age_over_21: true, }, displayConfig: { - "name": "PID - MDOC", - "description": "Person Identification Data (PID) VC in mso_mdoc format", - "background_image": { - "uri": "https://issuer.wwwallet.org/images/background-image.png" + name: 'PID - MDOC', + description: 'Person Identification Data (PID) VC in mso_mdoc format', + background_image: { + uri: 'https://issuer.wwwallet.org/images/background-image.png', }, - "background_color": "#4CC3DD", - "text_color": "#FFFFFF", - "locale": "en-US" - } + background_color: '#4CC3DD', + text_color: '#FFFFFF', + locale: 'en-US', + }, }); - console.log('result!', result) + console.log('result!', result); assert(result != null); assert(typeof result === 'string'); - convertDataUriToImage(result, path.join(__dirname, "../../output/openID4VCIRenderingResult")); + convertDataUriToImage(result, path.join(__dirname, '../../output/openID4VCIRenderingResult')); }); -}) +}); diff --git a/src/functions/CustomCredentialSvg.ts b/src/functions/CustomCredentialSvg.ts index 22494d5..4f9e21b 100644 --- a/src/functions/CustomCredentialSvg.ts +++ b/src/functions/CustomCredentialSvg.ts @@ -1,15 +1,13 @@ -import { HttpClient, CustomCredentialSvgI } from "../interfaces"; -import { CredentialClaims } from "../types"; -import { escapeSVG } from "../utils/escapeSVG"; -import { formatDate } from "./formatDate"; +import { HttpClient, CustomCredentialSvgI } from '../interfaces'; +import { CredentialClaims } from '../types'; +import { escapeSVG } from '../utils/escapeSVG'; +import { formatDate } from './formatDate'; export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCredentialSvgI { - - const defaultBackgroundColor = "#D3D3D3"; - const defaultTextColor = "#000000"; - const defaultName = "Credential"; - const svgTemplate = - ` @@ -18,8 +16,7 @@ export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCre {{name}} {{description}} {{expiry_date}} - ` - + `; function formatExpiryDate(signedClaims: CredentialClaims): string { if (signedClaims.expiry_date) { @@ -28,27 +25,31 @@ export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCre const expiryDateISO = new Date(Number(signedClaims.exp) * 1000).toISOString(); return formatDate(expiryDateISO, 'date'); } else { - return ""; + return ''; } } - async function getBase64Image(url: string) { if (!url) return null; try { - const response = await args.httpClient.get(url, {}, { responseType: 'arraybuffer', useCache: true }); - const mimeType = String(response.headers["content-type"] || "application/octet-stream"); - const isBrowser = typeof window !== "undefined"; + const response = await args.httpClient.get( + url, + {}, + { responseType: 'arraybuffer', useCache: true }, + ); + const mimeType = String(response.headers['content-type'] || 'application/octet-stream'); + const isBrowser = typeof window !== 'undefined'; if (isBrowser) { - if (typeof response.data === "string" && response.data.startsWith("data:")) { + if (typeof response.data === 'string' && response.data.startsWith('data:')) { return response.data; } - const blob = typeof response.data === "string" - ? await fetch(response.data).then((res) => res.blob()) - : new Blob([response.data as BlobPart], { type: mimeType }); + const blob = + typeof response.data === 'string' + ? await fetch(response.data).then((res) => res.blob()) + : new Blob([response.data as BlobPart], { type: mimeType }); return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -58,24 +59,29 @@ export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCre }); } else { const blob = response.data as any; - const base64 = Buffer.from(blob, "binary").toString("base64"); + const base64 = Buffer.from(blob, 'binary').toString('base64'); return `data:${mimeType};base64,${base64}`; } } catch (error) { - console.error("Failed to load image", url, error); + console.error('Failed to load image', url, error); return null; } } - - const renderCustomSvgTemplate = async ({ signedClaims, displayConfig }: { signedClaims: CredentialClaims, displayConfig: any }) => { - const name = displayConfig?.name ? escapeSVG(displayConfig?.name) : defaultName; - const description = displayConfig?.description ? escapeSVG(displayConfig?.description) : ""; + const renderCustomSvgTemplate = async ({ + signedClaims, + displayConfig, + }: { + signedClaims: CredentialClaims; + displayConfig: any; + }) => { + const name = displayConfig?.name ? escapeSVG(displayConfig?.name) : defaultName; + const description = displayConfig?.description ? escapeSVG(displayConfig?.description) : ''; const backgroundColor = displayConfig.background_color || defaultBackgroundColor; const textColor = displayConfig.text_color || defaultTextColor; - const backgroundImageBase64 = displayConfig?.background_image?.uri ? - displayConfig?.background_image?.uri?.startsWith("data:") ? - displayConfig?.background_image.uri + const backgroundImageBase64 = displayConfig?.background_image?.uri + ? displayConfig?.background_image?.uri?.startsWith('data:') + ? displayConfig?.background_image.uri : await getBase64Image(displayConfig?.background_image?.uri) : ''; @@ -88,13 +94,13 @@ export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCre /{{backgroundImageBase64}}/g, backgroundImageBase64 ? `` - : '' + : '', ) .replace( /{{logoBase64}}/g, logoBase64 ? `${displayConfig.logoAltText || 'Logo'}` - : '' + : '', ) .replace(/{{name}}/g, name) .replace(/{{textColor}}/g, textColor) @@ -107,5 +113,5 @@ export function CustomCredentialSvg(args: { httpClient: HttpClient }): CustomCre return { renderCustomSvgTemplate, - } + }; } diff --git a/src/functions/convertDataUriToImage.ts b/src/functions/convertDataUriToImage.ts index 1820897..5f04322 100644 --- a/src/functions/convertDataUriToImage.ts +++ b/src/functions/convertDataUriToImage.ts @@ -1,35 +1,37 @@ -import fs from "fs"; +import fs from 'fs'; const mimeToExt: Record = { - "image/png": ".png", - "image/jpeg": ".jpg", - "image/gif": ".gif", - "image/svg+xml": ".svg", - "image/webp": ".webp", - "image/bmp": ".bmp", - "image/x-icon": ".ico", - "image/tiff": ".tiff", - "image/heif": ".heif" + 'image/png': '.png', + 'image/jpeg': '.jpg', + 'image/gif': '.gif', + 'image/svg+xml': '.svg', + 'image/webp': '.webp', + 'image/bmp': '.bmp', + 'image/x-icon': '.ico', + 'image/tiff': '.tiff', + 'image/heif': '.heif', }; -export function convertDataUriToImage(dataUri: string, outputFileName = "output-image") { - const matches = dataUri.match(/^data:([a-zA-Z+\/]+);(?:charset=utf-8;)?(base64,|utf8,|utf-8,)?(.+)$/); +export function convertDataUriToImage(dataUri: string, outputFileName = 'output-image') { + const matches = dataUri.match( + /^data:([a-zA-Z+\/]+);(?:charset=utf-8;)?(base64,|utf8,|utf-8,)?(.+)$/, + ); if (!matches) { - throw new Error("Invalid Data URI"); + throw new Error('Invalid Data URI'); } const mimeType = matches[1]; const encoding = matches[2]; const data = matches[3]; - const fileExtension = mimeToExt[mimeType] || ".bin"; + const fileExtension = mimeToExt[mimeType] || '.bin'; let buffer: Buffer; - if (encoding === "base64") { - buffer = Buffer.from(data, "base64"); + if (encoding === 'base64') { + buffer = Buffer.from(data, 'base64'); } else { - buffer = Buffer.from(decodeURIComponent(data), "utf-8"); + buffer = Buffer.from(decodeURIComponent(data), 'utf-8'); } const outputPath = `${outputFileName}${fileExtension}`; diff --git a/src/functions/convertOpenid4vciToSdjwtvcClaims.test.ts b/src/functions/convertOpenid4vciToSdjwtvcClaims.test.ts index 7517249..7045bd5 100644 --- a/src/functions/convertOpenid4vciToSdjwtvcClaims.test.ts +++ b/src/functions/convertOpenid4vciToSdjwtvcClaims.test.ts @@ -1,22 +1,22 @@ -import { describe, it, expect } from "vitest"; -import { convertOpenid4vciToSdjwtvcClaims } from "./convertOpenid4vciToSdjwtvcClaims"; -import type { OpenIdClaim } from "../schemas"; +import { describe, it, expect } from 'vitest'; +import { convertOpenid4vciToSdjwtvcClaims } from './convertOpenid4vciToSdjwtvcClaims'; +import type { OpenIdClaim } from '../schemas'; // Simple deep clone for tests const deepClone = (v: T): T => JSON.parse(JSON.stringify(v)); -describe("convertOpenid4vciToSdjwtvcClaims", () => { - it("returns [] for undefined, null, or empty input", () => { +describe('convertOpenid4vciToSdjwtvcClaims', () => { + it('returns [] for undefined, null, or empty input', () => { expect(convertOpenid4vciToSdjwtvcClaims(undefined)).toEqual([]); expect(convertOpenid4vciToSdjwtvcClaims(null)).toEqual([]); expect(convertOpenid4vciToSdjwtvcClaims([])).toEqual([]); }); - it("maps display[].name → display[].label and trims locale/name", () => { + it('maps display[].name → display[].label and trims locale/name', () => { const input = [ { - path: ["person", "name", "family"], - display: [{ locale: " en-US ", name: " Last name " }], + path: ['person', 'name', 'family'], + display: [{ locale: ' en-US ', name: ' Last name ' }], } as unknown as OpenIdClaim, ]; @@ -24,29 +24,29 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { expect(out).toEqual([ { - path: ["person", "name", "family"], - display: [{ locale: "en-US", label: "Last name" }], + path: ['person', 'name', 'family'], + display: [{ locale: 'en-US', label: 'Last name' }], }, ]); }); - it("filters invalid display entries and omits display when none survive", () => { + it('filters invalid display entries and omits display when none survive', () => { const input = [ { - path: ["id"], + path: ['id'], display: [ - { locale: "", name: "no locale" }, // invalid - { locale: "en", name: "" }, // invalid - { locale: "fr-FR" } as any, // invalid (no name) - {} as any, // invalid - { locale: " el-GR ", name: " Ταυτότητα " } // valid after trim + { locale: '', name: 'no locale' }, // invalid + { locale: 'en', name: '' }, // invalid + { locale: 'fr-FR' } as any, // invalid (no name) + {} as any, // invalid + { locale: ' el-GR ', name: ' Ταυτότητα ' }, // valid after trim ], } as unknown as OpenIdClaim, { - path: ["noDisplayAfterFilter"], + path: ['noDisplayAfterFilter'], display: [ - { locale: "", name: "" }, // invalid - { locale: " ", name: " " }, // invalid + { locale: '', name: '' }, // invalid + { locale: ' ', name: ' ' }, // invalid ], } as unknown as OpenIdClaim, ]; @@ -55,32 +55,32 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { // First one keeps only the valid, trimmed Greek entry expect(out[0]).toEqual({ - path: ["id"], - display: [{ locale: "el-GR", label: "Ταυτότητα" }], + path: ['id'], + display: [{ locale: 'el-GR', label: 'Ταυτότητα' }], }); // Second one must NOT include a display key at all - expect(out[1]).toEqual({ path: ["noDisplayAfterFilter"] }); - expect(Object.prototype.hasOwnProperty.call(out[1], "display")).toBe(false); + expect(out[1]).toEqual({ path: ['noDisplayAfterFilter'] }); + expect(Object.prototype.hasOwnProperty.call(out[1], 'display')).toBe(false); }); - it("preserves claim order", () => { + it('preserves claim order', () => { const input = [ - { path: ["a"], display: [{ locale: "en", name: "A" }] }, - { path: ["b"] }, - { path: ["c"], display: [{ locale: "en-US", name: "C" }] }, - ].map(x => x as unknown as OpenIdClaim); + { path: ['a'], display: [{ locale: 'en', name: 'A' }] }, + { path: ['b'] }, + { path: ['c'], display: [{ locale: 'en-US', name: 'C' }] }, + ].map((x) => x as unknown as OpenIdClaim); const out = convertOpenid4vciToSdjwtvcClaims(input); - expect(out.map(e => e.path.join("."))).toEqual(["a", "b", "c"]); + expect(out.map((e) => e.path.join('.'))).toEqual(['a', 'b', 'c']); }); - it("does not mutate the input array or its objects", () => { + it('does not mutate the input array or its objects', () => { const original = [ { - path: ["immutable"], - display: [{ locale: "en-US", name: "Immutable" }], + path: ['immutable'], + display: [{ locale: 'en-US', name: 'Immutable' }], } as unknown as OpenIdClaim, ]; const input = deepClone(original); @@ -93,47 +93,45 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { expect(input).toEqual(original); // structure preserved }); - it("passes through path as-is (allows string | null | non-negative int per schema)", () => { + it('passes through path as-is (allows string | null | non-negative int per schema)', () => { const input = [ - { path: ["person", "age"] } as unknown as OpenIdClaim, - { path: ["address", null, "street"] } as unknown as OpenIdClaim, // includes null - { path: ["items", 0, "name"] } as unknown as OpenIdClaim, // includes non-negative int + { path: ['person', 'age'] } as unknown as OpenIdClaim, + { path: ['address', null, 'street'] } as unknown as OpenIdClaim, // includes null + { path: ['items', 0, 'name'] } as unknown as OpenIdClaim, // includes non-negative int ]; const out = convertOpenid4vciToSdjwtvcClaims(input); expect(out).toEqual([ - { path: ["person", "age"] }, - { path: ["address", null, "street"] }, - { path: ["items", 0, "name"] }, + { path: ['person', 'age'] }, + { path: ['address', null, 'street'] }, + { path: ['items', 0, 'name'] }, ]); }); - it("ignores display entirely when input claim has no display", () => { - const input = [ - { path: ["noDisplay"] } as unknown as OpenIdClaim, - ]; + it('ignores display entirely when input claim has no display', () => { + const input = [{ path: ['noDisplay'] } as unknown as OpenIdClaim]; const out = convertOpenid4vciToSdjwtvcClaims(input); - expect(out).toEqual([{ path: ["noDisplay"] }]); - expect(Object.prototype.hasOwnProperty.call(out[0], "display")).toBe(false); + expect(out).toEqual([{ path: ['noDisplay'] }]); + expect(Object.prototype.hasOwnProperty.call(out[0], 'display')).toBe(false); }); - it("passes `mandatory` if present (true OR false), omits only when missing", () => { + it('passes `mandatory` if present (true OR false), omits only when missing', () => { const input = [ { - path: ["must"], - display: [{ locale: "en", name: "Must" }], + path: ['must'], + display: [{ locale: 'en', name: 'Must' }], mandatory: true, } as unknown as OpenIdClaim, { - path: ["optional"], - display: [{ locale: "en", name: "Optional" }], + path: ['optional'], + display: [{ locale: 'en', name: 'Optional' }], mandatory: false, } as unknown as OpenIdClaim, { - path: ["unset"], - display: [{ locale: "en", name: "Unset" }], + path: ['unset'], + display: [{ locale: 'en', name: 'Unset' }], // no mandatory field } as unknown as OpenIdClaim, ]; @@ -142,34 +140,34 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { // TRUE: must be included expect(out[0]).toEqual({ - path: ["must"], - display: [{ locale: "en", label: "Must" }], + path: ['must'], + display: [{ locale: 'en', label: 'Must' }], mandatory: true, }); // FALSE: included because explicitly present expect(out[1]).toEqual({ - path: ["optional"], - display: [{ locale: "en", label: "Optional" }], + path: ['optional'], + display: [{ locale: 'en', label: 'Optional' }], mandatory: false, }); // MISSING: omitted entirely expect(out[2]).toEqual({ - path: ["unset"], - display: [{ locale: "en", label: "Unset" }], + path: ['unset'], + display: [{ locale: 'en', label: 'Unset' }], }); - expect("mandatory" in out[2]).toBe(false); + expect('mandatory' in out[2]).toBe(false); }); - it("handles mixed good/bad display entries and preserves valid ones", () => { + it('handles mixed good/bad display entries and preserves valid ones', () => { const input = [ { - path: ["mixed"], + path: ['mixed'], display: [ - { locale: " en ", name: " Title " }, // valid - { locale: " ", name: "X" }, // invalid (blank locale) - { locale: "fr-FR", name: " " }, // invalid (blank name) + { locale: ' en ', name: ' Title ' }, // valid + { locale: ' ', name: 'X' }, // invalid (blank locale) + { locale: 'fr-FR', name: ' ' }, // invalid (blank name) ], } as unknown as OpenIdClaim, ]; @@ -177,16 +175,16 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { expect(out).toEqual([ { - path: ["mixed"], - display: [{ locale: "en", label: "Title" }], + path: ['mixed'], + display: [{ locale: 'en', label: 'Title' }], }, ]); }); - it("works with many claims and ensures each output entry is a plain object with path", () => { + it('works with many claims and ensures each output entry is a plain object with path', () => { const input: OpenIdClaim[] = Array.from({ length: 10 }, (_, i) => ({ - path: ["root", `key${i}`], - display: i % 2 === 0 ? [{ locale: "en-US", name: `Key ${i}` }] : undefined, + path: ['root', `key${i}`], + display: i % 2 === 0 ? [{ locale: 'en-US', name: `Key ${i}` }] : undefined, // toggle mandatory presence/values for variety ...(i === 3 ? { mandatory: true } : {}), ...(i === 4 ? { mandatory: false } : {}), @@ -197,16 +195,16 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { expect(out).toHaveLength(10); out.forEach((entry, i) => { expect(Array.isArray(entry.path)).toBe(true); - expect(entry.path).toEqual(["root", `key${i}`]); + expect(entry.path).toEqual(['root', `key${i}`]); if (i % 2 === 0) { expect(entry).toEqual( expect.objectContaining({ - display: [{ locale: "en-US", label: `Key ${i}` }], - }) + display: [{ locale: 'en-US', label: `Key ${i}` }], + }), ); } else { - expect(Object.prototype.hasOwnProperty.call(entry, "display")).toBe(false); + expect(Object.prototype.hasOwnProperty.call(entry, 'display')).toBe(false); } if (i === 3) { @@ -214,7 +212,7 @@ describe("convertOpenid4vciToSdjwtvcClaims", () => { } else if (i === 4) { expect(entry).toEqual(expect.objectContaining({ mandatory: false })); } else { - expect("mandatory" in entry).toBe(false); + expect('mandatory' in entry).toBe(false); } }); }); diff --git a/src/functions/convertOpenid4vciToSdjwtvcClaims.ts b/src/functions/convertOpenid4vciToSdjwtvcClaims.ts index fc7e156..6ab9e14 100644 --- a/src/functions/convertOpenid4vciToSdjwtvcClaims.ts +++ b/src/functions/convertOpenid4vciToSdjwtvcClaims.ts @@ -1,5 +1,5 @@ -import { OpenIdClaim } from "../schemas"; -import { ClaimMetadataEntry, ClaimDisplayEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; +import { OpenIdClaim } from '../schemas'; +import { ClaimMetadataEntry, ClaimDisplayEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; export function convertOpenid4vciToSdjwtvcClaims( metadataClaims?: OpenIdClaim[] | null, @@ -11,21 +11,20 @@ export function convertOpenid4vciToSdjwtvcClaims( const normalizedDisplay: ClaimDisplayEntry[] = (claim.display ?? []) .filter( (d): d is { locale: string; name: string } => - typeof d?.locale === "string" && + typeof d?.locale === 'string' && d.locale.trim().length > 0 && - typeof d?.name === "string" && - d.name.trim().length > 0 + typeof d?.name === 'string' && + d.name.trim().length > 0, ) - .map(d => ({ + .map((d) => ({ locale: d.locale.trim(), label: d.name.trim(), })); // preserve mandatory if present (true OR false), skip if undefined - const mandatory = - Object.prototype.hasOwnProperty.call(claim, "mandatory") - ? (claim as any).mandatory - : undefined; + const mandatory = Object.prototype.hasOwnProperty.call(claim, 'mandatory') + ? (claim as any).mandatory + : undefined; return { path: claim.path, diff --git a/src/functions/convertSdjwtvcToOpenid4vciClaims.test.ts b/src/functions/convertSdjwtvcToOpenid4vciClaims.test.ts index 4bcd3b2..80b0226 100644 --- a/src/functions/convertSdjwtvcToOpenid4vciClaims.test.ts +++ b/src/functions/convertSdjwtvcToOpenid4vciClaims.test.ts @@ -1,22 +1,22 @@ -import { describe, it, expect } from "vitest"; -import { convertSdjwtvcToOpenid4vciClaims } from "./convertSdjwtvcToOpenid4vciClaims"; -import type { ClaimMetadataEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; +import { describe, it, expect } from 'vitest'; +import { convertSdjwtvcToOpenid4vciClaims } from './convertSdjwtvcToOpenid4vciClaims'; +import type { ClaimMetadataEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; // deep clone helper const deepClone = (v: T): T => JSON.parse(JSON.stringify(v)); -describe("convertSdjwtvcToOpenid4vciClaims", () => { - it("returns [] for undefined, null, or empty input", () => { +describe('convertSdjwtvcToOpenid4vciClaims', () => { + it('returns [] for undefined, null, or empty input', () => { expect(convertSdjwtvcToOpenid4vciClaims(undefined)).toEqual([]); expect(convertSdjwtvcToOpenid4vciClaims(null)).toEqual([]); expect(convertSdjwtvcToOpenid4vciClaims([])).toEqual([]); }); - it("maps display[].label → display[].name and trims", () => { + it('maps display[].label → display[].name and trims', () => { const input: ClaimMetadataEntry[] = [ { - path: ["person", "name", "family"], - display: [{ locale: " en-US ", label: " Last name " }], + path: ['person', 'name', 'family'], + display: [{ locale: ' en-US ', label: ' Last name ' }], }, ]; @@ -24,73 +24,70 @@ describe("convertSdjwtvcToOpenid4vciClaims", () => { expect(out).toEqual([ { - path: ["person", "name", "family"], - display: [{ locale: "en-US", name: "Last name" }], + path: ['person', 'name', 'family'], + display: [{ locale: 'en-US', name: 'Last name' }], }, ]); }); - it("omits display if all entries invalid", () => { + it('omits display if all entries invalid', () => { const input: ClaimMetadataEntry[] = [ { - path: ["id"], - display: [ - { locale: "", label: "bad" } as any, - { locale: "en", label: "" } as any, - ], + path: ['id'], + display: [{ locale: '', label: 'bad' } as any, { locale: 'en', label: '' } as any], }, ]; const out = convertSdjwtvcToOpenid4vciClaims(input); - expect(out).toEqual([{ path: ["id"] }]); - expect("display" in out[0]).toBe(false); + expect(out).toEqual([{ path: ['id'] }]); + expect('display' in out[0]).toBe(false); }); - it("passes mandatory if present (true OR false), omits only when missing", () => { + it('passes mandatory if present (true OR false), omits only when missing', () => { const input: ClaimMetadataEntry[] = [ { - path: ["must"], - display: [{ locale: "en", label: "Must" }], + path: ['must'], + display: [{ locale: 'en', label: 'Must' }], mandatory: true, }, { - path: ["optional"], - display: [{ locale: "en", label: "Optional" }], + path: ['optional'], + display: [{ locale: 'en', label: 'Optional' }], mandatory: false, }, { - path: ["unset"], - display: [{ locale: "en", label: "Unset" }], + path: ['unset'], + display: [{ locale: 'en', label: 'Unset' }], }, ]; const out = convertSdjwtvcToOpenid4vciClaims(input); expect(out[0]).toEqual({ - path: ["must"], - display: [{ locale: "en", name: "Must" }], + path: ['must'], + display: [{ locale: 'en', name: 'Must' }], mandatory: true, }); expect(out[1]).toEqual({ - path: ["optional"], - display: [{ locale: "en", name: "Optional" }], + path: ['optional'], + display: [{ locale: 'en', name: 'Optional' }], mandatory: false, }); expect(out[2]).toEqual({ - path: ["unset"], - display: [{ locale: "en", name: "Unset" }], + path: ['unset'], + display: [{ locale: 'en', name: 'Unset' }], }); - expect("mandatory" in out[2]).toBe(false); + expect('mandatory' in out[2]).toBe(false); }); - it("does not mutate the input", () => { + it('does not mutate the input', () => { const input: ClaimMetadataEntry[] = [ { - path: ["immutable"], - display: [{ locale: "en-US", label: "Immutable" }], + path: ['immutable'], + display: [{ locale: 'en-US', label: 'Immutable' }], mandatory: true, }, ]; @@ -100,15 +97,15 @@ describe("convertSdjwtvcToOpenid4vciClaims", () => { expect(input).toEqual(clone); }); - it("handles mixed path types (string|null|number)", () => { + it('handles mixed path types (string|null|number)', () => { const input: ClaimMetadataEntry[] = [ - { path: ["items", 0, "value"] }, - { path: ["address", null, "city"] }, + { path: ['items', 0, 'value'] }, + { path: ['address', null, 'city'] }, ]; expect(convertSdjwtvcToOpenid4vciClaims(input)).toEqual([ - { path: ["items", 0, "value"] }, - { path: ["address", null, "city"] }, + { path: ['items', 0, 'value'] }, + { path: ['address', null, 'city'] }, ]); }); }); diff --git a/src/functions/convertSdjwtvcToOpenid4vciClaims.ts b/src/functions/convertSdjwtvcToOpenid4vciClaims.ts index b5a29b4..279aea1 100644 --- a/src/functions/convertSdjwtvcToOpenid4vciClaims.ts +++ b/src/functions/convertSdjwtvcToOpenid4vciClaims.ts @@ -1,8 +1,5 @@ -import { - ClaimMetadataEntry, - ClaimDisplayEntry, -} from "../schemas/SdJwtVcTypeMetadataSchema"; -import { OpenIdClaim } from "../schemas"; +import { ClaimMetadataEntry, ClaimDisplayEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; +import { OpenIdClaim } from '../schemas'; /** * Converts SD-JWT VC type-metadata claims to OpenID4VCI claims. @@ -20,10 +17,10 @@ export function convertSdjwtvcToOpenid4vciClaims( const display = (claim.display ?? []) .filter( (d): d is ClaimDisplayEntry => - typeof d?.locale === "string" && - d.locale.trim() !== "" && - typeof d?.label === "string" && - d.label.trim() !== "" + typeof d?.locale === 'string' && + d.locale.trim() !== '' && + typeof d?.label === 'string' && + d.label.trim() !== '', ) .map((d) => ({ locale: d.locale.trim(), @@ -31,10 +28,7 @@ export function convertSdjwtvcToOpenid4vciClaims( })); // Only include mandatory if explicitly present (true OR false) - const hasMandatory = Object.prototype.hasOwnProperty.call( - claim, - "mandatory", - ); + const hasMandatory = Object.prototype.hasOwnProperty.call(claim, 'mandatory'); const result: any = { path: claim.path, diff --git a/src/functions/convertSdjwtvcToOpenid4vciDisplay.test.ts b/src/functions/convertSdjwtvcToOpenid4vciDisplay.test.ts index 6146006..72cfdf3 100644 --- a/src/functions/convertSdjwtvcToOpenid4vciDisplay.test.ts +++ b/src/functions/convertSdjwtvcToOpenid4vciDisplay.test.ts @@ -1,50 +1,50 @@ -import { describe, it, expect } from "vitest"; -import type { TypeDisplayEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; -import { convertSdjwtvcToOpenid4vciDisplay } from "./convertSdjwtvcToOpenid4vciDisplay"; +import { describe, it, expect } from 'vitest'; +import type { TypeDisplayEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; +import { convertSdjwtvcToOpenid4vciDisplay } from './convertSdjwtvcToOpenid4vciDisplay'; -describe("convertSdjwtvcToOpenid4vciDisplay (CredentialConfigurationSupported.display)", () => { - it("returns undefined for empty input", () => { +describe('convertSdjwtvcToOpenid4vciDisplay (CredentialConfigurationSupported.display)', () => { + it('returns undefined for empty input', () => { expect(convertSdjwtvcToOpenid4vciDisplay(undefined)).toBeUndefined(); expect(convertSdjwtvcToOpenid4vciDisplay([])).toBeUndefined(); }); - it("maps name/description/locale", () => { + it('maps name/description/locale', () => { const input: TypeDisplayEntry[] = [ - { locale: "en", name: "EHIC", description: "European Health Insurance Card" }, - { locale: "el", name: "ΕΚΑΑ", description: "Ευρωπαϊκή Κάρτα Ασφάλισης Ασθενείας" }, + { locale: 'en', name: 'EHIC', description: 'European Health Insurance Card' }, + { locale: 'el', name: 'ΕΚΑΑ', description: 'Ευρωπαϊκή Κάρτα Ασφάλισης Ασθενείας' }, ]; const out = convertSdjwtvcToOpenid4vciDisplay(input)!; expect(out).toEqual([ { - locale: "en", - name: "EHIC", - description: "European Health Insurance Card", + locale: 'en', + name: 'EHIC', + description: 'European Health Insurance Card', }, { - locale: "el", - name: "ΕΚΑΑ", - description: "Ευρωπαϊκή Κάρτα Ασφάλισης Ασθενείας", + locale: 'el', + name: 'ΕΚΑΑ', + description: 'Ευρωπαϊκή Κάρτα Ασφάλισης Ασθενείας', }, ]); }); - it("maps rendering.simple colors, background image and logo; ignores svg_templates", () => { + it('maps rendering.simple colors, background image and logo; ignores svg_templates', () => { const input: TypeDisplayEntry[] = [ { - locale: "en", - name: "EHIC", - description: "desc", + locale: 'en', + name: 'EHIC', + description: 'desc', rendering: { simple: { - background_color: "#fff", - text_color: "#000", - background_image: { uri: "https://example.com/bg.png" }, - logo: { uri: "https://example.com/logo.png", alt_text: "Logo" }, + background_color: '#fff', + text_color: '#000', + background_image: { uri: 'https://example.com/bg.png' }, + logo: { uri: 'https://example.com/logo.png', alt_text: 'Logo' }, }, // should be ignored - svg_templates: [{ uri: "https://example.com/template.svg" }], + svg_templates: [{ uri: 'https://example.com/template.svg' }], }, }, ]; @@ -53,33 +53,31 @@ describe("convertSdjwtvcToOpenid4vciDisplay (CredentialConfigurationSupported.di expect(out).toEqual([ { - locale: "en", - name: "EHIC", - description: "desc", - background_color: "#fff", - text_color: "#000", - background_image: { uri: "https://example.com/bg.png" }, - logo: { uri: "https://example.com/logo.png", alt_text: "Logo" }, + locale: 'en', + name: 'EHIC', + description: 'desc', + background_color: '#fff', + text_color: '#000', + background_image: { uri: 'https://example.com/bg.png' }, + logo: { uri: 'https://example.com/logo.png', alt_text: 'Logo' }, }, ]); }); - it("does not set background_image/logo if simple is missing (even if svg_templates exist)", () => { + it('does not set background_image/logo if simple is missing (even if svg_templates exist)', () => { const input: TypeDisplayEntry[] = [ { - locale: "en", - name: "EHIC", + locale: 'en', + name: 'EHIC', rendering: { // ignored - svg_templates: [{ uri: "https://example.com/template.svg" }], + svg_templates: [{ uri: 'https://example.com/template.svg' }], }, }, ]; const out = convertSdjwtvcToOpenid4vciDisplay(input)!; - expect(out).toEqual([ - { locale: "en", name: "EHIC" }, - ]); + expect(out).toEqual([{ locale: 'en', name: 'EHIC' }]); }); }); diff --git a/src/functions/convertSdjwtvcToOpenid4vciDisplay.ts b/src/functions/convertSdjwtvcToOpenid4vciDisplay.ts index bcaac4d..17d6367 100644 --- a/src/functions/convertSdjwtvcToOpenid4vciDisplay.ts +++ b/src/functions/convertSdjwtvcToOpenid4vciDisplay.ts @@ -1,8 +1,8 @@ -import type { TypeDisplayEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; -import type { CredentialConfigurationSupported } from "../schemas/CredentialConfigurationSupportedSchema"; +import type { TypeDisplayEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; +import type { CredentialConfigurationSupported } from '../schemas/CredentialConfigurationSupportedSchema'; -type CredentialMetadata = NonNullable; -type OpenIdCredentialDisplay = NonNullable; +type CredentialMetadata = NonNullable; +type OpenIdCredentialDisplay = NonNullable; type IssuerDisplayEntry = OpenIdCredentialDisplay[number]; /** @@ -22,7 +22,7 @@ type IssuerDisplayEntry = OpenIdCredentialDisplay[number]; */ export function convertSdjwtvcToOpenid4vciDisplay( - display?: TypeDisplayEntry[] + display?: TypeDisplayEntry[], ): OpenIdCredentialDisplay | undefined { if (!display?.length) return undefined; @@ -45,11 +45,11 @@ export function convertSdjwtvcToOpenid4vciDisplay( ...(simple?.logo?.uri ? { - logo: { - uri: simple.logo.uri, - alt_text: simple.logo.alt_text, - }, - } + logo: { + uri: simple.logo.uri, + alt_text: simple.logo.alt_text, + }, + } : {}), }; diff --git a/src/functions/formatDate.test.ts b/src/functions/formatDate.test.ts index 4029e2e..3f8184b 100644 --- a/src/functions/formatDate.test.ts +++ b/src/functions/formatDate.test.ts @@ -1,10 +1,8 @@ -import { assert, describe, it } from "vitest"; -import { formatDate } from "./formatDate"; - -describe("The Date/Time parser", () => { - - it("can match ISO 8601 format without decimals", () => { +import { assert, describe, it } from 'vitest'; +import { formatDate } from './formatDate'; +describe('The Date/Time parser', () => { + it('can match ISO 8601 format without decimals', () => { const rawDate = '2026-02-01T07:28:49Z'; const formattedDate = formatDate(rawDate); @@ -13,11 +11,9 @@ describe("The Date/Time parser", () => { console.log(formattedDate); assert(formattedDate != rawDate); - }); - it("can match ISO 8601 format with two ms decimals", () => { - + it('can match ISO 8601 format with two ms decimals', () => { const rawDate = '2026-02-01T07:28:49.11Z'; const formattedDate = formatDate(rawDate); @@ -26,10 +22,9 @@ describe("The Date/Time parser", () => { console.log(formattedDate); assert(formattedDate != rawDate); - }); - it("can match ISO 8601 format with three ms decimals", async () => { + it('can match ISO 8601 format with three ms decimals', async () => { const rawDate = '2026-02-01T07:28:49.117Z'; const formattedDate = formatDate(rawDate); @@ -40,7 +35,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match simple date YYYY-MM-DD format", async () => { + it('can match simple date YYYY-MM-DD format', async () => { const rawDate = '2026-02-01'; const formattedDate = formatDate(rawDate); @@ -51,7 +46,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can handle a long format date", async () => { + it('can handle a long format date', async () => { const rawDate = 'Sun Feb 01 2026 14:46:19 GMT+0200'; const formattedDate = formatDate(rawDate); @@ -62,7 +57,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match a UNIX timestamp in seconds", async () => { + it('can match a UNIX timestamp in seconds', async () => { const rawDate = 1769896800; const formattedDate = formatDate(rawDate); @@ -73,7 +68,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match a UNIX timestamp in milliseconds", async () => { + it('can match a UNIX timestamp in milliseconds', async () => { const rawDate = 1769896800000; const formattedDate = formatDate(rawDate); @@ -84,7 +79,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match ISO 8601 format with three ms decimals", async () => { + it('can match ISO 8601 format with three ms decimals', async () => { const rawDate = '2026-10-08T07:28:49.117Z'; const formattedDate = formatDate(rawDate); @@ -95,7 +90,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match ISO 8601 format with six ms decimals", async () => { + it('can match ISO 8601 format with six ms decimals', async () => { const rawDate = '2026-10-08T07:28:49.117456Z'; const formattedDate = formatDate(rawDate); @@ -106,7 +101,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match ISO 8601 format with nine ms decimals", async () => { + it('can match ISO 8601 format with nine ms decimals', async () => { const rawDate = '2026-10-08T07:28:49.117456789Z'; const formattedDate = formatDate(rawDate); @@ -117,7 +112,7 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("cannot match invalid, ISO 8601-like format with ten ms decimals", async () => { + it('cannot match invalid, ISO 8601-like format with ten ms decimals', async () => { const rawDate = '2026-10-08T07:28:49.1174567890Z'; const formattedDate = formatDate(rawDate); @@ -128,7 +123,7 @@ describe("The Date/Time parser", () => { assert(formattedDate == rawDate); }); - it("can match a Date object", async () => { + it('can match a Date object', async () => { const rawDate = new Date(); const formattedDate = formatDate(rawDate); @@ -139,9 +134,9 @@ describe("The Date/Time parser", () => { assert(formattedDate != rawDate); }); - it("can match a CBOR Date object", async () => { + it('can match a CBOR Date object', async () => { const cborDate = { - date: "14-08-2026" + date: '14-08-2026', }; const formattedDate = formatDate(cborDate); @@ -151,5 +146,4 @@ describe("The Date/Time parser", () => { assert(formattedDate != cborDate); }); - }); diff --git a/src/functions/formatDate.ts b/src/functions/formatDate.ts index 6192740..21dda2f 100644 --- a/src/functions/formatDate.ts +++ b/src/functions/formatDate.ts @@ -1,4 +1,4 @@ -import { formatCborDate, isCborDate } from "../utils"; +import { formatCborDate, isCborDate } from '../utils'; export function formatDate(value: any, format = 'datetime') { // Regex for ISO 8601 format like '2024-10-08T07:28:49.117Z'. milliseconds are optional. @@ -6,7 +6,8 @@ export function formatDate(value: any, format = 'datetime') { // Regex for simple YYYY-MM-DD format const simpleDateRegex = /^\d{4}-\d{2}-\d{2}$/; // Regex for long-form date strings like 'Wed Dec 11 2024 14:46:19 GMT+0200' - const longFormDateRegex = /^[A-Z][a-z]{2} [A-Z][a-z]{2} \d{2} \d{4} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}/; + const longFormDateRegex = + /^[A-Z][a-z]{2} [A-Z][a-z]{2} \d{2} \d{4} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}/; let date; @@ -14,12 +15,10 @@ export function formatDate(value: any, format = 'datetime') { if (value.toString().length === 10) { // Handle Unix timestamp (seconds) by converting to milliseconds date = new Date(value * 1000); - } - else if (value.toString().length === 13) { + } else if (value.toString().length === 13) { // Timestamp including milliseconds date = new Date(value); - } - else { + } else { // Unsupported timestamp formats return value; } @@ -47,9 +46,17 @@ export function formatDate(value: any, format = 'datetime') { return value; } - const options = format === 'datetime' - ? { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' } - : { day: '2-digit', month: '2-digit', year: 'numeric' }; + const options = + format === 'datetime' + ? { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + } + : { day: '2-digit', month: '2-digit', year: 'numeric' }; return date.toLocaleDateString('en-GB', options as any); } diff --git a/src/functions/pickBestSvgTemplate.test.ts b/src/functions/pickBestSvgTemplate.test.ts index e36c238..907adee 100644 --- a/src/functions/pickBestSvgTemplate.test.ts +++ b/src/functions/pickBestSvgTemplate.test.ts @@ -1,265 +1,257 @@ -import { describe, expect, it } from "vitest"; -import type { SvgTemplateEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; -import { pickBestSvgTemplate, rankSvgTemplates } from "./pickBestSvgTemplate"; +import { describe, expect, it } from 'vitest'; +import type { SvgTemplateEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; +import { pickBestSvgTemplate, rankSvgTemplates } from './pickBestSvgTemplate'; -describe("pickBestSvgTemplate", () => { +describe('pickBestSvgTemplate', () => { const realisticTemplates: SvgTemplateEntry[] = [ { - uri: "https://example.com/portrait-light-normal.svg", + uri: 'https://example.com/portrait-light-normal.svg', properties: { - orientation: "portrait", - color_scheme: "light", - contrast: "normal", + orientation: 'portrait', + color_scheme: 'light', + contrast: 'normal', }, }, { - uri: "https://example.com/portrait-dark-normal.svg", + uri: 'https://example.com/portrait-dark-normal.svg', properties: { - orientation: "portrait", - color_scheme: "dark", - contrast: "normal", + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'normal', }, }, { - uri: "https://example.com/portrait-dark-high.svg", + uri: 'https://example.com/portrait-dark-high.svg', properties: { - orientation: "portrait", - color_scheme: "dark", - contrast: "high", + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'high', }, }, { - uri: "https://example.com/landscape-light-normal.svg", + uri: 'https://example.com/landscape-light-normal.svg', properties: { - orientation: "landscape", - color_scheme: "light", - contrast: "normal", + orientation: 'landscape', + color_scheme: 'light', + contrast: 'normal', }, }, { - uri: "https://example.com/landscape-dark-high.svg", + uri: 'https://example.com/landscape-dark-high.svg', properties: { - orientation: "landscape", - color_scheme: "dark", - contrast: "high", + orientation: 'landscape', + color_scheme: 'dark', + contrast: 'high', }, }, ]; - it("returns null when there are no templates", () => { - expect( - pickBestSvgTemplate(undefined, { orientation: "portrait" }) - ).toBeNull(); - expect( - pickBestSvgTemplate([], { orientation: "portrait" }) - ).toBeNull(); + it('returns null when there are no templates', () => { + expect(pickBestSvgTemplate(undefined, { orientation: 'portrait' })).toBeNull(); + expect(pickBestSvgTemplate([], { orientation: 'portrait' })).toBeNull(); }); - it("returns the only template when one exists", () => { - const template: SvgTemplateEntry = { uri: "https://example.com/only.svg" }; + it('returns the only template when one exists', () => { + const template: SvgTemplateEntry = { uri: 'https://example.com/only.svg' }; - expect( - pickBestSvgTemplate([template], { orientation: "portrait" }) - ).toEqual(template); + expect(pickBestSvgTemplate([template], { orientation: 'portrait' })).toEqual(template); }); - it("prefers the template that matches orientation", () => { + it('prefers the template that matches orientation', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/landscape.svg", - properties: { orientation: "landscape" }, + uri: 'https://example.com/landscape.svg', + properties: { orientation: 'landscape' }, }, { - uri: "https://example.com/portrait.svg", - properties: { orientation: "portrait" }, + uri: 'https://example.com/portrait.svg', + properties: { orientation: 'portrait' }, }, ]; - expect( - pickBestSvgTemplate(templates, { orientation: "portrait" }) - ).toEqual(templates[1]); + expect(pickBestSvgTemplate(templates, { orientation: 'portrait' })).toEqual(templates[1]); }); - it("narrows matches with color scheme and contrast", () => { + it('narrows matches with color scheme and contrast', () => { expect( pickBestSvgTemplate(realisticTemplates, { - orientation: "portrait", - color_scheme: "dark", - contrast: "high", - }) + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'high', + }), ).toEqual(realisticTemplates[2]); }); - it("falls back to all templates when no orientation match exists", () => { + it('falls back to all templates when no orientation match exists', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/light.svg", - properties: { orientation: "landscape", color_scheme: "light" }, + uri: 'https://example.com/light.svg', + properties: { orientation: 'landscape', color_scheme: 'light' }, }, { - uri: "https://example.com/dark.svg", - properties: { orientation: "landscape", color_scheme: "dark" }, + uri: 'https://example.com/dark.svg', + properties: { orientation: 'landscape', color_scheme: 'dark' }, }, ]; expect( pickBestSvgTemplate(templates, { - orientation: "portrait", - color_scheme: "dark", - }) + orientation: 'portrait', + color_scheme: 'dark', + }), ).toEqual(templates[1]); }); - it("prefers a candidate with properties when fallback candidates remain", () => { + it('prefers a candidate with properties when fallback candidates remain', () => { const templates: SvgTemplateEntry[] = [ - { uri: "https://example.com/without-properties.svg" }, + { uri: 'https://example.com/without-properties.svg' }, { - uri: "https://example.com/with-properties.svg", - properties: { orientation: "portrait" }, + uri: 'https://example.com/with-properties.svg', + properties: { orientation: 'portrait' }, }, ]; - expect( - pickBestSvgTemplate(templates, { color_scheme: "light" }) - ).toEqual(templates[1]); + expect(pickBestSvgTemplate(templates, { color_scheme: 'light' })).toEqual(templates[1]); }); - it("keeps orientation matches even when color scheme does not match", () => { + it('keeps orientation matches even when color scheme does not match', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/portrait-dark.svg", + uri: 'https://example.com/portrait-dark.svg', properties: { - orientation: "portrait", - color_scheme: "dark", + orientation: 'portrait', + color_scheme: 'dark', }, }, { - uri: "https://example.com/portrait-dark-high.svg", + uri: 'https://example.com/portrait-dark-high.svg', properties: { - orientation: "portrait", - color_scheme: "dark", - contrast: "high", + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'high', }, }, { - uri: "https://example.com/landscape-light.svg", + uri: 'https://example.com/landscape-light.svg', properties: { - orientation: "landscape", - color_scheme: "light", + orientation: 'landscape', + color_scheme: 'light', }, }, ]; expect( pickBestSvgTemplate(templates, { - orientation: "portrait", - color_scheme: "light", - }) + orientation: 'portrait', + color_scheme: 'light', + }), ).toEqual(templates[0]); }); - it("uses contrast to break a tie among templates with the same orientation", () => { + it('uses contrast to break a tie among templates with the same orientation', () => { expect( pickBestSvgTemplate(realisticTemplates, { - orientation: "landscape", - contrast: "high", - }) + orientation: 'landscape', + contrast: 'high', + }), ).toEqual(realisticTemplates[4]); }); - it("supports templates with partial properties alongside fully specified ones", () => { + it('supports templates with partial properties alongside fully specified ones', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/portrait-only.svg", - properties: { orientation: "portrait" }, + uri: 'https://example.com/portrait-only.svg', + properties: { orientation: 'portrait' }, }, { - uri: "https://example.com/portrait-dark.svg", + uri: 'https://example.com/portrait-dark.svg', properties: { - orientation: "portrait", - color_scheme: "dark", + orientation: 'portrait', + color_scheme: 'dark', }, }, { - uri: "https://example.com/portrait-dark-high.svg", + uri: 'https://example.com/portrait-dark-high.svg', properties: { - orientation: "portrait", - color_scheme: "dark", - contrast: "high", + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'high', }, }, ]; expect( pickBestSvgTemplate(templates, { - orientation: "portrait", - color_scheme: "dark", - contrast: "high", - }) + orientation: 'portrait', + color_scheme: 'dark', + contrast: 'high', + }), ).toEqual(templates[2]); }); - it("returns the first matching template when multiple templates share the same properties", () => { + it('returns the first matching template when multiple templates share the same properties', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/first-portrait-dark.svg", + uri: 'https://example.com/first-portrait-dark.svg', properties: { - orientation: "portrait", - color_scheme: "dark", + orientation: 'portrait', + color_scheme: 'dark', }, }, { - uri: "https://example.com/second-portrait-dark.svg", + uri: 'https://example.com/second-portrait-dark.svg', properties: { - orientation: "portrait", - color_scheme: "dark", + orientation: 'portrait', + color_scheme: 'dark', }, }, ]; expect( pickBestSvgTemplate(templates, { - orientation: "portrait", - color_scheme: "dark", - }) + orientation: 'portrait', + color_scheme: 'dark', + }), ).toEqual(templates[0]); }); }); -describe("rankSvgTemplates", () => { - it("returns an empty list when there are no templates", () => { - expect(rankSvgTemplates(undefined, { orientation: "portrait" })).toEqual([]); - expect(rankSvgTemplates([], { orientation: "portrait" })).toEqual([]); +describe('rankSvgTemplates', () => { + it('returns an empty list when there are no templates', () => { + expect(rankSvgTemplates(undefined, { orientation: 'portrait' })).toEqual([]); + expect(rankSvgTemplates([], { orientation: 'portrait' })).toEqual([]); }); - it("orders all templates by preference and preserves metadata order for ties", () => { + it('orders all templates by preference and preserves metadata order for ties', () => { const templates: SvgTemplateEntry[] = [ { - uri: "https://example.com/landscape-light.svg", - properties: { orientation: "landscape", color_scheme: "light" }, + uri: 'https://example.com/landscape-light.svg', + properties: { orientation: 'landscape', color_scheme: 'light' }, }, { - uri: "https://example.com/portrait-dark-first.svg", - properties: { orientation: "portrait", color_scheme: "dark" }, + uri: 'https://example.com/portrait-dark-first.svg', + properties: { orientation: 'portrait', color_scheme: 'dark' }, }, { - uri: "https://example.com/portrait-dark-second.svg", - properties: { orientation: "portrait", color_scheme: "dark" }, + uri: 'https://example.com/portrait-dark-second.svg', + properties: { orientation: 'portrait', color_scheme: 'dark' }, }, { - uri: "https://example.com/landscape-dark.svg", - properties: { orientation: "landscape", color_scheme: "dark" }, + uri: 'https://example.com/landscape-dark.svg', + properties: { orientation: 'landscape', color_scheme: 'dark' }, }, ]; - expect(rankSvgTemplates(templates, { - orientation: "portrait", - color_scheme: "dark", - }).map(({ uri }) => uri)).toEqual([ - "https://example.com/portrait-dark-first.svg", - "https://example.com/portrait-dark-second.svg", - "https://example.com/landscape-dark.svg", - "https://example.com/landscape-light.svg", + expect( + rankSvgTemplates(templates, { + orientation: 'portrait', + color_scheme: 'dark', + }).map(({ uri }) => uri), + ).toEqual([ + 'https://example.com/portrait-dark-first.svg', + 'https://example.com/portrait-dark-second.svg', + 'https://example.com/landscape-dark.svg', + 'https://example.com/landscape-light.svg', ]); }); }); diff --git a/src/functions/pickBestSvgTemplate.ts b/src/functions/pickBestSvgTemplate.ts index 08c36f6..49f3cc3 100644 --- a/src/functions/pickBestSvgTemplate.ts +++ b/src/functions/pickBestSvgTemplate.ts @@ -1,18 +1,15 @@ -import type { - SvgTemplateEntry, - SvgTemplateProperties, -} from "../schemas/SdJwtVcTypeMetadataSchema"; +import type { SvgTemplateEntry, SvgTemplateProperties } from '../schemas/SdJwtVcTypeMetadataSchema'; export function pickBestSvgTemplate( templates: SvgTemplateEntry[] | undefined, - properties: SvgTemplateProperties + properties: SvgTemplateProperties, ): SvgTemplateEntry | null { return rankSvgTemplates(templates, properties)[0] ?? null; } export function rankSvgTemplates( templates: SvgTemplateEntry[] | undefined, - properties: SvgTemplateProperties + properties: SvgTemplateProperties, ): SvgTemplateEntry[] { if (!templates?.length) return []; @@ -26,29 +23,17 @@ export function rankSvgTemplates( .map(({ template }) => template); } -function getPreferenceScore( - template: SvgTemplateEntry, - properties: SvgTemplateProperties -): number { +function getPreferenceScore(template: SvgTemplateEntry, properties: SvgTemplateProperties): number { // Each preference outweighs every lower-priority match combined. let score = template.properties ? 1 : 0; - if ( - properties.orientation && - template.properties?.orientation === properties.orientation - ) { + if (properties.orientation && template.properties?.orientation === properties.orientation) { score += 8; } - if ( - properties.color_scheme && - template.properties?.color_scheme === properties.color_scheme - ) { + if (properties.color_scheme && template.properties?.color_scheme === properties.color_scheme) { score += 4; } - if ( - properties.contrast && - template.properties?.contrast === properties.contrast - ) { + if (properties.contrast && template.properties?.contrast === properties.contrast) { score += 2; } diff --git a/src/index.ts b/src/index.ts index edd9919..354ea7e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import "reflect-metadata"; +import 'reflect-metadata'; export * from './rendering'; diff --git a/src/interfaces.ts b/src/interfaces.ts index a90f1ee..ea76598 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,7 +1,19 @@ -import { JWK } from "jose"; -import { CredentialParsingError, CredentialVerificationError, PublicKeyResolutionError, CredentialRenderingError, ValidatePresentationRequirementsError } from "./error"; -import { CustomResult, ParsedCredential, CredentialClaims, ParserResult, CredentialClaimPath } from "./types"; -import { VctDocumentProvider } from "./core"; +import { JWK } from 'jose'; +import { + CredentialParsingError, + CredentialVerificationError, + PublicKeyResolutionError, + CredentialRenderingError, + ValidatePresentationRequirementsError, +} from './error'; +import { + CustomResult, + ParsedCredential, + CredentialClaims, + ParserResult, + CredentialClaimPath, +} from './types'; +import { VctDocumentProvider } from './core'; export interface CredentialRendering { renderSvgTemplate(args: { @@ -9,11 +21,14 @@ export interface CredentialRendering { credentialImageSvgTemplate: string; sdJwtVcMetadataClaims: any; filter?: Array; - }): Promise + }): Promise; } export interface CustomCredentialSvgI { - renderCustomSvgTemplate(args: { signedClaims: CredentialClaims, displayConfig: any }): Promise; + renderCustomSvgTemplate(args: { + signedClaims: CredentialClaims; + displayConfig: any; + }): Promise; } export interface CredentialIssuerInfo { @@ -23,25 +38,46 @@ export interface CredentialIssuerInfo { export interface ParsingEngineI { register(parser: CredentialParser): void; - parse({ rawCredential, credentialIssuer }: { - rawCredential: unknown, + parse({ + rawCredential, + credentialIssuer, + }: { + rawCredential: unknown; credentialIssuer?: CredentialIssuerInfo; }): Promise>; } export interface VerifyingEngineI { register(credentialVerifier: CredentialVerifier): void; - verify({ rawCredential, opts }: { rawCredential: unknown, opts: { expectedNonce?: string, expectedAudience?: string, holderNonce?: string, responseUri?: string, verifierEncryptionJwk?: JWK, handoverType?: "redirect" | "dc_api", dcApiOrigin?: string } }): Promise>; + verify({ + rawCredential, + opts, + }: { + rawCredential: unknown; + opts: { + expectedNonce?: string; + expectedAudience?: string; + holderNonce?: string; + responseUri?: string; + verifierEncryptionJwk?: JWK; + handoverType?: 'redirect' | 'dc_api'; + dcApiOrigin?: string; + }; + }): Promise>; } export interface PublicKeyResolverEngineI { register(resolver: PublicKeyResolver): void; - resolve(args: { identifier: string }): Promise>; + resolve(args: { identifier: string }): Promise< + CustomResult< + { + jwk: JWK; + }, + PublicKeyResolutionError + > + >; } - export interface CredentialParser { parse(args: { rawCredential: unknown; @@ -50,26 +86,52 @@ export interface CredentialParser { } export interface CredentialVerifier { - verify(args: { rawCredential: unknown, opts: { expectedNonce?: string, expectedAudience?: string, holderNonce?: string, responseUri?: string, verifierEncryptionJwk?: JWK, handoverType?: "redirect" | "dc_api", dcApiOrigin?: string } }): Promise>; + verify(args: { + rawCredential: unknown; + opts: { + expectedNonce?: string; + expectedAudience?: string; + holderNonce?: string; + responseUri?: string; + verifierEncryptionJwk?: JWK; + handoverType?: 'redirect' | 'dc_api'; + dcApiOrigin?: string; + }; + }): Promise< + CustomResult< + { + holderPublicKey: JWK; + }, + CredentialVerificationError + > + >; } - export interface PublicKeyResolver { - resolve(args: { identifier: string }): Promise>; + resolve(args: { identifier: string }): Promise< + CustomResult< + { + jwk: JWK; + }, + PublicKeyResolutionError + > + >; } export interface HttpClient { - get(url: string, headers?: Record, options?: any): Promise<{ status: number, headers: Record, data: unknown }>; - post(url: string, body: any, headers?: Record, options?: any): Promise<{ status: number, headers: Record, data: unknown }>; + get( + url: string, + headers?: Record, + options?: any, + ): Promise<{ status: number; headers: Record; data: unknown }>; + post( + url: string, + body: any, + headers?: Record, + options?: any, + ): Promise<{ status: number; headers: Record; data: unknown }>; } - - - export interface Context { clockTolerance: number; lang: string; diff --git a/src/protocols/openid4vp/OpenID4VPClientAPI.test.ts b/src/protocols/openid4vp/OpenID4VPClientAPI.test.ts index d1b7dc4..5f8a1cc 100644 --- a/src/protocols/openid4vp/OpenID4VPClientAPI.test.ts +++ b/src/protocols/openid4vp/OpenID4VPClientAPI.test.ts @@ -1,43 +1,42 @@ -import { assert, describe, it, vi } from "vitest"; -import { MemoryStore } from "../../core/MemoryStore"; -import { OpenID4VPClientAPI } from "./OpenID4VPClientAPI"; -import { OpenID4VPResponseMode } from "./types"; -import { generateKeyPair, exportPKCS8, exportJWK, CompactEncrypt } from "jose"; -import { fromBase64Url, toBase64Url } from "../../utils/util"; -import type { HttpClient } from "../../interfaces"; -import { VerifiableCredentialFormat } from "../../types"; -import { DcqlPresentationResult } from "dcql"; +import { assert, describe, it, vi } from 'vitest'; +import { MemoryStore } from '../../core/MemoryStore'; +import { OpenID4VPClientAPI } from './OpenID4VPClientAPI'; +import { OpenID4VPResponseMode } from './types'; +import { generateKeyPair, exportPKCS8, exportJWK, CompactEncrypt } from 'jose'; +import { fromBase64Url, toBase64Url } from '../../utils/util'; +import type { HttpClient } from '../../interfaces'; +import { VerifiableCredentialFormat } from '../../types'; +import { DcqlPresentationResult } from 'dcql'; const x5c = [ - "MIICyzCCAnGgAwIBAgIULnrxux9sI34oqbby3M4lSKOs8owwCgYIKoZIzj0EAwIwPzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQd3dXYWxsZXQgUm9vdCBDQTAeFw0yNTA0MjkxMDI5NTNaFw0yNjA0MjkxMDI5NTNaMEExCzAJBgNVBAYTAkVVMRUwEwYDVQQKDAx3d1dhbGxldC5vcmcxGzAZBgNVBAMMEmxvY2FsLnd3d2FsbGV0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFVivGt53M4qEP06QT20BSlGiMIdzLLvG+b9fq/fHKM+NGT+a3snXiPwU7X7jrOFWxwyjZeean40+vx6Gy06VfqjggFHMIIBQzAdBgNVHQ4EFgQUM/A3FTQLjww5/9u01MX/SRyVqaUwHwYDVR0jBBgwFoAU0HGu3T+/Wqh3yNifz9sNd+HPBS4wDgYDVR0PAQH/BAQDAgeAMDIGA1UdEgQrMCmBEWluZm9Ad3d3YWxsZXQub3JnhhRodHRwczovL3d3d2FsbGV0Lm9yZzASBgNVHSUECzAJBgcogYxdBQECMAwGA1UdEwEB/wQCMAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3dhbGxldC5vcmcvaWFjYS9jcmwvd3d3YWxsZXRfb3JnX2lhY2EuY3JsMFUGA1UdEQROMEyCEmxvY2FsLnd3d2FsbGV0Lm9yZ4IZbG9jYWwtaXNzdWVyLnd3d2FsbGV0Lm9yZ4IbbG9jYWwtdmVyaWZpZXIud3d3YWxsZXQub3JnMAoGCCqGSM49BAMCA0gAMEUCIQCQ8h+5krhO+f4woReDY1D7CaM6qCda3m814e6DLvOphAIgHQL+Wm7WFRwxgjzMLN37RojJGrZbF4OFChIkmm0uu5o=" + 'MIICyzCCAnGgAwIBAgIULnrxux9sI34oqbby3M4lSKOs8owwCgYIKoZIzj0EAwIwPzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQd3dXYWxsZXQgUm9vdCBDQTAeFw0yNTA0MjkxMDI5NTNaFw0yNjA0MjkxMDI5NTNaMEExCzAJBgNVBAYTAkVVMRUwEwYDVQQKDAx3d1dhbGxldC5vcmcxGzAZBgNVBAMMEmxvY2FsLnd3d2FsbGV0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFVivGt53M4qEP06QT20BSlGiMIdzLLvG+b9fq/fHKM+NGT+a3snXiPwU7X7jrOFWxwyjZeean40+vx6Gy06VfqjggFHMIIBQzAdBgNVHQ4EFgQUM/A3FTQLjww5/9u01MX/SRyVqaUwHwYDVR0jBBgwFoAU0HGu3T+/Wqh3yNifz9sNd+HPBS4wDgYDVR0PAQH/BAQDAgeAMDIGA1UdEgQrMCmBEWluZm9Ad3d3YWxsZXQub3JnhhRodHRwczovL3d3d2FsbGV0Lm9yZzASBgNVHSUECzAJBgcogYxdBQECMAwGA1UdEwEB/wQCMAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3dhbGxldC5vcmcvaWFjYS9jcmwvd3d3YWxsZXRfb3JnX2lhY2EuY3JsMFUGA1UdEQROMEyCEmxvY2FsLnd3d2FsbGV0Lm9yZ4IZbG9jYWwtaXNzdWVyLnd3d2FsbGV0Lm9yZ4IbbG9jYWwtdmVyaWZpZXIud3d3YWxsZXQub3JnMAoGCCqGSM49BAMCA0gAMEUCIQCQ8h+5krhO+f4woReDY1D7CaM6qCda3m814e6DLvOphAIgHQL+Wm7WFRwxgjzMLN37RojJGrZbF4OFChIkmm0uu5o=', ]; const presentationRequest = { dcql_query: { credentials: [ { - id: "pidMsoMdoc", - format: "mso_mdoc", - meta: { doctype_value: "eu.europa.ec.eudi.pid.1" }, + id: 'pidMsoMdoc', + format: 'mso_mdoc', + meta: { doctype_value: 'eu.europa.ec.eudi.pid.1' }, claims: [ { - path: ["eu.europa.ec.eudi.pid.1", "family_name"], - intent_to_retain: false - } - ] - } - ] - } + path: ['eu.europa.ec.eudi.pid.1', 'family_name'], + intent_to_retain: false, + }, + ], + }, + ], + }, }; -describe("OpenID4VPClientAPI.validateDcqlVpToken", () => { - - it("should serialize Map values when building presentation claim values", () => { +describe('OpenID4VPClientAPI.validateDcqlVpToken', () => { + it('should serialize Map values when building presentation claim values', () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( kv, @@ -45,39 +44,36 @@ describe("OpenID4VPClientAPI.validateDcqlVpToken", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); const result = (helper as any).calculatePresentationClaimValue( new Map([ - ["given_name", "John"], - ["family_name", "Doe"] - ]) + ['given_name', 'John'], + ['family_name', 'Doe'], + ]), ); - assert.equal( - result, - '{"given_name":"John","family_name":"Doe"}' - ); + assert.equal(result, '{"given_name":"John","family_name":"Doe"}'); }); }); -describe("OpenID4VPClientAPI.generateAuthorizationRequestURL", () => { - it("should build a valid URL, store rpState, and include DCQL in the request JWT", async () => { +describe('OpenID4VPClientAPI.generateAuthorizationRequestURL', () => { + it('should build a valid URL, store rpState, and include DCQL in the request JWT', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; - const { privateKey } = await generateKeyPair("ES256"); + const { privateKey } = await generateKeyPair('ES256'); const privateKeyPem = await exportPKCS8(privateKey); const helper = new OpenID4VPClientAPI( @@ -86,18 +82,18 @@ describe("OpenID4VPClientAPI.generateAuthorizationRequestURL", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const responseUri = "https://verifier.example.com/cb"; - const baseUri = "https://verifier.example.com"; - const sessionId = "session-123"; + const responseUri = 'https://verifier.example.com/cb'; + const baseUri = 'https://verifier.example.com'; + const sessionId = 'session-123'; const result = await helper.generateAuthorizationRequestURL( presentationRequest, @@ -107,14 +103,17 @@ describe("OpenID4VPClientAPI.generateAuthorizationRequestURL", () => { privateKeyPem, x5c, OpenID4VPResponseMode.DIRECT_POST, - "https://verifier.example.com/callback" + 'https://verifier.example.com/callback', ); assert(result.stateId === sessionId); - assert(result.url.protocol === "openid4vp:"); - assert(result.url.host === "cb"); - assert(result.url.searchParams.get("client_id") === "x509_san_dns:verifier.example.com"); - assert(result.url.searchParams.get("request_uri") === `${baseUri}/verification/request-object?id=${sessionId}`); + assert(result.url.protocol === 'openid4vp:'); + assert(result.url.host === 'cb'); + assert(result.url.searchParams.get('client_id') === 'x509_san_dns:verifier.example.com'); + assert( + result.url.searchParams.get('request_uri') === + `${baseUri}/verification/request-object?id=${sessionId}`, + ); const stored = await kv.get(`rpstate:${sessionId}`); assert(stored !== undefined); @@ -123,33 +122,33 @@ describe("OpenID4VPClientAPI.generateAuthorizationRequestURL", () => { assert(stored.signed_request); assert(stored.dcql_query !== null); assert(stored.completed === null); - assert(await kv.get(`key:${stored.rp_eph_kid}`) === sessionId); + assert((await kv.get(`key:${stored.rp_eph_kid}`)) === sessionId); - const [encodedHeader, encodedPayload] = stored.signed_request.split("."); + const [encodedHeader, encodedPayload] = stored.signed_request.split('.'); assert(encodedHeader && encodedPayload); const payload = JSON.parse(new TextDecoder().decode(fromBase64Url(encodedPayload))); assert(payload.response_uri === responseUri); - assert(payload.response_type === "vp_token"); + assert(payload.response_type === 'vp_token'); assert(payload.response_mode === OpenID4VPResponseMode.DIRECT_POST); assert(payload.state === sessionId); assert(payload.nonce); - assert(payload.client_id === "x509_san_dns:verifier.example.com"); - assert(payload.dcql_query?.credentials?.[0]?.id === "pidMsoMdoc"); - assert(!("transaction_data" in payload)); + assert(payload.client_id === 'x509_san_dns:verifier.example.com'); + assert(payload.dcql_query?.credentials?.[0]?.id === 'pidMsoMdoc'); + assert(!('transaction_data' in payload)); assert(payload.client_metadata?.jwks?.keys?.length === 1); - assert(payload.client_metadata?.jwks?.keys?.[0]?.alg === "ECDH-ES"); - assert(result.rpState.rp_eph_pub.alg === "ECDH-ES"); + assert(payload.client_metadata?.jwks?.keys?.[0]?.alg === 'ECDH-ES'); + assert(result.rpState.rp_eph_pub.alg === 'ECDH-ES'); }); - it("should build a valid URL using x509_hash client identifier", async () => { + it('should build a valid URL using x509_hash client identifier', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; - const { privateKey } = await generateKeyPair("ES256"); + const { privateKey } = await generateKeyPair('ES256'); const privateKeyPem = await exportPKCS8(privateKey); const helper = new OpenID4VPClientAPI( @@ -158,49 +157,49 @@ describe("OpenID4VPClientAPI.generateAuthorizationRequestURL", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const certBytes = Uint8Array.from(Buffer.from(x5c[0], "base64")); - const certHash = await crypto.subtle.digest("SHA-256", certBytes); + const certBytes = Uint8Array.from(Buffer.from(x5c[0], 'base64')); + const certHash = await crypto.subtle.digest('SHA-256', certBytes); const certHashB64Url = toBase64Url(new Uint8Array(certHash)); const expectedClientId = `x509_hash:${certHashB64Url}`; const result = await helper.generateAuthorizationRequestURL( presentationRequest, - "session-hash", - "https://verifier.example.com/cb", - "https://verifier.example.com", + 'session-hash', + 'https://verifier.example.com/cb', + 'https://verifier.example.com', privateKeyPem, x5c, OpenID4VPResponseMode.DIRECT_POST, - "https://verifier.example.com/callback", - "x509_hash" + 'https://verifier.example.com/callback', + 'x509_hash', ); - assert(result.url.searchParams.get("client_id") === expectedClientId); + assert(result.url.searchParams.get('client_id') === expectedClientId); assert(result.rpState.audience === expectedClientId); - assert(result.rpState.rp_eph_pub.alg === "ECDH-ES"); - const [, encodedPayload] = result.rpState.signed_request.split("."); + assert(result.rpState.rp_eph_pub.alg === 'ECDH-ES'); + const [, encodedPayload] = result.rpState.signed_request.split('.'); const payload = JSON.parse(new TextDecoder().decode(fromBase64Url(encodedPayload))); assert(payload.client_id === expectedClientId); - assert(payload.client_metadata?.jwks?.keys?.[0]?.alg === "ECDH-ES"); + assert(payload.client_metadata?.jwks?.keys?.[0]?.alg === 'ECDH-ES'); }); }); -describe("OpenID4VPClientAPI small get/set helpers", () => { - it("should save and load rpState by session id", async () => { +describe('OpenID4VPClientAPI small get/set helpers', () => { + it('should save and load rpState by session id', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -209,29 +208,29 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); const rpState = { - session_id: "session-a", + session_id: 'session-a', is_cross_device: true, - signed_request: "signed", - state: "session-a", - nonce: "nonce", + signed_request: 'signed', + state: 'session-a', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-a", - rp_eph_pub: { kty: "EC", crv: "P-256", x: "x", y: "y" }, - rp_eph_priv: { kty: "EC", crv: "P-256", x: "x", y: "y", d: "d" }, + rp_eph_kid: 'kid-a', + rp_eph_pub: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y' }, + rp_eph_priv: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y', d: 'd' }, encrypted_response: null, vp_token: null, presentation_submission: null, @@ -239,20 +238,20 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - const loaded = await helper.getRPStateBySessionId("session-a"); - assert(loaded?.session_id === "session-a"); + const loaded = await helper.getRPStateBySessionId('session-a'); + assert(loaded?.session_id === 'session-a'); }); - it("should return null when rpState is missing by session id", async () => { + it('should return null when rpState is missing by session id', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -261,25 +260,25 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const loaded = await helper.getRPStateBySessionId("missing"); + const loaded = await helper.getRPStateBySessionId('missing'); assert(loaded === null); }); - it("should return null when rpState is missing by kid", async () => { + it('should return null when rpState is missing by kid', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -288,25 +287,25 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const loaded = await helper.getRPStateByKid("missing-kid"); + const loaded = await helper.getRPStateByKid('missing-kid'); assert(loaded === null); }); - it("should return rpState by kid when mapping exists", async () => { + it('should return rpState by kid when mapping exists', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -315,29 +314,29 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); const rpState = { - session_id: "session-b", + session_id: 'session-b', is_cross_device: true, - signed_request: "signed", - state: "session-b", - nonce: "nonce", + signed_request: 'signed', + state: 'session-b', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-b", - rp_eph_pub: { kty: "EC", crv: "P-256", x: "x", y: "y" }, - rp_eph_priv: { kty: "EC", crv: "P-256", x: "x", y: "y", d: "d" }, + rp_eph_kid: 'kid-b', + rp_eph_pub: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y' }, + rp_eph_priv: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y', d: 'd' }, encrypted_response: null, vp_token: null, presentation_submission: null, @@ -345,22 +344,22 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - await kv.set("key:kid-b", rpState.session_id); + await kv.set('key:kid-b', rpState.session_id); - const loaded = await helper.getRPStateByKid("kid-b"); - assert(loaded?.session_id === "session-b"); + const loaded = await helper.getRPStateByKid('kid-b'); + assert(loaded?.session_id === 'session-b'); }); - it("should return null when response code mapping is missing", async () => { + it('should return null when response code mapping is missing', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -369,25 +368,25 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const loaded = await helper.getRPStateByResponseCode("missing-code"); + const loaded = await helper.getRPStateByResponseCode('missing-code'); assert(loaded === null); }); - it("should return rpState by response code when mapping exists", async () => { + it('should return rpState by response code when mapping exists', async () => { const kv = new MemoryStore(); const httpClient: HttpClient = { get: async () => { - throw new Error("unexpected http call"); - } + throw new Error('unexpected http call'); + }, }; const helper = new OpenID4VPClientAPI( @@ -396,51 +395,55 @@ describe("OpenID4VPClientAPI small get/set helpers", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); const rpState = { - session_id: "session-c", + session_id: 'session-c', is_cross_device: true, - signed_request: "signed", - state: "session-c", - nonce: "nonce", + signed_request: 'signed', + state: 'session-c', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-c", - rp_eph_pub: { kty: "EC", crv: "P-256", x: "x", y: "y" }, - rp_eph_priv: { kty: "EC", crv: "P-256", x: "x", y: "y", d: "d" }, + rp_eph_kid: 'kid-c', + rp_eph_pub: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y' }, + rp_eph_priv: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y', d: 'd' }, encrypted_response: null, vp_token: null, presentation_submission: null, - response_code: "resp-code", + response_code: 'resp-code', claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - await kv.set("response_code:resp-code", rpState.session_id); + await kv.set('response_code:resp-code', rpState.session_id); - const loaded = await helper.getRPStateByResponseCode("resp-code"); - assert(loaded?.session_id === "session-c"); + const loaded = await helper.getRPStateByResponseCode('resp-code'); + assert(loaded?.session_id === 'session-c'); }); }); -describe("OpenID4VPClientAPI.handleResponseDirectPost", () => { - it("should store response details and mark completed", async () => { +describe('OpenID4VPClientAPI.handleResponseDirectPost', () => { + it('should store response details and mark completed', async () => { const kv = new MemoryStore(); - const httpClient: HttpClient = { get: async () => { throw new Error("unexpected http call"); } }; + const httpClient: HttpClient = { + get: async () => { + throw new Error('unexpected http call'); + }, + }; const helper = new OpenID4VPClientAPI( kv, @@ -448,29 +451,29 @@ describe("OpenID4VPClientAPI.handleResponseDirectPost", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); const rpState = { - session_id: "session-d", + session_id: 'session-d', is_cross_device: true, - signed_request: "signed", - state: "session-d", - nonce: "nonce", + signed_request: 'signed', + state: 'session-d', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-d", - rp_eph_pub: { kty: "EC", crv: "P-256", x: "x", y: "y" }, - rp_eph_priv: { kty: "EC", crv: "P-256", x: "x", y: "y", d: "d" }, + rp_eph_kid: 'kid-d', + rp_eph_pub: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y' }, + rp_eph_priv: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y', d: 'd' }, encrypted_response: null, vp_token: null, presentation_submission: null, @@ -478,66 +481,70 @@ describe("OpenID4VPClientAPI.handleResponseDirectPost", () => { claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - const vpToken = { pid: "vp" }; - const result = await helper.handleResponseDirectPost("session-d", vpToken, { id: "ps" }); + const vpToken = { pid: 'vp' }; + const result = await helper.handleResponseDirectPost('session-d', vpToken, { id: 'ps' }); assert(result.ok === true); - const stored = await helper.getRPStateBySessionId("session-d"); + const stored = await helper.getRPStateBySessionId('session-d'); assert(stored?.completed === true); assert(stored?.response_code); - assert(stored?.presentation_submission?.id === "ps"); + assert(stored?.presentation_submission?.id === 'ps'); assert(stored?.vp_token); const mapped = await kv.get(`response_code:${stored?.response_code}`); - assert(mapped === "session-d"); + assert(mapped === 'session-d'); }); - it("should return errors for invalid inputs or state", async () => { + it('should return errors for invalid inputs or state', async () => { const kv = new MemoryStore(); - const httpClient: HttpClient = { get: async () => { throw new Error("unexpected http call"); } }; + const httpClient: HttpClient = { + get: async () => { + throw new Error('unexpected http call'); + }, + }; const helper = new OpenID4VPClientAPI( kv, { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const missingState = await helper.handleResponseDirectPost(undefined, "vp", null); + const missingState = await helper.handleResponseDirectPost(undefined, 'vp', null); assert(missingState.ok === false); - const missingVp = await helper.handleResponseDirectPost("state", undefined, null); + const missingVp = await helper.handleResponseDirectPost('state', undefined, null); assert(missingVp.ok === false); - const missingRp = await helper.handleResponseDirectPost("missing", "vp", null); + const missingRp = await helper.handleResponseDirectPost('missing', 'vp', null); assert(missingRp.ok === false); const rpState = { - session_id: "session-e", + session_id: 'session-e', is_cross_device: true, - signed_request: "signed", - state: "session-e", - nonce: "nonce", + signed_request: 'signed', + state: 'session-e', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-e", - rp_eph_pub: { kty: "EC", crv: "P-256", x: "x", y: "y" }, - rp_eph_priv: { kty: "EC", crv: "P-256", x: "x", y: "y", d: "d" }, + rp_eph_kid: 'kid-e', + rp_eph_pub: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y' }, + rp_eph_priv: { kty: 'EC', crv: 'P-256', x: 'x', y: 'y', d: 'd' }, encrypted_response: null, vp_token: null, presentation_submission: null, @@ -545,19 +552,23 @@ describe("OpenID4VPClientAPI.handleResponseDirectPost", () => { claims: null, completed: true, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - const alreadyDone = await helper.handleResponseDirectPost("session-e", "vp", null); + const alreadyDone = await helper.handleResponseDirectPost('session-e', 'vp', null); assert(alreadyDone.ok === false); }); }); -describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { - it("should decrypt and store response details", async () => { +describe('OpenID4VPClientAPI.handleEncryptedAuthorizationResponse', () => { + it('should decrypt and store response details', async () => { const kv = new MemoryStore(); - const httpClient: HttpClient = { get: async () => { throw new Error("unexpected http call"); } }; + const httpClient: HttpClient = { + get: async () => { + throw new Error('unexpected http call'); + }, + }; const helper = new OpenID4VPClientAPI( kv, @@ -565,33 +576,33 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const { publicKey, privateKey } = await generateKeyPair("ECDH-ES"); + const { publicKey, privateKey } = await generateKeyPair('ECDH-ES'); const rpEphPub = await exportJWK(publicKey); const rpEphPriv = await exportJWK(privateKey); - rpEphPub.kid = "kid-e2ee"; - rpEphPriv.kid = "kid-e2ee"; + rpEphPub.kid = 'kid-e2ee'; + rpEphPriv.kid = 'kid-e2ee'; const rpState = { - session_id: "session-e2ee", + session_id: 'session-e2ee', is_cross_device: true, - signed_request: "signed", - state: "session-e2ee", - nonce: "nonce", + signed_request: 'signed', + state: 'session-e2ee', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-e2ee", + rp_eph_kid: 'kid-e2ee', rp_eph_pub: rpEphPub, rp_eph_priv: rpEphPriv, encrypted_response: null, @@ -601,33 +612,41 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - await kv.set("key:kid-e2ee", rpState.session_id); + await kv.set('key:kid-e2ee', rpState.session_id); - const payload = { state: "session-e2ee", vp_token: { pid: "vp" }, presentation_submission: { id: "ps" } }; + const payload = { + state: 'session-e2ee', + vp_token: { pid: 'vp' }, + presentation_submission: { id: 'ps' }, + }; const jwe = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(payload))) - .setProtectedHeader({ alg: "ECDH-ES", enc: "A256GCM" }) + .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A256GCM' }) .encrypt(publicKey); - const result = await helper.handleEncryptedAuthorizationResponse(jwe, "kid-e2ee"); + const result = await helper.handleEncryptedAuthorizationResponse(jwe, 'kid-e2ee'); assert(result.ok === true); - const stored = await helper.getRPStateBySessionId("session-e2ee"); + const stored = await helper.getRPStateBySessionId('session-e2ee'); assert(stored?.completed === true); assert(stored?.response_code); - assert(stored?.presentation_submission?.id === "ps"); + assert(stored?.presentation_submission?.id === 'ps'); assert(stored?.vp_token); const mapped = await kv.get(`response_code:${stored?.response_code}`); - assert(mapped === "session-e2ee"); + assert(mapped === 'session-e2ee'); }); - it("should return errors for missing state, missing vp_token, and completed session", async () => { + it('should return errors for missing state, missing vp_token, and completed session', async () => { const kv = new MemoryStore(); - const httpClient: HttpClient = { get: async () => { throw new Error("unexpected http call"); } }; + const httpClient: HttpClient = { + get: async () => { + throw new Error('unexpected http call'); + }, + }; const helper = new OpenID4VPClientAPI( kv, @@ -635,33 +654,33 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const { publicKey, privateKey } = await generateKeyPair("ECDH-ES"); + const { publicKey, privateKey } = await generateKeyPair('ECDH-ES'); const rpEphPub = await exportJWK(publicKey); const rpEphPriv = await exportJWK(privateKey); - rpEphPub.kid = "kid-e2ee-2"; - rpEphPriv.kid = "kid-e2ee-2"; + rpEphPub.kid = 'kid-e2ee-2'; + rpEphPriv.kid = 'kid-e2ee-2'; const rpState = { - session_id: "session-e2ee2", + session_id: 'session-e2ee2', is_cross_device: true, - signed_request: "signed", - state: "session-e2ee2", - nonce: "nonce", + signed_request: 'signed', + state: 'session-e2ee2', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-e2ee-2", + rp_eph_kid: 'kid-e2ee-2', rp_eph_pub: rpEphPub, rp_eph_priv: rpEphPriv, encrypted_response: null, @@ -671,37 +690,48 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { claims: null, completed: true, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - await kv.set("key:kid-e2ee-2", rpState.session_id); + await kv.set('key:kid-e2ee-2', rpState.session_id); - const payloadMissingState = { vp_token: { pid: "vp" }, presentation_submission: null }; - const jweMissingState = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(payloadMissingState))) - .setProtectedHeader({ alg: "ECDH-ES", enc: "A256GCM" }) + const payloadMissingState = { vp_token: { pid: 'vp' }, presentation_submission: null }; + const jweMissingState = await new CompactEncrypt( + new TextEncoder().encode(JSON.stringify(payloadMissingState)), + ) + .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A256GCM' }) .encrypt(publicKey); - const missingState = await helper.handleEncryptedAuthorizationResponse(jweMissingState, "kid-e2ee-2"); + const missingState = await helper.handleEncryptedAuthorizationResponse( + jweMissingState, + 'kid-e2ee-2', + ); assert(missingState.ok === false); - const payloadMissingVp = { state: "session-e2ee2", presentation_submission: null }; - const jweMissingVp = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(payloadMissingVp))) - .setProtectedHeader({ alg: "ECDH-ES", enc: "A256GCM" }) + const payloadMissingVp = { state: 'session-e2ee2', presentation_submission: null }; + const jweMissingVp = await new CompactEncrypt( + new TextEncoder().encode(JSON.stringify(payloadMissingVp)), + ) + .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A256GCM' }) .encrypt(publicKey); - const missingVp = await helper.handleEncryptedAuthorizationResponse(jweMissingVp, "kid-e2ee-2"); + const missingVp = await helper.handleEncryptedAuthorizationResponse(jweMissingVp, 'kid-e2ee-2'); assert(missingVp.ok === false); - const payloadOk = { state: "session-e2ee2", vp_token: "vp", presentation_submission: null }; + const payloadOk = { state: 'session-e2ee2', vp_token: 'vp', presentation_submission: null }; const jweOk = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(payloadOk))) - .setProtectedHeader({ alg: "ECDH-ES", enc: "A256GCM" }) + .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A256GCM' }) .encrypt(publicKey); - const completed = await helper.handleEncryptedAuthorizationResponse(jweOk, "kid-e2ee-2"); + const completed = await helper.handleEncryptedAuthorizationResponse(jweOk, 'kid-e2ee-2'); assert(completed.ok === false); }); - it("should return error when rpState or decryption is missing/invalid", async () => { + it('should return error when rpState or decryption is missing/invalid', async () => { const kv = new MemoryStore(); - const httpClient: HttpClient = { get: async () => { throw new Error("unexpected http call"); } }; + const httpClient: HttpClient = { + get: async () => { + throw new Error('unexpected http call'); + }, + }; const helper = new OpenID4VPClientAPI( kv, @@ -709,36 +739,39 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { credentialEngineOptions: { clockTolerance: 0, subtle: crypto.subtle, - lang: "en", + lang: 'en', trustedCertificates: [], - trustedCredentialIssuerIdentifiers: undefined + trustedCredentialIssuerIdentifiers: undefined, }, - redirectUri: "openid4vp://cb" + redirectUri: 'openid4vp://cb', }, - httpClient + httpClient, ); - const missingRp = await helper.handleEncryptedAuthorizationResponse("invalid.jwe", "missing-kid"); + const missingRp = await helper.handleEncryptedAuthorizationResponse( + 'invalid.jwe', + 'missing-kid', + ); assert(missingRp.ok === false); - const { publicKey, privateKey } = await generateKeyPair("ECDH-ES"); + const { publicKey, privateKey } = await generateKeyPair('ECDH-ES'); const rpEphPub = await exportJWK(publicKey); const rpEphPriv = await exportJWK(privateKey); - rpEphPub.kid = "kid-e2ee-3"; - rpEphPriv.kid = "kid-e2ee-3"; + rpEphPub.kid = 'kid-e2ee-3'; + rpEphPriv.kid = 'kid-e2ee-3'; const rpState = { - session_id: "session-e2ee3", + session_id: 'session-e2ee3', is_cross_device: true, - signed_request: "signed", - state: "session-e2ee3", - nonce: "nonce", + signed_request: 'signed', + state: 'session-e2ee3', + nonce: 'nonce', callback_endpoint: null, - audience: "aud", - presentation_request_id: "pid", + audience: 'aud', + presentation_request_id: 'pid', presentation_definition: null, dcql_query: null, - rp_eph_kid: "kid-e2ee-3", + rp_eph_kid: 'kid-e2ee-3', rp_eph_pub: rpEphPub, rp_eph_priv: rpEphPriv, encrypted_response: null, @@ -748,18 +781,21 @@ describe("OpenID4VPClientAPI.handleEncryptedAuthorizationResponse", () => { claims: null, completed: null, presentation_during_issuance_session: null, - date_created: Date.now() + date_created: Date.now(), }; await helper.saveRPState(rpState.session_id, rpState); - await kv.set("key:kid-e2ee-3", rpState.session_id); + await kv.set('key:kid-e2ee-3', rpState.session_id); - const okPayload = { state: "session-e2ee3", vp_token: "vp", presentation_submission: null }; + const okPayload = { state: 'session-e2ee3', vp_token: 'vp', presentation_submission: null }; const okJwe = await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(okPayload))) - .setProtectedHeader({ alg: "ECDH-ES", enc: "A256GCM" }) + .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A256GCM' }) .encrypt(publicKey); - const tamperedJwe = okJwe.split(".").slice(0, 4).concat("invalid!").join("."); - const decryptFail = await helper.handleEncryptedAuthorizationResponse(tamperedJwe, "kid-e2ee-3"); + const tamperedJwe = okJwe.split('.').slice(0, 4).concat('invalid!').join('.'); + const decryptFail = await helper.handleEncryptedAuthorizationResponse( + tamperedJwe, + 'kid-e2ee-3', + ); assert(decryptFail.ok === false); }); }); diff --git a/src/protocols/openid4vp/OpenID4VPClientAPI.ts b/src/protocols/openid4vp/OpenID4VPClientAPI.ts index 8342d06..1db8794 100644 --- a/src/protocols/openid4vp/OpenID4VPClientAPI.ts +++ b/src/protocols/openid4vp/OpenID4VPClientAPI.ts @@ -1,39 +1,68 @@ -import { err, GenericStore, ok, Result } from "../../core"; -import { generateRandomIdentifier } from "../../utils/util"; -import { MsoMdocParser } from "../../credential-parsers/MsoMdocParser"; -import { SDJWTVCParser } from "../../credential-parsers/SDJWTVCParser"; -import { MsoMdocVerifier } from "../../credential-verifiers/MsoMdocVerifier"; -import { SDJWTVCVerifier } from "../../credential-verifiers/SDJWTVCVerifier"; -import { CustomCredentialSvg } from "../../functions/CustomCredentialSvg"; -import { HttpClient } from "../../interfaces"; -import { ParsingEngine } from "../../ParsingEngine"; -import { PublicKeyResolverEngine } from "../../PublicKeyResolverEngine"; -import { CredentialRenderingService } from "../../rendering"; -import { VerifiableCredentialFormat } from "../../types"; -import { fromBase64Url, toBase64Url } from "../../utils/util"; -import { TransactionData } from "./transactionData"; -import { CredentialEngineOptions, CredentialIssuerMetadata, IacasResponse, OpenID4VPClientIdScheme, OpenID4VPOptions, PresentationClaims, PresentationInfo, OpenID4VPResponseMode, RPState } from "./types"; +import { err, GenericStore, ok, Result } from '../../core'; +import { generateRandomIdentifier } from '../../utils/util'; +import { MsoMdocParser } from '../../credential-parsers/MsoMdocParser'; +import { SDJWTVCParser } from '../../credential-parsers/SDJWTVCParser'; +import { MsoMdocVerifier } from '../../credential-verifiers/MsoMdocVerifier'; +import { SDJWTVCVerifier } from '../../credential-verifiers/SDJWTVCVerifier'; +import { CustomCredentialSvg } from '../../functions/CustomCredentialSvg'; +import { HttpClient } from '../../interfaces'; +import { ParsingEngine } from '../../ParsingEngine'; +import { PublicKeyResolverEngine } from '../../PublicKeyResolverEngine'; +import { CredentialRenderingService } from '../../rendering'; +import { VerifiableCredentialFormat } from '../../types'; +import { fromBase64Url, toBase64Url } from '../../utils/util'; +import { TransactionData } from './transactionData'; +import { + CredentialEngineOptions, + CredentialIssuerMetadata, + IacasResponse, + OpenID4VPClientIdScheme, + OpenID4VPOptions, + PresentationClaims, + PresentationInfo, + OpenID4VPResponseMode, + RPState, +} from './types'; import { DcqlPresentationResult } from 'dcql'; -import { exportJWK, generateKeyPair, importPKCS8, SignJWT, compactDecrypt, CompactDecryptResult, importJWK } from "jose"; -import { serializeDcqlQuery } from "../../utils/serializeDcqlQuery"; -import { prependToPath } from "../../utils"; +import { + exportJWK, + generateKeyPair, + importPKCS8, + SignJWT, + compactDecrypt, + CompactDecryptResult, + importJWK, +} from 'jose'; +import { serializeDcqlQuery } from '../../utils/serializeDcqlQuery'; +import { prependToPath } from '../../utils'; export const OpenID4VPClientErrors = { - MissingRPStateForKid: "missing_rpstate_for_kid", - MissingRPState: "missing_rpstate", - JWEDecryptionFailure: "jwe_decryption_failure", - MissingState: "missing_state", - PresentationAlreadyCompleted: "presentation_already_completed", - MissingVpToken: "missing_vp_token", - MissingPresentationSubmissionAndVpToken: "missing_presentation_submission_and_vp_token", - SignedRequestObjectInvalidated: "signed_request_object_invalidated" + MissingRPStateForKid: 'missing_rpstate_for_kid', + MissingRPState: 'missing_rpstate', + JWEDecryptionFailure: 'jwe_decryption_failure', + MissingState: 'missing_state', + PresentationAlreadyCompleted: 'presentation_already_completed', + MissingVpToken: 'missing_vp_token', + MissingPresentationSubmissionAndVpToken: 'missing_presentation_submission_and_vp_token', + SignedRequestObjectInvalidated: 'signed_request_object_invalidated', } as const; -export type OpenID4VPClientError = typeof OpenID4VPClientErrors[keyof typeof OpenID4VPClientErrors]; +export type OpenID4VPClientError = + (typeof OpenID4VPClientErrors)[keyof typeof OpenID4VPClientErrors]; const RESERVED_SDJWT_TOPLEVEL = new Set([ - 'iss', 'sub', 'aud', 'nbf', 'exp', 'iat', 'jti', 'vct', 'cnf', - 'transaction_data_hashes', 'transaction_data_hashes_alg', 'vct#integrity' + 'iss', + 'sub', + 'aud', + 'nbf', + 'exp', + 'iat', + 'jti', + 'vct', + 'cnf', + 'transaction_data_hashes', + 'transaction_data_hashes_alg', + 'vct#integrity', ]); const decoder = new TextDecoder(); const encoder = new TextEncoder(); @@ -43,21 +72,27 @@ export class OpenID4VPClientAPI { private options: OpenID4VPOptions; private httpClient: HttpClient; - constructor(kvStore: GenericStore, options: OpenID4VPOptions, httpClient: HttpClient) { + constructor( + kvStore: GenericStore, + options: OpenID4VPOptions, + httpClient: HttpClient, + ) { this.rpStateKV = kvStore; this.options = options; this.httpClient = httpClient; } private getBase64UrlSha256(data: Uint8Array): Promise { - return this.options.credentialEngineOptions.subtle.digest("SHA-256", data as Uint8Array).then((digest) => { - return toBase64Url(new Uint8Array(digest)); - }); + return this.options.credentialEngineOptions.subtle + .digest('SHA-256', data as Uint8Array) + .then((digest) => { + return toBase64Url(new Uint8Array(digest)); + }); } private decodeBase64Certificate(base64Certificate: string): Uint8Array { - if (typeof Buffer !== "undefined") { - return Uint8Array.from(Buffer.from(base64Certificate, "base64")); + if (typeof Buffer !== 'undefined') { + return Uint8Array.from(Buffer.from(base64Certificate, 'base64')); } const binary = atob(base64Certificate); @@ -69,17 +104,15 @@ export class OpenID4VPClientAPI { } private calculatePresentationClaimValue = (value: unknown): string => { - const normalizedValue = value instanceof Map - ? Object.fromEntries(value) - : value; + const normalizedValue = value instanceof Map ? Object.fromEntries(value) : value; return typeof normalizedValue === 'object' ? JSON.stringify(normalizedValue) : String(normalizedValue); - } + }; private async initializeCredentialEngine() { - console.log("Initializing credential engine...") + console.log('Initializing credential engine...'); const ctx = { clockTolerance: this.options.credentialEngineOptions.clockTolerance, @@ -89,23 +122,50 @@ export class OpenID4VPClientAPI { }; if (this.options.credentialEngineOptions.trustedCredentialIssuerIdentifiers) { - const result = (await Promise.all(this.options.credentialEngineOptions.trustedCredentialIssuerIdentifiers.map(async (credentialIssuerIdentifier) => - this.httpClient.get(prependToPath(credentialIssuerIdentifier, ".well-known/openid-credential-issuer") ?? "") - .then((res) => res.data as CredentialIssuerMetadata) - .catch((e) => { console.error(e); return null; }) - ))).filter((r): r is CredentialIssuerMetadata => r !== null); - - const iacasResponses = (await Promise.all(result.map(async (metadata) => { - if (metadata && metadata.mdoc_iacas_uri) { - return this.httpClient.get(metadata.mdoc_iacas_uri).then((res) => res.data as IacasResponse).catch((e) => { console.error(e); return null; }) - } - return null; - }))).filter((r): r is IacasResponse => r !== null); + const result = ( + await Promise.all( + this.options.credentialEngineOptions.trustedCredentialIssuerIdentifiers.map( + async (credentialIssuerIdentifier) => + this.httpClient + .get( + prependToPath( + credentialIssuerIdentifier, + '.well-known/openid-credential-issuer', + ) ?? '', + ) + .then((res) => res.data as CredentialIssuerMetadata) + .catch((e) => { + console.error(e); + return null; + }), + ), + ) + ).filter((r): r is CredentialIssuerMetadata => r !== null); + + const iacasResponses = ( + await Promise.all( + result.map(async (metadata) => { + if (metadata && metadata.mdoc_iacas_uri) { + return this.httpClient + .get(metadata.mdoc_iacas_uri) + .then((res) => res.data as IacasResponse) + .catch((e) => { + console.error(e); + return null; + }); + } + return null; + }), + ) + ).filter((r): r is IacasResponse => r !== null); for (const iacaResponse of iacasResponses) { - const pemCertificates = iacaResponse.iacas?.map((cert) => - cert.certificate ? `-----BEGIN CERTIFICATE-----\n${cert.certificate}\n-----END CERTIFICATE-----\n` : null - ) ?? []; + const pemCertificates = + iacaResponse.iacas?.map((cert) => + cert.certificate + ? `-----BEGIN CERTIFICATE-----\n${cert.certificate}\n-----END CERTIFICATE-----\n` + : null, + ) ?? []; for (const pem of pemCertificates) { if (pem) { ctx.trustedCertificates.push(pem); @@ -116,9 +176,9 @@ export class OpenID4VPClientAPI { const credentialParsingEngine = ParsingEngine(); credentialParsingEngine.register(SDJWTVCParser({ context: ctx, httpClient: this.httpClient })); - console.log("Registered SDJWTVCParser..."); + console.log('Registered SDJWTVCParser...'); credentialParsingEngine.register(MsoMdocParser({ context: ctx, httpClient: this.httpClient })); - console.log("Registered MsoMdocParser..."); + console.log('Registered MsoMdocParser...'); const pkResolverEngine = PublicKeyResolverEngine(); const openid4vcRendering = CustomCredentialSvg({ httpClient: this.httpClient }); @@ -126,7 +186,11 @@ export class OpenID4VPClientAPI { return { credentialParsingEngine, msoMdocVerifier: MsoMdocVerifier({ context: ctx, pkResolverEngine: pkResolverEngine }), - sdJwtVerifier: SDJWTVCVerifier({ context: ctx, pkResolverEngine: pkResolverEngine, httpClient: this.httpClient }), + sdJwtVerifier: SDJWTVCVerifier({ + context: ctx, + pkResolverEngine: pkResolverEngine, + httpClient: this.httpClient, + }), openid4vcRendering, credentialRendering, }; @@ -140,32 +204,33 @@ export class OpenID4VPClientAPI { x5c: string[], responseMode: OpenID4VPResponseMode, callbackEndpoint?: string, - clientIdScheme: OpenID4VPClientIdScheme = "x509_san_dns", + clientIdScheme: OpenID4VPClientIdScheme = 'x509_san_dns', ): Promise<{ url: URL; stateId: string; rpState: RPState }> { - - console.log("Presentation Request: Session id used for authz req ", sessionId); + console.log('Presentation Request: Session id used for authz req ', sessionId); const nonce = crypto.randomUUID(); const state = sessionId; let clientIdWithoutPrefix; switch (clientIdScheme) { - case "x509_hash": + case 'x509_hash': const leafCertificate = x5c[0]; - clientIdWithoutPrefix = await this.getBase64UrlSha256(this.decodeBase64Certificate(leafCertificate)); + clientIdWithoutPrefix = await this.getBase64UrlSha256( + this.decodeBase64Certificate(leafCertificate), + ); break; - case "x509_san_dns": + case 'x509_san_dns': clientIdWithoutPrefix = new URL(responseUri).hostname; } const fullClientId = `${clientIdScheme}:${clientIdWithoutPrefix}`; const [rsaImportedPrivateKey, rpEphemeralKeypair] = await Promise.all([ importPKCS8(privateKeyPem, 'ES256'), - generateKeyPair('ECDH-ES') + generateKeyPair('ECDH-ES'), ]); const [exportedEphPub, exportedEphPriv] = await Promise.all([ exportJWK(rpEphemeralKeypair.publicKey), - exportJWK(rpEphemeralKeypair.privateKey) + exportJWK(rpEphemeralKeypair.privateKey), ]); exportedEphPub.kid = generateRandomIdentifier(8); @@ -174,62 +239,55 @@ export class OpenID4VPClientAPI { exportedEphPub.alg = 'ECDH-ES'; let transactionDataObject: any[] = []; if (presentationRequest?.dcql_query?.credentials) { - transactionDataObject = await Promise.all(presentationRequest?.dcql_query?.credentials - .filter((cred: any) => cred._transaction_data_type !== undefined) - .map(async (cred: any) => { - if (!cred._transaction_data_type) { - return null; - } - const txData = TransactionData(cred._transaction_data_type); - if (!txData) { - return null; - } - return await txData - .generateTransactionDataRequestObject(cred.id); - })); + transactionDataObject = await Promise.all( + presentationRequest?.dcql_query?.credentials + .filter((cred: any) => cred._transaction_data_type !== undefined) + .map(async (cred: any) => { + if (!cred._transaction_data_type) { + return null; + } + const txData = TransactionData(cred._transaction_data_type); + if (!txData) { + return null; + } + return await txData.generateTransactionDataRequestObject(cred.id); + }), + ); } transactionDataObject = transactionDataObject.filter((td) => td !== null); const signedRequestObject = await new SignJWT({ response_uri: responseUri, - aud: "https://self-issued.me/v2", + aud: 'https://self-issued.me/v2', iss: new URL(responseUri).hostname, client_id: fullClientId, - response_type: "vp_token", + response_type: 'vp_token', response_mode: responseMode, state: state, nonce: nonce, - dcql_query: presentationRequest?.dcql_query?.credentials ? serializeDcqlQuery(JSON.parse(JSON.stringify(presentationRequest.dcql_query))) : null, + dcql_query: presentationRequest?.dcql_query?.credentials + ? serializeDcqlQuery(JSON.parse(JSON.stringify(presentationRequest.dcql_query))) + : null, client_metadata: { - "jwks": { - "keys": [ - exportedEphPub - ] + jwks: { + keys: [exportedEphPub], }, - "encrypted_response_enc_values_supported": ["A256GCM"], - "vp_formats": { - "vc+sd-jwt": { - "sd-jwt_alg_values": [ - "ES256", - ], - "kb-jwt_alg_values": [ - "ES256", - ] + encrypted_response_enc_values_supported: ['A256GCM'], + vp_formats: { + 'vc+sd-jwt': { + 'sd-jwt_alg_values': ['ES256'], + 'kb-jwt_alg_values': ['ES256'], }, - "dc+sd-jwt": { - "sd-jwt_alg_values": [ - "ES256", - ], - "kb-jwt_alg_values": [ - "ES256", - ] + 'dc+sd-jwt': { + 'sd-jwt_alg_values': ['ES256'], + 'kb-jwt_alg_values': ['ES256'], }, - "mso_mdoc": { - "alg": ["ES256"] - } - } + mso_mdoc: { + alg: ['ES256'], + }, + }, }, - transaction_data: transactionDataObject.length > 0 ? transactionDataObject : undefined + transaction_data: transactionDataObject.length > 0 ? transactionDataObject : undefined, }) .setIssuedAt() .setProtectedHeader({ @@ -238,7 +296,7 @@ export class OpenID4VPClientAPI { typ: 'oauth-authz-req+jwt', }) .sign(rsaImportedPrivateKey); - const redirectUri = "openid4vp://cb"; + const redirectUri = 'openid4vp://cb'; const newRpState: RPState = { session_id: sessionId, @@ -251,13 +309,12 @@ export class OpenID4VPClientAPI { audience: fullClientId, presentation_request_id: - presentationRequest.id ?? - (presentationRequest.dcql_query as any)?.credentials?.[0]?.id, + presentationRequest.id ?? (presentationRequest.dcql_query as any)?.credentials?.[0]?.id, presentation_definition: null, dcql_query: presentationRequest?.dcql_query ?? null, - rp_eph_kid: exportedEphPub.kid ?? "", + rp_eph_kid: exportedEphPub.kid ?? '', rp_eph_pub: exportedEphPub, rp_eph_priv: exportedEphPriv, @@ -276,21 +333,21 @@ export class OpenID4VPClientAPI { }; await this.saveRPState(sessionId, newRpState); - await this.rpStateKV.set("key:" + exportedEphPub.kid, sessionId); + await this.rpStateKV.set('key:' + exportedEphPub.kid, sessionId); // await this.rpStateRepository.save(newRpState); - const requestUri = baseUri + "/verification/request-object?id=" + state; + const requestUri = baseUri + '/verification/request-object?id=' + state; const redirectParameters = { client_id: fullClientId, - request_uri: requestUri + request_uri: requestUri, }; const searchParams = new URLSearchParams(redirectParameters); - const authorizationRequestURL = new URL(redirectUri + "?" + searchParams.toString()); // must be openid4vp://cb + const authorizationRequestURL = new URL(redirectUri + '?' + searchParams.toString()); // must be openid4vp://cb - console.log("AUTHZ REQ = ", authorizationRequestURL); + console.log('AUTHZ REQ = ', authorizationRequestURL); return { url: authorizationRequestURL, stateId: state, rpState: newRpState }; } @@ -305,8 +362,12 @@ export class OpenID4VPClientAPI { private async validateDcqlVpToken( vp_token_list: any, dcql_query: any, - rpState: RPState - ): Promise<{ presentationClaims?: PresentationClaims, messages?: PresentationInfo, error?: Error }> { + rpState: RPState, + ): Promise<{ + presentationClaims?: PresentationClaims; + messages?: PresentationInfo; + error?: Error; + }> { const presentationClaims: PresentationClaims = {}; const ce = await this.initializeCredentialEngine(); const messages: PresentationInfo = {}; @@ -325,29 +386,42 @@ export class OpenID4VPClientAPI { try { const [kbjwt] = vp.split('~').reverse(); const [_kbjwtEncodedHeader, kbjwtEncodedPayload, _kbjwtSig] = kbjwt.split('.'); - const kbjwtPayload = JSON.parse(decoder.decode(fromBase64Url(kbjwtEncodedPayload))) as Record; - if (Object.keys(kbjwtPayload).includes('transaction_data_hashes') && descriptor._transaction_data_type !== undefined) { + const kbjwtPayload = JSON.parse( + decoder.decode(fromBase64Url(kbjwtEncodedPayload)), + ) as Record; + if ( + Object.keys(kbjwtPayload).includes('transaction_data_hashes') && + descriptor._transaction_data_type !== undefined + ) { const txData = TransactionData(descriptor._transaction_data_type); if (!txData) { - return { error: new Error("specific transaction_data not supported error") }; + return { error: new Error('specific transaction_data not supported error') }; } - const { status, message } = await txData.validateTransactionDataResponse(descriptor.id, { - transaction_data_hashes: (kbjwtPayload as any).transaction_data_hashes as string[], - transaction_data_hashes_alg: (kbjwtPayload as any).transaction_data_hashes_alg as string[] | undefined - }); - console.log("Message: ", message) + const { status, message } = await txData.validateTransactionDataResponse( + descriptor.id, + { + transaction_data_hashes: (kbjwtPayload as any) + .transaction_data_hashes as string[], + transaction_data_hashes_alg: (kbjwtPayload as any).transaction_data_hashes_alg as + string[] | undefined, + }, + ); + console.log('Message: ', message); messages[descriptor.id] = [message]; if (!status) { - return { error: new Error("transaction_data validation error") }; + return { error: new Error('transaction_data validation error') }; } - console.log("VALIDATED TRANSACTION DATA"); - } - else if (descriptor._transaction_data_type !== undefined) { - return { error: new Error("transaction_data_hashes is missing from transaction data response") }; + console.log('VALIDATED TRANSACTION DATA'); + } else if (descriptor._transaction_data_type !== undefined) { + return { + error: new Error( + 'transaction_data_hashes is missing from transaction data response', + ), + }; } } catch (e) { console.error(e); - return { error: new Error("transaction_data validation error") }; + return { error: new Error('transaction_data validation error') }; } const verificationResult = await ce.sdJwtVerifier.verify({ rawCredential: vp, @@ -357,12 +431,18 @@ export class OpenID4VPClientAPI { }, }); if (!verificationResult.success) { - return { error: new Error(`SD-JWT verification failed for ${descriptor.id}: ${verificationResult.error}`) }; + return { + error: new Error( + `SD-JWT verification failed for ${descriptor.id}: ${verificationResult.error}`, + ), + }; } const parseResult = await ce.credentialParsingEngine.parse({ rawCredential: vp }); if (!parseResult.success) { - return { error: new Error(`Parsing SD-JWT failed for ${descriptor.id}: ${parseResult.error}`) }; + return { + error: new Error(`Parsing SD-JWT failed for ${descriptor.id}: ${parseResult.error}`), + }; } const signedClaims = parseResult.value.signedClaims; @@ -370,45 +450,55 @@ export class OpenID4VPClientAPI { vct: signedClaims.vct, credential_format: VerifiableCredentialFormat.DC_SDJWT, claims: signedClaims, - cryptographic_holder_binding: true + cryptographic_holder_binding: true, }; const dcqlResult = DcqlPresentationResult.fromDcqlPresentation( /* @ts-ignore */ { [descriptor.id]: [shaped] }, - { dcqlQuery: dcql_query } + { dcqlQuery: dcql_query }, ); if (!dcqlResult.credential_matches[descriptor.id]?.success) { return { error: new Error(`DCQL validation failed for ${descriptor.id}`) }; } - const output = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0].meta.output as any; + const output = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0].meta + .output as any; if ( output.credential_format === VerifiableCredentialFormat.VC_SDJWT || output.credential_format === VerifiableCredentialFormat.DC_SDJWT ) { - const claims = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0]?.claims as any; - const dcqlOut = claims?.valid_claim_sets?.[0]?.output as Record | undefined; + const claims = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0] + ?.claims as any; + const dcqlOut = claims?.valid_claim_sets?.[0]?.output as + Record | undefined; const signedClaims = parseResult.value.signedClaims as Record; const requestedAll = descriptor?.claims == null; // Get all claims if no specific claims were requested - const source: Record = - requestedAll - ? signedClaims - : (dcqlOut && Object.keys(dcqlOut).length > 0 ? dcqlOut : signedClaims); + const source: Record = requestedAll + ? signedClaims + : dcqlOut && Object.keys(dcqlOut).length > 0 + ? dcqlOut + : signedClaims; const filteredSource = Object.fromEntries( - Object.entries(source).filter(([k]) => !RESERVED_SDJWT_TOPLEVEL.has(k) && !k.startsWith('_')) + Object.entries(source).filter( + ([k]) => !RESERVED_SDJWT_TOPLEVEL.has(k) && !k.startsWith('_'), + ), + ); + presentationClaims[descriptor.id] = Object.entries(filteredSource).map( + ([key, value]) => ({ + key, + name: key, + value: this.calculatePresentationClaimValue(value), + }), ); - presentationClaims[descriptor.id] = Object.entries(filteredSource).map(([key, value]) => ({ - key, - name: key, - value: this.calculatePresentationClaimValue(value), - })); } else { - return { error: new Error(`Unexpected credential_format for descriptor ${descriptor.id}`) }; + return { + error: new Error(`Unexpected credential_format for descriptor ${descriptor.id}`), + }; } } else { // ========== mdoc ========== @@ -420,76 +510,123 @@ export class OpenID4VPClientAPI { expectedNonce: rpState.nonce, responseUri: this.options.redirectUri, verifierEncryptionJwk, - handoverType: rpState.response_mode === OpenID4VPResponseMode.DC_API_JWT && rpState.audience.startsWith("origin:") ? "dc_api" : "redirect", - dcApiOrigin: rpState.response_mode === OpenID4VPResponseMode.DC_API_JWT && rpState.audience.startsWith("origin:") - ? rpState.audience.replace(/^origin:/, "") - : undefined, + handoverType: + rpState.response_mode === OpenID4VPResponseMode.DC_API_JWT && + rpState.audience.startsWith('origin:') + ? 'dc_api' + : 'redirect', + dcApiOrigin: + rpState.response_mode === OpenID4VPResponseMode.DC_API_JWT && + rpState.audience.startsWith('origin:') + ? rpState.audience.replace(/^origin:/, '') + : undefined, }, }); if (!verificationResult.success) { - return { error: new Error(`mDoc verification failed for ${descriptor.id}: ${verificationResult.error}`) }; + return { + error: new Error( + `mDoc verification failed for ${descriptor.id}: ${verificationResult.error}`, + ), + }; } const parseResult = await ce.credentialParsingEngine.parse({ rawCredential: vp }); if (!parseResult.success) { - return { error: new Error(`Parsing mDoc failed for ${descriptor.id}: ${parseResult.error}`) }; + return { + error: new Error(`Parsing mDoc failed for ${descriptor.id}: ${parseResult.error}`), + }; } const signedClaims = parseResult.value.signedClaims; const shaped = { credential_format: VerifiableCredentialFormat.MSO_MDOC, doctype: descriptor.meta?.doctype_value, cryptographic_holder_binding: true, - namespaces: signedClaims + namespaces: signedClaims, }; const dcqlResult = DcqlPresentationResult.fromDcqlPresentation( /* @ts-ignore */ { [descriptor.id]: [shaped] }, - { dcqlQuery: dcql_query } + { dcqlQuery: dcql_query }, ); if (!dcqlResult.credential_matches[descriptor.id]?.success) { - return { error: new Error(`DCQL validation failed for mdoc descriptor ${descriptor.id}`) }; + return { + error: new Error(`DCQL validation failed for mdoc descriptor ${descriptor.id}`), + }; } - const output = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0].meta.output as any; + const output = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0].meta + .output as any; if (output.credential_format === VerifiableCredentialFormat.MSO_MDOC) { - const claimsObject = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0].claims as any; + const claimsObject = dcqlResult.credential_matches[descriptor.id].valid_credentials?.[0] + .claims as any; if (!claimsObject) { - return { error: new Error(`No claims found in mdoc for doctype ${descriptor.meta?.doctype_value}`) }; + return { + error: new Error( + `No claims found in mdoc for doctype ${descriptor.meta?.doctype_value}`, + ), + }; } - const outputByNamespace = claimsObject.valid_claim_sets?.[0]?.output as Record> | undefined; + const outputByNamespace = claimsObject.valid_claim_sets?.[0]?.output as + Record> | undefined; const requestedNamespace = Array.isArray(descriptor?.claims) - ? descriptor.claims.find((c: any) => Array.isArray(c?.path) && typeof c.path[0] === "string")?.path?.[0] + ? descriptor.claims.find( + (c: any) => Array.isArray(c?.path) && typeof c.path[0] === 'string', + )?.path?.[0] : undefined; - const namespaceKey = requestedNamespace - ?? (outputByNamespace ? Object.keys(outputByNamespace)[0] : undefined); + const namespaceKey = + requestedNamespace ?? + (outputByNamespace ? Object.keys(outputByNamespace)[0] : undefined); if (!namespaceKey || !outputByNamespace?.[namespaceKey]) { - return { error: new Error(`No mdoc output namespace found for descriptor ${descriptor.id}`) }; + return { + error: new Error(`No mdoc output namespace found for descriptor ${descriptor.id}`), + }; } - presentationClaims[descriptor.id] = Object.entries(outputByNamespace[namespaceKey]).map(([key, value]) => ({ - key, - name: key, - value: this.calculatePresentationClaimValue(value), - })); + presentationClaims[descriptor.id] = Object.entries(outputByNamespace[namespaceKey]).map( + ([key, value]) => ({ + key, + name: key, + value: this.calculatePresentationClaimValue(value), + }), + ); } else { - return { error: new Error(`Unexpected mdoc credential_format in output for descriptor ${descriptor.id}`) }; + return { + error: new Error( + `Unexpected mdoc credential_format in output for descriptor ${descriptor.id}`, + ), + }; } } } catch (e) { console.error(`Error processing descriptor ${descriptor.id}:`, e); - return { error: new Error(`Internal error verifying or parsing VP for descriptor ${descriptor.id}`) }; + return { + error: new Error( + `Internal error verifying or parsing VP for descriptor ${descriptor.id}`, + ), + }; } } return { presentationClaims, messages }; } - public async getPresentationBySessionId(sessionId?: string, cleanupSession: boolean = false): Promise<{ status: true, presentations: unknown[], presentationInfo: PresentationInfo, rpState: RPState } | { status: false, error: Error }> { + public async getPresentationBySessionId( + sessionId?: string, + cleanupSession: boolean = false, + ): Promise< + | { + status: true; + presentations: unknown[]; + presentationInfo: PresentationInfo; + rpState: RPState; + } + | { status: false; error: Error } + > { if (!sessionId) { - console.error("getPresentationBySessionId: Invalid sessionId"); - const error = new Error("getPresentationBySessionId: Invalid sessionId"); + console.error('getPresentationBySessionId: Invalid sessionId'); + const error = new Error('getPresentationBySessionId: Invalid sessionId'); return { status: false, error }; } - const rpState = await this.rpStateKV.get(`rpstate:${sessionId}`) as RPState; + const rpState = (await this.rpStateKV.get(`rpstate:${sessionId}`)) as RPState; if (!rpState) { console.error("Couldn't get rpState with the session_id " + sessionId); @@ -498,12 +635,15 @@ export class OpenID4VPClientAPI { } if (!rpState.vp_token) { - console.error("Presentation has not been sent. session_id " + sessionId); - const error = new Error("Presentation has not been sent. session_id " + sessionId); + console.error('Presentation has not been sent. session_id ' + sessionId); + const error = new Error('Presentation has not been sent. session_id ' + sessionId); return { status: false, error }; } - const vp_token = JSON.parse(decoder.decode(fromBase64Url(rpState.vp_token))) as Record; + const vp_token = JSON.parse(decoder.decode(fromBase64Url(rpState.vp_token))) as Record< + string, + string[] + >; let presentationClaims; let presentationInfo: PresentationInfo = {}; @@ -515,14 +655,14 @@ export class OpenID4VPClientAPI { error = result.error; } if (error) { - console.error(error) + console.error(error); return { status: false, error }; } if (cleanupSession) { const responseCode = rpState.response_code; - rpState.state = ""; - rpState.session_id = ""; // invalidate session id - rpState.response_code = ""; + rpState.state = ''; + rpState.session_id = ''; // invalidate session id + rpState.response_code = ''; // await this.rpStateRepository.save(rpState); this.saveRPState(sessionId, rpState); if (responseCode) { @@ -539,10 +679,10 @@ export class OpenID4VPClientAPI { status: true, rpState: rpState, presentationInfo, - presentations: Object.values(vp_token).flatMap((v) => v) + presentations: Object.values(vp_token).flatMap((v) => v), }; } - const unkownErr = new Error("Uknown error"); + const unkownErr = new Error('Uknown error'); return { status: false, error: unkownErr }; } @@ -550,13 +690,13 @@ export class OpenID4VPClientAPI { const sessionId = await this.rpStateKV.get(`response_code:${responseCode}`); if (!sessionId) { - console.error("getPresentationByResponseCode: No session id for response code"); + console.error('getPresentationByResponseCode: No session id for response code'); return null; } - const rpState = await this.rpStateKV.get(`rpstate:${sessionId}`) as RPState; + const rpState = (await this.rpStateKV.get(`rpstate:${sessionId}`)) as RPState; if (!rpState) { - console.error("getPresentationByResponseCode: Missing rpState for session id"); + console.error('getPresentationByResponseCode: Missing rpState for session id'); return null; } @@ -564,9 +704,9 @@ export class OpenID4VPClientAPI { } public async getRPStateBySessionId(sessionId: string): Promise { - const rpState = await this.rpStateKV.get(`rpstate:${sessionId}`) as RPState; + const rpState = (await this.rpStateKV.get(`rpstate:${sessionId}`)) as RPState; if (!rpState) { - console.error("getRPStateBySessionId: Missing rpState for session id"); + console.error('getRPStateBySessionId: Missing rpState for session id'); return null; } @@ -574,71 +714,86 @@ export class OpenID4VPClientAPI { } public async getRPStateByKid(kid: string): Promise { - const sessionId = await this.rpStateKV.get("key:" + kid); + const sessionId = await this.rpStateKV.get('key:' + kid); if (!sessionId) { return null; } - const rpState = await this.rpStateKV.get(`rpstate:${sessionId}`) as RPState; + const rpState = (await this.rpStateKV.get(`rpstate:${sessionId}`)) as RPState; if (!rpState) { return null; } return rpState; } - - public async handleEncryptedAuthorizationResponse(response: any, kid: string): Promise> { + public async handleEncryptedAuthorizationResponse( + response: any, + kid: string, + ): Promise> { // get rpstate only to get the private key to decrypt the response const rpState = await this.getRPStateByKid(kid); if (!rpState) { return err( OpenID4VPClientErrors.MissingRPStateForKid, - "responseHandler: Could not retrieve rpState from kid" + 'responseHandler: Could not retrieve rpState from kid', ); } const rp_eph_priv = await importJWK(rpState.rp_eph_priv, 'ECDH-ES'); - const result = await compactDecrypt(response, rp_eph_priv).then((r: any) => ({ data: r, err: null })).catch((err: any) => ({ data: null, err: err })); + const result = await compactDecrypt(response, rp_eph_priv) + .then((r: any) => ({ data: r, err: null })) + .catch((err: any) => ({ data: null, err: err })); if (result.err) { - const errorDescription = result.err instanceof Error ? result.err.message : String(result.err); - console.error({ error: "JWE Decryption failure", error_description: result.err }); - console.log("Received JWE headers: ", JSON.parse(decoder.decode(fromBase64Url(response.split('.')[0])))); - console.log("Received JWE: ", response); + const errorDescription = + result.err instanceof Error ? result.err.message : String(result.err); + console.error({ error: 'JWE Decryption failure', error_description: result.err }); + console.log( + 'Received JWE headers: ', + JSON.parse(decoder.decode(fromBase64Url(response.split('.')[0]))), + ); + console.log('Received JWE: ', response); //ctx.res.status(500).send(error); return err(OpenID4VPClientErrors.JWEDecryptionFailure, errorDescription); } const { protectedHeader, plaintext } = result.data as CompactDecryptResult; - console.log("Protected header = ", protectedHeader) - const payload = JSON.parse(new TextDecoder().decode(plaintext)) as { state: string | undefined, vp_token: string | undefined, presentation_submission: any }; + console.log('Protected header = ', protectedHeader); + const payload = JSON.parse(new TextDecoder().decode(plaintext)) as { + state: string | undefined; + vp_token: string | undefined; + presentation_submission: any; + }; if (!payload?.state) { - return err(OpenID4VPClientErrors.MissingState, "Missing state"); + return err(OpenID4VPClientErrors.MissingState, 'Missing state'); } if (rpState.completed) { - return err(OpenID4VPClientErrors.PresentationAlreadyCompleted, "Presentation flow already completed"); + return err( + OpenID4VPClientErrors.PresentationAlreadyCompleted, + 'Presentation flow already completed', + ); } if (!payload.vp_token) { - return err(OpenID4VPClientErrors.MissingVpToken, "Encrypted Response: vp_token is missing"); + return err(OpenID4VPClientErrors.MissingVpToken, 'Encrypted Response: vp_token is missing'); } if (!payload.presentation_submission && !payload.vp_token) { return err( OpenID4VPClientErrors.MissingPresentationSubmissionAndVpToken, - "Encrypted Response: presentation_submission and vp_token are missing" + 'Encrypted Response: presentation_submission and vp_token are missing', ); } rpState.response_code = toBase64Url(encoder.encode(crypto.randomUUID())); await this.saveResponseCodeMapping(rpState.response_code, rpState.session_id); rpState.encrypted_response = response; rpState.presentation_submission = payload.presentation_submission; - console.log("Encoding....") + console.log('Encoding....'); rpState.vp_token = toBase64Url(encoder.encode(JSON.stringify(payload.vp_token))); rpState.date_created = Date.now(); rpState.completed = true; - console.log("Stored rp state = ", rpState) + console.log('Stored rp state = ', rpState); //await this.rpStateRepository.save(rpState); await this.saveRPState(rpState.session_id, rpState); return ok(rpState); @@ -647,23 +802,26 @@ export class OpenID4VPClientAPI { public async handleResponseDirectPost( state: string | undefined, vp_token: Record | undefined, - presentation_submission: any + presentation_submission: any, ): Promise> { if (!state) { - return err(OpenID4VPClientErrors.MissingState, "Missing state param"); + return err(OpenID4VPClientErrors.MissingState, 'Missing state param'); } if (!vp_token) { - return err(OpenID4VPClientErrors.MissingVpToken, "Missing vp_token param"); + return err(OpenID4VPClientErrors.MissingVpToken, 'Missing vp_token param'); } - const rpState = await this.rpStateKV.get(`rpstate:${state}`) as RPState; + const rpState = (await this.rpStateKV.get(`rpstate:${state}`)) as RPState; if (!rpState) { return err(OpenID4VPClientErrors.MissingRPState, "Couldn't get rp state with state"); } if (rpState.completed) { - return err(OpenID4VPClientErrors.PresentationAlreadyCompleted, "Presentation flow already completed"); + return err( + OpenID4VPClientErrors.PresentationAlreadyCompleted, + 'Presentation flow already completed', + ); } rpState.response_code = toBase64Url(encoder.encode(crypto.randomUUID())); @@ -677,17 +835,25 @@ export class OpenID4VPClientAPI { return ok(rpState); } - public async getSignedRequestObject(sessionId: string): Promise> { + public async getSignedRequestObject( + sessionId: string, + ): Promise> { const rpState = await this.getRPStateBySessionId(sessionId); if (!rpState) { - return err(OpenID4VPClientErrors.MissingRPState, "rpState state could not be fetched with this id"); + return err( + OpenID4VPClientErrors.MissingRPState, + 'rpState state could not be fetched with this id', + ); } - if (rpState.signed_request === "") { - return err(OpenID4VPClientErrors.SignedRequestObjectInvalidated, "rpState state signed request object has been invalidated"); + if (rpState.signed_request === '') { + return err( + OpenID4VPClientErrors.SignedRequestObjectInvalidated, + 'rpState state signed request object has been invalidated', + ); } const signedRequest = rpState.signed_request; - rpState.signed_request = ""; + rpState.signed_request = ''; await this.saveRPState(sessionId, rpState); return ok(signedRequest); } diff --git a/src/protocols/openid4vp/OpenID4VPServerAPI.test.ts b/src/protocols/openid4vp/OpenID4VPServerAPI.test.ts index 306711d..6e6fc01 100644 --- a/src/protocols/openid4vp/OpenID4VPServerAPI.test.ts +++ b/src/protocols/openid4vp/OpenID4VPServerAPI.test.ts @@ -1,10 +1,10 @@ -import { assert, describe, it } from "vitest"; -import Crypto from "node:crypto"; -import { Jwt, SDJwt } from "@sd-jwt/core"; -import { OpenID4VPServerAPI, retrieveKeys } from "./OpenID4VPServerAPI"; -import { OpenID4VPJweEncryption, OpenID4VPResponseMode } from "./types"; -import { VerifiableCredentialFormat } from "../../types"; -import { SignJWT, exportJWK, generateKeyPair, importPKCS8 } from "jose"; +import { assert, describe, it } from 'vitest'; +import Crypto from 'node:crypto'; +import { Jwt, SDJwt } from '@sd-jwt/core'; +import { OpenID4VPServerAPI, retrieveKeys } from './OpenID4VPServerAPI'; +import { OpenID4VPJweEncryption, OpenID4VPResponseMode } from './types'; +import { VerifiableCredentialFormat } from '../../types'; +import { SignJWT, exportJWK, generateKeyPair, importPKCS8 } from 'jose'; const issuerSignedB64U = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; /** @@ -38,12 +38,12 @@ Decoded issuerSignedB64U (for reference): */ const buildSdJwt = async (payload: Record) => { - const { privateKey } = Crypto.generateKeyPairSync("ed25519"); + const { privateKey } = Crypto.generateKeyPairSync('ed25519'); const signer = async (data: string) => { const sig = Crypto.sign(null, Buffer.from(data), privateKey); - return Buffer.from(sig).toString("base64url"); + return Buffer.from(sig).toString('base64url'); }; - const jwt = new Jwt({ header: { alg: "EdDSA" }, payload }); + const jwt = new Jwt({ header: { alg: 'EdDSA' }, payload }); await jwt.sign(signer); const sdJwt = new SDJwt({ jwt, disclosures: [] }); return sdJwt.encodeSDJwt(); @@ -75,45 +75,52 @@ Sq07Z4RcK/eo2GkLmLhCCcWchKv79JqAHiCoCrbY9k+UewK6SaOxx2OI -----END PRIVATE KEY-----`; const verifierCertificateBase64 = verifierCertificatePem - .replace("-----BEGIN CERTIFICATE-----", "") - .replace("-----END CERTIFICATE-----", "") - .replace(/\s+/g, ""); + .replace('-----BEGIN CERTIFICATE-----', '') + .replace('-----END CERTIFICATE-----', '') + .replace(/\s+/g, ''); const buildRequestUriJwt = async (clientId: string, dcqlQuery: Record) => { - const privateKey = await importPKCS8(verifierPrivateKeyPem, "ES256"); + const privateKey = await importPKCS8(verifierPrivateKeyPem, 'ES256'); return new SignJWT({ client_id: clientId, - response_uri: "https://verifier.example.com/cb", - state: "state-hash", - nonce: "nonce-hash", + response_uri: 'https://verifier.example.com/cb', + state: 'state-hash', + nonce: 'nonce-hash', client_metadata: { vp_formats: {} }, response_mode: OpenID4VPResponseMode.DIRECT_POST, dcql_query: dcqlQuery, }) .setProtectedHeader({ - alg: "ES256", + alg: 'ES256', x5c: [verifierCertificateBase64], - typ: "oauth-authz-req+jwt", + typ: 'oauth-authz-req+jwt', }) .setIssuedAt() .sign(privateKey); }; -const decodeJweProtectedHeader = (jwe: string) => JSON.parse(Buffer.from(jwe.split(".")[0], "base64url").toString()); +const decodeJweProtectedHeader = (jwe: string) => + JSON.parse(Buffer.from(jwe.split('.')[0], 'base64url').toString()); -describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { - it("should store rpState and return mapping for an sd-jwt DCQL query", async () => { - const signedClaimsPid = { vct: "urn:eudi:pid:1", given_name: "Alice", family_name: "Doe" }; - const signedClaimsOther = { vct: "urn:eudi:ehic:1", given_name: "Bob" }; +describe('OpenID4VPServerAPI.handleAuthorizationRequest', () => { + it('should store rpState and return mapping for an sd-jwt DCQL query', async () => { + const signedClaimsPid = { vct: 'urn:eudi:pid:1', given_name: 'Alice', family_name: 'Doe' }; + const signedClaimsOther = { vct: 'urn:eudi:ehic:1', given_name: 'Bob' }; const sdJwtPid = await buildSdJwt(signedClaimsPid); const sdJwtOther = await buildSdJwt(signedClaimsOther); const mdocPid = issuerSignedB64U; const storedState: { value: any | null } = { value: null }; const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { - store: async (state) => { storedState.value = state; }, + store: async (state) => { + storedState.value = state; + }, retrieve: async () => storedState.value, }, parseCredential: async (credential) => { @@ -127,34 +134,34 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { }, selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); const dcql_query = { credentials: [ { - id: "testCredential", + id: 'testCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "x509_san_dns:verifier.example.com"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-123"); - url.searchParams.set("state", "state-123"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'x509_san_dns:verifier.example.com'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-123'); + url.searchParams.set('state', 'state-123'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); const vcEntityList = [ { format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7, instanceId: 0 }, @@ -163,61 +170,67 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { ]; const result = await helper.handleAuthorizationRequest(url.toString(), vcEntityList); - assert(!("error" in result)); + assert(!('error' in result)); assert(result.parsedTransactionData === null); - assert(result.verifierDomainName === "x509_san_dns:verifier.example.com"); - const entry = result.conformantCredentialsMap.get("testCredential"); + assert(result.verifierDomainName === 'x509_san_dns:verifier.example.com'); + const entry = result.conformantCredentialsMap.get('testCredential'); assert(entry?.credentials?.includes(7)); - assert(entry?.requestedFields?.some((f) => f.path?.join(".") === "given_name")); - assert(storedState.value?.nonce === "nonce-123"); + assert(entry?.requestedFields?.some((f) => f.path?.join('.') === 'given_name')); + assert(storedState.value?.nonce === 'nonce-123'); assert(storedState.value?.response_mode === OpenID4VPResponseMode.DIRECT_POST); assert(Array.isArray(storedState.value?.transaction_data)); assert(storedState.value?.transaction_data.length === 0); }); - it("should return mapping for an mso_mdoc DCQL query", async () => { - const signedClaimsPid = { vct: "urn:eudi:pid:1", given_name: "Alice" }; + it('should return mapping for an mso_mdoc DCQL query', async () => { + const signedClaimsPid = { vct: 'urn:eudi:pid:1', given_name: 'Alice' }; const sdJwtPid = await buildSdJwt(signedClaimsPid); const mdocPid = issuerSignedB64U; const storedState: { value: any | null } = { value: null }; const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { - store: async (state) => { storedState.value = state; }, + store: async (state) => { + storedState.value = state; + }, retrieve: async () => storedState.value, }, parseCredential: async () => ({ signedClaims: signedClaimsPid }), selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); const dcql_query = { credentials: [ { - id: "pidMdoc", - format: "mso_mdoc", - meta: { doctype_value: "eu.europa.ec.eudi.pid.1" }, - claims: [{ path: ["eu.europa.ec.eudi.pid.1", "family_name"] }], + id: 'pidMdoc', + format: 'mso_mdoc', + meta: { doctype_value: 'eu.europa.ec.eudi.pid.1' }, + claims: [{ path: ['eu.europa.ec.eudi.pid.1', 'family_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "x509_san_dns:verifier.example.com"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-456"); - url.searchParams.set("state", "state-456"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'x509_san_dns:verifier.example.com'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-456'); + url.searchParams.set('state', 'state-456'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); const vcEntityList = [ { format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7, instanceId: 0 }, @@ -225,18 +238,22 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { ]; const result = await helper.handleAuthorizationRequest(url.toString(), vcEntityList); - assert(!("error" in result)); - const entry = result.conformantCredentialsMap.get("pidMdoc"); + assert(!('error' in result)); + const entry = result.conformantCredentialsMap.get('pidMdoc'); assert(entry?.credentials?.[0] === 9); }); - it("should return insufficient credentials when request asks for missing vct", async () => { - const signedClaimsPid = { vct: "urn:eudi:pid:1", given_name: "Alice" }; + it('should return insufficient credentials when request asks for missing vct', async () => { + const signedClaimsPid = { vct: 'urn:eudi:pid:1', given_name: 'Alice' }; const sdJwtPid = await buildSdJwt(signedClaimsPid); const mdocPid = issuerSignedB64U; const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { store: async () => {}, retrieve: async () => ({}) as any, @@ -244,34 +261,34 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { parseCredential: async () => ({ signedClaims: signedClaimsPid }), selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); const dcql_query = { credentials: [ { - id: "missingCredential", + id: 'missingCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:ehic:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:ehic:1'] }, + claims: [{ path: ['given_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "x509_san_dns:verifier.example.com"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-789"); - url.searchParams.set("state", "state-789"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'x509_san_dns:verifier.example.com'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-789'); + url.searchParams.set('state', 'state-789'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); const vcEntityList = [ { format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7, instanceId: 0 }, @@ -279,13 +296,17 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { ]; const result = await helper.handleAuthorizationRequest(url.toString(), vcEntityList); - assert("error" in result); - assert(result.error === "insufficient_credentials"); + assert('error' in result); + assert(result.error === 'insufficient_credentials'); }); - it("should return missing_dcql_query when dcql_query is omitted", async () => { + it('should return missing_dcql_query when dcql_query is omitted', async () => { const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { store: async () => {}, retrieve: async () => ({}) as any, @@ -293,31 +314,35 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { parseCredential: async () => null, selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "x509_san_dns:verifier.example.com"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-000"); - url.searchParams.set("state", "state-000"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'x509_san_dns:verifier.example.com'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-000'); + url.searchParams.set('state', 'state-000'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); const result = await helper.handleAuthorizationRequest(url.toString(), []); - assert("error" in result); - assert(result.error === "missing_dcql_query"); + assert('error' in result); + assert(result.error === 'missing_dcql_query'); }); - it("should return non_supported_client_id_scheme for unsupported client_id", async () => { + it('should return non_supported_client_id_scheme for unsupported client_id', async () => { const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { store: async () => {}, retrieve: async () => ({}) as any, @@ -325,43 +350,47 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { parseCredential: async () => null, selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); const dcql_query = { credentials: [ { - id: "testCredential", + id: 'testCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "did:example:verifier"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-001"); - url.searchParams.set("state", "state-001"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'did:example:verifier'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-001'); + url.searchParams.set('state', 'state-001'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); const result = await helper.handleAuthorizationRequest(url.toString(), []); - assert("error" in result); - assert(result.error === "non_supported_client_id_scheme"); + assert('error' in result); + assert(result.error === 'non_supported_client_id_scheme'); }); - it("should return old_state when nonce was already used", async () => { + it('should return old_state when nonce was already used', async () => { const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { store: async () => {}, retrieve: async () => ({}) as any, @@ -369,15 +398,15 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { parseCredential: async () => null, selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, lastUsedNonceStore: { - get: () => "nonce-reused", + get: () => 'nonce-reused', set: () => {}, }, }); @@ -385,39 +414,39 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { const dcql_query = { credentials: [ { - id: "testCredential", + id: 'testCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", "x509_san_dns:verifier.example.com"); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-reused"); - url.searchParams.set("state", "state-002"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', 'x509_san_dns:verifier.example.com'); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-reused'); + url.searchParams.set('state', 'state-002'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); const result = await helper.handleAuthorizationRequest(url.toString(), []); - assert("error" in result); - assert(result.error === "old_state"); + assert('error' in result); + assert(result.error === 'old_state'); }); - it("should accept x509_hash when the request_uri hash matches the signing certificate", async () => { - const signedClaimsPid = { vct: "urn:eudi:pid:1", given_name: "Alice" }; + it('should accept x509_hash when the request_uri hash matches the signing certificate', async () => { + const signedClaimsPid = { vct: 'urn:eudi:pid:1', given_name: 'Alice' }; const sdJwtPid = await buildSdJwt(signedClaimsPid); - const expectedClientId = `x509_hash:${Crypto.createHash("sha256").update(Buffer.from(verifierCertificateBase64, "base64")).digest("base64url")}`; + const expectedClientId = `x509_hash:${Crypto.createHash('sha256').update(Buffer.from(verifierCertificateBase64, 'base64')).digest('base64url')}`; const requestUriJwt = await buildRequestUriJwt(expectedClientId, { credentials: [ { - id: "testCredential", + id: 'testCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }); @@ -425,7 +454,7 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { const helper = new OpenID4VPServerAPI({ httpClient: { get: async (url: string) => { - if (url === "https://verifier.example.com/request-object") { + if (url === 'https://verifier.example.com/request-object') { return { data: requestUriJwt }; } throw new Error(`unexpected http call: ${url}`); @@ -438,75 +467,79 @@ describe("OpenID4VPServerAPI.handleAuthorizationRequest", () => { parseCredential: async () => ({ signedClaims: signedClaimsPid }), selectCredentialForBatch: async () => null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); const dcql_query = { credentials: [ { - id: "testCredential", + id: 'testCredential', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }; - const url = new URL("openid4vp://authorize"); - url.searchParams.set("client_id", expectedClientId); - url.searchParams.set("response_uri", "https://verifier.example.com/cb"); - url.searchParams.set("nonce", "nonce-hash"); - url.searchParams.set("state", "state-hash"); - url.searchParams.set("client_metadata", JSON.stringify({ vp_formats: {} })); - url.searchParams.set("response_mode", JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); - url.searchParams.set("dcql_query", JSON.stringify(dcql_query)); - url.searchParams.set("request_uri", "https://verifier.example.com/request-object"); + const url = new URL('openid4vp://authorize'); + url.searchParams.set('client_id', expectedClientId); + url.searchParams.set('response_uri', 'https://verifier.example.com/cb'); + url.searchParams.set('nonce', 'nonce-hash'); + url.searchParams.set('state', 'state-hash'); + url.searchParams.set('client_metadata', JSON.stringify({ vp_formats: {} })); + url.searchParams.set('response_mode', JSON.stringify(OpenID4VPResponseMode.DIRECT_POST)); + url.searchParams.set('dcql_query', JSON.stringify(dcql_query)); + url.searchParams.set('request_uri', 'https://verifier.example.com/request-object'); const result = await helper.handleAuthorizationRequest(url.toString(), [ { format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7, instanceId: 0 }, ]); - assert(!("error" in result)); + assert(!('error' in result)); assert(result.verifierDomainName === expectedClientId); }); }); -describe("OpenID4VPServerAPI.createAuthorizationResponse", () => { +describe('OpenID4VPServerAPI.createAuthorizationResponse', () => { const createJwtResponse = async (encryptedResponseEncValuesSupported?: string[]) => { - const signedClaims = { vct: "urn:eudi:pid:1", given_name: "Alice" }; + const signedClaims = { vct: 'urn:eudi:pid:1', given_name: 'Alice' }; const sdJwtPid = await buildSdJwt(signedClaims); - const { publicKey } = await generateKeyPair("ECDH-ES"); + const { publicKey } = await generateKeyPair('ECDH-ES'); const jwk = await exportJWK(publicKey); const helper = new OpenID4VPServerAPI({ - httpClient: { get: async () => { throw new Error("unexpected http call"); } }, + httpClient: { + get: async () => { + throw new Error('unexpected http call'); + }, + }, rpStateStore: { store: async () => {}, retrieve: async () => ({ - nonce: "nonce-jwe", - response_uri: "https://verifier.example.com/cb", - client_id: "x509_san_dns:verifier.example.com", - state: "state-jwe", + nonce: 'nonce-jwe', + response_uri: 'https://verifier.example.com/cb', + client_id: 'x509_san_dns:verifier.example.com', + state: 'state-jwe', client_metadata: { vp_formats: {}, ...(encryptedResponseEncValuesSupported ? { encrypted_response_enc_values_supported: encryptedResponseEncValuesSupported } : {}), - jwks: { keys: [{ ...jwk, use: "enc", alg: "ECDH-ES", kid: "kid-jwe" }] }, + jwks: { keys: [{ ...jwk, use: 'enc', alg: 'ECDH-ES', kid: 'kid-jwe' }] }, }, response_mode: OpenID4VPResponseMode.DIRECT_POST_JWT, dcql_query: { credentials: [ { - id: "pid", + id: 'pid', format: VerifiableCredentialFormat.DC_SDJWT, - meta: { vct_values: ["urn:eudi:pid:1"] }, - claims: [{ path: ["given_name"] }], + meta: { vct_values: ['urn:eudi:pid:1'] }, + claims: [{ path: ['given_name'] }], }, ], }, @@ -517,35 +550,34 @@ describe("OpenID4VPServerAPI.createAuthorizationResponse", () => { selectCredentialForBatch: async (batchId, vcEntityList) => vcEntityList.find((credential) => credential.batchId === batchId) ?? null, keystore: { - signJwtPresentation: async () => ({ vpjwt: "vp-jwt" }), + signJwtPresentation: async () => ({ vpjwt: 'vp-jwt' }), generateDeviceResponse: async () => ({ deviceResponseMDoc: {} }), }, strings: { - purposeNotSpecified: "No purpose provided", - allClaimsRequested: "All claims", + purposeNotSpecified: 'No purpose provided', + allClaimsRequested: 'All claims', }, }); - const result = await helper.createAuthorizationResponse( - new Map([["pid", 7]]), - [{ format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7 }] - ); + const result = await helper.createAuthorizationResponse(new Map([['pid', 7]]), [ + { format: VerifiableCredentialFormat.DC_SDJWT, data: sdJwtPid, batchId: 7 }, + ]); - assert("formData" in result); - return result.formData.get("response") as string; + assert('formData' in result); + return result.formData.get('response') as string; }; - it("uses the strongest wallet-preferred verifier JWE enc value", async () => { + it('uses the strongest wallet-preferred verifier JWE enc value', async () => { const jwe = await createJwtResponse([ OpenID4VPJweEncryption.A128GCM, - "unsupported", + 'unsupported', OpenID4VPJweEncryption.A256GCM, ]); assert(decodeJweProtectedHeader(jwe).enc === OpenID4VPJweEncryption.A256GCM); }); - it("uses the next strongest wallet-preferred JWE enc value when A256GCM is not supported", async () => { + it('uses the next strongest wallet-preferred JWE enc value when A256GCM is not supported', async () => { const jwe = await createJwtResponse([ OpenID4VPJweEncryption.A128GCM, OpenID4VPJweEncryption.A192GCM, @@ -554,33 +586,36 @@ describe("OpenID4VPServerAPI.createAuthorizationResponse", () => { assert(decodeJweProtectedHeader(jwe).enc === OpenID4VPJweEncryption.A192GCM); }); - it("throws when verifier JWE enc values are provided but unsupported", async () => { + it('throws when verifier JWE enc values are provided but unsupported', async () => { try { - await createJwtResponse(["unsupported"]); - assert.fail("Expected createAuthorizationResponse to throw for unsupported enc values"); + await createJwtResponse(['unsupported']); + assert.fail('Expected createAuthorizationResponse to throw for unsupported enc values'); } catch (error) { assert(error instanceof Error); - assert(error.message.includes("Could not find supported algorithm in encrypted_response_enc_values_supported")); + assert( + error.message.includes( + 'Could not find supported algorithm in encrypted_response_enc_values_supported', + ), + ); } }); - it("falls back to A256GCM when verifier JWE enc values are not provided", async () => { + it('falls back to A256GCM when verifier JWE enc values are not provided', async () => { const jwe = await createJwtResponse(); assert(decodeJweProtectedHeader(jwe).enc === OpenID4VPJweEncryption.A256GCM); }); }); - -describe("OpenID4VPServerAPI.retrieveKeys", () => { - it("should return the key and its alg when jwks is provided directly", async () => { +describe('OpenID4VPServerAPI.retrieveKeys', () => { + it('should return the key and its alg when jwks is provided directly', async () => { const testKey = { - kty: "EC", - crv: "P-256", - use: "enc", - alg: "ECDH-ES+HKDF-256+A128KW", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'enc', + alg: 'ECDH-ES+HKDF-256+A128KW', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { @@ -593,28 +628,28 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { const result = await retrieveKeys(state, {}); assert(result.rp_eph_pub_jwk === testKey); - assert(result.alg === "ECDH-ES+HKDF-256+A128KW"); + assert(result.alg === 'ECDH-ES+HKDF-256+A128KW'); }); - it("should fetch keys from jwks_uri and return the key with its alg", async () => { + it('should fetch keys from jwks_uri and return the key with its alg', async () => { const testKey = { - kty: "EC", - crv: "P-256", - use: "enc", - alg: "ECDH-ES+HKDF-256+A128KW", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'enc', + alg: 'ECDH-ES+HKDF-256+A128KW', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { client_metadata: { - jwks_uri: "https://example.com/.well-known/jwks.json", + jwks_uri: 'https://example.com/.well-known/jwks.json', }, } as any; const httpClient = { get: async (url: string) => { - if (url === "https://example.com/.well-known/jwks.json") { + if (url === 'https://example.com/.well-known/jwks.json') { return { data: { keys: [testKey] } }; } throw new Error(`unexpected http call: ${url}`); @@ -623,16 +658,16 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { const result = await retrieveKeys(state, httpClient); assert(result.rp_eph_pub_jwk === testKey); - assert(result.alg === "ECDH-ES+HKDF-256+A128KW"); + assert(result.alg === 'ECDH-ES+HKDF-256+A128KW'); }); - it("should throw error when selected jwks key is missing alg", async () => { + it('should throw error when selected jwks key is missing alg', async () => { const testKey = { - kty: "EC", - crv: "P-256", - use: "enc", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'enc', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { @@ -645,30 +680,30 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { try { await retrieveKeys(state, {}); - assert(false, "Should have thrown an error"); + assert(false, 'Should have thrown an error'); } catch (error) { assert((error as Error).message === "alg is required in verifier's JWK"); } }); - it("should throw error when fetched jwks key is missing alg", async () => { + it('should throw error when fetched jwks key is missing alg', async () => { const testKey = { - kty: "EC", - crv: "P-256", - use: "enc", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'enc', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { client_metadata: { - jwks_uri: "https://example.com/.well-known/jwks.json", + jwks_uri: 'https://example.com/.well-known/jwks.json', }, } as any; const httpClient = { get: async (url: string) => { - if (url === "https://example.com/.well-known/jwks.json") { + if (url === 'https://example.com/.well-known/jwks.json') { return { data: { keys: [testKey] } }; } throw new Error(`unexpected http call: ${url}`); @@ -677,20 +712,20 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { try { await retrieveKeys(state, httpClient); - assert(false, "Should have thrown an error"); + assert(false, 'Should have thrown an error'); } catch (error) { assert((error as Error).message === "alg is required in verifier's JWK"); } }); - it("should throw error when no encryption keys in jwks", async () => { + it('should throw error when no encryption keys in jwks', async () => { const nonEncryptionKey = { - kty: "EC", - crv: "P-256", - use: "sig", // Not for encryption - alg: "ES256", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'sig', // Not for encryption + alg: 'ES256', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { @@ -703,31 +738,31 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { try { await retrieveKeys(state, {}); - assert(false, "Should have thrown an error"); + assert(false, 'Should have thrown an error'); } catch (error) { - assert((error as Error).message === "Could not find Relying Party public key for encryption"); + assert((error as Error).message === 'Could not find Relying Party public key for encryption'); } }); - it("should throw error when no encryption keys in fetched jwks_uri", async () => { + it('should throw error when no encryption keys in fetched jwks_uri', async () => { const nonEncryptionKey = { - kty: "EC", - crv: "P-256", - use: "sig", // Not for encryption - alg: "ES256", - x: "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis", - y: "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE", + kty: 'EC', + crv: 'P-256', + use: 'sig', // Not for encryption + alg: 'ES256', + x: 'WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis', + y: 'y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE', }; const state = { client_metadata: { - jwks_uri: "https://example.com/.well-known/jwks.json", + jwks_uri: 'https://example.com/.well-known/jwks.json', }, } as any; const httpClient = { get: async (url: string) => { - if (url === "https://example.com/.well-known/jwks.json") { + if (url === 'https://example.com/.well-known/jwks.json') { return { data: { keys: [nonEncryptionKey] } }; } throw new Error(`unexpected http call: ${url}`); @@ -736,22 +771,22 @@ describe("OpenID4VPServerAPI.retrieveKeys", () => { try { await retrieveKeys(state, httpClient); - assert(false, "Should have thrown an error"); + assert(false, 'Should have thrown an error'); } catch (error) { - assert((error as Error).message === "Could not find Relying Party public key for encryption"); + assert((error as Error).message === 'Could not find Relying Party public key for encryption'); } }); - it("should throw error when neither jwks nor jwks_uri provided", async () => { + it('should throw error when neither jwks nor jwks_uri provided', async () => { const state = { client_metadata: {}, } as any; try { await retrieveKeys(state, {}); - assert(false, "Should have thrown an error"); + assert(false, 'Should have thrown an error'); } catch (error) { - assert((error as Error).message === "Could not find Relying Party public key for encryption"); + assert((error as Error).message === 'Could not find Relying Party public key for encryption'); } }); }); diff --git a/src/protocols/openid4vp/OpenID4VPServerAPI.ts b/src/protocols/openid4vp/OpenID4VPServerAPI.ts index ad8f424..a8e9c71 100644 --- a/src/protocols/openid4vp/OpenID4VPServerAPI.ts +++ b/src/protocols/openid4vp/OpenID4VPServerAPI.ts @@ -1,8 +1,8 @@ -import { SDJwt } from "@sd-jwt/core"; -import { IssuerSigned } from "@owf/mdoc"; -import { base64url, EncryptJWT, importJWK, importX509, jwtVerify } from "jose"; -import { DcqlPresentationResult, DcqlQuery } from "dcql"; -import { generateRandomIdentifier } from "../../utils"; +import { SDJwt } from '@sd-jwt/core'; +import { IssuerSigned } from '@owf/mdoc'; +import { base64url, EncryptJWT, importJWK, importX509, jwtVerify } from 'jose'; +import { DcqlPresentationResult, DcqlQuery } from 'dcql'; +import { generateRandomIdentifier } from '../../utils'; import { OpenID4VPClientMetadata, OpenID4VPRelyingPartyState, @@ -13,29 +13,30 @@ import { OpenID4VPJweEncryption, TransactionDataResponseGenerator, TransactionDataResponseGeneratorParams, - TransactionDataResponseParams -} from "./types"; -import { VerifiableCredentialFormat } from "../../types"; + TransactionDataResponseParams, +} from './types'; +import { VerifiableCredentialFormat } from '../../types'; export const HandleAuthorizationRequestErrors = { - NON_SUPPORTED_CLIENT_ID_SCHEME: "non_supported_client_id_scheme", - INSUFFICIENT_CREDENTIALS: "insufficient_credentials", - MISSING_DCQL_QUERY: "missing_dcql_query", - NONTRUSTED_VERIFIER: "nontrusted_verifier", - INVALID_RESPONSE_MODE: "invalid_response_mode", - OLD_STATE: "old_state", - INVALID_TRANSACTION_DATA: "invalid_transaction_data", - INVALID_TYP: "invalid_jwt_typ", - COULD_NOT_RESOLVE_REQUEST: "could_not_resolve_request", + NON_SUPPORTED_CLIENT_ID_SCHEME: 'non_supported_client_id_scheme', + INSUFFICIENT_CREDENTIALS: 'insufficient_credentials', + MISSING_DCQL_QUERY: 'missing_dcql_query', + NONTRUSTED_VERIFIER: 'nontrusted_verifier', + INVALID_RESPONSE_MODE: 'invalid_response_mode', + OLD_STATE: 'old_state', + INVALID_TRANSACTION_DATA: 'invalid_transaction_data', + INVALID_TYP: 'invalid_jwt_typ', + COULD_NOT_RESOLVE_REQUEST: 'could_not_resolve_request', } as const; -export type HandleAuthorizationRequestError = typeof HandleAuthorizationRequestErrors[keyof typeof HandleAuthorizationRequestErrors]; +export type HandleAuthorizationRequestError = + (typeof HandleAuthorizationRequestErrors)[keyof typeof HandleAuthorizationRequestErrors]; type OpenID4VPServerKeystore = { signJwtPresentation( nonce: string, audience: string, presentations: string[], - transactionDataResponseParams?: TransactionDataResponseParams + transactionDataResponseParams?: TransactionDataResponseParams, ): Promise<{ vpjwt: string }>; generateDeviceResponse( mdoc: any, @@ -45,24 +46,36 @@ type OpenID4VPServerKeystore = { clientId: string, responseUri: string, verifierEncryptionJwk?: JsonWebKey | Record, - handoverType?: "redirect" | "dc_api", - dcApiOrigin?: string + handoverType?: 'redirect' | 'dc_api', + dcApiOrigin?: string, ): Promise<{ deviceResponseMDoc: any }>; }; type OpenID4VPServerDeps = { - httpClient: { get: (url: string, options?: Record) => Promise<{ data: unknown }> }; + httpClient: { + get: (url: string, options?: Record) => Promise<{ data: unknown }>; + }; rpStateStore: { store(stateObject: OpenID4VPRelyingPartyState): Promise; retrieve(): Promise; }; - parseCredential: (credential: CredentialT) => Promise<{ signedClaims: Record } | null>; - selectCredentialForBatch: (batchId: number, vcEntityList: CredentialT[]) => Promise; + parseCredential: ( + credential: CredentialT, + ) => Promise<{ signedClaims: Record } | null>; + selectCredentialForBatch: ( + batchId: number, + vcEntityList: CredentialT[], + ) => Promise; keystore: OpenID4VPServerKeystore; strings: OpenID4VPServerMessages; lastUsedNonceStore?: OpenID4VPServerLastUsedNonceStore; - parseTransactionData?: (transaction_data: string[], dcql_query: Record) => ParsedTransactionDataT[] | null; - transactionDataResponseGenerator?: (params: TransactionDataResponseGeneratorParams) => TransactionDataResponseGenerator; + parseTransactionData?: ( + transaction_data: string[], + dcql_query: Record, + ) => ParsedTransactionDataT[] | null; + transactionDataResponseGenerator?: ( + params: TransactionDataResponseGeneratorParams, + ) => TransactionDataResponseGenerator; verifyRequestUriAndCerts?: OpenID4VPServerRequestVerifier; subtle?: SubtleCrypto; randomUUID?: () => string; @@ -79,8 +92,8 @@ export type OpenID4VPServerCredential = { const decoder = new TextDecoder(); const encoder = new TextEncoder(); const certFromB64 = (certBase64: string) => - `-----BEGIN CERTIFICATE-----\n${certBase64.match(/.{1,64}/g)?.join("\n")}\n-----END CERTIFICATE-----`; -const supportedClientIdSchemes = new Set(["x509_san_dns", "x509_hash"]); + `-----BEGIN CERTIFICATE-----\n${certBase64.match(/.{1,64}/g)?.join('\n')}\n-----END CERTIFICATE-----`; +const supportedClientIdSchemes = new Set(['x509_san_dns', 'x509_hash']); const preferredJweEncryptions = [ OpenID4VPJweEncryption.A256GCM, OpenID4VPJweEncryption.A192GCM, @@ -98,8 +111,7 @@ function decodeIssuerSignedCredential(credentialDataB64u: string): { const docType = issuerSigned.issuerAuth.mobileSecurityObject.docType; const issuerNamespaces = issuerSigned.issuerNamespaces?.issuerNamespaces ?? new Map(); const firstNamespaceEntry = issuerNamespaces.entries().next().value as - | [string, Array<{ elementIdentifier: string; elementValue: unknown }>] - | undefined; + [string, Array<{ elementIdentifier: string; elementValue: unknown }>] | undefined; if (!firstNamespaceEntry) { return { @@ -125,23 +137,29 @@ function decodeIssuerSignedCredential(credentialDataB64u: string): { } function isOpenID4VPResponseMode(value: unknown): value is OpenID4VPResponseMode { - return typeof value === "string" && Object.values(OpenID4VPResponseMode).includes(value as OpenID4VPResponseMode); + return ( + typeof value === 'string' && + Object.values(OpenID4VPResponseMode).includes(value as OpenID4VPResponseMode) + ); } function getSubtleCrypto(subtle?: SubtleCrypto): SubtleCrypto { if (subtle) return subtle; if (globalThis.crypto?.subtle) return globalThis.crypto.subtle; - throw new Error("Missing SubtleCrypto implementation"); + throw new Error('Missing SubtleCrypto implementation'); } function getClientIdScheme(clientId: string): string { - return clientId.split(":")[0]; + return clientId.split(':')[0]; } -async function calculateX509HashFromLeafCert(leafCertBase64: string, subtle?: SubtleCrypto): Promise { +async function calculateX509HashFromLeafCert( + leafCertBase64: string, + subtle?: SubtleCrypto, +): Promise { let certBytes: Uint8Array; - if (typeof Buffer !== "undefined") { - certBytes = Uint8Array.from(Buffer.from(leafCertBase64, "base64")); + if (typeof Buffer !== 'undefined') { + certBytes = Uint8Array.from(Buffer.from(leafCertBase64, 'base64')); } else { const binary = atob(leafCertBase64); certBytes = new Uint8Array(binary.length); @@ -149,15 +167,23 @@ async function calculateX509HashFromLeafCert(leafCertBase64: string, subtle?: Su certBytes[i] = binary.charCodeAt(i); } } - const digest = await getSubtleCrypto(subtle).digest("SHA-256", certBytes as Uint8Array); + const digest = await getSubtleCrypto(subtle).digest( + 'SHA-256', + certBytes as Uint8Array, + ); return base64url.encode(new Uint8Array(digest)); } -export const retrieveKeys = async (S: OpenID4VPRelyingPartyState, httpClient: { get: (url: string, options?: Record) => Promise<{ data: unknown }> }) => { +export const retrieveKeys = async ( + S: OpenID4VPRelyingPartyState, + httpClient: { + get: (url: string, options?: Record) => Promise<{ data: unknown }>; + }, +) => { if (S.client_metadata.jwks) { - const rp_eph_pub_jwk = S.client_metadata.jwks.keys.filter((k) => k.use === "enc")[0]; + const rp_eph_pub_jwk = S.client_metadata.jwks.keys.filter((k) => k.use === 'enc')[0]; if (!rp_eph_pub_jwk) { - throw new Error("Could not find Relying Party public key for encryption"); + throw new Error('Could not find Relying Party public key for encryption'); } if (!rp_eph_pub_jwk.alg) { throw new Error("alg is required in verifier's JWK"); @@ -166,11 +192,16 @@ export const retrieveKeys = async (S: OpenID4VPRelyingPartyState, httpClient: { } if (S.client_metadata.jwks_uri) { const response = await httpClient.get(S.client_metadata.jwks_uri).catch(() => null); - if (response && typeof response.data === "object" && response.data && "keys" in (response.data as Record)) { + if ( + response && + typeof response.data === 'object' && + response.data && + 'keys' in (response.data as Record) + ) { const keys = (response.data as { keys: any[] }).keys; - const rp_eph_pub_jwk = keys.filter((k) => k.use === "enc")[0]; + const rp_eph_pub_jwk = keys.filter((k) => k.use === 'enc')[0]; if (!rp_eph_pub_jwk) { - throw new Error("Could not find Relying Party public key for encryption"); + throw new Error('Could not find Relying Party public key for encryption'); } if (!rp_eph_pub_jwk.alg) { throw new Error("alg is required in verifier's JWK"); @@ -178,10 +209,13 @@ export const retrieveKeys = async (S: OpenID4VPRelyingPartyState, httpClient: { return { rp_eph_pub_jwk, alg: rp_eph_pub_jwk.alg }; } } - throw new Error("Could not find Relying Party public key for encryption"); + throw new Error('Could not find Relying Party public key for encryption'); }; -export class OpenID4VPServerAPI { +export class OpenID4VPServerAPI< + CredentialT extends OpenID4VPServerCredential, + ParsedTransactionDataT, +> { private deps: OpenID4VPServerDeps; constructor(deps: OpenID4VPServerDeps) { @@ -193,39 +227,43 @@ export class OpenID4VPServerAPI; parsedHeader: Record; leafCertBase64: string } | - { error: HandleAuthorizationRequestError } + | { + payload: Record; + parsedHeader: Record; + leafCertBase64: string; + } + | { error: HandleAuthorizationRequestError } > { const requestUriResponse = await this.deps.httpClient.get(request_uri, {}); - if (typeof requestUriResponse.data !== "string") { + if (typeof requestUriResponse.data !== 'string') { return { error: HandleAuthorizationRequestErrors.COULD_NOT_RESOLVE_REQUEST }; } const jwt = requestUriResponse.data; - const [header, payload] = jwt.split("."); + const [header, payload] = jwt.split('.'); const parsedHeader = JSON.parse(decoder.decode(base64url.decode(header))); - if (parsedHeader.typ !== "oauth-authz-req+jwt") { + if (parsedHeader.typ !== 'oauth-authz-req+jwt') { return { error: HandleAuthorizationRequestErrors.INVALID_TYP }; } const x5c = parsedHeader.x5c as string[]; @@ -242,8 +280,20 @@ export class OpenID4VPServerAPI; descriptorPurpose: string } + private async matchCredentialsToDCQL( + vcList: CredentialT[], + dcqlJson: any, + ): Promise< + | { + mapping: Map< + string, + { + credentials: number[]; + requestedFields: { name: string; purpose: string; path?: (string | null)[] }[]; + } + >; + descriptorPurpose: string; + } | { error: HandleAuthorizationRequestError } > { const descriptorPurpose = this.deps.strings.purposeNotSpecified ?? null; @@ -277,7 +327,7 @@ export class OpenID4VPServerAPI { if (failedCreds.meta.success === false) { - console.error("DCQL metadata issues: ", failedCreds.meta.issues); + console.error('DCQL metadata issues: ', failedCreds.meta.issues); } if (!failedCreds.claims.success) { - console.error("DCQL failed claims: ", failedCreds.claims); + console.error('DCQL failed claims: ', failedCreds.claims); } }); } - return match?.success === true && Array.isArray(match.valid_credentials) && match.valid_credentials.length > 0; + return ( + match?.success === true && + Array.isArray(match.valid_credentials) && + match.valid_credentials.length > 0 + ); } const satisfied = dcqlJson.credentials.every((cred: any) => hasValidMatch(cred.id)); @@ -310,7 +364,13 @@ export class OpenID4VPServerAPI(); + const mapping = new Map< + string, + { + credentials: number[]; + requestedFields: { name: string; purpose: string; path?: (string | null)[] }[]; + } + >(); for (const credReq of dcqlJson.credentials) { const match = result.credential_matches[credReq.id]; const conforming: number[] = []; @@ -326,12 +386,18 @@ export class OpenID4VPServerAPI ({ - name: cl.id || cl.path.join("."), - purpose: descriptorPurpose, - path: cl.path, - })), + name: cl.id || cl.path.join('.'), + purpose: descriptorPurpose, + path: cl.path, + })), }); } @@ -363,19 +429,23 @@ export class OpenID4VPServerAPI, - vcEntityList: CredentialT[] + vcEntityList: CredentialT[], ) { const { dcql_query, client_id, nonce, response_uri, transaction_data } = S; let verifierEncryptionJwk: JsonWebKey | Record | undefined; - let handoverType: "redirect" | "dc_api" = "redirect"; + let handoverType: 'redirect' | 'dc_api' = 'redirect'; let dcApiOrigin: string | undefined; - if ([OpenID4VPResponseMode.DIRECT_POST_JWT, OpenID4VPResponseMode.DC_API_JWT].includes(S.response_mode)) { + if ( + [OpenID4VPResponseMode.DIRECT_POST_JWT, OpenID4VPResponseMode.DC_API_JWT].includes( + S.response_mode, + ) + ) { const { rp_eph_pub_jwk } = await retrieveKeys(S, this.deps.httpClient); verifierEncryptionJwk = rp_eph_pub_jwk as JsonWebKey | Record; } if (S.response_mode === OpenID4VPResponseMode.DC_API_JWT) { - handoverType = "dc_api"; - dcApiOrigin = S.client_id.replace(/^origin:/, ""); + handoverType = 'dc_api'; + dcApiOrigin = S.client_id.replace(/^origin:/, ''); } const generatedVPs: string[] = []; const originalVCs: CredentialT[] = []; @@ -394,7 +464,7 @@ export class OpenID4VPServerAPI { - if (val === null || typeof val !== "object") { + if (val === null || typeof val !== 'object') { if (path.length) paths.push(path); return; } @@ -432,14 +502,15 @@ export class OpenID4VPServerAPI { - const bytes = typeof data === "string" ? encoder.encode(data) : new Uint8Array(data); + const bytes = typeof data === 'string' ? encoder.encode(data) : new Uint8Array(data); return subtle.digest(alg, bytes).then((buf) => new Uint8Array(buf)); }; const sdJwt = await SDJwt.fromEncode(credential.data, hasher); - const presentation = credential.data.split("~").length - 1 > 1 - ? await sdJwt.present(frame, hasher) - : credential.data; + const presentation = + credential.data.split('~').length - 1 > 1 + ? await sdJwt.present(frame, hasher) + : credential.data; const shaped = { credential_format: credential.format, @@ -449,14 +520,14 @@ export class OpenID4VPServerAPI - descriptor.claims.some((cl: any) => cl.path.includes(k)) - ) - ) + Object.entries(signedClaims).filter(([k]) => + descriptor.claims.some((cl: any) => cl.path.includes(k)), + ), + ), }; const presResult = DcqlPresentationResult.fromDcqlPresentation( { [selectionKey]: [shaped] } as any, - { dcqlQuery: dcql_query as any } + { dcqlQuery: dcql_query as any }, ); if (!presResult.credential_matches[selectionKey]?.success) { throw new Error(`Presentation for '${selectionKey}' did not satisfy DCQL`); @@ -479,7 +550,7 @@ export class OpenID4VPServerAPI c.id === selectionKey); + const descriptorIndex = dcqlQueryWithClaims.credentials.findIndex( + (c: any) => c.id === selectionKey, + ); if (descriptorIndex !== -1) { const namespaceForPaths = namespaceName ?? effectiveDocType; - dcqlQueryWithClaims.credentials[descriptorIndex].claims = Object.keys(namespaceClaims).map((key) => ({ + dcqlQueryWithClaims.credentials[descriptorIndex].claims = Object.keys( + namespaceClaims, + ).map((key) => ({ id: key, path: [namespaceForPaths, key], })); @@ -525,7 +601,7 @@ export class OpenID4VPServerAPI [key, [generatedVPs[idx]]]) + Array.from(selectionMap.keys()).map((key, idx) => [key, [generatedVPs[idx]]]), ); const presentationSubmission = { @@ -545,13 +621,20 @@ export class OpenID4VPServerAPI S.client_metadata.encrypted_response_enc_values_supported?.includes(enc)); + const bestSupportedEnc = preferredJweEncryptions.find((enc) => + S.client_metadata.encrypted_response_enc_values_supported?.includes(enc), + ); if (!bestSupportedEnc) { - throw new Error("Could not find supported algorithm in encrypted_response_enc_values_supported"); + throw new Error( + 'Could not find supported algorithm in encrypted_response_enc_values_supported', + ); } jweEnc = bestSupportedEnc; } else { @@ -573,10 +656,10 @@ export class OpenID4VPServerAPI; - verifierDomainName: string; - verifierPurpose: string; - parsedTransactionData: ParsedTransactionDataT[] | null; - } + conformantCredentialsMap: Map; + verifierDomainName: string; + verifierPurpose: string; + parsedTransactionData: ParsedTransactionDataT[] | null; + } | { error: HandleAuthorizationRequestError } > { let { @@ -619,7 +702,7 @@ export class OpenID4VPServerAPI); + parsedTransactionData = this.deps.parseTransactionData( + transaction_data, + dcql_query as Record, + ); if (parsedTransactionData === null) { return { error: HandleAuthorizationRequestErrors.INVALID_TRANSACTION_DATA }; } @@ -658,7 +744,7 @@ export class OpenID4VPServerAPI; descriptorPurpose: string; }; - const verifierDomainName = client_id.includes("http") - ? new URL(client_id).hostname - : client_id; + const verifierDomainName = client_id.includes('http') ? new URL(client_id).hostname : client_id; if (mapping.size === 0) { throw new Error("Credentials don't satisfy any descriptor"); @@ -717,19 +801,19 @@ export class OpenID4VPServerAPI, vcEntityList: CredentialT[]) { + async createAuthorizationResponse( + selectionMap: Map, + vcEntityList: CredentialT[], + ) { const S = await this.deps.rpStateStore.retrieve(); - if (!S || S.nonce === "" || (this.deps.lastUsedNonceStore?.get?.() ?? null) === S.nonce) { + if (!S || S.nonce === '' || (this.deps.lastUsedNonceStore?.get?.() ?? null) === S.nonce) { return {}; } this.deps.lastUsedNonceStore?.set?.(S.nonce); - const { formData, generatedVPs, presentationSubmission, filteredVCEntities } = await this.handleDCQLFlow( - S, - selectionMap, - vcEntityList - ); + const { formData, generatedVPs, presentationSubmission, filteredVCEntities } = + await this.handleDCQLFlow(S, selectionMap, vcEntityList); return { formData, diff --git a/src/protocols/openid4vp/sessionTranscript.ts b/src/protocols/openid4vp/sessionTranscript.ts index 7e0da0d..e46631b 100644 --- a/src/protocols/openid4vp/sessionTranscript.ts +++ b/src/protocols/openid4vp/sessionTranscript.ts @@ -1,9 +1,9 @@ -import { cborEncode } from "@owf/mdoc"; -import { calculateJwkThumbprint, type JWK } from "jose"; -import { DigestHashAlgorithm } from "../../types"; -import { base64urlToBytes } from "../../utils"; +import { cborEncode } from '@owf/mdoc'; +import { calculateJwkThumbprint, type JWK } from 'jose'; +import { DigestHashAlgorithm } from '../../types'; +import { base64urlToBytes } from '../../utils'; -export type OpenId4VpHandoverType = "redirect" | "dc_api"; +export type OpenId4VpHandoverType = 'redirect' | 'dc_api'; export async function buildOpenId4VpSessionTranscriptBytes(args: { subtle: SubtleCrypto; @@ -14,30 +14,33 @@ export async function buildOpenId4VpSessionTranscriptBytes(args: { dcApiOrigin?: string; verifierEncryptionJwk?: JWK | JsonWebKey | Record; }): Promise { - const handoverType = args.handoverType ?? "redirect"; + const handoverType = args.handoverType ?? 'redirect'; const thumbprintB64u = args.verifierEncryptionJwk - ? await calculateJwkThumbprint(args.verifierEncryptionJwk as JWK, "sha256") + ? await calculateJwkThumbprint(args.verifierEncryptionJwk as JWK, 'sha256') : null; const jwkThumbprint = thumbprintB64u ? base64urlToBytes(thumbprintB64u) : null; let handoverInfoBytes: Uint8Array; - let handoverIdentifier: "OpenID4VPHandover" | "OpenID4VPDCAPIHandover"; - if (handoverType === "dc_api") { + let handoverIdentifier: 'OpenID4VPHandover' | 'OpenID4VPDCAPIHandover'; + if (handoverType === 'dc_api') { if (!args.dcApiOrigin) { - throw new Error("Missing dcApiOrigin for OpenID4VP dc_api handover"); + throw new Error('Missing dcApiOrigin for OpenID4VP dc_api handover'); } handoverInfoBytes = cborEncode([args.dcApiOrigin, args.nonce, jwkThumbprint]); - handoverIdentifier = "OpenID4VPDCAPIHandover"; + handoverIdentifier = 'OpenID4VPDCAPIHandover'; } else { if (!args.clientId || !args.responseUri) { - throw new Error("Missing clientId/responseUri for OpenID4VP redirect handover"); + throw new Error('Missing clientId/responseUri for OpenID4VP redirect handover'); } handoverInfoBytes = cborEncode([args.clientId, args.nonce, jwkThumbprint, args.responseUri]); - handoverIdentifier = "OpenID4VPHandover"; + handoverIdentifier = 'OpenID4VPHandover'; } const handoverInfoHash = new Uint8Array( - await args.subtle.digest(DigestHashAlgorithm.SHA_256, handoverInfoBytes as Uint8Array) + await args.subtle.digest( + DigestHashAlgorithm.SHA_256, + handoverInfoBytes as Uint8Array, + ), ); return cborEncode([null, null, [handoverIdentifier, handoverInfoHash]]); } diff --git a/src/protocols/openid4vp/transactionData.ts b/src/protocols/openid4vp/transactionData.ts index e955881..bae4f8f 100644 --- a/src/protocols/openid4vp/transactionData.ts +++ b/src/protocols/openid4vp/transactionData.ts @@ -1,58 +1,72 @@ import { base64url } from 'jose'; -import { z } from "zod"; -import { fromBase64Url, toBase64Url } from "../../utils/util"; +import { z } from 'zod'; +import { fromBase64Url, toBase64Url } from '../../utils/util'; import { TransactionDataResponseGenerator, TransactionDataResponseGeneratorParams } from './types'; import { DigestHashAlgorithm, HashAlgorithm } from '../../types'; -export const TransactionDataRequestObject = z.discriminatedUnion("type", [ - z.object({ - type: z.literal("urn:wwwallet:example_transaction_data_type"), - credential_ids: z.array(z.string()), - }).strict(), - - z.object({ - type: z.literal("qes_authorization"), - credential_ids: z.array(z.string()), - signatureQualifier: z.string(), - transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), - documentDigests: z.array(z.object({ - hash: z.string().optional(), - label: z.string(), - hashAlgorithmOID: z.string(), - })), - }).strict(), - - z.object({ - type: z.literal("qcert_creation_acceptance"), - credential_ids: z.array(z.string()), - QC_terms_conditions_uri: z.string().optional(), - QC_hash: z.string().optional(), - QC_hashAlgorithmOID: z.string().optional(), - transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), - }).strict(), - - z.object({ - type: z.literal("https://cloudsignatureconsortium.org/2025/qes"), - credential_ids: z.array(z.string()), - numSignatures: z.number().optional(), - signatureQualifier: z.string(), - transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), - documentDigests: z.array(z.object({ - hash: z.string().optional(), - label: z.string(), - hashType: z.string(), - })), - processID: z.string().optional(), - }).strict(), - - z.object({ - type: z.literal("https://cloudsignatureconsortium.org/2025/qc-request"), - credential_ids: z.array(z.string()), - QC_terms_conditions_uri: z.string().optional(), - QC_hash: z.string().optional(), - QC_hashAlgorithmOID: z.string().optional(), - transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), - }).strict(), +export const TransactionDataRequestObject = z.discriminatedUnion('type', [ + z + .object({ + type: z.literal('urn:wwwallet:example_transaction_data_type'), + credential_ids: z.array(z.string()), + }) + .strict(), + + z + .object({ + type: z.literal('qes_authorization'), + credential_ids: z.array(z.string()), + signatureQualifier: z.string(), + transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), + documentDigests: z.array( + z.object({ + hash: z.string().optional(), + label: z.string(), + hashAlgorithmOID: z.string(), + }), + ), + }) + .strict(), + + z + .object({ + type: z.literal('qcert_creation_acceptance'), + credential_ids: z.array(z.string()), + QC_terms_conditions_uri: z.string().optional(), + QC_hash: z.string().optional(), + QC_hashAlgorithmOID: z.string().optional(), + transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), + }) + .strict(), + + z + .object({ + type: z.literal('https://cloudsignatureconsortium.org/2025/qes'), + credential_ids: z.array(z.string()), + numSignatures: z.number().optional(), + signatureQualifier: z.string(), + transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), + documentDigests: z.array( + z.object({ + hash: z.string().optional(), + label: z.string(), + hashType: z.string(), + }), + ), + processID: z.string().optional(), + }) + .strict(), + + z + .object({ + type: z.literal('https://cloudsignatureconsortium.org/2025/qc-request'), + credential_ids: z.array(z.string()), + QC_terms_conditions_uri: z.string().optional(), + QC_hash: z.string().optional(), + QC_hashAlgorithmOID: z.string().optional(), + transaction_data_hashes_alg: z.array(z.literal(HashAlgorithm.sha_256)), + }) + .strict(), ]); export type TransactionDataRequest = z.infer; @@ -64,14 +78,14 @@ export type ParsedTransactionDataType = { export function parseTransactionData( transaction_data: string[], - dcql_query: Record + dcql_query: Record, ): ParsedTransactionDataType[] | null { try { let validCredentialIds: string[] | null = null; if ((dcql_query as any)?.credentials instanceof Array) { validCredentialIds = (dcql_query as any).credentials.map( - (credential: { id: string }) => credential.id + (credential: { id: string }) => credential.id, ); } @@ -87,14 +101,15 @@ export function parseTransactionData( if (td.parsed.credential_ids && validCredentialIds) { for (const cred_id of td.parsed.credential_ids) { if (!validCredentialIds.includes(cred_id)) { - throw new Error("Transaction data includes invalid credential ids that don't exist in the definition"); + throw new Error( + "Transaction data includes invalid credential ids that don't exist in the definition", + ); } } } } return parsedTransactionData; - } - catch (e) { + } catch (e) { console.error(e); return null; } @@ -102,116 +117,165 @@ export function parseTransactionData( export async function convertTransactionDataB65uToHash(x: string) { const data = fromBase64Url(x); - const digest = await crypto.subtle.digest(DigestHashAlgorithm.SHA_256, data as Uint8Array); + const digest = await crypto.subtle.digest( + DigestHashAlgorithm.SHA_256, + data as Uint8Array, + ); return toBase64Url(digest); } -export const TransactionDataResponse = ({ descriptor_id, dcql_query }: TransactionDataResponseGeneratorParams): TransactionDataResponseGenerator => { +export const TransactionDataResponse = ({ + descriptor_id, + dcql_query, +}: TransactionDataResponseGeneratorParams): TransactionDataResponseGenerator => { return { generateTransactionDataResponse: async (transaction_data: string[]) => { const parsedTd = parseTransactionData(transaction_data, dcql_query); if (parsedTd === null) { - return [null, new Error("invalid_transaction_data")]; + return [null, new Error('invalid_transaction_data')]; } for (const td of parsedTd) { if (td.parsed.credential_ids.includes(descriptor_id)) { - return [{ - transaction_data_hashes: [await convertTransactionDataB65uToHash(td.transaction_data_b64u)], - transaction_data_hashes_alg: [HashAlgorithm.sha_256], - }, null] + return [ + { + transaction_data_hashes: [ + await convertTransactionDataB65uToHash(td.transaction_data_b64u), + ], + transaction_data_hashes_alg: [HashAlgorithm.sha_256], + }, + null, + ]; } } return [null, new Error("Couldn't generate transaction data response")]; }, - } -} - + }; +}; export const QESAuthorizationTransactionData = () => { const webcrypto = crypto.subtle; const generateTransactionDataRequestObject = async (descriptorId: string) => { - return base64url.encode(JSON.stringify({ - type: 'https://cloudsignatureconsortium.org/2025/qes', - credential_ids: [descriptorId], - signatureQualifier: "eu_eidas_qes", - transaction_data_hashes_alg: [HashAlgorithm.sha_256], - numSignatures: 1, - processID: "random-process-id", - documentDigests: [ - { - hash: "some-hash-of-the-document", - label: "Personal Loan Agreement", - hashType: "sodr" - } - ], - })); - } + return base64url.encode( + JSON.stringify({ + type: 'https://cloudsignatureconsortium.org/2025/qes', + credential_ids: [descriptorId], + signatureQualifier: 'eu_eidas_qes', + transaction_data_hashes_alg: [HashAlgorithm.sha_256], + numSignatures: 1, + processID: 'random-process-id', + documentDigests: [ + { + hash: 'some-hash-of-the-document', + label: 'Personal Loan Agreement', + hashType: 'sodr', + }, + ], + }), + ); + }; return { generateTransactionDataRequestObject, - validateTransactionDataResponse: async (exprectedDescriptorId: string, params: { transaction_data_hashes: string[], transaction_data_hashes_alg?: string[] }) => { + validateTransactionDataResponse: async ( + exprectedDescriptorId: string, + params: { transaction_data_hashes: string[]; transaction_data_hashes_alg?: string[] }, + ) => { const expectedObjectB64U = await generateTransactionDataRequestObject(exprectedDescriptorId); const expectedObjectDecoded = fromBase64Url(expectedObjectB64U); for (const hashB64U of params.transaction_data_hashes) { console.log(params.transaction_data_hashes_alg); - if (!params.transaction_data_hashes_alg || params.transaction_data_hashes_alg.includes(HashAlgorithm.sha_256)) { // sha256 case - const calculatedHashOfExpectedObject = toBase64Url(await webcrypto.digest(DigestHashAlgorithm.SHA_256, expectedObjectDecoded as Uint8Array)); - console.log("calculatedHash = ", calculatedHashOfExpectedObject); - console.log("hashB64U = ", hashB64U); + if ( + !params.transaction_data_hashes_alg || + params.transaction_data_hashes_alg.includes(HashAlgorithm.sha_256) + ) { + // sha256 case + const calculatedHashOfExpectedObject = toBase64Url( + await webcrypto.digest( + DigestHashAlgorithm.SHA_256, + expectedObjectDecoded as Uint8Array, + ), + ); + console.log('calculatedHash = ', calculatedHashOfExpectedObject); + console.log('hashB64U = ', hashB64U); if (calculatedHashOfExpectedObject === hashB64U) { - return { status: true, message: "User authorized the QTSP to create QES for the document \"Personal Loan Agreement\"" }; + return { + status: true, + message: + 'User authorized the QTSP to create QES for the document "Personal Loan Agreement"', + }; } } } - return { status: true, message: "" }; - } - } -} + return { status: true, message: '' }; + }, + }; +}; export const QCRequestTransactionData = () => { const webcrypto = crypto.subtle; const generateTransactionDataRequestObject = async (descriptorId: string) => { - return base64url.encode(JSON.stringify({ - type: 'https://cloudsignatureconsortium.org/2025/qc-request', - credential_ids: [descriptorId], - QC_terms_conditions_uri: "https://qtsp.example.com/policies/terms_and_conditions.pdf", - QC_hash: "ohxKcClPp/J1dI1iv5x519BpjduGZC794x4ABFeb+Ds=", - QC_hashAlgorithmOID: "2.16.840.1.101.3.4.2.1", - transaction_data_hashes_alg: [HashAlgorithm.sha_256] - })); - } + return base64url.encode( + JSON.stringify({ + type: 'https://cloudsignatureconsortium.org/2025/qc-request', + credential_ids: [descriptorId], + QC_terms_conditions_uri: 'https://qtsp.example.com/policies/terms_and_conditions.pdf', + QC_hash: 'ohxKcClPp/J1dI1iv5x519BpjduGZC794x4ABFeb+Ds=', + QC_hashAlgorithmOID: '2.16.840.1.101.3.4.2.1', + transaction_data_hashes_alg: [HashAlgorithm.sha_256], + }), + ); + }; return { generateTransactionDataRequestObject, - validateTransactionDataResponse: async (exprectedDescriptorId: string, params: { transaction_data_hashes: string[], transaction_data_hashes_alg?: string[] }) => { + validateTransactionDataResponse: async ( + exprectedDescriptorId: string, + params: { transaction_data_hashes: string[]; transaction_data_hashes_alg?: string[] }, + ) => { const expectedObjectB64U = await generateTransactionDataRequestObject(exprectedDescriptorId); const expectedObjectDecoded = fromBase64Url(expectedObjectB64U); for (const hashB64U of params.transaction_data_hashes) { - if (!params.transaction_data_hashes_alg || params.transaction_data_hashes_alg.includes(HashAlgorithm.sha_256)) { // sha256 case - const calculatedHashOfExpectedObject = toBase64Url(await webcrypto.digest(DigestHashAlgorithm.SHA_256, expectedObjectDecoded as Uint8Array)); - console.log("calculatedHash = ", calculatedHashOfExpectedObject); - console.log("hashB64U = ", hashB64U); + if ( + !params.transaction_data_hashes_alg || + params.transaction_data_hashes_alg.includes(HashAlgorithm.sha_256) + ) { + // sha256 case + const calculatedHashOfExpectedObject = toBase64Url( + await webcrypto.digest( + DigestHashAlgorithm.SHA_256, + expectedObjectDecoded as Uint8Array, + ), + ); + console.log('calculatedHash = ', calculatedHashOfExpectedObject); + console.log('hashB64U = ', hashB64U); if (calculatedHashOfExpectedObject === hashB64U) { - return { status: true, message: "User attested the creation of Qualified Certificates" }; + return { + status: true, + message: 'User attested the creation of Qualified Certificates', + }; } } } - return { status: false, message: "" }; - } - } -} + return { status: false, message: '' }; + }, + }; +}; -export const TransactionData = (transactionDataType: 'https://cloudsignatureconsortium.org/2025/qes' | 'https://cloudsignatureconsortium.org/2025/qc-request') => { - switch(transactionDataType) { - case "https://cloudsignatureconsortium.org/2025/qes": +export const TransactionData = ( + transactionDataType: + | 'https://cloudsignatureconsortium.org/2025/qes' + | 'https://cloudsignatureconsortium.org/2025/qc-request', +) => { + switch (transactionDataType) { + case 'https://cloudsignatureconsortium.org/2025/qes': return QESAuthorizationTransactionData(); - case "https://cloudsignatureconsortium.org/2025/qc-request": + case 'https://cloudsignatureconsortium.org/2025/qc-request': return QCRequestTransactionData(); default: return null; } -} +}; diff --git a/src/protocols/openid4vp/types.ts b/src/protocols/openid4vp/types.ts index 3e98f16..982336b 100644 --- a/src/protocols/openid4vp/types.ts +++ b/src/protocols/openid4vp/types.ts @@ -1,4 +1,4 @@ -import type { JWK } from "jose"; +import type { JWK } from 'jose'; export type ClaimRecord = { key: string; @@ -10,7 +10,7 @@ export type PresentationClaims = Record; export type PresentationInfo = { [descriptor_id: string]: Array; -} +}; export interface RPState { session_id: string; @@ -60,8 +60,8 @@ export type CredentialEngineOptions = { }; export type OpenID4VPOptions = { - credentialEngineOptions: CredentialEngineOptions, - redirectUri: string + credentialEngineOptions: CredentialEngineOptions; + redirectUri: string; }; export type CredentialIssuerMetadata = { @@ -73,19 +73,19 @@ export type IacasResponse = { }; export enum OpenID4VPResponseMode { - DIRECT_POST = "direct_post", - DIRECT_POST_JWT = "direct_post.jwt", - DC_API = "dc_api", - DC_API_JWT = "dc_api.jwt", + DIRECT_POST = 'direct_post', + DIRECT_POST_JWT = 'direct_post.jwt', + DC_API = 'dc_api', + DC_API_JWT = 'dc_api.jwt', } export enum OpenID4VPJweEncryption { - A128GCM = "A128GCM", - A192GCM = "A192GCM", - A256GCM = "A256GCM", + A128GCM = 'A128GCM', + A192GCM = 'A192GCM', + A256GCM = 'A256GCM', } -export type OpenID4VPClientIdScheme = "x509_san_dns" | "x509_hash"; +export type OpenID4VPClientIdScheme = 'x509_san_dns' | 'x509_hash'; export type OpenID4VPClientMetadata = { jwks?: { keys: any[] }; @@ -127,7 +127,9 @@ export type TransactionDataResponseParams = { }; export type TransactionDataResponseGenerator = { - generateTransactionDataResponse(transaction_data: string[]): Promise<[TransactionDataResponseParams | null, Error | null]>; + generateTransactionDataResponse( + transaction_data: string[], + ): Promise<[TransactionDataResponseParams | null, Error | null]>; }; export type TransactionDataResponseGeneratorParams = { diff --git a/src/rendering.test.ts b/src/rendering.test.ts index fecbfe5..e591d51 100644 --- a/src/rendering.test.ts +++ b/src/rendering.test.ts @@ -1,145 +1,141 @@ -import { assert, describe, it } from "vitest"; -import { CredentialRenderingService } from "./rendering"; -import { DOMParser, onErrorStopParsing } from "@xmldom/xmldom"; +import { assert, describe, it } from 'vitest'; +import { CredentialRenderingService } from './rendering'; +import { DOMParser, onErrorStopParsing } from '@xmldom/xmldom'; import fs from 'fs'; import path from 'path'; function dataUriToSvg(dataUri: string) { // Check if it starts with the SVG Data URI prefix - if (dataUri.startsWith("data:image/svg+xml;utf8,")) { + if (dataUri.startsWith('data:image/svg+xml;utf8,')) { // Remove the prefix and decode the remaining part - const svgString = decodeURIComponent(dataUri.replace("data:image/svg+xml;utf8,", "")); + const svgString = decodeURIComponent(dataUri.replace('data:image/svg+xml;utf8,', '')); return svgString; - } else if (dataUri.startsWith("data:image/svg+xml;base64,")) { + } else if (dataUri.startsWith('data:image/svg+xml;base64,')) { // For base64-encoded SVGs, handle accordingly - const base64Data = dataUri.replace("data:image/svg+xml;base64,", ""); + const base64Data = dataUri.replace('data:image/svg+xml;base64,', ''); const svgString = atob(base64Data); // Decode base64 to plain text return svgString; } else { - throw new Error("Invalid SVG Data URI format"); + throw new Error('Invalid SVG Data URI format'); } } function isValidSVG(dataUri: string) { - const svgString = dataUriToSvg(dataUri); try { const parser = new DOMParser({ onError: onErrorStopParsing }); - const doc = parser.parseFromString(svgString, "image/svg+xml"); + const doc = parser.parseFromString(svgString, 'image/svg+xml'); // Parse errors throw (onErrorStopParsing), so only check the root element - return doc.documentElement?.nodeName === "svg"; + return doc.documentElement?.nodeName === 'svg'; } catch (e) { return false; } } const exampleCredentialPayload = { - "cnf": { - "jwk": { - "crv": "P-256", - "ext": true, - "key_ops": [ - "verify" - ], - "kty": "EC", - "x": "lJ9Xd4t7nHpLdJRdhD5P6weX9KVDyefYKUrevWXjwm4", - "y": "QcjdcG_EwXyCA0jWyS66cUVsGSrCZTRwTRp3avmjFKA" - } + cnf: { + jwk: { + crv: 'P-256', + ext: true, + key_ops: ['verify'], + kty: 'EC', + x: 'lJ9Xd4t7nHpLdJRdhD5P6weX9KVDyefYKUrevWXjwm4', + y: 'QcjdcG_EwXyCA0jWyS66cUVsGSrCZTRwTRp3avmjFKA', + }, }, - "vct": "urn:credential:vid", - "jti": "urn:vid:b54b3fa2-4527-433f-b0ba-b3e7f4ad2826", - "iat": 1736936437, - "exp": 1736936, - "iss": "http://wallet-enterprise-vid-issuer:8003", - "sub": "vwGYXSfFHb3m9sVnIGbZ6aIpFPsy2YY-Yfthm8nBsXs", - "family_name": "Doe", - "given_name": "John", - "birth_date": "1990-10-15T00:00:00.000Z", - "issuing_authority": "PID:00001", - "issuing_country": "GR", - "document_number": "12313213", - "issuance_date": "2025-01-15T10:20:37.707Z", - "expiry_date": 1736936437 + vct: 'urn:credential:vid', + jti: 'urn:vid:b54b3fa2-4527-433f-b0ba-b3e7f4ad2826', + iat: 1736936437, + exp: 1736936, + iss: 'http://wallet-enterprise-vid-issuer:8003', + sub: 'vwGYXSfFHb3m9sVnIGbZ6aIpFPsy2YY-Yfthm8nBsXs', + family_name: 'Doe', + given_name: 'John', + birth_date: '1990-10-15T00:00:00.000Z', + issuing_authority: 'PID:00001', + issuing_country: 'GR', + document_number: '12313213', + issuance_date: '2025-01-15T10:20:37.707Z', + expiry_date: 1736936437, }; - /** * Follows: https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-08.html#name-claim-metadata */ const exampleSdJwtVcMetadataClaimsAttribute = [ { - "path": ["given_name"], - "display": [ + path: ['given_name'], + display: [ { - "lang": "en-US", - "label": "Given Name", - "description": "The given name of the VID holder" - } + lang: 'en-US', + label: 'Given Name', + description: 'The given name of the VID holder', + }, ], - "svg_id": "given_name" + svg_id: 'given_name', }, { - "path": ["family_name"], - "display": [ + path: ['family_name'], + display: [ { - "lang": "en-US", - "label": "Family Name", - "description": "The family name of the VID holder" - } + lang: 'en-US', + label: 'Family Name', + description: 'The family name of the VID holder', + }, ], - "svg_id": "family_name" + svg_id: 'family_name', }, { - "path": ["birth_date"], - "display": [ + path: ['birth_date'], + display: [ { - "lang": "en-US", - "label": "Birth date", - "description": "The birth date of the VID holder" - } + lang: 'en-US', + label: 'Birth date', + description: 'The birth date of the VID holder', + }, ], - "svg_id": "birth_date" + svg_id: 'birth_date', }, { - "path": ["issuing_authority"], - "display": [ + path: ['issuing_authority'], + display: [ { - "lang": "en-US", - "label": "Issuing authority", - "description": "The issuing authority of the VID credential" - } + lang: 'en-US', + label: 'Issuing authority', + description: 'The issuing authority of the VID credential', + }, ], - "svg_id": "issuing_authority" + svg_id: 'issuing_authority', }, { - "path": ["issuance_date"], - "display": [ + path: ['issuance_date'], + display: [ { - "lang": "en-US", - "label": "Issuance date", - "description": "The date that the credential was issued" - } + lang: 'en-US', + label: 'Issuance date', + description: 'The date that the credential was issued', + }, ], - "svg_id": "issuance_date" + svg_id: 'issuance_date', }, { - "path": ["expiry_date"], - "display": [ + path: ['expiry_date'], + display: [ { - "lang": "en-US", - "label": "Issuance date", - "description": "The date that the credential will expire" - } + lang: 'en-US', + label: 'Issuance date', + description: 'The date that the credential will expire', + }, ], - "svg_id": "expiry_date" - } + svg_id: 'expiry_date', + }, ]; -describe("The CredentialRendering", () => { +describe('The CredentialRendering', () => { const cr = CredentialRenderingService(); - it("can render credential in svg format", async () => { - const svgPath = path.join(__dirname, "../assets/template.svg"); + it('can render credential in svg format', async () => { + const svgPath = path.join(__dirname, '../assets/template.svg'); const credentialSvgTemplate = fs.readFileSync(svgPath, { encoding: 'utf-8' }); const dataUri = await cr.renderSvgTemplate({ json: { ...exampleCredentialPayload }, @@ -147,24 +143,26 @@ describe("The CredentialRendering", () => { sdJwtVcMetadataClaims: exampleSdJwtVcMetadataClaimsAttribute, }); - assert(dataUri !== null, "Svg not rendered"); - assert(isValidSVG(dataUri) == true, "Not valid generated datauri svg"); + assert(dataUri !== null, 'Svg not rendered'); + assert(isValidSVG(dataUri) == true, 'Not valid generated datauri svg'); }); - it("can render credential in svg format with filter", async () => { - const svgPath = path.join(__dirname, "../assets/template.svg"); + it('can render credential in svg format with filter', async () => { + const svgPath = path.join(__dirname, '../assets/template.svg'); const credentialSvgTemplate = fs.readFileSync(svgPath, { encoding: 'utf-8' }); const dataUri = await cr.renderSvgTemplate({ json: { ...exampleCredentialPayload }, credentialImageSvgTemplate: credentialSvgTemplate, sdJwtVcMetadataClaims: exampleSdJwtVcMetadataClaimsAttribute, - filter: [ - ["birth_date"] - ] + filter: [['birth_date']], }); - assert(dataUri !== null, "Svg not rendered"); - assert(isValidSVG(dataUri) == true, "Not valid generated datauri svg"); - fs.writeFileSync(path.join(__dirname, "../output/filtered.svg"), dataUriToSvg(dataUri), 'utf-8'); + assert(dataUri !== null, 'Svg not rendered'); + assert(isValidSVG(dataUri) == true, 'Not valid generated datauri svg'); + fs.writeFileSync( + path.join(__dirname, '../output/filtered.svg'), + dataUriToSvg(dataUri), + 'utf-8', + ); }); -}) +}); diff --git a/src/rendering.ts b/src/rendering.ts index 335cec3..d9a4466 100644 --- a/src/rendering.ts +++ b/src/rendering.ts @@ -13,7 +13,7 @@ function getPathValue(value: unknown, path: Array): any current = current.get(segment); continue; } - if (typeof current === "object") { + if (typeof current === 'object') { current = (current as Record)[segment]; continue; } @@ -23,8 +23,17 @@ function getPathValue(value: unknown, path: Array): any } export function CredentialRenderingService(): CredentialRendering { - const renderSvgTemplate = async ({ json, credentialImageSvgTemplate, sdJwtVcMetadataClaims, filter }: { json: any, credentialImageSvgTemplate: string, sdJwtVcMetadataClaims: any, filter?: Array }) => { - + const renderSvgTemplate = async ({ + json, + credentialImageSvgTemplate, + sdJwtVcMetadataClaims, + filter, + }: { + json: any; + credentialImageSvgTemplate: string; + sdJwtVcMetadataClaims: any; + filter?: Array; + }) => { let svgContent = null; try { svgContent = credentialImageSvgTemplate; @@ -47,7 +56,11 @@ export function CredentialRenderingService(): CredentialRendering { // Retrieve the path array for the current svgId from pathMap const pathArray = pathMap[svgId]; - if (Array.isArray(pathArray) && filter && !filter.map(f => f.join('.')).includes(pathArray.join('.'))) { + if ( + Array.isArray(pathArray) && + filter && + !filter.map((f) => f.join('.')).includes(pathArray.join('.')) + ) { return '-'; } if (Array.isArray(pathArray)) { @@ -69,5 +82,5 @@ export function CredentialRenderingService(): CredentialRendering { return { renderSvgTemplate, - } + }; } diff --git a/src/resolvers/dataUriResolver.test.ts b/src/resolvers/dataUriResolver.test.ts index d20ae9d..173a90d 100644 --- a/src/resolvers/dataUriResolver.test.ts +++ b/src/resolvers/dataUriResolver.test.ts @@ -1,14 +1,14 @@ -import { expect, it, vi } from "vitest"; -import { CustomCredentialSvg } from "../functions/CustomCredentialSvg"; -import { CredentialRenderingService } from "../rendering"; -import { dataUriResolver } from "./dataUriResolver"; +import { expect, it, vi } from 'vitest'; +import { CustomCredentialSvg } from '../functions/CustomCredentialSvg'; +import { CredentialRenderingService } from '../rendering'; +import { dataUriResolver } from './dataUriResolver'; -it("falls back when an uncached SVG template is unavailable offline", async () => { +it('falls back when an uncached SVG template is unavailable offline', async () => { const httpClient = { get: vi.fn().mockResolvedValue({ status: 504, headers: {}, - data: "No cached response available and offline", + data: 'No cached response available and offline', }), post: vi.fn(), }; @@ -16,54 +16,61 @@ it("falls back when an uncached SVG template is unavailable offline", async () = httpClient, customRenderer: CustomCredentialSvg({ httpClient }), sdJwtVcRenderer: CredentialRenderingService(), - credentialDisplayArray: [{ - locale: "en-US", - name: "Credential", - rendering: { - svg_templates: [{ uri: "https://issuer.example/template.svg" }], - simple: {}, + credentialDisplayArray: [ + { + locale: 'en-US', + name: 'Credential', + rendering: { + svg_templates: [{ uri: 'https://issuer.example/template.svg' }], + simple: {}, + }, }, - }], + ], }); const result = await resolveDataUri(); - const svg = decodeURIComponent(result!.replace("data:image/svg+xml;utf8,", "")); + const svg = decodeURIComponent(result!.replace('data:image/svg+xml;utf8,', '')); expect(svg).toContain('fill="#D3D3D3"'); }); -it("tries the next SVG template before simple rendering when the preferred one is unavailable", async () => { - const portraitUri = "https://issuer.example/portrait.svg"; - const landscapeUri = "https://issuer.example/landscape.svg"; - const landscapeSvg = 'Landscape fallback'; +it('tries the next SVG template before simple rendering when the preferred one is unavailable', async () => { + const portraitUri = 'https://issuer.example/portrait.svg'; + const landscapeUri = 'https://issuer.example/landscape.svg'; + const landscapeSvg = + 'Landscape fallback'; const httpClient = { - get: vi.fn(async (uri: string) => uri === landscapeUri - ? { status: 200, headers: {}, data: landscapeSvg } - : { status: 504, headers: {}, data: "No cached response available and offline" }), + get: vi.fn(async (uri: string) => + uri === landscapeUri + ? { status: 200, headers: {}, data: landscapeSvg } + : { status: 504, headers: {}, data: 'No cached response available and offline' }, + ), post: vi.fn(), }; const resolveDataUri = dataUriResolver({ httpClient, customRenderer: CustomCredentialSvg({ httpClient }), sdJwtVcRenderer: CredentialRenderingService(), - credentialDisplayArray: [{ - locale: "en-US", - name: "Credential", - rendering: { - svg_templates: [ - { uri: landscapeUri, properties: { orientation: "landscape" } }, - { uri: portraitUri, properties: { orientation: "portrait" } }, - ], - simple: { background_color: "#ff0000" }, + credentialDisplayArray: [ + { + locale: 'en-US', + name: 'Credential', + rendering: { + svg_templates: [ + { uri: landscapeUri, properties: { orientation: 'landscape' } }, + { uri: portraitUri, properties: { orientation: 'portrait' } }, + ], + simple: { background_color: '#ff0000' }, + }, }, - }], + ], }); - const result = await resolveDataUri(undefined, ["en-US"], { orientation: "portrait" }); - const svg = decodeURIComponent(result!.replace("data:image/svg+xml;utf8,", "")); + const result = await resolveDataUri(undefined, ['en-US'], { orientation: 'portrait' }); + const svg = decodeURIComponent(result!.replace('data:image/svg+xml;utf8,', '')); expect(httpClient.get).toHaveBeenNthCalledWith(1, portraitUri, {}, { useCache: true }); expect(httpClient.get).toHaveBeenNthCalledWith(2, landscapeUri, {}, { useCache: true }); - expect(svg).toContain("Landscape fallback"); + expect(svg).toContain('Landscape fallback'); expect(svg).not.toContain('fill="#ff0000"'); }); diff --git a/src/resolvers/dataUriResolver.ts b/src/resolvers/dataUriResolver.ts index 9c82971..2c35a21 100644 --- a/src/resolvers/dataUriResolver.ts +++ b/src/resolvers/dataUriResolver.ts @@ -1,15 +1,18 @@ -import type { HttpClient, CredentialRendering, CustomCredentialSvgI } from "../interfaces"; -import type { CredentialClaimPath, ImageDataUriCallback } from "../types"; -import { rankSvgTemplates } from "../functions/pickBestSvgTemplate"; -import { matchDisplayByLocale } from "../utils/matchLocalizedDisplay"; -import type { TypeDisplayEntry, ClaimMetadataEntry, SvgTemplateProperties } from "../schemas/SdJwtVcTypeMetadataSchema"; -import type { CredentialConfigurationSupported } from "../schemas/CredentialConfigurationSupportedSchema"; -import { loadSvgTemplate } from "./loadSvgTemplate"; - -type IssuerDisplayEntry = - NonNullable< - NonNullable["display"] - >[number]; +import type { HttpClient, CredentialRendering, CustomCredentialSvgI } from '../interfaces'; +import type { CredentialClaimPath, ImageDataUriCallback } from '../types'; +import { rankSvgTemplates } from '../functions/pickBestSvgTemplate'; +import { matchDisplayByLocale } from '../utils/matchLocalizedDisplay'; +import type { + TypeDisplayEntry, + ClaimMetadataEntry, + SvgTemplateProperties, +} from '../schemas/SdJwtVcTypeMetadataSchema'; +import type { CredentialConfigurationSupported } from '../schemas/CredentialConfigurationSupportedSchema'; +import { loadSvgTemplate } from './loadSvgTemplate'; + +type IssuerDisplayEntry = NonNullable< + NonNullable['display'] +>[number]; type DataUriResolverOptions = { httpClient: HttpClient; @@ -33,35 +36,31 @@ export function dataUriResolver({ httpClient, sdJwtVcRenderer, sdJwtVcMetadataClaims, - fallbackName = "Verifiable Credential", + fallbackName = 'Verifiable Credential', }: DataUriResolverOptions): ImageDataUriCallback { return async ( filter?: Array, - preferredLangs: string[] = ["en-US"], + preferredLangs: string[] = ['en-US'], preferredProperties: SvgTemplateProperties = { - orientation: "landscape", - color_scheme: "light", - contrast: "normal", - } + orientation: 'landscape', + color_scheme: 'light', + contrast: 'normal', + }, ) => { try { // Localize display configs const credentialDisplayLocalized = matchDisplayByLocale( credentialDisplayArray, - preferredLangs - ); - const issuerDisplayLocalized = matchDisplayByLocale( - issuerDisplayArray, - preferredLangs + preferredLangs, ); + const issuerDisplayLocalized = matchDisplayByLocale(issuerDisplayArray, preferredLangs); const svgTemplates = credentialDisplayLocalized?.rendering?.svg_templates ?? issuerDisplayLocalized?.rendering?.svg_templates; const rankedSvgTemplates = rankSvgTemplates(svgTemplates, preferredProperties); - const simpleDisplayConfig = - credentialDisplayLocalized?.rendering?.simple || null; + const simpleDisplayConfig = credentialDisplayLocalized?.rendering?.simple || null; // 1. Try every SVG template in preference order if (sdJwtVcRenderer) { diff --git a/src/resolvers/friendlyNameResolver.ts b/src/resolvers/friendlyNameResolver.ts index f3fea1c..651a884 100644 --- a/src/resolvers/friendlyNameResolver.ts +++ b/src/resolvers/friendlyNameResolver.ts @@ -1,12 +1,11 @@ -import type { FriendlyNameCallback } from "../types"; -import { matchDisplayByLocale } from "../utils/matchLocalizedDisplay"; -import type { TypeDisplayEntry } from "../schemas/SdJwtVcTypeMetadataSchema"; -import type { CredentialConfigurationSupported } from "../schemas/CredentialConfigurationSupportedSchema"; +import type { FriendlyNameCallback } from '../types'; +import { matchDisplayByLocale } from '../utils/matchLocalizedDisplay'; +import type { TypeDisplayEntry } from '../schemas/SdJwtVcTypeMetadataSchema'; +import type { CredentialConfigurationSupported } from '../schemas/CredentialConfigurationSupportedSchema'; -type IssuerDisplayEntry = - NonNullable< - NonNullable["display"] - >[number]; +type IssuerDisplayEntry = NonNullable< + NonNullable['display'] +>[number]; type FriendlyNameResolverOptions = { credentialDisplayArray?: TypeDisplayEntry[]; @@ -17,24 +16,18 @@ type FriendlyNameResolverOptions = { export function friendlyNameResolver({ credentialDisplayArray, issuerDisplayArray, - fallbackName = "Verifiable Credential", + fallbackName = 'Verifiable Credential', }: FriendlyNameResolverOptions): FriendlyNameCallback { - return async (preferredLangs: string[] = ["en-US"]): Promise => { + return async (preferredLangs: string[] = ['en-US']): Promise => { // 1) Credential display name - const credentialDisplayLocalized = matchDisplayByLocale( - credentialDisplayArray, - preferredLangs - ); + const credentialDisplayLocalized = matchDisplayByLocale(credentialDisplayArray, preferredLangs); if (credentialDisplayLocalized?.name) { return credentialDisplayLocalized.name; } // 2) Issuer display name - const issuerDisplayLocalized = matchDisplayByLocale( - issuerDisplayArray, - preferredLangs - ); + const issuerDisplayLocalized = matchDisplayByLocale(issuerDisplayArray, preferredLangs); if (issuerDisplayLocalized?.name) { return issuerDisplayLocalized.name; diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts index c4dcc54..075f528 100644 --- a/src/resolvers/index.ts +++ b/src/resolvers/index.ts @@ -1,2 +1,2 @@ -export * from "./dataUriResolver"; -export * from "./friendlyNameResolver"; +export * from './dataUriResolver'; +export * from './friendlyNameResolver'; diff --git a/src/resolvers/loadSvgTemplate.ts b/src/resolvers/loadSvgTemplate.ts index d8b40a0..09352d6 100644 --- a/src/resolvers/loadSvgTemplate.ts +++ b/src/resolvers/loadSvgTemplate.ts @@ -1,9 +1,6 @@ -import type { HttpClient } from "../interfaces"; +import type { HttpClient } from '../interfaces'; -export async function loadSvgTemplate( - uri: string, - httpClient: HttpClient -): Promise { +export async function loadSvgTemplate(uri: string, httpClient: HttpClient): Promise { if (uri.startsWith('data:')) { const blob = await fetch(uri) .then((response) => response.blob()) @@ -19,9 +16,7 @@ export async function loadSvgTemplate( } if (uri.startsWith('http')) { - const response = await httpClient - .get(uri, {}, { useCache: true }) - .catch(() => null); + const response = await httpClient.get(uri, {}, { useCache: true }).catch(() => null); if ( response && diff --git a/src/schemas/CredentialConfigurationSupportedSchema.ts b/src/schemas/CredentialConfigurationSupportedSchema.ts index 0c0f96f..802c283 100644 --- a/src/schemas/CredentialConfigurationSupportedSchema.ts +++ b/src/schemas/CredentialConfigurationSupportedSchema.ts @@ -3,19 +3,15 @@ import { VerifiableCredentialFormat } from '../types'; import { SvgTemplateEntry } from './SdJwtVcTypeMetadataSchema'; const attackPotentialResistanceValues = [ - "iso_18045_high", - "iso_18045_moderate", - "iso_18045_enhanced-basic", - "iso_18045_basic" + 'iso_18045_high', + 'iso_18045_moderate', + 'iso_18045_enhanced-basic', + 'iso_18045_basic', ] as const; const KeyAttestationsRequiredSchema = z.object({ - key_storage: z.array( - z.enum(attackPotentialResistanceValues) - ).min(1).optional(), - user_authentication: z.array( - z.enum(attackPotentialResistanceValues) - ).min(1).optional(), + key_storage: z.array(z.enum(attackPotentialResistanceValues)).min(1).optional(), + user_authentication: z.array(z.enum(attackPotentialResistanceValues)).min(1).optional(), }); const ProofTypeSchema = z.object({ @@ -29,68 +25,83 @@ const proofTypesSupportedSchema = z.object({ }); const OpenIdClaimSchema = z.object({ - path: z.array( - z.union([z.string(), z.null(), z.number().int().nonnegative()]) - ).nonempty(), + path: z.array(z.union([z.string(), z.null(), z.number().int().nonnegative()])).nonempty(), mandatory: z.boolean().optional(), svg_id: z.string().optional(), - display: z.array( - z.object({ - name: z.string().optional(), - locale: z.string().optional(), - }) - ).optional(), + display: z + .array( + z.object({ + name: z.string().optional(), + locale: z.string().optional(), + }), + ) + .optional(), }); const commonSchema = z.object({ - credential_metadata: z.object({ - display: z.array(z.object({ - name: z.string(), - description: z.string().optional(), - background_color: z.string().optional(), - text_color: z.string().optional(), - alt_text: z.string().optional(), - background_image: z.object({ - uri: z.string() - }).optional(), - locale: z.string().optional(), - logo: z.object({ - uri: z.string(), - alt_text: z.string().optional(), - }).optional(), - rendering: z.object({ - svg_templates: z.array(SvgTemplateEntry).optional(), - }).optional(), - })).optional(), - claims: z.array(OpenIdClaimSchema).optional(), - }).optional(), + credential_metadata: z + .object({ + display: z + .array( + z.object({ + name: z.string(), + description: z.string().optional(), + background_color: z.string().optional(), + text_color: z.string().optional(), + alt_text: z.string().optional(), + background_image: z + .object({ + uri: z.string(), + }) + .optional(), + locale: z.string().optional(), + logo: z + .object({ + uri: z.string(), + alt_text: z.string().optional(), + }) + .optional(), + rendering: z + .object({ + svg_templates: z.array(SvgTemplateEntry).optional(), + }) + .optional(), + }), + ) + .optional(), + claims: z.array(OpenIdClaimSchema).optional(), + }) + .optional(), scope: z.string(), cryptographic_binding_methods_supported: z.array(z.string()).optional(), proof_types_supported: proofTypesSupportedSchema.optional(), }); const sdJwtSchema = commonSchema.extend({ - format: z.literal(VerifiableCredentialFormat.VC_SDJWT).or(z.literal(VerifiableCredentialFormat.DC_SDJWT)), + format: z + .literal(VerifiableCredentialFormat.VC_SDJWT) + .or(z.literal(VerifiableCredentialFormat.DC_SDJWT)), credential_signing_alg_values_supported: z.array(z.string()).optional(), - vct: z.string() + vct: z.string(), }); - const msoDocSchema = commonSchema.extend({ format: z.literal(VerifiableCredentialFormat.MSO_MDOC), doctype: z.string(), credential_signing_alg_values_supported: z.array(z.number()).optional(), }); - const jwtVcJsonSchema = commonSchema.extend({ format: z.literal(VerifiableCredentialFormat.JWT_VC_JSON), credential_signing_alg_values_supported: z.array(z.string()).optional(), }); +export const CredentialConfigurationSupportedSchema = sdJwtSchema + .or(msoDocSchema) + .or(jwtVcJsonSchema); -export const CredentialConfigurationSupportedSchema = sdJwtSchema.or(msoDocSchema).or(jwtVcJsonSchema); - -export type CredentialConfigurationSupported = z.infer; +export type CredentialConfigurationSupported = z.infer< + typeof CredentialConfigurationSupportedSchema +>; export type OpenIdClaim = z.infer; diff --git a/src/schemas/CredentialOfferSchema.ts b/src/schemas/CredentialOfferSchema.ts index b495c29..4846a97 100644 --- a/src/schemas/CredentialOfferSchema.ts +++ b/src/schemas/CredentialOfferSchema.ts @@ -1,10 +1,10 @@ -import { z } from 'zod' +import { z } from 'zod'; import { GrantsSchema } from './GrantSchema'; export const CredentialOfferSchema = z.object({ credential_issuer: z.string(), credential_configuration_ids: z.array(z.string()), - grants: GrantsSchema.optional() + grants: GrantsSchema.optional(), }); export type CredentialOffer = z.infer; diff --git a/src/schemas/GrantSchema.ts b/src/schemas/GrantSchema.ts index 6077cce..9364d01 100644 --- a/src/schemas/GrantSchema.ts +++ b/src/schemas/GrantSchema.ts @@ -1,10 +1,10 @@ -import { z } from 'zod' +import { z } from 'zod'; export enum GrantType { - CODE = "code", - AUTHORIZATION_CODE = "authorization_code", - REFRESH = "refresh_token", - PRE_AUTHORIZED_CODE = "urn:ietf:params:oauth:grant-type:pre-authorized_code", + CODE = 'code', + AUTHORIZATION_CODE = 'authorization_code', + REFRESH = 'refresh_token', + PRE_AUTHORIZED_CODE = 'urn:ietf:params:oauth:grant-type:pre-authorized_code', } const CommonGrantSchema = z.object({ @@ -18,7 +18,7 @@ const AuthorizationCodeGrantSchema = CommonGrantSchema.extend({ const TxCodeSchema = z.object({ input_mode: z.enum(['numeric', 'text']).optional().default('numeric'), length: z.number().int().optional(), - description: z.string().max(300).optional() + description: z.string().max(300).optional(), }); const PreAuthorizedCodeGrantSchema = CommonGrantSchema.extend({ @@ -26,10 +26,12 @@ const PreAuthorizedCodeGrantSchema = CommonGrantSchema.extend({ tx_code: TxCodeSchema.optional(), }); -export const GrantsSchema = z.object({ - authorization_code: AuthorizationCodeGrantSchema.optional(), - 'urn:ietf:params:oauth:grant-type:pre-authorized_code': PreAuthorizedCodeGrantSchema.optional(), -}).catchall(z.record(z.string(), z.any())); +export const GrantsSchema = z + .object({ + authorization_code: AuthorizationCodeGrantSchema.optional(), + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': PreAuthorizedCodeGrantSchema.optional(), + }) + .catchall(z.record(z.string(), z.any())); export const GrantSchema = z.union([ z.object({ diff --git a/src/schemas/MdocIacasResponseSchema.ts b/src/schemas/MdocIacasResponseSchema.ts index ee2833e..4e68d49 100644 --- a/src/schemas/MdocIacasResponseSchema.ts +++ b/src/schemas/MdocIacasResponseSchema.ts @@ -1,9 +1,11 @@ -import { z } from "zod"; +import { z } from 'zod'; export const MdocIacasResponseSchema = z.object({ - iacas: z.array(z.object({ - certificate: z.string().optional(), - })) -}) + iacas: z.array( + z.object({ + certificate: z.string().optional(), + }), + ), +}); export type MdocIacasResponse = z.infer; diff --git a/src/schemas/OpenidAuthorizationServerMetadataSchema.ts b/src/schemas/OpenidAuthorizationServerMetadataSchema.ts index 9377a08..6c119fc 100644 --- a/src/schemas/OpenidAuthorizationServerMetadataSchema.ts +++ b/src/schemas/OpenidAuthorizationServerMetadataSchema.ts @@ -15,8 +15,10 @@ export const OpenidAuthorizationServerMetadataSchema = z.object({ scopes_supported: z.array(z.string()).optional(), grant_types_supported: z.array(z.string()).optional(), jwks_uri: z.string().optional(), - "pre-authorized_grant_anonymous_access_supported": z.boolean().optional(), - end_session_endpoint: z.string().optional() + 'pre-authorized_grant_anonymous_access_supported': z.boolean().optional(), + end_session_endpoint: z.string().optional(), }); -export type OpenidAuthorizationServerMetadata = z.infer; +export type OpenidAuthorizationServerMetadata = z.infer< + typeof OpenidAuthorizationServerMetadataSchema +>; diff --git a/src/schemas/OpenidAuthorizationServerMetadataSchemaTest1.test.ts b/src/schemas/OpenidAuthorizationServerMetadataSchemaTest1.test.ts index 1c02e1e..22d6cb3 100644 --- a/src/schemas/OpenidAuthorizationServerMetadataSchemaTest1.test.ts +++ b/src/schemas/OpenidAuthorizationServerMetadataSchemaTest1.test.ts @@ -2,26 +2,22 @@ import { describe, it } from 'vitest'; import { OpenidAuthorizationServerMetadataSchema } from './OpenidAuthorizationServerMetadataSchema'; import assert from 'assert'; - const metadata = { - "issuer": "https://wallet.a-sit.at/m6", - "authorization_endpoint": "https://wallet.a-sit.at/m6/authorize", - "pushed_authorization_request_endpoint": "https://wallet.a-sit.at/m6/par", - "require_pushed_authorization_requests": true, - "token_endpoint": "https://wallet.a-sit.at/m6/token", - "token_endpoint_auth_methods_supported": [ - "attest_jwt_client_auth" - ], - "jwks_uri": "https://example.com/jwks", + issuer: 'https://wallet.a-sit.at/m6', + authorization_endpoint: 'https://wallet.a-sit.at/m6/authorize', + pushed_authorization_request_endpoint: 'https://wallet.a-sit.at/m6/par', + require_pushed_authorization_requests: true, + token_endpoint: 'https://wallet.a-sit.at/m6/token', + token_endpoint_auth_methods_supported: ['attest_jwt_client_auth'], + jwks_uri: 'https://example.com/jwks', }; - -describe("OpenidCredentialIssuerMetadataSchemaValera", () => { +describe('OpenidCredentialIssuerMetadataSchemaValera', () => { it("should successfully parse Valera issuer's metadata", async () => { const res = OpenidAuthorizationServerMetadataSchema.safeParse(metadata); if (res.error) { console.dir(res.error, { depth: null }); } assert(res.success === true); - }) -}) + }); +}); diff --git a/src/schemas/OpenidAuthorizationServerMetadataSchemaTest2.test.ts b/src/schemas/OpenidAuthorizationServerMetadataSchemaTest2.test.ts index 34557ff..3cec2f7 100644 --- a/src/schemas/OpenidAuthorizationServerMetadataSchemaTest2.test.ts +++ b/src/schemas/OpenidAuthorizationServerMetadataSchemaTest2.test.ts @@ -2,16 +2,346 @@ import { describe, it } from 'vitest'; import { OpenidAuthorizationServerMetadataSchema } from './OpenidAuthorizationServerMetadataSchema'; import assert from 'assert'; +const metadata = { + issuer: 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm', + authorization_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth', + token_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token', + introspection_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token/introspect', + userinfo_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/userinfo', + end_session_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/logout', + frontchannel_logout_session_supported: true, + frontchannel_logout_supported: true, + jwks_uri: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/certs', + check_session_iframe: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/login-status-iframe.html', + grant_types_supported: [ + 'authorization_code', + 'implicit', + 'refresh_token', + 'password', + 'client_credentials', + 'urn:openid:params:grant-type:ciba', + 'urn:ietf:params:oauth:grant-type:device_code', + ], + acr_values_supported: ['0', '1'], + response_types_supported: [ + 'code', + 'none', + 'id_token', + 'token', + 'id_token token', + 'code id_token', + 'code token', + 'code id_token token', + ], + subject_types_supported: ['public', 'pairwise'], + prompt_values_supported: ['none', 'login', 'consent', 'create'], + id_token_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + id_token_encryption_alg_values_supported: [ + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA-OAEP', + 'RSA-OAEP-256', + 'RSA1_5', + 'ECDH-ES', + ], + id_token_encryption_enc_values_supported: [ + 'A256GCM', + 'A192GCM', + 'A128GCM', + 'A128CBC-HS256', + 'A192CBC-HS384', + 'A256CBC-HS512', + ], + userinfo_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + 'none', + ], + userinfo_encryption_alg_values_supported: [ + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA-OAEP', + 'RSA-OAEP-256', + 'RSA1_5', + 'ECDH-ES', + ], + userinfo_encryption_enc_values_supported: [ + 'A256GCM', + 'A192GCM', + 'A128GCM', + 'A128CBC-HS256', + 'A192CBC-HS384', + 'A256CBC-HS512', + ], + request_object_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + 'none', + ], + request_object_encryption_alg_values_supported: [ + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA-OAEP', + 'RSA-OAEP-256', + 'RSA1_5', + 'ECDH-ES', + ], + request_object_encryption_enc_values_supported: [ + 'A256GCM', + 'A192GCM', + 'A128GCM', + 'A128CBC-HS256', + 'A192CBC-HS384', + 'A256CBC-HS512', + ], + response_modes_supported: [ + 'query', + 'fragment', + 'form_post', + 'query.jwt', + 'fragment.jwt', + 'form_post.jwt', + 'jwt', + ], + registration_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/clients-registrations/openid-connect', + token_endpoint_auth_methods_supported: [ + 'private_key_jwt', + 'client_secret_basic', + 'client_secret_post', + 'tls_client_auth', + 'client_secret_jwt', + ], + token_endpoint_auth_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + introspection_endpoint_auth_methods_supported: [ + 'private_key_jwt', + 'client_secret_basic', + 'client_secret_post', + 'tls_client_auth', + 'client_secret_jwt', + ], + introspection_endpoint_auth_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + authorization_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + authorization_encryption_alg_values_supported: [ + 'ECDH-ES+A256KW', + 'ECDH-ES+A192KW', + 'ECDH-ES+A128KW', + 'RSA-OAEP', + 'RSA-OAEP-256', + 'RSA1_5', + 'ECDH-ES', + ], + authorization_encryption_enc_values_supported: [ + 'A256GCM', + 'A192GCM', + 'A128GCM', + 'A128CBC-HS256', + 'A192CBC-HS384', + 'A256CBC-HS512', + ], + claims_supported: [ + 'aud', + 'sub', + 'iss', + 'auth_time', + 'name', + 'given_name', + 'family_name', + 'preferred_username', + 'email', + 'acr', + ], + claim_types_supported: ['normal'], + claims_parameter_supported: true, + scopes_supported: [ + 'openid', + 'eu.europa.ec.eudi.pid_vc_sd_jwt', + 'web-origins', + 'offline_access', + 'eu.europa.ec.eudi.pid_mso_mdoc', + 'service_account', + 'roles', + 'urn:eudi:ehic:1:dc+sd-jwt', + 'org.iso.18013.5.1.mDL', + 'basic', + ], + request_parameter_supported: true, + request_uri_parameter_supported: true, + require_request_uri_registration: true, + code_challenge_methods_supported: ['plain', 'S256'], + tls_client_certificate_bound_access_tokens: true, + dpop_signing_alg_values_supported: [ + 'PS384', + 'ES384', + 'RS384', + 'ES256', + 'RS256', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + revocation_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/revoke', + revocation_endpoint_auth_methods_supported: [ + 'private_key_jwt', + 'client_secret_basic', + 'client_secret_post', + 'tls_client_auth', + 'client_secret_jwt', + ], + revocation_endpoint_auth_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'HS256', + 'HS512', + 'ES256', + 'RS256', + 'HS384', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + backchannel_logout_supported: true, + backchannel_logout_session_supported: true, + device_authorization_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth/device', + backchannel_token_delivery_modes_supported: ['poll', 'ping'], + backchannel_authentication_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/ciba/auth', + backchannel_authentication_request_signing_alg_values_supported: [ + 'PS384', + 'RS384', + 'EdDSA', + 'ES384', + 'ES256', + 'RS256', + 'ES512', + 'PS256', + 'PS512', + 'RS512', + ], + require_pushed_authorization_requests: false, + pushed_authorization_request_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/par/request', + mtls_endpoint_aliases: { + token_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token', + revocation_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/revoke', + introspection_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token/introspect', + device_authorization_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth/device', + registration_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/clients-registrations/openid-connect', + userinfo_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/userinfo', + pushed_authorization_request_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/par/request', + backchannel_authentication_endpoint: + 'https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/ciba/auth', + }, + authorization_response_iss_parameter_supported: true, +}; -const metadata = { "issuer": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm", "authorization_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth", "token_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token", "introspection_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token/introspect", "userinfo_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/userinfo", "end_session_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/logout", "frontchannel_logout_session_supported": true, "frontchannel_logout_supported": true, "jwks_uri": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/certs", "check_session_iframe": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/login-status-iframe.html", "grant_types_supported": ["authorization_code", "implicit", "refresh_token", "password", "client_credentials", "urn:openid:params:grant-type:ciba", "urn:ietf:params:oauth:grant-type:device_code"], "acr_values_supported": ["0", "1"], "response_types_supported": ["code", "none", "id_token", "token", "id_token token", "code id_token", "code token", "code id_token token"], "subject_types_supported": ["public", "pairwise"], "prompt_values_supported": ["none", "login", "consent", "create"], "id_token_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512"], "id_token_encryption_alg_values_supported": ["ECDH-ES+A256KW", "ECDH-ES+A192KW", "ECDH-ES+A128KW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "ECDH-ES"], "id_token_encryption_enc_values_supported": ["A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"], "userinfo_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512", "none"], "userinfo_encryption_alg_values_supported": ["ECDH-ES+A256KW", "ECDH-ES+A192KW", "ECDH-ES+A128KW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "ECDH-ES"], "userinfo_encryption_enc_values_supported": ["A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"], "request_object_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512", "none"], "request_object_encryption_alg_values_supported": ["ECDH-ES+A256KW", "ECDH-ES+A192KW", "ECDH-ES+A128KW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "ECDH-ES"], "request_object_encryption_enc_values_supported": ["A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"], "response_modes_supported": ["query", "fragment", "form_post", "query.jwt", "fragment.jwt", "form_post.jwt", "jwt"], "registration_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/clients-registrations/openid-connect", "token_endpoint_auth_methods_supported": ["private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt"], "token_endpoint_auth_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512"], "introspection_endpoint_auth_methods_supported": ["private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt"], "introspection_endpoint_auth_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512"], "authorization_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512"], "authorization_encryption_alg_values_supported": ["ECDH-ES+A256KW", "ECDH-ES+A192KW", "ECDH-ES+A128KW", "RSA-OAEP", "RSA-OAEP-256", "RSA1_5", "ECDH-ES"], "authorization_encryption_enc_values_supported": ["A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512"], "claims_supported": ["aud", "sub", "iss", "auth_time", "name", "given_name", "family_name", "preferred_username", "email", "acr"], "claim_types_supported": ["normal"], "claims_parameter_supported": true, "scopes_supported": ["openid", "eu.europa.ec.eudi.pid_vc_sd_jwt", "web-origins", "offline_access", "eu.europa.ec.eudi.pid_mso_mdoc", "service_account", "roles", "urn:eudi:ehic:1:dc+sd-jwt", "org.iso.18013.5.1.mDL", "basic"], "request_parameter_supported": true, "request_uri_parameter_supported": true, "require_request_uri_registration": true, "code_challenge_methods_supported": ["plain", "S256"], "tls_client_certificate_bound_access_tokens": true, "dpop_signing_alg_values_supported": ["PS384", "ES384", "RS384", "ES256", "RS256", "ES512", "PS256", "PS512", "RS512"], "revocation_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/revoke", "revocation_endpoint_auth_methods_supported": ["private_key_jwt", "client_secret_basic", "client_secret_post", "tls_client_auth", "client_secret_jwt"], "revocation_endpoint_auth_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "HS256", "HS512", "ES256", "RS256", "HS384", "ES512", "PS256", "PS512", "RS512"], "backchannel_logout_supported": true, "backchannel_logout_session_supported": true, "device_authorization_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth/device", "backchannel_token_delivery_modes_supported": ["poll", "ping"], "backchannel_authentication_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/ciba/auth", "backchannel_authentication_request_signing_alg_values_supported": ["PS384", "RS384", "EdDSA", "ES384", "ES256", "RS256", "ES512", "PS256", "PS512", "RS512"], "require_pushed_authorization_requests": false, "pushed_authorization_request_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/par/request", "mtls_endpoint_aliases": { "token_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token", "revocation_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/revoke", "introspection_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/token/introspect", "device_authorization_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/auth/device", "registration_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/clients-registrations/openid-connect", "userinfo_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/userinfo", "pushed_authorization_request_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/par/request", "backchannel_authentication_endpoint": "https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm/protocol/openid-connect/ext/ciba/auth" }, "authorization_response_iss_parameter_supported": true }; - - -describe("OpenidCredentialIssuerMetadataSchemaEudiw", () => { +describe('OpenidCredentialIssuerMetadataSchemaEudiw', () => { it("should successfully parse Eudiw issuer's metadata", async () => { const res = OpenidAuthorizationServerMetadataSchema.safeParse(metadata); if (res.error) { console.dir(res.error, { depth: null }); } assert(res.success === true); - }) -}) + }); +}); diff --git a/src/schemas/OpenidCredentialIssuerMetadataSchema.ts b/src/schemas/OpenidCredentialIssuerMetadataSchema.ts index 4b43bad..fea6947 100644 --- a/src/schemas/OpenidCredentialIssuerMetadataSchema.ts +++ b/src/schemas/OpenidCredentialIssuerMetadataSchema.ts @@ -5,37 +5,49 @@ export const OpenidCredentialIssuerMetadataSchema = z.object({ credential_issuer: z.string(), credential_endpoint: z.string(), nonce_endpoint: z.string().optional(), - credential_request_encryption: z.object({ - jwks: z.object({ - keys: z.array(z.object({ kid: z.string(), }).passthrough()).min(1), - }), - enc_values_supported: z.array(z.string()).min(1), - zip_values_supported: z.array(z.string()).min(1).optional(), - encryption_required: z.boolean(), - }).optional(), - credential_response_encryption: z.object({ - alg_values_supported: z.array(z.string()).min(1), - enc_values_supported: z.array(z.string()).min(1), - zip_values_supported: z.array(z.string().min(1)).optional(), - encryption_required: z.boolean(), - }).optional(), + credential_request_encryption: z + .object({ + jwks: z.object({ + keys: z.array(z.object({ kid: z.string() }).passthrough()).min(1), + }), + enc_values_supported: z.array(z.string()).min(1), + zip_values_supported: z.array(z.string()).min(1).optional(), + encryption_required: z.boolean(), + }) + .optional(), + credential_response_encryption: z + .object({ + alg_values_supported: z.array(z.string()).min(1), + enc_values_supported: z.array(z.string()).min(1), + zip_values_supported: z.array(z.string().min(1)).optional(), + encryption_required: z.boolean(), + }) + .optional(), authorization_servers: z.array(z.string()).optional(), - display: z.array(z.object({ - name: z.string().optional(), - locale: z.string().optional(), - logo: z.object({ - uri: z.string(), - alt_text: z.string().optional(), - }).optional(), - })).optional(), - batch_credential_issuance: z.object({ - batch_size: z.number(), - }).optional(), + display: z + .array( + z.object({ + name: z.string().optional(), + locale: z.string().optional(), + logo: z + .object({ + uri: z.string(), + alt_text: z.string().optional(), + }) + .optional(), + }), + ) + .optional(), + batch_credential_issuance: z + .object({ + batch_size: z.number(), + }) + .optional(), deferred_credential_endpoint: z.string().optional(), credential_configurations_supported: z.record(CredentialConfigurationSupportedSchema), - signed_metadata: z.string().optional(), // mdoc-specific property - mdoc_iacas_uri: z.string().optional(), // mdoc-specific property + signed_metadata: z.string().optional(), // mdoc-specific property + mdoc_iacas_uri: z.string().optional(), // mdoc-specific property notification_endpoint: z.string().optional(), -}) +}); export type OpenidCredentialIssuerMetadata = z.infer; diff --git a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest.test.ts b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest.test.ts index 1e850b8..d2bb10d 100644 --- a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest.test.ts +++ b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest.test.ts @@ -2,16 +2,267 @@ import { describe, it } from 'vitest'; import { OpenidCredentialIssuerMetadataSchema } from './OpenidCredentialIssuerMetadataSchema'; import assert from 'assert'; +const metadata = { + credential_issuer: 'https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291', + credential_endpoint: + 'https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/credential', + deferred_credential_endpoint: + 'https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/deferred-credential', + nonce_endpoint: 'https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/nonce', + batch_credential_issuance: { batch_size: 10 }, + display: [ + { + name: 'Bürgeramt Köln', + logo: { + uri: 'https://playground.animo.id/assets/issuers/koln/issuer.png', + url: 'https://playground.animo.id/assets/issuers/koln/issuer.png', + }, + }, + ], + credential_configurations_supported: { + 'certificate-of-residence-sd-jwt': { + format: 'dc+sd-jwt', + scope: 'certificate-of-residence-sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + proof_types_supported: { jwt: { proof_signing_alg_values_supported: ['ES256'] } }, + credential_metadata: { + display: [ + { + name: 'Certificate of Residence', + locale: 'en', + background_color: '#E4DEDD', + background_image: { + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + text_color: '#525C75', + }, + ], + }, + vct: 'https://example.eudi.ec.europa.eu/cor/1', + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + background_image: { + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + }, + ], + }, + 'certificate-of-residence-sd-jwt-key-attestations': { + format: 'dc+sd-jwt', + scope: 'certificate-of-residence-sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + proof_types_supported: { + jwt: { + proof_signing_alg_values_supported: ['ES256'], + key_attestations_required: { + key_storage: ['iso_18045_high'], + user_authentication: ['iso_18045_high'], + }, + }, + attestation: { + proof_signing_alg_values_supported: ['ES256'], + key_attestations_required: { + key_storage: ['iso_18045_high'], + user_authentication: ['iso_18045_high'], + }, + }, + }, + credential_metadata: { + display: [ + { + name: 'Certificate of Residence', + locale: 'en', + background_color: '#E4DEDD', + background_image: { + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + text_color: '#525C75', + }, + ], + }, + vct: 'https://example.eudi.ec.europa.eu/cor/1', + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + background_image: { + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + }, + ], + }, + 'certificate-of-residence-mdoc': { + format: 'mso_mdoc', + scope: 'certificate-of-residence-mdoc', + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + proof_types_supported: { jwt: { proof_signing_alg_values_supported: ['ES256'] } }, + credential_metadata: { + display: [ + { + name: 'Certificate of Residence', + locale: 'en', + background_color: '#E4DEDD', + background_image: { + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + text_color: '#525C75', + }, + ], + }, + doctype: 'eu.europa.ec.eudi.cor.1', + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + background_image: { + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + }, + ], + }, + 'certificate-of-residence-mdoc-key-attestations': { + format: 'mso_mdoc', + scope: 'certificate-of-residence-mdoc', + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + proof_types_supported: { + jwt: { + proof_signing_alg_values_supported: ['ES256'], + key_attestations_required: { + key_storage: ['iso_18045_high'], + user_authentication: ['iso_18045_high'], + }, + }, + attestation: { + proof_signing_alg_values_supported: ['ES256'], + key_attestations_required: { + key_storage: ['iso_18045_high'], + user_authentication: ['iso_18045_high'], + }, + }, + }, + credential_metadata: { + display: [ + { + name: 'Certificate of Residence', + locale: 'en', + background_color: '#E4DEDD', + background_image: { + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + text_color: '#525C75', + }, + ], + }, + doctype: 'eu.europa.ec.eudi.cor.1', + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + background_image: { + url: 'https://playground.animo.id/assets/issuers/koln/credential.png', + uri: 'https://playground.animo.id/assets/issuers/koln/credential.png', + }, + }, + ], + }, + }, + credentials_supported: [ + { + format: 'vc+sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + vct: 'https://example.eudi.ec.europa.eu/cor/1', + cryptographic_suites_supported: ['ES256'], + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + logo: { url: 'https://playground.animo.id/assets/issuers/koln/credential.png' }, + }, + ], + id: 'certificate-of-residence-sd-jwt', + }, + { + format: 'vc+sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + vct: 'https://example.eudi.ec.europa.eu/cor/1', + cryptographic_suites_supported: ['ES256'], + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + logo: { url: 'https://playground.animo.id/assets/issuers/koln/credential.png' }, + }, + ], + id: 'certificate-of-residence-sd-jwt-key-attestations', + }, + { + format: 'mso_mdoc', + cryptographic_binding_methods_supported: ['cose_key'], + doctype: 'eu.europa.ec.eudi.cor.1', + cryptographic_suites_supported: ['ES256'], + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + logo: { url: 'https://playground.animo.id/assets/issuers/koln/credential.png' }, + }, + ], + id: 'certificate-of-residence-mdoc', + }, + { + format: 'mso_mdoc', + cryptographic_binding_methods_supported: ['cose_key'], + doctype: 'eu.europa.ec.eudi.cor.1', + cryptographic_suites_supported: ['ES256'], + display: [ + { + locale: 'en', + name: 'Certificate of Residence', + text_color: '#525C75', + background_color: '#E4DEDD', + logo: { url: 'https://playground.animo.id/assets/issuers/koln/credential.png' }, + }, + ], + id: 'certificate-of-residence-mdoc-key-attestations', + }, + ], + token_endpoint: 'https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/token', + dpop_signing_alg_values_supported: ['ES256'], +}; - -const metadata = {"credential_issuer":"https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291","credential_endpoint":"https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/credential","deferred_credential_endpoint":"https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/deferred-credential","nonce_endpoint":"https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/nonce","batch_credential_issuance":{"batch_size":10},"display":[{"name":"Bürgeramt Köln","logo":{"uri":"https://playground.animo.id/assets/issuers/koln/issuer.png","url":"https://playground.animo.id/assets/issuers/koln/issuer.png"}}],"credential_configurations_supported":{"certificate-of-residence-sd-jwt":{"format":"dc+sd-jwt","scope":"certificate-of-residence-sd-jwt","cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]}},"credential_metadata":{"display":[{"name":"Certificate of Residence","locale":"en","background_color":"#E4DEDD","background_image":{"uri":"https://playground.animo.id/assets/issuers/koln/credential.png","url":"https://playground.animo.id/assets/issuers/koln/credential.png"},"text_color":"#525C75"}]},"vct":"https://example.eudi.ec.europa.eu/cor/1","display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","background_image":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png","uri":"https://playground.animo.id/assets/issuers/koln/credential.png"}}]},"certificate-of-residence-sd-jwt-key-attestations":{"format":"dc+sd-jwt","scope":"certificate-of-residence-sd-jwt","cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"],"key_attestations_required":{"key_storage":["iso_18045_high"],"user_authentication":["iso_18045_high"]}},"attestation":{"proof_signing_alg_values_supported":["ES256"],"key_attestations_required":{"key_storage":["iso_18045_high"],"user_authentication":["iso_18045_high"]}}},"credential_metadata":{"display":[{"name":"Certificate of Residence","locale":"en","background_color":"#E4DEDD","background_image":{"uri":"https://playground.animo.id/assets/issuers/koln/credential.png","url":"https://playground.animo.id/assets/issuers/koln/credential.png"},"text_color":"#525C75"}]},"vct":"https://example.eudi.ec.europa.eu/cor/1","display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","background_image":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png","uri":"https://playground.animo.id/assets/issuers/koln/credential.png"}}]},"certificate-of-residence-mdoc":{"format":"mso_mdoc","scope":"certificate-of-residence-mdoc","cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]}},"credential_metadata":{"display":[{"name":"Certificate of Residence","locale":"en","background_color":"#E4DEDD","background_image":{"uri":"https://playground.animo.id/assets/issuers/koln/credential.png","url":"https://playground.animo.id/assets/issuers/koln/credential.png"},"text_color":"#525C75"}]},"doctype":"eu.europa.ec.eudi.cor.1","display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","background_image":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png","uri":"https://playground.animo.id/assets/issuers/koln/credential.png"}}]},"certificate-of-residence-mdoc-key-attestations":{"format":"mso_mdoc","scope":"certificate-of-residence-mdoc","cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"],"key_attestations_required":{"key_storage":["iso_18045_high"],"user_authentication":["iso_18045_high"]}},"attestation":{"proof_signing_alg_values_supported":["ES256"],"key_attestations_required":{"key_storage":["iso_18045_high"],"user_authentication":["iso_18045_high"]}}},"credential_metadata":{"display":[{"name":"Certificate of Residence","locale":"en","background_color":"#E4DEDD","background_image":{"uri":"https://playground.animo.id/assets/issuers/koln/credential.png","url":"https://playground.animo.id/assets/issuers/koln/credential.png"},"text_color":"#525C75"}]},"doctype":"eu.europa.ec.eudi.cor.1","display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","background_image":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png","uri":"https://playground.animo.id/assets/issuers/koln/credential.png"}}]}},"credentials_supported":[{"format":"vc+sd-jwt","cryptographic_binding_methods_supported":["jwk"],"vct":"https://example.eudi.ec.europa.eu/cor/1","cryptographic_suites_supported":["ES256"],"display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","logo":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png"}}],"id":"certificate-of-residence-sd-jwt"},{"format":"vc+sd-jwt","cryptographic_binding_methods_supported":["jwk"],"vct":"https://example.eudi.ec.europa.eu/cor/1","cryptographic_suites_supported":["ES256"],"display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","logo":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png"}}],"id":"certificate-of-residence-sd-jwt-key-attestations"},{"format":"mso_mdoc","cryptographic_binding_methods_supported":["cose_key"],"doctype":"eu.europa.ec.eudi.cor.1","cryptographic_suites_supported":["ES256"],"display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","logo":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png"}}],"id":"certificate-of-residence-mdoc"},{"format":"mso_mdoc","cryptographic_binding_methods_supported":["cose_key"],"doctype":"eu.europa.ec.eudi.cor.1","cryptographic_suites_supported":["ES256"],"display":[{"locale":"en","name":"Certificate of Residence","text_color":"#525C75","background_color":"#E4DEDD","logo":{"url":"https://playground.animo.id/assets/issuers/koln/credential.png"}}],"id":"certificate-of-residence-mdoc-key-attestations"}],"token_endpoint":"https://playground.animo.id/oid4vci/832f1c72-817d-4a54-b0fc-9994ecaba291/token","dpop_signing_alg_values_supported":["ES256"]} - -describe("OpenidCredentialIssuerMetadataSchemaEudiw", () => { +describe('OpenidCredentialIssuerMetadataSchemaEudiw', () => { it("should successfully parse Animo issuer's metadata", async () => { const res = OpenidCredentialIssuerMetadataSchema.safeParse(metadata); if (res.error) { console.dir(res.error, { depth: null }); } assert(res.success === true); - }) -}) + }); +}); diff --git a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest2.test.ts b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest2.test.ts index a22b0f7..6ec009d 100644 --- a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest2.test.ts +++ b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest2.test.ts @@ -2,14 +2,734 @@ import { describe, it } from 'vitest'; import { OpenidCredentialIssuerMetadataSchema } from './OpenidCredentialIssuerMetadataSchema'; import assert from 'assert'; -const metadata = {"credential_issuer":"https://dev.issuer-backend.eudiw.dev","authorization_servers":["https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm"],"credential_endpoint":"https://dev.issuer-backend.eudiw.dev/wallet/credentialEndpoint","deferred_credential_endpoint":"https://dev.issuer-backend.eudiw.dev/wallet/deferredEndpoint","notification_endpoint":"https://dev.issuer-backend.eudiw.dev/wallet/notificationEndpoint","nonce_endpoint":"https://dev.issuer-backend.eudiw.dev/wallet/nonceEndpoint","credential_response_encryption":{"alg_values_supported":["RSA-OAEP-256","ECDH-ES"],"enc_values_supported":["A128CBC-HS256"],"encryption_required":true},"batch_credential_issuance":{"batch_size":15},"signed_metadata":"eyJ4NWMiOlsiTUlJQzZ6Q0NBcEdnQXdJQkFnSVViWDhuYllTTFJ2eTEwbUtOK2hmQ1ZyLzhjQmN3Q2dZSUtvWkl6ajBFQXdJd1hERWVNQndHQTFVRUF3d1ZVRWxFSUVsemMzVmxjaUJEUVNBdElGVlVJREF5TVMwd0t3WURWUVFLRENSRlZVUkpJRmRoYkd4bGRDQlNaV1psY21WdVkyVWdTVzF3YkdWdFpXNTBZWFJwYjI0eEN6QUpCZ05WQkFZVEFsVlVNQjRYRFRJMU1EUXhNREUwTWpVME1Gb1hEVEkyTURjd05ERTBNalV6T1Zvd1VqRVVNQklHQTFVRUF3d0xVRWxFSUVSVElDMGdNRE14TFRBckJnTlZCQW9NSkVWVlJFa2dWMkZzYkdWMElGSmxabVZ5Wlc1alpTQkpiWEJzWlcxbGJuUmhkR2x2YmpFTE1Ba0dBMVVFQmhNQ1ZWUXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBU3J4WjEzd0xqL25VdUdlYllSbVBPMHE3cFJrMXgxU2pycUxUdnRRRnBRY3k5VHdGQ2NnaWUvQkJDMmovS3BMY0NyK29qNHR5WkFvZm12SGRhVEV4YkJvNElCT1RDQ0FUVXdId1lEVlIwakJCZ3dGb0FVWXNlVVJ5aTlENklXSUtlYXdrbVVSUEVCMDhjd0p3WURWUjBSQkNBd0hvSWNaR1YyTG1semMzVmxjaTFpWVdOclpXNWtMbVYxWkdsM0xtUmxkakFXQmdOVkhTVUJBZjhFRERBS0JnZ3JnUUlDQUFBQkFqQkRCZ05WSFI4RVBEQTZNRGlnTnFBMGhqSm9kSFJ3Y3pvdkwzQnlaWEJ5YjJRdWNHdHBMbVYxWkdsM0xtUmxkaTlqY213dmNHbGtYME5CWDFWVVh6QXlMbU55YkRBZEJnTlZIUTRFRmdRVWNzM0t5cWl6SGd0WFJlMzJuNkpCSkhBZmFMWXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1GMEdBMVVkRWdSV01GU0dVbWgwZEhCek9pOHZaMmwwYUhWaUxtTnZiUzlsZFMxa2FXZHBkR0ZzTFdsa1pXNTBhWFI1TFhkaGJHeGxkQzloY21Ob2FYUmxZM1IxY21VdFlXNWtMWEpsWm1WeVpXNWpaUzFtY21GdFpYZHZjbXN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVFZabmNoRCtRanE1M1hzMG9jMDd5M3pHNmtBWEZrSitaS3psVkcyMnNDOENJUUR0RE1RcTBRbS9mUTVvcnJqUlQ0WEIrMEpiNnhGUHhYOVFrVlJhTXkvSWlBPT0iXSwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJodHRwczovL2Rldi5pc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYiLCJkaXNwbGF5IjpbeyJuYW1lIjoiRGlnaXRhbCBDcmVkZW50aWFscyBJc3N1ZXIiLCJsb2NhbGUiOiJlbiIsImxvZ28iOnsidXJpIjoiaHR0cHM6Ly9ldWRpdy5kZXYvaWMtbG9nby5zdmciLCJhbHRfdGV4dCI6IkVVIERpZ2l0YWwgSWRlbnRpdHkgV2FsbGV0IExvZ28ifX0seyJuYW1lIjoizpXOus60z4zPhM63z4IgzqjOt8-GzrnOsc66z47OvSDOlM65zrHPgM65z4PPhM61z4XPhM63z4HOr8-Jzr0iLCJsb2NhbGUiOiJlbCIsImxvZ28iOnsidXJpIjoiaHR0cHM6Ly9ldWRpdy5kZXYvaWMtbG9nby5zdmciLCJhbHRfdGV4dCI6Is6bzr_Os8-Mz4TPhc-Azr8gzpXPhc-Bz4nPgM6xz4rOus6_z40gzqDOv8-Bz4TOv8-Gzr_Ou865zr_PjSDOqM63z4bOuc6xzrrOrs-CIM6kzrHPhc-Ez4zPhM63z4TOsc-CIn19XSwiaXNzIjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2IiwiYmF0Y2hfY3JlZGVudGlhbF9pc3N1YW5jZSI6eyJiYXRjaF9zaXplIjoxNX0sImNyZWRlbnRpYWxfY29uZmlndXJhdGlvbnNfc3VwcG9ydGVkIjp7ImV1LmV1cm9wYS5lYy5ldWRpLnBpZF9tc29fbWRvYyI6eyJmb3JtYXQiOiJtc29fbWRvYyIsInNjb3BlIjoiZXUuZXVyb3BhLmVjLmV1ZGkucGlkX21zb19tZG9jIiwicHJvb2ZfdHlwZXNfc3VwcG9ydGVkIjp7Imp3dCI6eyJwcm9vZl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2Il19fSwiZG9jdHlwZSI6ImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZGlzcGxheSI6W3sibmFtZSI6IlBJRCIsImxvY2FsZSI6ImVuIiwibG9nbyI6eyJ1cmkiOiJodHRwczovL2V4YW1wbGVzdGF0ZS5jb20vcHVibGljL21kbC5wbmciLCJhbHRfdGV4dCI6IkEgc3F1YXJlIGZpZ3VyZSBvZiBhIFBJRCJ9fV0sInBvbGljeSI6eyJvbmVfdGltZV91c2UiOnRydWUsImJhdGNoX3NpemUiOjE1fSwiY2xhaW1zIjpbeyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZmFtaWx5X25hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRmFtaWx5IE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZ2l2ZW5fbmFtZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImJpcnRoX2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJiaXJ0aF9wbGFjZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBQbGFjZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJuYXRpb25hbGl0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJyZXNpZGVudF9hZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2NvdW50cnkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IENvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfc3RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IFN0YXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2NpdHkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IENpdHkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfcG9zdGFsX2NvZGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IFBvc3RhbCBDb2RlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X3N0cmVldCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgU3RyZWV0IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2hvdXNlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgSG91c2UgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUGVyc29uYWwgQWRtaW5pc3RyYXRpdmUgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInBvcnRyYWl0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJQb3J0cmFpdCBJbWFnZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJmYW1pbHlfbmFtZV9iaXJ0aCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggRmFtaWx5IE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZ2l2ZW5fbmFtZV9iaXJ0aCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggR2l2ZW4gTmFtZShzKSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJzZXgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlNleCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJlbWFpbF9hZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFbWFpbCBBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsIm1vYmlsZV9waG9uZV9udW1iZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Ik1vYmlsZSBQaG9uZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZXhwaXJ5X2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiaXNzdWluZ19hdXRob3JpdHkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWFuY2UgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImlzc3VpbmdfY291bnRyeSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIENvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZG9jdW1lbnRfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImlzc3VhbmNlX2RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VhbmNlIERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiYWdlX292ZXJfMTgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBPdmVyIDE4IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImFnZV9pbl95ZWFycyJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIGluIFllYXJzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImFnZV9iaXJ0aF95ZWFyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgWWVhciBvZiBCaXJ0aCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJ0cnVzdF9hbmNob3IiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlRydXN0IEFuY2hvciIsImxvY2FsZSI6ImVuIn1dfV19LCJldS5ldXJvcGEuZWMuZXVkaS5waWRfdmNfc2Rfand0Ijp7ImZvcm1hdCI6ImRjK3NkLWp3dCIsInNjb3BlIjoiZXUuZXVyb3BhLmVjLmV1ZGkucGlkX3ZjX3NkX2p3dCIsImNyeXB0b2dyYXBoaWNfYmluZGluZ19tZXRob2RzX3N1cHBvcnRlZCI6WyJqd2siXSwiY3JlZGVudGlhbF9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2Il0sInByb29mX3R5cGVzX3N1cHBvcnRlZCI6eyJqd3QiOnsicHJvb2Zfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJSUzI1NiIsIkVTMjU2Il19fSwiZGlzcGxheSI6W3sibmFtZSI6IlBJRCIsImxvY2FsZSI6ImVuIiwibG9nbyI6eyJ1cmkiOiJodHRwczovL2V4YW1wbGVzdGF0ZS5jb20vcHVibGljL21kbC5wbmciLCJhbHRfdGV4dCI6IkEgc3F1YXJlIGZpZ3VyZSBvZiBhIFBJRCJ9fV0sInZjdCI6InVybjpldWRpOnBpZDoxIiwiY2xhaW1zIjpbeyJwYXRoIjpbImZhbWlseV9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJnaXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkdpdmVuIE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImJpcnRoZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJwbGFjZV9vZl9iaXJ0aCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBQbGFjZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJsb2NhbGl0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJMb2NhbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJyZWdpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlZ2lvbiIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJjb3VudHJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJuYXRpb25hbGl0aWVzIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Ik5hdGlvbmFsaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwiaG91c2VfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJIb3VzZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJzdHJlZXRfYWRkcmVzcyJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiU3RyZWV0IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwibG9jYWxpdHkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkxvY2FsaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwicmVnaW9uIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZWdpb24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJwb3N0YWxfY29kZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUG9zdGFsIENvZGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJjb3VudHJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwiZm9ybWF0dGVkIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJGdWxsIEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUGVyc29uYWwgQWRtaW5pc3RyYXRpdmUgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJwaWN0dXJlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJQb3J0cmFpdCBJbWFnZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYmlydGhfZmFtaWx5X25hbWUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkJpcnRoIEZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJiaXJ0aF9naXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJzZXgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlNleCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZW1haWwiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkVtYWlsIEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInBob25lX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiTW9iaWxlIFBob25lIE51bWJlciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZGF0ZV9vZl9leHBpcnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2NvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkb2N1bWVudF9udW1iZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkRvY3VtZW50IE51bWJlciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkYXRlX29mX2lzc3VhbmNlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1YW5jZSBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZ2VfZXF1YWxfb3Jfb3ZlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIEVxdWFsIG9yIE92ZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFnZV9lcXVhbF9vcl9vdmVyIiwiMTgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBPdmVyIDE4IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZ2VfaW5feWVhcnMiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBpbiBZZWFycyIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYWdlX2JpcnRoX3llYXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBZZWFyIG9mIEJpcnRoIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJ0cnVzdF9hbmNob3IiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlRydXN0IEFuY2hvciIsImxvY2FsZSI6ImVuIn1dfV19LCJvcmcuaXNvLjE4MDEzLjUuMS5tREwiOnsiZm9ybWF0IjoibXNvX21kb2MiLCJzY29wZSI6Im9yZy5pc28uMTgwMTMuNS4xLm1ETCIsInByb29mX3R5cGVzX3N1cHBvcnRlZCI6eyJqd3QiOnsicHJvb2Zfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFUzI1NiJdfX0sImRvY3R5cGUiOiJvcmcuaXNvLjE4MDEzLjUuMS5tREwiLCJkaXNwbGF5IjpbeyJuYW1lIjoiTW9iaWxlIERyaXZpbmcgTGljZW5jZSIsImxvY2FsZSI6ImVuIn1dLCJwb2xpY3kiOnsib25lX3RpbWVfdXNlIjpmYWxzZSwiYmF0Y2hfc2l6ZSI6MTV9LCJjbGFpbXMiOlt7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJmYW1pbHlfbmFtZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJGYW1pbHkgTmFtZShzKSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJnaXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkdpdmVuIE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYmlydGhfZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImlzc3VlX2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWFuY2UgRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJleHBpcnlfZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJFeHBpcnkgRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJpc3N1aW5nX2NvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImRvY3VtZW50X251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicG9ydHJhaXQiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUG9ydHJhaXQgSW1hZ2UiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiZHJpdmluZ19wcml2aWxlZ2VzIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRyaXZpbmcgUHJpdmlsZWdlcyIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJ1bl9kaXN0aW5ndWlzaGluZ19zaWduIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRpc3Rpbmd1aXNoaW5nIFNpZ24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWRtaW5pc3RyYXRpdmVfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZG1pbmlzdHJhdGl2ZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwic2V4Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJTZXgiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiaGVpZ2h0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJIZWlnaHQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwid2VpZ2h0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJXZWlnaHQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiZXllX2NvbG91ciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXllIENvbG91ciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJoYWlyX2NvbG91ciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSGFpciBDb2xvdXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYmlydGhfcGxhY2UiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkJpcnRoIFBsYWNlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInJlc2lkZW50X2FkZHJlc3MiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicG9ydHJhaXRfY2FwdHVyZV9kYXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDYXB0dXJlIERhdGUgb2YgUG9ydHJhaXQgSW1hZ2UiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWdlX2luX3llYXJzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgaW4gWWVhcnMiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWdlX2JpcnRoX3llYXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBZZWFyIG9mIEJpcnRoIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImFnZV9vdmVyXzE4Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgT3ZlciAxOCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJhZ2Vfb3Zlcl8yMSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIE92ZXIgMjEiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsIm5hdGlvbmFsaXR5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJyZXNpZGVudF9jaXR5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBDaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInJlc2lkZW50X3N0YXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBTdGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJyZXNpZGVudF9wb3N0YWxfY29kZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgUG9zdGFsIENvZGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicmVzaWRlbnRfY291bnRyeSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgQ291bnRyeSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJmYW1pbHlfbmFtZV9uYXRpb25hbF9jaGFyYWN0ZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Ik5hdGlvbmFsIEZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImdpdmVuX25hbWVfbmF0aW9uYWxfY2hhcmFjdGVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbCBHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInNpZ25hdHVyZV91c3VhbF9tYXJrIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJTaWduYXR1cmUiLCJsb2NhbGUiOiJlbiJ9XX1dfSwidXJuOmV1ZGk6ZWhpYzoxOmRjK3NkLWp3dC1qd3MtanNvbiI6eyJmb3JtYXQiOiJkYytzZC1qd3QiLCJzY29wZSI6InVybjpldWRpOmVoaWM6MTpkYytzZC1qd3QiLCJjcnlwdG9ncmFwaGljX2JpbmRpbmdfbWV0aG9kc19zdXBwb3J0ZWQiOlsiandrIl0sImNyZWRlbnRpYWxfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFUzI1NiJdLCJwcm9vZl90eXBlc19zdXBwb3J0ZWQiOnsiand0Ijp7InByb29mX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRVMyNTYiLCJFUzM4NCIsIkVTNTEyIiwiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIl19fSwiZGlzcGxheSI6W3sibmFtZSI6IkV1cm9wZWFuIEhlYWx0aCBJbnN1cmFuY2UgQ2FyZCIsImxvY2FsZSI6ImVuIn1dLCJ2Y3QiOiJ1cm46ZXVkaTplaGljOjEiLCJjbGFpbXMiOlt7InBhdGgiOlsicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IlNvY2lhbCBTZWN1cml0eSBQSU4iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSIsImlkIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IGlkIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSIsIm5hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBhdXRob3JpdHkgbmFtZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiY291bnRyeSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGNvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRhdGVfb2ZfZXhwaXJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFeHBpcnkgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZGF0ZV9vZl9pc3N1YW5jZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1ZSBkYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhdXRoZW50aWNfc291cmNlIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSIsImlkIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiBpZCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSIsIm5hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQ29tcGV0ZW50IGluc3RpdHV0aW9uIG5hbWUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImVuZGluZ19kYXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFbmRpbmcgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsic3RhcnRpbmdfZGF0ZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiU3RhcnRpbmcgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZG9jdW1lbnRfbnVtYmVyIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRvY3VtZW50IG51bWJlciIsImxvY2FsZSI6ImVuIn1dfV19LCJ1cm46ZXVkaTplaGljOjE6ZGMrc2Qtand0LWNvbXBhY3QiOnsiZm9ybWF0IjoiZGMrc2Qtand0Iiwic2NvcGUiOiJ1cm46ZXVkaTplaGljOjE6ZGMrc2Qtand0IiwiY3J5cHRvZ3JhcGhpY19iaW5kaW5nX21ldGhvZHNfc3VwcG9ydGVkIjpbImp3ayJdLCJjcmVkZW50aWFsX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRVMyNTYiXSwicHJvb2ZfdHlwZXNfc3VwcG9ydGVkIjp7Imp3dCI6eyJwcm9vZl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2IiwiRVMzODQiLCJFUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiJdfX0sImRpc3BsYXkiOlt7Im5hbWUiOiJFdXJvcGVhbiBIZWFsdGggSW5zdXJhbmNlIENhcmQiLCJsb2NhbGUiOiJlbiJ9XSwidmN0IjoidXJuOmV1ZGk6ZWhpYzoxIiwiY2xhaW1zIjpbeyJwYXRoIjpbInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJTb2NpYWwgU2VjdXJpdHkgUElOIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGF1dGhvcml0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19hdXRob3JpdHkiLCJpZCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGF1dGhvcml0eSBpZCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19hdXRob3JpdHkiLCJuYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IG5hbWUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImNvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBjb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkYXRlX29mX2V4cGlyeSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRhdGVfb2ZfaXNzdWFuY2UiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWUgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJDb21wZXRlbnQgaW5zdGl0dXRpb24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImF1dGhlbnRpY19zb3VyY2UiLCJpZCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJDb21wZXRlbnQgaW5zdGl0dXRpb24gaWQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImF1dGhlbnRpY19zb3VyY2UiLCJuYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiBuYW1lIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJlbmRpbmdfZGF0ZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRW5kaW5nIGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInN0YXJ0aW5nX2RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlN0YXJ0aW5nIGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRvY3VtZW50X251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBudW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX1dfX0sIm5vbmNlX2VuZHBvaW50IjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2L3dhbGxldC9ub25jZUVuZHBvaW50Iiwib3BlbmlkNHZjaV92ZXJzaW9uIjoiZHJhZnQgMTUiLCJjcmVkZW50aWFsX2lzc3VlciI6Imh0dHBzOi8vZGV2Lmlzc3Vlci1iYWNrZW5kLmV1ZGl3LmRldiIsImNyZWRlbnRpYWxfcmVzcG9uc2VfZW5jcnlwdGlvbiI6eyJhbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJSU0EtT0FFUC0yNTYiLCJFQ0RILUVTIl0sImVuY192YWx1ZXNfc3VwcG9ydGVkIjpbIkExMjhDQkMtSFMyNTYiXSwiZW5jcnlwdGlvbl9yZXF1aXJlZCI6dHJ1ZX0sIm5vdGlmaWNhdGlvbl9lbmRwb2ludCI6Imh0dHBzOi8vZGV2Lmlzc3Vlci1iYWNrZW5kLmV1ZGl3LmRldi93YWxsZXQvbm90aWZpY2F0aW9uRW5kcG9pbnQiLCJpYXQiOjE3NTEwMjI4ODYsImRlZmVycmVkX2NyZWRlbnRpYWxfZW5kcG9pbnQiOiJodHRwczovL2Rldi5pc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYvd2FsbGV0L2RlZmVycmVkRW5kcG9pbnQiLCJhdXRob3JpemF0aW9uX3NlcnZlcnMiOlsiaHR0cHM6Ly9kZXYuYXV0aGVudGljYXRlLmV1ZGl3LmRldi9yZWFsbXMvcGlkLWlzc3Vlci1yZWFsbSJdLCJjcmVkZW50aWFsX2VuZHBvaW50IjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2L3dhbGxldC9jcmVkZW50aWFsRW5kcG9pbnQifQ.vV3Zqv8lPyV2mW8PwAfNY0ASRIaqjGWZCQ3JW-JWar50P_jaPZDC6Vf0rogX0NmPUC5zDES1zoam2znqEAN5AA","display":[{"name":"Digital Credentials Issuer","locale":"en","logo":{"uri":"https://eudiw.dev/ic-logo.svg","alt_text":"EU Digital Identity Wallet Logo"}},{"name":"Εκδότης Ψηφιακών Διαπιστευτηρίων","locale":"el","logo":{"uri":"https://eudiw.dev/ic-logo.svg","alt_text":"Λογότυπο Ευρωπαϊκού Πορτοφολιού Ψηφιακής Ταυτότητας"}}],"credential_configurations_supported":{"eu.europa.ec.eudi.pid_mso_mdoc":{"format":"mso_mdoc","scope":"eu.europa.ec.eudi.pid_mso_mdoc","proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]}},"doctype":"eu.europa.ec.eudi.pid.1","display":[{"name":"PID","locale":"en","logo":{"uri":"https://examplestate.com/public/mdl.png","alt_text":"A square figure of a PID"}}],"policy":{"one_time_use":true,"batch_size":15},"claims":[{"path":["eu.europa.ec.eudi.pid.1","family_name"],"mandatory":true,"display":[{"name":"Family Name(s)","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","given_name"],"mandatory":true,"display":[{"name":"Given Name(s)","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","birth_date"],"mandatory":true,"display":[{"name":"Birth Date","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","birth_place"],"mandatory":true,"display":[{"name":"Birth Place","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","nationality"],"mandatory":true,"display":[{"name":"Nationality","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_address"],"mandatory":false,"display":[{"name":"Resident Address","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_country"],"mandatory":false,"display":[{"name":"Resident Country","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_state"],"mandatory":false,"display":[{"name":"Resident State","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_city"],"mandatory":false,"display":[{"name":"Resident City","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_postal_code"],"mandatory":false,"display":[{"name":"Resident Postal Code","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_street"],"mandatory":false,"display":[{"name":"Resident Street","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","resident_house_number"],"mandatory":false,"display":[{"name":"Resident House Number","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","personal_administrative_number"],"mandatory":false,"display":[{"name":"Personal Administrative Number","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","portrait"],"mandatory":false,"display":[{"name":"Portrait Image","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","family_name_birth"],"mandatory":false,"display":[{"name":"Birth Family Name(s)","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","given_name_birth"],"mandatory":false,"display":[{"name":"Birth Given Name(s)","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","sex"],"mandatory":false,"display":[{"name":"Sex","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","email_address"],"mandatory":false,"display":[{"name":"Email Address","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","mobile_phone_number"],"mandatory":false,"display":[{"name":"Mobile Phone Number","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","expiry_date"],"mandatory":true,"display":[{"name":"Expiry Date","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","issuing_authority"],"mandatory":true,"display":[{"name":"Issuance Authority","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","issuing_country"],"mandatory":true,"display":[{"name":"Issuing Country","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","document_number"],"mandatory":false,"display":[{"name":"Document Number","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","issuing_jurisdiction"],"mandatory":false,"display":[{"name":"Issuing Jurisdiction","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","issuance_date"],"mandatory":false,"display":[{"name":"Issuance Date","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","age_over_18"],"mandatory":false,"display":[{"name":"Age Over 18","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","age_in_years"],"mandatory":false,"display":[{"name":"Age in Years","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","age_birth_year"],"mandatory":false,"display":[{"name":"Age Year of Birth","locale":"en"}]},{"path":["eu.europa.ec.eudi.pid.1","trust_anchor"],"mandatory":false,"display":[{"name":"Trust Anchor","locale":"en"}]}]},"eu.europa.ec.eudi.pid_vc_sd_jwt":{"format":"dc+sd-jwt","scope":"eu.europa.ec.eudi.pid_vc_sd_jwt","cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["RS256","ES256"]}},"display":[{"name":"PID","locale":"en","logo":{"uri":"https://examplestate.com/public/mdl.png","alt_text":"A square figure of a PID"}}],"vct":"urn:eudi:pid:1","claims":[{"path":["family_name"],"mandatory":true,"display":[{"name":"Family Name(s)","locale":"en"}]},{"path":["given_name"],"mandatory":true,"display":[{"name":"Given Name(s)","locale":"en"}]},{"path":["birthdate"],"mandatory":true,"display":[{"name":"Birth Date","locale":"en"}]},{"path":["place_of_birth"],"mandatory":true,"display":[{"name":"Birth Place","locale":"en"}]},{"path":["place_of_birth","locality"],"mandatory":true,"display":[{"name":"Locality","locale":"en"}]},{"path":["place_of_birth","region"],"mandatory":false,"display":[{"name":"Region","locale":"en"}]},{"path":["place_of_birth","country"],"mandatory":false,"display":[{"name":"Country","locale":"en"}]},{"path":["nationalities"],"mandatory":true,"display":[{"name":"Nationality","locale":"en"}]},{"path":["address"],"mandatory":false,"display":[{"name":"Address","locale":"en"}]},{"path":["address","house_number"],"mandatory":false,"display":[{"name":"House Number","locale":"en"}]},{"path":["address","street_address"],"mandatory":false,"display":[{"name":"Street","locale":"en"}]},{"path":["address","locality"],"mandatory":false,"display":[{"name":"Locality","locale":"en"}]},{"path":["address","region"],"mandatory":false,"display":[{"name":"Region","locale":"en"}]},{"path":["address","postal_code"],"mandatory":false,"display":[{"name":"Postal Code","locale":"en"}]},{"path":["address","country"],"mandatory":false,"display":[{"name":"Country","locale":"en"}]},{"path":["address","formatted"],"mandatory":false,"display":[{"name":"Full Address","locale":"en"}]},{"path":["personal_administrative_number"],"mandatory":false,"display":[{"name":"Personal Administrative Number","locale":"en"}]},{"path":["picture"],"mandatory":false,"display":[{"name":"Portrait Image","locale":"en"}]},{"path":["birth_family_name"],"mandatory":false,"display":[{"name":"Birth Family Name(s)","locale":"en"}]},{"path":["birth_given_name"],"mandatory":false,"display":[{"name":"Birth Given Name(s)","locale":"en"}]},{"path":["sex"],"mandatory":false,"display":[{"name":"Sex","locale":"en"}]},{"path":["email"],"mandatory":false,"display":[{"name":"Email Address","locale":"en"}]},{"path":["phone_number"],"mandatory":false,"display":[{"name":"Mobile Phone Number","locale":"en"}]},{"path":["date_of_expiry"],"mandatory":true,"display":[{"name":"Expiry Date","locale":"en"}]},{"path":["issuing_authority"],"mandatory":true,"display":[{"name":"Issuing Authority","locale":"en"}]},{"path":["issuing_country"],"mandatory":true,"display":[{"name":"Issuing Country","locale":"en"}]},{"path":["document_number"],"mandatory":false,"display":[{"name":"Document Number","locale":"en"}]},{"path":["issuing_jurisdiction"],"mandatory":false,"display":[{"name":"Issuing Jurisdiction","locale":"en"}]},{"path":["date_of_issuance"],"mandatory":false,"display":[{"name":"Issuance Date","locale":"en"}]},{"path":["age_equal_or_over"],"mandatory":false,"display":[{"name":"Age Equal or Over","locale":"en"}]},{"path":["age_equal_or_over","18"],"mandatory":false,"display":[{"name":"Age Over 18","locale":"en"}]},{"path":["age_in_years"],"mandatory":false,"display":[{"name":"Age in Years","locale":"en"}]},{"path":["age_birth_year"],"mandatory":false,"display":[{"name":"Age Year of Birth","locale":"en"}]},{"path":["trust_anchor"],"mandatory":false,"display":[{"name":"Trust Anchor","locale":"en"}]}]},"org.iso.18013.5.1.mDL":{"format":"mso_mdoc","scope":"org.iso.18013.5.1.mDL","proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]}},"doctype":"org.iso.18013.5.1.mDL","display":[{"name":"Mobile Driving Licence","locale":"en"}],"policy":{"one_time_use":false,"batch_size":15},"claims":[{"path":["org.iso.18013.5.1","family_name"],"mandatory":true,"display":[{"name":"Family Name(s)","locale":"en"}]},{"path":["org.iso.18013.5.1","given_name"],"mandatory":true,"display":[{"name":"Given Name(s)","locale":"en"}]},{"path":["org.iso.18013.5.1","birth_date"],"mandatory":true,"display":[{"name":"Birth Date","locale":"en"}]},{"path":["org.iso.18013.5.1","issue_date"],"mandatory":true,"display":[{"name":"Issuance Date","locale":"en"}]},{"path":["org.iso.18013.5.1","expiry_date"],"mandatory":true,"display":[{"name":"Expiry Date","locale":"en"}]},{"path":["org.iso.18013.5.1","issuing_country"],"mandatory":true,"display":[{"name":"Issuing Country","locale":"en"}]},{"path":["org.iso.18013.5.1","issuing_authority"],"mandatory":true,"display":[{"name":"Issuing Authority","locale":"en"}]},{"path":["org.iso.18013.5.1","document_number"],"mandatory":true,"display":[{"name":"Document Number","locale":"en"}]},{"path":["org.iso.18013.5.1","portrait"],"mandatory":true,"display":[{"name":"Portrait Image","locale":"en"}]},{"path":["org.iso.18013.5.1","driving_privileges"],"mandatory":true,"display":[{"name":"Driving Privileges","locale":"en"}]},{"path":["org.iso.18013.5.1","un_distinguishing_sign"],"mandatory":true,"display":[{"name":"Distinguishing Sign","locale":"en"}]},{"path":["org.iso.18013.5.1","administrative_number"],"mandatory":false,"display":[{"name":"Administrative Number","locale":"en"}]},{"path":["org.iso.18013.5.1","sex"],"mandatory":false,"display":[{"name":"Sex","locale":"en"}]},{"path":["org.iso.18013.5.1","height"],"mandatory":false,"display":[{"name":"Height","locale":"en"}]},{"path":["org.iso.18013.5.1","weight"],"mandatory":false,"display":[{"name":"Weight","locale":"en"}]},{"path":["org.iso.18013.5.1","eye_colour"],"mandatory":false,"display":[{"name":"Eye Colour","locale":"en"}]},{"path":["org.iso.18013.5.1","hair_colour"],"mandatory":false,"display":[{"name":"Hair Colour","locale":"en"}]},{"path":["org.iso.18013.5.1","birth_place"],"mandatory":false,"display":[{"name":"Birth Place","locale":"en"}]},{"path":["org.iso.18013.5.1","resident_address"],"mandatory":false,"display":[{"name":"Resident Address","locale":"en"}]},{"path":["org.iso.18013.5.1","portrait_capture_date"],"mandatory":false,"display":[{"name":"Capture Date of Portrait Image","locale":"en"}]},{"path":["org.iso.18013.5.1","age_in_years"],"mandatory":false,"display":[{"name":"Age in Years","locale":"en"}]},{"path":["org.iso.18013.5.1","age_birth_year"],"mandatory":false,"display":[{"name":"Age Year of Birth","locale":"en"}]},{"path":["org.iso.18013.5.1","age_over_18"],"mandatory":false,"display":[{"name":"Age Over 18","locale":"en"}]},{"path":["org.iso.18013.5.1","age_over_21"],"mandatory":false,"display":[{"name":"Age Over 21","locale":"en"}]},{"path":["org.iso.18013.5.1","issuing_jurisdiction"],"mandatory":false,"display":[{"name":"Issuing Jurisdiction","locale":"en"}]},{"path":["org.iso.18013.5.1","nationality"],"mandatory":false,"display":[{"name":"Nationality","locale":"en"}]},{"path":["org.iso.18013.5.1","resident_city"],"mandatory":false,"display":[{"name":"Resident City","locale":"en"}]},{"path":["org.iso.18013.5.1","resident_state"],"mandatory":false,"display":[{"name":"Resident State","locale":"en"}]},{"path":["org.iso.18013.5.1","resident_postal_code"],"mandatory":false,"display":[{"name":"Resident Postal Code","locale":"en"}]},{"path":["org.iso.18013.5.1","resident_country"],"mandatory":false,"display":[{"name":"Resident Country","locale":"en"}]},{"path":["org.iso.18013.5.1","family_name_national_character"],"mandatory":false,"display":[{"name":"National Family Name(s)","locale":"en"}]},{"path":["org.iso.18013.5.1","given_name_national_character"],"mandatory":false,"display":[{"name":"National Given Name(s)","locale":"en"}]},{"path":["org.iso.18013.5.1","signature_usual_mark"],"mandatory":false,"display":[{"name":"Signature","locale":"en"}]}]},"urn:eudi:ehic:1:dc+sd-jwt-jws-json":{"format":"dc+sd-jwt","scope":"urn:eudi:ehic:1:dc+sd-jwt","cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256","ES384","ES512","RS256","RS384","RS512"]}},"display":[{"name":"European Health Insurance Card","locale":"en"}],"vct":"urn:eudi:ehic:1","claims":[{"path":["personal_administrative_number"],"mandatory":true,"display":[{"name":"Social Security PIN","locale":"en"}]},{"path":["issuing_authority"],"mandatory":true,"display":[{"name":"Issuing authority","locale":"en"}]},{"path":["issuing_authority","id"],"mandatory":true,"display":[{"name":"Issuing authority id","locale":"en"}]},{"path":["issuing_authority","name"],"mandatory":true,"display":[{"name":"Issuing authority name","locale":"en"}]},{"path":["country"],"mandatory":true,"display":[{"name":"Issuing country","locale":"en"}]},{"path":["date_of_expiry"],"mandatory":false,"display":[{"name":"Expiry date","locale":"en"}]},{"path":["date_of_issuance"],"mandatory":true,"display":[{"name":"Issue date","locale":"en"}]},{"path":["authentic_source"],"mandatory":true,"display":[{"name":"Competent institution","locale":"en"}]},{"path":["authentic_source","id"],"mandatory":true,"display":[{"name":"Competent institution id","locale":"en"}]},{"path":["authentic_source","name"],"mandatory":true,"display":[{"name":"Competent institution name","locale":"en"}]},{"path":["ending_date"],"mandatory":false,"display":[{"name":"Ending date","locale":"en"}]},{"path":["starting_date"],"mandatory":false,"display":[{"name":"Starting date","locale":"en"}]},{"path":["document_number"],"mandatory":true,"display":[{"name":"Document number","locale":"en"}]}]},"urn:eudi:ehic:1:dc+sd-jwt-compact":{"format":"dc+sd-jwt","scope":"urn:eudi:ehic:1:dc+sd-jwt","cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256","ES384","ES512","RS256","RS384","RS512"]}},"display":[{"name":"European Health Insurance Card","locale":"en"}],"vct":"urn:eudi:ehic:1","claims":[{"path":["personal_administrative_number"],"mandatory":true,"display":[{"name":"Social Security PIN","locale":"en"}]},{"path":["issuing_authority"],"mandatory":true,"display":[{"name":"Issuing authority","locale":"en"}]},{"path":["issuing_authority","id"],"mandatory":true,"display":[{"name":"Issuing authority id","locale":"en"}]},{"path":["issuing_authority","name"],"mandatory":true,"display":[{"name":"Issuing authority name","locale":"en"}]},{"path":["country"],"mandatory":true,"display":[{"name":"Issuing country","locale":"en"}]},{"path":["date_of_expiry"],"mandatory":false,"display":[{"name":"Expiry date","locale":"en"}]},{"path":["date_of_issuance"],"mandatory":true,"display":[{"name":"Issue date","locale":"en"}]},{"path":["authentic_source"],"mandatory":true,"display":[{"name":"Competent institution","locale":"en"}]},{"path":["authentic_source","id"],"mandatory":true,"display":[{"name":"Competent institution id","locale":"en"}]},{"path":["authentic_source","name"],"mandatory":true,"display":[{"name":"Competent institution name","locale":"en"}]},{"path":["ending_date"],"mandatory":false,"display":[{"name":"Ending date","locale":"en"}]},{"path":["starting_date"],"mandatory":false,"display":[{"name":"Starting date","locale":"en"}]},{"path":["document_number"],"mandatory":true,"display":[{"name":"Document number","locale":"en"}]}]}},"openid4vci_version":"draft 15"} +const metadata = { + credential_issuer: 'https://dev.issuer-backend.eudiw.dev', + authorization_servers: ['https://dev.authenticate.eudiw.dev/realms/pid-issuer-realm'], + credential_endpoint: 'https://dev.issuer-backend.eudiw.dev/wallet/credentialEndpoint', + deferred_credential_endpoint: 'https://dev.issuer-backend.eudiw.dev/wallet/deferredEndpoint', + notification_endpoint: 'https://dev.issuer-backend.eudiw.dev/wallet/notificationEndpoint', + nonce_endpoint: 'https://dev.issuer-backend.eudiw.dev/wallet/nonceEndpoint', + credential_response_encryption: { + alg_values_supported: ['RSA-OAEP-256', 'ECDH-ES'], + enc_values_supported: ['A128CBC-HS256'], + encryption_required: true, + }, + batch_credential_issuance: { batch_size: 15 }, + signed_metadata: + 'eyJ4NWMiOlsiTUlJQzZ6Q0NBcEdnQXdJQkFnSVViWDhuYllTTFJ2eTEwbUtOK2hmQ1ZyLzhjQmN3Q2dZSUtvWkl6ajBFQXdJd1hERWVNQndHQTFVRUF3d1ZVRWxFSUVsemMzVmxjaUJEUVNBdElGVlVJREF5TVMwd0t3WURWUVFLRENSRlZVUkpJRmRoYkd4bGRDQlNaV1psY21WdVkyVWdTVzF3YkdWdFpXNTBZWFJwYjI0eEN6QUpCZ05WQkFZVEFsVlVNQjRYRFRJMU1EUXhNREUwTWpVME1Gb1hEVEkyTURjd05ERTBNalV6T1Zvd1VqRVVNQklHQTFVRUF3d0xVRWxFSUVSVElDMGdNRE14TFRBckJnTlZCQW9NSkVWVlJFa2dWMkZzYkdWMElGSmxabVZ5Wlc1alpTQkpiWEJzWlcxbGJuUmhkR2x2YmpFTE1Ba0dBMVVFQmhNQ1ZWUXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBU3J4WjEzd0xqL25VdUdlYllSbVBPMHE3cFJrMXgxU2pycUxUdnRRRnBRY3k5VHdGQ2NnaWUvQkJDMmovS3BMY0NyK29qNHR5WkFvZm12SGRhVEV4YkJvNElCT1RDQ0FUVXdId1lEVlIwakJCZ3dGb0FVWXNlVVJ5aTlENklXSUtlYXdrbVVSUEVCMDhjd0p3WURWUjBSQkNBd0hvSWNaR1YyTG1semMzVmxjaTFpWVdOclpXNWtMbVYxWkdsM0xtUmxkakFXQmdOVkhTVUJBZjhFRERBS0JnZ3JnUUlDQUFBQkFqQkRCZ05WSFI4RVBEQTZNRGlnTnFBMGhqSm9kSFJ3Y3pvdkwzQnlaWEJ5YjJRdWNHdHBMbVYxWkdsM0xtUmxkaTlqY213dmNHbGtYME5CWDFWVVh6QXlMbU55YkRBZEJnTlZIUTRFRmdRVWNzM0t5cWl6SGd0WFJlMzJuNkpCSkhBZmFMWXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1GMEdBMVVkRWdSV01GU0dVbWgwZEhCek9pOHZaMmwwYUhWaUxtTnZiUzlsZFMxa2FXZHBkR0ZzTFdsa1pXNTBhWFI1TFhkaGJHeGxkQzloY21Ob2FYUmxZM1IxY21VdFlXNWtMWEpsWm1WeVpXNWpaUzFtY21GdFpYZHZjbXN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVFZabmNoRCtRanE1M1hzMG9jMDd5M3pHNmtBWEZrSitaS3psVkcyMnNDOENJUUR0RE1RcTBRbS9mUTVvcnJqUlQ0WEIrMEpiNnhGUHhYOVFrVlJhTXkvSWlBPT0iXSwiYWxnIjoiRVMyNTYifQ.eyJzdWIiOiJodHRwczovL2Rldi5pc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYiLCJkaXNwbGF5IjpbeyJuYW1lIjoiRGlnaXRhbCBDcmVkZW50aWFscyBJc3N1ZXIiLCJsb2NhbGUiOiJlbiIsImxvZ28iOnsidXJpIjoiaHR0cHM6Ly9ldWRpdy5kZXYvaWMtbG9nby5zdmciLCJhbHRfdGV4dCI6IkVVIERpZ2l0YWwgSWRlbnRpdHkgV2FsbGV0IExvZ28ifX0seyJuYW1lIjoizpXOus60z4zPhM63z4IgzqjOt8-GzrnOsc66z47OvSDOlM65zrHPgM65z4PPhM61z4XPhM63z4HOr8-Jzr0iLCJsb2NhbGUiOiJlbCIsImxvZ28iOnsidXJpIjoiaHR0cHM6Ly9ldWRpdy5kZXYvaWMtbG9nby5zdmciLCJhbHRfdGV4dCI6Is6bzr_Os8-Mz4TPhc-Azr8gzpXPhc-Bz4nPgM6xz4rOus6_z40gzqDOv8-Bz4TOv8-Gzr_Ou865zr_PjSDOqM63z4bOuc6xzrrOrs-CIM6kzrHPhc-Ez4zPhM63z4TOsc-CIn19XSwiaXNzIjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2IiwiYmF0Y2hfY3JlZGVudGlhbF9pc3N1YW5jZSI6eyJiYXRjaF9zaXplIjoxNX0sImNyZWRlbnRpYWxfY29uZmlndXJhdGlvbnNfc3VwcG9ydGVkIjp7ImV1LmV1cm9wYS5lYy5ldWRpLnBpZF9tc29fbWRvYyI6eyJmb3JtYXQiOiJtc29fbWRvYyIsInNjb3BlIjoiZXUuZXVyb3BhLmVjLmV1ZGkucGlkX21zb19tZG9jIiwicHJvb2ZfdHlwZXNfc3VwcG9ydGVkIjp7Imp3dCI6eyJwcm9vZl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2Il19fSwiZG9jdHlwZSI6ImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZGlzcGxheSI6W3sibmFtZSI6IlBJRCIsImxvY2FsZSI6ImVuIiwibG9nbyI6eyJ1cmkiOiJodHRwczovL2V4YW1wbGVzdGF0ZS5jb20vcHVibGljL21kbC5wbmciLCJhbHRfdGV4dCI6IkEgc3F1YXJlIGZpZ3VyZSBvZiBhIFBJRCJ9fV0sInBvbGljeSI6eyJvbmVfdGltZV91c2UiOnRydWUsImJhdGNoX3NpemUiOjE1fSwiY2xhaW1zIjpbeyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZmFtaWx5X25hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRmFtaWx5IE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZ2l2ZW5fbmFtZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImJpcnRoX2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJiaXJ0aF9wbGFjZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBQbGFjZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJuYXRpb25hbGl0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJyZXNpZGVudF9hZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2NvdW50cnkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IENvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfc3RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IFN0YXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2NpdHkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IENpdHkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfcG9zdGFsX2NvZGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IFBvc3RhbCBDb2RlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X3N0cmVldCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgU3RyZWV0IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2hvdXNlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgSG91c2UgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUGVyc29uYWwgQWRtaW5pc3RyYXRpdmUgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInBvcnRyYWl0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJQb3J0cmFpdCBJbWFnZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJmYW1pbHlfbmFtZV9iaXJ0aCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggRmFtaWx5IE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZ2l2ZW5fbmFtZV9iaXJ0aCJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQmlydGggR2l2ZW4gTmFtZShzKSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJzZXgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlNleCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJlbWFpbF9hZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFbWFpbCBBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsIm1vYmlsZV9waG9uZV9udW1iZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Ik1vYmlsZSBQaG9uZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZXhwaXJ5X2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiaXNzdWluZ19hdXRob3JpdHkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWFuY2UgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImlzc3VpbmdfY291bnRyeSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIENvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiZG9jdW1lbnRfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImlzc3VhbmNlX2RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VhbmNlIERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwiYWdlX292ZXJfMTgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBPdmVyIDE4IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImFnZV9pbl95ZWFycyJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIGluIFllYXJzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImFnZV9iaXJ0aF95ZWFyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgWWVhciBvZiBCaXJ0aCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJ0cnVzdF9hbmNob3IiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlRydXN0IEFuY2hvciIsImxvY2FsZSI6ImVuIn1dfV19LCJldS5ldXJvcGEuZWMuZXVkaS5waWRfdmNfc2Rfand0Ijp7ImZvcm1hdCI6ImRjK3NkLWp3dCIsInNjb3BlIjoiZXUuZXVyb3BhLmVjLmV1ZGkucGlkX3ZjX3NkX2p3dCIsImNyeXB0b2dyYXBoaWNfYmluZGluZ19tZXRob2RzX3N1cHBvcnRlZCI6WyJqd2siXSwiY3JlZGVudGlhbF9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2Il0sInByb29mX3R5cGVzX3N1cHBvcnRlZCI6eyJqd3QiOnsicHJvb2Zfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJSUzI1NiIsIkVTMjU2Il19fSwiZGlzcGxheSI6W3sibmFtZSI6IlBJRCIsImxvY2FsZSI6ImVuIiwibG9nbyI6eyJ1cmkiOiJodHRwczovL2V4YW1wbGVzdGF0ZS5jb20vcHVibGljL21kbC5wbmciLCJhbHRfdGV4dCI6IkEgc3F1YXJlIGZpZ3VyZSBvZiBhIFBJRCJ9fV0sInZjdCI6InVybjpldWRpOnBpZDoxIiwiY2xhaW1zIjpbeyJwYXRoIjpbImZhbWlseV9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJnaXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkdpdmVuIE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImJpcnRoZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJwbGFjZV9vZl9iaXJ0aCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBQbGFjZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJsb2NhbGl0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJMb2NhbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJyZWdpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlZ2lvbiIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsicGxhY2Vfb2ZfYmlydGgiLCJjb3VudHJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJuYXRpb25hbGl0aWVzIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Ik5hdGlvbmFsaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZGRyZXNzIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwiaG91c2VfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJIb3VzZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJzdHJlZXRfYWRkcmVzcyJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiU3RyZWV0IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwibG9jYWxpdHkiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkxvY2FsaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwicmVnaW9uIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZWdpb24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJwb3N0YWxfY29kZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUG9zdGFsIENvZGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFkZHJlc3MiLCJjb3VudHJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZGRyZXNzIiwiZm9ybWF0dGVkIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJGdWxsIEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUGVyc29uYWwgQWRtaW5pc3RyYXRpdmUgTnVtYmVyIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJwaWN0dXJlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJQb3J0cmFpdCBJbWFnZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYmlydGhfZmFtaWx5X25hbWUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkJpcnRoIEZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJiaXJ0aF9naXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJzZXgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlNleCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZW1haWwiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkVtYWlsIEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInBob25lX251bWJlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiTW9iaWxlIFBob25lIE51bWJlciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZGF0ZV9vZl9leHBpcnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IERhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2NvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkb2N1bWVudF9udW1iZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkRvY3VtZW50IE51bWJlciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkYXRlX29mX2lzc3VhbmNlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1YW5jZSBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZ2VfZXF1YWxfb3Jfb3ZlciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIEVxdWFsIG9yIE92ZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImFnZV9lcXVhbF9vcl9vdmVyIiwiMTgiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBPdmVyIDE4IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhZ2VfaW5feWVhcnMiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBpbiBZZWFycyIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYWdlX2JpcnRoX3llYXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBZZWFyIG9mIEJpcnRoIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJ0cnVzdF9hbmNob3IiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlRydXN0IEFuY2hvciIsImxvY2FsZSI6ImVuIn1dfV19LCJvcmcuaXNvLjE4MDEzLjUuMS5tREwiOnsiZm9ybWF0IjoibXNvX21kb2MiLCJzY29wZSI6Im9yZy5pc28uMTgwMTMuNS4xLm1ETCIsInByb29mX3R5cGVzX3N1cHBvcnRlZCI6eyJqd3QiOnsicHJvb2Zfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFUzI1NiJdfX0sImRvY3R5cGUiOiJvcmcuaXNvLjE4MDEzLjUuMS5tREwiLCJkaXNwbGF5IjpbeyJuYW1lIjoiTW9iaWxlIERyaXZpbmcgTGljZW5jZSIsImxvY2FsZSI6ImVuIn1dLCJwb2xpY3kiOnsib25lX3RpbWVfdXNlIjpmYWxzZSwiYmF0Y2hfc2l6ZSI6MTV9LCJjbGFpbXMiOlt7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJmYW1pbHlfbmFtZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJGYW1pbHkgTmFtZShzKSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJnaXZlbl9uYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkdpdmVuIE5hbWUocykiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYmlydGhfZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJCaXJ0aCBEYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImlzc3VlX2RhdGUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWFuY2UgRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJleHBpcnlfZGF0ZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJFeHBpcnkgRGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJpc3N1aW5nX2NvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBDb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgQXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImRvY3VtZW50X251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicG9ydHJhaXQiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUG9ydHJhaXQgSW1hZ2UiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiZHJpdmluZ19wcml2aWxlZ2VzIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRyaXZpbmcgUHJpdmlsZWdlcyIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJ1bl9kaXN0aW5ndWlzaGluZ19zaWduIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRpc3Rpbmd1aXNoaW5nIFNpZ24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWRtaW5pc3RyYXRpdmVfbnVtYmVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZG1pbmlzdHJhdGl2ZSBOdW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwic2V4Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJTZXgiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiaGVpZ2h0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJIZWlnaHQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwid2VpZ2h0Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJXZWlnaHQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiZXllX2NvbG91ciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXllIENvbG91ciIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJoYWlyX2NvbG91ciJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSGFpciBDb2xvdXIiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYmlydGhfcGxhY2UiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkJpcnRoIFBsYWNlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInJlc2lkZW50X2FkZHJlc3MiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlJlc2lkZW50IEFkZHJlc3MiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicG9ydHJhaXRfY2FwdHVyZV9kYXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJDYXB0dXJlIERhdGUgb2YgUG9ydHJhaXQgSW1hZ2UiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWdlX2luX3llYXJzIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgaW4gWWVhcnMiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiYWdlX2JpcnRoX3llYXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IkFnZSBZZWFyIG9mIEJpcnRoIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImFnZV9vdmVyXzE4Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJBZ2UgT3ZlciAxOCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJhZ2Vfb3Zlcl8yMSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQWdlIE92ZXIgMjEiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwiaXNzdWluZ19qdXJpc2RpY3Rpb24iXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgSnVyaXNkaWN0aW9uIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsIm5hdGlvbmFsaXR5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbGl0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJyZXNpZGVudF9jaXR5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBDaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInJlc2lkZW50X3N0YXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJSZXNpZGVudCBTdGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJyZXNpZGVudF9wb3N0YWxfY29kZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgUG9zdGFsIENvZGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbIm9yZy5pc28uMTgwMTMuNS4xIiwicmVzaWRlbnRfY291bnRyeSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiUmVzaWRlbnQgQ291bnRyeSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsib3JnLmlzby4xODAxMy41LjEiLCJmYW1pbHlfbmFtZV9uYXRpb25hbF9jaGFyYWN0ZXIiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6Ik5hdGlvbmFsIEZhbWlseSBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsImdpdmVuX25hbWVfbmF0aW9uYWxfY2hhcmFjdGVyIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJOYXRpb25hbCBHaXZlbiBOYW1lKHMpIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJvcmcuaXNvLjE4MDEzLjUuMSIsInNpZ25hdHVyZV91c3VhbF9tYXJrIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJTaWduYXR1cmUiLCJsb2NhbGUiOiJlbiJ9XX1dfSwidXJuOmV1ZGk6ZWhpYzoxOmRjK3NkLWp3dC1qd3MtanNvbiI6eyJmb3JtYXQiOiJkYytzZC1qd3QiLCJzY29wZSI6InVybjpldWRpOmVoaWM6MTpkYytzZC1qd3QiLCJjcnlwdG9ncmFwaGljX2JpbmRpbmdfbWV0aG9kc19zdXBwb3J0ZWQiOlsiandrIl0sImNyZWRlbnRpYWxfc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFUzI1NiJdLCJwcm9vZl90eXBlc19zdXBwb3J0ZWQiOnsiand0Ijp7InByb29mX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRVMyNTYiLCJFUzM4NCIsIkVTNTEyIiwiUlMyNTYiLCJSUzM4NCIsIlJTNTEyIl19fSwiZGlzcGxheSI6W3sibmFtZSI6IkV1cm9wZWFuIEhlYWx0aCBJbnN1cmFuY2UgQ2FyZCIsImxvY2FsZSI6ImVuIn1dLCJ2Y3QiOiJ1cm46ZXVkaTplaGljOjEiLCJjbGFpbXMiOlt7InBhdGgiOlsicGVyc29uYWxfYWRtaW5pc3RyYXRpdmVfbnVtYmVyIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IlNvY2lhbCBTZWN1cml0eSBQSU4iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImlzc3VpbmdfYXV0aG9yaXR5Il0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSIsImlkIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IGlkIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSIsIm5hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBhdXRob3JpdHkgbmFtZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiY291bnRyeSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGNvdW50cnkiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRhdGVfb2ZfZXhwaXJ5Il0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFeHBpcnkgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZGF0ZV9vZl9pc3N1YW5jZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1ZSBkYXRlIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJhdXRoZW50aWNfc291cmNlIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSIsImlkIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiBpZCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSIsIm5hbWUiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiQ29tcGV0ZW50IGluc3RpdHV0aW9uIG5hbWUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImVuZGluZ19kYXRlIl0sIm1hbmRhdG9yeSI6ZmFsc2UsImRpc3BsYXkiOlt7Im5hbWUiOiJFbmRpbmcgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsic3RhcnRpbmdfZGF0ZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiU3RhcnRpbmcgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiZG9jdW1lbnRfbnVtYmVyIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkRvY3VtZW50IG51bWJlciIsImxvY2FsZSI6ImVuIn1dfV19LCJ1cm46ZXVkaTplaGljOjE6ZGMrc2Qtand0LWNvbXBhY3QiOnsiZm9ybWF0IjoiZGMrc2Qtand0Iiwic2NvcGUiOiJ1cm46ZXVkaTplaGljOjE6ZGMrc2Qtand0IiwiY3J5cHRvZ3JhcGhpY19iaW5kaW5nX21ldGhvZHNfc3VwcG9ydGVkIjpbImp3ayJdLCJjcmVkZW50aWFsX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRVMyNTYiXSwicHJvb2ZfdHlwZXNfc3VwcG9ydGVkIjp7Imp3dCI6eyJwcm9vZl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVTMjU2IiwiRVMzODQiLCJFUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiJdfX0sImRpc3BsYXkiOlt7Im5hbWUiOiJFdXJvcGVhbiBIZWFsdGggSW5zdXJhbmNlIENhcmQiLCJsb2NhbGUiOiJlbiJ9XSwidmN0IjoidXJuOmV1ZGk6ZWhpYzoxIiwiY2xhaW1zIjpbeyJwYXRoIjpbInBlcnNvbmFsX2FkbWluaXN0cmF0aXZlX251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJTb2NpYWwgU2VjdXJpdHkgUElOIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGF1dGhvcml0eSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19hdXRob3JpdHkiLCJpZCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJJc3N1aW5nIGF1dGhvcml0eSBpZCIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiaXNzdWluZ19hdXRob3JpdHkiLCJuYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6Iklzc3VpbmcgYXV0aG9yaXR5IG5hbWUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImNvdW50cnkiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWluZyBjb3VudHJ5IiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJkYXRlX29mX2V4cGlyeSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRXhwaXJ5IGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRhdGVfb2ZfaXNzdWFuY2UiXSwibWFuZGF0b3J5Ijp0cnVlLCJkaXNwbGF5IjpbeyJuYW1lIjoiSXNzdWUgZGF0ZSIsImxvY2FsZSI6ImVuIn1dfSx7InBhdGgiOlsiYXV0aGVudGljX3NvdXJjZSJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJDb21wZXRlbnQgaW5zdGl0dXRpb24iLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImF1dGhlbnRpY19zb3VyY2UiLCJpZCJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJDb21wZXRlbnQgaW5zdGl0dXRpb24gaWQiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImF1dGhlbnRpY19zb3VyY2UiLCJuYW1lIl0sIm1hbmRhdG9yeSI6dHJ1ZSwiZGlzcGxheSI6W3sibmFtZSI6IkNvbXBldGVudCBpbnN0aXR1dGlvbiBuYW1lIiwibG9jYWxlIjoiZW4ifV19LHsicGF0aCI6WyJlbmRpbmdfZGF0ZSJdLCJtYW5kYXRvcnkiOmZhbHNlLCJkaXNwbGF5IjpbeyJuYW1lIjoiRW5kaW5nIGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbInN0YXJ0aW5nX2RhdGUiXSwibWFuZGF0b3J5IjpmYWxzZSwiZGlzcGxheSI6W3sibmFtZSI6IlN0YXJ0aW5nIGRhdGUiLCJsb2NhbGUiOiJlbiJ9XX0seyJwYXRoIjpbImRvY3VtZW50X251bWJlciJdLCJtYW5kYXRvcnkiOnRydWUsImRpc3BsYXkiOlt7Im5hbWUiOiJEb2N1bWVudCBudW1iZXIiLCJsb2NhbGUiOiJlbiJ9XX1dfX0sIm5vbmNlX2VuZHBvaW50IjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2L3dhbGxldC9ub25jZUVuZHBvaW50Iiwib3BlbmlkNHZjaV92ZXJzaW9uIjoiZHJhZnQgMTUiLCJjcmVkZW50aWFsX2lzc3VlciI6Imh0dHBzOi8vZGV2Lmlzc3Vlci1iYWNrZW5kLmV1ZGl3LmRldiIsImNyZWRlbnRpYWxfcmVzcG9uc2VfZW5jcnlwdGlvbiI6eyJhbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJSU0EtT0FFUC0yNTYiLCJFQ0RILUVTIl0sImVuY192YWx1ZXNfc3VwcG9ydGVkIjpbIkExMjhDQkMtSFMyNTYiXSwiZW5jcnlwdGlvbl9yZXF1aXJlZCI6dHJ1ZX0sIm5vdGlmaWNhdGlvbl9lbmRwb2ludCI6Imh0dHBzOi8vZGV2Lmlzc3Vlci1iYWNrZW5kLmV1ZGl3LmRldi93YWxsZXQvbm90aWZpY2F0aW9uRW5kcG9pbnQiLCJpYXQiOjE3NTEwMjI4ODYsImRlZmVycmVkX2NyZWRlbnRpYWxfZW5kcG9pbnQiOiJodHRwczovL2Rldi5pc3N1ZXItYmFja2VuZC5ldWRpdy5kZXYvd2FsbGV0L2RlZmVycmVkRW5kcG9pbnQiLCJhdXRob3JpemF0aW9uX3NlcnZlcnMiOlsiaHR0cHM6Ly9kZXYuYXV0aGVudGljYXRlLmV1ZGl3LmRldi9yZWFsbXMvcGlkLWlzc3Vlci1yZWFsbSJdLCJjcmVkZW50aWFsX2VuZHBvaW50IjoiaHR0cHM6Ly9kZXYuaXNzdWVyLWJhY2tlbmQuZXVkaXcuZGV2L3dhbGxldC9jcmVkZW50aWFsRW5kcG9pbnQifQ.vV3Zqv8lPyV2mW8PwAfNY0ASRIaqjGWZCQ3JW-JWar50P_jaPZDC6Vf0rogX0NmPUC5zDES1zoam2znqEAN5AA', + display: [ + { + name: 'Digital Credentials Issuer', + locale: 'en', + logo: { uri: 'https://eudiw.dev/ic-logo.svg', alt_text: 'EU Digital Identity Wallet Logo' }, + }, + { + name: 'Εκδότης Ψηφιακών Διαπιστευτηρίων', + locale: 'el', + logo: { + uri: 'https://eudiw.dev/ic-logo.svg', + alt_text: 'Λογότυπο Ευρωπαϊκού Πορτοφολιού Ψηφιακής Ταυτότητας', + }, + }, + ], + credential_configurations_supported: { + 'eu.europa.ec.eudi.pid_mso_mdoc': { + format: 'mso_mdoc', + scope: 'eu.europa.ec.eudi.pid_mso_mdoc', + proof_types_supported: { jwt: { proof_signing_alg_values_supported: ['ES256'] } }, + doctype: 'eu.europa.ec.eudi.pid.1', + display: [ + { + name: 'PID', + locale: 'en', + logo: { + uri: 'https://examplestate.com/public/mdl.png', + alt_text: 'A square figure of a PID', + }, + }, + ], + policy: { one_time_use: true, batch_size: 15 }, + claims: [ + { + path: ['eu.europa.ec.eudi.pid.1', 'family_name'], + mandatory: true, + display: [{ name: 'Family Name(s)', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'given_name'], + mandatory: true, + display: [{ name: 'Given Name(s)', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'birth_date'], + mandatory: true, + display: [{ name: 'Birth Date', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'birth_place'], + mandatory: true, + display: [{ name: 'Birth Place', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'nationality'], + mandatory: true, + display: [{ name: 'Nationality', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_address'], + mandatory: false, + display: [{ name: 'Resident Address', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_country'], + mandatory: false, + display: [{ name: 'Resident Country', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_state'], + mandatory: false, + display: [{ name: 'Resident State', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_city'], + mandatory: false, + display: [{ name: 'Resident City', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_postal_code'], + mandatory: false, + display: [{ name: 'Resident Postal Code', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_street'], + mandatory: false, + display: [{ name: 'Resident Street', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'resident_house_number'], + mandatory: false, + display: [{ name: 'Resident House Number', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'personal_administrative_number'], + mandatory: false, + display: [{ name: 'Personal Administrative Number', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'portrait'], + mandatory: false, + display: [{ name: 'Portrait Image', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'family_name_birth'], + mandatory: false, + display: [{ name: 'Birth Family Name(s)', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'given_name_birth'], + mandatory: false, + display: [{ name: 'Birth Given Name(s)', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'sex'], + mandatory: false, + display: [{ name: 'Sex', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'email_address'], + mandatory: false, + display: [{ name: 'Email Address', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'mobile_phone_number'], + mandatory: false, + display: [{ name: 'Mobile Phone Number', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'expiry_date'], + mandatory: true, + display: [{ name: 'Expiry Date', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'issuing_authority'], + mandatory: true, + display: [{ name: 'Issuance Authority', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'issuing_country'], + mandatory: true, + display: [{ name: 'Issuing Country', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'document_number'], + mandatory: false, + display: [{ name: 'Document Number', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'issuing_jurisdiction'], + mandatory: false, + display: [{ name: 'Issuing Jurisdiction', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'issuance_date'], + mandatory: false, + display: [{ name: 'Issuance Date', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'age_over_18'], + mandatory: false, + display: [{ name: 'Age Over 18', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'age_in_years'], + mandatory: false, + display: [{ name: 'Age in Years', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'age_birth_year'], + mandatory: false, + display: [{ name: 'Age Year of Birth', locale: 'en' }], + }, + { + path: ['eu.europa.ec.eudi.pid.1', 'trust_anchor'], + mandatory: false, + display: [{ name: 'Trust Anchor', locale: 'en' }], + }, + ], + }, + 'eu.europa.ec.eudi.pid_vc_sd_jwt': { + format: 'dc+sd-jwt', + scope: 'eu.europa.ec.eudi.pid_vc_sd_jwt', + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + proof_types_supported: { jwt: { proof_signing_alg_values_supported: ['RS256', 'ES256'] } }, + display: [ + { + name: 'PID', + locale: 'en', + logo: { + uri: 'https://examplestate.com/public/mdl.png', + alt_text: 'A square figure of a PID', + }, + }, + ], + vct: 'urn:eudi:pid:1', + claims: [ + { + path: ['family_name'], + mandatory: true, + display: [{ name: 'Family Name(s)', locale: 'en' }], + }, + { + path: ['given_name'], + mandatory: true, + display: [{ name: 'Given Name(s)', locale: 'en' }], + }, + { path: ['birthdate'], mandatory: true, display: [{ name: 'Birth Date', locale: 'en' }] }, + { + path: ['place_of_birth'], + mandatory: true, + display: [{ name: 'Birth Place', locale: 'en' }], + }, + { + path: ['place_of_birth', 'locality'], + mandatory: true, + display: [{ name: 'Locality', locale: 'en' }], + }, + { + path: ['place_of_birth', 'region'], + mandatory: false, + display: [{ name: 'Region', locale: 'en' }], + }, + { + path: ['place_of_birth', 'country'], + mandatory: false, + display: [{ name: 'Country', locale: 'en' }], + }, + { + path: ['nationalities'], + mandatory: true, + display: [{ name: 'Nationality', locale: 'en' }], + }, + { path: ['address'], mandatory: false, display: [{ name: 'Address', locale: 'en' }] }, + { + path: ['address', 'house_number'], + mandatory: false, + display: [{ name: 'House Number', locale: 'en' }], + }, + { + path: ['address', 'street_address'], + mandatory: false, + display: [{ name: 'Street', locale: 'en' }], + }, + { + path: ['address', 'locality'], + mandatory: false, + display: [{ name: 'Locality', locale: 'en' }], + }, + { + path: ['address', 'region'], + mandatory: false, + display: [{ name: 'Region', locale: 'en' }], + }, + { + path: ['address', 'postal_code'], + mandatory: false, + display: [{ name: 'Postal Code', locale: 'en' }], + }, + { + path: ['address', 'country'], + mandatory: false, + display: [{ name: 'Country', locale: 'en' }], + }, + { + path: ['address', 'formatted'], + mandatory: false, + display: [{ name: 'Full Address', locale: 'en' }], + }, + { + path: ['personal_administrative_number'], + mandatory: false, + display: [{ name: 'Personal Administrative Number', locale: 'en' }], + }, + { + path: ['picture'], + mandatory: false, + display: [{ name: 'Portrait Image', locale: 'en' }], + }, + { + path: ['birth_family_name'], + mandatory: false, + display: [{ name: 'Birth Family Name(s)', locale: 'en' }], + }, + { + path: ['birth_given_name'], + mandatory: false, + display: [{ name: 'Birth Given Name(s)', locale: 'en' }], + }, + { path: ['sex'], mandatory: false, display: [{ name: 'Sex', locale: 'en' }] }, + { path: ['email'], mandatory: false, display: [{ name: 'Email Address', locale: 'en' }] }, + { + path: ['phone_number'], + mandatory: false, + display: [{ name: 'Mobile Phone Number', locale: 'en' }], + }, + { + path: ['date_of_expiry'], + mandatory: true, + display: [{ name: 'Expiry Date', locale: 'en' }], + }, + { + path: ['issuing_authority'], + mandatory: true, + display: [{ name: 'Issuing Authority', locale: 'en' }], + }, + { + path: ['issuing_country'], + mandatory: true, + display: [{ name: 'Issuing Country', locale: 'en' }], + }, + { + path: ['document_number'], + mandatory: false, + display: [{ name: 'Document Number', locale: 'en' }], + }, + { + path: ['issuing_jurisdiction'], + mandatory: false, + display: [{ name: 'Issuing Jurisdiction', locale: 'en' }], + }, + { + path: ['date_of_issuance'], + mandatory: false, + display: [{ name: 'Issuance Date', locale: 'en' }], + }, + { + path: ['age_equal_or_over'], + mandatory: false, + display: [{ name: 'Age Equal or Over', locale: 'en' }], + }, + { + path: ['age_equal_or_over', '18'], + mandatory: false, + display: [{ name: 'Age Over 18', locale: 'en' }], + }, + { + path: ['age_in_years'], + mandatory: false, + display: [{ name: 'Age in Years', locale: 'en' }], + }, + { + path: ['age_birth_year'], + mandatory: false, + display: [{ name: 'Age Year of Birth', locale: 'en' }], + }, + { + path: ['trust_anchor'], + mandatory: false, + display: [{ name: 'Trust Anchor', locale: 'en' }], + }, + ], + }, + 'org.iso.18013.5.1.mDL': { + format: 'mso_mdoc', + scope: 'org.iso.18013.5.1.mDL', + proof_types_supported: { jwt: { proof_signing_alg_values_supported: ['ES256'] } }, + doctype: 'org.iso.18013.5.1.mDL', + display: [{ name: 'Mobile Driving Licence', locale: 'en' }], + policy: { one_time_use: false, batch_size: 15 }, + claims: [ + { + path: ['org.iso.18013.5.1', 'family_name'], + mandatory: true, + display: [{ name: 'Family Name(s)', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'given_name'], + mandatory: true, + display: [{ name: 'Given Name(s)', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'birth_date'], + mandatory: true, + display: [{ name: 'Birth Date', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'issue_date'], + mandatory: true, + display: [{ name: 'Issuance Date', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'expiry_date'], + mandatory: true, + display: [{ name: 'Expiry Date', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'issuing_country'], + mandatory: true, + display: [{ name: 'Issuing Country', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'issuing_authority'], + mandatory: true, + display: [{ name: 'Issuing Authority', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'document_number'], + mandatory: true, + display: [{ name: 'Document Number', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'portrait'], + mandatory: true, + display: [{ name: 'Portrait Image', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'driving_privileges'], + mandatory: true, + display: [{ name: 'Driving Privileges', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'un_distinguishing_sign'], + mandatory: true, + display: [{ name: 'Distinguishing Sign', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'administrative_number'], + mandatory: false, + display: [{ name: 'Administrative Number', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'sex'], + mandatory: false, + display: [{ name: 'Sex', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'height'], + mandatory: false, + display: [{ name: 'Height', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'weight'], + mandatory: false, + display: [{ name: 'Weight', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'eye_colour'], + mandatory: false, + display: [{ name: 'Eye Colour', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'hair_colour'], + mandatory: false, + display: [{ name: 'Hair Colour', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'birth_place'], + mandatory: false, + display: [{ name: 'Birth Place', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'resident_address'], + mandatory: false, + display: [{ name: 'Resident Address', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'portrait_capture_date'], + mandatory: false, + display: [{ name: 'Capture Date of Portrait Image', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'age_in_years'], + mandatory: false, + display: [{ name: 'Age in Years', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'age_birth_year'], + mandatory: false, + display: [{ name: 'Age Year of Birth', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'age_over_18'], + mandatory: false, + display: [{ name: 'Age Over 18', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'age_over_21'], + mandatory: false, + display: [{ name: 'Age Over 21', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'issuing_jurisdiction'], + mandatory: false, + display: [{ name: 'Issuing Jurisdiction', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'nationality'], + mandatory: false, + display: [{ name: 'Nationality', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'resident_city'], + mandatory: false, + display: [{ name: 'Resident City', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'resident_state'], + mandatory: false, + display: [{ name: 'Resident State', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'resident_postal_code'], + mandatory: false, + display: [{ name: 'Resident Postal Code', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'resident_country'], + mandatory: false, + display: [{ name: 'Resident Country', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'family_name_national_character'], + mandatory: false, + display: [{ name: 'National Family Name(s)', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'given_name_national_character'], + mandatory: false, + display: [{ name: 'National Given Name(s)', locale: 'en' }], + }, + { + path: ['org.iso.18013.5.1', 'signature_usual_mark'], + mandatory: false, + display: [{ name: 'Signature', locale: 'en' }], + }, + ], + }, + 'urn:eudi:ehic:1:dc+sd-jwt-jws-json': { + format: 'dc+sd-jwt', + scope: 'urn:eudi:ehic:1:dc+sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + proof_types_supported: { + jwt: { + proof_signing_alg_values_supported: [ + 'ES256', + 'ES384', + 'ES512', + 'RS256', + 'RS384', + 'RS512', + ], + }, + }, + display: [{ name: 'European Health Insurance Card', locale: 'en' }], + vct: 'urn:eudi:ehic:1', + claims: [ + { + path: ['personal_administrative_number'], + mandatory: true, + display: [{ name: 'Social Security PIN', locale: 'en' }], + }, + { + path: ['issuing_authority'], + mandatory: true, + display: [{ name: 'Issuing authority', locale: 'en' }], + }, + { + path: ['issuing_authority', 'id'], + mandatory: true, + display: [{ name: 'Issuing authority id', locale: 'en' }], + }, + { + path: ['issuing_authority', 'name'], + mandatory: true, + display: [{ name: 'Issuing authority name', locale: 'en' }], + }, + { + path: ['country'], + mandatory: true, + display: [{ name: 'Issuing country', locale: 'en' }], + }, + { + path: ['date_of_expiry'], + mandatory: false, + display: [{ name: 'Expiry date', locale: 'en' }], + }, + { + path: ['date_of_issuance'], + mandatory: true, + display: [{ name: 'Issue date', locale: 'en' }], + }, + { + path: ['authentic_source'], + mandatory: true, + display: [{ name: 'Competent institution', locale: 'en' }], + }, + { + path: ['authentic_source', 'id'], + mandatory: true, + display: [{ name: 'Competent institution id', locale: 'en' }], + }, + { + path: ['authentic_source', 'name'], + mandatory: true, + display: [{ name: 'Competent institution name', locale: 'en' }], + }, + { + path: ['ending_date'], + mandatory: false, + display: [{ name: 'Ending date', locale: 'en' }], + }, + { + path: ['starting_date'], + mandatory: false, + display: [{ name: 'Starting date', locale: 'en' }], + }, + { + path: ['document_number'], + mandatory: true, + display: [{ name: 'Document number', locale: 'en' }], + }, + ], + }, + 'urn:eudi:ehic:1:dc+sd-jwt-compact': { + format: 'dc+sd-jwt', + scope: 'urn:eudi:ehic:1:dc+sd-jwt', + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + proof_types_supported: { + jwt: { + proof_signing_alg_values_supported: [ + 'ES256', + 'ES384', + 'ES512', + 'RS256', + 'RS384', + 'RS512', + ], + }, + }, + display: [{ name: 'European Health Insurance Card', locale: 'en' }], + vct: 'urn:eudi:ehic:1', + claims: [ + { + path: ['personal_administrative_number'], + mandatory: true, + display: [{ name: 'Social Security PIN', locale: 'en' }], + }, + { + path: ['issuing_authority'], + mandatory: true, + display: [{ name: 'Issuing authority', locale: 'en' }], + }, + { + path: ['issuing_authority', 'id'], + mandatory: true, + display: [{ name: 'Issuing authority id', locale: 'en' }], + }, + { + path: ['issuing_authority', 'name'], + mandatory: true, + display: [{ name: 'Issuing authority name', locale: 'en' }], + }, + { + path: ['country'], + mandatory: true, + display: [{ name: 'Issuing country', locale: 'en' }], + }, + { + path: ['date_of_expiry'], + mandatory: false, + display: [{ name: 'Expiry date', locale: 'en' }], + }, + { + path: ['date_of_issuance'], + mandatory: true, + display: [{ name: 'Issue date', locale: 'en' }], + }, + { + path: ['authentic_source'], + mandatory: true, + display: [{ name: 'Competent institution', locale: 'en' }], + }, + { + path: ['authentic_source', 'id'], + mandatory: true, + display: [{ name: 'Competent institution id', locale: 'en' }], + }, + { + path: ['authentic_source', 'name'], + mandatory: true, + display: [{ name: 'Competent institution name', locale: 'en' }], + }, + { + path: ['ending_date'], + mandatory: false, + display: [{ name: 'Ending date', locale: 'en' }], + }, + { + path: ['starting_date'], + mandatory: false, + display: [{ name: 'Starting date', locale: 'en' }], + }, + { + path: ['document_number'], + mandatory: true, + display: [{ name: 'Document number', locale: 'en' }], + }, + ], + }, + }, + openid4vci_version: 'draft 15', +}; -describe("OpenidCredentialIssuerMetadataSchemaEudiw", () => { +describe('OpenidCredentialIssuerMetadataSchemaEudiw', () => { it("should successfully parse Eudiw issuer's metadata", async () => { const res = OpenidCredentialIssuerMetadataSchema.safeParse(metadata); if (res.error) { console.dir(res.error, { depth: null }); } assert(res.success === true); - }) -}) + }); +}); diff --git a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest3.test.ts b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest3.test.ts index 146ecc7..0d23e1a 100644 --- a/src/schemas/OpenidCredentialIssuerMetadataSchemaTest3.test.ts +++ b/src/schemas/OpenidCredentialIssuerMetadataSchemaTest3.test.ts @@ -2,14 +2,172 @@ import { describe, it } from 'vitest'; import { OpenidCredentialIssuerMetadataSchema } from './OpenidCredentialIssuerMetadataSchema'; import assert from 'assert'; -const metadata = {"credential_issuer":"https://issuer.multipaz.org/issuer","credential_endpoint":"https://issuer.multipaz.org/issuer/credential","nonce_endpoint":"https://issuer.multipaz.org/issuer/nonce","authorization_servers":["https://issuer.multipaz.org/issuer"],"display":[{"name":"Multipaz Sample Issuer","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/logo.png"}}],"batch_credential_issuance":{"batch_size":12},"credential_configurations_supported":{"mDL":{"scope":"mDL","format":"mso_mdoc","doctype":"org.iso.18013.5.1.mDL","proof_types_supported":{"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"credential_metadata":{"display":[{"name":"Driver license","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card-mdl.png"}}]}},"pid_mdoc":{"scope":"core","format":"mso_mdoc","doctype":"eu.europa.ec.eudi.pid.1","proof_types_supported":{"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"credential_metadata":{"display":[{"name":"Personal ID","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card-pid.png"}}]}},"pid_sd_jwt":{"scope":"core","format":"dc+sd-jwt","vct":"urn:eudi:pid:1","proof_types_supported":{"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"credential_metadata":{"display":[{"name":"Personal ID","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card-pid.png"}}]}},"utopia_naturalization":{"scope":"naturalization","format":"dc+sd-jwt","vct":"http://utopia.example.com/vct/naturalization","proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]},"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["jwk"],"credential_signing_alg_values_supported":["ES256"],"credential_metadata":{"display":[{"name":"Naturalization certificate","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/naturalization.png"}}]}},"utopia_movie_ticket":{"scope":"movie","format":"dc+sd-jwt","vct":"https://utopia.example.com/vct/movieticket","credential_signing_alg_values_supported":["ES256"],"credential_metadata":{"display":[{"name":"Movie ticket","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/movie_ticket.png"}}]}},"mDoc-AgeVerification":{"scope":"core","format":"mso_mdoc","doctype":"eu.europa.ec.av.1","proof_types_supported":{"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"credential_metadata":{"display":[{"name":"Age verification","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card-age-verification.png"}}]}},"utopia_wholesale":{"scope":"wholesale","format":"mso_mdoc","doctype":"org.multipaz.loyalty.1","proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]},"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"credential_metadata":{"display":[{"name":"Wholesale card","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card_utopia_wholesale.png"}}]}},"payment_sca_mdoc":{"scope":"payment","format":"mso_mdoc","doctype":"org.multipaz.payment.sca.1","proof_types_supported":{"jwt":{"proof_signing_alg_values_supported":["ES256"]},"attestation":{"proof_signing_alg_values_supported":["ES256"]}},"cryptographic_binding_methods_supported":["cose_key"],"credential_signing_alg_values_supported":[-9],"credential_metadata":{"display":[{"name":"Payment card","locale":"en-US","logo":{"uri":"https://issuer.multipaz.org/issuer/card_payment_sca_v2.png"}}]}}}} +const metadata = { + credential_issuer: 'https://issuer.multipaz.org/issuer', + credential_endpoint: 'https://issuer.multipaz.org/issuer/credential', + nonce_endpoint: 'https://issuer.multipaz.org/issuer/nonce', + authorization_servers: ['https://issuer.multipaz.org/issuer'], + display: [ + { + name: 'Multipaz Sample Issuer', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/logo.png' }, + }, + ], + batch_credential_issuance: { batch_size: 12 }, + credential_configurations_supported: { + mDL: { + scope: 'mDL', + format: 'mso_mdoc', + doctype: 'org.iso.18013.5.1.mDL', + proof_types_supported: { attestation: { proof_signing_alg_values_supported: ['ES256'] } }, + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + credential_metadata: { + display: [ + { + name: 'Driver license', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card-mdl.png' }, + }, + ], + }, + }, + pid_mdoc: { + scope: 'core', + format: 'mso_mdoc', + doctype: 'eu.europa.ec.eudi.pid.1', + proof_types_supported: { attestation: { proof_signing_alg_values_supported: ['ES256'] } }, + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + credential_metadata: { + display: [ + { + name: 'Personal ID', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card-pid.png' }, + }, + ], + }, + }, + pid_sd_jwt: { + scope: 'core', + format: 'dc+sd-jwt', + vct: 'urn:eudi:pid:1', + proof_types_supported: { attestation: { proof_signing_alg_values_supported: ['ES256'] } }, + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + credential_metadata: { + display: [ + { + name: 'Personal ID', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card-pid.png' }, + }, + ], + }, + }, + utopia_naturalization: { + scope: 'naturalization', + format: 'dc+sd-jwt', + vct: 'http://utopia.example.com/vct/naturalization', + proof_types_supported: { + jwt: { proof_signing_alg_values_supported: ['ES256'] }, + attestation: { proof_signing_alg_values_supported: ['ES256'] }, + }, + cryptographic_binding_methods_supported: ['jwk'], + credential_signing_alg_values_supported: ['ES256'], + credential_metadata: { + display: [ + { + name: 'Naturalization certificate', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/naturalization.png' }, + }, + ], + }, + }, + utopia_movie_ticket: { + scope: 'movie', + format: 'dc+sd-jwt', + vct: 'https://utopia.example.com/vct/movieticket', + credential_signing_alg_values_supported: ['ES256'], + credential_metadata: { + display: [ + { + name: 'Movie ticket', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/movie_ticket.png' }, + }, + ], + }, + }, + 'mDoc-AgeVerification': { + scope: 'core', + format: 'mso_mdoc', + doctype: 'eu.europa.ec.av.1', + proof_types_supported: { attestation: { proof_signing_alg_values_supported: ['ES256'] } }, + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + credential_metadata: { + display: [ + { + name: 'Age verification', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card-age-verification.png' }, + }, + ], + }, + }, + utopia_wholesale: { + scope: 'wholesale', + format: 'mso_mdoc', + doctype: 'org.multipaz.loyalty.1', + proof_types_supported: { + jwt: { proof_signing_alg_values_supported: ['ES256'] }, + attestation: { proof_signing_alg_values_supported: ['ES256'] }, + }, + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + credential_metadata: { + display: [ + { + name: 'Wholesale card', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card_utopia_wholesale.png' }, + }, + ], + }, + }, + payment_sca_mdoc: { + scope: 'payment', + format: 'mso_mdoc', + doctype: 'org.multipaz.payment.sca.1', + proof_types_supported: { + jwt: { proof_signing_alg_values_supported: ['ES256'] }, + attestation: { proof_signing_alg_values_supported: ['ES256'] }, + }, + cryptographic_binding_methods_supported: ['cose_key'], + credential_signing_alg_values_supported: [-9], + credential_metadata: { + display: [ + { + name: 'Payment card', + locale: 'en-US', + logo: { uri: 'https://issuer.multipaz.org/issuer/card_payment_sca_v2.png' }, + }, + ], + }, + }, + }, +}; -describe("OpenidCredentialIssuerMetadataSchemaEudiw", () => { +describe('OpenidCredentialIssuerMetadataSchemaEudiw', () => { it("should successfully parse Multipaz issuer's metadata", async () => { const res = OpenidCredentialIssuerMetadataSchema.safeParse(metadata); if (res.error) { console.dir(res.error, { depth: null }); } assert(res.success === true); - }) -}) + }); +}); diff --git a/src/schemas/SdJwtVcPayloadSchema.ts b/src/schemas/SdJwtVcPayloadSchema.ts index 9626d8c..f2b3bf4 100644 --- a/src/schemas/SdJwtVcPayloadSchema.ts +++ b/src/schemas/SdJwtVcPayloadSchema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from 'zod'; export const SdJwtVcPayloadSchema = z .object({ @@ -14,7 +14,7 @@ export const SdJwtVcPayloadSchema = z .optional(), vc: z .object({ - "@context": z.array(z.string()).optional(), + '@context': z.array(z.string()).optional(), type: z.array(z.string()).optional(), }) .optional(), diff --git a/src/schemas/SdJwtVcTypeMetadataSchema.ts b/src/schemas/SdJwtVcTypeMetadataSchema.ts index d9a9775..da3acb9 100644 --- a/src/schemas/SdJwtVcTypeMetadataSchema.ts +++ b/src/schemas/SdJwtVcTypeMetadataSchema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from 'zod'; /** Integrity string per W3C SRI, e.g., "sha256-" */ export const IntegrityString = z.string().min(1); @@ -8,103 +8,113 @@ export const Uri = z.string().url(); export const LocaleTag = z.string().min(1); /** Claim path per §9.1: array of string | null | non-negative integer */ -export const ClaimPath = z.array( - z.union([z.string(), z.null(), z.number().int().nonnegative()]) -).nonempty(); +export const ClaimPath = z + .array(z.union([z.string(), z.null(), z.number().int().nonnegative()])) + .nonempty(); /** svg_id: [A-Za-z_][A-Za-z0-9_]* per §8.1.2.2 */ export const SvgId = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/); /** ---------- §8.1.1 "simple" rendering ---------- */ export const LogoMetadata = z.object({ - uri: Uri, // REQUIRED - ["uri#integrity"]: IntegrityString.optional(), + uri: Uri, // REQUIRED + ['uri#integrity']: IntegrityString.optional(), alt_text: z.string().optional(), }); /** ---------- §8.1.2 "svg_templates" rendering ---------- */ export const RenderingSimple = z.object({ logo: LogoMetadata.optional(), - background_image: z.object({ - uri: Uri, - ["uri#integrity"]: IntegrityString.optional(), - }).optional(), + background_image: z + .object({ + uri: Uri, + ['uri#integrity']: IntegrityString.optional(), + }) + .optional(), background_color: z.string().optional(), // CSS color; keep as string - text_color: z.string().optional(), // CSS color; keep as string + text_color: z.string().optional(), // CSS color; keep as string }); -export const SvgTemplateProperties = z.object({ - orientation: z.enum(["portrait", "landscape"]).optional(), - color_scheme: z.enum(["light", "dark"]).optional(), - contrast: z.enum(["normal", "high"]).optional(), -}).refine( - (o) => o.orientation !== undefined || o.color_scheme !== undefined || o.contrast !== undefined, - { message: "svg_template.properties must contain at least one of orientation, color_scheme, contrast" } -); +export const SvgTemplateProperties = z + .object({ + orientation: z.enum(['portrait', 'landscape']).optional(), + color_scheme: z.enum(['light', 'dark']).optional(), + contrast: z.enum(['normal', 'high']).optional(), + }) + .refine( + (o) => o.orientation !== undefined || o.color_scheme !== undefined || o.contrast !== undefined, + { + message: + 'svg_template.properties must contain at least one of orientation, color_scheme, contrast', + }, + ); export const SvgTemplateEntry = z.object({ - uri: Uri, // REQUIRED - ["uri#integrity"]: IntegrityString.optional(), + uri: Uri, // REQUIRED + ['uri#integrity']: IntegrityString.optional(), properties: SvgTemplateProperties.optional(), // REQUIRED if >1 template; enforced at array level }); -const Rendering = z.object({ - simple: RenderingSimple.optional(), - svg_templates: z.array(SvgTemplateEntry).optional(), // optional here -}).superRefine((val, ctx) => { - const arr = val.svg_templates; - if (arr && arr.length > 1) { - arr.forEach((t, i) => { - if (!t.properties) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `rendering.svg_templates[${i}].properties is required when more than one template is present`, - path: ["svg_templates", i, "properties"], - }); - } - }); - } -}); +const Rendering = z + .object({ + simple: RenderingSimple.optional(), + svg_templates: z.array(SvgTemplateEntry).optional(), // optional here + }) + .superRefine((val, ctx) => { + const arr = val.svg_templates; + if (arr && arr.length > 1) { + arr.forEach((t, i) => { + if (!t.properties) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `rendering.svg_templates[${i}].properties is required when more than one template is present`, + path: ['svg_templates', i, 'properties'], + }); + } + }); + } + }); /** ---------- §8 Display metadata for the TYPE ---------- */ export const TypeDisplayEntry = z.object({ - locale: LocaleTag, // REQUIRED - name: z.string().min(1), // REQUIRED + locale: LocaleTag, // REQUIRED + name: z.string().min(1), // REQUIRED description: z.string().optional(), rendering: Rendering.optional(), }); /** ---------- §9.2 Display metadata for CLAIMS ---------- */ export const ClaimDisplayEntry = z.object({ - locale: LocaleTag, // REQUIRED - label: z.string().min(1), // REQUIRED + locale: LocaleTag, // REQUIRED + label: z.string().min(1), // REQUIRED description: z.string().optional(), }); /** ---------- §9 Claim metadata entry ---------- */ export const ClaimMetadataEntry = z.object({ - path: ClaimPath, // REQUIRED - display: z.array(ClaimDisplayEntry).optional(), // §9.2 - mandatory: z.boolean().optional(), // §9.3 d12 - sd: z.enum(["always", "allowed", "never"]).optional(), // §9.3 (default "allowed") - svg_id: SvgId.optional(), // §8.1.2.2 + path: ClaimPath, // REQUIRED + display: z.array(ClaimDisplayEntry).optional(), // §9.2 + mandatory: z.boolean().optional(), // §9.3 d12 + sd: z.enum(['always', 'allowed', 'never']).optional(), // §9.3 (default "allowed") + svg_id: SvgId.optional(), // §8.1.2.2 }); /** ---------- §6.2 Type Metadata Document ---------- */ -export const TypeMetadata = z.object({ - vct: z.string().nonempty("VCT URN cannot be empty"), - name: z.string().optional(), - description: z.string().optional(), +export const TypeMetadata = z + .object({ + vct: z.string().nonempty('VCT URN cannot be empty'), + name: z.string().optional(), + description: z.string().optional(), - extends: z.string().optional(), - ["extends#integrity"]: IntegrityString.optional(), + extends: z.string().optional(), + ['extends#integrity']: IntegrityString.optional(), - display: z.array(TypeDisplayEntry).optional(), // §8 - claims: z.array(ClaimMetadataEntry).optional(), // §9 + display: z.array(TypeDisplayEntry).optional(), // §8 + claims: z.array(ClaimMetadataEntry).optional(), // §9 - // §7 integrity for the vct reference when used - ["vct#integrity"]: IntegrityString.optional(), -}) + // §7 integrity for the vct reference when used + ['vct#integrity']: IntegrityString.optional(), + }) .strip() // remove unknown fields but don't error .superRefine((val, ctx) => { const ids = new Set(); @@ -114,7 +124,7 @@ export const TypeMetadata = z.object({ ctx.addIssue({ code: z.ZodIssueCode.custom, message: `svg_id "${c.svg_id}" must be unique within the type metadata`, - path: ["claims", i, "svg_id"], + path: ['claims', i, 'svg_id'], }); } ids.add(c.svg_id); diff --git a/src/types.ts b/src/types.ts index 438ac38..2d29093 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,19 +1,22 @@ -import { CredentialParsingError } from "./error"; -import type { ClaimMetadataEntry, SvgTemplateProperties } from "./schemas/SdJwtVcTypeMetadataSchema"; +import { CredentialParsingError } from './error'; +import type { + ClaimMetadataEntry, + SvgTemplateProperties, +} from './schemas/SdJwtVcTypeMetadataSchema'; export enum VerifiableCredentialFormat { - VC_SDJWT = "vc+sd-jwt", - DC_SDJWT = "dc+sd-jwt", - MSO_MDOC = "mso_mdoc", - JWT_VC_JSON = "jwt_vc_json" + VC_SDJWT = 'vc+sd-jwt', + DC_SDJWT = 'dc+sd-jwt', + MSO_MDOC = 'mso_mdoc', + JWT_VC_JSON = 'jwt_vc_json', } export enum HashAlgorithm { - sha_256 = "sha-256" + sha_256 = 'sha-256', } export enum DigestHashAlgorithm { - SHA_256 = "SHA-256" + SHA_256 = 'SHA-256', } export type CredentialIssuer = { @@ -21,15 +24,14 @@ export type CredentialIssuer = { name: string; // ...other metadata -} +}; export type CredentialClaims = Record; export type CustomResult = { success: true; value: T } | { success: false; error: E }; export type ParserResult = - | { success: true; value: ParsedCredential } - | { success: false; error: CredentialParsingError }; + { success: true; value: ParsedCredential } | { success: false; error: CredentialParsingError }; export type CredentialPayload = { iss: string; @@ -47,17 +49,14 @@ export type MetadataWarning = { export type CredentialClaimPath = Array; -export type FriendlyNameCallback = ( - preferredLangs?: string[] -) => Promise; +export type FriendlyNameCallback = (preferredLangs?: string[]) => Promise; export type ImageDataUriCallback = ( filter?: Array, preferredLangs?: string[], - svgPreference?: SvgTemplateProperties + svgPreference?: SvgTemplateProperties, ) => Promise; - export type AugmentedClaimMetadataEntry = ClaimMetadataEntry & { required?: boolean; }; @@ -68,30 +67,32 @@ export type TypeMetadataResult = { export type ParsedCredential = { metadata: { - credential: { - format: VerifiableCredentialFormat.VC_SDJWT | VerifiableCredentialFormat.DC_SDJWT, - vct: string, - name: FriendlyNameCallback, - TypeMetadata: TypeMetadataResult, - image: { - dataUri: ImageDataUriCallback, - }, - } | { - format: VerifiableCredentialFormat.MSO_MDOC, - doctype: string, - name: FriendlyNameCallback, - TypeMetadata: TypeMetadataResult, - image: { - dataUri: ImageDataUriCallback, - }, - }, - issuer: CredentialIssuer, - }, + credential: + | { + format: VerifiableCredentialFormat.VC_SDJWT | VerifiableCredentialFormat.DC_SDJWT; + vct: string; + name: FriendlyNameCallback; + TypeMetadata: TypeMetadataResult; + image: { + dataUri: ImageDataUriCallback; + }; + } + | { + format: VerifiableCredentialFormat.MSO_MDOC; + doctype: string; + name: FriendlyNameCallback; + TypeMetadata: TypeMetadataResult; + image: { + dataUri: ImageDataUriCallback; + }; + }; + issuer: CredentialIssuer; + }; validityInfo: { - validUntil?: Date, - validFrom?: Date, - signed?: Date, - } - signedClaims: CredentialClaims, + validUntil?: Date; + validFrom?: Date; + signed?: Date; + }; + signedClaims: CredentialClaims; warnings?: Array; }; diff --git a/src/utils/cborDate.test.ts b/src/utils/cborDate.test.ts index b58bbd6..839ea86 100644 --- a/src/utils/cborDate.test.ts +++ b/src/utils/cborDate.test.ts @@ -1,46 +1,43 @@ -import { assert, describe, it } from "vitest"; -import { formatCborDate, isCborDate } from "./cborDate"; +import { assert, describe, it } from 'vitest'; +import { formatCborDate, isCborDate } from './cborDate'; -describe("isCborDate", () => { - - it("can match a CBOR Date object", () => { +describe('isCborDate', () => { + it('can match a CBOR Date object', () => { const cborDate = { - date: '01-01-2026' + date: '01-01-2026', }; assert(isCborDate(cborDate)); }); - it("can match a CBOR Date object with additional fields", () => { + it('can match a CBOR Date object with additional fields', () => { const cborDate = { date: '01-01-2026', - approximate_mask: 'XXXXXXXX' + approximate_mask: 'XXXXXXXX', }; assert(isCborDate(cborDate)); }); - it("does not match an object that is not a CBOR date", () => { + it('does not match an object that is not a CBOR date', () => { const randomObject = { - foo: 'bar' + foo: 'bar', }; assert(!isCborDate(randomObject)); }); - - it("does not match a simple date string", () => { - const dateString = "04-06-2026"; + it('does not match a simple date string', () => { + const dateString = '04-06-2026'; assert(!isCborDate(dateString)); }); }); -describe("isCborDate", () => { - - it("can format a CBOR Date object", () => { +describe('isCborDate', () => { + it('can format a CBOR Date object', () => { const cborDate = { - date: '01-01-2026' + date: '01-01-2026', }; const formattedDate = formatCborDate(cborDate); @@ -48,7 +45,7 @@ describe("isCborDate", () => { assert(formattedDate === '01/01/2026'); }); - it("does not alter an invalid CBOR object", () => { + it('does not alter an invalid CBOR object', () => { const invalidCborDateObject = { invalid_date: '01-01-2026', }; @@ -60,5 +57,4 @@ describe("isCborDate", () => { assert(JSON.stringify(invalidCborDateObject) === JSON.stringify(formattedDate)); }); - }); diff --git a/src/utils/cborDate.ts b/src/utils/cborDate.ts index e2a2b09..1462cbf 100644 --- a/src/utils/cborDate.ts +++ b/src/utils/cborDate.ts @@ -17,9 +17,7 @@ export const isCborDate = (value: unknown): value is CborDateWrapper => { const dateValue = value.date; return ( - dateValue instanceof Date || - typeof dateValue === 'string' || - typeof dateValue === 'number' + dateValue instanceof Date || typeof dateValue === 'string' || typeof dateValue === 'number' ); }; @@ -30,20 +28,16 @@ export const formatCborDate = ( value: CborDateWrapper, locales: string | string[] = 'en-GB', ): string | object => { - if (value === undefined || value === null) { return value; - }; + } const rawDate = value.date; if (rawDate === undefined) { return value; } - const parsedDate = - rawDate instanceof Date - ? rawDate - : new Date(rawDate); + const parsedDate = rawDate instanceof Date ? rawDate : new Date(rawDate); if (Number.isNaN(parsedDate.getTime())) { return String(rawDate); diff --git a/src/utils/escapeSVG.ts b/src/utils/escapeSVG.ts index 0efa8f0..18b4f89 100644 --- a/src/utils/escapeSVG.ts +++ b/src/utils/escapeSVG.ts @@ -1,14 +1,20 @@ export function escapeSVG(str: string) { - if (typeof str !== "string") return str; + if (typeof str !== 'string') return str; return str.replace(/[<>&"']/g, function (match) { switch (match) { - case '<': return '<'; - case '>': return '>'; - case '&': return '&'; - case '"': return '"'; - case "'": return '''; - default: return match; + case '<': + return '<'; + case '>': + return '>'; + case '&': + return '&'; + case '"': + return '"'; + case "'": + return '''; + default: + return match; } }); } diff --git a/src/utils/getIssuerMetadata.test.ts b/src/utils/getIssuerMetadata.test.ts index 8bae864..1ec1777 100644 --- a/src/utils/getIssuerMetadata.test.ts +++ b/src/utils/getIssuerMetadata.test.ts @@ -1,47 +1,47 @@ -import { describe, it, assert } from "vitest"; -import { getIssuerMetadata } from "../utils/getIssuerMetadata"; -import { HttpClient } from "../interfaces"; -import { CredentialParsingError } from "../error"; -import { MetadataWarning } from "../types"; +import { describe, it, assert } from 'vitest'; +import { getIssuerMetadata } from '../utils/getIssuerMetadata'; +import { HttpClient } from '../interfaces'; +import { CredentialParsingError } from '../error'; +import { MetadataWarning } from '../types'; -describe("getIssuerMetadata", () => { - it("should warn if issuer metadata fetch fails", async () => { +describe('getIssuerMetadata', () => { + it('should warn if issuer metadata fetch fails', async () => { const warnings: MetadataWarning[] = []; const httpClient: HttpClient = { get: async (url: string) => { - if (url.includes(".well-known/openid-credential-issuer")) { + if (url.includes('.well-known/openid-credential-issuer')) { return { status: 400 }; } - throw new Error("Unexpected call"); - } + throw new Error('Unexpected call'); + }, }; - const result = await getIssuerMetadata(httpClient, "https://example.com", warnings); + const result = await getIssuerMetadata(httpClient, 'https://example.com', warnings); assert(result.metadata === null); - assert(warnings.some(w => w.code === CredentialParsingError.FailFetchIssuerMetadata)); + assert(warnings.some((w) => w.code === CredentialParsingError.FailFetchIssuerMetadata)); }); - it("should warn if issuer metadata has invalid schema", async () => { + it('should warn if issuer metadata has invalid schema', async () => { const warnings: MetadataWarning[] = []; const httpClient: HttpClient = { get: async (url: string) => { - if (url.includes(".well-known/openid-credential-issuer")) { + if (url.includes('.well-known/openid-credential-issuer')) { return { status: 200, data: { invalid_field: true, // missing required schema fields - } + }, }; } - throw new Error("Unexpected call"); - } + throw new Error('Unexpected call'); + }, }; - const result = await getIssuerMetadata(httpClient, "https://example.com", warnings); + const result = await getIssuerMetadata(httpClient, 'https://example.com', warnings); assert(result.metadata === null); - assert(warnings.some(w => w.code === CredentialParsingError.FailSchemaIssuerMetadata)); + assert(warnings.some((w) => w.code === CredentialParsingError.FailSchemaIssuerMetadata)); }); }); diff --git a/src/utils/getIssuerMetadata.ts b/src/utils/getIssuerMetadata.ts index a1d7ee2..92f3559 100644 --- a/src/utils/getIssuerMetadata.ts +++ b/src/utils/getIssuerMetadata.ts @@ -1,26 +1,26 @@ -import { z } from "zod"; -import { OpenidCredentialIssuerMetadataSchema } from "../schemas"; -import type { HttpClient } from "../interfaces"; -import { MetadataWarning } from "../types"; -import { CredentialParsingError } from "../error"; -import { prependToPath } from "./urlPathUtils"; +import { z } from 'zod'; +import { OpenidCredentialIssuerMetadataSchema } from '../schemas'; +import type { HttpClient } from '../interfaces'; +import { MetadataWarning } from '../types'; +import { CredentialParsingError } from '../error'; +import { prependToPath } from './urlPathUtils'; export async function getIssuerMetadata( httpClient: HttpClient, issuer: string, warnings: MetadataWarning[], - useCache: boolean = true + useCache: boolean = true, ): Promise<{ metadata: z.infer | null; }> { if (!issuer) return { metadata: null }; - const url = prependToPath(issuer, ".well-known/openid-credential-issuer"); + const url = prependToPath(issuer, '.well-known/openid-credential-issuer'); if (!url) return { metadata: null }; let issuerResponse = null; try { - issuerResponse = await httpClient.get(url, {"Accept": "application/json"}, { useCache }); + issuerResponse = await httpClient.get(url, { Accept: 'application/json' }, { useCache }); } catch (err) { warnings.push({ code: CredentialParsingError.FailFetchIssuerMetadata, diff --git a/src/utils/getSdJwtVcMetadata.test.ts b/src/utils/getSdJwtVcMetadata.test.ts index f7b69bb..f228b92 100644 --- a/src/utils/getSdJwtVcMetadata.test.ts +++ b/src/utils/getSdJwtVcMetadata.test.ts @@ -1,44 +1,52 @@ -import { describe, it, expect } from "vitest"; -import { getSdJwtVcMetadata } from "./getSdJwtVcMetadata"; -import { Context, HttpClient } from "../interfaces"; +import { describe, it, expect } from 'vitest'; +import { getSdJwtVcMetadata } from './getSdJwtVcMetadata'; +import { Context, HttpClient } from '../interfaces'; +import crypto from 'crypto'; -import crypto from "crypto"; - -export function generateSRIFromObject(obj: Record, algorithm: "sha256" | "sha384" | "sha512" = "sha256"): string { +export function generateSRIFromObject( + obj: Record, + algorithm: 'sha256' | 'sha384' | 'sha512' = 'sha256', +): string { const jsonString = JSON.stringify(obj); - const hash = crypto.createHash(algorithm).update(jsonString, "utf8").digest("base64"); + const hash = crypto.createHash(algorithm).update(jsonString, 'utf8').digest('base64'); return `${algorithm}-${hash}`; } -const encodeBase64Url = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url"); +const encodeBase64Url = (obj) => Buffer.from(JSON.stringify(obj)).toString('base64url'); const parentMetadata = { - vct: "https://issuer.com/parent.json", - display: [{ locale: "en-US", name: "Parent Credential", description: "This is the parent metadata." }], - claims: [{ path: ["parent_id"], sd: "always", display: [{ locale: "en-US", label: "Parent ID" }] }] + vct: 'https://issuer.com/parent.json', + display: [ + { locale: 'en-US', name: 'Parent Credential', description: 'This is the parent metadata.' }, + ], + claims: [ + { path: ['parent_id'], sd: 'always', display: [{ locale: 'en-US', label: 'Parent ID' }] }, + ], }; const parentIntegrity = generateSRIFromObject(parentMetadata); const childMetadata = { - vct: "https://issuer.com/child.json", - extends: "https://issuer.com/parent.json", - "extends#integrity": parentIntegrity, - display: [{ locale: "en-US", name: "Child Credential", description: "This is the child metadata." }], - claims: [{ path: ["child_id"], sd: "always", display: [{ locale: "en-US", label: "Child ID" }] }] + vct: 'https://issuer.com/child.json', + extends: 'https://issuer.com/parent.json', + 'extends#integrity': parentIntegrity, + display: [ + { locale: 'en-US', name: 'Child Credential', description: 'This is the child metadata.' }, + ], + claims: [{ path: ['child_id'], sd: 'always', display: [{ locale: 'en-US', label: 'Child ID' }] }], }; -const childIntegrity = "sha256-ilSsfKQ7sIAV8o2aXWOxzotWG6mJNK8TwemSpdFB57k="; -const childVct = "https://issuer.com/child.json"; +const childIntegrity = 'sha256-ilSsfKQ7sIAV8o2aXWOxzotWG6mJNK8TwemSpdFB57k='; +const childVct = 'https://issuer.com/child.json'; -describe("getSdJwtVcMetadata - vct url failure cases", () => { +describe('getSdJwtVcMetadata - vct url failure cases', () => { function createHttpClient({ childMetadataOverride, parentMetadataOverride, - wellKnownIssuer = "https://issuer.com", + wellKnownIssuer = 'https://issuer.com', failChild = false, - failParent = false + failParent = false, }: { childMetadataOverride?: object; parentMetadataOverride?: object; @@ -50,69 +58,77 @@ describe("getSdJwtVcMetadata - vct url failure cases", () => { get: async (url: string) => { const baseResponse = { headers: {} }; - if (url.endsWith("child.json")) { + if (url.endsWith('child.json')) { if (failChild) return { status: 404, data: null }; return { status: 200, data: childMetadataOverride || childMetadata, - ...baseResponse + ...baseResponse, }; } - if (url.endsWith("parent.json")) { + if (url.endsWith('parent.json')) { if (failParent) return { status: 404, data: null }; return { status: 200, data: parentMetadataOverride || parentMetadata, - ...baseResponse + ...baseResponse, }; } return { status: 404, data: null, ...baseResponse }; }, post: async () => { - throw new Error("POST not implemented"); - } + throw new Error('POST not implemented'); + }, }; } - it("warning when fetching main vct fails", async () => { - + it('warning when fetching main vct fails', async () => { const httpClient = createHttpClient({ failChild: true }); - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, httpClient, childVct, childIntegrity); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + httpClient, + childVct, + childIntegrity, + ); if ('warnings' in result) { - expect(result.warnings.some(w => w.code === 'NotFound')).toBe(true); - + expect(result.warnings.some((w) => w.code === 'NotFound')).toBe(true); } else { throw new Error(`Expected result to be success with warnings`); } }); - it("warning when fetching parent metadata (extends) not found", async () => { - + it('warning when fetching parent metadata (extends) not found', async () => { const httpClient = createHttpClient({ childMetadataOverride: { ...childMetadata, - extends: "https://issuer.com/parent.json", - "extends#integrity": parentIntegrity + extends: 'https://issuer.com/parent.json', + 'extends#integrity': parentIntegrity, }, - failParent: true + failParent: true, }); - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, httpClient, childVct, childIntegrity); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + httpClient, + childVct, + childIntegrity, + ); if ('warnings' in result) { - expect(result.warnings.some(w => w.code === 'NotFoundExtends')).toBe(true); - + expect(result.warnings.some((w) => w.code === 'NotFoundExtends')).toBe(true); } else { throw new Error(`Expected result to be success with warnings`); } }); - it("fails with InfiniteRecursion when metadata extends each other", async () => { + it('fails with InfiniteRecursion when metadata extends each other', async () => { const circularParent = { ...parentMetadata, - extends: "https://issuer.com/child.json" + extends: 'https://issuer.com/child.json', }; // Generate integrity for parent with the circular extend @@ -120,8 +136,8 @@ describe("getSdJwtVcMetadata - vct url failure cases", () => { const circularChild = { ...childMetadata, - extends: "https://issuer.com/parent.json", - "extends#integrity": circularParentIntegrity + extends: 'https://issuer.com/parent.json', + 'extends#integrity': circularParentIntegrity, }; const circularChildIntegrity = generateSRIFromObject(circularChild); @@ -130,57 +146,66 @@ describe("getSdJwtVcMetadata - vct url failure cases", () => { parentMetadataOverride: circularParent, }); - const result = await getSdJwtVcMetadata(undefined, crypto.subtle, httpClient, "https://issuer.com/child.json", circularChildIntegrity); - expect(result).toMatchObject({ error: "InfiniteRecursion" }); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + httpClient, + 'https://issuer.com/child.json', + circularChildIntegrity, + ); + expect(result).toMatchObject({ error: 'InfiniteRecursion' }); }); - - it("warning on incorrect vct#integrity", async () => { - const badIntegrity = "sha256-invalidhash==="; + it('warning on incorrect vct#integrity', async () => { + const badIntegrity = 'sha256-invalidhash==='; const httpClient = createHttpClient({ - childMetadataOverride: childMetadata + childMetadataOverride: childMetadata, }); - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, httpClient, "https://issuer.com/child.json",badIntegrity); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + httpClient, + 'https://issuer.com/child.json', + badIntegrity, + ); if ('warnings' in result) { - expect(result.warnings.some(w => w.code === 'IntegrityFail')).toBe(true); - + expect(result.warnings.some((w) => w.code === 'IntegrityFail')).toBe(true); } else { throw new Error(`Expected result to be success with warnings`); } }); - }); function createHttpClient(): HttpClient { return { get: async () => { - throw new Error("Should not be called for vct url or registry tests"); + throw new Error('Should not be called for vct url or registry tests'); }, post: async () => { - throw new Error("POST not implemented"); - } + throw new Error('POST not implemented'); + }, }; } const metadata1 = { - vct: "urn:eudi:pid:1", - display: [{ locale: "en-US", name: "PID", description: "Person ID" }], - claims: [{ path: ["pid"], sd: "always", display: [{ locale: "en-US", label: "PID" }] }] + vct: 'urn:eudi:pid:1', + display: [{ locale: 'en-US', name: 'PID', description: 'Person ID' }], + claims: [{ path: ['pid'], sd: 'always', display: [{ locale: 'en-US', label: 'PID' }] }], }; const metadata2 = { - vct: "urn:eudi:nin:2", - display: [{ locale: "en-US", name: "NIN", description: "National ID" }], - claims: [{ path: ["nin"], sd: "always", display: [{ locale: "en-US", label: "NIN" }] }] + vct: 'urn:eudi:nin:2', + display: [{ locale: 'en-US', name: 'NIN', description: 'National ID' }], + claims: [{ path: ['nin'], sd: 'always', display: [{ locale: 'en-US', label: 'NIN' }] }], }; /** * Decodes a base64url-encoded string to a parsed JSON object. */ export function decodeBase64UrlToObject(base64url: string): unknown { - const json = Buffer.from(base64url, "base64url").toString("utf8"); + const json = Buffer.from(base64url, 'base64url').toString('utf8'); return JSON.parse(json); } @@ -188,126 +213,159 @@ export function decodeBase64UrlToObject(base64url: string): unknown { * Builds a JWT string from header and payload objects. */ export function buildJwtLikeCredential(header: any, payload: any): string { - return [ - encodeBase64Url(header), - encodeBase64Url(payload), - "sig" - ].join("."); + return [encodeBase64Url(header), encodeBase64Url(payload), 'sig'].join('.'); } -describe("getSdJwtVcTypeMetadata - failure cases", () => { - - it("warning when vct is a URN and registry is missing", async () => { - - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, createHttpClient(), "urn:eudi:pid:1",undefined); +describe('getSdJwtVcTypeMetadata - failure cases', () => { + it('warning when vct is a URN and registry is missing', async () => { + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + createHttpClient(), + 'urn:eudi:pid:1', + undefined, + ); if ('warnings' in result) { - expect(result.warnings.some(w => w.code === 'NotFound')).toBe(true); - + expect(result.warnings.some((w) => w.code === 'NotFound')).toBe(true); } else { throw new Error(`Expected result to be success with warnings`); } }); - describe("getSdJwtVcMetadata - rendering.simple validation", () => { + describe('getSdJwtVcMetadata - rendering.simple validation', () => { function httpWithSimpleRendering(child: any): HttpClient { return { get: async (url: string) => { - if (url.endsWith("child.json")) { + if (url.endsWith('child.json')) { return { status: 200, data: child, headers: {} }; } return { status: 404, data: null, headers: {} }; }, - post: async () => { throw new Error("POST not implemented"); } + post: async () => { + throw new Error('POST not implemented'); + }, }; } - it("preserves simple rendering (logo, background_image, colors)", async () => { + it('preserves simple rendering (logo, background_image, colors)', async () => { const childWithSimple = { - vct: "https://issuer.com/child.json", - display: [{ - locale: "en-US", - name: "Child with Simple Rendering", - description: "desc", - rendering: { - simple: { - logo: { uri: "https://issuer.com/logo.svg" }, - background_image: { uri: "https://issuer.com/bg.svg" }, - background_color: "#112233", - text_color: "#ffffff" - } - } - }], - claims: [{ path: ["id"], sd: "always", display: [{ locale: "en-US", label: "ID" }] }] + vct: 'https://issuer.com/child.json', + display: [ + { + locale: 'en-US', + name: 'Child with Simple Rendering', + description: 'desc', + rendering: { + simple: { + logo: { uri: 'https://issuer.com/logo.svg' }, + background_image: { uri: 'https://issuer.com/bg.svg' }, + background_color: '#112233', + text_color: '#ffffff', + }, + }, + }, + ], + claims: [{ path: ['id'], sd: 'always', display: [{ locale: 'en-US', label: 'ID' }] }], }; - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, httpWithSimpleRendering(childWithSimple), "https://issuer.com/child.json",undefined); - - if ("error" in result) throw new Error(`Unexpected error: ${result.error}`); - expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.logo?.uri) - .toBe("https://issuer.com/logo.svg"); - expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.background_image?.uri) - .toBe("https://issuer.com/bg.svg"); - expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.background_color) - .toBe("#112233"); - expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.text_color) - .toBe("#ffffff"); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + httpWithSimpleRendering(childWithSimple), + 'https://issuer.com/child.json', + undefined, + ); + + if ('error' in result) throw new Error(`Unexpected error: ${result.error}`); + expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.logo?.uri).toBe( + 'https://issuer.com/logo.svg', + ); + expect( + result.credentialMetadata?.display?.[0]?.rendering?.simple?.background_image?.uri, + ).toBe('https://issuer.com/bg.svg'); + expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.background_color).toBe( + '#112233', + ); + expect(result.credentialMetadata?.display?.[0]?.rendering?.simple?.text_color).toBe( + '#ffffff', + ); }); }); - describe("getSdJwtVcMetadata - rendering.svg_templates validation", () => { + describe('getSdJwtVcMetadata - rendering.svg_templates validation', () => { function http(child: any): HttpClient { return { get: async (url: string) => { - if (url.endsWith("child.json")) { + if (url.endsWith('child.json')) { return { status: 200, data: child, headers: {} }; } return { status: 404, data: null, headers: {} }; }, - post: async () => { throw new Error("POST not implemented"); } + post: async () => { + throw new Error('POST not implemented'); + }, }; } - it("fails when >1 svg_templates and a template lacks properties", async () => { + it('fails when >1 svg_templates and a template lacks properties', async () => { const bad = { - vct: "https://issuer.com/child.json", - display: [{ - locale: "en-US", - name: "Bad SVG Templates", - rendering: { - svg_templates: [ - { uri: "https://issuer.com/t1.svg", properties: { orientation: "portrait" } }, - { uri: "https://issuer.com/t2.svg" } // ❌ missing properties - ] - } - }], - claims: [{ path: ["id"], sd: "always", display: [{ locale: "en-US", label: "ID" }] }] + vct: 'https://issuer.com/child.json', + display: [ + { + locale: 'en-US', + name: 'Bad SVG Templates', + rendering: { + svg_templates: [ + { uri: 'https://issuer.com/t1.svg', properties: { orientation: 'portrait' } }, + { uri: 'https://issuer.com/t2.svg' }, // ❌ missing properties + ], + }, + }, + ], + claims: [{ path: ['id'], sd: 'always', display: [{ locale: 'en-US', label: 'ID' }] }], }; - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, http(bad), "https://issuer.com/child.json",undefined); - expect(result).toMatchObject({ error: "SchemaShapeFail" }); // schema rejects it + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + http(bad), + 'https://issuer.com/child.json', + undefined, + ); + expect(result).toMatchObject({ error: 'SchemaShapeFail' }); // schema rejects it }); - it("succeeds when >1 svg_templates and all have properties", async () => { + it('succeeds when >1 svg_templates and all have properties', async () => { const ok = { - vct: "https://issuer.com/child.json", - display: [{ - locale: "en-US", - name: "Good SVG Templates", - rendering: { - svg_templates: [ - { uri: "https://issuer.com/t1.svg", properties: { orientation: "portrait" } }, - { uri: "https://issuer.com/t2.svg", properties: { orientation: "landscape", contrast: "high" } } - ] - } - }], - claims: [{ path: ["id"], sd: "always", display: [{ locale: "en-US", label: "ID" }] }] + vct: 'https://issuer.com/child.json', + display: [ + { + locale: 'en-US', + name: 'Good SVG Templates', + rendering: { + svg_templates: [ + { uri: 'https://issuer.com/t1.svg', properties: { orientation: 'portrait' } }, + { + uri: 'https://issuer.com/t2.svg', + properties: { orientation: 'landscape', contrast: 'high' }, + }, + ], + }, + }, + ], + claims: [{ path: ['id'], sd: 'always', display: [{ locale: 'en-US', label: 'ID' }] }], }; - const result = await getSdJwtVcMetadata(undefined,crypto.subtle, http(ok), "https://issuer.com/child.json",undefined); - if ("error" in result) throw new Error(`Unexpected error: ${result.error}`); + const result = await getSdJwtVcMetadata( + undefined, + crypto.subtle, + http(ok), + 'https://issuer.com/child.json', + undefined, + ); + if ('error' in result) throw new Error(`Unexpected error: ${result.error}`); expect(result.credentialMetadata?.display?.[0]?.rendering?.svg_templates?.length).toBe(2); }); }); - }); diff --git a/src/utils/getSdJwtVcMetadata.ts b/src/utils/getSdJwtVcMetadata.ts index 0d204c3..507d536 100644 --- a/src/utils/getSdJwtVcMetadata.ts +++ b/src/utils/getSdJwtVcMetadata.ts @@ -2,7 +2,7 @@ import { HttpClient } from '../interfaces'; import { verifySRIFromObject } from './sri'; import { MetadataError, MetadataWarning } from '../types'; import { CredentialParsingError, isCredentialParsingWarnings } from '../error'; -import { TypeMetadata as TypeMetadataSchema } from "../schemas/SdJwtVcTypeMetadataSchema"; +import { TypeMetadata as TypeMetadataSchema } from '../schemas/SdJwtVcTypeMetadataSchema'; import { VctDocumentProvider } from '../core'; import { prependToPath } from './urlPathUtils'; @@ -11,7 +11,7 @@ function validateTypeMetadataShape( ): { ok: true; data: TypeMetadataSchema } | { ok: false; code: CredentialParsingError } { const res = TypeMetadataSchema.safeParse(metadata); if (!res.success) { - console.error("❌ Type Metadata shape validation failed:", res.error.issues); + console.error('❌ Type Metadata shape validation failed:', res.error.issues); return { ok: false, code: CredentialParsingError.SchemaShapeFail }; } return { ok: true, data: res.data }; @@ -19,9 +19,8 @@ function validateTypeMetadataShape( function handleMetadataCode( code: CredentialParsingError, - warnings: MetadataWarning[] + warnings: MetadataWarning[], ): MetadataError | undefined { - if (isCredentialParsingWarnings(code)) { warnings.push({ code }); return undefined; // continue flow @@ -32,7 +31,6 @@ function handleMetadataCode( } function deepMerge(parent: any, child: any): any { - if (Array.isArray(parent) && Array.isArray(child)) { // Merge display[] by locale if (parent[0]?.locale && child[0]?.locale) { @@ -83,10 +81,14 @@ function deepMerge(parent: any, child: any): any { // Otherwise, merge arrays of objects (default behavior) return [...parent, ...child]; - } - if (typeof parent === 'object' && typeof child === 'object' && parent !== null && child !== null) { + if ( + typeof parent === 'object' && + typeof child === 'object' && + parent !== null && + child !== null + ) { const result: Record = { ...parent }; for (const key of Object.keys(child)) { if (key in parent) { @@ -109,9 +111,8 @@ async function fetchAndMergeMetadata( metadataId: string, visited = new Set(), integrity?: string, - warnings: MetadataWarning[] = [] + warnings: MetadataWarning[] = [], ): Promise { - if (visited.has(metadataId)) { const resultCode = handleMetadataCode(CredentialParsingError.InfiniteRecursion, warnings); if (resultCode) return resultCode; @@ -136,7 +137,6 @@ async function fetchAndMergeMetadata( if (!validated.ok) { const resultCode = handleMetadataCode(validated.code, warnings); if (resultCode) return resultCode; - } else { metadata = validated.data as TypeMetadataSchema; } @@ -148,7 +148,7 @@ async function fetchAndMergeMetadata( const maybe = await vctResolutionEngine.getVctMetadataDocument(metadataId); if (maybe?.ok) { metadata = maybe.value as TypeMetadataSchema; - } else if (maybe?.error === "invalid_schema") { + } else if (maybe?.error === 'invalid_schema') { const resultCode = handleMetadataCode(CredentialParsingError.SchemaShapeFail, warnings); if (resultCode) return resultCode; } @@ -168,7 +168,15 @@ async function fetchAndMergeMetadata( if (typeof metadata.extends === 'string') { const childIntegrity = metadata['extends#integrity'] as string | undefined; - const parent = await fetchAndMergeMetadata(vctResolutionEngine, subtle, httpClient, metadata.extends, visited, childIntegrity, warnings); + const parent = await fetchAndMergeMetadata( + vctResolutionEngine, + subtle, + httpClient, + metadata.extends, + visited, + childIntegrity, + warnings, + ); if (parent === undefined) { const resultCode = handleMetadataCode(CredentialParsingError.NotFoundExtends, warnings); if (resultCode) return resultCode; @@ -182,16 +190,23 @@ async function fetchAndMergeMetadata( return merged; } -export async function resolveIssuerMetadata(httpClient: any, issuerUrl: string): Promise<{ code: CredentialParsingError } | undefined> { +export async function resolveIssuerMetadata( + httpClient: any, + issuerUrl: string, +): Promise<{ code: CredentialParsingError } | undefined> { try { - const result = await httpClient.get(prependToPath(issuerUrl, ".well-known/jwt-vc-issuer"), {}, { useCache: true }) as { + const result = (await httpClient.get( + prependToPath(issuerUrl, '.well-known/jwt-vc-issuer'), + {}, + { useCache: true }, + )) as { data: { issuer: string }; }; if ( result && typeof result === 'object' && - ('data' in result) && + 'data' in result && typeof (result as any).data === 'object' && typeof (result as any).data.issuer === 'string' ) { @@ -215,10 +230,19 @@ function isValidHttpUrl(value: string): boolean { } } -export async function getSdJwtVcMetadata(vctResolutionEngine: VctDocumentProvider | undefined, subtle : SubtleCrypto, httpClient: HttpClient, vct: string, vctIntegrity: string | undefined, warnings: MetadataWarning[] = []): Promise<{ credentialMetadata: TypeMetadataSchema | undefined; warnings: MetadataWarning[] } | MetadataError> { +export async function getSdJwtVcMetadata( + vctResolutionEngine: VctDocumentProvider | undefined, + subtle: SubtleCrypto, + httpClient: HttpClient, + vct: string, + vctIntegrity: string | undefined, + warnings: MetadataWarning[] = [], +): Promise< + | { credentialMetadata: TypeMetadataSchema | undefined; warnings: MetadataWarning[] } + | MetadataError +> { try { if (vct && typeof vct === 'string') { - // TODO: Move to SDJWTVCVerifier // Check jwt-vc-issuer by iss // if (isValidHttpUrl(vct)) { @@ -230,10 +254,18 @@ export async function getSdJwtVcMetadata(vctResolutionEngine: VctDocumentProvide // } try { - const mergedMetadata = await fetchAndMergeMetadata(vctResolutionEngine, subtle, httpClient, vct, new Set(), vctIntegrity, warnings); + const mergedMetadata = await fetchAndMergeMetadata( + vctResolutionEngine, + subtle, + httpClient, + vct, + new Set(), + vctIntegrity, + warnings, + ); if (mergedMetadata) { if ('error' in mergedMetadata) { - return { error: mergedMetadata.error } + return { error: mergedMetadata.error }; } else { return { credentialMetadata: mergedMetadata, warnings }; } @@ -247,8 +279,7 @@ export async function getSdJwtVcMetadata(vctResolutionEngine: VctDocumentProvide warnings.push({ code: CredentialParsingError.NotFound }); return { credentialMetadata: undefined, warnings }; - } - catch (err) { + } catch (err) { console.log(err); return { error: CredentialParsingError.UnknownError }; } diff --git a/src/utils/matchLocalizedDisplay.ts b/src/utils/matchLocalizedDisplay.ts index 44f170e..e11bbcf 100644 --- a/src/utils/matchLocalizedDisplay.ts +++ b/src/utils/matchLocalizedDisplay.ts @@ -2,15 +2,14 @@ type DisplayWithLocale = { locale?: string }; export function matchDisplayByLocale( arr: T[] | undefined, - preferredLangs: string[] + preferredLangs: string[], ): T | null { if (!Array.isArray(arr)) return null; for (const lang of preferredLangs) { - const match = arr.find(d => - d.locale === lang || - d.locale?.startsWith(lang + '-') || - lang?.startsWith(d.locale + '-') + const match = arr.find( + (d) => + d.locale === lang || d.locale?.startsWith(lang + '-') || lang?.startsWith(d.locale + '-'), ); if (match) return match; } diff --git a/src/utils/serializeDcqlQuery.ts b/src/utils/serializeDcqlQuery.ts index 59c6dec..0fa5efa 100644 --- a/src/utils/serializeDcqlQuery.ts +++ b/src/utils/serializeDcqlQuery.ts @@ -10,7 +10,7 @@ export function serializeDcqlQuery(obj: any): any { return Object.fromEntries( Object.entries(obj) .filter(([key, _]) => !key.startsWith('_')) - .map(([key, value]) => [key, serializeDcqlQuery(value)]) + .map(([key, value]) => [key, serializeDcqlQuery(value)]), ); } return obj; diff --git a/src/utils/sri.test.ts b/src/utils/sri.test.ts index 2ea41ae..c6c1745 100644 --- a/src/utils/sri.test.ts +++ b/src/utils/sri.test.ts @@ -10,10 +10,7 @@ import { SubtleAlgorithm, } from './sri'; -const ALGORITHMS = Object.entries(sriToSubtleAlgorithm) as [ - sriAlgorithm, - SubtleAlgorithm, -][]; +const ALGORITHMS = Object.entries(sriToSubtleAlgorithm) as [sriAlgorithm, SubtleAlgorithm][]; function mockDigestFromString(str: string): ArrayBuffer { const encoder = new TextEncoder(); @@ -21,8 +18,7 @@ function mockDigestFromString(str: string): ArrayBuffer { } function mockBtoa() { - (globalThis as any).btoa = (input: string) => - Buffer.from(input, 'binary').toString('base64'); + (globalThis as any).btoa = (input: string) => Buffer.from(input, 'binary').toString('base64'); } function createMockSubtle(expectedDigest: ArrayBuffer) { @@ -54,67 +50,42 @@ describe('sriUtils', () => { it('returns SRI string with correct prefix', async () => { const subtle = createMockSubtle(mockDigestFromString('hello')); - const result = await calculateDataSRI( - subtle, - new TextEncoder().encode('test'), - 'SHA-256' - ); + const result = await calculateDataSRI(subtle, new TextEncoder().encode('test'), 'SHA-256'); expect(result.startsWith('sha256-')).toBe(true); - expect(subtle.digest).toHaveBeenCalledWith( - 'SHA-256', - expect.any(Uint8Array) - ); + expect(subtle.digest).toHaveBeenCalledWith('SHA-256', expect.any(Uint8Array)); }); it('produces different hashes for different input', async () => { const subtleA = createMockSubtle(mockDigestFromString('a')); const subtleB = createMockSubtle(mockDigestFromString('b')); - const r1 = await calculateDataSRI( - subtleA, - new TextEncoder().encode('a'), - 'SHA-256' - ); + const r1 = await calculateDataSRI(subtleA, new TextEncoder().encode('a'), 'SHA-256'); - const r2 = await calculateDataSRI( - subtleB, - new TextEncoder().encode('b'), - 'SHA-256' - ); + const r2 = await calculateDataSRI(subtleB, new TextEncoder().encode('b'), 'SHA-256'); expect(r1).not.toBe(r2); }); - it.each( - Object.entries(sriToSubtleAlgorithm) as Array< - [sriAlgorithm, SubtleAlgorithm] - > - )('supports %s algorithm', async (sriAlgo, subtleAlgo) => { - const subtle = createMockSubtle(mockDigestFromString('test')); - - const result = await calculateDataSRI( - subtle, - new TextEncoder().encode('data'), - subtleAlgo - ); + it.each(Object.entries(sriToSubtleAlgorithm) as Array<[sriAlgorithm, SubtleAlgorithm]>)( + 'supports %s algorithm', + async (sriAlgo, subtleAlgo) => { + const subtle = createMockSubtle(mockDigestFromString('test')); - expect(result.startsWith(`${sriAlgo}-`)).toBe(true); + const result = await calculateDataSRI(subtle, new TextEncoder().encode('data'), subtleAlgo); - expect(subtle.digest).toHaveBeenCalledWith( - subtleAlgo, - expect.any(Uint8Array) - ); - }); + expect(result.startsWith(`${sriAlgo}-`)).toBe(true); + + expect(subtle.digest).toHaveBeenCalledWith(subtleAlgo, expect.any(Uint8Array)); + }, + ); }); describe('calculateObjectSRI', () => { it('hashes JSON stringified object', async () => { const obj = { a: 1, b: 'test' }; - const subtle = createMockSubtle( - mockDigestFromString(JSON.stringify(obj)) - ); + const subtle = createMockSubtle(mockDigestFromString(JSON.stringify(obj))); const result = await calculateObjectSRI(subtle, obj); @@ -134,35 +105,27 @@ describe('sriUtils', () => { expect(r1).toBe(r2); }); - it.each( - Object.entries(sriToSubtleAlgorithm) as Array< - [sriAlgorithm, SubtleAlgorithm] - > - )('supports %s algorithm', async (sriAlgo, subtleAlgo) => { - const obj = { value: 123 }; + it.each(Object.entries(sriToSubtleAlgorithm) as Array<[sriAlgorithm, SubtleAlgorithm]>)( + 'supports %s algorithm', + async (sriAlgo, subtleAlgo) => { + const obj = { value: 123 }; - const subtle = createMockSubtle( - mockDigestFromString(JSON.stringify(obj)) - ); + const subtle = createMockSubtle(mockDigestFromString(JSON.stringify(obj))); - const result = await calculateObjectSRI(subtle, obj, subtleAlgo); + const result = await calculateObjectSRI(subtle, obj, subtleAlgo); - expect(result.startsWith(`${sriAlgo}-`)).toBe(true); + expect(result.startsWith(`${sriAlgo}-`)).toBe(true); - expect(subtle.digest).toHaveBeenCalledWith( - subtleAlgo, - expect.any(Uint8Array) - ); - }); + expect(subtle.digest).toHaveBeenCalledWith(subtleAlgo, expect.any(Uint8Array)); + }, + ); }); describe('verifySRIFromObject', () => { it('returns true when integrity matches', async () => { const obj = { x: 42 }; - const subtle = createMockSubtle( - mockDigestFromString(JSON.stringify(obj)) - ); + const subtle = createMockSubtle(mockDigestFromString(JSON.stringify(obj))); const sri = await calculateObjectSRI(subtle, obj); @@ -174,11 +137,7 @@ describe('sriUtils', () => { it('returns false when integrity does not match', async () => { const subtle = createMockSubtle(mockDigestFromString('x')); - const result = await verifySRIFromObject( - subtle, - { a: 1 }, - 'sha256-wronghash' - ); + const result = await verifySRIFromObject(subtle, { a: 1 }, 'sha256-wronghash'); expect(result).toBe(false); }); @@ -186,17 +145,17 @@ describe('sriUtils', () => { it('throws on invalid integrity format', async () => { const subtle = createMockSubtle(mockDigestFromString('x')); - await expect( - verifySRIFromObject(subtle, { a: 1 }, 'invalidformat') - ).rejects.toThrow('Invalid integrity string format'); + await expect(verifySRIFromObject(subtle, { a: 1 }, 'invalidformat')).rejects.toThrow( + 'Invalid integrity string format', + ); }); it('throws on unsupported algorithm', async () => { const subtle = createMockSubtle(mockDigestFromString('x')); - await expect( - verifySRIFromObject(subtle, { a: 1 }, 'md5-abc123') - ).rejects.toThrow('Unsupported algorithm'); + await expect(verifySRIFromObject(subtle, { a: 1 }, 'md5-abc123')).rejects.toThrow( + 'Unsupported algorithm', + ); }); }); }); diff --git a/src/utils/sri.ts b/src/utils/sri.ts index 6d2cd06..028e145 100644 --- a/src/utils/sri.ts +++ b/src/utils/sri.ts @@ -17,21 +17,19 @@ export const subtleToSriAlgorithm: Record = { export async function calculateObjectSRI( subtle: SubtleCrypto, obj: Record, - algorithm: SubtleAlgorithm = 'SHA-256' + algorithm: SubtleAlgorithm = 'SHA-256', ): Promise { - const jsonString = JSON.stringify(obj); const encoder = new TextEncoder(); const data = encoder.encode(jsonString); return calculateDataSRI(subtle, data, algorithm); - } export async function calculateDataSRI( subtle: SubtleCrypto, data: Uint8Array | Buffer, - algorithm: SubtleAlgorithm = 'SHA-256' + algorithm: SubtleAlgorithm = 'SHA-256', ): Promise { const digest = await subtle.digest(algorithm, data as Uint8Array); const hashArray = Array.from(new Uint8Array(digest)); diff --git a/src/utils/urlPathUtils.test.ts b/src/utils/urlPathUtils.test.ts index 214e5b9..865b0b9 100644 --- a/src/utils/urlPathUtils.test.ts +++ b/src/utils/urlPathUtils.test.ts @@ -1,49 +1,67 @@ -import { describe, it, assert } from "vitest"; -import { prependToPath } from "./urlPathUtils"; +import { describe, it, assert } from 'vitest'; +import { prependToPath } from './urlPathUtils'; -describe("prependToPath", () => { - it("should insert a path segment between the host and the existing path", async () => { - const result = prependToPath("https://example.com/bar", "foo"); - assert(result === "https://example.com/foo/bar"); +describe('prependToPath', () => { + it('should insert a path segment between the host and the existing path', async () => { + const result = prependToPath('https://example.com/bar', 'foo'); + assert(result === 'https://example.com/foo/bar'); }); - it("should handle a URL with an empty path", async () => { - const result = prependToPath("https://example.com", "foo"); - assert(result === "https://example.com/foo"); + it('should handle a URL with an empty path', async () => { + const result = prependToPath('https://example.com', 'foo'); + assert(result === 'https://example.com/foo'); }); - it("should handle an empty path segment", async () => { - const result = prependToPath("https://example.com/foo", ""); - assert(result === "https://example.com/foo"); + it('should handle an empty path segment', async () => { + const result = prependToPath('https://example.com/foo', ''); + assert(result === 'https://example.com/foo'); }); - it("should return null for an invalid URL", async () => { - const result = prependToPath("not a url", "test"); + it('should return null for an invalid URL', async () => { + const result = prependToPath('not a url', 'test'); assert(result === null); }); - it("should handle multiple path segments in the segment", async () => { - const result = prependToPath("https://example.com/issuer", ".well-known/oauth-authorization-server"); - assert(result === "https://example.com/.well-known/oauth-authorization-server/issuer"); + it('should handle multiple path segments in the segment', async () => { + const result = prependToPath( + 'https://example.com/issuer', + '.well-known/oauth-authorization-server', + ); + assert(result === 'https://example.com/.well-known/oauth-authorization-server/issuer'); }); - it("should handle multiple path segments in the URL path", async () => { - const result = prependToPath("https://example.com/idp/realms/issuer", ".well-known/openid-credential-issuer"); - assert(result === "https://example.com/.well-known/openid-credential-issuer/idp/realms/issuer"); + it('should handle multiple path segments in the URL path', async () => { + const result = prependToPath( + 'https://example.com/idp/realms/issuer', + '.well-known/openid-credential-issuer', + ); + assert(result === 'https://example.com/.well-known/openid-credential-issuer/idp/realms/issuer'); }); - it("should handle a trailing slash in the URL", async () => { - const result = prependToPath("https://example.com/test/", "/.well-known/oauth-authorization-server/"); - assert(result === "https://example.com/.well-known/oauth-authorization-server/test"); + it('should handle a trailing slash in the URL', async () => { + const result = prependToPath( + 'https://example.com/test/', + '/.well-known/oauth-authorization-server/', + ); + assert(result === 'https://example.com/.well-known/oauth-authorization-server/test'); }); - it("should handle leading/trailing slashes in the segment", async () => { - const result = prependToPath("https://example.com/test", "/.well-known/oauth-authorization-server/"); - assert(result === "https://example.com/.well-known/oauth-authorization-server/test"); + it('should handle leading/trailing slashes in the segment', async () => { + const result = prependToPath( + 'https://example.com/test', + '/.well-known/oauth-authorization-server/', + ); + assert(result === 'https://example.com/.well-known/oauth-authorization-server/test'); }); - it("should handle a URL with query parameters and hash fragment", async () => { - const result = prependToPath("https://example.com/test?query=param#fragment", ".well-known/oauth-authorization-server"); - assert(result === "https://example.com/.well-known/oauth-authorization-server/test?query=param#fragment"); + it('should handle a URL with query parameters and hash fragment', async () => { + const result = prependToPath( + 'https://example.com/test?query=param#fragment', + '.well-known/oauth-authorization-server', + ); + assert( + result === + 'https://example.com/.well-known/oauth-authorization-server/test?query=param#fragment', + ); }); }); diff --git a/src/utils/urlPathUtils.ts b/src/utils/urlPathUtils.ts index 05789f9..7d980ca 100644 --- a/src/utils/urlPathUtils.ts +++ b/src/utils/urlPathUtils.ts @@ -9,7 +9,6 @@ * @example prependToPath("https://example.com/", ".well-known/openid-credential-issuer") => "https://example.com/.well-known/openid-credential-issuer" */ export function prependToPath(urlString: string, segment: string): string | null { - if (!urlString) return null; if (!segment) return urlString; @@ -20,12 +19,12 @@ export function prependToPath(urlString: string, segment: string): string | null return null; } - const pathSegments = url.pathname.split("/").filter(Boolean); + const pathSegments = url.pathname.split('/').filter(Boolean); - const newSegment = segment.split("/").filter(Boolean); + const newSegment = segment.split('/').filter(Boolean); const newPathSegments = [...newSegment, ...pathSegments]; - url.pathname = "/" + newPathSegments.join("/").replace(/^\/+/, ""); + url.pathname = '/' + newPathSegments.join('/').replace(/^\/+/, ''); return url.toString(); } diff --git a/src/utils/util.ts b/src/utils/util.ts index fc74ce6..ae04bd2 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -9,7 +9,7 @@ function toU8(b: BufferSource) { export function toBase64(binary: BufferSource): string { const uint8Array = toU8(binary); const chunkSize = 0x8000; // 32KB - let result = ""; + let result = ''; for (let i = 0; i < uint8Array.length; i += chunkSize) { const chunk = uint8Array.subarray(i, i + chunkSize); result += String.fromCharCode(...chunk); @@ -18,10 +18,7 @@ export function toBase64(binary: BufferSource): string { } export function toBase64Url(binary: BufferSource): string { - return toBase64(binary) - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); + return toBase64(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } export function byteArrayEquals(a: BufferSource, b: BufferSource): boolean { @@ -33,33 +30,29 @@ function base64pad(s: string): string { if (m === 0) { return s; } else if (m === 2) { - return s + "=="; + return s + '=='; } else if (m === 3) { - return s + "="; + return s + '='; } else { - throw Error("Invalid length of Base64 encoded data"); + throw Error('Invalid length of Base64 encoded data'); } } export function fromBase64(s: string): Uint8Array { - return new Uint8Array( - Array.from(atob(base64pad(s))).map((c) => c.charCodeAt(0)), - ); + return new Uint8Array(Array.from(atob(base64pad(s))).map((c) => c.charCodeAt(0))); } export function fromBase64Url(s: string): Uint8Array { - return fromBase64(s.replace(/-/g, "+").replace(/_/g, "/")); + return fromBase64(s.replace(/-/g, '+').replace(/_/g, '/')); } export function base64urlToBytes(base64urlValue: string): Uint8Array { - const base64 = base64urlValue.replace(/-/g, "+").replace(/_/g, "/"); + const base64 = base64urlValue.replace(/-/g, '+').replace(/_/g, '/'); return fromBase64(base64); } export function generateRandomIdentifier(length: number) { const array = new Uint8Array(length); crypto.getRandomValues(array); - return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( - "", - ); + return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(''); } diff --git a/src/utils/verifyCertificate.ts b/src/utils/verifyCertificate.ts index b707659..a161e9c 100644 --- a/src/utils/verifyCertificate.ts +++ b/src/utils/verifyCertificate.ts @@ -1,9 +1,13 @@ -import * as pkijs from "pkijs"; -import * as asn1js from "asn1js"; -import { Buffer } from "buffer"; +import * as pkijs from 'pkijs'; +import * as asn1js from 'asn1js'; +import { Buffer } from 'buffer'; const webcrypto = globalThis.crypto; -pkijs.setEngine("webcrypto", webcrypto, new pkijs.CryptoEngine({ name: "", crypto: webcrypto, subtle: webcrypto.subtle })) +pkijs.setEngine( + 'webcrypto', + webcrypto, + new pkijs.CryptoEngine({ name: '', crypto: webcrypto, subtle: webcrypto.subtle }), +); /** * Convert a PEM certificate to an ArrayBuffer @@ -11,8 +15,8 @@ pkijs.setEngine("webcrypto", webcrypto, new pkijs.CryptoEngine({ name: "", crypt * @returns {ArrayBuffer} */ function pemToArrayBuffer(pem: string) { - const base64 = pem.replace(/(-----(BEGIN|END) CERTIFICATE-----|\s)/g, ""); - const binaryString = Buffer.from(base64, "base64").toString("binary"); + const base64 = pem.replace(/(-----(BEGIN|END) CERTIFICATE-----|\s)/g, ''); + const binaryString = Buffer.from(base64, 'base64').toString('binary'); const byteArray = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { byteArray[i] = binaryString.charCodeAt(i); diff --git a/src/utils/verifyX5C.test.ts b/src/utils/verifyX5C.test.ts index 25353c6..af9ba8e 100644 --- a/src/utils/verifyX5C.test.ts +++ b/src/utils/verifyX5C.test.ts @@ -1,9 +1,9 @@ -import { afterEach, assert, beforeEach, describe, it, vi } from "vitest"; -import { verifyX5C } from "./verifyX5C"; +import { afterEach, assert, beforeEach, describe, it, vi } from 'vitest'; +import { verifyX5C } from './verifyX5C'; // this x5c is signed by the first element in the trustedCertificates array const x5c = [ - "MIICyzCCAnGgAwIBAgIULnrxux9sI34oqbby3M4lSKOs8owwCgYIKoZIzj0EAwIwPzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQd3dXYWxsZXQgUm9vdCBDQTAeFw0yNTA0MjkxMDI5NTNaFw0yNjA0MjkxMDI5NTNaMEExCzAJBgNVBAYTAkVVMRUwEwYDVQQKDAx3d1dhbGxldC5vcmcxGzAZBgNVBAMMEmxvY2FsLnd3d2FsbGV0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFVivGt53M4qEP06QT20BSlGiMIdzLLvG+b9fq/fHKM+NGT+a3snXiPwU7X7jrOFWxwyjZeean40+vx6Gy06VfqjggFHMIIBQzAdBgNVHQ4EFgQUM/A3FTQLjww5/9u01MX/SRyVqaUwHwYDVR0jBBgwFoAU0HGu3T+/Wqh3yNifz9sNd+HPBS4wDgYDVR0PAQH/BAQDAgeAMDIGA1UdEgQrMCmBEWluZm9Ad3d3YWxsZXQub3JnhhRodHRwczovL3d3d2FsbGV0Lm9yZzASBgNVHSUECzAJBgcogYxdBQECMAwGA1UdEwEB/wQCMAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3dhbGxldC5vcmcvaWFjYS9jcmwvd3d3YWxsZXRfb3JnX2lhY2EuY3JsMFUGA1UdEQROMEyCEmxvY2FsLnd3d2FsbGV0Lm9yZ4IZbG9jYWwtaXNzdWVyLnd3d2FsbGV0Lm9yZ4IbbG9jYWwtdmVyaWZpZXIud3d3YWxsZXQub3JnMAoGCCqGSM49BAMCA0gAMEUCIQCQ8h+5krhO+f4woReDY1D7CaM6qCda3m814e6DLvOphAIgHQL+Wm7WFRwxgjzMLN37RojJGrZbF4OFChIkmm0uu5o=" + 'MIICyzCCAnGgAwIBAgIULnrxux9sI34oqbby3M4lSKOs8owwCgYIKoZIzj0EAwIwPzELMAkGA1UEBhMCRVUxFTATBgNVBAoMDHd3V2FsbGV0Lm9yZzEZMBcGA1UEAwwQd3dXYWxsZXQgUm9vdCBDQTAeFw0yNTA0MjkxMDI5NTNaFw0yNjA0MjkxMDI5NTNaMEExCzAJBgNVBAYTAkVVMRUwEwYDVQQKDAx3d1dhbGxldC5vcmcxGzAZBgNVBAMMEmxvY2FsLnd3d2FsbGV0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFVivGt53M4qEP06QT20BSlGiMIdzLLvG+b9fq/fHKM+NGT+a3snXiPwU7X7jrOFWxwyjZeean40+vx6Gy06VfqjggFHMIIBQzAdBgNVHQ4EFgQUM/A3FTQLjww5/9u01MX/SRyVqaUwHwYDVR0jBBgwFoAU0HGu3T+/Wqh3yNifz9sNd+HPBS4wDgYDVR0PAQH/BAQDAgeAMDIGA1UdEgQrMCmBEWluZm9Ad3d3YWxsZXQub3JnhhRodHRwczovL3d3d2FsbGV0Lm9yZzASBgNVHSUECzAJBgcogYxdBQECMAwGA1UdEwEB/wQCMAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cHM6Ly93d3dhbGxldC5vcmcvaWFjYS9jcmwvd3d3YWxsZXRfb3JnX2lhY2EuY3JsMFUGA1UdEQROMEyCEmxvY2FsLnd3d2FsbGV0Lm9yZ4IZbG9jYWwtaXNzdWVyLnd3d2FsbGV0Lm9yZ4IbbG9jYWwtdmVyaWZpZXIud3d3YWxsZXQub3JnMAoGCCqGSM49BAMCA0gAMEUCIQCQ8h+5krhO+f4woReDY1D7CaM6qCda3m814e6DLvOphAIgHQL+Wm7WFRwxgjzMLN37RojJGrZbF4OFChIkmm0uu5o=', ]; const trustedCertificates = [ @@ -36,29 +36,27 @@ cHN0YW1hdG9wL2NhLWNybC5wZW0wIAYDVR0RBBkwF4YVaHR0cDovL3d3dy53YWxs ZXQub3JnMCoGA1UdEgQjMCGGFWh0dHA6Ly93d3cud2FsbGV0LmNvbYIId3dXYWxs ZXQwCgYIKoZIzj0EAwIDRwAwRAIgGMfgLwOXvEk0sD3nEtCuwkZRzX9vyYZ/hfg6 VPrJszACIHBsYf7toXfUFjr6y1nAJ/oXP9l/fWBDydcQIq+Vnfem ------END CERTIFICATE-----` +-----END CERTIFICATE-----`, ]; - - -describe("verifyX5C function", () => { +describe('verifyX5C function', () => { beforeEach(() => { // Keep cert validity checks deterministic regardless of current date vi.useFakeTimers(); - vi.setSystemTime(new Date("2025-10-01T00:00:00.000Z")); + vi.setSystemTime(new Date('2025-10-01T00:00:00.000Z')); }); afterEach(() => { vi.useRealTimers(); }); - it("should verify successfully the chain", async () => { + it('should verify successfully the chain', async () => { const result = await verifyX5C(x5c, trustedCertificates); assert(result === true); - }) + }); - it("should result in invalid chain", async () => { - const result = await verifyX5C(x5c, [ trustedCertificates[1] ]); // include only the second + it('should result in invalid chain', async () => { + const result = await verifyX5C(x5c, [trustedCertificates[1]]); // include only the second assert(result === false); - }) -}) + }); +}); diff --git a/src/utils/verifyX5C.ts b/src/utils/verifyX5C.ts index a454411..1761b0c 100644 --- a/src/utils/verifyX5C.ts +++ b/src/utils/verifyX5C.ts @@ -1,4 +1,4 @@ -import { verifyCertificate } from "./verifyCertificate"; +import { verifyCertificate } from './verifyCertificate'; export async function verifyX5C(x5c: string[], trustedCertificates: string[]): Promise { if (x5c.length === 0) { @@ -6,8 +6,13 @@ export async function verifyX5C(x5c: string[], trustedCertificates: string[]): P } const lastCertificate: string = x5c[x5c.length - 1]; const lastCertificatePem = `-----BEGIN CERTIFICATE-----\n${lastCertificate}\n-----END CERTIFICATE-----`; - const certificateValidationResult = await verifyCertificate(lastCertificatePem, trustedCertificates); - const lastCertificateIsRootCa = trustedCertificates.map((c) => c.trim()).includes(lastCertificatePem); + const certificateValidationResult = await verifyCertificate( + lastCertificatePem, + trustedCertificates, + ); + const lastCertificateIsRootCa = trustedCertificates + .map((c) => c.trim()) + .includes(lastCertificatePem); if (!certificateValidationResult && !lastCertificateIsRootCa) { return false; diff --git a/tsconfig.json b/tsconfig.json index b97504a..e1fb415 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,11 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": [ "es2022", "dom" ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": [ + "es2022", + "dom" + ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -25,14 +28,16 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./src", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, + "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": [ "node" ], /* Specify type package names to be included without being referenced in a source file. */ + "types": [ + "node" + ] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ @@ -51,14 +56,14 @@ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */, // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + "inlineSourceMap": true /* Include sourcemap files inside the emitted JavaScript. */, // "noEmit": true, /* Disable emitting files from a compilation. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, // "removeComments": true, /* Disable emitting comments. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ @@ -78,12 +83,12 @@ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -106,8 +111,8 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": [ "src/**/*" ], - "exclude": [ "node_modules", "dist", "src/**/*.test.ts" ] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "src/**/*.test.ts"] } From 4e9c02c04f7ada2583e8081b68183b358950bd41 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 13:39:13 +0300 Subject: [PATCH 05/10] ci: check Prettier and ESLint in pre-commit and CI --- .github/workflows/code-formatting.yml | 25 +++++++++++++++++++++++++ .pre-commit-config.yaml | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/.github/workflows/code-formatting.yml b/.github/workflows/code-formatting.yml index 1f0dcc3..f12bc9c 100644 --- a/.github/workflows/code-formatting.yml +++ b/.github/workflows/code-formatting.yml @@ -33,3 +33,28 @@ jobs: - name: Check code formatting run: editorconfig-checker -no-color + + prettier-eslint: + name: Check Prettier and ESLint + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v7 + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: yarn + + - name: Install dependencies + env: + YARN_NETWORK_CONCURRENCY: '1' + run: yarn install --frozen-lockfile --ignore-scripts + + - name: Check Prettier formatting + run: yarn format:check + + - name: Check ESLint rules + run: yarn lint diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 91c5022..0ea282c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,3 +13,17 @@ repos: name: Check .editorconfig rules args: ['-no-color'] types_or: [text] + + - repo: local + hooks: + - id: prettier + name: Check Prettier formatting + entry: yarn format:check + language: system + pass_filenames: false + + - id: eslint + name: Check ESLint rules + entry: yarn lint + language: system + pass_filenames: false From bbca244d118fb703369de4dc133bafae0aa079f5 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Wed, 21 Oct 2026 15:13:52 +0300 Subject: [PATCH 06/10] chore: add typecheck to pre-commit --- .pre-commit-config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ea282c..9ed2254 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,3 +27,9 @@ repos: entry: yarn lint language: system pass_filenames: false + + - id: typecheck + name: Check TypeScript types + entry: yarn typecheck + language: system + pass_filenames: false From c70ed3865d7eac2c9f1fcb3d55497daab0461938 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Mon, 26 Oct 2026 09:10:20 +0200 Subject: [PATCH 07/10] chore(ci): remove Yarn network concurrency limit --- .github/workflows/code-formatting.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/code-formatting.yml b/.github/workflows/code-formatting.yml index f12bc9c..79eab14 100644 --- a/.github/workflows/code-formatting.yml +++ b/.github/workflows/code-formatting.yml @@ -49,8 +49,6 @@ jobs: cache: yarn - name: Install dependencies - env: - YARN_NETWORK_CONCURRENCY: '1' run: yarn install --frozen-lockfile --ignore-scripts - name: Check Prettier formatting From d86a54479b3d66a95285c57af818b0d7216a67ba Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Tue, 27 Oct 2026 11:37:24 +0200 Subject: [PATCH 08/10] fix(format): exclude JS/TS files from editorconfig-checker --- .editorconfig-checker.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index 6a8e7ce..dcfaa6f 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -1,4 +1,3 @@ { - "SpacesAfterTabs": true, - "Exclude": ["^(node_modules|dist|output|build)/"] + "Exclude": ["^(node_modules|dist|output|build)/", "\\.(mjs|js|mts|ts|tsx)$"] } From 5ca9da114752a924db59e72ba728ce0b1ed1f992 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Tue, 27 Oct 2026 11:47:00 +0200 Subject: [PATCH 09/10] fix(format): align Prettier and EditorConfig checks --- eslint.config.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 3220238..27e16f1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -42,4 +42,9 @@ export default defineConfig( }, }, prettierConfig, + { + rules: { + 'no-unexpected-multiline': 'error', + }, + }, ); From 60b012fa4d4813ff3af7a8b99813e3d5386e23b5 Mon Sep 17 00:00:00 2001 From: gkatrakazas Date: Tue, 27 Oct 2026 13:16:01 +0200 Subject: [PATCH 10/10] fix(format): apply Prettier formatting to README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a48ceee..49b022d 100644 --- a/README.md +++ b/README.md @@ -77,11 +77,11 @@ type(optional-scope): lower-case summary wallet-common follows [semantic versioning](https://semver.org/). Every merge to `master` runs the `release` workflow, which uses [semantic-release](https://github.com/semantic-release/semantic-release) to work out the next version from the commits merged since the last `v*` tag (PR title and description). If there are several, the largest bump wins: | PR title | Release (e.g. from `1.0.1`) | -| -------------------------------------------------- | ---------------------------- | -| any type with `!`, e.g. `feat!: …` | major (`2.0.0`) | -| `feat: …` | minor (`1.1.0`) | -| `fix: …`, `perf: …` | patch (`1.0.2`) | -| `docs`, `test`, `ci`, `build`, `refactor`, `chore` | no release | +| -------------------------------------------------- | --------------------------- | +| any type with `!`, e.g. `feat!: …` | major (`2.0.0`) | +| `feat: …` | minor (`1.1.0`) | +| `fix: …`, `perf: …` | patch (`1.0.2`) | +| `docs`, `test`, `ci`, `build`, `refactor`, `chore` | no release | A line starting with `BREAKING CHANGE:` (or `BREAKING-CHANGE:`) in the PR description also makes a major release, whatever the title.