@animo-id/pex
Version:
A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification
162 lines • 14.5 kB
JavaScript
/* 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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