You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
4.3 KiB
166 lines
4.3 KiB
var bignum = require('bignum');
|
|
var Point = require('../Point');
|
|
var SecureRandom = require('../SecureRandom');
|
|
var bignum = require('bignum');
|
|
var elliptic = require('elliptic');
|
|
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.recoverPubKey = function(e, r, s, i) {
|
|
var bnjs = require('bn.js');
|
|
|
|
if (i>3 || i<0)
|
|
throw new Error('Recovery param is more than two bits');
|
|
|
|
e = new bnjs(e.toBuffer({size: 32}));
|
|
r = new bnjs(r.toBuffer({size: 32}));
|
|
s = new bnjs(s.toBuffer({size: 32}));
|
|
|
|
var ec = elliptic.curves.secp256k1;
|
|
|
|
// A set LSB signifies that the y-coordinate is odd
|
|
var isYOdd = i & 1;
|
|
|
|
// The more significant bit specifies whether we should use the
|
|
// first or second candidate key.
|
|
var isSecondKey = i >> 1;
|
|
|
|
var n = ec.curve.n;
|
|
var G = ec.curve.g;
|
|
|
|
// 1.1 Let x = r + jn
|
|
var x = isSecondKey ? r.add(n) : r;
|
|
var R = ec.curve.pointFromX(isYOdd, x.toArray());
|
|
|
|
// 1.4 Check that nR is at infinity
|
|
var nR = R.mul(n);
|
|
|
|
//TODO: check that nR is not infinity
|
|
//assert(curve.isInfinity(nR), 'nR is not a valid curve point');
|
|
|
|
// Compute -e from e
|
|
var eNeg = e.neg().mod(n);
|
|
|
|
// 1.6.1 Compute Q = r^-1 (sR - eG)
|
|
// Q = r^-1 (sR + -eG)
|
|
var rInv = r.invm(n);
|
|
|
|
//var Q = R.multiplyTwo(s, G, eNeg).mul(rInv);
|
|
var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv);
|
|
ec.curve.validate(Q);
|
|
var pubkey = new Point();
|
|
pubkey.x = bignum(Q.x.toString());
|
|
pubkey.y = bignum(Q.y.toString());
|
|
|
|
return pubkey;
|
|
}
|
|
|
|
/**
|
|
* Calculate pubkey extraction parameter.
|
|
*
|
|
* When extracting a pubkey from a signature, we have to
|
|
* distinguish four different cases. Rather than putting this
|
|
* burden on the verifier, Bitcoin includes a 2-bit value with the
|
|
* signature.
|
|
*
|
|
* This function simply tries all four cases and returns the value
|
|
* that resulted in a successful pubkey recovery.
|
|
*/
|
|
Key.calcPubKeyRecoveryParam = function(e, r, s, Q) {
|
|
for (var i = 0; i < 4; i++) {
|
|
var Qprime = Key.recoverPubKey(e, r, s, i);
|
|
|
|
// 1.6.2 Verify Q
|
|
if (Qprime.x.toString() == Q.x.toString() && Qprime.y.toString() == Q.y.toString()) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw new Error('Unable to find valid recovery factor');
|
|
}
|
|
|
|
Key.genk = function() {
|
|
//TODO: account for when >= n
|
|
var k = new bignum(SecureRandom.getRandomBuffer(32))
|
|
return k;
|
|
};
|
|
|
|
module.exports = Key;
|
|
|