remove ocsp code (#649)
This commit is contained in:
Родитель
f37a6d245e
Коммит
a79b92f5bd
|
@ -1,47 +0,0 @@
|
|||
|
||||
/** Declaration file generated by dts-gen */
|
||||
|
||||
import * as http from "http";
|
||||
import { DetailedPeerCertificate, Certificate, TlsOptions } from "tls";
|
||||
import { HttpsProxyAgentOptions } from "https-proxy-agent";
|
||||
|
||||
export class Agent extends http.Agent {
|
||||
public constructor(options: any);
|
||||
public fetchIssuer(peerCert: DetailedPeerCertificate, stapling: Certificate, cb: (error: string, result: DetailedPeerCertificate) => void): void;
|
||||
}
|
||||
|
||||
export function check(options: any, cb: (error: Error, res: any) => void): any;
|
||||
export function verify(options: VerifyOptions, cb: (error: string, res: any) => void): void;
|
||||
|
||||
export interface Response {
|
||||
start: any;
|
||||
end: any;
|
||||
value: any;
|
||||
certs: any;
|
||||
certsTbs: any;
|
||||
}
|
||||
|
||||
export interface Request {
|
||||
id: Buffer;
|
||||
certID: any;
|
||||
data: any;
|
||||
|
||||
// Just to avoid re-parsing DER
|
||||
cert: any;
|
||||
issuer: any;
|
||||
}
|
||||
|
||||
export interface VerifyOptions {
|
||||
request: Request;
|
||||
response: Buffer;
|
||||
}
|
||||
|
||||
export class utils {
|
||||
public static parseResponse(response: any): Response;
|
||||
public static getAuthorityInfo(cert: DetailedPeerCertificate, ocspMethod: string, cb: (err: string, uri: string) => void): void;
|
||||
public static getResponse(httpOptions: http.RequestOptions | string, data: any, cb: (err: string, raw: Buffer) => void): void;
|
||||
}
|
||||
|
||||
export class request {
|
||||
public static generate(rawCert: Buffer, rawIssuer: Buffer): Request;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
exports.Cache = require('./ocsp/cache');
|
||||
exports.Agent = require('./ocsp/agent');
|
||||
|
||||
exports.getOCSPURI = require('./ocsp/api').getOCSPURI;
|
||||
|
||||
exports.request = require('./ocsp/request');
|
||||
exports.check = require('./ocsp/check');
|
||||
exports.verify = require('./ocsp/verify');
|
||||
exports.utils = require('./ocsp/utils');
|
|
@ -1,160 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
|
||||
var util = require('util');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var rfc5280 = require('asn1.js-rfc5280');
|
||||
var SimpleCache = require('simple-lru-cache');
|
||||
|
||||
function Agent(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
https.Agent.call(this, options);
|
||||
|
||||
this.caCache = new SimpleCache({ maxSize: options.CACacheSize || 1024 });
|
||||
}
|
||||
module.exports = Agent;
|
||||
util.inherits(Agent, https.Agent);
|
||||
|
||||
Agent.prototype.createConnection = function createConnection(port,
|
||||
host,
|
||||
options) {
|
||||
|
||||
if (port !== null && typeof port === 'object') {
|
||||
options = port;
|
||||
port = null;
|
||||
} else if (host !== null && typeof host === 'object') {
|
||||
options = host;
|
||||
host = null;
|
||||
} else if (options === null || typeof options !== 'object')
|
||||
options = {};
|
||||
|
||||
if (typeof port === 'number')
|
||||
options.port = port;
|
||||
|
||||
if (typeof host === 'string')
|
||||
options.host = host;
|
||||
|
||||
var ocspOptions = util._extend({ requestOCSP: true }, options);
|
||||
var socket = https.Agent.prototype.createConnection.call(
|
||||
this, port, host, ocspOptions);
|
||||
|
||||
var self = this;
|
||||
var stapling = null;
|
||||
socket.on('OCSPResponse', function(data) {
|
||||
stapling = data;
|
||||
});
|
||||
|
||||
socket.on('secure', function() {
|
||||
return self.handleOCSPResponse(socket, stapling, function(err) {
|
||||
if (err)
|
||||
return socket.destroy(err);
|
||||
|
||||
// Time to allow all writes!
|
||||
socket.uncork();
|
||||
});
|
||||
});
|
||||
|
||||
// Do not let any writes come through until we will verify OCSP
|
||||
socket.cork();
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
Agent.prototype.handleOCSPResponse = function handleOCSPResponse(socket,
|
||||
stapling,
|
||||
cb) {
|
||||
var cert = socket.ssl.getPeerCertificate(true);
|
||||
var issuer = cert.issuerCertificate;
|
||||
|
||||
cert = cert.raw;
|
||||
try {
|
||||
cert = rfc5280.Certificate.decode(cert, 'der');
|
||||
|
||||
if (issuer) {
|
||||
issuer = issuer.raw;
|
||||
issuer = rfc5280.Certificate.decode(issuer, 'der');
|
||||
}
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
|
||||
function onIssuer(err, x509) {
|
||||
if (err)
|
||||
return cb(err);
|
||||
|
||||
issuer = x509;
|
||||
|
||||
if (stapling) {
|
||||
var req = ocsp.request.generate(cert, issuer);
|
||||
ocsp.verify({
|
||||
request: req,
|
||||
response: stapling
|
||||
}, cb);
|
||||
} else {
|
||||
return ocsp.check({ cert: cert, issuer: issuer }, cb);
|
||||
}
|
||||
}
|
||||
|
||||
if (issuer)
|
||||
return onIssuer(null, issuer);
|
||||
else
|
||||
return this.fetchIssuer(cert, stapling, onIssuer);
|
||||
};
|
||||
|
||||
Agent.prototype.fetchIssuer = function fetchIssuer(cert, stapling, cb) {
|
||||
var issuers = ocsp.utils['id-ad-caIssuers'].join('.');
|
||||
var self = this;
|
||||
|
||||
// TODO(indutny): use info from stapling response
|
||||
ocsp.utils.getAuthorityInfo(cert, issuers, function(err, uri) {
|
||||
if (err)
|
||||
return cb(err);
|
||||
|
||||
var ca = self.caCache.get(uri);
|
||||
if (ca)
|
||||
return cb(null, ca);
|
||||
|
||||
var once = false;
|
||||
function done(err, data) {
|
||||
if (once)
|
||||
return;
|
||||
|
||||
once = true;
|
||||
cb(err, data);
|
||||
}
|
||||
|
||||
function onResponse(res) {
|
||||
if (res.statusCode < 200 || res.statusCode >= 400)
|
||||
return done(new Error('Failed to fetch CA: ' + res.statusCode));
|
||||
|
||||
var chunks = [];
|
||||
res.on('readable', function() {
|
||||
var chunk = res.read();
|
||||
if (!chunk)
|
||||
return;
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
var cert = Buffer.concat(chunks);
|
||||
|
||||
try {
|
||||
cert = rfc5280.Certificate.decode(cert, 'der');
|
||||
} catch (e) {
|
||||
return done(e);
|
||||
}
|
||||
|
||||
self.caCache.set(uri, cert);
|
||||
done(null, cert);
|
||||
});
|
||||
}
|
||||
|
||||
http.get(uri)
|
||||
.on('error', done)
|
||||
.on('response', onResponse);
|
||||
});
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
var rfc2560 = require('asn1.js-rfc2560');
|
||||
var rfc5280 = require('asn1.js-rfc5280');
|
||||
|
||||
exports.getOCSPURI = function getOCSPURI(rawCert, cb) {
|
||||
var ocspMethod = rfc2560['id-pkix-ocsp'].join('.');
|
||||
|
||||
var cert = ocsp.utils.toDER(rawCert, 'CERTIFICATE');
|
||||
cert = rfc5280.Certificate.decode(cert, 'der');
|
||||
|
||||
ocsp.utils.getAuthorityInfo(cert, ocspMethod, cb);
|
||||
};
|
|
@ -1,117 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
|
||||
function Cache(options) {
|
||||
this.options = options || {};
|
||||
this.cache = {};
|
||||
|
||||
// Override methods
|
||||
if (this.options.probe)
|
||||
this.probe = this.options.probe;
|
||||
if (this.options.store)
|
||||
this.store = this.options.store;
|
||||
if (this.options.filter)
|
||||
this.filter = this.options.filter;
|
||||
}
|
||||
module.exports = Cache;
|
||||
|
||||
Cache.prototype.filter = function filter(url, callback) {
|
||||
callback(null);
|
||||
};
|
||||
|
||||
Cache.prototype.probe = function probe(id, callback) {
|
||||
if (this.cache.hasOwnProperty(id))
|
||||
callback(null, this.cache[id]);
|
||||
else
|
||||
callback(null, false);
|
||||
};
|
||||
|
||||
Cache.prototype.store = function store(id, response, maxTime, callback) {
|
||||
if (this.cache.hasOwnProperty(id))
|
||||
clearTimeout(this.cache[id].timer);
|
||||
var self = this;
|
||||
this.cache[id] = {
|
||||
response: response,
|
||||
timer: setTimeout(function() {
|
||||
delete self.cache[id];
|
||||
}, maxTime)
|
||||
};
|
||||
|
||||
callback(null, null);
|
||||
};
|
||||
|
||||
Cache.prototype.request = function request(id, data, callback) {
|
||||
var self = this;
|
||||
|
||||
function done(err, response) {
|
||||
if (callback)
|
||||
callback(err, response);
|
||||
callback = null;
|
||||
}
|
||||
|
||||
function onResponse(err, ocsp) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
// Respond early
|
||||
done(null, ocsp);
|
||||
|
||||
// Try parsing and caching response
|
||||
self.getMaxStoreTime(ocsp, function(err, maxTime) {
|
||||
if (err)
|
||||
return;
|
||||
|
||||
self.store(id, ocsp, maxTime, function() {
|
||||
// No-op
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Check that url isn't blacklisted
|
||||
this.filter(data.url, function(err) {
|
||||
if (err)
|
||||
return done(err, null);
|
||||
|
||||
ocsp.utils.getResponse(data.url, data.ocsp, onResponse);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Cache.prototype.getMaxStoreTime = function getMaxStoreTime(response, callback) {
|
||||
var basic;
|
||||
try {
|
||||
basic = ocsp.utils.parseResponse(response).value;
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
// Not enough responses
|
||||
if (basic.tbsResponseData.responses.length === 0)
|
||||
return callback(new Error('No OCSP responses'));
|
||||
|
||||
var responses = basic.tbsResponseData.responses;
|
||||
|
||||
// Every response should be positive
|
||||
var good = responses.every(function(response) {
|
||||
return response.certStatus.type === 'good';
|
||||
});
|
||||
|
||||
// No good - no cache
|
||||
if (!good)
|
||||
return callback(new Error('Some OCSP responses are not good'));
|
||||
|
||||
// Find minimum nextUpdate time
|
||||
var nextUpdate = 0;
|
||||
for (var i = 0; i < responses.length; i++) {
|
||||
var response = responses[i];
|
||||
var responseNext = response.nextUpdate;
|
||||
if (!responseNext)
|
||||
continue;
|
||||
|
||||
if (nextUpdate === 0 || nextUpdate > responseNext)
|
||||
nextUpdate = responseNext;
|
||||
}
|
||||
|
||||
return callback(null, Math.max(0, nextUpdate - new Date()));
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
|
||||
var rfc2560 = require('asn1.js-rfc2560');
|
||||
|
||||
module.exports = function check(options, cb) {
|
||||
var sync = true;
|
||||
var req;
|
||||
|
||||
function done(err, data) {
|
||||
if (sync) {
|
||||
sync = false;
|
||||
process.nextTick(function() {
|
||||
cb(err, data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
cb(err, data);
|
||||
}
|
||||
|
||||
try {
|
||||
req = ocsp.request.generate(options.cert, options.issuer);
|
||||
} catch (e) {
|
||||
return done(e);
|
||||
}
|
||||
|
||||
var ocspMethod = rfc2560['id-pkix-ocsp'].join('.');
|
||||
ocsp.utils.getAuthorityInfo(req.cert, ocspMethod, function(err, uri) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
var httpOptions;
|
||||
|
||||
if (options.httpOptions !== undefined) {
|
||||
httpOptions = util._extend(options.httpOptions, url.parse(uri));
|
||||
} else {
|
||||
httpOptions = uri;
|
||||
}
|
||||
|
||||
ocsp.utils.getResponse(httpOptions, req.data, function(err, raw) {
|
||||
if (err)
|
||||
return done(err);
|
||||
|
||||
ocsp.verify({
|
||||
request: req,
|
||||
response: raw
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
sync = false;
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
var crypto = require('crypto');
|
||||
var rfc2560 = require('asn1.js-rfc2560');
|
||||
var rfc5280 = require('asn1.js-rfc5280');
|
||||
|
||||
function sha1(data) {
|
||||
return crypto.createHash('sha1').update(data).digest();
|
||||
}
|
||||
|
||||
exports.generate = function generate(rawCert, rawIssuer) {
|
||||
var cert;
|
||||
if (rawCert.tbsCertificate) {
|
||||
cert = rawCert;
|
||||
} else {
|
||||
cert = rfc5280.Certificate.decode(
|
||||
ocsp.utils.toDER(rawCert, 'CERTIFICATE'),
|
||||
'der');
|
||||
}
|
||||
var issuer;
|
||||
if (rawIssuer.tbsCertificate) {
|
||||
issuer = rawIssuer;
|
||||
} else {
|
||||
issuer = rfc5280.Certificate.decode(
|
||||
ocsp.utils.toDER(rawIssuer, 'CERTIFICATE'),
|
||||
'der');
|
||||
}
|
||||
|
||||
var tbsCert = cert.tbsCertificate;
|
||||
var tbsIssuer = issuer.tbsCertificate;
|
||||
|
||||
var certID = {
|
||||
hashAlgorithm: {
|
||||
// algorithm: [ 2, 16, 840, 1, 101, 3, 4, 2, 1 ] // sha256
|
||||
algorithm: [ 1, 3, 14, 3, 2, 26 ] // sha1
|
||||
},
|
||||
issuerNameHash: sha1(rfc5280.Name.encode(tbsCert.issuer, 'der')),
|
||||
issuerKeyHash: sha1(
|
||||
tbsIssuer.subjectPublicKeyInfo.subjectPublicKey.data),
|
||||
serialNumber: tbsCert.serialNumber
|
||||
};
|
||||
|
||||
var tbs = {
|
||||
version: 'v1',
|
||||
requestList: [ {
|
||||
reqCert: certID
|
||||
} ],
|
||||
requestExtensions: [ {
|
||||
extnID: rfc2560['id-pkix-ocsp-nonce'],
|
||||
critical: false,
|
||||
extnValue: rfc2560.Nonce.encode(crypto.randomBytes(16), 'der')
|
||||
} ]
|
||||
};
|
||||
|
||||
var req = {
|
||||
tbsRequest: tbs
|
||||
};
|
||||
|
||||
return {
|
||||
id: sha1(rfc2560.CertID.encode(certID, 'der')),
|
||||
certID: certID,
|
||||
data: rfc2560.OCSPRequest.encode(req, 'der'),
|
||||
|
||||
// Just to avoid re-parsing DER
|
||||
cert: cert,
|
||||
issuer: issuer
|
||||
};
|
||||
};
|
|
@ -1,210 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var http = require('http');
|
||||
var util = require('util');
|
||||
var url = require('url');
|
||||
var asn1 = require('asn1.js');
|
||||
|
||||
var rfc2560 = require('asn1.js-rfc2560');
|
||||
|
||||
exports['id-ad-caIssuers'] = [ 1, 3, 6, 1, 5, 5, 7, 48, 2 ];
|
||||
exports['id-kp-OCSPSigning'] = [ 1, 3, 6, 1, 5, 5, 7, 3, 9 ];
|
||||
|
||||
exports.getResponse = function getResponse(uri, req, cb) {
|
||||
if (typeof uri !== 'object') {
|
||||
uri = url.parse(uri);
|
||||
}
|
||||
|
||||
var options = util._extend({
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/ocsp-request',
|
||||
'Content-Length': req.length
|
||||
}
|
||||
}, uri);
|
||||
|
||||
function done(err, response) {
|
||||
if (cb)
|
||||
cb(err, response);
|
||||
cb = null;
|
||||
}
|
||||
|
||||
function onResponse(response) {
|
||||
if (response.statusCode < 200 || response.statusCode >= 400) {
|
||||
return done(
|
||||
new Error('Failed to obtain OCSP response: ' + response.statusCode));
|
||||
}
|
||||
|
||||
var chunks = [];
|
||||
response.on('readable', function() {
|
||||
var chunk = response.read();
|
||||
if (!chunk)
|
||||
return;
|
||||
chunks.push(chunk);
|
||||
});
|
||||
response.on('end', function() {
|
||||
var ocsp = Buffer.concat(chunks);
|
||||
|
||||
done(null, ocsp);
|
||||
});
|
||||
}
|
||||
|
||||
http.request(options, onResponse)
|
||||
.on('error', done)
|
||||
.end(req);
|
||||
};
|
||||
|
||||
exports.parseResponse = function parseResponse(raw) {
|
||||
var body = { start: 0, end: raw.length };
|
||||
var response = rfc2560.OCSPResponse.decode(raw, 'der', {
|
||||
track: function(key, start, end, type) {
|
||||
if (type !== 'content' || key !== 'responseBytes/response')
|
||||
return;
|
||||
body.start = start;
|
||||
body.end = end;
|
||||
}
|
||||
});
|
||||
|
||||
var status = response.responseStatus;
|
||||
if (status !== 'successful')
|
||||
throw new Error('Bad OCSP response status: ' + status);
|
||||
|
||||
// Unknown response type
|
||||
var responseType = response.responseBytes.responseType;
|
||||
if (responseType !== 'id-pkix-ocsp-basic')
|
||||
throw new Error('Unknown OCSP response type: ' + responseType);
|
||||
|
||||
var bytes = response.responseBytes.response;
|
||||
|
||||
var tbs = { start: body.start, end: body.end };
|
||||
var certsTbs = [];
|
||||
var basic = rfc2560.BasicOCSPResponse.decode(bytes, 'der', {
|
||||
track: function(key, start, end, type) {
|
||||
if (type !== 'tagged')
|
||||
return;
|
||||
|
||||
if (key === 'tbsResponseData') {
|
||||
tbs.start = body.start + start;
|
||||
tbs.end = body.start + end;
|
||||
} else if (key === 'certs/tbsCertificate') {
|
||||
certsTbs.push({ start: body.start + start, end: body.start + end });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var OCSPSigning = exports['id-kp-OCSPSigning'].join('.');
|
||||
var certs = (basic.certs || []).filter(function(cert) {
|
||||
return cert.tbsCertificate.extensions.some(function(ext) {
|
||||
if (ext.extnID !== 'extendedKeyUsage')
|
||||
return false;
|
||||
|
||||
return ext.extnValue.some(function(value) {
|
||||
return value.join('.') === OCSPSigning;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
start: tbs.start,
|
||||
end: tbs.end,
|
||||
value: basic,
|
||||
certs: certs,
|
||||
certsTbs: certsTbs
|
||||
};
|
||||
};
|
||||
|
||||
exports.digest = {
|
||||
'1.3.14.3.2.26': 'sha1',
|
||||
'2.16.840.1.101.3.4.2.1': 'sha256'
|
||||
};
|
||||
|
||||
exports.digestRev = {
|
||||
sha1: '1.3.14.3.2.26',
|
||||
sha256: '2.16.840.1.101.3.4.2.1'
|
||||
};
|
||||
|
||||
exports.sign = {
|
||||
'1.2.840.113549.1.1.5': 'sha1WithRSAEncryption',
|
||||
'1.2.840.113549.1.1.11': 'sha256WithRSAEncryption',
|
||||
'1.2.840.113549.1.1.12': 'sha384WithRSAEncryption',
|
||||
'1.2.840.113549.1.1.13': 'sha512WithRSAEncryption'
|
||||
};
|
||||
|
||||
exports.signRev = {
|
||||
sha1WithRSAEncryption: [ 1, 2, 840, 113549, 1, 1, 5 ],
|
||||
sha256WithRSAEncryption: [ 1, 2, 840, 113549, 1, 1, 11 ],
|
||||
sha384WithRSAEncryption: [ 1, 2, 840, 113549, 1, 1, 12 ],
|
||||
sha512WithRSAEncryption: [ 1, 2, 840, 113549, 1, 1, 13 ]
|
||||
};
|
||||
|
||||
exports.toPEM = function toPEM(buf, label) {
|
||||
var p = buf.toString('base64');
|
||||
var out = [ '-----BEGIN ' + label + '-----' ];
|
||||
for (var i = 0; i < p.length; i += 64)
|
||||
out.push(p.slice(i, i + 64));
|
||||
out.push('-----END ' + label + '-----');
|
||||
return out.join('\n');
|
||||
};
|
||||
|
||||
exports.toDER = function toDER(raw, what) {
|
||||
var der = raw.toString().match(new RegExp(
|
||||
'-----BEGIN ' + what + '-----([^-]*)-----END ' + what + '-----'));
|
||||
if (der)
|
||||
der = new Buffer(der[1].replace(/[\r\n]/g, ''), 'base64');
|
||||
else if (typeof raw === 'string')
|
||||
der = new Buffer(raw);
|
||||
else
|
||||
der = raw;
|
||||
return der;
|
||||
};
|
||||
|
||||
exports.getAuthorityInfo = function getAuthorityInfo(cert, key, done) {
|
||||
var exts = cert.tbsCertificate.extensions;
|
||||
if (!exts)
|
||||
exts = [];
|
||||
|
||||
var infoAccess = exts.filter(function(ext) {
|
||||
return ext.extnID === 'authorityInformationAccess';
|
||||
});
|
||||
|
||||
if (infoAccess.length === 0)
|
||||
return done(new Error('AuthorityInfoAccess not found in extensions'));
|
||||
|
||||
var res = null;
|
||||
var found = infoAccess.some(function(info) {
|
||||
var ext = info.extnValue;
|
||||
|
||||
return ext.some(function(ad) {
|
||||
if (ad.accessMethod.join('.') !== key)
|
||||
return false;
|
||||
|
||||
var loc = ad.accessLocation;
|
||||
if (loc.type !== 'uniformResourceIdentifier')
|
||||
return false;
|
||||
|
||||
res = loc.value + '';
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
if (!found)
|
||||
return done(new Error(key + ' not found in AuthorityInfoAccess'));
|
||||
|
||||
return done(null, res);
|
||||
};
|
||||
|
||||
var RSAPrivateKey = asn1.define('RSAPrivateKey', function() {
|
||||
this.seq().obj(
|
||||
this.key('version').int(),
|
||||
this.key('modulus').int(),
|
||||
this.key('publicExponent').int(),
|
||||
this.key('privateExponent').int(),
|
||||
this.key('prime1').int(),
|
||||
this.key('prime2').int(),
|
||||
this.key('exponent1').int(),
|
||||
this.key('exponent2').int(),
|
||||
this.key('coefficient').int()
|
||||
);
|
||||
});
|
||||
exports.RSAPrivateKey = RSAPrivateKey;
|
|
@ -1,118 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ocsp = require('../ocsp');
|
||||
var rfc5280 = require('asn1.js-rfc5280');
|
||||
var crypto = require('crypto');
|
||||
|
||||
// TODO(indutny): verify issuer, etc...
|
||||
function findResponder(issuer, certs, raws) {
|
||||
var issuerKey = issuer.tbsCertificate.subjectPublicKeyInfo;
|
||||
issuerKey = ocsp.utils.toPEM(
|
||||
rfc5280.SubjectPublicKeyInfo.encode(issuerKey, 'der'), 'PUBLIC KEY');
|
||||
|
||||
for (var i = 0; i < certs.length; i++) {
|
||||
var cert = certs[i];
|
||||
var signAlg = ocsp.utils.sign[cert.signatureAlgorithm.algorithm.join('.')];
|
||||
if (!signAlg) {
|
||||
throw new Error('Unknown signature algorithm ' +
|
||||
cert.signatureAlgorithm.algorithm);
|
||||
}
|
||||
|
||||
var verify = crypto.createVerify(signAlg);
|
||||
|
||||
verify.update(raws[i]);
|
||||
if (!verify.verify(issuerKey, cert.signature.data))
|
||||
throw new Error('Invalid signature');
|
||||
|
||||
var certKey = cert.tbsCertificate.subjectPublicKeyInfo;
|
||||
certKey = ocsp.utils.toPEM(
|
||||
rfc5280.SubjectPublicKeyInfo.encode(certKey, 'der'), 'PUBLIC KEY');
|
||||
return certKey;
|
||||
}
|
||||
|
||||
return issuerKey;
|
||||
}
|
||||
|
||||
module.exports = function verify(options, cb) {
|
||||
var req = options.request;
|
||||
var issuer;
|
||||
var res;
|
||||
|
||||
function done(err) {
|
||||
process.nextTick(function() {
|
||||
cb(err, res && res.certStatus);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
issuer = req.issuer ||
|
||||
rfc5280.Certificate.decode(
|
||||
ocsp.utils.toDER(options.issuer, 'CERTIFICATE'), 'der');
|
||||
|
||||
res = ocsp.utils.parseResponse(options.response);
|
||||
} catch (e) {
|
||||
return done(e);
|
||||
}
|
||||
|
||||
var rawTBS = options.response.slice(res.start, res.end);
|
||||
var certs = res.certs;
|
||||
var raws = res.certsTbs.map(function(tbs) {
|
||||
return options.response.slice(tbs.start, tbs.end);
|
||||
});
|
||||
res = res.value;
|
||||
|
||||
// Verify signature using CAs Public Key
|
||||
var signAlg = ocsp.utils.sign[res.signatureAlgorithm.algorithm.join('.')];
|
||||
if (!signAlg) {
|
||||
done(new Error('Unknown signature algorithm ' +
|
||||
res.signatureAlgorithm.algorithm));
|
||||
return;
|
||||
}
|
||||
|
||||
var responderKey = findResponder(issuer, certs, raws);
|
||||
|
||||
var verify = crypto.createVerify(signAlg);
|
||||
var tbs = res.tbsResponseData;
|
||||
|
||||
var signature = res.signature.data;
|
||||
|
||||
verify.update(rawTBS);
|
||||
if (!verify.verify(responderKey, signature))
|
||||
return done(new Error('Invalid signature'));
|
||||
|
||||
if (tbs.responses.length < 1)
|
||||
return done(new Error('Expected at least one response'));
|
||||
|
||||
var res = tbs.responses[0];
|
||||
|
||||
// Verify CertID
|
||||
// XXX(indutny): verify parameters
|
||||
if (res.certId.hashAlgorithm.algorithm.join('.') !==
|
||||
req.certID.hashAlgorithm.algorithm.join('.')) {
|
||||
return done(new Error('Hash algorithm mismatch'));
|
||||
}
|
||||
|
||||
if (res.certId.issuerNameHash.toString('hex') !==
|
||||
req.certID.issuerNameHash.toString('hex')) {
|
||||
return done(new Error('Issuer name hash mismatch'));
|
||||
}
|
||||
|
||||
if (res.certId.issuerKeyHash.toString('hex') !==
|
||||
req.certID.issuerKeyHash.toString('hex')) {
|
||||
return done(new Error('Issuer key hash mismatch'));
|
||||
}
|
||||
|
||||
if (res.certId.serialNumber.cmp(req.certID.serialNumber) !== 0)
|
||||
return done(new Error('Serial number mismatch'));
|
||||
|
||||
if (res.certStatus.type !== 'good') {
|
||||
return done(new Error('OCSP Status: ' + res.certStatus.type));
|
||||
}
|
||||
|
||||
var now = +new Date();
|
||||
var nudge = options.nudge || 60000;
|
||||
if (res.thisUpdate - nudge > now || res.nextUpdate + nudge < now)
|
||||
return done(new Error('OCSP Response expired'));
|
||||
|
||||
return done(null);
|
||||
};
|
|
@ -30,9 +30,6 @@
|
|||
.pipe(tsProject())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('distrib/lib'));
|
||||
}, function () {
|
||||
return gulp.src('./external/**/*')
|
||||
.pipe(gulp.dest('./distrib/lib/external/'));
|
||||
}));
|
||||
|
||||
gulp.task('build2015', gulp.series(function build() {
|
||||
|
@ -50,9 +47,6 @@
|
|||
.pipe(tsProject2015())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('distrib/es2015'));
|
||||
}, function () {
|
||||
return gulp.src('./external/**/*')
|
||||
.pipe(gulp.dest('./distrib/es2015/external/'));
|
||||
}));
|
||||
|
||||
gulp.task('bundle', gulp.series('build', function bundle() {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -33,9 +33,6 @@
|
|||
"simple-lru-cache": false,
|
||||
"ws": false,
|
||||
"fs": false,
|
||||
"async-disk-cache": false,
|
||||
"distrib/es2015/external/ocsp/ocsp": false,
|
||||
"distrib/lib/external/ocsp/ocsp": false,
|
||||
"agent-base": false,
|
||||
"tls": false,
|
||||
"net": false
|
||||
|
@ -106,12 +103,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"agent-base": "^6.0.1",
|
||||
"asn1.js-rfc2560": "^5.0.1",
|
||||
"asn1.js-rfc5280": "^3.0.0",
|
||||
"async-disk-cache": "^2.1.0",
|
||||
"bent": "^7.3.12",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"simple-lru-cache": "0.0.2",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^7.5.6"
|
||||
},
|
||||
|
|
|
@ -4,48 +4,14 @@
|
|||
|
||||
import * as http from "http";
|
||||
import * as tls from "tls";
|
||||
import * as ocsp from "../../external/ocsp/ocsp";
|
||||
import {
|
||||
Events,
|
||||
OCSPCacheEntryExpiredEvent,
|
||||
OCSPCacheEntryNeedsRefreshEvent,
|
||||
OCSPCacheFetchErrorEvent,
|
||||
OCSPCacheHitEvent,
|
||||
OCSPCacheMissEvent,
|
||||
OCSPCacheUpdateCompleteEvent,
|
||||
OCSPCacheUpdateNeededEvent,
|
||||
OCSPDiskCacheHitEvent,
|
||||
OCSPDiskCacheStoreEvent,
|
||||
OCSPEvent,
|
||||
OCSPMemoryCacheHitEvent,
|
||||
OCSPMemoryCacheStoreEvent,
|
||||
OCSPResponseRetrievedEvent,
|
||||
OCSPStapleReceivedEvent,
|
||||
OCSPVerificationFailedEvent,
|
||||
} from "../common/Exports";
|
||||
import { IStringDictionary } from "../common/IDictionary";
|
||||
import { ProxyInfo } from "./ProxyInfo";
|
||||
|
||||
import Agent from "agent-base";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import Cache from "async-disk-cache";
|
||||
import HttpsProxyAgent from "https-proxy-agent";
|
||||
import * as net from "net";
|
||||
import { OCSPCacheUpdateErrorEvent } from "../common/OCSPEvents";
|
||||
|
||||
|
||||
interface tbsUpdateResponse {
|
||||
thisUpdate: number;
|
||||
nextUpdate: number;
|
||||
}
|
||||
|
||||
interface tbsResponse {
|
||||
tbsResponseData: {
|
||||
responses: tbsUpdateResponse[];
|
||||
};
|
||||
}
|
||||
|
||||
export class CertCheckAgent {
|
||||
|
||||
|
@ -55,34 +21,16 @@ export class CertCheckAgent {
|
|||
// Test hook to disable stapling for cache testing.
|
||||
public static forceDisableOCSPStapling: boolean = false;
|
||||
|
||||
// An in memory cache for recived responses.
|
||||
private static privMemCache: IStringDictionary<Buffer> = {};
|
||||
|
||||
// The on disk cache.
|
||||
private static privDiskCache: Cache;
|
||||
|
||||
private privProxyInfo: ProxyInfo;
|
||||
|
||||
public constructor(proxyInfo?: ProxyInfo) {
|
||||
if (!!proxyInfo) {
|
||||
this.privProxyInfo = proxyInfo;
|
||||
}
|
||||
|
||||
// Initialize this here to allow tests to set the env variable before the cache is constructed.
|
||||
if (!CertCheckAgent.privDiskCache) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
CertCheckAgent.privDiskCache = new Cache("microsoft-cognitiveservices-speech-sdk-cache", { supportBuffer: true, location: (typeof process !== "undefined" && !!process.env.SPEECH_OCSP_CACHE_ROOT) ? process.env.SPEECH_OCSP_CACHE_ROOT : undefined });
|
||||
}
|
||||
}
|
||||
|
||||
// Test hook to force the disk cache to be recreated.
|
||||
public static forceReinitDiskCache(): void {
|
||||
CertCheckAgent.privDiskCache = undefined;
|
||||
CertCheckAgent.privMemCache = {};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public GetAgent(disableStapling?: boolean): http.Agent {
|
||||
public GetAgent(): http.Agent {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const agent: any = new Agent.Agent(this.CreateConnection);
|
||||
|
||||
|
@ -117,250 +65,7 @@ export class CertCheckAgent {
|
|||
return httpProxyAgent;
|
||||
}
|
||||
|
||||
private static async OCSPCheck(socketPromise: Promise<net.Socket>, proxyInfo: ProxyInfo): Promise<net.Socket> {
|
||||
let ocspRequest: ocsp.Request;
|
||||
let stapling: Buffer;
|
||||
let resolved: boolean = false;
|
||||
|
||||
const socket: net.Socket = await socketPromise;
|
||||
socket.cork();
|
||||
|
||||
const tlsSocket: tls.TLSSocket = socket as tls.TLSSocket;
|
||||
|
||||
return new Promise<net.Socket>((resolve: (value: net.Socket) => void, reject: (error: string | Error) => void): void => {
|
||||
socket.on("OCSPResponse", (data: Buffer): void => {
|
||||
if (!!data) {
|
||||
this.onEvent(new OCSPStapleReceivedEvent());
|
||||
stapling = data;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("error", (error: Error): void => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
socket.destroy();
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/explicit-function-return-type
|
||||
tlsSocket.on("secure", async () => {
|
||||
const peer: tls.DetailedPeerCertificate = tlsSocket.getPeerCertificate(true);
|
||||
try {
|
||||
const issuer: tls.DetailedPeerCertificate = await this.GetIssuer(peer);
|
||||
|
||||
// We always need a request to verify the response.
|
||||
ocspRequest = ocsp.request.generate(peer.raw, issuer.raw);
|
||||
|
||||
// Do we have a result for this certificate in our memory cache?
|
||||
const sig: string = ocspRequest.id.toString("hex");
|
||||
|
||||
// Stapled response trumps cached response.
|
||||
if (!stapling) {
|
||||
const cacheEntry: Buffer = await CertCheckAgent.GetResponseFromCache(sig, ocspRequest, proxyInfo);
|
||||
stapling = cacheEntry;
|
||||
}
|
||||
|
||||
await this.VerifyOCSPResponse(stapling, ocspRequest, proxyInfo);
|
||||
|
||||
socket.uncork();
|
||||
resolved = true;
|
||||
resolve(socket);
|
||||
} catch (e) {
|
||||
socket.destroy();
|
||||
resolved = true;
|
||||
reject(e as string);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static GetIssuer(peer: tls.DetailedPeerCertificate): Promise<tls.DetailedPeerCertificate> {
|
||||
if (peer.issuerCertificate) {
|
||||
return Promise.resolve(peer.issuerCertificate);
|
||||
}
|
||||
|
||||
return new Promise<tls.DetailedPeerCertificate>((resolve: (value: tls.DetailedPeerCertificate) => void, reject: (reason: string) => void): void => {
|
||||
const ocspAgent: ocsp.Agent = new ocsp.Agent({});
|
||||
ocspAgent.fetchIssuer(peer, null, (error: string, value: tls.DetailedPeerCertificate): void => {
|
||||
if (!!error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static async GetResponseFromCache(signature: string, ocspRequest: ocsp.Request, proxyInfo: ProxyInfo): Promise<Buffer> {
|
||||
let cachedResponse: Buffer = CertCheckAgent.privMemCache[signature];
|
||||
|
||||
if (!!cachedResponse) {
|
||||
this.onEvent(new OCSPMemoryCacheHitEvent(signature));
|
||||
}
|
||||
|
||||
// Do we have a result for this certificate on disk in %TMP%?
|
||||
if (!cachedResponse) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||
const diskCacheResponse: { value: Buffer; isCached?: any } = await CertCheckAgent.privDiskCache.get(signature) as { value: Buffer; isCached?: any };
|
||||
if (!!diskCacheResponse.isCached) {
|
||||
CertCheckAgent.onEvent(new OCSPDiskCacheHitEvent(signature));
|
||||
CertCheckAgent.StoreMemoryCacheEntry(signature, diskCacheResponse.value);
|
||||
cachedResponse = diskCacheResponse.value;
|
||||
}
|
||||
} catch (error) {
|
||||
cachedResponse = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
const cachedOcspResponse: ocsp.Response = ocsp.utils.parseResponse(cachedResponse);
|
||||
const responseValue: tbsResponse = cachedOcspResponse.value as tbsResponse;
|
||||
const tbsData: { responses: tbsUpdateResponse[] } = responseValue.tbsResponseData;
|
||||
if (tbsData.responses.length < 1) {
|
||||
this.onEvent(new OCSPCacheFetchErrorEvent(signature, "Not enough data in cached response"));
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedStartTime: number = tbsData.responses[0].thisUpdate;
|
||||
const cachedNextTime: number = tbsData.responses[0].nextUpdate;
|
||||
|
||||
if (cachedNextTime < (Date.now() + this.testTimeOffset - 60000)) {
|
||||
// Cached entry has expired.
|
||||
this.onEvent(new OCSPCacheEntryExpiredEvent(signature, cachedNextTime));
|
||||
cachedResponse = null;
|
||||
} else {
|
||||
// If we're within one day of the next update, or 50% of the way through the validity period,
|
||||
// background an update to the cache.
|
||||
|
||||
const minUpdate: number = Math.min(24 * 60 * 60 * 1000, (cachedNextTime - cachedStartTime) / 2);
|
||||
|
||||
if ((cachedNextTime - (Date.now() + this.testTimeOffset)) < minUpdate) {
|
||||
this.onEvent(new OCSPCacheEntryNeedsRefreshEvent(signature, cachedStartTime, cachedNextTime));
|
||||
this.UpdateCache(ocspRequest, proxyInfo).catch((error: string): void => {
|
||||
// Well, not much we can do here.
|
||||
this.onEvent(new OCSPCacheUpdateErrorEvent(signature, error.toString()));
|
||||
});
|
||||
} else {
|
||||
this.onEvent(new OCSPCacheHitEvent(signature, cachedStartTime, cachedNextTime));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.onEvent(new OCSPCacheFetchErrorEvent(signature, error as string));
|
||||
cachedResponse = null;
|
||||
}
|
||||
if (!cachedResponse) {
|
||||
this.onEvent(new OCSPCacheMissEvent(signature));
|
||||
}
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
private static async VerifyOCSPResponse(cacheValue: Buffer, ocspRequest: ocsp.Request, proxyInfo: ProxyInfo): Promise<void> {
|
||||
let ocspResponse: Buffer = cacheValue;
|
||||
|
||||
// Do we have a valid response?
|
||||
if (!ocspResponse) {
|
||||
ocspResponse = await CertCheckAgent.GetOCSPResponse(ocspRequest, proxyInfo);
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve: () => void, reject: (error: string | Error) => void): void => {
|
||||
ocsp.verify({ request: ocspRequest, response: ocspResponse }, (error: string): void => {
|
||||
if (!!error) {
|
||||
CertCheckAgent.onEvent(new OCSPVerificationFailedEvent(ocspRequest.id.toString("hex"), error));
|
||||
|
||||
// Bad Cached Value? One more try without the cache.
|
||||
if (!!cacheValue) {
|
||||
this.VerifyOCSPResponse(null, ocspRequest, proxyInfo).then((): void => {
|
||||
resolve();
|
||||
}, (error: Error): void => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
if (!cacheValue) {
|
||||
CertCheckAgent.StoreCacheEntry(ocspRequest.id.toString("hex"), ocspResponse);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static async UpdateCache(req: ocsp.Request, proxyInfo: ProxyInfo): Promise<void> {
|
||||
const signature: string = req.id.toString("hex");
|
||||
this.onEvent(new OCSPCacheUpdateNeededEvent(signature));
|
||||
|
||||
const rawResponse: Buffer = await this.GetOCSPResponse(req, proxyInfo);
|
||||
this.StoreCacheEntry(signature, rawResponse);
|
||||
this.onEvent(new OCSPCacheUpdateCompleteEvent(req.id.toString("hex")));
|
||||
|
||||
}
|
||||
|
||||
private static StoreCacheEntry(sig: string, rawResponse: Buffer): void {
|
||||
this.StoreMemoryCacheEntry(sig, rawResponse);
|
||||
this.StoreDiskCacheEntry(sig, rawResponse);
|
||||
}
|
||||
|
||||
private static StoreMemoryCacheEntry(sig: string, rawResponse: Buffer): void {
|
||||
this.privMemCache[sig] = rawResponse;
|
||||
this.onEvent(new OCSPMemoryCacheStoreEvent(sig));
|
||||
}
|
||||
|
||||
private static StoreDiskCacheEntry(sig: string, rawResponse: Buffer): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||
this.privDiskCache.set(sig, rawResponse).then((): void => {
|
||||
this.onEvent(new OCSPDiskCacheStoreEvent(sig));
|
||||
});
|
||||
}
|
||||
|
||||
private static GetOCSPResponse(req: ocsp.Request, proxyInfo: ProxyInfo): Promise<Buffer> {
|
||||
|
||||
const ocspMethod: string = "1.3.6.1.5.5.7.48.1";
|
||||
let options: http.RequestOptions = {};
|
||||
|
||||
if (!!proxyInfo) {
|
||||
const agent: HttpsProxyAgent = CertCheckAgent.GetProxyAgent(proxyInfo);
|
||||
options.agent = agent;
|
||||
}
|
||||
|
||||
return new Promise<Buffer>((resolve: (value: Buffer) => void, reject: (error: string | Error) => void): void => {
|
||||
ocsp.utils.getAuthorityInfo(req.cert as tls.DetailedPeerCertificate, ocspMethod, (error: string, uri: string): void => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(uri);
|
||||
options = { ...options, host: url.host, protocol: url.protocol, port: url.port, path: url.pathname, hostname: url.host };
|
||||
|
||||
ocsp.utils.getResponse(options, req.data, (error: string, raw: Buffer): void => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const certID: Buffer = req.certID as Buffer;
|
||||
this.onEvent(new OCSPResponseRetrievedEvent(certID.toString("hex")));
|
||||
resolve(raw);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static onEvent(event: OCSPEvent): void {
|
||||
Events.instance.onEvent(event);
|
||||
}
|
||||
|
||||
private CreateConnection(request: Agent.ClientRequest, options: Agent.RequestOptions): Promise<net.Socket> {
|
||||
const enableOCSP: boolean = (typeof process !== "undefined" && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0" && process.env.SPEECH_CONDUCT_OCSP_CHECK !== "0") && options.secureEndpoint;
|
||||
let socketPromise: Promise<net.Socket>;
|
||||
|
||||
options = {
|
||||
|
@ -392,10 +97,6 @@ export class CertCheckAgent {
|
|||
}
|
||||
}
|
||||
|
||||
if (!!enableOCSP) {
|
||||
return CertCheckAgent.OCSPCheck(socketPromise, this.privProxyInfo);
|
||||
} else {
|
||||
return socketPromise;
|
||||
}
|
||||
return socketPromise;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,6 @@ export class WebsocketMessageAdapter {
|
|||
const checkAgent: CertCheckAgent = new CertCheckAgent(this.proxyInfo);
|
||||
|
||||
options.agent = checkAgent.GetAgent();
|
||||
|
||||
// Workaround for https://github.com/microsoft/cognitive-services-speech-sdk-js/issues/465
|
||||
// Which is root caused by https://github.com/TooTallNate/node-agent-base/issues/61
|
||||
const uri = new URL(this.privUri);
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { CertCheckAgent } from "../src/common.browser/CertChecks";
|
||||
import {
|
||||
ConsoleLoggingListener
|
||||
} from "../src/common.browser/Exports";
|
||||
import {
|
||||
Events,
|
||||
EventType,
|
||||
IDetachable,
|
||||
OCSPEvent,
|
||||
PlatformEvent
|
||||
} from "../src/common/Exports";
|
||||
import { Settings } from "./Settings";
|
||||
import { WaitForPromise } from "./Utilities";
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import path from "path";
|
||||
import rimraf from "rimraf";
|
||||
|
||||
const origCacehDir: string = process.env.SPEECH_OSCP_CACHE_ROOT;
|
||||
let cacheDir: string;
|
||||
let events: OCSPEvent[];
|
||||
let currentListener: IDetachable;
|
||||
|
||||
beforeAll(() => {
|
||||
// override inputs, if necessary
|
||||
Settings.LoadSettings();
|
||||
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("------------------Starting test case: " + expect.getState().currentTestName + "-------------------------");
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
|
||||
cacheDir = path.join(os.tmpdir(), Math.random().toString(36).substr(2, 15));
|
||||
process.env.SPEECH_OCSP_CACHE_ROOT = cacheDir;
|
||||
fs.mkdirSync(cacheDir);
|
||||
events = [];
|
||||
currentListener = Events.instance.attach((event: PlatformEvent): void => {
|
||||
if (event.name.startsWith("OCSP")) {
|
||||
events.push(event as OCSPEvent);
|
||||
}
|
||||
});
|
||||
CertCheckAgent.forceReinitDiskCache();
|
||||
CertCheckAgent.testTimeOffset = 0;
|
||||
});
|
||||
|
||||
jest.retryTimes(Settings.RetryCount);
|
||||
|
||||
afterEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("End Time: " + new Date(Date.now()).toLocaleString());
|
||||
rimraf(cacheDir, (error: Error): void => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Error " + Error.toString() + " cleaning up.");
|
||||
});
|
||||
currentListener.detach().catch();
|
||||
currentListener = null;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.SPEECH_OSCP_CACHE_ROOT = origCacehDir;
|
||||
});
|
||||
|
||||
/*
|
||||
function findEvent(eventName: string): number {
|
||||
let found: number = 0;
|
||||
events.forEach((event: OCSPEvent, index: number, array: OCSPEvent[]): void => {
|
||||
if (event.name === eventName) {
|
||||
found++;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
function waitForEvents(eventName: string, eventCount: number, rejectMessage?: string, timeoutMS: number = 5000): Promise<void> {
|
||||
return WaitForPromise((): boolean => {
|
||||
return findEvent(eventName) === eventCount;
|
||||
}, rejectMessage === undefined ? eventName : rejectMessage, timeoutMS);
|
||||
}
|
||||
|
||||
const makeRequest = (disableOCSPStapling: boolean = true): Promise<void> => {
|
||||
return new Promise(async (resolve: (value: void) => void, reject: (reason: string) => void): Promise<void> => {
|
||||
const testUrl: string = "https://www.microsoft.com/";
|
||||
|
||||
const agent: CertCheckAgent = new CertCheckAgent();
|
||||
CertCheckAgent.forceDisableOCSPStapling = disableOCSPStapling;
|
||||
|
||||
// TODO: if we re-enable this test, make it work without got (find another http request lib
|
||||
// that replaces the user agent)
|
||||
const { statusCode } = await got(testUrl, {
|
||||
agent: { http: agent.GetAgent() },
|
||||
followRedirect: false,
|
||||
});
|
||||
try {
|
||||
if (statusCode !== 200) {
|
||||
reject(`error: statusCode ${statusCode} received`);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/chromium/badssl.com/issues/477
|
||||
test.skip("Test OCSP Revoked", async (done: jest.DoneCallback): Promise<void> => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Test OCSP Revoked");
|
||||
|
||||
const testUrl: string = "https://revoked.badssl.com/";
|
||||
|
||||
const agent: CertCheckAgent = new CertCheckAgent();
|
||||
|
||||
try {
|
||||
// TODO: if we re-enable this test, make it work without got (find another http request lib
|
||||
// that replaces the user agent)
|
||||
await got(testUrl, {
|
||||
agent: { http: agent.GetAgent() },
|
||||
followRedirect: false,
|
||||
});
|
||||
} catch (error) {
|
||||
try {
|
||||
expect(error).not.toBeUndefined();
|
||||
expect(error).not.toBeNull();
|
||||
expect(error.toString()).toContain("revoked");
|
||||
done();
|
||||
} catch (ex) {
|
||||
done(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
test.skip("Test OCSP Staple", async (done: jest.DoneCallback) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info("Name: Test OCSP Staple");
|
||||
|
||||
// await makeRequest(false);
|
||||
// await waitForEvents("OCSPStapleReceivedEvent", 1);
|
||||
// await waitForEvents("OCSPResponseRetrievedEvent", 0);
|
||||
done();
|
||||
});
|
||||
|
||||
/*
|
||||
test.skip("Test OCSP Basic", async (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Test OCSP Basic");
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
done();
|
||||
});
|
||||
|
||||
test.skip("Test OCSP 2nd request mem cache hit.", async (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Test OCSP 2nd request mem cache hit.");
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
|
||||
events = [];
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 0);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 0);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 0);
|
||||
await waitForEvents("OCSPDiskCacheHitEvent", 0);
|
||||
await waitForEvents("OCSPMemoryCacheHitEvent", 1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
test.skip("Test OCSP expirey refreshes.", async (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Test OCSP expirey refreshes.");
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
|
||||
events = [];
|
||||
CertCheckAgent.testTimeOffset = 1000 * 60 * 60 * 24 * 7.5;
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheHitEvent", 0);
|
||||
await waitForEvents("OCSPCacheEntryExpiredEvent", 1);
|
||||
done();
|
||||
});
|
||||
|
||||
test.skip("Test OCSP expirey approaching refreshes.", async (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Test OCSP expirey approaching refreshes.");
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
|
||||
events = [];
|
||||
CertCheckAgent.testTimeOffset = 1000 * 60 * 60 * 24 * 3.5;
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPCacheUpdateNeededEvent", 1);
|
||||
await waitForEvents("OCSPCacheUpdateCompleteEvent", 1);
|
||||
done();
|
||||
});
|
||||
|
||||
test.skip("Test OCSP invalid cert refreshes.", async (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: invalid cert refreshes.");
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
|
||||
events = [];
|
||||
CertCheckAgent.forceReinitDiskCache();
|
||||
|
||||
const dir: string = path.join(cacheDir, "if-you-need-to-delete-this-open-an-issue-async-disk-cache", "microsoft-cognitiveservices-speech-sdk-cache");
|
||||
fs.readdir(dir, (error: NodeJS.ErrnoException, files: string[]): void => {
|
||||
files.forEach((value: string, index: number, array: string[]): void => {
|
||||
const file: string = path.join(dir, value);
|
||||
const content: Buffer = fs.readFileSync(file);
|
||||
content.set([2], 7);
|
||||
fs.writeFileSync(file, content);
|
||||
});
|
||||
});
|
||||
|
||||
await makeRequest();
|
||||
await waitForEvents("OCSPResponseRetrievedEvent", 1);
|
||||
await waitForEvents("OCSPMemoryCacheStoreEvent", 2);
|
||||
await waitForEvents("OCSPDiskCacheStoreEvent", 1);
|
||||
await waitForEvents("OCSPCacheFetchErrorEvent", 1);
|
||||
await waitForEvents("OCSPCacheMissEvent", 1);
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
*/
|
|
@ -50,7 +50,6 @@ import { closeAsyncObjects, RepeatingPullStream, WaitForCondition } from "./Util
|
|||
|
||||
import { AudioStreamFormatImpl } from "../src/sdk/Audio/AudioStreamFormat";
|
||||
import { Console } from "console";
|
||||
import { utils } from "../external/ocsp/ocsp";
|
||||
import { PullAudioInputStream } from "../microsoft.cognitiveservices.speech.sdk";
|
||||
|
||||
const FIRST_EVENT_ID: number = 1;
|
||||
|
|
Загрузка…
Ссылка в новой задаче