UNPKG

@sphereon/pex

Version:

A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification

437 lines 49.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PEX = void 0; const ssi_types_1 = require("@sphereon/ssi-types"); const ConstraintUtils_1 = require("./ConstraintUtils"); const evaluation_1 = require("./evaluation"); const signing_1 = require("./signing"); const types_1 = require("./types"); const utils_1 = require("./utils"); const validation_1 = require("./validation"); /** * This is the main interfacing class to be used by developers using the PEX library. */ class PEX { constructor(options) { // TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it elsewhere. We need to retrhink how to instantiate PEX this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper(); this.options = options; } /*** * The evaluatePresentation compares what is expected from one or more presentations with a presentationDefinition. * presentationDefinition: It can be either v1 or v2 of presentationDefinition * * @param presentationDefinition the definition of what is expected in the presentation. * @param presentations the presentation(s) which have to be evaluated in comparison of the definition. * @param opts - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure * * @return the evaluation results specify what was expected and was fulfilled and also specifies which requirements described in the input descriptors * were not fulfilled by the presentation(s). */ evaluatePresentation(presentationDefinition, presentations, opts) { var _a; // We map it to an array for now to make processing on the presentations easier, but before checking against the submission // we will transform it to the original structure (array vs single) so the references in the submission stay correct const presentationsArray = Array.isArray(presentations) ? presentations : [presentations]; if (presentationsArray.length === 0) { throw new Error('At least one presentation must be provided'); } const generatePresentationSubmission = (opts === null || opts === void 0 ? void 0 : opts.generatePresentationSubmission) !== undefined ? opts.generatePresentationSubmission : (opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) === undefined; const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition); const presentationsCopy = JSON.parse(JSON.stringify(presentationsArray)); const wrappedPresentations = presentationsCopy.map((p) => { var _a; return types_1.SSITypesBuilder.mapExternalVerifiablePresentationToWrappedVP(p, (_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher); }); let presentationSubmission = opts === null || opts === void 0 ? void 0 : opts.presentationSubmission; let presentationSubmissionLocation = (_a = opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) !== null && _a !== void 0 ? _a : ((Array.isArray(presentations) && presentations.length > 1) || !ssi_types_1.CredentialMapper.isW3cPresentation(wrappedPresentations[0].presentation) ? signing_1.PresentationSubmissionLocation.EXTERNAL : signing_1.PresentationSubmissionLocation.PRESENTATION); // When only one presentation, we also allow it to be present in the VP if (!presentationSubmission && presentationsArray.length === 1 && ssi_types_1.CredentialMapper.isW3cPresentation(wrappedPresentations[0].presentation) && !generatePresentationSubmission) { const decoded = wrappedPresentations[0].decoded; if ('presentation_submission' in decoded) { presentationSubmission = decoded.presentation_submission; } if (!presentationSubmission) { throw Error(`Either a presentation submission as part of the VP or provided in options was expected`); } presentationSubmissionLocation = signing_1.PresentationSubmissionLocation.PRESENTATION; if ((opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) && opts.presentationSubmissionLocation !== signing_1.PresentationSubmissionLocation.PRESENTATION) { throw new Error(`unexpected presentationSubmissionLocation ${opts.presentationSubmissionLocation} was provided. Expected ${signing_1.PresentationSubmissionLocation.PRESENTATION} when no presentationSubmission passed and first verifiable presentation contains a presentation_submission and generatePresentationSubmission is false`); } } else if (!presentationSubmission && !generatePresentationSubmission) { throw new Error('Presentation submission in options was expected.'); } // TODO: we should probably add support for holder dids in the kb-jwt of an SD-JWT. We can extract this from the // `wrappedPresentation.original.compactKbJwt`, but as HAIP doesn't use dids, we'll leave it for now. const holderDIDs = wrappedPresentations .map((p) => { return ssi_types_1.CredentialMapper.isW3cPresentation(p.presentation) && p.presentation.holder ? p.presentation.holder : undefined; }) .filter((d) => d !== undefined); const updatedOpts = Object.assign(Object.assign({}, opts), { holderDIDs, presentationSubmission, presentationSubmissionLocation, generatePresentationSubmission }); const allWvcs = wrappedPresentations.reduce((all, wvp) => [...all, ...wvp.vcs], []); const result = this._evaluationClientWrapper.evaluatePresentations(pd, Array.isArray(presentations) ? wrappedPresentations : wrappedPresentations[0], updatedOpts); if (result.areRequiredCredentialsPresent !== ConstraintUtils_1.Status.ERROR) { const selectFromClientWrapper = new evaluation_1.EvaluationClientWrapper(); const selectResults = selectFromClientWrapper.selectFrom(pd, allWvcs, updatedOpts); if (selectResults.areRequiredCredentialsPresent !== ConstraintUtils_1.Status.ERROR) { result.errors = []; } } return result; } /*** * The evaluate compares what is expected from a verifiableCredentials with the presentationDefinition. * * @param presentationDefinition the v1 or v2 definition of what is expected in the presentation. * @param verifiableCredentials the verifiable credentials which are candidates to fulfill requirements defined in the presentationDefinition param. * @param opts - holderDIDs the list of the DIDs that the wallet holders controls. Optional, but needed by some input requirements that do a holderDID check. * @ - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure * * @return the evaluation results specify what was expected and was fulfilled and also specifies which requirements described in the input descriptors * were not fulfilled by the verifiable credentials. */ evaluateCredentials(presentationDefinition, verifiableCredentials, opts) { var _a; const wrappedVerifiableCredentials = types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(verifiableCredentials, (_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher); // TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it here. We need to retrhink how to instantiate PEX this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper(); const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition); const result = this._evaluationClientWrapper.evaluate(pd, wrappedVerifiableCredentials, opts); if (result.value && result.value.descriptor_map.length) { const selectFromClientWrapper = new evaluation_1.EvaluationClientWrapper(); const selectResults = selectFromClientWrapper.selectFrom(pd, wrappedVerifiableCredentials, opts); result.areRequiredCredentialsPresent = selectResults.areRequiredCredentialsPresent; result.errors = selectResults.errors; } else { result.areRequiredCredentialsPresent = ConstraintUtils_1.Status.ERROR; } return result; } /** * The selectFrom method is a helper function that helps filter out the verifiable credentials which can not be selected and returns * the selectable credentials. * * @param presentationDefinition the v1 or v2 definition of what is expected in the presentation. * @param verifiableCredentials verifiable credentials are the credentials from wallet provided to the library to find selectable credentials. * @param opts - holderDIDs the decentralized identifier(s) of the wallet holderDID. This is used to identify the credentials issued to the holderDID of wallet in certain scenario's. * - limitDisclosureSignatureSuites the credential signature suites that support limit disclosure * * @return the selectable credentials. */ selectFrom(presentationDefinition, verifiableCredentials, opts) { var _a; const verifiableCredentialCopy = JSON.parse(JSON.stringify(verifiableCredentials)); const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition); // TODO: So we have state in the form of this property which is set in the constructor, but we are overwriting it here. We need to retrhink how to instantiate PEX this._evaluationClientWrapper = new evaluation_1.EvaluationClientWrapper(); return this._evaluationClientWrapper.selectFrom(pd, types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(verifiableCredentialCopy, (_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher), opts); } presentationSubmissionFrom(presentationDefinition, selectedCredentials, opts) { var _a; const pd = types_1.SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition); return this._evaluationClientWrapper.submissionFrom(pd, types_1.SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(selectedCredentials, (_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher), opts); } /** * This method helps create an Unsigned Presentation. An Unsigned Presentation after signing becomes a Presentation. And can be sent to * the verifier after signing it. * * @param presentationDefinition the v1 or v2 definition of what is expected in the presentation. * @param selectedCredentials the credentials which were declared selectable by getSelectableCredentials and then chosen by the intelligent-user * (e.g. human). * @param opts - holderDID optional; the decentralized identity of the wallet holderDID. This is used to identify the holderDID of the presentation. * * @return the presentation. */ presentationFrom(presentationDefinition, selectedCredentials, opts) { var _a, _b; const presentationSubmission = this.presentationSubmissionFrom(presentationDefinition, selectedCredentials, opts); const hasSdJwtCredentials = selectedCredentials.some((c) => ssi_types_1.CredentialMapper.isSdJwtDecodedCredential(c) || ssi_types_1.CredentialMapper.isSdJwtEncoded(c)); // We could include it in the KB-JWT? Not sure if we want that if ((opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) === signing_1.PresentationSubmissionLocation.PRESENTATION && hasSdJwtCredentials) { throw new Error('Presentation submission location cannot be set to presentation when creating a presentation with an SD-JWT VC'); } const presentationSubmissionLocation = (_a = opts === null || opts === void 0 ? void 0 : opts.presentationSubmissionLocation) !== null && _a !== void 0 ? _a : (hasSdJwtCredentials ? signing_1.PresentationSubmissionLocation.EXTERNAL : signing_1.PresentationSubmissionLocation.PRESENTATION); const presentations = this.constructPresentations(selectedCredentials, Object.assign(Object.assign({}, opts), { // We only pass in the submission in case it needs to be included in the presentation presentationSubmission: presentationSubmissionLocation === signing_1.PresentationSubmissionLocation.PRESENTATION ? presentationSubmission : undefined, hasher: (_b = this.options) === null || _b === void 0 ? void 0 : _b.hasher })); this.updateSdJwtCredentials(presentations); return { presentations, presentationSubmissionLocation, presentationSubmission, }; } constructPresentations(selectedCredentials, opts) { var _a, _b, _c, _d; if (!selectedCredentials) { throw Error(`At least a verifiable credential needs to be passed in to create a presentation`); } const verifiableCredentials = (Array.isArray(selectedCredentials) ? selectedCredentials : [selectedCredentials]); if (verifiableCredentials.some((c) => ssi_types_1.CredentialMapper.isSdJwtDecodedCredential(c) || ssi_types_1.CredentialMapper.isSdJwtEncoded(c))) { if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher)) { throw new Error('Hasher must be provided when creating a presentation with an SD-JWT VC'); } } const wVCs = verifiableCredentials.map((vc) => { var _a; return ssi_types_1.CredentialMapper.toWrappedVerifiableCredential(vc, { hasher: (_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher }); }); const holders = Array.from(new Set(wVCs.flatMap((wvc) => (0, utils_1.getSubjectIdsAsString)(wvc.credential)))); if (holders.length !== 1 && !(opts === null || opts === void 0 ? void 0 : opts.holderDID)) { console.log(`We deduced ${holders.length} subject from ${wVCs.length} Verifiable Credentials, and no holder property was given. This might lead to undesired results`); } const holder = (_b = opts === null || opts === void 0 ? void 0 : opts.holderDID) !== null && _b !== void 0 ? _b : (holders.length === 1 ? holders[0] : undefined); const type = ((_c = opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload) === null || _c === void 0 ? void 0 : _c.type) ? Array.isArray(opts.basePresentationPayload.type) ? opts.basePresentationPayload.type : [opts.basePresentationPayload.type] : []; if (!type.includes('VerifiablePresentation')) { type.push('VerifiablePresentation'); } const context = ((_d = opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload) === null || _d === void 0 ? void 0 : _d['@context']) ? Array.isArray(opts.basePresentationPayload['@context']) ? opts.basePresentationPayload['@context'] : [opts.basePresentationPayload['@context']] : []; if (!context.includes('https://www.w3.org/2018/credentials/v1')) { context.push('https://www.w3.org/2018/credentials/v1'); } if (opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) { if (!type.includes('PresentationSubmission')) { type.push('PresentationSubmission'); } if (!context.includes('https://identity.foundation/presentation-exchange/submission/v1')) { context.push('https://identity.foundation/presentation-exchange/submission/v1'); } } const result = []; if (PEX.allowMultipleVCsPerPresentation(verifiableCredentials)) { result.push(Object.assign(Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload), { '@context': context, type, holder }), (!!(opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) && { presentation_submission: opts.presentationSubmission })), { verifiableCredential: verifiableCredentials })); } else { verifiableCredentials.forEach((vc) => { if (ssi_types_1.CredentialMapper.isSdJwtDecodedCredential(vc)) { result.push(vc); } else if (ssi_types_1.CredentialMapper.isSdJwtEncoded(vc)) { const decoded = ssi_types_1.CredentialMapper.decodeVerifiableCredential(vc, opts === null || opts === void 0 ? void 0 : opts.hasher); result.push(decoded); } else { // This should be jwt or json-ld result.push(Object.assign(Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.basePresentationPayload), { '@context': context, type, holder }), (!!(opts === null || opts === void 0 ? void 0 : opts.presentationSubmission) && { presentation_submission: opts.presentationSubmission })), { verifiableCredential: [vc] })); } }); } return result; } /* TODO SDK-37 refinement needed */ static allowMultipleVCsPerPresentation(verifiableCredentials) { const jwtCredentials = verifiableCredentials.filter((c) => ssi_types_1.CredentialMapper.isJwtEncoded(c) || ssi_types_1.CredentialMapper.isJwtDecodedCredential(c)); if (jwtCredentials.length > 0) { const subjects = new Set(); // const verificationMethods = new Set<string>(); for (const credential of jwtCredentials) { const decodedCredential = ssi_types_1.CredentialMapper.isJwtEncoded(credential) ? ssi_types_1.CredentialMapper.decodeVerifiableCredential(credential) : credential; const subject = decodedCredential.sub || (decodedCredential.vc && decodedCredential.sub); if (subject) { subjects.add(subject); } /* const vcProof = decodedCredential.proof ?? decodedCredential.vc.proof; const proofs = Array.isArray(vcProof) ? vcProof : [vcProof]; proofs.filter((proof: IProof) => proof.verificationMethod).forEach((proof: IProof) => verificationMethods.add(proof.verificationMethod)); */ } // If there's more than one unique subject or verification method, we can't allow multiple VCs in a single presentation if (subjects.size > 1) { return false; } } if (verifiableCredentials.some((c) => ssi_types_1.CredentialMapper.isSdJwtEncoded(c) || ssi_types_1.CredentialMapper.isSdJwtDecodedCredential(c))) { return false; } return true; } /** * This method validates whether an object is usable as a presentation definition or not. * * @param presentationDefinition presentationDefinition of V1 or v2 to be validated. * * @return the validation results to reveal what is acceptable/unacceptable about the passed object to be considered a valid presentation definition */ static validateDefinition(presentationDefinition) { const result = (0, utils_1.definitionVersionDiscovery)(presentationDefinition); if (result.error) { const errorParts = [result.error]; const v1ErrorString = (0, utils_1.formatValidationErrors)(result.v1Errors); if (v1ErrorString) { errorParts.push('\nVersion 1 validation errors:\n ' + v1ErrorString); } const v2ErrorString = (0, utils_1.formatValidationErrors)(result.v2Errors); if (v2ErrorString) { errorParts.push('\nVersion 2 validation errors:\n ' + v2ErrorString); } throw new Error(errorParts.join('')); } const validators = []; result.version === types_1.PEVersion.v1 ? validators.push({ bundler: new validation_1.PresentationDefinitionV1VB('root'), target: types_1.SSITypesBuilder.modelEntityToInternalPresentationDefinitionV1(presentationDefinition), }) : validators.push({ bundler: new validation_1.PresentationDefinitionV2VB('root'), target: types_1.SSITypesBuilder.modelEntityInternalPresentationDefinitionV2(presentationDefinition), }); return new validation_1.ValidationEngine().validate(validators); } /** * This method validates whether an object is usable as a presentation submission or not. * * @param presentationSubmission the object to be validated. * * @return the validation results to reveal what is acceptable/unacceptable about the passed object to be considered a valid presentation submission */ static validateSubmission(presentationSubmission) { return new validation_1.ValidationEngine().validate([ { bundler: new validation_1.PresentationSubmissionVB('root'), target: presentationSubmission, }, ]); } /** * This method can be used to combine a definition, selected Verifiable Credentials, together with * signing opts and a callback to sign a presentation, making it a Verifiable Presentation before sending. * * Please note that PEX has no signature support on purpose. We didn't want this library to depend on all kinds of signature suites. * The callback function next to the Signing Params also gets a Presentation which is evaluated against the definition. * It is up to you to decide whether you simply update the supplied partial proof and add it to the presentation in the callback, * or whether you will use the selected Credentials, Presentation definition, evaluation results and/or presentation submission together with the signature opts * * @param presentationDefinition the Presentation Definition V1 or V2 * @param selectedCredentials the PEX and/or User selected/filtered credentials that will become part of the Verifiable Presentation * @param signingCallBack the function which will be provided as a parameter. And this will be the method that will be able to perform actual * signing. One example of signing is available in the project named. pe-selective-disclosure. * @param opts Signing Params these are the signing params required to sign. * * @return the signed and thus Verifiable Presentation. */ verifiablePresentationFrom(presentationDefinition, selectedCredentials, signingCallBack, opts) { return __awaiter(this, void 0, void 0, function* () { const { holderDID, signatureOptions, proofOptions } = opts; function limitedDisclosureSuites() { let limitDisclosureSignatureSuites = []; if (proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.typeSupportsSelectiveDisclosure) { if (!(proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.type)) { throw Error('Please provide a proof type if you enable selective disclosure'); } limitDisclosureSignatureSuites = [proofOptions.type]; } return limitDisclosureSignatureSuites; } const holderDIDs = holderDID ? [holderDID] : []; const limitDisclosureSignatureSuites = limitedDisclosureSuites(); const evaluationResult = this.evaluateCredentials(presentationDefinition, selectedCredentials, { holderDIDs, limitDisclosureSignatureSuites, }); const presentationResult = this.presentationFrom(presentationDefinition, evaluationResult.verifiableCredential, opts); const presentations = presentationResult.presentations; const evaluationResults = this.evaluatePresentation(presentationDefinition, presentations, Object.assign({ limitDisclosureSignatureSuites }, (presentationResult.presentationSubmissionLocation === signing_1.PresentationSubmissionLocation.EXTERNAL && { presentationSubmission: presentationResult.presentationSubmission, }))); if (!evaluationResults.value && selectedCredentials.length === 0) { evaluationResults.value = presentationResult.presentationSubmission; } if (!evaluationResults.value) { throw new Error('Could not get evaluation results from presentationResult'); } const proof = { type: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.type, verificationMethod: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.verificationMethod, created: (proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.created) ? proofOptions.created : new Date().toISOString(), proofPurpose: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.proofPurpose, proofValue: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.proofValue, jws: signatureOptions === null || signatureOptions === void 0 ? void 0 : signatureOptions.jws, challenge: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.challenge, nonce: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.nonce, domain: proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.domain, }; this.updateSdJwtCredentials(presentations, proofOptions === null || proofOptions === void 0 ? void 0 : proofOptions.nonce); const verifiablePresentations = []; for (const presentation of presentations) { const callBackParams = { options: Object.assign(Object.assign({}, opts), { presentationSubmissionLocation: presentationResult.presentationSubmissionLocation }), presentation, presentationDefinition, selectedCredentials, proof, presentationSubmission: evaluationResults.value, evaluationResults, }; verifiablePresentations.push(yield signingCallBack(callBackParams)); } return { verifiablePresentations, presentationSubmissionLocation: presentationResult.presentationSubmissionLocation, presentationSubmission: evaluationResults.value, }; }); } updateSdJwtCredentials(presentations, nonce) { presentations.forEach((presentation, index) => { var _a, _b; // Select type without kbJwt as isSdJwtDecodedCredential and won't accept the partial sdvc type if (ssi_types_1.CredentialMapper.isSdJwtDecodedCredential(presentation)) { const sdJwtCredential = presentation; if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.hasher)) { throw new Error('Hasher must be provided when creating a presentation with an SD-JWT VC'); } // extract sd_alg or default to sha-256 const hashAlg = (_b = sdJwtCredential.signedPayload._sd_alg) !== null && _b !== void 0 ? _b : 'sha-256'; const sdHash = (0, utils_1.calculateSdHash)(sdJwtCredential.compactSdJwtVc, hashAlg, this.options.hasher); const kbJwt = { // alg MUST be set by the signer header: { typ: 'kb+jwt', }, // aud MUST be set by the signer or provided by e.g. SIOP/OpenID4VP lib payload: { iat: Math.floor(new Date().getTime() / 1000), nonce: nonce, sd_hash: sdHash, }, }; presentations[index] = Object.assign(Object.assign({}, sdJwtCredential), { kbJwt }); } }); } static definitionVersionDiscovery(presentationDefinition) { return (0, utils_1.definitionVersionDiscovery)(presentationDefinition); } } exports.PEX = PEX; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUEVYLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbGliL1BFWC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFDQSxtREFnQjZCO0FBRTdCLHVEQUEyQztBQUMzQyw2Q0FBd0g7QUFDeEgsdUNBU21CO0FBQ25CLG1DQUEySTtBQUMzSSxtQ0FBcUg7QUFDckgsNkNBQTZJO0FBYzdJOztHQUVHO0FBQ0gsTUFBYSxHQUFHO0lBSWQsWUFBWSxPQUFvQjtRQUM5Qix3S0FBd0s7UUFDeEssSUFBSSxDQUFDLHdCQUF3QixHQUFHLElBQUksb0NBQXVCLEVBQUUsQ0FBQztRQUU5RCxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUN6QixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7T0FVRztJQUNJLG9CQUFvQixDQUN6QixzQkFBK0MsRUFDL0MsYUFBZ0gsRUFDaEgsSUFZQzs7UUFFRCwySEFBMkg7UUFDM0gsb0hBQW9IO1FBQ3BILE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzFGLElBQUksa0JBQWtCLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3BDLE1BQU0sSUFBSSxLQUFLLENBQUMsNENBQTRDLENBQUMsQ0FBQztRQUNoRSxDQUFDO1FBRUQsTUFBTSw4QkFBOEIsR0FDbEMsQ0FBQSxJQUFJLGFBQUosSUFBSSx1QkFBSixJQUFJLENBQUUsOEJBQThCLE1BQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsOEJBQThCLENBQUMsQ0FBQyxDQUFDLENBQUEsSUFBSSxhQUFKLElBQUksdUJBQUosSUFBSSxDQUFFLHNCQUFzQixNQUFLLFNBQVMsQ0FBQztRQUN4SSxNQUFNLEVBQUUsR0FBb0MsdUJBQWUsQ0FBQyxnQ0FBZ0MsQ0FBQyxzQkFBc0IsQ0FBQyxD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