From bf5f770a673d22442d01c9b2b2c1c10f16220c73 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 16 Dec 2014 12:19:23 -0500 Subject: [PATCH] Payment Protocol: Added tests and misc formatting --- lib/paymentprotocol/browser.js | 70 +++++++------------ lib/paymentprotocol/common.js | 110 +++++++++++++++++------------- lib/paymentprotocol/node.js | 56 +++++----------- lib/paymentprotocol/rootcerts.js | 22 ++++-- test/paymentprotocol/index.js | 111 +++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 140 deletions(-) diff --git a/lib/paymentprotocol/browser.js b/lib/paymentprotocol/browser.js index cc2d0838b..18b1d2d4f 100644 --- a/lib/paymentprotocol/browser.js +++ b/lib/paymentprotocol/browser.js @@ -1,7 +1,6 @@ -"use strict"; +'use strict'; var KJUR = require('jsrsasign'); -var assert = require('assert'); var PaymentProtocol = require('./common'); var RootCerts = require('./rootcerts'); var rfc3280 = require('asn1.js/rfc/3280'); @@ -15,15 +14,15 @@ PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { var pki_data = this.get('pki_data'); // contains one or more x509 certs pki_data = PaymentProtocol.X509Certificates.decode(pki_data); pki_data = pki_data.certificate; - var type = pki_type !== 'none' - ? pki_type.split('+')[1].toUpperCase() - : pki_type; + var type = pki_type !== 'none' ? pki_type.split('+')[1].toUpperCase() : pki_type; var buf = this.serializeForSig(); var rsa = new KJUR.RSAKey(); rsa.readPrivateKeyFromPEMString(key.toString()); key = rsa; + var sig; + if (type !== 'none') { var jsrsaSig = new KJUR.crypto.Signature({ alg: type + 'withRSA', @@ -34,9 +33,9 @@ PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { jsrsaSig.updateHex(buf.toString('hex')); - var sig = new Buffer(jsrsaSig.sign(), 'hex'); + sig = new Buffer(jsrsaSig.sign(), 'hex'); } else { - var sig = ''; + sig = ''; } if (returnTrust) { @@ -46,9 +45,7 @@ PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { var caName = RootCerts.getTrusted(pem); var selfSigned = 0; if (!caName) { - selfSigned = pki_data.length > 1 - ? -1 - : 1; + selfSigned = pki_data.length > 1 ? -1 : 1; } return { selfSigned: selfSigned, @@ -69,9 +66,11 @@ PaymentProtocol.prototype.x509Verify = function(returnTrust) { pki_data = PaymentProtocol.X509Certificates.decode(pki_data); pki_data = pki_data.certificate; var buf = this.serializeForSig(); - var type = pki_type !== 'none' - ? pki_type.split('+')[1].toUpperCase() - : pki_type; + var type = pki_type !== 'none' ? pki_type.split('+')[1].toUpperCase() : pki_type; + + var der; + var pem; + var verified; if (type !== 'none') { var jsrsaSig = new KJUR.crypto.Signature({ @@ -79,24 +78,21 @@ PaymentProtocol.prototype.x509Verify = function(returnTrust) { prov: 'cryptojs/jsrsa' }); var signedCert = pki_data[0]; - var der = signedCert.toString('hex'); - // var pem = self._DERtoPEM(der, 'CERTIFICATE'); - var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); + der = signedCert.toString('hex'); + pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); jsrsaSig.initVerifyByCertificatePEM(pem); jsrsaSig.updateHex(buf.toString('hex')); - var verified = jsrsaSig.verify(sig.toString('hex')); + verified = jsrsaSig.verify(sig.toString('hex')); } else { - var verified = true; + verified = true; } var chain = pki_data; - // // Get the CA cert's name - // + var issuer = chain[chain.length - 1]; der = issuer.toString('hex'); - // pem = this._DERtoPEM(der, 'CERTIFICATE'); pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); var caName = RootCerts.getTrusted(pem); @@ -152,7 +148,6 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { } return chain.every(function(cert, i) { var der = cert.toString('hex'); - // var pem = self._DERtoPEM(der, 'CERTIFICATE'); var pem = KJUR.asn1.ASN1Util.getPEMStringFromHex(der, 'CERTIFICATE'); var name = RootCerts.getTrusted(pem); @@ -166,52 +161,37 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { return true; } var nder = ncert.toString('hex'); - // var npem = self._DERtoPEM(nder, 'CERTIFICATE'); var npem = KJUR.asn1.ASN1Util.getPEMStringFromHex(nder, 'CERTIFICATE'); - // // Get Next Certificate: - // var ndata = new Buffer(nder, 'hex'); var nc = rfc3280.Certificate.decode(ndata, 'der'); - var npubKeyAlg = PaymentProtocol.getAlgorithm( - nc.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm); - // + var npubKey; // Get Public Key from next certificate (via KJUR because it's a mess): - // if (sigHashAlg !== 'none') { var js = new KJUR.crypto.Signature({ alg: sigHashAlg + 'withRSA', prov: 'cryptojs/jsrsa' }); js.initVerifyByCertificatePEM(npem); - var npubKey = js.pubKey; + npubKey = js.pubKey; } - // XXX Somehow change the pubKey format to npubKeyAlg. - // // Get Signature Value from current certificate: - // var data = new Buffer(der, 'hex'); var c = rfc3280.Certificate.decode(data, 'der'); - var sigAlg = PaymentProtocol.getAlgorithm(c.signatureAlgorithm.algorithm, 1); var sig = c.signature.data; - // // Check Validity of Certificates - // var validityVerified = PaymentProtocol.validateCertTime(c, nc); - // // Check the Issuer matches the Subject of the next certificate: - // var issuerVerified = PaymentProtocol.validateCertIssuer(c, nc); - // - // Verify current Certificate signature - // + var sigVerified; + // Verify current Certificate signature if (sigHashAlg !== 'none') { var jsrsaSig = new KJUR.crypto.Signature({ alg: sigHashAlg + 'withRSA', @@ -225,14 +205,12 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { jsrsaSig.updateHex(tbs.toString('hex')); - var sigVerified = jsrsaSig.verify(sig.toString('hex')); + sigVerified = jsrsaSig.verify(sig.toString('hex')); } else { - var sigVerified = true; + sigVerified = true; } - return validityVerified - && issuerVerified - && sigVerified; + return validityVerified && issuerVerified && sigVerified; }); }; diff --git a/lib/paymentprotocol/common.js b/lib/paymentprotocol/common.js index d62966549..8a3180266 100644 --- a/lib/paymentprotocol/common.js +++ b/lib/paymentprotocol/common.js @@ -18,9 +18,9 @@ function PaymentProtocol() { PaymentProtocol.PAYMENT_REQUEST_MAX_SIZE = 50000; PaymentProtocol.PAYMENT_MAX_SIZE = 50000; PaymentProtocol.PAYMENT_ACK_MAX_SIZE = 60000; -PaymentProtocol.PAYMENT_REQUEST_CONTENT_TYPE = "application/bitcoin-paymentrequest"; -PaymentProtocol.PAYMENT_CONTENT_TYPE = "application/bitcoin-payment"; -PaymentProtocol.PAYMENT_ACK_CONTENT_TYPE = "application/bitcoin-paymentack"; +PaymentProtocol.PAYMENT_REQUEST_CONTENT_TYPE = 'application/bitcoin-paymentrequest'; +PaymentProtocol.PAYMENT_CONTENT_TYPE = 'application/bitcoin-payment'; +PaymentProtocol.PAYMENT_ACK_CONTENT_TYPE = 'application/bitcoin-paymentack'; // https://www.google.com/search?q=signatureAlgorithm+1.2.840.113549.1.1.1 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa379057(v=vs.85).aspx @@ -43,7 +43,7 @@ PaymentProtocol.getAlgorithm = function(value, index) { value = value.join('.'); } value = PaymentProtocol.X509_ALGORITHM[value]; - if (index != null) { + if (typeof(index) !== 'undefined') { value = value.split('_'); if (index === true) { return { @@ -66,7 +66,7 @@ PaymentProtocol.getTBSCertificate = function(data) { // [ 48, 130, 5, 32, 48, 130, 4, 8, 160, 3 ] var start = 0; var starts = 0; - for (var start = 0; start < data.length; start++) { + for (start = 0; start < data.length; start++) { if (starts === 1 && data[start] === 48) { break; } @@ -84,7 +84,7 @@ PaymentProtocol.getTBSCertificate = function(data) { // SEQ of the TBSCertificate. var end = 0; var ends = 0; - for (var end = data.length - 1; end > 0; end--) { + for (end = data.length - 1; end > 0; end--) { if (ends === 2 && data[end] === 48) { break; } @@ -126,8 +126,7 @@ PaymentProtocol.validateCertIssuer = function(c, nc) { var issuerObjectValue = issuerObject.value.toString('hex'); var subjectObjectValue = subjectObject.value.toString('hex'); - return issuerObjectType === subjectObjectType - && issuerObjectValue === subjectObjectValue; + return issuerObjectType === subjectObjectType && issuerObjectValue === subjectObjectValue; }); }); return issuerVerified; @@ -137,12 +136,12 @@ PaymentProtocol.RootCerts = RootCerts; PaymentProtocol.proto = {}; -PaymentProtocol.proto.Output = "message Output {\ +PaymentProtocol.proto.Output = 'message Output {\ optional uint64 amount = 1 [default = 0];\ optional bytes script = 2;\ -}\n"; +}\n'; -PaymentProtocol.proto.PaymentDetails = "message PaymentDetails {\ +PaymentProtocol.proto.PaymentDetails = 'message PaymentDetails {\ optional string network = 1 [default = \"main\"];\ repeated Output outputs = 2;\ required uint64 time = 3;\ @@ -150,33 +149,33 @@ PaymentProtocol.proto.PaymentDetails = "message PaymentDetails {\ optional string memo = 5;\ optional string payment_url = 6;\ optional bytes merchant_data = 7;\ -}\n"; +}\n'; -PaymentProtocol.proto.PaymentRequest = "message PaymentRequest {\ +PaymentProtocol.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"; +}\n'; -PaymentProtocol.proto.Payment = "message Payment {\ +PaymentProtocol.proto.Payment = 'message Payment {\ optional bytes merchant_data = 1;\ repeated bytes transactions = 2;\ repeated Output refund_to = 3;\ optional string memo = 4;\ -}\n"; +}\n'; -PaymentProtocol.proto.PaymentACK = "message PaymentACK {\ +PaymentProtocol.proto.PaymentACK = 'message PaymentACK {\ required Payment payment = 1;\ optional string memo = 2;\ -}\n"; +}\n'; -PaymentProtocol.proto.X509Certificates = "message X509Certificates {\ +PaymentProtocol.proto.X509Certificates = 'message X509Certificates {\ repeated bytes certificate = 1;\ -}\n"; +}\n'; -PaymentProtocol.proto.all = ""; +PaymentProtocol.proto.all = ''; PaymentProtocol.proto.all = PaymentProtocol.proto.all + PaymentProtocol.proto.Output; PaymentProtocol.proto.all = PaymentProtocol.proto.all + PaymentProtocol.proto.PaymentDetails; PaymentProtocol.proto.all = PaymentProtocol.proto.all + PaymentProtocol.proto.PaymentRequest; @@ -186,12 +185,12 @@ PaymentProtocol.proto.all = PaymentProtocol.proto.all + PaymentProtocol.proto.X5 PaymentProtocol.builder = protobufjs.loadProto(PaymentProtocol.proto.all); -PaymentProtocol.Output = PaymentProtocol.builder.build("Output"); -PaymentProtocol.PaymentDetails = PaymentProtocol.builder.build("PaymentDetails"); -PaymentProtocol.PaymentRequest = PaymentProtocol.builder.build("PaymentRequest"); -PaymentProtocol.Payment = PaymentProtocol.builder.build("Payment"); -PaymentProtocol.PaymentACK = PaymentProtocol.builder.build("PaymentACK"); -PaymentProtocol.X509Certificates = PaymentProtocol.builder.build("X509Certificates"); +PaymentProtocol.Output = PaymentProtocol.builder.build('Output'); +PaymentProtocol.PaymentDetails = PaymentProtocol.builder.build('PaymentDetails'); +PaymentProtocol.PaymentRequest = PaymentProtocol.builder.build('PaymentRequest'); +PaymentProtocol.Payment = PaymentProtocol.builder.build('Payment'); +PaymentProtocol.PaymentACK = PaymentProtocol.builder.build('PaymentACK'); +PaymentProtocol.X509Certificates = PaymentProtocol.builder.build('X509Certificates'); PaymentProtocol.prototype.makeOutput = function(obj) { this.messageType = 'Output'; @@ -237,24 +236,30 @@ PaymentProtocol.prototype.makeX509Certificates = function(obj) { PaymentProtocol.prototype.isValidSize = function() { var s = this.serialize(); - if (this.messageType == 'PaymentRequest') + if (this.messageType === 'PaymentRequest') { return s.length < PaymentProtocol.PAYMENT_REQUEST_MAX_SIZE; - if (this.messageType == 'Payment') + } + if (this.messageType === 'Payment') { return s.length < PaymentProtocol.PAYMENT_MAX_SIZE; - if (this.messageType == 'PaymentACK') + } + if (this.messageType === 'PaymentACK') { return s.length < PaymentProtocol.PAYMENT_ACK_MAX_SIZE; + } return true; }; PaymentProtocol.prototype.getContentType = function() { - if (this.messageType == 'PaymentRequest') + if (this.messageType === 'PaymentRequest') { return PaymentProtocol.PAYMENT_REQUEST_CONTENT_TYPE; + } - if (this.messageType == 'Payment') + if (this.messageType === 'Payment') { return PaymentProtocol.PAYMENT_CONTENT_TYPE; + } - if (this.messageType == 'PaymentACK') + if (this.messageType === 'PaymentACK') { return PaymentProtocol.PAYMENT_ACK_CONTENT_TYPE; + } throw new Error('No known content type for this message type'); }; @@ -267,13 +272,15 @@ PaymentProtocol.prototype.set = function(key, val) { PaymentProtocol.prototype.get = function(key) { var v = this.message.get(key); - if (v === null) + 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') + if (typeof v.low !== 'undefined' && typeof v.high !== 'undefined') { return v.toInt(); + } if (typeof v.toBuffer !== 'undefined') { var maybebuf = v.toBuffer(); @@ -294,8 +301,9 @@ PaymentProtocol.prototype.setObj = function(obj) { }; PaymentProtocol.prototype.serializeForSig = function() { - if (this.messageType !== 'PaymentRequest') + if (this.messageType !== 'PaymentRequest') { throw new Error('serializeForSig is only for PaymentRequest'); + } var save = this.message.get('signature'); this.message.set('signature', new Buffer([])); @@ -314,22 +322,25 @@ PaymentProtocol.prototype.serialize = function() { PaymentProtocol.prototype.deserialize = function(buf, messageType) { this.messageType = messageType || this.messageType; - if (!this.messageType) + if (!this.messageType) { throw new Error('Must specify messageType'); + } this.message = PaymentProtocol[this.messageType].decode(buf); return this; }; PaymentProtocol.prototype.sign = function(key, returnTrust) { - if (this.messageType !== 'PaymentRequest') + if (this.messageType !== 'PaymentRequest') { throw new Error('Signing can only be performed on a PaymentRequest'); + } var pki_type = this.get('pki_type'); + var sig; if (pki_type === 'SIN') { - var sig = this.sinSign(key); + sig = this.sinSign(key); } else if (pki_type === 'x509+sha1' || pki_type === 'x509+sha256') { - var sig = this.x509Sign(key, returnTrust); + sig = this.x509Sign(key, returnTrust); } else if (pki_type === 'none') { return this; } else { @@ -342,8 +353,9 @@ PaymentProtocol.prototype.sign = function(key, returnTrust) { }; PaymentProtocol.prototype.verify = function(returnTrust) { - if (this.messageType !== 'PaymentRequest') + if (this.messageType !== 'PaymentRequest') { throw new Error('Verifying can only be performed on a PaymentRequest'); + } var pki_type = this.get('pki_type'); @@ -366,7 +378,7 @@ function magicHash(str) { var buf = Buffer.concat([prefix1, magicBytes, prefix2, message]); var hash = sha256sha256(buf); return hash; -}; +} //default signing function for prototype.sign PaymentProtocol.prototype.sinSign = function(privateKey) { @@ -413,7 +425,9 @@ PaymentProtocol.prototype._PEMtoDERParam = function(pem, param) { var type = /-----BEGIN ([^-]+)-----/.exec(part)[1]; part = part.replace(/-----BEGIN ([^-]+)-----/g, ''); part = part.replace(/\s+/g, ''); - if (!param || type !== param) return; + if (!param || type !== param) { + return; + } return new Buffer(part, 'base64'); }).filter(Boolean); }; @@ -423,14 +437,14 @@ PaymentProtocol.prototype._DERtoPEM = function(der, type) { if (typeof der === 'string') { der = new Buffer(der, 'hex'); } - var type = type || 'PRIVACY-ENHANCED MESSAGE'; + type = type || 'PRIVACY-ENHANCED MESSAGE'; 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'; + return '' + + '-----BEGIN ' + type + '-----\r\n' + + der + + '\r\n-----END ' + type + '-----\r\n'; }; // Expose RootCerts diff --git a/lib/paymentprotocol/node.js b/lib/paymentprotocol/node.js index c50be0dd2..cf45391c1 100644 --- a/lib/paymentprotocol/node.js +++ b/lib/paymentprotocol/node.js @@ -6,24 +6,20 @@ var PaymentProtocol = require('./common'); var rfc3280 = require('asn1.js/rfc/3280'); PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { - var self = this; var pki_type = this.get('pki_type'); var pki_data = this.get('pki_data'); pki_data = PaymentProtocol.X509Certificates.decode(pki_data); pki_data = pki_data.certificate; - var details = this.get('serialized_payment_details'); - var type = pki_type !== 'none' - ? pki_type.split('+')[1].toUpperCase() - : pki_type; + var type = pki_type !== 'none' ? pki_type.split('+')[1].toUpperCase() : pki_type; + var sig; if (type !== 'none') { var signature = crypto.createSign('RSA-' + type); var buf = this.serializeForSig(); signature.update(buf); - var sig = signature.sign(key); + sig = signature.sign(key); } else { - var buf = this.serializeForSig(); - var sig = ''; + sig = ''; } if (returnTrust) { @@ -33,9 +29,7 @@ PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { var caName = RootCerts.getTrusted(pem); var selfSigned = 0; if (!caName) { - selfSigned = pki_data.length > 1 - ? -1 - : 1; + selfSigned = pki_data.length > 1 ? -1 : 1; } return { selfSigned: selfSigned, @@ -50,34 +44,32 @@ PaymentProtocol.prototype.x509Sign = function(key, returnTrust) { }; PaymentProtocol.prototype.x509Verify = function(returnTrust) { - var self = this; var pki_type = this.get('pki_type'); var sig = this.get('signature'); var pki_data = this.get('pki_data'); pki_data = PaymentProtocol.X509Certificates.decode(pki_data); pki_data = pki_data.certificate; - var details = this.get('serialized_payment_details'); var buf = this.serializeForSig(); - var type = pki_type !== 'none' - ? pki_type.split('+')[1].toUpperCase() - : pki_type; + var type = pki_type !== 'none' ? pki_type.split('+')[1].toUpperCase() : pki_type; + + var verified; + var der; + var pem; if (type !== 'none') { var verifier = crypto.createVerify('RSA-' + type); verifier.update(buf); var signedCert = pki_data[0]; - var der = signedCert.toString('hex'); - var pem = PaymentProtocol.DERtoPEM(der, 'CERTIFICATE'); - var verified = verifier.verify(pem, sig); + der = signedCert.toString('hex'); + pem = PaymentProtocol.DERtoPEM(der, 'CERTIFICATE'); + verified = verifier.verify(pem, sig); } else { - var verified = true; + verified = true; } var chain = pki_data; - // // Get the CA cert's name - // var issuer = chain[chain.length - 1]; der = issuer.toString('hex'); pem = PaymentProtocol.DERtoPEM(der, 'CERTIFICATE'); @@ -139,6 +131,7 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { var name = RootCerts.getTrusted(pem); var ncert = chain[i + 1]; + // The root cert, check if it's trusted: if (!ncert || name) { if (!name) { @@ -148,11 +141,8 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { return true; } var nder = ncert.toString('hex'); - var npem = PaymentProtocol.DERtoPEM(nder, 'CERTIFICATE'); - // // Get Public Key from next certificate: - // var ndata = new Buffer(nder, 'hex'); var nc = rfc3280.Certificate.decode(ndata, 'der'); var npubKeyAlg = PaymentProtocol.getAlgorithm( @@ -160,28 +150,18 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { var npubKey = nc.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; npubKey = PaymentProtocol.DERtoPEM(npubKey, npubKeyAlg + ' PUBLIC KEY'); - // // Get Signature Value from current certificate: - // var data = new Buffer(der, 'hex'); var c = rfc3280.Certificate.decode(data, 'der'); - var sigAlg = PaymentProtocol.getAlgorithm(c.signatureAlgorithm.algorithm, 1); var sig = c.signature.data; - // // Check Validity of Certificates - // var validityVerified = PaymentProtocol.validateCertTime(c, nc); - // // Check the Issuer matches the Subject of the next certificate: - // var issuerVerified = PaymentProtocol.validateCertIssuer(c, nc); - // - // Verify current Certificate signature - // - + // Verify current Certificate signature: // Get the raw DER TBSCertificate // from the DER Certificate: var tbs = PaymentProtocol.getTBSCertificate(data); @@ -190,9 +170,7 @@ PaymentProtocol.verifyCertChain = function(chain, sigHashAlg) { verifier.update(tbs); var sigVerified = verifier.verify(npubKey, sig); - return validityVerified - && issuerVerified - && sigVerified; + return validityVerified && issuerVerified && sigVerified; }); }; diff --git a/lib/paymentprotocol/rootcerts.js b/lib/paymentprotocol/rootcerts.js index d9f760b53..855a1880c 100644 --- a/lib/paymentprotocol/rootcerts.js +++ b/lib/paymentprotocol/rootcerts.js @@ -1,3 +1,5 @@ +'use strict'; + var RootCerts = exports; var certs = require('./rootcerts.json'); @@ -14,13 +16,17 @@ var trusted = Object.keys(certs).reduce(function(trusted, key) { RootCerts.getTrusted = function(pem) { pem = RootCerts.parsePEM(pem)[0].pem; - if (!Object.prototype.hasOwnProperty.call(trusted, pem)) return; + if (!Object.prototype.hasOwnProperty.call(trusted, pem)) { + return; + } return trusted[pem]; }; RootCerts.getCert = function(name) { name = name.replace(/^s+|s+$/g, ''); - if (!Object.prototype.hasOwnProperty.call(certs, name)) return; + if (!Object.prototype.hasOwnProperty.call(certs, name)) { + return; + } return certs[name]; }; @@ -29,9 +35,13 @@ RootCerts.parsePEM = function(pem) { var concatted = pem.trim().split(/-----BEGIN [^\-\r\n]+-----/); if (concatted.length > 2) { return concatted.reduce(function(out, pem) { - if (!pem) return out; + if (!pem) { + return out; + } pem = RootCerts.parsePEM(pem)[0].pem; - if (pem) out.push(pem); + if (pem) { + out.push(pem); + } return out; }, []); } @@ -51,9 +61,7 @@ RootCerts.parsePEM = function(pem) { pem = parts.slice(1).join(''); } pem = pem.replace(/\s+/g, ''); - var der = pem - ? new Buffer(pem, 'base64') - : null; + var der = pem ? new Buffer(pem, 'base64') : null; return [{ type: type, headers: headers, diff --git a/test/paymentprotocol/index.js b/test/paymentprotocol/index.js index f99996c12..17fd076a4 100644 --- a/test/paymentprotocol/index.js +++ b/test/paymentprotocol/index.js @@ -297,6 +297,12 @@ describe('PaymentProtocol', function() { output.get('amount').toInt().should.equal(20); }); + it('should be able to make output using "makeOutput"', function() { + var output = new PaymentProtocol().makeOutput(); + output.message.set('amount', 20); + output.message.get('amount').toInt().should.equal(20); + }); + }); describe('#PaymentDetails', function() { @@ -380,6 +386,24 @@ describe('PaymentProtocol', function() { pahex.length.should.be.greaterThan(0); }); + it('makePaymentACK', function() { + var payment = new PaymentProtocol.Payment(); + var ack = new PaymentProtocol().makePaymentACK(); + ack.set('payment', payment); + ack.set('memo', 'this is a memo'); + ack.get('memo').should.equal('this is a memo'); + var valid = ack.isValidSize(); + valid.should.equal(true); + var contentType = ack.getContentType(); + contentType.should.equal(PaymentProtocol.PAYMENT_ACK_CONTENT_TYPE); + var serialized = ack.serialize(); + serialized.length.should.be.greaterThan(0); + var ack2 = new PaymentProtocol().makePaymentACK(); + ack2.deserialize(serialized, 'PaymentACK'); + var serialized2 = ack2.serialize(); + serialized.should.deep.equal(serialized2); + }); + }); describe('#X509Certificates', function() { @@ -413,6 +437,14 @@ describe('PaymentProtocol', function() { describe('#getContentType', function() { + it('should error without a known message type', function() { + var paypro = new PaymentProtocol(); + paypro.messageType = 'unknown'; + expect(function(){ + paypro.getContentType(); + }).to.throw(Error); + }); + it('should get a content type for payment', function() { var paypro = new PaymentProtocol(); paypro.makePayment(); @@ -463,6 +495,14 @@ describe('PaymentProtocol', function() { describe('#serializeForSig', function() { + it('should error when not a payment request', function() { + var paypro = new PaymentProtocol(); + paypro.messageType = 'unknown'; + expect(function(){ + paypro.serializeForSig(); + }).to.throw(Error); + }); + it('should serialize a PaymentRequest and not fail', function() { var pd = new PaymentProtocol.PaymentDetails(); pd.set('time', 0); @@ -472,6 +512,10 @@ describe('PaymentProtocol', function() { paypro.makePaymentRequest(); paypro.set('serialized_payment_details', pdbuf); var buf = paypro.serializeForSig(); + var valid = paypro.isValidSize(); + var contentType = paypro.getContentType(); + contentType.should.equal(PaymentProtocol.PAYMENT_REQUEST_CONTENT_TYPE); + valid.should.equal(true); buf.length.should.be.greaterThan(0); }); @@ -494,6 +538,13 @@ describe('PaymentProtocol', function() { describe('#deserialize', function() { + it('should error without a message type', function() { + var paypro = new PaymentProtocol(); + expect(function(){ + paypro.deserialize(new Buffer({size: 12})); + }).to.throw(Error); + }); + it('should deserialize a serialized message', function() { var obj = {}; var paypro = new PaymentProtocol(); @@ -511,6 +562,29 @@ describe('PaymentProtocol', function() { describe('#sign', function() { + it('should error when not a payment request', function() { + var paypro = new PaymentProtocol(); + expect(function(){ + paypro.sign(); + }).to.throw(Error); + }); + + it('should not sign if the pki_type is "none"', function() { + var paypro = new PaymentProtocol().makePaymentRequest(); + paypro.set('pki_type', 'none'); + var a = paypro.sign(); + var signature = a.get('signature'); + should.not.exist(signature); + }); + + it('should error if unkown pki_type', function() { + var paypro = new PaymentProtocol().makePaymentRequest(); + paypro.set('pki_type', 'x508'); //typo + expect(function(){ + paypro.sign(); + }).to.throw(Error); + }); + it('should sign a payment request', function() { // SIN var pd = new PaymentProtocol.PaymentDetails(); @@ -549,6 +623,28 @@ describe('PaymentProtocol', function() { describe('#verify', function() { + it('should error if not a payment request', function() { + var paypro = new PaymentProtocol(); + expect(function(){ + paypro.verify(); + }).to.throw(Error); + }); + + it('should return true if pki_type is set to "none"', function() { + var paypro = new PaymentProtocol().makePaymentRequest(); + paypro.set('pki_type', 'none'); + var valid = paypro.verify(); + valid.should.equal(true); + }); + + it('should error if unsupported pki_type', function() { + var paypro = new PaymentProtocol().makePaymentRequest(); + paypro.set('pki_type', 'x508'); // typo + expect(function(){ + paypro.verify(); + }).to.throw(Error); + }); + it('should verify a signed payment request', function() { // SIN var pd = new PaymentProtocol.PaymentDetails(); @@ -595,6 +691,13 @@ describe('PaymentProtocol', function() { describe('#sinSign', function() { + it('should error if not sent an instance of PrivateKey', function() { + var paypro = new PaymentProtocol(); + expect(function(){ + paypro.sinSign(Number(7)); // not a private key + }).to.throw(TypeError); + }); + it('should sign assuming pki_type is SIN', function() { var pd = new PaymentProtocol.PaymentDetails(); pd.set('time', 0); @@ -648,6 +751,7 @@ describe('PaymentProtocol', function() { paypro.set('pki_data', cr.serialize()); // contains one or more x509 certs var sig = paypro.x509Sign(x509.priv); + paypro.set('signature', sig); x509.sig2 = paypro.get('signature'); @@ -708,6 +812,13 @@ describe('PaymentProtocol', function() { paypro.set('pki_data', cr.serialize()); // contains one or more x509 certs + var sigTrust = paypro.x509Sign(x509.priv, true); + sigTrust.selfSigned.should.equal(1); + sigTrust.isChain.should.equal(false); + sigTrust.signature.length.should.be.greaterThan(0); + sigTrust.caTrusted.should.equal(false); + should.not.exist(sigTrust.caName); + var sig = paypro.x509Sign(x509.priv); paypro.set('signature', sig);