UNPKG

@sphereon/ssi-sdk.mdl-mdoc

Version:

260 lines • 14.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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.X509CallbackService = exports.CoseCryptoService = 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 crypto = __importStar(require("crypto")); const pkijs_1 = require("pkijs"); const u8a = __importStar(require("uint8arrays")); var CoseKeyCbor = kmp_mdoc_core_1.com.sphereon.crypto.cose.CoseKeyCbor; var CoseJoseKeyMappingService = kmp_mdoc_core_1.com.sphereon.crypto.CoseJoseKeyMappingService; var DefaultCallbacks = kmp_mdoc_core_1.com.sphereon.crypto.DefaultCallbacks; var SignatureAlgorithm = kmp_mdoc_core_1.com.sphereon.crypto.generic.SignatureAlgorithm; var KeyInfo = kmp_mdoc_core_1.com.sphereon.crypto.KeyInfo; var ResolvedKeyInfo = kmp_mdoc_core_1.com.sphereon.crypto.ResolvedKeyInfo; 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; class CoseCryptoService { constructor(context) { this.context = context; } setContext(context) { this.context = context; } signAsync(input, requireX5Chain) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; if (!this.context) { throw Error('No context provided. Please provide a context with the setContext method or constructor'); } const { keyInfo, alg, value } = input; let kmsKeyRef = (_a = keyInfo.kmsKeyRef) !== null && _a !== void 0 ? _a : undefined; if (!kmsKeyRef) { const key = keyInfo.key; if (key == null) { return Promise.reject(Error('No key present in keyInfo. This implementation cannot sign without a key!')); } const resolvedKeyInfo = ResolvedKeyInfo.Static.fromKeyInfo(keyInfo, key); const jwkKeyInfo = CoseJoseKeyMappingService.toResolvedJwkKeyInfo(resolvedKeyInfo); const kid = (_c = (_b = jwkKeyInfo.kid) !== null && _b !== void 0 ? _b : (0, ssi_sdk_ext_key_utils_1.calculateJwkThumbprint)({ jwk: jwkKeyInfo.key.toJsonDTO() })) !== null && _c !== void 0 ? _c : jwkKeyInfo.key.getKidAsString(true); if (!kid) { return Promise.reject(Error('No kid present and not kmsKeyRef provided')); } kmsKeyRef = kid; } const result = yield this.context.agent.keyManagerSign({ algorithm: alg.jose.value, data: encodeTo(value, Encoding.UTF8), encoding: 'utf-8', keyRef: kmsKeyRef, }); return decodeFrom(result, Encoding.UTF8); }); } verify1Async(input, keyInfo, requireX5Chain) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; const getCertAndKey = (x5c) => __awaiter(this, void 0, void 0, function* () { if (requireX5Chain && (!x5c || x5c.length === 0)) { // We should not be able to get here anyway, as the MLD-mdoc library already validated at this point. But let's make sure return Promise.reject(new Error(`No x5chain was present in the CoseSign headers!`)); } // TODO: According to the IETF spec there should be a x5t in case the x5chain is in the protected headers. In the Funke this does not seem to be done/used! issuerCert = x5c ? (0, ssi_sdk_ext_x509_utils_1.pemOrDerToX509Certificate)(x5c[0]) : undefined; let issuerJwk; if (issuerCert) { const info = yield (0, ssi_sdk_ext_x509_utils_1.getCertificateInfo)(issuerCert); issuerJwk = info.publicKeyJWK; } return { issuerCert, issuerJwk }; }); const coseKeyInfo = CoseJoseKeyMappingService.toCoseKeyInfo(keyInfo); if ((_a = coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.key) === null || _a === void 0 ? void 0 : _a.d) { throw Error('Do not use private keys to verify!'); } else if (!((_b = input.payload) === null || _b === void 0 ? void 0 : _b.value)) { return Promise.reject(Error('Signature validation without payload not supported')); } const sign1Json = input.toJson(); // Let's make it a bit easier on ourselves, instead of working with CBOR const coseAlg = sign1Json.protectedHeader.alg; if (!coseAlg) { return Promise.reject(Error('No alg protected header present')); } let issuerCert; let issuerCoseKey; let kid = (_d = (_c = coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.kid) !== null && _c !== void 0 ? _c : sign1Json.protectedHeader.kid) !== null && _d !== void 0 ? _d : (_e = sign1Json.unprotectedHeader) === null || _e === void 0 ? void 0 : _e.kid; // Please note this method does not perform chain validation. The MDL-MSO_MDOC library already performed this before this step const x5c = (_j = (_g = (_f = coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.key) === null || _f === void 0 ? void 0 : _f.getX509CertificateChain()) !== null && _g !== void 0 ? _g : (_h = sign1Json.protectedHeader) === null || _h === void 0 ? void 0 : _h.x5chain) !== null && _j !== void 0 ? _j : (_k = sign1Json.unprotectedHeader) === null || _k === void 0 ? void 0 : _k.x5chain; if (!coseKeyInfo || !(coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.key) || ((_l = coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.key) === null || _l === void 0 ? void 0 : _l.x5chain)) { const certAndKey = yield getCertAndKey(x5c); issuerCoseKey = certAndKey.issuerJwk ? CoseJoseKeyMappingService.toCoseKey(certAndKey.issuerJwk) : undefined; issuerCert = certAndKey.issuerCert; } if (!issuerCoseKey) { if (!(coseKeyInfo === null || coseKeyInfo === void 0 ? void 0 : coseKeyInfo.key)) { return Promise.reject(Error(`Either a x5c needs to be in the headers, or you need to provide a key for verification`)); } if (kid === null) { kid = coseKeyInfo.key.getKidAsString(false); } issuerCoseKey = CoseKeyCbor.Static.fromDTO(coseKeyInfo.key); } const issuerCoseKeyInfo = new KeyInfo(kid, issuerCoseKey, coseKeyInfo.opts, coseKeyInfo.keyVisibility, (_m = issuerCoseKey.getSignatureAlgorithm()) !== null && _m !== void 0 ? _m : coseKeyInfo.signatureAlgorithm, x5c, coseKeyInfo.kmsKeyRef, coseKeyInfo.kms, (_o = coseKeyInfo.keyType) !== null && _o !== void 0 ? _o : issuerCoseKey.getKty()); const recalculatedToBeSigned = input.toBeSignedJson(issuerCoseKeyInfo, SignatureAlgorithm.Static.fromCose(coseAlg)); const key = CoseJoseKeyMappingService.toJoseJwk(issuerCoseKeyInfo.key).toJsonDTO(); const valid = yield (0, ssi_sdk_ext_key_utils_1.verifyRawSignature)({ data: u8a.fromString(recalculatedToBeSigned.base64UrlValue, 'base64url'), signature: u8a.fromString(sign1Json.signature, 'base64url'), key, }); return { name: 'mdoc', critical: true, error: !valid, message: `Signature of '${issuerCert ? (0, ssi_sdk_ext_x509_utils_1.getSubjectDN)(issuerCert).DN : kid}' was ${valid ? '' : 'in'}valid`, keyInfo: issuerCoseKeyInfo, }; }); } resolvePublicKeyAsync(keyInfo) { if (keyInfo.key) { return Promise.resolve(CoseJoseKeyMappingService.toResolvedKeyInfo(keyInfo, keyInfo.key)); } return Promise.reject(Error('No key present in keyInfo. This implementation cannot resolve public keys on its own currently!')); } } exports.CoseCryptoService = CoseCryptoService; /** * This class can be used for X509 validations. * Either have an instance per trustedCerts and verification invocation or use a single instance and provide the trusted certs in the method argument * * The class is also registered with the low-level mDL/mdoc Kotlin Multiplatform library * Next to the specific function for the library it exports a more powerful version of the same verification method as well */ class X509CallbackService { constructor(trustedCerts) { this.setTrustedCerts = (trustedCertsInPEM) => { this._trustedCerts = trustedCertsInPEM === null || trustedCertsInPEM === void 0 ? void 0 : trustedCertsInPEM.map((cert) => { if (cert.includes('CERTIFICATE')) { // PEM return cert; } return (0, ssi_sdk_ext_x509_utils_1.derToPEM)(cert); }); }; this.getTrustedCerts = () => this._trustedCerts; this.setTrustedCerts(trustedCerts); } /** * A more powerful version of the method below. Allows to verify at a specific time and returns more information * @param chain * @param trustAnchors * @param verificationTime */ verifyCertificateChain(_a) { return __awaiter(this, arguments, void 0, function* ({ chain, trustAnchors = this.getTrustedCerts(), verificationTime, opts, }) { return yield (0, ssi_sdk_ext_x509_utils_1.validateX509CertificateChain)({ chain, trustAnchors, verificationTime, opts, }); }); } /** * This method is the implementation used within the mDL/Mdoc library */ verifyCertificateChainJS(chainDER, chainPEM, trustedCerts, verificationProfile, verificationTime) { return __awaiter(this, void 0, void 0, function* () { var _a; const verificationAt = verificationTime !== null && verificationTime !== void 0 ? verificationTime : DateTimeUtils.Static.DEFAULT.dateTimeLocal(); let chain = []; if (chainDER && chainDER.length > 0) { chain = chainDER.map((der) => Uint8Array.from(der)); } if (chainPEM && chainPEM.length > 0) { chain = (chain !== null && chain !== void 0 ? chain : []).concat(chainPEM); } const result = yield (0, ssi_sdk_ext_x509_utils_1.validateX509CertificateChain)({ chain: chain, // The function will handle an empty array trustAnchors: trustedCerts !== null && trustedCerts !== void 0 ? trustedCerts : this.getTrustedCerts(), verificationTime: new Date(verificationAt.toEpochSeconds().toULong() * 1000), opts: { trustRootWhenNoAnchors: true }, }); const cert = result.certificateChain ? result.certificateChain[result.certificateChain.length - 1] : undefined; return { publicKey: cert === null || cert === void 0 ? void 0 : cert.publicKeyJWK, // fixme publicKeyAlgorithm: (_a = cert === null || cert === void 0 ? void 0 : cert.publicKeyJWK) === null || _a === void 0 ? void 0 : _a.alg, name: 'x.509', critical: result.critical, message: result.message, error: result.error, verificationTime: verificationAt, }; }); } } exports.X509CallbackService = X509CallbackService; const defaultCryptoEngine = () => { if (typeof self !== 'undefined') { if ('crypto' in self) { let engineName = 'webcrypto'; if ('webkitSubtle' in self.crypto) { engineName = 'safari'; } // @ts-ignore (0, pkijs_1.setEngine)(engineName, new pkijs_1.CryptoEngine({ name: engineName, crypto: crypto })); } } else if (typeof crypto !== 'undefined' && 'webcrypto' in crypto) { const name = 'NodeJS ^15'; const nodeCrypto = crypto.webcrypto; // @ts-ignore (0, pkijs_1.setEngine)(name, new pkijs_1.CryptoEngine({ name, crypto: nodeCrypto })); } else { // @ts-ignore const name = 'crypto'; (0, pkijs_1.setEngine)(name, new pkijs_1.CryptoEngine({ name, crypto: (0, ssi_sdk_ext_key_utils_1.globalCrypto)(false) })); } }; defaultCryptoEngine(); // We register the services with the mDL/mdoc library. Please note that the context is not passed in, meaning we cannot sign by default. DefaultCallbacks.setCoseCryptoDefault(new CoseCryptoService()); DefaultCallbacks.setX509Default(new X509CallbackService()); //# sourceMappingURL=index.js.map