UNPKG

@sphereon/ssi-sdk.mdl-mdoc

Version:

251 lines • 15.8 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.MDLMdoc = exports.mdocSupportMethods = void 0; const kmp_mdoc_core_1 = require("@sphereon/kmp-mdoc-core"); const ssi_sdk_ext_key_utils_1 = require("@sphereon/ssi-sdk-ext.key-utils"); const ssi_sdk_ext_x509_utils_1 = require("@sphereon/ssi-sdk-ext.x509-utils"); const __1 = require(".."); const functions_1 = require("../functions"); var CborByteString = kmp_mdoc_core_1.com.sphereon.cbor.CborByteString; var CoseKeyCbor = kmp_mdoc_core_1.com.sphereon.crypto.cose.CoseKeyCbor; var CoseSign1Json = kmp_mdoc_core_1.com.sphereon.crypto.cose.CoseSign1Json; var CoseCryptoServiceJS = kmp_mdoc_core_1.com.sphereon.crypto.CoseCryptoServiceJS; var CoseJoseKeyMappingService = kmp_mdoc_core_1.com.sphereon.crypto.CoseJoseKeyMappingService; var KeyInfo = kmp_mdoc_core_1.com.sphereon.crypto.KeyInfo; var DateTimeUtils = kmp_mdoc_core_1.com.sphereon.kmp.DateTimeUtils; var decodeFrom = kmp_mdoc_core_1.com.sphereon.kmp.decodeFrom; var encodeTo = kmp_mdoc_core_1.com.sphereon.kmp.encodeTo; var Encoding = kmp_mdoc_core_1.com.sphereon.kmp.Encoding; var DeviceResponseCbor = kmp_mdoc_core_1.com.sphereon.mdoc.data.device.DeviceResponseCbor; var MdocValidations = kmp_mdoc_core_1.com.sphereon.mdoc.data.MdocValidations; var MdocOid4vpService = kmp_mdoc_core_1.com.sphereon.mdoc.oid4vp.MdocOid4vpServiceJs; var Oid4VPPresentationSubmission = kmp_mdoc_core_1.com.sphereon.mdoc.oid4vp.Oid4VPPresentationSubmission; exports.mdocSupportMethods = [ 'x509VerifyCertificateChain', 'x509GetCertificateInfo', 'mdocVerifyIssuerSigned', 'mdocOid4vpHolderPresent', 'mdocOid4vpRPVerify', ]; /** * The MDLMdoc class implements the IAgentPlugin interface, providing methods for * verification and information retrieval related to X.509 certificates and mDL (mobile * driver's license) documents. */ class MDLMdoc { constructor(args) { var _a, _b; this.schema = __1.schema.IMDLMdoc; this.methods = { x509VerifyCertificateChain: this.x509VerifyCertificateChain.bind(this), x509GetCertificateInfo: this.x509GetCertificateInfo.bind(this), mdocVerifyIssuerSigned: this.mdocVerifyIssuerSigned.bind(this), mdocOid4vpHolderPresent: this.mdocOid4vpHolderPresent.bind(this), mdocOid4vpRPVerify: this.mdocOid4vpRPVerify.bind(this), }; this.trustAnchors = (_a = args === null || args === void 0 ? void 0 : args.trustAnchors) !== null && _a !== void 0 ? _a : []; this.opts = (_b = args === null || args === void 0 ? void 0 : args.opts) !== null && _b !== void 0 ? _b : { trustRootWhenNoAnchors: true }; } /** * Processes and verifies the provided mdoc, generates device response and presentation submission tokens. * * @param {MdocOid4vpPresentArgs} args - An object containing arguments for mdoc oid4vp holder presentation. * @param {IRequiredContext} _context - Required context for the operation. * @return {Promise<MdocOid4VPPresentationAuth>} A promise that resolves to an object containing vp_token and presentation_submission. */ mdocOid4vpHolderPresent(args, _context) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f; const { mdocs, presentationDefinition, trustAnchors, verifications, mdocHolderNonce, authorizationRequestNonce, responseUri, clientId } = args; const oid4vpService = new MdocOid4vpService(); // const mdoc = DocumentCbor.Static.cborDecode(decodeFrom(mdocBase64Url, Encoding.BASE64URL)) const validate = (mdoc) => __awaiter(this, void 0, void 0, function* () { var _a, _b; try { const result = yield MdocValidations.fromDocumentAsync(mdoc, null, trustAnchors !== null && trustAnchors !== void 0 ? trustAnchors : this.trustAnchors, DateTimeUtils.Static.DEFAULT.dateTimeLocal(((_b = (_a = verifications === null || verifications === void 0 ? void 0 : verifications.verificationTime) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : Date.now()) / 1000), verifications === null || verifications === void 0 ? void 0 : verifications.allowExpiredDocuments); if (result.error) { console.log(JSON.stringify(result, null, 2)); } return result; } catch (e) { console.log(e); return { error: true, verifications: [ { name: 'mdoc', error: true, critical: true, message: e.message, }, ], }; } }); const allMatches = oid4vpService.matchDocumentsAndDescriptors(mdocHolderNonce, mdocs, presentationDefinition); const docsAndDescriptors = []; let lastError = undefined; for (let match of allMatches) { if (match.document) { const result = yield validate(match.document); if (!result.error || responseUri.includes('openid.net')) { // TODO: We relax for the conformance suite, as the cert would be invalid try { const cborKey = ((_a = result.keyInfo) === null || _a === void 0 ? void 0 : _a.key) ? CoseKeyCbor.Static.fromDTO(result.keyInfo.key) : undefined; if (!cborKey) { throw Error('No key found in result'); } let jwk = CoseJoseKeyMappingService.toJoseJwk(cborKey).toJsonDTO(); if (!((_b = result.keyInfo) === null || _b === void 0 ? void 0 : _b.kmsKeyRef)) { const keyInfo = result.keyInfo; const kid = (_c = jwk.kid) !== null && _c !== void 0 ? _c : (0, ssi_sdk_ext_key_utils_1.calculateJwkThumbprint)({ jwk: jwk }); const key = yield _context.agent.keyManagerGet({ kid }); const kms = key.kms; const kmsKeyRef = (_d = key.meta) === null || _d === void 0 ? void 0 : _d.kmsKeyRef; const updateCborKey = cborKey.copy(false, cborKey.kty, (_e = cborKey.kid) !== null && _e !== void 0 ? _e : new CborByteString(decodeFrom(kid, Encoding.UTF8))); const deviceKeyInfo = KeyInfo.Static.fromDTO(keyInfo).copy(kid, updateCborKey, keyInfo.opts, keyInfo.keyVisibility, keyInfo.signatureAlgorithm, keyInfo.x5c, kmsKeyRef, kms); const updateMatch = match.copy(match.inputDescriptor, match.document, match.documentError, deviceKeyInfo); match = updateMatch; } } catch (e) { console.log(`We tied to ammend key info from the KMS, but failed. Potential trouble ahead ${e.message}`, e); } docsAndDescriptors.push(match); } else if (result.error) { lastError = result; } } } if (docsAndDescriptors.length === 0) { if (lastError) { return Promise.reject(Error((_f = lastError.verifications[0].message) !== null && _f !== void 0 ? _f : 'No matching documents found')); } return Promise.reject(Error('No matching documents found')); } const deviceResponse = yield oid4vpService.createDeviceResponse(docsAndDescriptors, presentationDefinition, clientId, responseUri, authorizationRequestNonce); const vp_token = encodeTo(deviceResponse.cborEncode(), Encoding.BASE64URL); const presentation_submission = Oid4VPPresentationSubmission.Static.fromPresentationDefinition(presentationDefinition); return { vp_token, presentation_submission }; }); } /** * Verifies on the Relying Party (RP) side for mdoc (mobile document) OIDC4VP (OpenID Connect for Verifiable Presentations). * * @param {MdocOid4vpRPVerifyArgs} args - The arguments required for verification, including the vp_token, presentation_submission, and trustAnchors. * @param {IRequiredContext} _context - The required context for this method. * @return {Promise<MdocOid4vpRPVerifyResult>} - A promise that resolves to an object containing error status, * validated documents, and the original presentation submission. */ mdocOid4vpRPVerify(args, _context) { return __awaiter(this, void 0, void 0, function* () { const { vp_token, presentation_submission, trustAnchors } = args; const deviceResponse = DeviceResponseCbor.Static.cborDecode(decodeFrom(vp_token, Encoding.BASE64URL)); if (!deviceResponse.documents) { return Promise.reject(Error(`No documents found in vp_token`)); } let error = false; const documents = yield Promise.all(deviceResponse.documents.map((document) => __awaiter(this, void 0, void 0, function* () { try { const validations = yield MdocValidations.fromDocumentAsync(document, null, trustAnchors !== null && trustAnchors !== void 0 ? trustAnchors : this.trustAnchors); if (!validations || validations.error) { error = true; } if (presentation_submission.descriptor_map.find((m) => m.id === document.docType.value) === null) { error = true; validations.verifications.push({ name: 'mdoc', error, critical: error, message: `No descriptor map id with document type ${document.docType.value} present`, }); } return { document: document.toJson(), validations }; } catch (e) { error = true; return { document: document.toJson(), validations: { error: true, verifications: [ { name: 'mdoc', error, critical: true, message: e.message, }, ], }, }; } }))); if (error) { console.log(JSON.stringify(documents, null, 2)); } return { error, documents, presentation_submission }; }); } /** * Verifies the issuer-signed Mobile Document (mDoc) using the provided arguments and context. * * @param {MdocVerifyIssuerSignedArgs} args - The arguments required for verification, including input and key information. * @param {IRequiredContext} context - The context encompassing necessary dependencies and configurations. * @return {Promise<IVerifySignatureResult<KeyType>>} A promise that resolves to the result of the signature verification, including key information if available. */ mdocVerifyIssuerSigned(args, context) { return __awaiter(this, void 0, void 0, function* () { const { input, keyInfo, requireX5Chain } = args; const coseKeyInfo = keyInfo && CoseJoseKeyMappingService.toCoseKeyInfo(keyInfo); const verification = yield new CoseCryptoServiceJS(new functions_1.CoseCryptoService(context)).verify1(CoseSign1Json.Static.fromDTO(input).toCbor(), coseKeyInfo, requireX5Chain); return Object.assign(Object.assign({}, verification), { keyInfo: keyInfo }); }); } /** * Verifies an X.509 certificate chain against a set of trust anchors. * * @param {VerifyCertificateChainArgs} args - The arguments required for verifying the certificate chain. * This includes the certificate chain to be verified and any additional trust anchors to be used. * @param {IRequiredContext} _context - The context required for verification, including necessary dependencies and settings. * @return {Promise<X509ValidationResult>} A promise that resolves to the result of the validation process, indicating the success or failure of the certificate chain verification. */ x509VerifyCertificateChain(args, _context) { return __awaiter(this, void 0, void 0, function* () { var _a; const mergedAnchors = [...this.trustAnchors, ...((_a = args.trustAnchors) !== null && _a !== void 0 ? _a : [])]; const trustAnchors = new Set(mergedAnchors); const validationResult = yield new functions_1.X509CallbackService(Array.from(mergedAnchors)).verifyCertificateChain(Object.assign(Object.assign({}, args), { trustAnchors: Array.from(trustAnchors), opts: Object.assign(Object.assign({}, args === null || args === void 0 ? void 0 : args.opts), this.opts) })); console.log(`x509 validation for ${validationResult.error ? 'Error' : 'Success'}. message: ${validationResult.message}, details: ${validationResult.detailMessage}`); return validationResult; }); } /** * Extracts information from a list of X509 certificates. * * @param {GetX509CertificateInfoArgs} args - Arguments required to retrieve certificate information, * including the certificates and optional Subject Alternative Name (SAN) type filter. * @param {IRequiredContext} context - The context required for the operation, which may include * logging, configuration, and other operational details. * @return {Promise<CertificateInfo[]>} A promise that resolves with an array of certificate * information objects, each containing details extracted from individual certificates. */ x509GetCertificateInfo(args, context) { return __awaiter(this, void 0, void 0, function* () { const certificates = args.certificates.map((cert) => (0, ssi_sdk_ext_x509_utils_1.pemOrDerToX509Certificate)(cert)); return yield Promise.all(certificates.map((cert) => (0, ssi_sdk_ext_x509_utils_1.getCertificateInfo)(cert, args.sanTypeFilter && { sanTypeFilter: args.sanTypeFilter }))); }); } } exports.MDLMdoc = MDLMdoc; //# sourceMappingURL=mDLMdoc.js.map