UNPKG

vess-mdl

Version:

Parse and and validate MDOC CBOR encoded binaries according to ISO 18013-5.

249 lines 19.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _Document_issuerNameSpaces, _Document_deviceKeyInfo, _Document_validityInfo, _Document_digestAlgorithm; Object.defineProperty(exports, "__esModule", { value: true }); exports.Document = void 0; const jose = __importStar(require("jose")); const cose_kit_1 = require("cose-kit"); const utils_1 = require("../utils"); const cbor_1 = require("../../cbor"); const IssuerSignedItem_1 = require("../IssuerSignedItem"); const IssuerAuth_1 = __importDefault(require("./IssuerAuth")); const IssuerSignedDocument_1 = require("./IssuerSignedDocument"); const DEFAULT_NS = 'org.iso.18013.5.1'; const addYears = (date, years) => { const r = new Date(date.getTime()); r.setFullYear(date.getFullYear() + years); return r; }; /** * Use this class when building new documents. * * This class allow you to build a document and sign it with the issuer's private key. */ class Document { constructor(doc = 'org.iso.18013.5.1.mDL') { _Document_issuerNameSpaces.set(this, {}); _Document_deviceKeyInfo.set(this, void 0); _Document_validityInfo.set(this, { signed: new Date(), validFrom: new Date(), validUntil: addYears(new Date(), 1), }); _Document_digestAlgorithm.set(this, 'SHA-256'); this.docType = doc; } // eslint-disable-next-line @typescript-eslint/no-unused-vars validateValues(values) { // TODO // validate required fields, no extra fields, data types, etc... } /** * Add a namespace to an unsigned document. * * @param {string} namespace - The namespace to add. * @param {Record<string, any>} values - The values to add to the namespace. * @returns {Document} - The document */ addIssuerNameSpace(namespace, values) { if (namespace === DEFAULT_NS) { this.validateValues(values); } __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace] = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace] ?? []; const addAttribute = (key, value) => { let elementValue = value; if (namespace === DEFAULT_NS) { // the following namespace attributes must be a full-date as specified in RFC 3339 if (['birth_date', 'issue_date', 'expiry_date'].includes(key) && typeof value === 'string') { elementValue = new cbor_1.DateOnly(value); } if (key === 'driving_privileges' && Array.isArray(value)) { value.forEach((v, i) => { if (typeof v.issue_date === 'string') { elementValue[i].issue_date = new cbor_1.DateOnly(v.issue_date); } if (typeof v.expiry_date === 'string') { elementValue[i].expiry_date = new cbor_1.DateOnly(v.expiry_date); } }); } } const digestID = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace].length; const issuerSignedItem = IssuerSignedItem_1.IssuerSignedItem.create(digestID, key, elementValue); __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace].push(issuerSignedItem); }; for (const [key, value] of Object.entries(values)) { addAttribute(key, value); } return this; } /** * Get the values in a namespace. * * @param {string} namespace - The namespace to add. * @returns {Record<string, any>} - The values in the namespace as an object */ getIssuerNameSpace(namespace) { const nameSpace = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace]; return Object.fromEntries(nameSpace.map((item) => [item.elementIdentifier, item.elementValue])); } /** * Add the device public key which will be include in the issuer signature. * The device public key could be in JWK format or as COSE_Key format. * * @param params * @param {jose.JWK | Uint8Array} params.devicePublicKey - The device public key. */ addDeviceKeyInfo({ deviceKey }) { const deviceKeyCOSEKey = deviceKey instanceof Uint8Array ? deviceKey : (0, cose_kit_1.COSEKeyFromJWK)(deviceKey); const decodedCoseKey = (0, cbor_1.cborDecode)(deviceKeyCOSEKey); __classPrivateFieldSet(this, _Document_deviceKeyInfo, { deviceKey: decodedCoseKey, }, "f"); return this; } /** * Add validity info to the document that will be used in the issuer signature. * * @param info - the validity info * @param {Date} [info.signed] - The date the document is signed. default: now * @param {Date} [info.validFrom] - The date the document is valid from. default: signed * @param {Date} [info.validUntil] - The date the document is valid until. default: signed + 1 year * @param {Date} [info.expectedUpdate] - [Optional] The date the document is expected to be re-signed and potentially have its data updated. * @returns */ addValidityInfo(info = {}) { const signed = info.signed ?? new Date(); const validFrom = info.validFrom ?? signed; const validUntil = info.validUntil ?? addYears(signed, 1); __classPrivateFieldSet(this, _Document_validityInfo, { signed, validFrom, validUntil, }, "f"); if (info.expectedUpdate) { __classPrivateFieldGet(this, _Document_validityInfo, "f").expectedUpdate = info.expectedUpdate; } return this; } /** * Set the digest algorithm used for the value digests in the issuer signature. * * The default is SHA-256. * * @param {DigestAlgorithm} digestAlgorithm - The digest algorithm to use. * @returns */ useDigestAlgorithm(digestAlgorithm) { __classPrivateFieldSet(this, _Document_digestAlgorithm, digestAlgorithm, "f"); return this; } /** * Generate the issuer signature for the document. * * @param {Object} params - The parameters object * @param {jose.JWK | Uint8Array} params.issuerPrivateKey - The issuer's private key either in JWK format or COSE_KEY format as buffer. * @param {string | Uint8Array | Array<string | Uint8Array>} params.issuerCertificate - The issuer's certificate in pem format, as a buffer, or an array. * @param {SupportedAlgs} params.alg - The algorhitm used for the MSO signature. * @param {string | Uint8Array} [params.kid] - The key id of the issuer's private key. default: issuerPrivateKey.kid * @returns {Promise<IssuerSignedDoc>} - The signed document */ async sign(params) { if (!__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")) { throw new Error('No namespaces added'); } let issuerCertificateChain; if (Array.isArray(params.issuerCertificate)) { issuerCertificateChain = params.issuerCertificate.flatMap((cert) => (typeof cert === 'string' ? (0, utils_1.fromPEM)(cert) : [cert])); } else if (typeof params.issuerCertificate === 'string') { issuerCertificateChain = (0, utils_1.fromPEM)(params.issuerCertificate); } else { issuerCertificateChain = [params.issuerCertificate]; } const issuerPrivateKeyJWK = params.issuerPrivateKey instanceof Uint8Array ? (0, cose_kit_1.COSEKeyToJWK)(params.issuerPrivateKey) : params.issuerPrivateKey; const issuerPrivateKey = await jose.importJWK(issuerPrivateKeyJWK); const valueDigests = new Map(await Promise.all(Object.entries(__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")).map(async ([namespace, items]) => { const digestMap = new Map(); await Promise.all(items.map(async (item, index) => { const hash = await item.calculateDigest(__classPrivateFieldGet(this, _Document_digestAlgorithm, "f")); digestMap.set(index, new Uint8Array(hash)); })); return [namespace, digestMap]; }))); const mso = { version: '1.0', digestAlgorithm: __classPrivateFieldGet(this, _Document_digestAlgorithm, "f"), valueDigests, deviceKeyInfo: __classPrivateFieldGet(this, _Document_deviceKeyInfo, "f"), docType: this.docType, validityInfo: __classPrivateFieldGet(this, _Document_validityInfo, "f"), }; const payload = (0, cbor_1.cborEncode)(cbor_1.DataItem.fromData(mso)); const protectedHeader = { alg: params.alg }; const unprotectedHeader = { kid: params.kid ?? issuerPrivateKeyJWK.kid, x5chain: issuerCertificateChain.length === 1 ? issuerCertificateChain[0] : issuerCertificateChain, }; const issuerAuth = await IssuerAuth_1.default.sign(protectedHeader, unprotectedHeader, payload, issuerPrivateKey); const issuerSigned = { issuerAuth, nameSpaces: __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f"), }; return new IssuerSignedDocument_1.IssuerSignedDocument(this.docType, issuerSigned); } } exports.Document = Document; _Document_issuerNameSpaces = new WeakMap(), _Document_deviceKeyInfo = new WeakMap(), _Document_validityInfo = new WeakMap(), _Document_digestAlgorithm = new WeakMap(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG9jdW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbWRvYy9tb2RlbC9Eb2N1bWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSwyQ0FBNkI7QUFDN0IsdUNBQThGO0FBQzlGLG9DQUFtQztBQUNuQyxxQ0FBd0U7QUFDeEUsMERBQXVEO0FBQ3ZELDhEQUFzQztBQUV0QyxpRUFBOEQ7QUFFOUQsTUFBTSxVQUFVLEdBQUcsbUJBQW1CLENBQUM7QUFFdkMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUFVLEVBQUUsS0FBYSxFQUFRLEVBQUU7SUFDbkQsTUFBTSxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDbkMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEdBQUcsS0FBSyxDQUFDLENBQUM7SUFDMUMsT0FBTyxDQUFDLENBQUM7QUFDWCxDQUFDLENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsTUFBYSxRQUFRO0lBV25CLFlBQVksTUFBZSx1QkFBdUI7UUFUbEQscUNBQXNDLEVBQUUsRUFBQztRQUN6QywwQ0FBOEI7UUFDOUIsaUNBQThCO1lBQzVCLE1BQU0sRUFBRSxJQUFJLElBQUksRUFBRTtZQUNsQixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDckIsVUFBVSxFQUFFLFFBQVEsQ0FBQyxJQUFJLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztTQUNwQyxFQUFDO1FBQ0Ysb0NBQW9DLFNBQVMsRUFBQztRQUc1QyxJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQztJQUNyQixDQUFDO0lBRUQsNkRBQTZEO0lBQ3JELGNBQWMsQ0FBQyxNQUEyQjtRQUNoRCxPQUFPO1FBQ1AsZ0VBQWdFO0lBQ2xFLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxrQkFBa0IsQ0FBQyxTQUF1QyxFQUFFLE1BQTJCO1FBQ3JGLElBQUksU0FBUyxLQUFLLFVBQVUsRUFBRSxDQUFDO1lBQzdCLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDOUIsQ0FBQztRQUVELHVCQUFBLElBQUksa0NBQWtCLENBQUMsU0FBUyxDQUFDLEdBQUcsdUJBQUEsSUFBSSxrQ0FBa0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFNUUsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFXLEVBQUUsS0FBVSxFQUFFLEVBQUU7WUFDL0MsSUFBSSxZQUFZLEdBQUcsS0FBSyxDQUFDO1lBRXpCLElBQUksU0FBUyxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUM3QixrRkFBa0Y7Z0JBQ2xGLElBQUksQ0FBQyxZQUFZLEVBQUUsWUFBWSxFQUFFLGFBQWEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztvQkFDM0YsWUFBWSxHQUFHLElBQUksZUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNyQyxDQUFDO2dCQUVELElBQUksR0FBRyxLQUFLLG9CQUFvQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDekQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRTt3QkFDckIsSUFBSSxPQUFPLENBQUMsQ0FBQyxVQUFVLEtBQUssUUFBUSxFQUFFLENBQUM7NEJBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsR0FBRyxJQUFJLGVBQVEsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUM7d0JBQUMsQ0FBQzt3QkFDbEcsSUFBSSxPQUFPLENBQUMsQ0FBQyxXQUFXLEtBQUssUUFBUSxFQUFFLENBQUM7NEJBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsR0FBRyxJQUFJLGVBQVEsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUM7d0JBQUMsQ0FBQztvQkFDdkcsQ0FBQyxDQUFDLENBQUM7Z0JBQ0wsQ0FBQztZQUNILENBQUM7WUFFRCxNQUFNLFFBQVEsR0FBRyx1QkFBQSxJQUFJLGtDQUFrQixDQUFDLFNBQVMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztZQUMxRCxNQUFNLGdCQUFnQixHQUFHLG1DQUFnQixDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBQzlFLHVCQUFBLElBQUksa0NBQWtCLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDM0QsQ0FBQyxDQUFDO1FBRUYsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNsRCxZQUFZLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzNCLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILGtCQUFrQixDQUFDLFNBQWlCO1FBQ2xDLE1BQU0sU0FBUyxHQUFHLHVCQUFBLElBQUksa0NBQWtCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDcEQsT0FBTyxNQUFNLENBQUMsV0FBVyxDQUN2QixTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FDckUsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLFNBQVMsRUFBd0M7UUFDbEUsTUFBTSxnQkFBZ0IsR0FDcEIsU0FBUyxZQUFZLFVBQVUsQ0FBQyxDQUFDO1lBQy9CLFNBQVMsQ0FBQyxDQUFDO1lBQ1gsSUFBQSx5QkFBYyxFQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sY0FBYyxHQUFHLElBQUEsaUJBQVUsRUFBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRXBELHVCQUFBLElBQUksMkJBQWtCO1lBQ3BCLFNBQVMsRUFBRSxjQUFjO1NBQzFCLE1BQUEsQ0FBQztRQUVGLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILGVBQWUsQ0FBQyxPQUE4QixFQUFFO1FBQzlDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN6QyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQztRQUMzQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxJQUFJLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDMUQsdUJBQUEsSUFBSSwwQkFBaUI7WUFDbkIsTUFBTTtZQUNOLFNBQVM7WUFDVCxVQUFVO1NBQ1gsTUFBQSxDQUFDO1FBRUYsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDeEIsdUJBQUEsSUFBSSw4QkFBYyxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDO1FBQzFELENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsa0JBQWtCLENBQUMsZUFBZ0M7UUFDakQsdUJBQUEsSUFBSSw2QkFBb0IsZUFBZSxNQUFBLENBQUM7UUFDeEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0gsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUtWO1FBQ0MsSUFBSSxDQUFDLHVCQUFBLElBQUksa0NBQWtCLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDekMsQ0FBQztRQUVELElBQUksc0JBQW9DLENBQUM7UUFFekMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDNUMsc0JBQXNCLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUEsZUFBTyxFQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzSCxDQUFDO2FBQU0sSUFBSSxPQUFPLE1BQU0sQ0FBQyxpQkFBaUIsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN4RCxzQkFBc0IsR0FBRyxJQUFBLGVBQU8sRUFBQyxNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM3RCxDQUFDO2FBQU0sQ0FBQztZQUNOLHNCQUFzQixHQUFHLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDdEQsQ0FBQztRQUVELE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixZQUFZLFVBQVUsQ0FBQyxDQUFDO1lBQ3pFLElBQUEsdUJBQVksRUFBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDO1lBQ3ZDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztRQUUxQixNQUFNLGdCQUFnQixHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBRW5FLE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLHVCQUFBLElBQUksa0NBQWtCLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUU7WUFDckgsTUFBTSxTQUFTLEdBQUcsSUFBSSxHQUFHLEVBQXNCLENBQUM7WUFDaEQsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRTtnQkFDaEQsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLHVCQUFBLElBQUksaUNBQWlCLENBQUMsQ0FBQztnQkFDL0QsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUM3QyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ0osT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQXNDLENBQUM7UUFDckUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRUwsTUFBTSxHQUFHLEdBQVE7WUFDZixPQUFPLEVBQUUsS0FBSztZQUNkLGVBQWUsRUFBRSx1QkFBQSxJQUFJLGlDQUFpQjtZQUN0QyxZQUFZO1lBQ1osYUFBYSxFQUFFLHVCQUFBLElBQUksK0JBQWU7WUFDbEMsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPO1lBQ3JCLFlBQVksRUFBRSx1QkFBQSxJQUFJLDhCQUFjO1NBQ2pDLENBQUM7UUFFRixNQUFNLE9BQU8sR0FBRyxJQUFBLGlCQUFVLEVBQUMsZUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25ELE1BQU0sZUFBZSxHQUFxQixFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDOUQsTUFBTSxpQkFBaUIsR0FBdUI7WUFDNUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHLElBQUksbUJBQW1CLENBQUMsR0FBRztZQUMxQyxPQUFPLEVBQUUsc0JBQXNCLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsc0JBQXNCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLHNCQUFzQjtTQUNsRyxDQUFDO1FBRUYsTUFBTSxVQUFVLEdBQUcsTUFBTSxvQkFBVSxDQUFDLElBQUksQ0FDdEMsZUFBZSxFQUNmLGlCQUFpQixFQUNqQixPQUFPLEVBQ1AsZ0JBQWdCLENBQ2pCLENBQUM7UUFFRixNQUFNLFlBQVksR0FBRztZQUNuQixVQUFVO1lBQ1YsVUFBVSxFQUFFLHVCQUFBLElBQUksa0NBQWtCO1NBQ25DLENBQUM7UUFFRixPQUFPLElBQUksMkNBQW9CLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxZQUFZLENBQUMsQ0FBQztJQUM5RCxDQUFDO0NBQ0Y7QUFyTkQsNEJBcU5DIn0=