Merge pull request #435 from chjj/paypro

WIP: BIP-70 x509 signing and verification
This commit is contained in:
Ryan X. Charles 2014-07-22 16:55:28 -04:00
commit 5f81682bd6
13 changed files with 4543 additions and 230 deletions

198
browser/root-certs Executable file
View File

@ -0,0 +1,198 @@
#!/usr/bin/env node
/**
* Modules
*/
var fs = require('fs');
var url = require('url');
var http = require('http');
var path = require('path');
var Stream = require('stream').Stream;
var StringDecoder = require('string_decoder').StringDecoder;
/**
* Mozilla Root Cert URL
*/
var certUrl = 'https://raw.githubusercontent.com/joyent/node/master/src/node_root_certs.h';
/**
* Get Root Certs
*/
function getRootCerts(callback) {
return request(certUrl, function(err, res, body) {
if (err) return callback(err);
body = body.replace(/,$/, '');
body = 'var RootCerts = [\n' + body + '\n];\n';
body = body.replace(/^"/gm, '+ "');
body = body.replace(/^\+ "-----B/gm, '"-----B');
body += ''
+ '\n'
+ '// Use hash table for efficiency:\n'
+ 'RootCerts = RootCerts.reduce(function(trusted, cert) {\n'
+ ' cert = cert.replace(/\\s+/g, "");\n'
+ ' trusted[cert] = true;\n'
+ ' return trusted;\n'
+ '}, {});\n'
+ '\n'
+ 'function isTrusted(pem) {\n'
+ ' pem = pem + "";\n'
+ ' pem = pem.replace(/\\s+/g, "");\n'
+ ' return !!RootCerts[pem];\n'
+ '}\n'
+ '\n'
+ 'exports = RootCerts;\n'
+ 'exports.isTrusted = isTrusted;\n'
+ 'module.exports = exports;\n';
return callback(null, body);
});
}
/**
* Helpers
*/
function request(options, callback) {
if (typeof options === 'string' || options.hostname) {
options = { uri: options };
}
var uri = options.uri || options.url
, body = options.json
? JSON.stringify(options.json)
: options.body || '';
if (typeof uri !== 'object') {
uri = url.parse(uri);
}
if (options.qs) {
var query = uri.query ? qs.parse(uri.query) : {};
Object.keys(options.qs).forEach(function(key) {
query[key] = options.qs[key];
});
uri.path = uri.pathname + '?' + qs.stringify(query);
}
var protocol = uri.protocol === 'https:'
? require('https')
: http;
options.method = options.method || (body ? 'POST' : 'GET');
options.method = options.method.toUpperCase();
options.headers = options.headers || {};
options.headers['Accept'] = options.headers['Accept'] || 'text/plain; charset=utf-8';
if (options.json) {
options.headers['Content-Type'] = 'application/json; charset=utf-8';
options.headers['Accept'] = 'application/json';
}
if (options.method !== 'GET' && options.method !== 'HEAD') {
options.headers['Content-Length'] = Buffer.byteLength(body);
}
var opt = {
auth: uri.auth,
host: uri.hostname,
port: uri.port || (protocol === http ? 80 : 443),
path: uri.path,
method: options.method,
headers: options.headers
};
var req = protocol.request(opt)
, response = new Stream;
req.on('error', function(err) {
if (callback) {
callback(err);
} else {
response.emit('error', err);
}
});
req.on('response', function(res) {
var decoder = new StringDecoder('utf8')
, done = false
, body = '';
function end() {
if (done) return;
done = true;
if (callback) {
res.body = body;
if (options.json) {
try {
body = JSON.parse(body);
} catch (e) {
;
}
}
callback(null, res, body);
} else {
response.emit('end');
}
res.socket.removeListener('error', error);
res.socket.removeListener('end', end);
}
function error(err) {
res.destroy();
if (callback) {
callback(err);
} else {
response.emit('error', err);
}
}
res.on('data', function(data) {
if (callback) {
body += decoder.write(data);
} else {
response.emit('data', data);
}
});
res.on('error', error);
res.socket.on('error', error);
res.on('end', end);
// An agent socket's `end` sometimes
// wont be emitted on the response.
res.socket.on('end', end);
});
req.end(body);
return response;
}
/**
* Execute
*/
function main(argv, callback) {
if (!callback) {
callback = argv;
argv = null;
}
return getRootCerts(function(err, certs) {
var file = path.resolve(__dirname, '..', 'lib', 'common', 'RootCerts.js');
return fs.writeFile(file, certs, callback);
});
}
if (!module.parent) {
process.title = 'root-certs';
main(process.argv.slice(), function(err, code) {
if (err) throw err;
return process.exit(code || 0);
});
} else {
module.exports = main;
}

View File

@ -1,245 +1,61 @@
'use strict';
var protobufjs = protobufjs || require('protobufjs/dist/ProtoBuf');
var Message = Message || require('./Message');
// BIP 70 - payment protocol
function PayPro() {
this.messageType = null;
this.message = null;
}
var RootCerts = require('./common/RootCerts');
PayPro.PAYMENT_REQUEST_MAX_SIZE = 50000;
PayPro.PAYMENT_MAX_SIZE = 50000;
PayPro.PAYMENT_ACK_MAX_SIZE = 60000;
PayPro.PAYMENT_REQUEST_CONTENT_TYPE = "application/bitcoin-paymentrequest";
PayPro.PAYMENT_CONTENT_TYPE = "application/bitcoin-payment";
PayPro.PAYMENT_ACK_CONTENT_TYPE = "application/bitcoin-paymentack";
var PayPro = require('./common/PayPro');
PayPro.proto = {};
PayPro.prototype.x509Sign = function(key) {
var self = this;
var crypto = require('crypto');
var pki_type = this.get('pki_type');
var pki_data = this.get('pki_data'); // contains one or more x509 certs
var details = this.get('serialized_payment_details');
var type = pki_type.split('+')[1].toUpperCase();
PayPro.proto.Output = "message Output {\
optional uint64 amount = 1 [default = 0];\
optional bytes script = 2;\
}\n";
var trusted = [].concat(pki_data).every(function(cert) {
var der = cert.toString('hex');
var pem = self._DERtoPEM(der, 'CERTIFICATE');
return RootCerts.isTrusted(pem);
});
PayPro.proto.PaymentDetails = "message PaymentDetails {\
optional string network = 1 [default = \"main\"];\
repeated Output outputs = 2;\
required uint64 time = 3;\
optional uint64 expires = 4;\
optional string memo = 5;\
optional string payment_url = 6;\
optional bytes merchant_data = 7;\
}\n";
PayPro.proto.PaymentRequest = "message PaymentRequest {\
optional uint32 payment_details_version = 1 [default = 1];\
optional string pki_type = 2 [default = \"none\"];\
optional bytes pki_data = 3;\
required bytes serialized_payment_details = 4;\
optional bytes signature = 5;\
}\n";
PayPro.proto.Payment = "message Payment {\
optional bytes merchant_data = 1;\
repeated bytes transactions = 2;\
repeated Output refund_to = 3;\
optional string memo = 4;\
}\n";
PayPro.proto.PaymentACK = "message PaymentACK {\
required Payment payment = 1;\
optional string memo = 2;\
}\n";
PayPro.proto.X509Certificates = "message X509Certificates {\
repeated bytes certificate = 1;\
}\n";
PayPro.proto.all = "";
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Output;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentDetails;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentRequest;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Payment;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentACK;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.X509Certificates;
PayPro.builder = protobufjs.loadProto(PayPro.proto.all);
PayPro.Output = PayPro.builder.build("Output");
PayPro.PaymentDetails = PayPro.builder.build("PaymentDetails");
PayPro.PaymentRequest = PayPro.builder.build("PaymentRequest");
PayPro.Payment = PayPro.builder.build("Payment");
PayPro.PaymentACK = PayPro.builder.build("PaymentACK");
PayPro.X509Certificates = PayPro.builder.build("X509Certificates");
PayPro.prototype.makeOutput = function(obj) {
this.messageType = 'Output';
this.message = new PayPro.Output();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentDetails = function(obj) {
this.messageType = 'PaymentDetails';
this.message = new PayPro.PaymentDetails();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentRequest = function(obj) {
this.messageType = 'PaymentRequest';
this.message = new PayPro.PaymentRequest();
this.setObj(obj);
return this;
};
PayPro.prototype.makePayment = function(obj) {
this.messageType = 'Payment';
this.message = new PayPro.Payment();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentACK = function(obj) {
this.messageType = 'Payment';
this.message = new PayPro.PaymentACK();
this.setObj(obj);
return this;
};
PayPro.prototype.makeX509Certificates = function(obj) {
this.messageType = 'X509Certificates';
this.message = new PayPro.X509Certificates();
this.setObj(obj);
return this;
};
PayPro.prototype.isValidSize = function() {
var s = this.serialize();
if (this.messageType == 'PaymentRequest')
return s.length < PayPro.PAYMENT_REQUEST_MAX_SIZE;
if (this.messageType == 'Payment')
return s.length < PayPro.PAYMENT_MAX_SIZE;
if (this.messageType == 'PaymentACK')
return s.length < PayPro.PAYMENT_ACK_MAX_SIZE;
return true;
};
PayPro.prototype.getContentType = function() {
if (this.messageType == 'PaymentRequest')
return PayPro.PAYMENT_REQUEST_CONTENT_TYPE;
if (this.messageType == 'Payment')
return PayPro.PAYMENT_CONTENT_TYPE;
if (this.messageType == 'PaymentACK')
return PayPro.PAYMENT_ACK_CONTENT_TYPE;
throw new Error('No known content type for this message type');
};
PayPro.prototype.set = function(key, val) {
this.message.set(key, val);
return this;
};
PayPro.prototype.get = function(key) {
var v = this.message.get(key);
if (v === null)
return v;
//protobuf supports longs, javascript naturally does not
//convert longs (see long.js, e.g. require('long')) to Numbers
if (typeof v.low !== 'undefined' && typeof v.high !== 'undefined')
return v.toInt();
if (typeof v.toBuffer !== 'undefined') {
var maybebuf = v.toBuffer();
return Buffer.isBuffer(maybebuf) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
if (!trusted) {
// XXX Figure out what to do here
// throw new Error('Unstrusted certificate.');
}
return v;
};
PayPro.prototype.setObj = function(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
this.message.set(key, val);
}
}
return this;
};
PayPro.prototype.serializeForSig = function() {
if (this.messageType !== 'PaymentRequest')
throw new Error('serializeForSig is only for PaymentRequest');
var save = this.message.get('signature');
this.message.set('signature', new Buffer([]));
var buf = this.serialize();
this.message.set('signature', save);
return buf;
};
PayPro.prototype.serialize = function() {
//protobufjs returns either a Buffer or an ArrayBuffer
//but we always want a Buffer (which browserify understands, browser or no)
var maybebuf = this.message.toBuffer();
var buf = (Buffer.isBuffer(maybebuf)) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
return buf;
};
PayPro.prototype.deserialize = function(buf, messageType) {
this.messageType = messageType || this.messageType;
if (!this.messageType)
throw new Error('Must specify messageType');
this.message = PayPro[this.messageType].decode(buf);
return this;
};
PayPro.prototype.sign = function(key) {
if (this.messageType !== 'PaymentRequest')
throw new Error('Signing can only be performed on a PaymentRequest');
var pki_type = this.get('pki_type');
if (pki_type === 'SIN')
var sig = this.sinSign(key);
else
throw new Error('Unsupported pki_type');
this.set('signature', sig);
return this;
};
PayPro.prototype.verify = function() {
if (this.messageType !== 'PaymentRequest')
throw new Error('Verifying can only be performed on a PaymentRequest');
var pki_type = this.get('pki_type');
if (pki_type === 'SIN')
return this.sinVerify();
else
throw new Error('Unsupported pki_type');
};
//default signing function for prototype.sign
PayPro.prototype.sinSign = function(key) {
this.set('pki_data', key.public)
var signature = crypto.createSign('RSA-' + type);
var buf = this.serializeForSig();
return Message.sign(buf, key);
signature.update(buf);
var sig = signature.sign(key);
return sig;
};
//default verify function
PayPro.prototype.sinVerify = function() {
PayPro.prototype.x509Verify = function() {
var self = this;
var crypto = require('crypto');
var pki_type = this.get('pki_type');
var sig = this.get('signature');
var pubkey = this.get('pki_data');
var pki_data = this.get('pki_data');
var details = this.get('serialized_payment_details');
var buf = this.serializeForSig();
return Message.verifyWithPubKey(pubkey, buf, sig);
var type = pki_type.split('+')[1].toUpperCase();
var verifier = crypto.createVerify('RSA-' + type);
verifier.update(buf);
return [].concat(pki_data).every(function(cert) {
var der = cert.toString('hex');
var pem = self._DERtoPEM(der, 'CERTIFICATE');
if (!RootCerts.isTrusted(pem)) {
// XXX Figure out what to do here
// throw new Error('Unstrusted certificate.');
}
return verifier.verify(pem, sig);
});
};
module.exports = PayPro;

79
lib/browser/PayPro.js Normal file
View File

@ -0,0 +1,79 @@
"use strict";
var Key = require('./Key');
var KJUR = require('jsrsasign');
var assert = require('assert');
var PayPro = require('../common/PayPro');
var RootCerts = require('../common/RootCerts');
// Documentation:
// http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html#.sign
// http://kjur.github.io/jsrsasign/api/symbols/RSAKey.html
PayPro.prototype.x509Sign = function(key) {
var pki_type = this.get('pki_type');
var pki_data = this.get('pki_data'); // contains one or more x509 certs
var type = pki_type.split('+')[1].toUpperCase();
var buf = this.serializeForSig();
var trusted = [].concat(pki_data).every(function(cert) {
var der = cert.toString('hex');
var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
return RootCerts.isTrusted(pem);
});
if (!trusted) {
// XXX Figure out what to do here
// throw new Error('Unstrusted certificate.');
}
var rsa = new KJUR.RSAKey();
rsa.readPrivateKeyFromPEMString(key.toString());
key = rsa;
var jsrsaSig = new KJUR.crypto.Signature({
alg: type.toUpperCase() + 'withRSA',
prov: 'cryptojs/jsrsa'
});
// XXX Could use this?
//jsrsaSig.initSign(key);
jsrsaSig.init(key);
jsrsaSig.updateHex(buf.toString('hex'));
var sig = new Buffer(jsrsaSig.sign(), 'hex');
return sig;
};
PayPro.prototype.x509Verify = function(key) {
var sig = this.get('signature');
var pki_type = this.get('pki_type');
var pki_data = this.get('pki_data');
var buf = this.serializeForSig();
var type = pki_type.split('+')[1].toUpperCase();
var jsrsaSig = new KJUR.crypto.Signature({
alg: type.toUpperCase() + 'withRSA',
prov: 'cryptojs/jsrsa'
});
return [].concat(pki_data).every(function(cert) {
var der = cert.toString('hex');
var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE');
if (!RootCerts.isTrusted(pem)) {
// XXX Figure out what to do here
// throw new Error('Unstrusted certificate.');
}
jsrsaSig.initVerifyByCertificatePEM(pem);
jsrsaSig.updateHex(buf.toString('hex'));
return jsrsaSig.verify(sig.toString('hex'));
});
};
module.exports = PayPro;

296
lib/common/PayPro.js Normal file
View File

@ -0,0 +1,296 @@
'use strict';
var protobufjs = require('protobufjs/dist/ProtoBuf');
var Message = require('../Message');
var RootCerts = require('../common/RootCerts');
// BIP 70 - payment protocol
function PayPro() {
this.messageType = null;
this.message = null;
}
PayPro.PAYMENT_REQUEST_MAX_SIZE = 50000;
PayPro.PAYMENT_MAX_SIZE = 50000;
PayPro.PAYMENT_ACK_MAX_SIZE = 60000;
PayPro.PAYMENT_REQUEST_CONTENT_TYPE = "application/bitcoin-paymentrequest";
PayPro.PAYMENT_CONTENT_TYPE = "application/bitcoin-payment";
PayPro.PAYMENT_ACK_CONTENT_TYPE = "application/bitcoin-paymentack";
PayPro.proto = {};
PayPro.proto.Output = "message Output {\
optional uint64 amount = 1 [default = 0];\
optional bytes script = 2;\
}\n";
PayPro.proto.PaymentDetails = "message PaymentDetails {\
optional string network = 1 [default = \"main\"];\
repeated Output outputs = 2;\
required uint64 time = 3;\
optional uint64 expires = 4;\
optional string memo = 5;\
optional string payment_url = 6;\
optional bytes merchant_data = 7;\
}\n";
PayPro.proto.PaymentRequest = "message PaymentRequest {\
optional uint32 payment_details_version = 1 [default = 1];\
optional string pki_type = 2 [default = \"none\"];\
optional bytes pki_data = 3;\
required bytes serialized_payment_details = 4;\
optional bytes signature = 5;\
}\n";
PayPro.proto.Payment = "message Payment {\
optional bytes merchant_data = 1;\
repeated bytes transactions = 2;\
repeated Output refund_to = 3;\
optional string memo = 4;\
}\n";
PayPro.proto.PaymentACK = "message PaymentACK {\
required Payment payment = 1;\
optional string memo = 2;\
}\n";
PayPro.proto.X509Certificates = "message X509Certificates {\
repeated bytes certificate = 1;\
}\n";
PayPro.proto.all = "";
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Output;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentDetails;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentRequest;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.Payment;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.PaymentACK;
PayPro.proto.all = PayPro.proto.all + PayPro.proto.X509Certificates;
PayPro.builder = protobufjs.loadProto(PayPro.proto.all);
PayPro.Output = PayPro.builder.build("Output");
PayPro.PaymentDetails = PayPro.builder.build("PaymentDetails");
PayPro.PaymentRequest = PayPro.builder.build("PaymentRequest");
PayPro.Payment = PayPro.builder.build("Payment");
PayPro.PaymentACK = PayPro.builder.build("PaymentACK");
PayPro.X509Certificates = PayPro.builder.build("X509Certificates");
PayPro.prototype.makeOutput = function(obj) {
this.messageType = 'Output';
this.message = new PayPro.Output();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentDetails = function(obj) {
this.messageType = 'PaymentDetails';
this.message = new PayPro.PaymentDetails();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentRequest = function(obj) {
this.messageType = 'PaymentRequest';
this.message = new PayPro.PaymentRequest();
this.setObj(obj);
return this;
};
PayPro.prototype.makePayment = function(obj) {
this.messageType = 'Payment';
this.message = new PayPro.Payment();
this.setObj(obj);
return this;
};
PayPro.prototype.makePaymentACK = function(obj) {
this.messageType = 'Payment';
this.message = new PayPro.PaymentACK();
this.setObj(obj);
return this;
};
PayPro.prototype.makeX509Certificates = function(obj) {
this.messageType = 'X509Certificates';
this.message = new PayPro.X509Certificates();
this.setObj(obj);
return this;
};
PayPro.prototype.isValidSize = function() {
var s = this.serialize();
if (this.messageType == 'PaymentRequest')
return s.length < PayPro.PAYMENT_REQUEST_MAX_SIZE;
if (this.messageType == 'Payment')
return s.length < PayPro.PAYMENT_MAX_SIZE;
if (this.messageType == 'PaymentACK')
return s.length < PayPro.PAYMENT_ACK_MAX_SIZE;
return true;
};
PayPro.prototype.getContentType = function() {
if (this.messageType == 'PaymentRequest')
return PayPro.PAYMENT_REQUEST_CONTENT_TYPE;
if (this.messageType == 'Payment')
return PayPro.PAYMENT_CONTENT_TYPE;
if (this.messageType == 'PaymentACK')
return PayPro.PAYMENT_ACK_CONTENT_TYPE;
throw new Error('No known content type for this message type');
};
PayPro.prototype.set = function(key, val) {
this.message.set(key, val);
return this;
};
PayPro.prototype.get = function(key) {
var v = this.message.get(key);
if (v === null)
return v;
//protobuf supports longs, javascript naturally does not
//convert longs (see long.js, e.g. require('long')) to Numbers
if (typeof v.low !== 'undefined' && typeof v.high !== 'undefined')
return v.toInt();
if (typeof v.toBuffer !== 'undefined') {
var maybebuf = v.toBuffer();
return Buffer.isBuffer(maybebuf) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
}
return v;
};
PayPro.prototype.setObj = function(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
this.message.set(key, val);
}
}
return this;
};
PayPro.prototype.serializeForSig = function() {
if (this.messageType !== 'PaymentRequest')
throw new Error('serializeForSig is only for PaymentRequest');
var save = this.message.get('signature');
this.message.set('signature', new Buffer([]));
var buf = this.serialize();
this.message.set('signature', save);
return buf;
};
PayPro.prototype.serialize = function() {
//protobufjs returns either a Buffer or an ArrayBuffer
//but we always want a Buffer (which browserify understands, browser or no)
var maybebuf = this.message.toBuffer();
var buf = (Buffer.isBuffer(maybebuf)) ? maybebuf : new Buffer(new Uint8Array(maybebuf));
return buf;
};
PayPro.prototype.deserialize = function(buf, messageType) {
this.messageType = messageType || this.messageType;
if (!this.messageType)
throw new Error('Must specify messageType');
this.message = PayPro[this.messageType].decode(buf);
return this;
};
PayPro.prototype.sign = function(key) {
if (this.messageType !== 'PaymentRequest')
throw new Error('Signing can only be performed on a PaymentRequest');
var pki_type = this.get('pki_type');
if (pki_type === 'SIN') {
var sig = this.sinSign(key);
} else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
var sig = this.x509Sign(key);
} else if (pki_type === 'none') {
return this;
} else {
throw new Error('Unsupported pki_type');
}
this.set('signature', sig);
return this;
};
PayPro.prototype.verify = function() {
if (this.messageType !== 'PaymentRequest')
throw new Error('Verifying can only be performed on a PaymentRequest');
var pki_type = this.get('pki_type');
if (pki_type === 'SIN') {
return this.sinVerify();
} else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') {
return this.x509Verify();
} else if (pki_type === 'none') {
return true;
}
throw new Error('Unsupported pki_type');
};
//default signing function for prototype.sign
PayPro.prototype.sinSign = function(key) {
this.set('pki_data', key.public)
var buf = this.serializeForSig();
return Message.sign(buf, key);
};
//default verify function
PayPro.prototype.sinVerify = function() {
var sig = this.get('signature');
var pubkey = this.get('pki_data');
var buf = this.serializeForSig();
return Message.verifyWithPubKey(pubkey, buf, sig);
};
// Helpers
PayPro.prototype._PEMtoDER = function(pem) {
return this._PEMtoDERParam(pem);
};
PayPro.prototype._PEMtoDERParam = function(pem, param) {
if (Buffer.isBuffer(pem)) {
pem = pem.toString();
}
var start = new RegExp('(?=-----BEGIN ' + (param || '[^-]+') + '-----)', 'i');
var end = new RegExp('^-----END ' + (param || '[^-]+') + '-----$', 'gmi');
pem = pem.replace(end, '');
var parts = pem.split(start);
return parts.map(function(part) {
var type = /-----BEGIN ([^-]+)-----/.exec(part)[1];
part = part.replace(/-----BEGIN ([^-]+)-----/g, '');
part = part.replace(/\s+/g, '');
if (!param || type !== param) return;
return new Buffer(part, 'base64');
}).filter(Boolean);
};
PayPro.prototype._DERtoPEM = function(der, type) {
if (typeof der === 'string') {
der = new Buffer(der, 'hex');
}
var type = type || 'UNKNOWN';
der = der.toString('base64');
der = der.replace(/(.{64})/g, '$1\r\n');
der = der.replace(/\r\n$/, '');
return ''
+ '-----BEGIN ' + type + '-----\r\n'
+ der
+ '\r\n-----END ' + type + '-----\r\n';
};
module.exports = PayPro;

3579
lib/common/RootCerts.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,7 @@
"grunt-contrib-watch": "~0.5.3",
"grunt-markdown": "~0.5.0",
"grunt-mocha-test": "~0.8.2",
"grunt-shell": "~0.6.4",
"grunt-shell": "~0.6.4",
"protobufjs": "=3.0.0",
"coveralls": "^2.10.0",
"istanbul": "~0.2.6",
@ -65,6 +65,7 @@
"sjcl": "=1.0.1",
"hash.js": "=0.3.1",
"bn.js": "=0.13.3",
"jsrsasign": "=0.0.3",
"elliptic": "=0.15.7",
"bindings": "=1.1.1",
"bufferput": "git://github.com/bitpay/node-bufferput.git",
@ -104,7 +105,8 @@
"./lib/Key.js": "./lib/browser/Key.js",
"./lib/Point.js": "./lib/browser/Point.js",
"./lib/ECIES.js": "./lib/browser/ECIES.js",
"./lib/SecureRandom.js": "./lib/browser/SecureRandom.js"
"./lib/SecureRandom.js": "./lib/browser/SecureRandom.js",
"./lib/PayPro.js": "./lib/browser/PayPro.js"
},
"license": "MIT",
"engines": {

19
test/data/x509.crt Normal file
View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQDI2qWdA3/VpDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE0MDcxNjAxMzM1MVoXDTE1MDcxNjAxMzM1MVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMUybitmhi59XVySg4eDMDy1JJdxyOaRpxuWnOJf9IoZqvHMAvhVT4GTEufWspIr
Afswu77b5CZP27/gUjl+6MXSLLi93SkBabl11X63W/ecGJ+DPv54xIVzJYVTB/zg
ogIPSSoUyli+hkVmQAIQO5QUYYEjQ04kwudQm4iu8QOqTePrUB8aJiT162UZGwdp
JTn1sG7/d2NydShQ/otDzEBy8O64RLpsQvd/hx1FWFR+x9Uz2R9W3VCSdNoEHspS
bLGAicnWT8zU/87rbPyiVnUb+ZccBvbvbTDTIISW5gGjJDBovqOzzRMj96hxa2KW
IbOaaixzETQe/EDPSHjYyxcCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAL6AMMfC3
TlRcmsIgHxjVD4XYtISlldnrn2X9zvFbJKCpNy8XQQosQxrhyfzPHQKjlS2L/KCG
Mnjx9QkYD2Hlp1MJ1uVv9888th/gcZOv3Or3hQyi5K1Sh5xCG+69lUOqUEGu9B4i
rsqoFomQVbQolSy+t4apdJi7kuEDwFDk4gZiVEfsuX+naN5a6pCnWnhX1Vf4fKwf
kLobKKXm2zQVsjxlwBAqOEmJGDLoRMXH56qJnEZ/dqsczaJOHQSi9mFEHL0r5rsE
DTT5AVxdnBfNnyGaCH7/zANEko+FGBj1JdJaJgFTXdbxDoyoPTPD+LJqSK5XYToo
46y/T0u9CLveNA==
-----END CERTIFICATE-----

16
test/data/x509.csr Normal file
View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMUybitmhi59XVySg4eDMDy1JJdxyOaRpxuWnOJf
9IoZqvHMAvhVT4GTEufWspIrAfswu77b5CZP27/gUjl+6MXSLLi93SkBabl11X63
W/ecGJ+DPv54xIVzJYVTB/zgogIPSSoUyli+hkVmQAIQO5QUYYEjQ04kwudQm4iu
8QOqTePrUB8aJiT162UZGwdpJTn1sG7/d2NydShQ/otDzEBy8O64RLpsQvd/hx1F
WFR+x9Uz2R9W3VCSdNoEHspSbLGAicnWT8zU/87rbPyiVnUb+ZccBvbvbTDTIISW
5gGjJDBovqOzzRMj96hxa2KWIbOaaixzETQe/EDPSHjYyxcCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQCu1Pdo1bKywBtzBXkqGudI5Nab6ixthjtlByTXdRd/7iy9
zPMqoM6DBB9VtS6mnco7G1R8VaJu83N/uRoWCM80lb47isXRoMLe1DOrkjWmzjdU
/rvwjhEzq2v42OVASgTD/sRziDOJ2DTG5W4VMEDzIEHFCXdbCc2HodA5/iSM9m5i
FhhYC9HAHdHXgh1SVulxSGj6zOanOoNs68UBRnyGhS+c4lEDqlWw+WMioxmGgKic
XnFvGu5u6g7lzjKUV5H/oHnLhkY3llA67dyz3EK6Nij9+CLfNHfTz4c6DO2J9FQp
ME9S6EZSnvyDIKT6pgVk3nDiLzyYpQJ44cDlH2Db
-----END CERTIFICATE REQUEST-----

BIN
test/data/x509.der Normal file

Binary file not shown.

27
test/data/x509.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxTJuK2aGLn1dXJKDh4MwPLUkl3HI5pGnG5ac4l/0ihmq8cwC
+FVPgZMS59aykisB+zC7vtvkJk/bv+BSOX7oxdIsuL3dKQFpuXXVfrdb95wYn4M+
/njEhXMlhVMH/OCiAg9JKhTKWL6GRWZAAhA7lBRhgSNDTiTC51CbiK7xA6pN4+tQ
HxomJPXrZRkbB2klOfWwbv93Y3J1KFD+i0PMQHLw7rhEumxC93+HHUVYVH7H1TPZ
H1bdUJJ02gQeylJssYCJydZPzNT/zuts/KJWdRv5lxwG9u9tMNMghJbmAaMkMGi+
o7PNEyP3qHFrYpYhs5pqLHMRNB78QM9IeNjLFwIDAQABAoIBAQDERrjPbAGcnl1h
+db+9734fthIARJVJJ5u4E+RJq8REhFLEKPYJ5m2P/xuVA1zXWlgaxZEFzwUQiJY
7l8JKV9pHxQyYZCS8vwXg8iXksvwPidoBcuan/wDVCQBey6VLcUzRaGuR/lLsX+V
7ftB0oRqlIqkbcPdMMvqTxowRuhPmuCrVTjO4pbNqnSONPLOiJ/FAXb2pfzFfpBR
Gx+EMmzwe+HFnJBGDhHZ99nn/TBfaJzNVCqDY/3bwz5X7HQNY7T+JyTUFseQ94xs
zrkibtdfTcTjpu+UhZo4sZzCr+fHGZoE9GDPqtd4gCprk4EKJsmqBESxBXSDhYgN
9pUD38sZAoGBAOrfDjt6Z/FCjanU8WzNFif+9T1A2xoMwD5VSlMuRrYmGlfr0C9M
f1EogivuTkbp7rkgdTaYTSbwfNqZBKxctyc7BhdpZxDEWJkfyq8qVx/zmBzMI+Vs
2Kb/apvWrbeosDOCryH5c8JsUT9xTX3XbxEjyOJQBSYGDMjPyJ6E9s6LAoGBANbv
wgtKckkKKl2a5vshdvDCg6qK/QgOm/6KTJUJESjhz2tR+fPVR70TH9RhhTRlpDWB
Bwz2ScBsTQ42/Lk1Fy20T/rMvKufHL5TMA4fz5dq1LHnczz6Uk5gXKAOOkR9UuXi
Tty3hDG2BC86MKMRxJ51EbqjoxwER100SanMPfMlAoGAIHKcZr8saPpG0/WlPODA
dNoWS1YQbvLgBDyIPiGghz2QWiEr67znwfCUuzj6+3UKE+1WBCraTczfktuc96r/
ap4O42EeagSWMOFhgP2ad8GRfDj/pIx7CeczdUAdU8gsP5GIXGs4AN4yA0/F4uLG
Z1nIQOvJKk2fqoZ6MtwvtK0CgYEAgJ7FLeCE93Rf2dgCdTGXbYfZJss5lAK6Et5L
6budSyul5gEOZH2zClBRcdRR1AMm+uWVhIo1pDKrAeCh52r/zdfjLKAsHz9+Ad7i
GPGsVl4Vnch1S344lrJPe3BIKggc/Xgp3SbssprLcj+OL2dIk9JWo6ucxf1Bf2L0
2elhAQkCgYBXsyzVV/ZJqXNpWCg55L3UFoTLyKSqlVKM3WiG5BKn4AazVCHL+GQU
xwxSgR9fQ4Inu+rPrN3Imyk0lKPGF9SzCRRTiJFR72sNqlO6l0V8CWRAPTPJcgqV
1U8NHK8b3ZiIoGJ+msNzpdxrj62H3A6+Y+AsNY4SmUTXH9yjg+nukg==
-----END RSA PRIVATE KEY-----

46
test/data/x509.pem Normal file
View File

@ -0,0 +1,46 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQDI2qWdA3/VpDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE0MDcxNjAxMzM1MVoXDTE1MDcxNjAxMzM1MVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMUybitmhi59XVySg4eDMDy1JJdxyOaRpxuWnOJf9IoZqvHMAvhVT4GTEufWspIr
Afswu77b5CZP27/gUjl+6MXSLLi93SkBabl11X63W/ecGJ+DPv54xIVzJYVTB/zg
ogIPSSoUyli+hkVmQAIQO5QUYYEjQ04kwudQm4iu8QOqTePrUB8aJiT162UZGwdp
JTn1sG7/d2NydShQ/otDzEBy8O64RLpsQvd/hx1FWFR+x9Uz2R9W3VCSdNoEHspS
bLGAicnWT8zU/87rbPyiVnUb+ZccBvbvbTDTIISW5gGjJDBovqOzzRMj96hxa2KW
IbOaaixzETQe/EDPSHjYyxcCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAL6AMMfC3
TlRcmsIgHxjVD4XYtISlldnrn2X9zvFbJKCpNy8XQQosQxrhyfzPHQKjlS2L/KCG
Mnjx9QkYD2Hlp1MJ1uVv9888th/gcZOv3Or3hQyi5K1Sh5xCG+69lUOqUEGu9B4i
rsqoFomQVbQolSy+t4apdJi7kuEDwFDk4gZiVEfsuX+naN5a6pCnWnhX1Vf4fKwf
kLobKKXm2zQVsjxlwBAqOEmJGDLoRMXH56qJnEZ/dqsczaJOHQSi9mFEHL0r5rsE
DTT5AVxdnBfNnyGaCH7/zANEko+FGBj1JdJaJgFTXdbxDoyoPTPD+LJqSK5XYToo
46y/T0u9CLveNA==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxTJuK2aGLn1dXJKDh4MwPLUkl3HI5pGnG5ac4l/0ihmq8cwC
+FVPgZMS59aykisB+zC7vtvkJk/bv+BSOX7oxdIsuL3dKQFpuXXVfrdb95wYn4M+
/njEhXMlhVMH/OCiAg9JKhTKWL6GRWZAAhA7lBRhgSNDTiTC51CbiK7xA6pN4+tQ
HxomJPXrZRkbB2klOfWwbv93Y3J1KFD+i0PMQHLw7rhEumxC93+HHUVYVH7H1TPZ
H1bdUJJ02gQeylJssYCJydZPzNT/zuts/KJWdRv5lxwG9u9tMNMghJbmAaMkMGi+
o7PNEyP3qHFrYpYhs5pqLHMRNB78QM9IeNjLFwIDAQABAoIBAQDERrjPbAGcnl1h
+db+9734fthIARJVJJ5u4E+RJq8REhFLEKPYJ5m2P/xuVA1zXWlgaxZEFzwUQiJY
7l8JKV9pHxQyYZCS8vwXg8iXksvwPidoBcuan/wDVCQBey6VLcUzRaGuR/lLsX+V
7ftB0oRqlIqkbcPdMMvqTxowRuhPmuCrVTjO4pbNqnSONPLOiJ/FAXb2pfzFfpBR
Gx+EMmzwe+HFnJBGDhHZ99nn/TBfaJzNVCqDY/3bwz5X7HQNY7T+JyTUFseQ94xs
zrkibtdfTcTjpu+UhZo4sZzCr+fHGZoE9GDPqtd4gCprk4EKJsmqBESxBXSDhYgN
9pUD38sZAoGBAOrfDjt6Z/FCjanU8WzNFif+9T1A2xoMwD5VSlMuRrYmGlfr0C9M
f1EogivuTkbp7rkgdTaYTSbwfNqZBKxctyc7BhdpZxDEWJkfyq8qVx/zmBzMI+Vs
2Kb/apvWrbeosDOCryH5c8JsUT9xTX3XbxEjyOJQBSYGDMjPyJ6E9s6LAoGBANbv
wgtKckkKKl2a5vshdvDCg6qK/QgOm/6KTJUJESjhz2tR+fPVR70TH9RhhTRlpDWB
Bwz2ScBsTQ42/Lk1Fy20T/rMvKufHL5TMA4fz5dq1LHnczz6Uk5gXKAOOkR9UuXi
Tty3hDG2BC86MKMRxJ51EbqjoxwER100SanMPfMlAoGAIHKcZr8saPpG0/WlPODA
dNoWS1YQbvLgBDyIPiGghz2QWiEr67znwfCUuzj6+3UKE+1WBCraTczfktuc96r/
ap4O42EeagSWMOFhgP2ad8GRfDj/pIx7CeczdUAdU8gsP5GIXGs4AN4yA0/F4uLG
Z1nIQOvJKk2fqoZ6MtwvtK0CgYEAgJ7FLeCE93Rf2dgCdTGXbYfZJss5lAK6Et5L
6budSyul5gEOZH2zClBRcdRR1AMm+uWVhIo1pDKrAeCh52r/zdfjLKAsHz9+Ad7i
GPGsVl4Vnch1S344lrJPe3BIKggc/Xgp3SbssprLcj+OL2dIk9JWo6ucxf1Bf2L0
2elhAQkCgYBXsyzVV/ZJqXNpWCg55L3UFoTLyKSqlVKM3WiG5BKn4AazVCHL+GQU
xwxSgR9fQ4Inu+rPrN3Imyk0lKPGF9SzCRRTiJFR72sNqlO6l0V8CWRAPTPJcgqV
1U8NHK8b3ZiIoGJ+msNzpdxrj62H3A6+Y+AsNY4SmUTXH9yjg+nukg==
-----END RSA PRIVATE KEY-----

9
test/data/x509.pub Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxTJuK2aGLn1dXJKDh4Mw
PLUkl3HI5pGnG5ac4l/0ihmq8cwC+FVPgZMS59aykisB+zC7vtvkJk/bv+BSOX7o
xdIsuL3dKQFpuXXVfrdb95wYn4M+/njEhXMlhVMH/OCiAg9JKhTKWL6GRWZAAhA7
lBRhgSNDTiTC51CbiK7xA6pN4+tQHxomJPXrZRkbB2klOfWwbv93Y3J1KFD+i0PM
QHLw7rhEumxC93+HHUVYVH7H1TPZH1bdUJJ02gQeylJssYCJydZPzNT/zuts/KJW
dRv5lxwG9u9tMNMghJbmAaMkMGi+o7PNEyP3qHFrYpYhs5pqLHMRNB78QM9IeNjL
FwIDAQAB
-----END PUBLIC KEY-----

View File

@ -8,6 +8,93 @@ var bitcore = bitcore || require('../bitcore');
var PayPro = bitcore.PayPro;
var Key = bitcore.Key;
var x509 = {
priv: ''
+ 'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBeFRKdUsyYUdM'
+ 'bjFkWEpLRGg0TXdQTFVrbDNISTVwR25HNWFjNGwvMGlobXE4Y3dDCitGVlBnWk1TNTlheWtpc0Ir'
+ 'ekM3dnR2a0prL2J2K0JTT1g3b3hkSXN1TDNkS1FGcHVYWFZmcmRiOTV3WW40TSsKL25qRWhYTWxo'
+ 'Vk1IL09DaUFnOUpLaFRLV0w2R1JXWkFBaEE3bEJSaGdTTkRUaVRDNTFDYmlLN3hBNnBONCt0UQpI'
+ 'eG9tSlBYclpSa2JCMmtsT2ZXd2J2OTNZM0oxS0ZEK2kwUE1RSEx3N3JoRXVteEM5MytISFVWWVZI'
+ 'N0gxVFBaCkgxYmRVSkowMmdRZXlsSnNzWUNKeWRaUHpOVC96dXRzL0tKV2RSdjVseHdHOXU5dE1O'
+ 'TWdoSmJtQWFNa01HaSsKbzdQTkV5UDNxSEZyWXBZaHM1cHFMSE1STkI3OFFNOUllTmpMRndJREFR'
+ 'QUJBb0lCQVFERVJyalBiQUdjbmwxaAorZGIrOTczNGZ0aElBUkpWSko1dTRFK1JKcThSRWhGTEVL'
+ 'UFlKNW0yUC94dVZBMXpYV2xnYXhaRUZ6d1VRaUpZCjdsOEpLVjlwSHhReVlaQ1M4dndYZzhpWGtz'
+ 'dndQaWRvQmN1YW4vd0RWQ1FCZXk2VkxjVXpSYUd1Ui9sTHNYK1YKN2Z0QjBvUnFsSXFrYmNQZE1N'
+ 'dnFUeG93UnVoUG11Q3JWVGpPNHBiTnFuU09OUExPaUovRkFYYjJwZnpGZnBCUgpHeCtFTW16d2Ur'
+ 'SEZuSkJHRGhIWjk5bm4vVEJmYUp6TlZDcURZLzNid3o1WDdIUU5ZN1QrSnlUVUZzZVE5NHhzCnpy'
+ 'a2lidGRmVGNUanB1K1VoWm80c1p6Q3IrZkhHWm9FOUdEUHF0ZDRnQ3ByazRFS0pzbXFCRVN4QlhT'
+ 'RGhZZ04KOXBVRDM4c1pBb0dCQU9yZkRqdDZaL0ZDamFuVThXek5GaWYrOVQxQTJ4b013RDVWU2xN'
+ 'dVJyWW1HbGZyMEM5TQpmMUVvZ2l2dVRrYnA3cmtnZFRhWVRTYndmTnFaQkt4Y3R5YzdCaGRwWnhE'
+ 'RVdKa2Z5cThxVngvem1Cek1JK1ZzCjJLYi9hcHZXcmJlb3NET0NyeUg1YzhKc1VUOXhUWDNYYnhF'
+ 'anlPSlFCU1lHRE1qUHlKNkU5czZMQW9HQkFOYnYKd2d0S2Nra0tLbDJhNXZzaGR2RENnNnFLL1Fn'
+ 'T20vNktUSlVKRVNqaHoydFIrZlBWUjcwVEg5UmhoVFJscERXQgpCd3oyU2NCc1RRNDIvTGsxRnky'
+ 'MFQvck12S3VmSEw1VE1BNGZ6NWRxMUxIbmN6ejZVazVnWEtBT09rUjlVdVhpClR0eTNoREcyQkM4'
+ 'Nk1LTVJ4SjUxRWJxam94d0VSMTAwU2FuTVBmTWxBb0dBSUhLY1pyOHNhUHBHMC9XbFBPREEKZE5v'
+ 'V1MxWVFidkxnQkR5SVBpR2doejJRV2lFcjY3em53ZkNVdXpqNiszVUtFKzFXQkNyYVRjemZrdHVj'
+ 'OTZyLwphcDRPNDJFZWFnU1dNT0ZoZ1AyYWQ4R1JmRGovcEl4N0NlY3pkVUFkVThnc1A1R0lYR3M0'
+ 'QU40eUEwL0Y0dUxHCloxbklRT3ZKS2syZnFvWjZNdHd2dEswQ2dZRUFnSjdGTGVDRTkzUmYyZGdD'
+ 'ZFRHWGJZZlpKc3M1bEFLNkV0NUwKNmJ1ZFN5dWw1Z0VPWkgyekNsQlJjZFJSMUFNbSt1V1ZoSW8x'
+ 'cERLckFlQ2g1MnIvemRmakxLQXNIejkrQWQ3aQpHUEdzVmw0Vm5jaDFTMzQ0bHJKUGUzQklLZ2dj'
+ 'L1hncDNTYnNzcHJMY2orT0wyZElrOUpXbzZ1Y3hmMUJmMkwwCjJlbGhBUWtDZ1lCWHN5elZWL1pK'
+ 'cVhOcFdDZzU1TDNVRm9UTHlLU3FsVktNM1dpRzVCS240QWF6VkNITCtHUVUKeHd4U2dSOWZRNElu'
+ 'dStyUHJOM0lteWswbEtQR0Y5U3pDUlJUaUpGUjcyc05xbE82bDBWOENXUkFQVFBKY2dxVgoxVThO'
+ 'SEs4YjNaaUlvR0orbXNOenBkeHJqNjJIM0E2K1krQXNOWTRTbVVUWEg5eWpnK251a2c9PQotLS0t'
+ 'LUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=',
pub: ''
+ 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FR'
+ 'OEFNSUlCQ2dLQ0FRRUF4VEp1SzJhR0xuMWRYSktEaDRNdwpQTFVrbDNISTVwR25HNWFjNGwvMGlo'
+ 'bXE4Y3dDK0ZWUGdaTVM1OWF5a2lzQit6Qzd2dHZrSmsvYnYrQlNPWDdvCnhkSXN1TDNkS1FGcHVY'
+ 'WFZmcmRiOTV3WW40TSsvbmpFaFhNbGhWTUgvT0NpQWc5SktoVEtXTDZHUldaQUFoQTcKbEJSaGdT'
+ 'TkRUaVRDNTFDYmlLN3hBNnBONCt0UUh4b21KUFhyWlJrYkIya2xPZld3YnY5M1kzSjFLRkQraTBQ'
+ 'TQpRSEx3N3JoRXVteEM5MytISFVWWVZIN0gxVFBaSDFiZFVKSjAyZ1FleWxKc3NZQ0p5ZFpQek5U'
+ 'L3p1dHMvS0pXCmRSdjVseHdHOXU5dE1OTWdoSmJtQWFNa01HaStvN1BORXlQM3FIRnJZcFloczVw'
+ 'cUxITVJOQjc4UU05SWVOakwKRndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==',
der: ''
+ 'MIIDBjCCAe4CCQDI2qWdA3/VpDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTETMBEGA1UE'
+ 'CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE0MDcx'
+ 'NjAxMzM1MVoXDTE1MDcxNjAxMzM1MVowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh'
+ 'dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD'
+ 'ggEPADCCAQoCggEBAMUybitmhi59XVySg4eDMDy1JJdxyOaRpxuWnOJf9IoZqvHMAvhVT4GTEufW'
+ 'spIrAfswu77b5CZP27/gUjl+6MXSLLi93SkBabl11X63W/ecGJ+DPv54xIVzJYVTB/zgogIPSSoU'
+ 'yli+hkVmQAIQO5QUYYEjQ04kwudQm4iu8QOqTePrUB8aJiT162UZGwdpJTn1sG7/d2NydShQ/otD'
+ 'zEBy8O64RLpsQvd/hx1FWFR+x9Uz2R9W3VCSdNoEHspSbLGAicnWT8zU/87rbPyiVnUb+ZccBvbv'
+ 'bTDTIISW5gGjJDBovqOzzRMj96hxa2KWIbOaaixzETQe/EDPSHjYyxcCAwEAATANBgkqhkiG9w0B'
+ 'AQUFAAOCAQEAL6AMMfC3TlRcmsIgHxjVD4XYtISlldnrn2X9zvFbJKCpNy8XQQosQxrhyfzPHQKj'
+ 'lS2L/KCGMnjx9QkYD2Hlp1MJ1uVv9888th/gcZOv3Or3hQyi5K1Sh5xCG+69lUOqUEGu9B4irsqo'
+ 'FomQVbQolSy+t4apdJi7kuEDwFDk4gZiVEfsuX+naN5a6pCnWnhX1Vf4fKwfkLobKKXm2zQVsjxl'
+ 'wBAqOEmJGDLoRMXH56qJnEZ/dqsczaJOHQSi9mFEHL0r5rsEDTT5AVxdnBfNnyGaCH7/zANEko+F'
+ 'GBj1JdJaJgFTXdbxDoyoPTPD+LJqSK5XYToo46y/T0u9CLveNA==',
pem: ''
+ 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQWU0Q0NRREkycVdkQTMvVnBEQU5C'
+ 'Z2txaGtpRzl3MEJBUVVGQURCRk1Rc3dDUVlEVlFRR0V3SkIKVlRFVE1CRUdBMVVFQ0F3S1UyOXRa'
+ 'UzFUZEdGMFpURWhNQjhHQTFVRUNnd1lTVzUwWlhKdVpYUWdWMmxrWjJsMApjeUJRZEhrZ1RIUmtN'
+ 'QjRYRFRFME1EY3hOakF4TXpNMU1Wb1hEVEUxTURjeE5qQXhNek0xTVZvd1JURUxNQWtHCkExVUVC'
+ 'aE1DUVZVeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJt'
+ 'VjAKSUZkcFpHZHBkSE1nVUhSNUlFeDBaRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFE'
+ 'Q0NBUW9DZ2dFQgpBTVV5Yml0bWhpNTlYVnlTZzRlRE1EeTFKSmR4eU9hUnB4dVduT0pmOUlvWnF2'
+ 'SE1BdmhWVDRHVEV1ZldzcElyCkFmc3d1NzdiNUNaUDI3L2dVamwrNk1YU0xMaTkzU2tCYWJsMTFY'
+ 'NjNXL2VjR0orRFB2NTR4SVZ6SllWVEIvemcKb2dJUFNTb1V5bGkraGtWbVFBSVFPNVFVWVlFalEw'
+ 'NGt3dWRRbTRpdThRT3FUZVByVUI4YUppVDE2MlVaR3dkcApKVG4xc0c3L2QyTnlkU2hRL290RHpF'
+ 'Qnk4TzY0Ukxwc1F2ZC9oeDFGV0ZSK3g5VXoyUjlXM1ZDU2ROb0VIc3BTCmJMR0FpY25XVDh6VS84'
+ 'N3JiUHlpVm5VYitaY2NCdmJ2YlREVElJU1c1Z0dqSkRCb3ZxT3p6Uk1qOTZoeGEyS1cKSWJPYWFp'
+ 'eHpFVFFlL0VEUFNIall5eGNDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFMNkFNTWZD'
+ 'MwpUbFJjbXNJZ0h4alZENFhZdElTbGxkbnJuMlg5enZGYkpLQ3BOeThYUVFvc1F4cmh5ZnpQSFFL'
+ 'amxTMkwvS0NHCk1uang5UWtZRDJIbHAxTUoxdVZ2OTg4OHRoL2djWk92M09yM2hReWk1SzFTaDV4'
+ 'Q0crNjlsVU9xVUVHdTlCNGkKcnNxb0ZvbVFWYlFvbFN5K3Q0YXBkSmk3a3VFRHdGRGs0Z1ppVkVm'
+ 'c3VYK25hTjVhNnBDblduaFgxVmY0Zkt3ZgprTG9iS0tYbTJ6UVZzanhsd0JBcU9FbUpHRExvUk1Y'
+ 'SDU2cUpuRVovZHFzY3phSk9IUVNpOW1GRUhMMHI1cnNFCkRUVDVBVnhkbkJmTm55R2FDSDcvekFO'
+ 'RWtvK0ZHQmoxSmRKYUpnRlRYZGJ4RG95b1BUUEQrTEpxU0s1WFlUb28KNDZ5L1QwdTlDTHZlTkE9'
+ 'PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==',
sig1: new Buffer(0),
sig2: new Buffer(0),
sig3: new Buffer(0)
};
x509.priv = new Buffer(x509.priv, 'base64');
x509.pub = new Buffer(x509.pub, 'base64');
x509.der = new Buffer(x509.der, 'base64');
x509.pem = new Buffer(x509.pem, 'base64');
describe('PayPro', function() {
it('should be able to create class', function() {
@ -243,6 +330,7 @@ describe('PayPro', function() {
describe('#sign', function() {
it('should sign a payment request', function() {
// SIN
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
@ -256,6 +344,19 @@ describe('PayPro', function() {
paypro.sign(key);
var sig = paypro.get('signature');
sig.length.should.be.greaterThan(0);
// X509
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha256');
paypro.set('pki_data', x509.der);
paypro.sign(x509.priv);
x509.sig1 = paypro.get('signature');
x509.sig1.length.should.be.greaterThan(0);
});
});
@ -263,6 +364,7 @@ describe('PayPro', function() {
describe('#verify', function() {
it('should verify a signed payment request', function() {
// SIN
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
@ -276,6 +378,19 @@ describe('PayPro', function() {
paypro.sign(key);
var verify = paypro.verify();
verify.should.equal(true);
// X509
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha256');
paypro.set('signature', x509.sig1); // sig buffer
paypro.set('pki_data', x509.der); // contains one or more x509 certs
var verify = paypro.verify();
verify.should.equal(true);
});
});
@ -319,4 +434,115 @@ describe('PayPro', function() {
});
describe('#x509+sha256Sign', function() {
it('should sign assuming pki_type is x509+sha256', function() {
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha256');
paypro.set('pki_data', x509.der);
var sig = paypro.x509Sign(x509.priv);
paypro.set('signature', sig);
x509.sig2 = paypro.get('signature');
x509.sig2.length.should.be.greaterThan(0);
});
});
describe('#x509+sha256Verify', function() {
it('should verify assuming pki_type is x509+sha256', function() {
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha256');
paypro.set('signature', x509.sig2); // sig buffer
paypro.set('pki_data', x509.der); // contains one or more x509 certs
var verify = paypro.x509Verify();
verify.should.equal(true);
});
});
describe('#x509+sha1Sign', function() {
it('should sign assuming pki_type is x509+sha1', function() {
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha1');
paypro.set('pki_data', x509.der);
var sig = paypro.x509Sign(x509.priv);
paypro.set('signature', sig);
x509.sig3 = paypro.get('signature');
x509.sig3.length.should.be.greaterThan(0);
});
});
describe('#x509+sha1Verify', function() {
it('should verify assuming pki_type is x509+sha1', function() {
var pd = new PayPro.PaymentDetails();
pd.set('time', 0);
var pdbuf = pd.toBuffer();
var paypro = new PayPro();
paypro.makePaymentRequest();
paypro.set('serialized_payment_details', pdbuf);
paypro.set('pki_type', 'x509+sha1');
paypro.set('signature', x509.sig3); // sig buffer
paypro.set('pki_data', x509.der); // contains one or more x509 certs
var verify = paypro.x509Verify();
verify.should.equal(true);
});
});
describe('#PEMtoDER', function() {
it('should convert a PEM cert to DER', function() {
var paypro = new PayPro();
var der1 = paypro._PEMtoDERParam(x509.pem.toString(), 'CERTIFICATE').map(function(der) {
return der.toString('hex');
});
der1 = der1[0];
var der2 = x509.der.toString('hex');
der1.should.equal(der2);
});
});
describe('#DERtoPEM', function() {
it('convert a DER cert to PEM', function() {
var paypro = new PayPro();
var pem1 = paypro._DERtoPEM(x509.der, 'CERTIFICATE');
//var KJUR = require('jsrsasign');
//var pem2 = KJUR.asn1.ASN1Util.getPEMStringFromHex(x509.der.toString('hex'), 'CERTIFICATE');
var pem2 = x509.pem.toString();
pem1 = pem1.replace(/\s+/g, '');
pem2 = pem2.replace(/\s+/g, '');
pem1.should.equal(pem2);
});
});
});