add proper DER signature support to Key
both creating DER signature from the r and s values, and parsing a DER signature into the r, s, and other properties.
This commit is contained in:
parent
b8b500e109
commit
5f6d02f5de
10
lib/Key.js
10
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;
|
||||||
|
|
|
@ -2,12 +2,19 @@ var SecureRandom = require('../SecureRandom');
|
||||||
var bignum = require('bignum');
|
var bignum = require('bignum');
|
||||||
var elliptic = require('elliptic');
|
var elliptic = require('elliptic');
|
||||||
var Point = require('./Point');
|
var Point = require('./Point');
|
||||||
|
var CommonKey = require('../common/Key');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
var Key = function() {
|
var Key = function() {
|
||||||
this._pub = null;
|
this._pub = null;
|
||||||
this._compressed = true; // default
|
this._compressed = true; // default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (var i in CommonKey) {
|
||||||
|
if (CommonKey.hasOwnProperty(i))
|
||||||
|
Key[i] = CommonKey[i];
|
||||||
|
}
|
||||||
|
|
||||||
var bufferToArray = Key.bufferToArray = function(buffer) {
|
var bufferToArray = Key.bufferToArray = function(buffer) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
|
|
||||||
|
@ -117,37 +124,18 @@ Key.prototype.signSync = function(hash) {
|
||||||
|
|
||||||
var sign = function(hash, priv) {
|
var sign = function(hash, priv) {
|
||||||
var d = priv;
|
var d = priv;
|
||||||
var n = ec.n;
|
var n = Point.getN();
|
||||||
var e = new bignum(hash);
|
var e = new bignum(hash);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var k = genk();
|
var k = genk();
|
||||||
var G = ec.g;
|
var G = Point.getG();
|
||||||
var Q = G.mul(k);
|
var Q = Point.multiply(G, k);
|
||||||
var r = Q.getX().mod(n);
|
var r = Q.x.mod(n);
|
||||||
var s = k.invm(n).mul(e.add(d.mul(r))).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);
|
} while (r.cmp(new bignum(0)) <= 0 || s.cmp(new bignum(0)) <= 0);
|
||||||
|
|
||||||
return serializeSig(r, s);
|
return {r: r, s: 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) {
|
if (!this.private) {
|
||||||
|
@ -158,9 +146,10 @@ Key.prototype.signSync = function(hash) {
|
||||||
throw new Error('Arg should be a 32 bytes hash buffer');
|
throw new Error('Arg should be a 32 bytes hash buffer');
|
||||||
}
|
}
|
||||||
var privnum = new bignum(this.private);
|
var privnum = new bignum(this.private);
|
||||||
var signature = sign(hash, privnum);
|
var sigrs = sign(hash, privnum);
|
||||||
|
var der = Key.rs2DER(sigrs.r, sigrs.s);
|
||||||
|
|
||||||
return new Buffer(signature);
|
return der;
|
||||||
};
|
};
|
||||||
|
|
||||||
Key.prototype.verifySignature = function(hash, sig, callback) {
|
Key.prototype.verifySignature = function(hash, sig, callback) {
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
var bignum = require('bignum');
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Key;
|
|
@ -6,6 +6,29 @@ var Point = function(x, y) {
|
||||||
this.y = 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
|
//convert the public key of a Key into a Point
|
||||||
Point.fromUncompressedPubKey = function(pubkey) {
|
Point.fromUncompressedPubKey = function(pubkey) {
|
||||||
var point = new Point();
|
var point = new Point();
|
||||||
|
|
|
@ -132,6 +132,33 @@ describe('Key (ECKey)', function() {
|
||||||
ret.should.equal(false);
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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() {
|
describe('generateSync', function() {
|
||||||
it('should not generate the same key twice in a row', function() {
|
it('should not generate the same key twice in a row', function() {
|
||||||
var key1 = Key.generateSync();
|
var key1 = Key.generateSync();
|
||||||
|
|
Loading…
Reference in New Issue