snowflake-sdk
Version:
Node.js driver for Snowflake
124 lines • 5.95 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CertificateRevokedError = exports.CRL_VALIDATOR_INTERNAL = void 0;
exports.isCrlValidationEnabled = isCrlValidationEnabled;
exports.corkSocketAndValidateCrl = corkSocketAndValidateCrl;
exports.validateCrl = validateCrl;
const asn1_js_rfc5280_1 = __importDefault(require("asn1.js-rfc5280"));
const crypto_1 = __importDefault(require("crypto"));
const logger_1 = __importDefault(require("../../logger"));
const certificate_utils_1 = require("./certificate_utils");
const crl_fetcher_1 = require("./crl_fetcher");
const crl_signature_verifier_1 = require("./crl_signature_verifier");
const errors_1 = require("../../errors");
// Allows to mock/spy internal calls in tests
exports.CRL_VALIDATOR_INTERNAL = {
validateCrl: (...args) => validateCrl(...args),
};
class CertificateRevokedError extends Error {
constructor(message) {
super(message);
this.name = 'CertificateRevokedError';
}
}
exports.CertificateRevokedError = CertificateRevokedError;
function isCrlValidationEnabled(config) {
return config.checkMode !== 'DISABLED';
}
function corkSocketAndValidateCrl(socket, config) {
socket.once('secureConnect', async () => {
const certChain = socket.getPeerCertificate(true);
try {
await exports.CRL_VALIDATOR_INTERNAL.validateCrl(certChain, config);
}
catch (error) {
if (!(error instanceof CertificateRevokedError) && config.checkMode === 'ADVISORY') {
(0, logger_1.default)().warn('Failed to check CRL revocation, but checkMode=ADVISORY. Allowing connection. Error: %j', error);
}
else {
// NOTE: Wrap error into CrlError to prevent retries
socket.destroy((0, errors_1.createCrlError)(error));
}
}
socket.uncork();
});
socket.cork();
}
function* iterateCertChain(cert) {
let current = cert;
while (current) {
if (current === current.issuerCertificate) {
break; // Root is self-signed, ignoring
}
yield current;
current = current.issuerCertificate;
}
}
// NOTE:
// Sticking with asn1.js-rfc5280 + custom signature validation, because popular libraries have issues:
// - jsrsasign: has outdated crypto library with CEV issues
// - pkijs: takes 4 seconds to parse 9Mb CRL
// - @peculiar/x509: takes 2.5 seconds to parse 9Mb CRL
async function validateCrl(certChain, config) {
for (const certificate of iterateCertChain(certChain)) {
const decodedCertificate = asn1_js_rfc5280_1.default.Certificate.decode(certificate.raw, 'der');
const name = (0, certificate_utils_1.getCertificateDebugName)(certificate);
const logDebug = (msg) => (0, logger_1.default)().debug(`validateCrl[${name}]: ${msg}`);
logDebug('starting validation');
if ((0, certificate_utils_1.isShortLivedCertificate)(decodedCertificate)) {
logDebug('certificate is short-lived, skipping');
continue;
}
logDebug('getting CRL distribution points');
const crlUrls = (0, certificate_utils_1.getCertificateCrlUrls)(name, decodedCertificate);
if (!crlUrls) {
if (config.allowCertificatesWithoutCrlURL) {
logDebug('certificate has no CRL distribution points, skipping');
continue;
}
throw new Error(`Certificate ${name} does not have CRL http URL. This could be disabled with allowCertificatesWithoutCrlURL`);
}
const decodedIssuerCertificate = asn1_js_rfc5280_1.default.Certificate.decode(certificate.issuerCertificate.raw, 'der');
const issuerSubject = JSON.stringify(decodedIssuerCertificate.tbsCertificate.subject);
const issuerPublicKey = crypto_1.default
.createPublicKey({
key: certificate.issuerCertificate.pubkey,
format: 'der',
type: 'spki',
})
.export({ format: 'pem', type: 'spki' });
for (const crlUrl of crlUrls) {
logDebug(`fetching ${crlUrl}`);
const crl = await (0, crl_fetcher_1.getCrl)(crlUrl, {
inMemoryCache: config.inMemoryCache,
onDiskCache: config.onDiskCache,
});
logDebug(`validating ${crlUrl} signature`);
if (!(0, crl_signature_verifier_1.isCrlSignatureValid)(crl, issuerPublicKey)) {
throw new Error(`CRL ${crlUrl} signature is invalid. Expected signature by ${(0, certificate_utils_1.getCertificateDebugName)(certificate.issuerCertificate)}`);
}
logDebug(`validating ${crlUrl} issuingDistributionPoint extension`);
if (!(0, certificate_utils_1.isIssuingDistributionPointExtensionValid)(crl, crlUrl)) {
throw new Error(`CRL ${crlUrl} issuingDistributionPoint extension is invalid`);
}
logDebug(`validating ${crlUrl} issuer`);
const crlIssuer = JSON.stringify(crl.tbsCertList.issuer);
if (issuerSubject !== crlIssuer) {
throw new Error(`CRL ${crlUrl} issuer is invalid. Expected ${issuerSubject} but got ${crlIssuer}`);
}
logDebug(`validating ${crlUrl} nextUpdate`);
if (crl.tbsCertList.nextUpdate.value < Date.now()) {
throw new Error(`CRL ${crlUrl} nextUpdate is expired`);
}
logDebug(`checking if certificate is revoked in ${crlUrl}`);
if ((0, certificate_utils_1.isCertificateRevoked)(decodedCertificate, crl)) {
throw new CertificateRevokedError(`Certificate ${name} is revoked in ${crlUrl}`);
}
}
}
return true;
}
//# sourceMappingURL=index.js.map