StealthMessage

This code should be regarded as being a proof-of-concept, and needs more review
before being used in production code. At least one thing is guaranteed to
change, and that is the format of a stealth address.
This commit is contained in:
Ryan X. Charles 2014-09-10 14:00:53 -07:00
parent 74549a53ec
commit 5a86a1a5c6
4 changed files with 219 additions and 3 deletions

View File

@ -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 = {};

View File

@ -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;

View File

@ -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;

130
test/stealthmessage.js Normal file
View File

@ -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);
});
});
});