@sphereon/ssi-sdk.mdl-mdoc
Version:
251 lines • 15.8 kB
JavaScript
;
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