diff --git a/bitcore.js b/bitcore.js
index 658a31d..d9d8cdf 100644
--- a/bitcore.js
+++ b/bitcore.js
@@ -33,7 +33,6 @@ requireWhenAccessed('buffertools', 'buffertools');
requireWhenAccessed('Buffers.monkey', './patches/Buffers.monkey');
requireWhenAccessed('config', './config');
requireWhenAccessed('const', './const');
-requireWhenAccessed('Curve', './lib/Curve');
requireWhenAccessed('Deserialize', './lib/Deserialize');
requireWhenAccessed('ECIES', './lib/ECIES');
requireWhenAccessed('log', './util/log');
diff --git a/browser/build.js b/browser/build.js
index a34aae6..6d6de0a 100644
--- a/browser/build.js
+++ b/browser/build.js
@@ -20,7 +20,6 @@ var modules = [
'lib/Block',
'lib/Bloom',
'lib/Connection',
- 'lib/Curve',
'lib/Deserialize',
'lib/ECIES',
'lib/Electrum',
@@ -167,7 +166,7 @@ if (require.main === module) {
var pjson = require('../package.json');
bitcoreBundle.pipe(
program.stdout ? process.stdout :
- fs.createWriteStream('browser/bitcore-dev.js'));
+ fs.createWriteStream('browser/bundle.js'));
}
module.exports.createBitcore = createBitcore;
diff --git a/lib/Curve.js b/lib/Curve.js
deleted file mode 100644
index 842bea7..0000000
--- a/lib/Curve.js
+++ /dev/null
@@ -1,32 +0,0 @@
-"use strict";
-var bignum = require('bignum');
-var Point = require('./Point');
-
-var n = bignum.fromBuffer(new Buffer("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 'hex'), {
- size: 32
-});
-
-
-var Curve = function() {};
-
-/* secp256k1 curve */
-var G;
-Curve.getG = function() {
- // don't use Point in top scope, causes exception in browser
- // when Point is not loaded yet
-
- // use cached version if available
- G = G || new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {
- size: 32
- }),
- bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {
- size: 32
- }));
- return G;
-};
-
-Curve.getN = function() {
- return n;
-};
-
-module.exports = Curve;
diff --git a/lib/Key.js b/lib/Key.js
index 8e079d9..6d69639 100644
--- a/lib/Key.js
+++ b/lib/Key.js
@@ -1 +1,9 @@
-module.exports = require('bindings')('KeyModule').Key;
+var Key = require('bindings')('KeyModule').Key;
+var CommonKey = require('./common/Key');
+
+for (var i in CommonKey) {
+ if (CommonKey.hasOwnProperty(i))
+ Key[i] = CommonKey[i];
+}
+
+module.exports = Key;
diff --git a/lib/browser/Key.js b/lib/browser/Key.js
index 8c42a96..6de6edc 100644
--- a/lib/browser/Key.js
+++ b/lib/browser/Key.js
@@ -2,12 +2,19 @@ var SecureRandom = require('../SecureRandom');
var bignum = require('bignum');
var elliptic = require('elliptic');
var Point = require('./Point');
+var CommonKey = require('../common/Key');
+var util = require('util');
var Key = function() {
this._pub = null;
this._compressed = true; // default
};
+for (var i in CommonKey) {
+ if (CommonKey.hasOwnProperty(i))
+ Key[i] = CommonKey[i];
+}
+
var bufferToArray = Key.bufferToArray = function(buffer) {
var ret = [];
@@ -110,46 +117,6 @@ Key.prototype.regenerateSync = function() {
Key.prototype.signSync = function(hash) {
var ec = elliptic.curves.secp256k1;
- var genk = function() {
- //TODO: account for when >= n
- return new bignum(SecureRandom.getRandomBuffer(8));
- };
-
- var sign = function(hash, priv) {
- var d = priv;
- var n = ec.n;
- var e = new bignum(hash);
-
- do {
- var k = genk();
- var G = ec.g;
- var Q = G.mul(k);
- var r = Q.getX().mod(n);
- var s = k.invm(n).mul(e.add(d.mul(r))).mod(n);
- } while (r.cmp(new bignum(0)) <= 0 || s.cmp(new bignum(0)) <= 0);
-
- return serializeSig(r, s);
- };
-
- var serializeSig = function(r, s) {
- var rBa = r.toArray();
- var sBa = s.toArray();
-
- var sequence = [];
- sequence.push(0x02); // INTEGER
- sequence.push(rBa.length);
- sequence = sequence.concat(rBa);
-
- sequence.push(0x02); // INTEGER
- sequence.push(sBa.length);
- sequence = sequence.concat(sBa);
-
- sequence.unshift(sequence.length);
- sequence.unshift(0x30); // SEQUENCE
-
- return sequence;
- };
-
if (!this.private) {
throw new Error('Key does not have a private key set');
}
@@ -158,9 +125,10 @@ Key.prototype.signSync = function(hash) {
throw new Error('Arg should be a 32 bytes hash buffer');
}
var privnum = new bignum(this.private);
- var signature = sign(hash, privnum);
+ var sigrs = Key.sign(hash, privnum);
+ var der = Key.rs2DER(sigrs.r, sigrs.s);
- return new Buffer(signature);
+ return der;
};
Key.prototype.verifySignature = function(hash, sig, callback) {
diff --git a/lib/common/Key.js b/lib/common/Key.js
new file mode 100644
index 0000000..0665c49
--- /dev/null
+++ b/lib/common/Key.js
@@ -0,0 +1,106 @@
+var bignum = require('bignum');
+var Point = require('./Point');
+var SecureRandom = require('./SecureRandom');
+var Key = function() {}
+
+Key.parseDERsig = function(sig) {
+ if (!Buffer.isBuffer(sig))
+ throw new Error('DER formatted signature should be a buffer');
+
+ var header = sig[0];
+
+ if (header !== 0x30)
+ throw new Error('Header byte should be 0x30');
+
+ var length = sig[1];
+ if (length !== sig.slice(2).length)
+ throw new Error('Length byte should length of what follows');
+
+ var rheader = sig[2 + 0];
+ if (rheader !== 0x02)
+ throw new Error('Integer byte for r should be 0x02');
+
+ var rlength = sig[2 + 1];
+ var rbuf = sig.slice(2 + 2, 2 + 2 + rlength);
+ var r = bignum.fromBuffer(rbuf);
+ var rneg = sig[2 + 1 + 1] === 0x00 ? true : false;
+ if (rlength !== rbuf.length)
+ throw new Error('Length of r incorrect');
+
+ var sheader = sig[2 + 2 + rlength + 0];
+ if (sheader !== 0x02)
+ throw new Error('Integer byte for s should be 0x02');
+
+ var slength = sig[2 + 2 + rlength + 1];
+ var sbuf = sig.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength);
+ var s = bignum.fromBuffer(sbuf);
+ var sneg = sig[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false;
+ if (slength !== sbuf.length)
+ throw new Error('Length of s incorrect');
+
+ var sumlength = 2 + 2 + rlength + 2 + slength;
+ if (length !== sumlength - 2)
+ throw new Error('Length of signature incorrect');
+
+
+ var obj = {
+ header: header,
+ length: length,
+ rheader: rheader,
+ rlength: rlength,
+ rneg: rneg,
+ rbuf: rbuf,
+ r: r,
+ sheader: sheader,
+ slength: slength,
+ sneg: sneg,
+ sbuf: sbuf,
+ s: s
+ };
+
+ return obj;
+};
+
+Key.rs2DER = function(r, s) {
+ var rnbuf = r.toBuffer();
+ var snbuf = s.toBuffer();
+
+ var rneg = rnbuf[0] & 0x80 ? true : false;
+ var sneg = snbuf[0] & 0x80 ? true : false;
+
+ var rbuf = rneg ? Buffer.concat([new Buffer([0x00]), rnbuf]) : rnbuf;
+ var sbuf = sneg ? Buffer.concat([new Buffer([0x00]), snbuf]) : snbuf;
+
+ var length = 2 + rbuf.length + 2 + sbuf.length;
+ var rlength = rbuf.length;
+ var slength = sbuf.length;
+ var rheader = 0x02;
+ var sheader = 0x02;
+ var header = 0x30;
+
+ var der = Buffer.concat([new Buffer([header, length, rheader, rlength]), rbuf, new Buffer([sheader, slength]), sbuf]);
+ return der;
+};
+
+Key.sign = function(hash, priv, k) {
+ var d = priv;
+ var n = Point.getN();
+ var e = new bignum(hash);
+
+ do {
+ var k = k || Key.genk();
+ var G = Point.getG();
+ var Q = Point.multiply(G, k);
+ var r = Q.x.mod(n);
+ var s = k.invm(n).mul(e.add(d.mul(r))).mod(n);
+ } while (r.cmp(new bignum(0)) <= 0 || s.cmp(new bignum(0)) <= 0);
+
+ return {r: r, s: s};
+};
+
+Key.genk = function() {
+ //TODO: account for when >= n
+ return new bignum(SecureRandom.getRandomBuffer(8));
+};
+
+module.exports = Key;
diff --git a/lib/common/Point.js b/lib/common/Point.js
index 7f5518d..64682f8 100644
--- a/lib/common/Point.js
+++ b/lib/common/Point.js
@@ -6,6 +6,29 @@ var Point = function(x, y) {
this.y = y;
};
+var n = bignum.fromBuffer(new Buffer("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 'hex'), {
+ size: 32
+});
+
+Point.getN = function() {
+ return n;
+};
+
+var G;
+Point.getG = function() {
+ // don't use Point in top scope, causes exception in browser
+ // when Point is not loaded yet
+
+ // use cached version if available
+ G = G || new Point(bignum.fromBuffer(new Buffer("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 'hex'), {
+ size: 32
+ }),
+ bignum.fromBuffer(new Buffer("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 'hex'), {
+ size: 32
+ }));
+ return G;
+};
+
//convert the public key of a Key into a Point
Point.fromUncompressedPubKey = function(pubkey) {
var point = new Point();
diff --git a/test/index.html b/test/index.html
index 8dd4bd5..db755ff 100644
--- a/test/index.html
+++ b/test/index.html
@@ -11,7 +11,7 @@
-
+
@@ -23,7 +23,6 @@
-
diff --git a/test/test.Curve.js b/test/test.Curve.js
deleted file mode 100644
index bb6ba04..0000000
--- a/test/test.Curve.js
+++ /dev/null
@@ -1,37 +0,0 @@
-'use strict';
-
-var chai = chai || require('chai');
-var bitcore = bitcore || require('../bitcore');
-var coinUtil = coinUtil || bitcore.util;
-var buffertools = require('buffertools');
-var bignum = bitcore.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);
- });
- });
-
-});
diff --git a/test/test.Key.js b/test/test.Key.js
index 5b2df64..244035c 100644
--- a/test/test.Key.js
+++ b/test/test.Key.js
@@ -132,6 +132,65 @@ describe('Key (ECKey)', function() {
ret.should.equal(false);
});
+ describe('#parseDERsig', function() {
+ it('should parse this signature generated in node', function() {
+ var sighex = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72';
+ var sig = new Buffer(sighex, 'hex');
+ var parsed = Key.parseDERsig(sig);
+ parsed.header.should.equal(0x30)
+ parsed.length.should.equal(69)
+ parsed.rlength.should.equal(33);
+ parsed.rneg.should.equal(true);
+ parsed.rbuf.toString('hex').should.equal('008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa');
+ parsed.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810');
+ parsed.slength.should.equal(32);
+ parsed.sneg.should.equal(false);
+ parsed.sbuf.toString('hex').should.equal('0993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
+ parsed.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130');
+ });
+
+ it('should parse this 69 byte signature', function() {
+ var sighex = '3043021f59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b50302202f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa';
+ var sig = new Buffer(sighex, 'hex');
+ var parsed = Key.parseDERsig(sig);
+ parsed.header.should.equal(0x30)
+ parsed.length.should.equal(67)
+ parsed.rlength.should.equal(31);
+ parsed.rneg.should.equal(false);
+ parsed.rbuf.toString('hex').should.equal('59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b503');
+ parsed.r.toString().should.equal('158826015856106182499128681792325160381907915189052224498209222621383996675');
+ parsed.slength.should.equal(32);
+ parsed.sneg.should.equal(false);
+ parsed.sbuf.toString('hex').should.equal('2f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa');
+ parsed.s.toString().should.equal('21463938592353267769710297084836796652964571266930856168996063301532842380538');
+ });
+
+ it('should parse this 68 byte signature', function() {
+ var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51';
+ var sig = new Buffer(sighex, 'hex');
+ var parsed = Key.parseDERsig(sig);
+ parsed.header.should.equal(0x30)
+ parsed.length.should.equal(66)
+ parsed.rlength.should.equal(30);
+ parsed.rneg.should.equal(false);
+ parsed.rbuf.toString('hex').should.equal('17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632');
+ parsed.r.toString().should.equal('164345250294671732127776123343329699648286106708464198588053542748255794');
+ parsed.slength.should.equal(32);
+ parsed.sneg.should.equal(false);
+ parsed.sbuf.toString('hex').should.equal('61bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51');
+ parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721');
+ });
+ });
+
+ describe('#rs2DER', function() {
+ it('should convert these known r and s values into a known signature', function() {
+ var r = new bignum('63173831029936981022572627018246571655303050627048489594159321588908385378810');
+ var s = new bignum('4331694221846364448463828256391194279133231453999942381442030409253074198130');
+ var der = Key.rs2DER(r, s);
+ der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
+ });
+ });
+
describe('generateSync', function() {
it('should not generate the same key twice in a row', function() {
var key1 = Key.generateSync();
@@ -171,6 +230,17 @@ describe('Key (ECKey)', function() {
key.private = bitcore.util.sha256('my fake private key');
key.regenerateSync();
+ it('should verify a signature created right here', function() {
+ var sig = key.signSync(hash);
+ key.verifySignatureSync(hash, sig).should.equal(true);
+ });
+
+ it('should fail on an invalid signature', function() {
+ var sig = key.signSync(hash);
+ sig[15] = !sig[15];
+ key.verifySignatureSync(hash, sig).should.equal(false);
+ });
+
it('should verify this example generated in the browser', function() {
var sig = new Buffer('304402200e02016b816e1b229559b6db97abc528438c64056a412eee2b7c41887ddf17010220ad9f1cd56fd382650286f51a842bba0a7664da164093db956b51f623b0d8e64f', 'hex');
key.verifySignatureSync(hash, sig).should.equal(true);
diff --git a/test/test.Point.js b/test/test.Point.js
index 722e49b..43f7a5b 100644
--- a/test/test.Point.js
+++ b/test/test.Point.js
@@ -184,7 +184,7 @@ describe('Point', function() {
describe('secp256k1 test vectors', function() {
//test vectors from http://crypto.stackexchange.com/questions/784/are-there-any-secp256k1-ecdsa-test-examples-available
- var G = bitcore.Curve.getG();
+ var G = Point.getG();
testdata.dataSecp256k1.nTimesG.forEach(function(val) {
it('should multiply n by G and get p from test data', function() {
var n = new Buffer(val.n, 'hex');