forked from svrjs/svrjs
118 lines
3.3 KiB
JavaScript
118 lines
3.3 KiB
JavaScript
'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);
|
|
};
|