diff --git a/index.js b/index.js index 9cf8bc88a..e7889dc2a 100644 --- a/index.js +++ b/index.js @@ -28,8 +28,9 @@ bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.ECIES = require('./lib/expmt/ecies'); -bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); bitcore.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); +bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); +bitcore.expmt.StealthMessage = require('./lib/expmt/stealthmessage'); //dependencies, subject to change bitcore.deps = {}; diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 6a2e2dc28..2570c777b 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -60,12 +60,12 @@ Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { return key; }; -Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { +Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhashbuf) { var pubkey = this.getReceivePubkey(senderPubkey); var pubkeybuf = pubkey.toDER(true); var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); - if (pubkeyhash.toString('hex') === myPossiblePubkeyhash.toString('hex')) + if (pubkeyhash.toString('hex') === myPossiblePubkeyhashbuf.toString('hex')) return true; else return false; diff --git a/lib/expmt/stealthmessage.js b/lib/expmt/stealthmessage.js new file mode 100644 index 000000000..de7b6071c --- /dev/null +++ b/lib/expmt/stealthmessage.js @@ -0,0 +1,85 @@ +var Stealthkey = require('./stealthkey'); +var StealthAddress = require('./stealthaddress'); +var ECIES = require('./ecies'); +var Message = require('../message'); +var Keypair = require('../keypair'); +var Address = require('../address'); +var Pubkey = require('../pubkey'); + +var StealthMessage = function StealthMessage(obj) { + if (!(this instanceof StealthMessage)) + return new StealthMessage(obj); + if (obj) + this.set(obj); +}; + +StealthMessage.prototype.set = function(obj) { + this.messagebuf = obj.messagebuf || this.messagebuf; + this.encbuf = obj.encbuf || this.encbuf; + this.toStealthAddress = obj.toStealthAddress || this.toStealthAddress; + this.toStealthkey = obj.toStealthkey || this.toStealthkey; + this.fromKeypair = obj.fromKeypair || this.fromKeypair; + this.receiveAddress = obj.receiveAddress || this.receiveAddress; + return this; +}; + +StealthMessage.encrypt = function(messagebuf, toStealthAddress, fromKeypair, ivbuf) { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: toStealthAddress, + fromKeypair: fromKeypair + }); + sm.encrypt(ivbuf); + var buf = Buffer.concat([ + sm.receiveAddress.hashbuf, + sm.fromKeypair.pubkey.toDER(true), + sm.encbuf + ]); + return buf; +}; + +StealthMessage.decrypt = function(buf, toStealthkey) { + var sm = StealthMessage().set({ + toStealthkey: toStealthkey, + receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), + fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), + encbuf: buf.slice(20 + 33) + }); + return sm.decrypt().messagebuf; +}; + +StealthMessage.isForMe = function(buf, toStealthkey) { + var sm = StealthMessage().set({ + toStealthkey: toStealthkey, + receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), + fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), + encbuf: buf.slice(20 + 33) + }); + return sm.isForMe(); +}; + +StealthMessage.prototype.encrypt = function(ivbuf) { + if (!this.fromKeypair) + this.fromKeypair = Keypair().fromRandom(); + var receivePubkey = this.toStealthAddress.getReceivePubkey(this.fromKeypair); + this.receiveAddress = Address().fromPubkey(receivePubkey); + this.encbuf = ECIES.encrypt(this.messagebuf, Keypair().set({pubkey: receivePubkey}), this.fromKeypair, ivbuf); + return this; +}; + +StealthMessage.prototype.decrypt = function() { + var receiveKeypair = this.toStealthkey.getReceiveKeypair(this.fromKeypair.pubkey); + this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair); + return this; +}; + +StealthMessage.prototype.isForMe = function() { + var receivePubkey = this.toStealthkey.getReceivePubkey(this.fromKeypair.pubkey); + var receiveAddress = Address().fromPubkey(receivePubkey); + if (receiveAddress.toString('hex') === this.receiveAddress.toString('hex')) + return true; + else + return false; +}; + +module.exports = StealthMessage; diff --git a/test/stealthmessage.js b/test/stealthmessage.js new file mode 100644 index 000000000..3a0015c61 --- /dev/null +++ b/test/stealthmessage.js @@ -0,0 +1,130 @@ +var StealthMessage = require('../lib/expmt/stealthmessage'); +var Stealthkey = require('../lib/expmt/stealthkey'); +var StealthAddress = require('../lib/expmt/stealthAddress'); +var KDF = require('../lib/kdf'); +var Hash = require('../lib/hash'); +var should = require('chai').should(); +var Address = require('../lib/address'); + +describe('StealthMessage', function() { + + var payloadKeypair = KDF.buf2keypair(new Buffer('key1')); + var scanKeypair = KDF.buf2keypair(new Buffer('key2')); + var fromKeypair = KDF.buf2keypair(new Buffer('key3')); + var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3'; + var encbuf = new Buffer(enchex, 'hex'); + var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8); + var sk = Stealthkey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair}); + var sa = StealthAddress().fromStealthkey(sk); + var messagebuf = new Buffer('this is my message'); + + it('should make a new stealthmessage', function() { + var sm = new StealthMessage(); + should.exist(sm); + sm = StealthMessage() + should.exist(sm); + }); + + it('should allow "set" style syntax', function() { + var encbuf = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa + }).encrypt().encbuf; + should.exist(encbuf); + encbuf.length.should.equal(113); + }); + + describe('#set', function() { + + it('should set the messagebuf', function() { + var sm = StealthMessage().set({messagebuf: messagebuf}); + should.exist(sm.messagebuf); + }); + + }); + + describe('@encrypt', function() { + + it('should encrypt a message', function() { + var encbuf = StealthMessage.encrypt(messagebuf, sa); + encbuf.length.should.equal(166); + }); + + it('should encrypt a message with this fromKeypair and ivbuf the same each time', function() { + var encbuf = StealthMessage.encrypt(messagebuf, sa, fromKeypair, ivbuf); + encbuf.length.should.equal(166); + encbuf.toString('hex').should.equal(enchex); + }); + + }); + + describe('@decrypt', function() { + + it('should decrypt this known message correctly', function() { + var messagebuf2 = StealthMessage.decrypt(encbuf, sk); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + + describe('@isForMe', function() { + + it('should know that this message is for me', function() { + StealthMessage.isForMe(encbuf, sk).should.equal(true); + }); + + }); + + describe('#encrypt', function() { + + it('should encrypt this message', function() { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa, + fromKeypair: fromKeypair + }); + sm.encrypt().encbuf.length.should.equal(113); + }); + + }); + + describe('#decrypt', function() { + + it('should decrypt that which was encrypted', function() { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa + }).encrypt(); + var messagebuf2 = StealthMessage().set({ + encbuf: sm.encbuf, + fromKeypair: sm.fromKeypair, + toStealthkey: sk + }).decrypt().messagebuf; + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + + describe('#isForMe', function() { + + it('should know that this message is for me', function() { + StealthMessage().set({ + encbuf: encbuf, + toStealthkey: sk, + fromKeypair: fromKeypair, + receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) + }).isForMe().should.equal(true); + }); + + it('should know that this message is not for me', function() { + StealthMessage().set({ + encbuf: encbuf, + toStealthkey: sk, + fromKeypair: fromKeypair, + receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) + }).isForMe().should.equal(true); + }); + + }); + +});