"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const crypto = require("crypto");
const https = require("https");
const rs = require('jsrsasign');
const pinningData_1 = require("./pinningData");
function buildCert(buffer) {
    return `-----BEGIN CERTIFICATE-----\n${buffer.toString('base64')}\n-----END CERTIFICATE-----`;
}
exports.buildCert = buildCert;
function getDERFormattedCertificate(url) {
    return new Promise((resolve, reject) => {
        try {
            const request = https.get(url, () => {
                const certificate = request.socket.getPeerCertificate(true);
                resolve(certificate.raw);
            });
        }
        catch (error) {
            reject(error);
        }
    });
}
exports.getDERFormattedCertificate = getDERFormattedCertificate;
function getFingerprint(derCert) {
    const derBinary = derCert.toString('binary');
    const hexDerFileContents = rs.rstrtohex(derBinary);
    const pemString = rs.KJUR.asn1.ASN1Util.getPEMStringFromHex(hexDerFileContents, 'CERTIFICATE');
    const publicKey = rs.X509.getPublicKeyInfoPropOfCertPEM(pemString);
    const publicKeyBytes = Buffer.from(publicKey.keyhex, 'hex').toString('binary');
    return crypto
        .createHash('sha256')
        .update(publicKeyBytes)
        .digest('base64');
}
exports.getFingerprint = getFingerprint;
function hostnameShouldBePinned(hostname) {
    return pinningData_1.PINS.some(pin => pin.url.test(hostname.toLowerCase().trim()));
}
exports.hostnameShouldBePinned = hostnameShouldBePinned;
function verifyPinning(hostname, certificate) {
    const { data: certData, issuerCert: { data: issuerCertData }, } = certificate;
    let issuerCertHex;
    let publicKey;
    let publicKeyBytes;
    let publicKeyFingerprint;
    const errorMessages = [];
    try {
        issuerCertHex = rs.pemtohex(issuerCertData);
        publicKey = rs.X509.getPublicKeyInfoPropOfCertPEM(certData);
        publicKeyBytes = Buffer.from(publicKey.keyhex, 'hex').toString('binary');
        publicKeyFingerprint = crypto
            .createHash('sha256')
            .update(publicKeyBytes)
            .digest('base64');
    }
    catch (error) {
        return {
            decoding: false,
            errorMessage: error,
        };
    }
    const result = {};
    for (const pin of pinningData_1.PINS) {
        const { url, publicKeyInfo = [], issuerRootPubkeys = [] } = pin;
        if (url.test(hostname.toLowerCase().trim())) {
            if (issuerRootPubkeys.length > 0) {
                result.verifiedIssuerRootPubkeys = issuerRootPubkeys.some(pubkey => rs.X509.verifySignature(issuerCertHex, rs.KEYUTIL.getKey(pubkey)));
                if (!result.verifiedIssuerRootPubkeys) {
                    const pubkeysCombined = issuerRootPubkeys.join(', ');
                    const errorMessage = `Issuer root public key signatures: none of "${pubkeysCombined}" could be verified.`;
                    errorMessages.push(errorMessage);
                }
            }
            result.verifiedPublicKeyInfo = publicKeyInfo
                .reduce((arr, pubkey) => {
                const { algorithmID: knownAlgorithmID, algorithmParam: knownAlgorithmParam, fingerprints: knownFingerprints, } = pubkey;
                const fingerprintCheck = knownFingerprints.length > 0 &&
                    knownFingerprints.some(knownFingerprint => knownFingerprint === publicKeyFingerprint);
                const algorithmIDCheck = knownAlgorithmID === publicKey.algoid;
                const algorithmParamCheck = knownAlgorithmParam === publicKey.algparam;
                if (!fingerprintCheck) {
                    const fingerprintsCombined = knownFingerprints.join(', ');
                    const errorMessage = `Public key fingerprints: "${publicKeyFingerprint}" could not be verified with any of the known fingerprints "${fingerprintsCombined}".`;
                    errorMessages.push(errorMessage);
                }
                if (!algorithmIDCheck) {
                    const algorithmID = publicKey.algoid;
                    const errorMessage = `Algorithm ID: "${algorithmID}" could not be verified with the known ID "${knownAlgorithmID}".`;
                    errorMessages.push(errorMessage);
                }
                if (!algorithmParamCheck) {
                    const algorithmParam = publicKey.algparam;
                    const errorMessage = `Algorithm parameter: "${algorithmParam}" could not be verified with the known parameter "${knownAlgorithmParam}".`;
                    errorMessages.push(errorMessage);
                }
                arr.push(fingerprintCheck, algorithmIDCheck, algorithmParamCheck);
                return arr;
            }, [])
                .every(value => Boolean(value));
            break;
        }
    }
    if (errorMessages.length > 0) {
        result.errorMessage = errorMessages.join('\n');
        result.certificate = certificate;
    }
    return result;
}
exports.verifyPinning = verifyPinning;
//# sourceMappingURL=CertUtil.js.map