diff --git a/lib/PayPro.js b/lib/PayPro.js index 237a57c..1a1623e 100644 --- a/lib/PayPro.js +++ b/lib/PayPro.js @@ -1,6 +1,7 @@ 'use strict'; var imports = require('soop').imports(); var protobufjs = protobufjs || require('protobufjs/dist/ProtoBuf'); +var Message = Message || require('./Message'); // BIP 70 - payment protocol function PayPro() { @@ -8,8 +9,6 @@ function PayPro() { this.message = null; } -PayPro.constants = {}; - PayPro.PAYMENT_REQUEST_MAX_SIZE = 50000; PayPro.PAYMENT_MAX_SIZE = 50000; PayPro.PAYMENT_ACK_MAX_SIZE = 60000; @@ -149,11 +148,19 @@ PayPro.prototype.set = function(key, val) { 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; }; @@ -167,6 +174,17 @@ PayPro.prototype.setObj = function(obj) { 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) @@ -183,4 +201,46 @@ PayPro.prototype.deserialize = function(buf, messageType) { 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 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); +}; + module.exports = require('soop')(PayPro); diff --git a/test/test.PayPro.js b/test/test.PayPro.js index 4b654f6..d8dcf31 100644 --- a/test/test.PayPro.js +++ b/test/test.PayPro.js @@ -177,6 +177,37 @@ describe('PayPro', function() { }); + describe('#setObj', function() { + + it('should set properties of paymentdetails', function() { + var pd = new PayPro.PaymentDetails(); + var paypro = new PayPro(); + paypro.messageType = "PaymentDetails"; + paypro.message = pd; + paypro.setObj({ + time: 0 + }); + paypro.get('time').should.equal(0); + }); + + }); + + describe('#serializeForSig', function() { + + it('should serialize a PaymentRequest and not fail', 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); + var buf = paypro.serializeForSig(); + buf.length.should.be.greaterThan(0); + }); + + }); + describe('#serialize', function() { it('should serialize', function() { @@ -209,4 +240,83 @@ describe('PayPro', function() { }); + describe('#sign', function() { + + it('should sign a payment request', 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', 'SIN'); + var key = new bitcore.Key(); + key.private = bitcore.util.sha256('test key'); + key.regenerateSync(); + paypro.sign(key); + var sig = paypro.get('signature'); + sig.length.should.be.greaterThan(0); + }); + + }); + + describe('#verify', function() { + + it('should verify a signed payment request', 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', 'SIN'); + var key = new bitcore.Key(); + key.private = bitcore.util.sha256('test key'); + key.regenerateSync(); + paypro.sign(key); + var verify = paypro.verify(); + verify.should.equal(true); + }); + + }); + + describe('#sinSign', function() { + + it('should sign assuming pki_type is SIN', 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', 'SIN'); + var key = new bitcore.Key(); + key.private = bitcore.util.sha256('test key'); + key.regenerateSync(); + var sig = paypro.sinSign(key); + sig.length.should.be.greaterThan(0); + }); + + }); + + describe('#sinVerify', function() { + + it('should verify assuming pki_type is SIN', 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', 'SIN'); + var key = new bitcore.Key(); + key.private = bitcore.util.sha256('test key'); + key.regenerateSync(); + paypro.sign(key); + var verify = paypro.sinVerify(); + verify.should.equal(true); + }); + + }); + });