bitcore-lib-zcash/lib/common/Key.js

167 lines
4.3 KiB
JavaScript

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;