add wrapper for elliptic vs secp256k1. see #52.
This commit is contained in:
parent
e7478372cf
commit
10804959d5
15
lib/bcoin.js
15
lib/bcoin.js
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
var bcoin = exports;
|
||||
var assert = require('assert');
|
||||
var inherits = require('inherits');
|
||||
var elliptic = require('elliptic');
|
||||
var bn = require('bn.js');
|
||||
@ -27,6 +28,12 @@ if (!bcoin.isBrowser) {
|
||||
bcoin.crypto = require('cry' + 'pto');
|
||||
bcoin.net = require('n' + 'et');
|
||||
bcoin.cp = require('child_' + 'process');
|
||||
try {
|
||||
bcoin.secp256k1 = require('secp' + '256k1');
|
||||
} catch (e) {
|
||||
utils.debug('Warning secp256k1 not found.'
|
||||
+ ' Full block validation will be slow.');
|
||||
}
|
||||
}
|
||||
|
||||
if (bcoin.fs) {
|
||||
@ -44,16 +51,14 @@ bcoin.hash = hash;
|
||||
|
||||
bcoin.ecdsa = elliptic.ec('secp256k1');
|
||||
|
||||
if (bcoin.ecdsa.signature)
|
||||
throw new Error;
|
||||
|
||||
if (bcoin.ecdsa.keypair)
|
||||
throw new Error;
|
||||
assert(!bcoin.ecdsa.signature);
|
||||
assert(!bcoin.ecdsa.keypair);
|
||||
|
||||
bcoin.ecdsa.signature = require('elliptic/lib/elliptic/ec/signature');
|
||||
bcoin.ecdsa.keypair = require('elliptic/lib/elliptic/ec/key');
|
||||
|
||||
bcoin.utils = require('./bcoin/utils');
|
||||
bcoin.ec = require('./bcoin/ec');
|
||||
bcoin.lru = require('./bcoin/lru');
|
||||
bcoin.protocol = require('./bcoin/protocol');
|
||||
bcoin.bloom = require('./bcoin/bloom');
|
||||
|
||||
200
lib/bcoin/ec.js
Normal file
200
lib/bcoin/ec.js
Normal file
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* ec.js - ecdsa wrapper for secp256k1 and elliptic
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* https://github.com/indutny/bcoin
|
||||
*/
|
||||
|
||||
var bcoin = require('../bcoin');
|
||||
var bn = require('bn.js');
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
var ec = exports;
|
||||
|
||||
/**
|
||||
* EC
|
||||
*/
|
||||
|
||||
ec.generate = function generate(options) {
|
||||
var priv, pub;
|
||||
|
||||
if (bcoin.secp256k1 && bcoin.crypto) {
|
||||
do {
|
||||
priv = bcoin.crypto.randomBytes(32);
|
||||
} while (!bcoin.secp256k1.privateKeyVerify(priv));
|
||||
pub = bcoin.secp256k1.publicKeyCreate(priv, true);
|
||||
priv = utils.toArray(priv);
|
||||
pub = utils.toArray(pub);
|
||||
return bcoin.ecdsa.keyPair({ priv: priv, pub: pub });
|
||||
}
|
||||
|
||||
return bcoin.ecdsa.genKeyPair(options);
|
||||
};
|
||||
|
||||
ec.verify = function verify(msg, sig, key, historical) {
|
||||
if (key.getPublic)
|
||||
key = key.getPublic(true, 'array');
|
||||
|
||||
if (!utils.isBuffer(sig))
|
||||
return false;
|
||||
|
||||
if (sig.length === 0)
|
||||
return false;
|
||||
|
||||
// Attempt to normalize the signature
|
||||
// length before passing to elliptic.
|
||||
// Note: We only do this for historical data!
|
||||
// https://github.com/indutny/elliptic/issues/78
|
||||
if (historical)
|
||||
sig = bcoin.ec.normalizeLength(sig);
|
||||
|
||||
try {
|
||||
if (bcoin.secp256k1) {
|
||||
// secp256k1 fails on low s values. This is
|
||||
// bad for verifying historical data.
|
||||
if (historical)
|
||||
sig = bcoin.ec.toLowS(sig);
|
||||
|
||||
msg = new Buffer(msg);
|
||||
sig = new Buffer(sig);
|
||||
key = new Buffer(key)
|
||||
|
||||
// Import from DER.
|
||||
sig = bcoin.secp256k1.signatureImport(sig);
|
||||
|
||||
// This is supposed to lower the S value
|
||||
// but it doesn't seem to work.
|
||||
// if (historical)
|
||||
// sig = bcoin.secp256k1.signatureNormalize(sig);
|
||||
|
||||
return bcoin.secp256k1.verify(msg, sig, key);
|
||||
}
|
||||
return bcoin.ecdsa.verify(msg, sig, key);
|
||||
} catch (e) {
|
||||
utils.debug('Elliptic threw during verification:');
|
||||
utils.debug(e.stack + '');
|
||||
utils.debug({
|
||||
msg: utils.toHex(msg),
|
||||
sig: utils.toHex(sig),
|
||||
key: utils.toHex(key)
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
ec.sign = function sign(msg, key) {
|
||||
var sig;
|
||||
|
||||
if (bcoin.secp256k1) {
|
||||
msg = new Buffer(msg);
|
||||
key = new Buffer(key.getPrivate().toArray('be', 32));
|
||||
|
||||
// Sign message
|
||||
sig = bcoin.secp256k1.sign(msg, key);
|
||||
|
||||
// Ensure low S value
|
||||
sig = bcoin.secp256k1.signatureNormalize(sig.signature);
|
||||
|
||||
// Convert to DER array
|
||||
sig = bcoin.secp256k1.signatureExport(sig);
|
||||
|
||||
sig = utils.toArray(sig);
|
||||
} else {
|
||||
// Sign message and ensure low S value
|
||||
sig = bcoin.ecdsa.sign(msg, key.priv, { canonical: true });
|
||||
|
||||
// Convert to DER array
|
||||
sig = sig.toDER();
|
||||
}
|
||||
|
||||
return sig;
|
||||
};
|
||||
|
||||
ec.normalizeLength = function normalizeLength(sig) {
|
||||
var data, p, len, rlen, slen;
|
||||
|
||||
data = sig.slice();
|
||||
p = { place: 0 };
|
||||
|
||||
if (data[p.place++] !== 0x30)
|
||||
return sig;
|
||||
|
||||
len = getLength(data, p);
|
||||
|
||||
if (data.length > len + p.place)
|
||||
data = data.slice(0, len + p.place);
|
||||
|
||||
if (data[p.place++] !== 0x02)
|
||||
return sig;
|
||||
|
||||
rlen = getLength(data, p);
|
||||
p.place += rlen;
|
||||
|
||||
if (data[p.place++] !== 0x02)
|
||||
return sig;
|
||||
|
||||
slen = getLength(data, p);
|
||||
if (data.length > slen + p.place)
|
||||
data = data.slice(0, slen + p.place);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
function getLength(buf, p) {
|
||||
var initial = buf[p.place++];
|
||||
if (!(initial & 0x80)) {
|
||||
return initial;
|
||||
}
|
||||
var octetLen = initial & 0xf;
|
||||
var val = 0;
|
||||
for (var i = 0, off = p.place; i < octetLen; i++, off++) {
|
||||
val <<= 8;
|
||||
val |= buf[off];
|
||||
}
|
||||
p.place = off;
|
||||
return val;
|
||||
}
|
||||
|
||||
ec.isLowS = function isLowS(sig) {
|
||||
if (!sig.s) {
|
||||
if (!utils.isBuffer(sig))
|
||||
return false;
|
||||
|
||||
try {
|
||||
sig = new bcoin.ecdsa.signature(sig);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Technically a negative S value is low,
|
||||
// but we don't want to ever use negative
|
||||
// S values in bitcoin.
|
||||
if (sig.s.cmpn(0) <= 0)
|
||||
return false;
|
||||
|
||||
// If S is greater than half the order,
|
||||
// it's too high.
|
||||
if (sig.s.cmp(bcoin.ecdsa.nh) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
ec.toLowS = function toLowS(sig) {
|
||||
if (!sig.s) {
|
||||
assert(utils.isBuffer(sig));
|
||||
|
||||
try {
|
||||
sig = new bcoin.ecdsa.signature(sig);
|
||||
} catch (e) {
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
|
||||
// If S is greater than half the order,
|
||||
// it's too high.
|
||||
if (sig.s.cmp(bcoin.ecdsa.nh) > 0)
|
||||
sig.s = bcoin.ecdsa.n.sub(sig.s);
|
||||
|
||||
return sig.toDER();
|
||||
};
|
||||
@ -565,7 +565,7 @@ HDPrivateKey.prototype._normalize = function _normalize(data) {
|
||||
// private key = 32 bytes
|
||||
if (data.privateKey) {
|
||||
if (data.privateKey.getPrivate)
|
||||
data.privateKey = data.privateKey.getPrivate().toArray();
|
||||
data.privateKey = data.privateKey.getPrivate().toArray('be', 32);
|
||||
else if (typeof data.privateKey === 'string')
|
||||
data.privateKey = utils.toBuffer(data.privateKey);
|
||||
}
|
||||
@ -616,12 +616,12 @@ HDPrivateKey.prototype._seed = function _seed(seed) {
|
||||
|
||||
HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) {
|
||||
if (!privateKey)
|
||||
privateKey = bcoin.ecdsa.genKeyPair().getPrivate().toArray();
|
||||
privateKey = bcoin.ec.generate().getPrivate().toArray('be', 32);
|
||||
|
||||
if (utils.isHex(privateKey))
|
||||
privateKey = utils.toArray(privateKey, 'hex');
|
||||
else if (utils.isBase58(privateKey))
|
||||
privateKey = bcoin.keypair.fromSecret(privateKey).getPrivate().toArray();
|
||||
privateKey = bcoin.keypair.fromSecret(privateKey).getPrivate().toArray('be', 32);
|
||||
|
||||
if (!entropy)
|
||||
entropy = elliptic.rand(32);
|
||||
@ -691,7 +691,7 @@ HDPrivateKey.prototype._build = function _build(data) {
|
||||
xprivkey = utils.toBase58(sequence);
|
||||
|
||||
pair = bcoin.ecdsa.keyPair({ priv: data.privateKey });
|
||||
privateKey = pair.getPrivate().toArray();
|
||||
privateKey = pair.getPrivate().toArray('be', 32);
|
||||
publicKey = pair.getPublic(true, 'array');
|
||||
|
||||
size = constants.hd.parentFingerPrintSize;
|
||||
|
||||
@ -64,7 +64,7 @@ function KeyPair(options) {
|
||||
pub: options.publicKey
|
||||
});
|
||||
} else {
|
||||
this.pair = bcoin.ecdsa.genKeyPair({
|
||||
this.pair = bcoin.ec.generate({
|
||||
pers: options.personalization,
|
||||
entropy: options.entropy
|
||||
});
|
||||
@ -113,7 +113,7 @@ KeyPair.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||
if (!privateKey)
|
||||
return;
|
||||
|
||||
privateKey = privateKey.toArray();
|
||||
privateKey = privateKey.toArray('be', 32);
|
||||
|
||||
if (enc === 'base58')
|
||||
return KeyPair.toSecret(privateKey, this.compressed);
|
||||
|
||||
@ -234,7 +234,7 @@ regtest.seeds = [
|
||||
|
||||
regtest.port = 18444;
|
||||
|
||||
regtest._alertKey = bcoin.ecdsa.genKeyPair();
|
||||
regtest._alertKey = bcoin.ec.generate();
|
||||
regtest.alertKey = regtest._alertKey.getPublic(true, 'array');
|
||||
|
||||
regtest.checkpoints = {};
|
||||
|
||||
@ -293,20 +293,17 @@ script.concat = function concat(scripts) {
|
||||
};
|
||||
|
||||
script.checksig = function checksig(msg, sig, key, flags) {
|
||||
var historical = false;
|
||||
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (key.getPublic)
|
||||
key = key.getPublic();
|
||||
|
||||
if (!utils.isBuffer(sig))
|
||||
return false;
|
||||
|
||||
if (sig.length === 0)
|
||||
return false;
|
||||
|
||||
sig = sig.slice(0, -1);
|
||||
|
||||
// Attempt to normalize the signature
|
||||
// length before passing to elliptic.
|
||||
// Note: We only do this for historical data!
|
||||
@ -314,42 +311,14 @@ script.checksig = function checksig(msg, sig, key, flags) {
|
||||
if (!((flags & constants.flags.VERIFY_DERSIG)
|
||||
|| (flags & constants.flags.VERIFY_LOW_S)
|
||||
|| (flags & constants.flags.VERIFY_STRICTENC))) {
|
||||
sig = script.normalizeDER(sig);
|
||||
historical = true;
|
||||
}
|
||||
|
||||
// Use a try catch in case there are
|
||||
// any uncaught errors for bad inputs in verify().
|
||||
try {
|
||||
return bcoin.ecdsa.verify(msg, sig, key);
|
||||
} catch (e) {
|
||||
utils.debug('Elliptic threw during verification:');
|
||||
utils.debug(e.stack + '');
|
||||
utils.debug({
|
||||
msg: utils.toHex(msg),
|
||||
sig: utils.toHex(sig),
|
||||
key: utils.toHex(key)
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return bcoin.ec.verify(msg, sig.slice(0, -1), key, historical);
|
||||
};
|
||||
|
||||
script.sign = function sign(msg, key, type) {
|
||||
var half = bcoin.ecdsa.n.ushrn(1);
|
||||
var sig = bcoin.ecdsa.sign(msg, key.priv);
|
||||
|
||||
// Elliptic shouldn't be generating
|
||||
// negative S values.
|
||||
assert(sig.s.cmpn(0) > 0);
|
||||
|
||||
// If S value is bigger than half of the
|
||||
// order of the curve, it's too damn big.
|
||||
// Subtract from the `n` order to make
|
||||
// it smaller.
|
||||
if (sig.s.cmp(half) > 0)
|
||||
sig.s = bcoin.ecdsa.n.sub(sig.s);
|
||||
|
||||
// Convert to DER array
|
||||
sig = sig.toDER();
|
||||
var sig = bcoin.ec.sign(msg, key);
|
||||
|
||||
// Add the sighash type as a single byte
|
||||
// to the signature.
|
||||
@ -358,56 +327,6 @@ script.sign = function sign(msg, key, type) {
|
||||
return sig;
|
||||
};
|
||||
|
||||
script.normalizeDER = function normalizeDER(signature) {
|
||||
var data, p, len, rlen, slen;
|
||||
|
||||
if (Buffer.isBuffer(signature))
|
||||
signature = Array.prototype.slice.call(signature);
|
||||
else if (typeof signature === 'string')
|
||||
signature = utils.toArray(signature, 'hex');
|
||||
|
||||
data = signature.slice();
|
||||
p = { place: 0 };
|
||||
|
||||
if (data[p.place++] !== 0x30)
|
||||
return signature;
|
||||
|
||||
len = getLength(data, p);
|
||||
|
||||
if (data.length > len + p.place)
|
||||
data = data.slice(0, len + p.place);
|
||||
|
||||
if (data[p.place++] !== 0x02)
|
||||
return signature;
|
||||
|
||||
rlen = getLength(data, p);
|
||||
p.place += rlen;
|
||||
|
||||
if (data[p.place++] !== 0x02)
|
||||
return signature;
|
||||
|
||||
slen = getLength(data, p);
|
||||
if (data.length > slen + p.place)
|
||||
data = data.slice(0, slen + p.place);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
function getLength(buf, p) {
|
||||
var initial = buf[p.place++];
|
||||
if (!(initial & 0x80)) {
|
||||
return initial;
|
||||
}
|
||||
var octetLen = initial & 0xf;
|
||||
var val = 0;
|
||||
for (var i = 0, off = p.place; i < octetLen; i++, off++) {
|
||||
val <<= 8;
|
||||
val |= buf[off];
|
||||
}
|
||||
p.place = off;
|
||||
return val;
|
||||
}
|
||||
|
||||
script._next = function _next(to, s, pc) {
|
||||
var depth = 0;
|
||||
var o;
|
||||
@ -2314,8 +2233,6 @@ script.isHashType = function isHashType(sig) {
|
||||
};
|
||||
|
||||
script.isLowDER = function isLowDER(sig) {
|
||||
var half = bcoin.ecdsa.n.ushrn(1);
|
||||
|
||||
if (!sig.s) {
|
||||
if (!utils.isBuffer(sig))
|
||||
return false;
|
||||
@ -2323,25 +2240,10 @@ script.isLowDER = function isLowDER(sig) {
|
||||
if (!script.isSignatureEncoding(sig))
|
||||
return false;
|
||||
|
||||
try {
|
||||
sig = new bcoin.ecdsa.signature(sig.slice(0, -1));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
sig = sig.slice(0, -1);
|
||||
}
|
||||
|
||||
// Technically a negative S value is low,
|
||||
// but we don't want to ever use negative
|
||||
// S values in bitcoin.
|
||||
if (sig.s.cmpn(0) <= 0)
|
||||
return false;
|
||||
|
||||
// If S is greater than half the order,
|
||||
// it's too high.
|
||||
if (sig.s.cmp(half) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return bcoin.ec.isLowS(sig);
|
||||
};
|
||||
|
||||
script.format = function format(s) {
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"bn.js": "4.6.3",
|
||||
"elliptic": "6.0.2",
|
||||
"hash.js": "1.0.3",
|
||||
"secp256k1": "3.0.0",
|
||||
"inherits": "2.0.1",
|
||||
"leveldown": "1.4.4",
|
||||
"level-js": "2.2.3",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user