basic stealth address support

Math only. Does not yet support transactions. Not yet compatible with Dark
Wallet.
This commit is contained in:
Ryan X. Charles 2014-08-13 20:54:05 -04:00
parent f8fc3812f0
commit bc1c4235f2
3 changed files with 290 additions and 2 deletions

View File

@ -18,8 +18,8 @@ privsec.Random = require('./lib/random');
privsec.Signature = require('./lib/signature');
//experimental
//privsec.expmt = {};
//privsec.expmt.Stealth = require('./lib/expmt/stealth');
privsec.expmt = {};
privsec.expmt.Stealth = require('./lib/expmt/stealth');
//dependencies
privsec.deps = {};

111
lib/expmt/stealth.js Normal file
View File

@ -0,0 +1,111 @@
var Key = require('../key');
var Privkey = require('../privkey');
var Pubkey = require('../pubkey');
var Hash = require('../hash');
var KDF = require('../kdf');
var base58check = require('../base58check');
var Stealth = function Stealth(payloadKey, scanKey) {
if (!(this instanceof Stealth))
return new Stealth(payloadKey, scanKey);
this.payloadKey = payloadKey;
this.scanKey = scanKey;
};
Stealth.prototype.fromAddressBuffer = function(buf) {
if (!Buffer.isBuffer(buf) || buf.length !== 66)
throw new Error('stealth: A stealth address must have length 66');
var pPubBuf = buf.slice(0, 33);
var sPubBuf = buf.slice(33, 66);
var payloadPubkey = Pubkey().fromDER(pPubBuf);
this.payloadKey = Key(undefined, payloadPubkey);
var scanPubkey = Pubkey().fromDER(sPubBuf);
this.scanKey = Key(undefined, scanPubkey);
return this;
};
Stealth.prototype.fromAddressString = function(str) {
var buf = base58check.decode(str);
this.fromAddressBuffer(buf);
return this;
};
Stealth.prototype.fromRandom = function() {
this.payloadKey = Key().fromRandom();
this.scanKey = Key().fromRandom();
return this;
};
Stealth.prototype.getSharedKeyAsReceiver = function(senderPubkey) {
var sharedSecretPoint = senderPubkey.point.mul(this.scanKey.privkey.bn);
var sharedSecretPubkey = Pubkey(sharedSecretPoint);
var buf = sharedSecretPubkey.toDER(true);
var sharedKey = KDF.sha256hmac2key(buf);
return sharedKey;
};
Stealth.prototype.getSharedKeyAsSender = function(senderKey) {
var sharedSecretPoint = this.scanKey.pubkey.point.mul(senderKey.privkey.bn);
var sharedSecretPubkey = Pubkey(sharedSecretPoint);
var buf = sharedSecretPubkey.toDER(true);
var sharedKey = KDF.sha256hmac2key(buf);
return sharedKey;
};
Stealth.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) {
var sharedKey = this.getSharedKeyAsReceiver(senderPubkey);
var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point));
return pubkey;
};
Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) {
var sharedKey = this.getSharedKeyAsSender(senderKey);
var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point));
return pubkey;
};
Stealth.prototype.getReceiveKey = function(senderPubkey) {
var sharedKey = this.getSharedKeyAsReceiver(senderPubkey);
var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn));
var key = Key(privkey);
key.privkey2pubkey();
return key;
};
Stealth.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) {
var pubkey = this.getReceivePubkeyAsReceiver(senderPubkey);
var pubkeybuf = pubkey.toDER(true);
var pubkeyhash = Hash.sha256ripemd160(pubkeybuf);
if (pubkeyhash.toString('hex') === myPossiblePubkeyhash.toString('hex'))
return true;
else
return false;
};
Stealth.prototype.toAddressBuffer = function() {
var pBuf = this.payloadKey.pubkey.toDER(true);
var sBuf = this.scanKey.pubkey.toDER(true);
return Buffer.concat([pBuf, sBuf]);
};
Stealth.prototype.toAddressString = function() {
var buf = this.toAddressBuffer();
var b58 = base58check.encode(buf);
return b58;
};
module.exports = Stealth;

177
test/test.stealth.js Normal file
View File

