bip70: comments.
This commit is contained in:
parent
91af611866
commit
5b1966a6c3
@ -15,6 +15,16 @@ var PaymentDetails = require('./paymentdetails');
|
||||
var ProtoReader = protobuf.ProtoReader;
|
||||
var ProtoWriter = protobuf.ProtoWriter;
|
||||
|
||||
/**
|
||||
* Represents a BIP70 payment.
|
||||
* @constructor
|
||||
* @param {Object?} options
|
||||
* @property {Buffer} merchantData
|
||||
* @property {TX[]} transactions
|
||||
* @property {Output[]} refundTo
|
||||
* @property {String|null} memo
|
||||
*/
|
||||
|
||||
function Payment(options) {
|
||||
if (!(this instanceof Payment))
|
||||
return new Payment(options);
|
||||
@ -28,6 +38,13 @@ function Payment(options) {
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {Payment}
|
||||
*/
|
||||
|
||||
Payment.prototype.fromOptions = function fromOptions(options) {
|
||||
var i, tx, output;
|
||||
|
||||
@ -58,13 +75,43 @@ Payment.prototype.fromOptions = function fromOptions(options) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment from options.
|
||||
* @param {Object} options
|
||||
* @returns {Payment}
|
||||
*/
|
||||
|
||||
Payment.fromOptions = function fromOptions(options) {
|
||||
return new Payment().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set payment details.
|
||||
* @method
|
||||
* @alias Payment#setData
|
||||
* @param {Object} data
|
||||
* @param {String?} enc
|
||||
*/
|
||||
|
||||
Payment.prototype.setData = PaymentDetails.prototype.setData;
|
||||
|
||||
/**
|
||||
* Get payment details.
|
||||
* @method
|
||||
* @alias Payment#getData
|
||||
* @param {String?} enc
|
||||
* @returns {String|Object|null}
|
||||
*/
|
||||
|
||||
Payment.prototype.getData = PaymentDetails.prototype.getData;
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @returns {Payment}
|
||||
*/
|
||||
|
||||
Payment.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new ProtoReader(data);
|
||||
var tx, op, output;
|
||||
@ -89,12 +136,23 @@ Payment.prototype.fromRaw = function fromRaw(data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @returns {Payment}
|
||||
*/
|
||||
|
||||
Payment.fromRaw = function fromRaw(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new Payment().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the payment (protobuf).
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Payment.prototype.toRaw = function toRaw() {
|
||||
var bw = new ProtoWriter();
|
||||
var i, tx, op, output;
|
||||
@ -121,4 +179,8 @@ Payment.prototype.toRaw = function toRaw() {
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Payment;
|
||||
|
||||
@ -12,6 +12,13 @@ var Payment = require('./payment');
|
||||
var ProtoReader = protobuf.ProtoReader;
|
||||
var ProtoWriter = protobuf.ProtoWriter;
|
||||
|
||||
/**
|
||||
* Represents a BIP70 payment ack.
|
||||
* @param {Object?} options
|
||||
* @property {Payment} payment
|
||||
* @property {String|null} memo
|
||||
*/
|
||||
|
||||
function PaymentACK(options) {
|
||||
if (!(this instanceof PaymentACK))
|
||||
return new PaymentACK(options);
|
||||
@ -23,6 +30,13 @@ function PaymentACK(options) {
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {PaymentACK}
|
||||
*/
|
||||
|
||||
PaymentACK.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.payment)
|
||||
this.payment.fromOptions(options.payment);
|
||||
@ -35,10 +49,23 @@ PaymentACK.prototype.fromOptions = function fromOptions(options) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment ack from options.
|
||||
* @param {Object} options
|
||||
* @returns {PaymentACK}
|
||||
*/
|
||||
|
||||
PaymentACK.fromOptions = function fromOptions(options) {
|
||||
return new PaymentACK().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentACK}
|
||||
*/
|
||||
|
||||
PaymentACK.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new ProtoReader(data);
|
||||
|
||||
@ -48,12 +75,23 @@ PaymentACK.prototype.fromRaw = function fromRaw(data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment ack from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentACK}
|
||||
*/
|
||||
|
||||
PaymentACK.fromRaw = function fromRaw(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new PaymentACK().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the payment ack (protobuf).
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
PaymentACK.prototype.toRaw = function toRaw() {
|
||||
var bw = new ProtoWriter();
|
||||
|
||||
@ -65,4 +103,8 @@ PaymentACK.prototype.toRaw = function toRaw() {
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = PaymentACK;
|
||||
|
||||
@ -13,6 +13,18 @@ var protobuf = require('../utils/protobuf');
|
||||
var ProtoReader = protobuf.ProtoReader;
|
||||
var ProtoWriter = protobuf.ProtoWriter;
|
||||
|
||||
/**
|
||||
* Represents BIP70 payment details.
|
||||
* @param {Object?} options
|
||||
* @property {String|null} network
|
||||
* @property {Output[]} outputs
|
||||
* @property {Number} time
|
||||
* @property {Number} expires
|
||||
* @property {String|null} memo
|
||||
* @property {String|null} paymentUrl
|
||||
* @property {Buffer|null} merchantData
|
||||
*/
|
||||
|
||||
function PaymentDetails(options) {
|
||||
if (!(this instanceof PaymentDetails))
|
||||
return new PaymentDetails(options);
|
||||
@ -29,6 +41,13 @@ function PaymentDetails(options) {
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {PaymentDetails}
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.fromOptions = function fromOptions(options) {
|
||||
var i, output;
|
||||
|
||||
@ -71,16 +90,33 @@ PaymentDetails.prototype.fromOptions = function fromOptions(options) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment details from options.
|
||||
* @param {Object} options
|
||||
* @returns {PaymentDetails}
|
||||
*/
|
||||
|
||||
PaymentDetails.fromOptions = function fromOptions(options) {
|
||||
return new PaymentDetails().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the payment is expired.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.isExpired = function isExpired() {
|
||||
if (this.expires === -1)
|
||||
return false;
|
||||
return util.now() > this.expires;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set payment details.
|
||||
* @param {Object} data
|
||||
* @param {String?} enc
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.setData = function setData(data, enc) {
|
||||
if (data == null || Buffer.isBuffer(data)) {
|
||||
this.merchantData = data;
|
||||
@ -96,6 +132,12 @@ PaymentDetails.prototype.setData = function setData(data, enc) {
|
||||
this.merchantData = new Buffer(data, enc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get payment details.
|
||||
* @param {String?} enc
|
||||
* @returns {String|Object|null}
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.getData = function getData(enc) {
|
||||
var data = this.merchantData;
|
||||
|
||||
@ -118,6 +160,13 @@ PaymentDetails.prototype.getData = function getData(enc) {
|
||||
return data.toString(enc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentDetails}
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new ProtoReader(data);
|
||||
var op, output;
|
||||
@ -141,12 +190,23 @@ PaymentDetails.prototype.fromRaw = function fromRaw(data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment details from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentDetails}
|
||||
*/
|
||||
|
||||
PaymentDetails.fromRaw = function fromRaw(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new PaymentDetails().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the payment details (protobuf).
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
PaymentDetails.prototype.toRaw = function toRaw() {
|
||||
var bw = new ProtoWriter();
|
||||
var i, op, output;
|
||||
@ -179,4 +239,8 @@ PaymentDetails.prototype.toRaw = function toRaw() {
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = PaymentDetails;
|
||||
|
||||
@ -16,6 +16,16 @@ var PaymentDetails = require('./paymentdetails');
|
||||
var ProtoReader = protobuf.ProtoReader;
|
||||
var ProtoWriter = protobuf.ProtoWriter;
|
||||
|
||||
/**
|
||||
* Represents a BIP70 payment request.
|
||||
* @param {Object?} options
|
||||
* @property {Number} version
|
||||
* @property {String|null} pkiType
|
||||
* @property {Buffer|null} pkiData
|
||||
* @property {PaymentDetails} paymentDetails
|
||||
* @property {Buffer|null} signature
|
||||
*/
|
||||
|
||||
function PaymentRequest(options) {
|
||||
if (!(this instanceof PaymentRequest))
|
||||
return new PaymentRequest(options);
|
||||
@ -30,6 +40,13 @@ function PaymentRequest(options) {
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @returns {PaymentRequest}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.version != null) {
|
||||
assert(util.isNumber(options.version));
|
||||
@ -60,10 +77,23 @@ PaymentRequest.prototype.fromOptions = function fromOptions(options) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment request from options.
|
||||
* @param {Object} options
|
||||
* @returns {PaymentRequest}
|
||||
*/
|
||||
|
||||
PaymentRequest.fromOptions = function fromOptions(options) {
|
||||
return new PaymentRequest().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentRequest}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new ProtoReader(data);
|
||||
|
||||
@ -76,12 +106,23 @@ PaymentRequest.prototype.fromRaw = function fromRaw(data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate payment request from serialized data.
|
||||
* @param {Buffer} data
|
||||
* @returns {PaymentRequest}
|
||||
*/
|
||||
|
||||
PaymentRequest.fromRaw = function fromRaw(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new PaymentRequest().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the payment request (protobuf).
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.toRaw = function toRaw() {
|
||||
var bw = new ProtoWriter();
|
||||
|
||||
@ -102,6 +143,11 @@ PaymentRequest.prototype.toRaw = function toRaw() {
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get payment request signature algorithm.
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.getAlgorithm = function getAlgorithm() {
|
||||
var parts;
|
||||
|
||||
@ -119,9 +165,14 @@ PaymentRequest.prototype.getAlgorithm = function getAlgorithm() {
|
||||
if (parts[1] !== 'sha1' && parts[1] !== 'sha256')
|
||||
return;
|
||||
|
||||
return { key: parts[0], hash: parts[1] };
|
||||
return new Algorithm(parts[0], parts[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize payment request for sighash.
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.signatureData = function signatureData() {
|
||||
var signature = this.signature;
|
||||
var data;
|
||||
@ -135,12 +186,22 @@ PaymentRequest.prototype.signatureData = function signatureData() {
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get signature hash.
|
||||
* @returns {Hash}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.signatureHash = function signatureHash() {
|
||||
var alg = this.getAlgorithm();
|
||||
assert(alg, 'No hash algorithm available.');
|
||||
return crypto.hash(alg.hash, this.signatureData());
|
||||
};
|
||||
|
||||
/**
|
||||
* Set x509 certificate chain.
|
||||
* @param {Buffer[]} chain
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.setChain = function setChain(chain) {
|
||||
var bw = new ProtoWriter();
|
||||
var i, cert, pem;
|
||||
@ -162,6 +223,11 @@ PaymentRequest.prototype.setChain = function setChain(chain) {
|
||||
this.pkiData = bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get x509 certificate chain.
|
||||
* @returns {Buffer[]}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.getChain = function getChain() {
|
||||
var chain = [];
|
||||
var br;
|
||||
@ -177,6 +243,12 @@ PaymentRequest.prototype.getChain = function getChain() {
|
||||
return chain;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sign payment request (chain must be set).
|
||||
* @param {Buffer} key
|
||||
* @param {Buffer[]?} chain
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.sign = function sign(key, chain) {
|
||||
var alg, msg;
|
||||
|
||||
@ -195,6 +267,11 @@ PaymentRequest.prototype.sign = function sign(key, chain) {
|
||||
this.signature = x509.signSubject(alg.hash, msg, key, chain);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify payment request signature.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.verify = function verify() {
|
||||
var alg, msg, sig, chain;
|
||||
|
||||
@ -216,6 +293,11 @@ PaymentRequest.prototype.verify = function verify() {
|
||||
return x509.verifySubject(alg.hash, msg, sig, chain);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify x509 certificate chain.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.verifyChain = function verifyChain() {
|
||||
if (!this.pkiType || this.pkiType === 'none')
|
||||
return true;
|
||||
@ -223,6 +305,11 @@ PaymentRequest.prototype.verifyChain = function verifyChain() {
|
||||
return x509.verifyChain(this.getChain());
|
||||
};
|
||||
|
||||
/**
|
||||
* Get root certificate authority.
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
|
||||
PaymentRequest.prototype.getCA = function getCA() {
|
||||
var chain, root;
|
||||
|
||||
@ -239,11 +326,32 @@ PaymentRequest.prototype.getCA = function getCA() {
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
return {
|
||||
name: x509.getCAName(root),
|
||||
trusted: x509.isTrusted(root),
|
||||
cert: root
|
||||
};
|
||||
return new CA(root);
|
||||
};
|
||||
|
||||
/**
|
||||
* Algorithm
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function Algorithm(key, hash) {
|
||||
this.key = key;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* CA
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function CA(root) {
|
||||
this.name = x509.getCAName(root);
|
||||
this.trusted = x509.isTrusted(root);
|
||||
this.cert = root;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = PaymentRequest;
|
||||
|
||||
@ -14,6 +14,71 @@ var crypto = require('../crypto/crypto');
|
||||
var pk = require('./pk');
|
||||
var x509 = exports;
|
||||
|
||||
/**
|
||||
* Map of trusted root certs.
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
x509.trusted = {};
|
||||
|
||||
/**
|
||||
* Whether to allow untrusted root
|
||||
* certs during verification.
|
||||
* @type {Boolean}
|
||||
*/
|
||||
|
||||
x509.allowUntrusted = false;
|
||||
|
||||
/**
|
||||
* OID to algorithm map for PKI.
|
||||
* @const {Object}
|
||||
* @see https://www.ietf.org/rfc/rfc2459.txt
|
||||
* @see https://tools.ietf.org/html/rfc3279
|
||||
* @see http://oid-info.com/get/1.2.840.10040.4
|
||||
* @see http://oid-info.com/get/1.2.840.113549.1.1
|
||||
* @see http://oid-info.com/get/1.2.840.10045.4.3
|
||||
*/
|
||||
|
||||
x509.oid = {
|
||||
'1.2.840.10040.4.1' : { key: 'dsa', hash: null },
|
||||
'1.2.840.10040.4.2' : { key: 'dsa', hash: null },
|
||||
'1.2.840.10040.4.3' : { key: 'dsa', hash: 'sha1' },
|
||||
'1.2.840.113549.1.1.1' : { key: 'rsa', hash: null },
|
||||
'1.2.840.113549.1.1.2' : { key: 'rsa', hash: 'md2' },
|
||||
'1.2.840.113549.1.1.3' : { key: 'rsa', hash: 'md4' },
|
||||
'1.2.840.113549.1.1.4' : { key: 'rsa', hash: 'md5' },
|
||||
'1.2.840.113549.1.1.5' : { key: 'rsa', hash: 'sha1' },
|
||||
'1.2.840.113549.1.1.11': { key: 'rsa', hash: 'sha256' },
|
||||
'1.2.840.113549.1.1.12': { key: 'rsa', hash: 'sha384' },
|
||||
'1.2.840.113549.1.1.13': { key: 'rsa', hash: 'sha512' },
|
||||
'1.2.840.113549.1.1.14': { key: 'rsa', hash: 'sha224' },
|
||||
'1.2.840.10045.2.1' : { key: 'ecdsa', hash: null },
|
||||
'1.2.840.10045.4.1' : { key: 'ecdsa', hash: 'sha1' },
|
||||
'1.2.840.10045.4.3.1' : { key: 'ecdsa', hash: 'sha224' },
|
||||
'1.2.840.10045.4.3.2' : { key: 'ecdsa', hash: 'sha256' },
|
||||
'1.2.840.10045.4.3.3' : { key: 'ecdsa', hash: 'sha384' },
|
||||
'1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' }
|
||||
};
|
||||
|
||||
/**
|
||||
* OID to curve name map for ECDSA.
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
x509.curves = {
|
||||
'1.3.132.0.33': 'p224',
|
||||
'1.2.840.10045.3.1.7': 'p256',
|
||||
'1.3.132.0.34': 'p384',
|
||||
'1.3.132.0.35': 'p521'
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve cert value by OID.
|
||||
* @param {Object} cert
|
||||
* @param {String} oid
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
x509.getSubjectOID = function getSubjectOID(cert, oid) {
|
||||
var subject = cert.tbs.subject;
|
||||
var i, entry;
|
||||
@ -25,6 +90,13 @@ x509.getSubjectOID = function getSubjectOID(cert, oid) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to retrieve CA name by checking
|
||||
* for a few different OIDs.
|
||||
* @param {Object} cert
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
x509.getCAName = function getCAName(cert) {
|
||||
// This seems to work the best in practice
|
||||
// for getting a human-readable and
|
||||
@ -41,8 +113,12 @@ x509.getCAName = function getCAName(cert) {
|
||||
|| 'Unknown';
|
||||
};
|
||||
|
||||
x509.trusted = {};
|
||||
x509.allowUntrusted = false;
|
||||
/**
|
||||
* Test whether a cert is trusted by hashing
|
||||
* and looking it up in the trusted map.
|
||||
* @param {Object} cert
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
x509.isTrusted = function isTrusted(cert) {
|
||||
var fingerprint = crypto.sha256(cert.raw);
|
||||
@ -50,6 +126,11 @@ x509.isTrusted = function isTrusted(cert) {
|
||||
return x509.trusted[hash] === true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add root certificates to the trusted map.
|
||||
* @param {Buffer[]} certs
|
||||
*/
|
||||
|
||||
x509.setTrust = function setTrust(certs) {
|
||||
var i, cert, pem, hash;
|
||||
|
||||
@ -85,52 +166,34 @@ x509.setTrust = function setTrust(certs) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* https://www.ietf.org/rfc/rfc2459.txt
|
||||
* https://tools.ietf.org/html/rfc3279
|
||||
* http://oid-info.com/get/1.2.840.10040.4
|
||||
* http://oid-info.com/get/1.2.840.113549.1.1
|
||||
* http://oid-info.com/get/1.2.840.10045.4.3
|
||||
/**
|
||||
* Retrieve key algorithm from cert.
|
||||
* @param {Object} cert
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
x509.oid = {
|
||||
'1.2.840.10040.4.1' : { key: 'dsa', hash: null },
|
||||
'1.2.840.10040.4.2' : { key: 'dsa', hash: null },
|
||||
'1.2.840.10040.4.3' : { key: 'dsa', hash: 'sha1' },
|
||||
'1.2.840.113549.1.1.1' : { key: 'rsa', hash: null },
|
||||
'1.2.840.113549.1.1.2' : { key: 'rsa', hash: 'md2' },
|
||||
'1.2.840.113549.1.1.3' : { key: 'rsa', hash: 'md4' },
|
||||
'1.2.840.113549.1.1.4' : { key: 'rsa', hash: 'md5' },
|
||||
'1.2.840.113549.1.1.5' : { key: 'rsa', hash: 'sha1' },
|
||||
'1.2.840.113549.1.1.11': { key: 'rsa', hash: 'sha256' },
|
||||
'1.2.840.113549.1.1.12': { key: 'rsa', hash: 'sha384' },
|
||||
'1.2.840.113549.1.1.13': { key: 'rsa', hash: 'sha512' },
|
||||
'1.2.840.113549.1.1.14': { key: 'rsa', hash: 'sha224' },
|
||||
'1.2.840.10045.2.1' : { key: 'ecdsa', hash: null },
|
||||
'1.2.840.10045.4.1' : { key: 'ecdsa', hash: 'sha1' },
|
||||
'1.2.840.10045.4.3.1' : { key: 'ecdsa', hash: 'sha224' },
|
||||
'1.2.840.10045.4.3.2' : { key: 'ecdsa', hash: 'sha256' },
|
||||
'1.2.840.10045.4.3.3' : { key: 'ecdsa', hash: 'sha384' },
|
||||
'1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' }
|
||||
};
|
||||
|
||||
x509.curves = {
|
||||
'1.3.132.0.33': 'p224',
|
||||
'1.2.840.10045.3.1.7': 'p256',
|
||||
'1.3.132.0.34': 'p384',
|
||||
'1.3.132.0.35': 'p521'
|
||||
};
|
||||
|
||||
x509.getKeyAlgorithm = function getKeyAlgorithm(cert) {
|
||||
var alg = cert.tbs.pubkey.alg.alg;
|
||||
return x509.oid[alg];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve signature algorithm from cert.
|
||||
* @param {Object} cert
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
x509.getSigAlgorithm = function getSigAlgorithm(cert) {
|
||||
var alg = cert.sigAlg.alg;
|
||||
return x509.oid[alg];
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup curve based on key parameters.
|
||||
* @param {Buffer} params
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
x509.getCurve = function getCurve(params) {
|
||||
var oid;
|
||||
|
||||
@ -146,6 +209,12 @@ x509.getCurve = function getCurve(params) {
|
||||
return x509.curves[oid];
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a DER formatted cert.
|
||||
* @param {Buffer} der
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
|
||||
x509.parse = function parse(der) {
|
||||
try {
|
||||
return ASN1.parseCert(der);
|
||||
@ -154,6 +223,12 @@ x509.parse = function parse(der) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get cert public key.
|
||||
* @param {Object} cert
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
|
||||
x509.getPublicKey = function getPublicKey(cert) {
|
||||
var alg = x509.getKeyAlgorithm(cert);
|
||||
var key, params, curve;
|
||||
@ -175,12 +250,25 @@ x509.getPublicKey = function getPublicKey(cert) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify cert expiration time.
|
||||
* @param {Object} cert
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
x509.verifyTime = function verifyTime(cert) {
|
||||
var time = cert.tbs.validity;
|
||||
var now = util.now();
|
||||
return now > time.notBefore && now < time.notAfter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get signature key info from cert chain.
|
||||
* @param {Buffer} key
|
||||
* @param {Buffer[]} chain
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
x509.getSigningKey = function getSigningKey(key, chain) {
|
||||
var cert, pub, curve;
|
||||
|
||||
@ -213,35 +301,65 @@ x509.getSigningKey = function getSigningKey(key, chain) {
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sign a hash with the chain signing key.
|
||||
* @param {String} hash
|
||||
* @param {Buffer} msg
|
||||
* @param {Buffer} key
|
||||
* @param {Buffer[]} chain
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
x509.signSubject = function signSubject(hash, msg, key, chain) {
|
||||
var priv = x509.getSigningKey(key, chain);
|
||||
return pk.sign(hash, msg, priv);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get chain verification key.
|
||||
* @param {Buffer[]} chain
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
|
||||
x509.getVerifyKey = function getVerifyKey(chain) {
|
||||
var cert, key;
|
||||
|
||||
if (chain.length === 0)
|
||||
return false;
|
||||
return;
|
||||
|
||||
cert = x509.parse(chain[0]);
|
||||
|
||||
if (!cert)
|
||||
return false;
|
||||
return;
|
||||
|
||||
key = x509.getPublicKey(cert);
|
||||
|
||||
if (!key)
|
||||
return false;
|
||||
return;
|
||||
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a sighash against chain verification key.
|
||||
* @param {String} hash
|
||||
* @param {Buffer} msg
|
||||
* @param {Buffer} sig
|
||||
* @param {Buffer[]} chain
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
|
||||
var key = x509.getVerifyKey(chain);
|
||||
return pk.verify(hash, msg, sig, key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse certificate chain.
|
||||
* @param {Buffer[]} chain
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
|
||||
x509.parseChain = function parseChain(chain) {
|
||||
var certs = [];
|
||||
var i, cert;
|
||||
@ -258,6 +376,12 @@ x509.parseChain = function parseChain(chain) {
|
||||
return certs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify all expiration times in a certificate chain.
|
||||
* @param {Object[]} chain
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
x509.verifyTimes = function verifyTimes(chain) {
|
||||
var i, cert;
|
||||
|
||||
@ -270,6 +394,13 @@ x509.verifyTimes = function verifyTimes(chain) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify that at least one parent
|
||||
* cert in the chain is trusted.
|
||||
* @param {Object[]} chain
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
x509.verifyTrust = function verifyTrust(chain) {
|
||||
var i, cert;
|
||||
|
||||
@ -294,6 +425,11 @@ x509.verifyTrust = function verifyTrust(chain) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify certificate chain.
|
||||
* @param {Object[]} certs
|
||||
*/
|
||||
|
||||
x509.verifyChain = function verifyChain(certs) {
|
||||
var chain = x509.parseChain(certs);
|
||||
var i, child, parent, alg, key, sig, msg;
|
||||
@ -331,6 +467,10 @@ x509.verifyChain = function verifyChain(certs) {
|
||||
return x509.verifyTrust(chain);
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function isHash(data) {
|
||||
if (typeof data === 'string')
|
||||
return util.isHex(data) && data.length === 64;
|
||||
@ -341,4 +481,8 @@ function isHash(data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load trusted certs.
|
||||
*/
|
||||
|
||||
x509.setTrust(require('../../etc/certs.json'));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user