UNPKG

@animo-id/pex

Version:

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

162 lines 14.5 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { cborEncode, DateOnly, IssuerSignedDocument, MDoc, parseDeviceResponse, parseIssuerSigned, uint8ArrayToBase64Url } from '@animo-id/mdoc'; import { CredentialMapper, OriginalType, } from '@sphereon/ssi-types'; import * as u8a from 'uint8arrays'; // NOTE: this is partial reimplementation of CredentialMapper from ssi-types to overwrite the // methods relying on Sphereon's mdoc implementation export class PexCredentialMapper { static isMsoMdocDecodedPresentation(original) { return original instanceof MDoc; } static isMsoMdocDecodedCredential(original) { return original instanceof IssuerSignedDocument; } /** * Decodes a Verifiable Presentation to a uniform format. * * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods * instead of the compact SD-JWT. * * @param presentation * @param hasher Hasher implementation to use for SD-JWT decoding. */ static decodeVerifiablePresentation(presentation, hasher) { if (CredentialMapper.isMsoMdocOid4VPEncoded(presentation)) { return presentation; } else if (this.isMsoMdocDecodedPresentation(presentation)) { return presentation; } else { return CredentialMapper.decodeVerifiablePresentation(presentation, hasher); } } static isW3cCredential(credential) { return CredentialMapper.isW3cCredential(credential); } /** * Converts a presentation to a wrapped presentation. * * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods * instead of the compact SD-JWT. * * @param hasher Hasher implementation to use for SD-JWT decoding */ static toWrappedVerifiablePresentation(originalPresentation, opts) { // MSO_MDOC if (this.isMsoMdocDecodedPresentation(originalPresentation) || CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) { let deviceResponse; let originalType; if (CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) { deviceResponse = parseDeviceResponse(convertBase64urlToBinary(originalPresentation)); originalType = OriginalType.MSO_MDOC_ENCODED; } else { deviceResponse = originalPresentation; originalType = OriginalType.MSO_MDOC_DECODED; } const mdocCredentials = deviceResponse.documents?.map((doc) => PexCredentialMapper.toWrappedVerifiableCredential(doc, opts)); if (!mdocCredentials || mdocCredentials.length === 0) { throw new Error('could not extract any mdoc credentials from mdoc device response'); } return { type: originalType, format: 'mso_mdoc', original: originalPresentation, presentation: deviceResponse, decoded: deviceResponse, vcs: mdocCredentials, }; } return CredentialMapper.toWrappedVerifiablePresentation(originalPresentation, opts); } /** * Converts a credential to a wrapped credential. * * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods * instead of the compact SD-JWT. * * @param hasher Hasher implementation to use for SD-JWT decoding */ static toWrappedVerifiableCredential(verifiableCredential, opts) { // MSO_MDOC if (this.isMsoMdocDecodedCredential(verifiableCredential) || CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) { let mdoc; if (CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) { mdoc = parseIssuerSigned(convertBase64urlToBinary(verifiableCredential)); } else { mdoc = verifiableCredential; } return { type: CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) ? OriginalType.MSO_MDOC_DECODED : OriginalType.MSO_MDOC_ENCODED, format: 'mso_mdoc', original: verifiableCredential, credential: mdoc, decoded: getMdocDecodedPayload(mdoc), }; } return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential, opts); } static isW3cPresentation(presentation) { return CredentialMapper.isW3cPresentation(presentation); } static isWrappedSdJwtVerifiableCredential = (vc) => CredentialMapper.isWrappedSdJwtVerifiableCredential(vc); static isWrappedSdJwtVerifiablePresentation = (vc) => CredentialMapper.isWrappedSdJwtVerifiablePresentation(vc); static isWrappedW3CVerifiableCredential = (vc) => CredentialMapper.isWrappedW3CVerifiableCredential(vc); static isWrappedW3CVerifiablePresentation = (vc) => CredentialMapper.isWrappedW3CVerifiablePresentation(vc); static isWrappedMdocCredential = (vc) => CredentialMapper.isWrappedMdocCredential(vc); static isWrappedMdocPresentation = (vc) => CredentialMapper.isWrappedMdocPresentation(vc); static isSdJwtDecodedCredential(original) { return CredentialMapper.isSdJwtDecodedCredential(original); } static isJwtDecodedCredential(original) { return CredentialMapper.isJwtDecodedCredential(original); } static isSdJwtEncoded(original) { return CredentialMapper.isSdJwtEncoded(original); } static isJwtEncoded(original) { return CredentialMapper.isJwtEncoded(original); } static decodeVerifiableCredential(credential, hasher) { return CredentialMapper.decodeVerifiableCredential(credential, hasher); } static isCredential(original) { return CredentialMapper.isCredential(original); } static areOriginalVerifiableCredentialsEqual(firstOriginal, secondOriginal) { if (this.isMsoMdocDecodedCredential(firstOriginal) || this.isMsoMdocDecodedCredential(secondOriginal)) { return uint8ArrayToBase64Url(cborEncode(firstOriginal)) === uint8ArrayToBase64Url(cborEncode(secondOriginal)); } return CredentialMapper.areOriginalVerifiableCredentialsEqual(firstOriginal, secondOriginal); } } export function getMdocDecodedPayload(mdoc) { const namespaces = mdoc.issuerSigned.nameSpaces; const decodedPayload = {}; for (const [namespace, items] of Array.from(namespaces.entries())) { decodedPayload[namespace] = items.reduce((acc, item) => ({ ...acc, [item.elementIdentifier]: encodeMdocValue(item.elementValue), }), {}); } return decodedPayload; } function encodeMdocValue(value) { if (typeof value === 'string' || typeof value === 'number' || typeof value === 'undefined' || value === null) return value; if (value instanceof Date || value instanceof DateOnly) return value.toISOString(); // TODO: we don't want undefined, but empty object might also not work? return {}; } function convertBase64urlToBinary(data) { return u8a.fromString(data, 'base64url'); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGV4Q3JlZGVudGlhbE1hcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2xpYi90eXBlcy9QZXhDcmVkZW50aWFsTWFwcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHVEQUF1RDtBQUN2RCxPQUFPLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxvQkFBb0IsRUFBRSxJQUFJLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNqSixPQUFPLEVBR0wsZ0JBQWdCLEVBT2hCLFlBQVksR0FRYixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sS0FBSyxHQUFHLE1BQU0sYUFBYSxDQUFDO0FBdUVuQyw2RkFBNkY7QUFDN0Ysb0RBQW9EO0FBQ3BELE1BQU0sT0FBTyxtQkFBbUI7SUFDdkIsTUFBTSxDQUFDLDRCQUE0QixDQUFDLFFBQXdDO1FBQ2pGLE9BQU8sUUFBUSxZQUFZLElBQUksQ0FBQztJQUNsQyxDQUFDO0lBRU0sTUFBTSxDQUFDLDBCQUEwQixDQUN0QyxRQUFxRztRQUVyRyxPQUFPLFFBQVEsWUFBWSxvQkFBb0IsQ0FBQztJQUNsRCxDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0gsTUFBTSxDQUFDLDRCQUE0QixDQUNqQyxZQUE0QyxFQUM1QyxNQUFtQjtRQUVuQixJQUFJLGdCQUFnQixDQUFDLHNCQUFzQixDQUFDLFlBQW1CLENBQUMsRUFBRSxDQUFDO1lBQ2pFLE9BQU8sWUFBc0IsQ0FBQztRQUNoQyxDQUFDO2FBQU0sSUFBSSxJQUFJLENBQUMsNEJBQTRCLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUMzRCxPQUFPLFlBQW9CLENBQUM7UUFDOUIsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLGdCQUFnQixDQUFDLDRCQUE0QixDQUFDLFlBQVksRUFBRSxNQUFNLENBQVEsQ0FBQztRQUNwRixDQUFDO0lBQ0gsQ0FBQztJQUVNLE1BQU0s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