"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const sgx_native_module_1 = require("../sgx-native-module");
const constants_1 = require("./constants");
// TODO: SP-323 check if possible to remove one of this libs
const x509_1 = require("@fidm/x509");
// @ts-ignore no types for this lib
const asn1_tree_1 = __importDefault(require("asn1-tree"));
class SGXUntrustedValidator {
    constructor() {
        this.dqvc = new sgx_native_module_1.DcapQuoteVerifyService();
    }
    async validate(quote) {
        // Extract certs from quote
        const quoteCerts = await this.dqvc.getQeCertificationData({ quote });
        const [pckCert, intermediateCert, rootCert] = this.splitChain(quoteCerts.pemCertChain);
        // Fetch CRLs and real root cert
        const [rootCrl, { intermediateCrl, realRootCert }] = await Promise.all([
            this.fetchRootCrl(rootCert),
            this.fetchIntermediateCrl(),
        ]);
        // Compare root cert from quote with root cert from Intel
        if (rootCert !== realRootCert)
            throw new Error("Invalid root certificate");
        // Validate pck cert
        const expirationDate = Math.floor(Date.now() / 1000);
        await this.dqvc.validatePckCertificate({
            pemCertChain: pckCert + intermediateCert + rootCert,
            pemIntermediateCaCrl: intermediateCrl,
            pemRootCaCrl: rootCrl,
            pemRootCaCertificate: rootCert,
            expirationCheckDate: expirationDate,
        });
        // Fetch and validate TCB info and enclave identity
        const [tcbInfo, qeIdentity] = await Promise.all([
            this.fetchTcbInfo(pckCert, rootCert, rootCrl),
            this.fetchQeIdentity(rootCert, rootCrl),
        ]);
        // Validate quote
        await this.dqvc.validateQuote({
            quote,
            pemPckCertificate: pckCert,
            pemIntermediateCrl: intermediateCrl,
            tcbInfoJson: tcbInfo,
            qeIdentityJson: qeIdentity,
        });
    }
    async fetchRootCrl(rootCert) {
        // Extract root CRL url form root cert
        const rootCertParsed = x509_1.Certificate.fromPEM(Buffer.from(rootCert));
        const rootCertExtension = rootCertParsed.extensions.find((item) => item.oid === constants_1.CRL_URL_EXTENSION_OID);
        if (!rootCertExtension)
            throw new Error("CRL url not found in root certificate");
        let rootCrlUrl = Buffer.from(rootCertExtension.value).toString();
        rootCrlUrl = rootCrlUrl.substring(rootCrlUrl.indexOf("http"), rootCrlUrl.length);
        // Fetch root CRL
        const rootCrlResult = await axios_1.default.get(rootCrlUrl, { responseType: "arraybuffer" });
        let rootCrl = rootCrlResult.data;
        // Convert to pem from der
        rootCrl = `-----BEGIN X509 CRL-----\n${rootCrl
            .toString("base64")
            .match(/.{0,64}/g)
            .join("\n")}-----END X509 CRL-----`;
        return rootCrl;
    }
    async fetchIntermediateCrl() {
        const intermediateCrlResult = await axios_1.default.get(`${constants_1.BASE_URL}/pckcrl?ca=platform&encoding=pem`);
        // Get root cert from intel response
        const mainChain = decodeURIComponent(intermediateCrlResult.headers["sgx-pck-crl-issuer-chain"]);
        const [, realRootCert] = this.splitChain(mainChain);
        return {
            intermediateCrl: intermediateCrlResult.data,
            realRootCert,
        };
    }
    async fetchTcbInfo(pckCert, rootCert, rootCrl) {
        // Extract FMSPC from pck cert
        const pckCertParsed = x509_1.Certificate.fromPEM(Buffer.from(pckCert));
        const fmspcExtensions = pckCertParsed.extensions
            .map((item) => {
            // Check top level oid part
            if (constants_1.FMSPC_EXTENSION_OID.indexOf(item.oid) !== 0)
                return null;
            // Get deep oid part
            const oidItemsLeft = constants_1.FMSPC_EXTENSION_OID.substring(item.oid.length, constants_1.FMSPC_EXTENSION_OID.length)
                .split(".")
                .filter((item) => item);
            // Return value, if full oid found on top level
            if (!oidItemsLeft.length)
                return item.value;
            // Decode deep tree structure
            let fmspcTreeItem = asn1_tree_1.default.decode(item.value);
            try {
                // Find deep item in tree by oid part
                oidItemsLeft.forEach((key) => (fmspcTreeItem = fmspcTreeItem.elements[+key - 1]));
                // Return value (second sub item, oid first sub item) of deep item
                return fmspcTreeItem.elements[1].value;
            }
            catch (e) {
                return null;
            }
        })
            .filter((item) => item);
        if (!fmspcExtensions.length)
            throw new Error("FMSPC not found in pck certificate");
        const FMSPC = Buffer.from(fmspcExtensions[0]).toString("hex");
        // Fetch TCB info and issuer chain
        const tcbInfoResult = await axios_1.default.get(`${constants_1.BASE_URL}/tcb?fmspc=${FMSPC}`);
        const tcbInfo = JSON.stringify(tcbInfoResult.data);
        const tcbInfoChain = decodeURIComponent(tcbInfoResult.headers["sgx-tcb-info-issuer-chain"]);
        // Validate TCB info
        const expirationDate = Math.floor(Date.now() / 1000);
        await this.dqvc.validateTcbInfo({
            tcbInfoJson: tcbInfo,
            pemCertChain: tcbInfoChain,
            pemRootCaCrl: rootCrl,
            pemRootCaCertificate: rootCert,
            expirationCheckDate: expirationDate,
        });
        return tcbInfo;
    }
    async fetchQeIdentity(rootCert, rootCrl) {
        // Fetch enclave identity and issuer chain
        const qeIdentityResult = await axios_1.default.get(`${constants_1.BASE_URL}/qe/identity`);
        const qeIdentity = JSON.stringify(qeIdentityResult.data);
        const qeIdentityChain = decodeURIComponent(qeIdentityResult.headers["sgx-enclave-identity-issuer-chain"]);
        // Validate enclave identity
        const expirationDate = Math.floor(Date.now() / 1000);
        await this.dqvc.validateEnclaveIdentity({
            enclaveIdentityJson: qeIdentity,
            pemCertChain: qeIdentityChain,
            pemRootCaCrl: rootCrl,
            pemRootCaCertificate: rootCert,
            expirationCheckDate: expirationDate,
        });
        return qeIdentity;
    }
    splitChain(chain) {
        return chain
            .split("-----BEGIN CERTIFICATE-----")
            .filter((cert) => cert)
            .map((cert) => `-----BEGIN CERTIFICATE-----` + cert);
    }
}
exports.default = SGXUntrustedValidator;
//# sourceMappingURL=data:application/json;base64, 