Key should make sure new privkey is less than N

...this involves adding a Curve class, and significant refactoring to make this
possible in a clean way.
This commit is contained in:
Ryan X. Charles 2014-04-23 21:15:55 -03:00
parent dbcf270826
commit 350f6ae998
13 changed files with 151 additions and 59 deletions

View File

@ -17,6 +17,7 @@ requireWhenAccessed('buffertools', 'buffertools');
requireWhenAccessed('Buffers.monkey', './patches/Buffers.monkey');
requireWhenAccessed('config', './config');
requireWhenAccessed('const', './const');
requireWhenAccessed('Curve', './lib/Curve');
requireWhenAccessed('Deserialize', './lib/Deserialize');
requireWhenAccessed('log', './util/log');
requireWhenAccessed('networks', './networks');

View File

@ -28,6 +28,7 @@ var modules = [
'lib/Block',
'lib/Bloom',
'lib/Connection',
'lib/Curve',
'lib/Deserialize',
'lib/Electrum',
'lib/Message',

View File

@ -289,17 +289,20 @@ BIP32.prototype.deriveChild = function(i) {
var ilGkey = new Key();
ilGkey.private = il;
ilGkey.regenerateSync();
var ilG = Point.fromKey(ilGkey);
ilGkey.compressed = false;
var ilG = Point.fromUncompressedPubKey(ilGkey.public);
var oldkey = new Key();
oldkey.public = this.eckey.public;
var Kpar = Point.fromKey(oldkey);
var newpub = Point.add(ilG, Kpar).toKey().public;
oldkey.compressed = false;
var Kpar = Point.fromUncompressedPubKey(oldkey.public);
var newpub = Point.add(ilG, Kpar).toUncompressedPubKey();
ret = new BIP32(null);
ret.chainCode = new Buffer(ir);
var eckey = new Key();
eckey.public = newpub;
eckey.compressed = true;
ret.eckey = eckey;
ret.hasPrivateKey = false;
}

22
lib/Curve.js Normal file
View File

@ -0,0 +1,22 @@
"use strict";
var imports = require('soop');
var bignum = imports.bignum || require('bignum');
var Point = imports.Point || require('./Point');
var n = bignum.fromBuffer(new Buffer("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 'hex'), {size: 32});
var G = new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {size: 32}),
bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {size: 32}));
/* secp256k1 curve */
var Curve = function() {
};
Curve.getG = function() {
return G;
};
Curve.getN = function() {
return n;
};
module.exports = require('soop')(Curve);

View File