@ -0,0 +1,177 @@
var should = require('chai').should();
var Stealth = require('../lib/expmt/stealth');
var Key = require('../lib/key');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var BN = require('../lib/bn');
var Hash = require('../lib/hash');
var base58check = require('../lib/base58check');
describe('stealth', function() {
var stealth = Stealth();
stealth.payloadKey = Key();
stealth.payloadKey.privkey = Privkey();
stealth.payloadKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1')));
stealth.payloadKey.privkey2pubkey();
stealth.scanKey = Key();
stealth.scanKey.privkey = Privkey();
stealth.scanKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2')));
stealth.scanKey.privkey2pubkey();
var senderKey = Key();
senderKey.privkey = Privkey();
senderKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3')));
senderKey.privkey2pubkey();
var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW';
it('should create a new stealth', function() {
var stealth = new Stealth();
should.exist(stealth);
});
it('should create a new stealth without using "new"', function() {
var stealth = Stealth();
should.exist(stealth);
});
describe('#fromAddressBuffer', function() {
it('should give a stealth address with the right pubkeys', function() {
var stealth2 = new Stealth();
var buf = base58check.decode(addressString);
stealth2.fromAddressBuffer(buf);
stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString());
stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString());
});
});
describe('#fromAddressString', function() {
it('should give a stealth address with the right pubkeys', function() {
var stealth2 = new Stealth();
stealth2.fromAddressString(addressString);
stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString());
stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString());
});
});
describe('#fromRandom', function() {
it('should create a new stealth from random', function() {
var stealth = Stealth().fromRandom();
should.exist(stealth.payloadKey.privkey.bn.gt(0));
should.exist(stealth.scanKey.privkey.bn.gt(0));
});
});
describe('#getSharedKeyAsReceiver', function() {
it('should return a key', function() {
var key = stealth.getSharedKeyAsReceiver(senderKey.pubkey);
(key instanceof Key).should.equal(true);
});
});
describe('#getSharedKeyAsSender', function() {
it('should return a key', function() {
var stealth2 = new Stealth();
stealth2.payloadKey = new Key();
stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey;
stealth2.scanKey = new Key();
stealth2.scanKey.pubkey = stealth.scanKey.pubkey;
var key = stealth2.getSharedKeyAsSender(senderKey);
(key instanceof Key).should.equal(true);
});
it('should return the same key as getSharedKeyAsReceiver', function() {
var stealth2 = new Stealth();
stealth2.payloadKey = new Key();
stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey;
stealth2.scanKey = new Key();
stealth2.scanKey.pubkey = stealth.scanKey.pubkey;
var key = stealth2.getSharedKeyAsSender(senderKey);
var key2 = stealth.getSharedKeyAsReceiver(senderKey.pubkey);
key.toString().should.equal(key2.toString());
});
});
describe('#getReceivePubkeyAsReceiver', function() {
it('should return a pubkey', function() {
var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey);
(pubkey instanceof Pubkey).should.equal(true);
});
});
describe('#getReceivePubkeyAsSender', function() {
it('should return a pubkey', function() {
var pubkey = stealth.getReceivePubkeyAsSender(senderKey);
(pubkey instanceof Pubkey).should.equal(true);
});
it('should return the same pubkey as getReceivePubkeyAsReceiver', function() {
var pubkey = stealth.getReceivePubkeyAsSender(senderKey);
var pubkey2 = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey);
pubkey2.toString().should.equal(pubkey.toString());
});
});
describe('#getReceiveKey', function() {
it('should return a key', function() {
var key = stealth.getReceiveKey(senderKey.pubkey);
(key instanceof Key).should.equal(true);
});
it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() {
var key = stealth.getReceiveKey(senderKey.pubkey);
var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey);
key.pubkey.toString().should.equal(pubkey.toString());
});
});
describe('#isForMe', function() {
it('should return true if it (the transaction or message) is for me', function() {
var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex');
stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(true);
});
it('should return false if it (the transaction or message) is not for me', function() {
var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex');
stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(false);
});
});
describe('#toAddressBuffer', function() {
it('should return this known address buffer', function() {
var buf = stealth.toAddressBuffer();
base58check.encode(buf).should.equal(addressString);
});
});
describe('#toAddressString', function() {
it('should return this known address string', function() {
stealth.toAddressString().should.equal(addressString);
});
});
});