@ -31,8 +31,9 @@ Electrum.prototype.generatePubKey = function (n, for_change) {
var sequence_key = new Key();
sequence_key.private = sequence.toBuffer();
sequence_key.regenerateSync();
sequence_key.compressed = false;
var sequence_pt = Point.fromKey(sequence_key);
var sequence_pt = Point.fromUncompressedPubKey(sequence_key.public);
pt = Point.add(mpk_pt, sequence_pt);

View File

@ -1,10 +1,10 @@
var ECKey = require('../../browser/vendor-bundle.js').ECKey;
var buffertools = require('buffertools');
var SecureRandom = require('../SecureRandom');
var Curve = require('../Curve');
var Key = function() {
this._pub = null;
this.compressed = true; // default
this._compressed = true; // default
};
var bufferToArray = Key.bufferToArray = function(buffer) {
@ -18,14 +18,13 @@ var bufferToArray = Key.bufferToArray = function(buffer) {
return ret;
}
Object.defineProperty(Key.prototype, 'public', {
set: function(p){
if (!Buffer.isBuffer(p) ) {
throw new Error('Arg should be a buffer');
}
var type = p[0];
this.compressed = type!==0x04;
this._compressed = type!==0x04;
this._pub = p;
},
get: function(){
@ -33,8 +32,38 @@ Object.defineProperty(Key.prototype, 'public', {
}
});
Object.defineProperty(Key.prototype, 'compressed', {
set: function(c) {
var oldc = this._compressed;
this._compressed = !!c;
if (oldc == this._compressed)
return;
var oldp = this._pub;
if (this._pub) {
var eckey = new ECKey();
eckey.setPub(bufferToArray(this.public));
eckey.setCompressed(this._compressed);
this._pub = new Buffer(eckey.getPub());
}
if (!this._compressed) {
//bug in eckey
//oldp.slice(1).copy(this._pub, 1);
}
},
get: function() {
return this._compressed;
}
});
Key.generateSync = function() {
var privbuf = SecureRandom.getRandomBuffer(32);
var privbuf;
while(true) {
privbuf = SecureRandom.getRandomBuffer(32);
if ((bignum.fromBuffer(privbuf, {size: 32})).cmp(Curve.getN()) < 0)
break;
}
var privhex = privbuf.toString('hex');
var eck = new ECKey(privhex);
eck.setCompressed(true);
@ -42,7 +71,7 @@ Key.generateSync = function() {
ret = new Key();
ret.private = privbuf;
ret.compressed = true;
ret._compressed = true;
ret.public = new Buffer(eck.getPub());
return ret;
@ -54,8 +83,8 @@ Key.prototype.regenerateSync = function() {
}
var eck = new ECKey(buffertools.toHex(this.private));
eck.setCompressed(this.compressed);
this.public = new Buffer(eck.getPub());
eck.setCompressed(this._compressed);
this._pub = new Buffer(eck.getPub());
return this;
};
@ -68,7 +97,7 @@ Key.prototype.signSync = function(hash) {
throw new Error('Arg should be a 32 bytes hash buffer');
}
var eck = new ECKey(buffertools.toHex(this.private));
eck.setCompressed(this.compressed);
eck.setCompressed(this._compressed);
var signature = eck.sign(bufferToArray(hash));
// return it as a buffer to keep c++ compatibility
return new Buffer(signature);
@ -98,7 +127,7 @@ Key.prototype.verifySignatureSync = function(hash, sig) {
var eck = new ECKey();
eck.setPub(bufferToArray(self.public));
eck.setCompressed(self.compressed);
eck.setCompressed(self._compressed);
var sigA = bufferToArray(sig);
var ret = eck.verify(bufferToArray(hash),sigA);
return ret;

View File

@ -51,31 +51,20 @@ Point.add = function(p1, p2) {
};
//convert the public key of a Key into a Point
Point.fromKey = function(key) {
Point.fromUncompressedPubKey = function(pubkey) {
var point = new Point();
var pubKeyBuf = new Buffer(key.public);
var key2 = new ECKey();
key2.setCompressed(key.compressed);
key2.setPub(Key.bufferToArray(pubKeyBuf));
key2.setCompressed(false);
point.x = bignum.fromBuffer((new Buffer(key2.getPub())).slice(1, 33), {size: 32});
point.y = bignum.fromBuffer((new Buffer(key2.getPub())).slice(33, 65), {size: 32});
point.x = bignum.fromBuffer((new Buffer(pubkey)).slice(1, 33), {size: 32});
point.y = bignum.fromBuffer((new Buffer(pubkey)).slice(33, 65), {size: 32});
return point;
};
//convert the Point into the Key containing a compressed public key
Point.prototype.toKey = function() {
Point.prototype.toUncompressedPubKey = function() {
var xbuf = this.x.toBuffer({size: 32});
var ybuf = this.y.toBuffer({size: 32});
var key = new ECKey();
key.setCompressed(false);
var prefix = new Buffer([0x04]);
var pub = Buffer.concat([prefix, xbuf, ybuf]); //this might be wrong
key.setPub(Key.bufferToArray(pub));
key.setCompressed(true);
var key2 = new Key();
key2.public = new Buffer(key.getPub());
return key2;
var pub = Buffer.concat([prefix, xbuf, ybuf]);
return pub;
};
module.exports = require('soop')(Point);

View File

@ -1 +1,3 @@
module.exports = require('bindings')('KeyModule').Key;
var Key = require('bindings')('KeyModule').Key;
module.exports = Key;

View File

@ -1,8 +1,8 @@
"use strict";
var imports = require('soop').imports();
var Key = imports.Key || require('../Key');
var bignum = imports.bignum || require('bignum');
var CPPKey = imports.CPPKey || require('bindings')('KeyModule').Key;
var assert = require('assert');
//a point on the secp256k1 curve
@ -13,41 +13,27 @@ var Point = function(x, y) {
};
Point.add = function(p1, p2) {
var key1 = p1.toKey();
key1.compressed = false;
var key2 = p2.toKey();
key2.compressed = false;
var pubKey = Key.addUncompressed(key1.public, key2.public);
var key = new Key();
key.compressed = false;
key.public = pubKey;
key.compressed = true;
return Point.fromKey(key);
var u1 = p1.toUncompressedPubKey();
var u2 = p2.toUncompressedPubKey();
var pubKey = CPPKey.addUncompressed(u1, u2);
return Point.fromUncompressedPubKey(pubKey);
};
//convert the public key of a Key into a Point
Point.fromKey = function(key) {
Point.fromUncompressedPubKey = function(pubkey) {
var point = new Point();
var pubKeyBuf = new Buffer(key.public);
var key2 = new Key();
key2.compressed = key.compressed;
key2.public = pubKeyBuf;
key2.compressed = false;
point.x = bignum.fromBuffer(key2.public.slice(1, 33), {size: 32});
point.y = bignum.fromBuffer(key2.public.slice(33, 65), {size: 32});
point.x = bignum.fromBuffer(pubkey.slice(1, 33), {size: 32});
point.y = bignum.fromBuffer(pubkey.slice(33, 65), {size: 32});
return point;
};
//convert the Point into the Key containing a compressed public key
Point.prototype.toKey = function() {
Point.prototype.toUncompressedPubKey = function() {
var xbuf = this.x.toBuffer({size: 32});
var ybuf = this.y.toBuffer({size: 32});
var key = new Key();
key.compressed = false;
var prefix = new Buffer([0x04]);
key.public = Buffer.concat([prefix, xbuf, ybuf]); //this might be wrong
key.compressed = true;
return key;
var pubkey = Buffer.concat([prefix, xbuf, ybuf]);
return pubkey;
};
module.exports = require('soop')(Point);

View File

@ -21,6 +21,7 @@
<script src="test.Block.js"></script>
<script src="test.Bloom.js"></script>
<script src="test.Connection.js"></script>
<script src="test.Curve.js"></script>
<script src="test.EncodedData.js"></script>
<script src="test.Electrum.js"></script>
<script src="test.Key.js"></script>

37
test/test.Curve.js Normal file
View File

@ -0,0 +1,37 @@
'use strict';
var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore');
var coinUtil = coinUtil || bitcore.util;
var buffertools = require('buffertools');
var bignum = require('bignum');
var should = chai.should();
var assert = chai.assert;
var Curve = bitcore.Curve;
describe('Curve', function() {
it('should initialize the main object', function() {
should.exist(Curve);
});
describe('getN', function() {
it('should return a big number', function() {
var N = Curve.getN();
should.exist(N);
N.toBuffer({size: 32}).toString('hex').length.should.equal(64);
});
});
describe('getG', function() {
it('should return a Point', function() {
var G = Curve.getG();
should.exist(G.x);
G.x.toBuffer({size: 32}).toString('hex').length.should.equal(64);
G.y.toBuffer({size: 32}).toString('hex').length.should.equal(64);
});
});
});

View File

@ -7,6 +7,9 @@ var should = chai.should();
var assert = chai.assert;
var Key = bitcore.Key;
var Point = bitcore.Point;
var bignum = require('bignum');
describe('Key', function() {
it('should initialize the main object', function() {
should.exist(Key);
@ -15,6 +18,20 @@ describe('Key', function() {
var k = new Key();
should.exist(k);
});
it('should set change compressed to uncompressed', function() {
var key = Key.generateSync();
key.public.length.should.equal(33);
key.compressed = false;
key.public.length.should.equal(65);
});
it('should change uncompressed to compressed', function() {
var key = Key.generateSync();
key.compressed = false;
var key2 = new Key();
key2.public = key.public;
key2.compressed = true;
key2.public.length.should.equal(33);
});
it('should be able to generateSync instance', function() {
var k = Key.generateSync();
should.exist(k);
@ -112,4 +129,5 @@ describe('Key', function() {
var ret= k.verifySignatureSync(a_hash, sig2);
ret.should.equal(false);
});
});

View File

@ -49,8 +49,9 @@ describe('Point', function() {
var pubKeyBufCompressedHex = "0369b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d";
var key = new Key();
key.public = new Buffer(pubKeyBufCompressedHex, 'hex');
key.compressed = false;
key.public.toString('hex').should.equal(a.toKey().public.toString('hex'));
key.public.toString('hex').should.equal(a.toUncompressedPubKey().toString('hex'));
});
it('should convert the public key of a Key into a Point', function() {
@ -60,8 +61,9 @@ describe('Point', function() {
var pubKeyBufCompressedHex = "0369b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d";
var key = new Key();
key.public = new Buffer(pubKeyBufCompressedHex, 'hex');
key.compressed = false;
var point = Point.fromKey(key);
var point = Point.fromUncompressedPubKey(key.public);
point.x.toBuffer({size: 32}).toString('hex').should.equal(axhex);
point.y.toBuffer({size: 32}).toString('hex').should.equal(ayhex);
});