Added deterministic signing and added a public key class. Note that getPub should now be replaced with getPub().export('bytes')
This commit is contained in:
parent
02a8db111b
commit
30e8b572d6
7 changed files with 130 additions and 120 deletions
6
bitcoinjs-min.js
vendored
6
bitcoinjs-min.js
vendored
File diff suppressed because one or more lines are too long
26
src/bip32.js
26
src/bip32.js
|
@ -1,7 +1,8 @@
|
|||
var Script = require('./script'),
|
||||
util = require('./util'),
|
||||
conv = require('./convert'),
|
||||
key = require('./eckey'),
|
||||
ECKey = require('./eckey').ECKey,
|
||||
ECPubKey = require('./eckey').ECPubKey,
|
||||
base58 = require('./base58'),
|
||||
Crypto = require('./crypto-js/crypto'),
|
||||
ECPointFp = require('./jsbn/ec').ECPointFp,
|
||||
|
@ -48,7 +49,8 @@ BIP32key.prototype.deserialize = function(str) {
|
|||
fingerprint: bytes.slice(5,9),
|
||||
i: util.bytesToNum(bytes.slice(9,13).reverse()),
|
||||
chaincode: bytes.slice(13,45),
|
||||
key: new key(type == 'priv' ? bytes.slice(46,78).concat([1]) : bytes.slice(45,78))
|
||||
key: type == 'priv' ? new ECKey(bytes.slice(46,78).concat([1]),true)
|
||||
: new ECPubKey(bytes.slice(45,78))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -59,7 +61,7 @@ BIP32key.prototype.serialize = function() {
|
|||
util.numToBytes(this.i,4).reverse(),
|
||||
this.chaincode,
|
||||
this.type == 'priv' ? [0].concat(this.key.export('bytes').slice(0,32))
|
||||
: this.key)
|
||||
: this.key.export('bytes'))
|
||||
var checksum = Crypto.SHA256(Crypto.SHA256(bytes,{asBytes: true}), {asBytes: true})
|
||||
.slice(0,4)
|
||||
return base58.encode(bytes.concat(checksum))
|
||||
|
@ -69,9 +71,9 @@ BIP32key.prototype.ckd = function(i) {
|
|||
var priv, pub, newkey, fingerprint, blob, I;
|
||||
if (this.type == 'priv') {
|
||||
priv = this.key.export('bytes')
|
||||
pub = this.key.getPub()
|
||||
pub = this.key.getPub().export('bytes')
|
||||
}
|
||||
else pub = this.key
|
||||
else pub = this.key.export('bytes')
|
||||
|
||||
if (i >= 2147483648) {
|
||||
if (this.priv) throw new Error("Can't do private derivation on public key!")
|
||||
|
@ -82,16 +84,12 @@ BIP32key.prototype.ckd = function(i) {
|
|||
I = Crypto.HMAC(Crypto.SHA512,blob,this.chaincode,{ asBytes: true })
|
||||
|
||||
if (this.type == 'priv') {
|
||||
Ikey = Bitcoin.BigInteger.fromByteArrayUnsigned(I.slice(0,32))
|
||||
newkey = new key(this.key.priv.add(Ikey))
|
||||
newkey.compressed = true
|
||||
fingerprint = util.sha256ripe160(this.key.getPub()).slice(0,4)
|
||||
newkey = this.key.add(ECKey(I.slice(0,32).concat([1])))
|
||||
fingerprint = util.sha256ripe160(this.key.getPub().export('bytes')).slice(0,4)
|
||||
}
|
||||
else {
|
||||
newkey = ECPointFp.decodeFrom(ecparams.getCurve(),this.key)
|
||||
.add(new key(I.slice(0,32).concat([1])).getPubPoint())
|
||||
.getEncoded(true);
|
||||
fingerprint = util.sha256ripe160(this.key).slice(0,4)
|
||||
newkey = this.key.add(ECKey(I.slice(0,32).concat([1])).getPub());
|
||||
fingerprint = util.sha256ripe160(this.key.export('bytes')).slice(0,4)
|
||||
}
|
||||
return new BIP32key({
|
||||
vbytes: this.vbytes,
|
||||
|
@ -130,7 +128,7 @@ BIP32key.prototype.fromMasterKey = function(seed) {
|
|||
fingerprint: [0,0,0,0],
|
||||
i: 0,
|
||||
chaincode: I.slice(32),
|
||||
key: new key(I.slice(0,32).concat([1]))
|
||||
key: new ECKey(I.slice(0,32).concat([1]),true)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
30
src/ecdsa.js
30
src/ecdsa.js
|
@ -2,6 +2,7 @@ var sec = require('./jsbn/sec');
|
|||
var util = require('./util');
|
||||
var SecureRandom = require('./jsbn/rng');
|
||||
var BigInteger = require('./jsbn/jsbn');
|
||||
var conv = require('./convert')
|
||||
|
||||
var ECPointFp = require('./jsbn/ec').ECPointFp;
|
||||
|
||||
|
@ -36,6 +37,19 @@ function implShamirsTrick(P, k, Q, l)
|
|||
return R;
|
||||
};
|
||||
|
||||
function deterministicGenerateK(hash,key) {
|
||||
var v = [];
|
||||
var k = [];
|
||||
for (var i = 0;i < 32;i++) v.push(1);
|
||||
for (var i = 0;i < 32;i++) k.push(0);
|
||||
k = Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v.concat([0]).concat(key).concat(hash),k,{ asBytes: true })
|
||||
v = Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{ asBytes: true })
|
||||
k = Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v.concat([1]).concat(key).concat(hash),k,{ asBytes: true })
|
||||
v = Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{ asBytes: true })
|
||||
v = Bitcoin.Crypto.HMAC(Bitcoin.Crypto.SHA256,v,k,{ asBytes: true })
|
||||
return Bitcoin.BigInteger.fromByteArrayUnsigned(v);
|
||||
}
|
||||
|
||||
var ECDSA = {
|
||||
getBigRandom: function (limit) {
|
||||
return new BigInteger(limit.bitLength(), rng)
|
||||
|
@ -48,12 +62,10 @@ var ECDSA = {
|
|||
var n = ecparams.getN();
|
||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||
|
||||
do {
|
||||
var k = ECDSA.getBigRandom(n);
|
||||
var G = ecparams.getG();
|
||||
var Q = G.multiply(k);
|
||||
var r = Q.getX().toBigInteger().mod(n);
|
||||
} while (r.compareTo(BigInteger.ZERO) <= 0);
|
||||
var k = deterministicGenerateK(hash,priv.toByteArrayUnsigned())
|
||||
var G = ecparams.getG();
|
||||
var Q = G.multiply(k);
|
||||
var r = Q.getX().toBigInteger().mod(n);
|
||||
|
||||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
||||
|
||||
|
@ -258,10 +270,8 @@ var ECDSA = {
|
|||
|
||||
// TODO (shtylman) this is stupid because this file and eckey
|
||||
// have circular dependencies
|
||||
var ECKey = require('./eckey');
|
||||
var pubKey = ECKey();
|
||||
pubKey.pub = Q;
|
||||
return pubKey;
|
||||
var ECPubKey = require('./eckey').ECPubKey;
|
||||
return ECPubKey(Q);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
176
src/eckey.js
176
src/eckey.js
|
@ -11,70 +11,46 @@ var ECPointFp = require('./jsbn/ec').ECPointFp;
|
|||
var ecparams = sec("secp256k1");
|
||||
|
||||
// input can be nothing, array of bytes, hex string, or base58 string
|
||||
var ECKey = function (input) {
|
||||
if (!(this instanceof ECKey)) {
|
||||
return new ECKey(input);
|
||||
}
|
||||
|
||||
this.compressed = !!ECKey.compressByDefault;
|
||||
|
||||
var ECKey = function (input,compressed) {
|
||||
if (!(this instanceof ECKey)) { return new ECKey(input); }
|
||||
if (!input) {
|
||||
// Generate new key
|
||||
var n = ecparams.getN();
|
||||
this.priv = ecdsa.getBigRandom(n);
|
||||
this.compressed = compressed || false;
|
||||
}
|
||||
else this.import(input)
|
||||
else this.import(input,compressed)
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether public keys should be returned compressed by default.
|
||||
*/
|
||||
ECKey.compressByDefault = false;
|
||||
ECKey.prototype.import = function (input,compressed) {
|
||||
function has(li,v) { return li.indexOf(v) >= 0 }
|
||||
function fromBin(x) { return BigInteger.fromByteArrayUnsigned(x) }
|
||||
this.priv =
|
||||
input instanceof ECKey ? input.priv
|
||||
: input instanceof BigInteger ? input.mod(ecparams.getN())
|
||||
: util.isArray(input) ? fromBin(input.slice(0,32))
|
||||
: typeof input != "string" ? null
|
||||
: input.length == 51 && input[0] == '5' ? fromBin(base58.checkDecode(input))
|
||||
: input.length == 52 && has('LK',input[0]) ? fromBin(base58.checkDecode(input))
|
||||
: has([64,65],input.length) ? fromBin(conv.hexToBytes(input.slice(0,64)))
|
||||
: null
|
||||
|
||||
/**
|
||||
* Set whether the public key should be returned compressed or not.
|
||||
*/
|
||||
ECKey.prototype.setCompressed = function (v) {
|
||||
this.compressed = !!v;
|
||||
this.compressed =
|
||||
arguments.length > 1 ? compressed
|
||||
: input instanceof ECKey ? input.compressed
|
||||
: input instanceof BigInteger ? false
|
||||
: util.isArray(input) ? false
|
||||
: typeof input != "string" ? null
|
||||
: input.length == 51 && input[0] == '5' ? false
|
||||
: input.length == 52 && has('LK',input[0]) ? true
|
||||
: input.length == 64 ? false
|
||||
: input.length == 65 ? true
|
||||
: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Return public key in DER encoding.
|
||||
*/
|
||||
ECKey.prototype.getPub = function () {
|
||||
return this.getPubPoint().getEncoded(this.compressed);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return public point as ECPoint object.
|
||||
*/
|
||||
ECKey.prototype.getPubPoint = function () {
|
||||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
|
||||
return this.pub;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the pubKeyHash for this key.
|
||||
*
|
||||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as
|
||||
* a byte array.
|
||||
*/
|
||||
ECKey.prototype.getPubKeyHash = function () {
|
||||
if (this.pubKeyHash) return this.pubKeyHash;
|
||||
return this.pubKeyHash = util.sha256ripe160(this.getPub());
|
||||
};
|
||||
|
||||
ECKey.prototype.getBitcoinAddress = function (version) {
|
||||
var hash = this.getPubKeyHash();
|
||||
var addr = new Address(hash,version);
|
||||
return addr;
|
||||
};
|
||||
|
||||
ECKey.prototype.setPub = function (pub) {
|
||||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
|
||||
this.compressed = (pub[0] < 4)
|
||||
return this
|
||||
};
|
||||
ECKey.prototype.getPub = function() {
|
||||
return ECPubKey(ecparams.getG().multiply(this.priv),this.compressed)
|
||||
}
|
||||
|
||||
ECKey.prototype.export = function (format) {
|
||||
var bytes = this.priv.toByteArrayUnsigned();
|
||||
|
@ -86,12 +62,66 @@ ECKey.prototype.export = function (format) {
|
|||
: format === "hex" ? conv.bytesToHex(bytes)
|
||||
: bytes
|
||||
};
|
||||
ECKey.prototype.getExportedPrivateKey = ECKey.prototype.export;
|
||||
|
||||
ECKey.prototype.toString = function (format) {
|
||||
return ''+this.export(format)
|
||||
}
|
||||
|
||||
ECKey.prototype.getBitcoinAddress = function(v) {
|
||||
return this.getPub().getBitcoinAddress(v)
|
||||
}
|
||||
|
||||
ECKey.prototype.add = function(key) {
|
||||
return ECKey(this.priv.add(ECKey(key).priv),this.compressed)
|
||||
}
|
||||
|
||||
ECKey.prototype.multiply = function(key) {
|
||||
return ECKey(this.priv.multiply(ECKey(key).priv),this.compressed)
|
||||
}
|
||||
|
||||
var ECPubKey = function(input,compressed) {
|
||||
|
||||
if (!(this instanceof ECPubKey)) { return new ECPubKey(input,compressed); }
|
||||
|
||||
var decode = function(x) { return ECPointFp.decodeFrom(ecparams.getCurve(), x) }
|
||||
this.pub =
|
||||
input instanceof ECPointFp ? input
|
||||
: input instanceof ECKey ? ecparams.getG().multiply(input.priv)
|
||||
: input instanceof ECPubKey ? input.pub
|
||||
: typeof input == "string" ? decode(conv.hexToBytes(input))
|
||||
: util.isArray(input) ? decode(input)
|
||||
: ecparams.getG().multiply(ecdsa.getBigRandom(ecparams.getN()))
|
||||
|
||||
this.compressed =
|
||||
arguments.length > 1 ? compressed
|
||||
: input instanceof ECPointFp ? input.compressed
|
||||
: input instanceof ECPubKey ? input.compressed
|
||||
: (this.pub[0] < 4)
|
||||
|
||||
}
|
||||
|
||||
ECPubKey.prototype.add = function(key) {
|
||||
return ECPubKey(this.pub.add(ECPubKey(key).pub),this.compressed)
|
||||
}
|
||||
|
||||
ECPubKey.prototype.multiply = function(key) {
|
||||
return ECPubKey(this.pub.multiply(ECKey(key).priv),this.compressed)
|
||||
}
|
||||
|
||||
ECPubKey.prototype.export = function(formt) {
|
||||
var o = this.pub.getEncoded(this.compressed)
|
||||
return formt == 'hex' ? conv.bytesToHex(o) : o;
|
||||
}
|
||||
|
||||
ECPubKey.prototype.toString = function (format) {
|
||||
return ''+this.export(format)
|
||||
}
|
||||
|
||||
ECPubKey.prototype.getBitcoinAddress = function(v) {
|
||||
return new Address(util.sha256ripe160(this.export()),version);
|
||||
}
|
||||
|
||||
|
||||
ECKey.prototype.sign = function (hash) {
|
||||
return ecdsa.sign(hash, this.priv);
|
||||
};
|
||||
|
@ -103,38 +133,6 @@ ECKey.prototype.verify = function (hash, sig) {
|
|||
/**
|
||||
* Parse an exported private key contained in a string.
|
||||
*/
|
||||
ECKey.prototype.import = function (input) {
|
||||
if (input instanceof ECKey) {
|
||||
this.priv = input.priv;
|
||||
this.compressed = input.compressed;
|
||||
}
|
||||
else if (input instanceof BigInteger) {
|
||||
// Input is a private key value
|
||||
this.priv = input;
|
||||
this.compressed = ECKey.compressByDefault;
|
||||
}
|
||||
else if (util.isArray(input)) {
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(input.slice(0,32));
|
||||
this.compressed = (input.length == 33);
|
||||
}
|
||||
else if ("string" == typeof input) {
|
||||
if (input.length == 51 && input[0] == '5') {
|
||||
// Base58 encoded private key
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(base58.checkDecode(input));
|
||||
this.compressed = false;
|
||||
}
|
||||
else if (input.length == 52 && (input[0] === 'K' || input[0] === 'L')) {
|
||||
// Base58 encoded private key
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(base58.checkDecode(input));
|
||||
this.compressed = true;
|
||||
}
|
||||
else if (input.length >= 64) {
|
||||
// hex string?
|
||||
this.priv = BigInteger.fromByteArrayUnsigned(conv.hexToBytes(input.slice(0,64)));
|
||||
this.compressed = (input.length == 66)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ECKey;
|
||||
|
||||
module.exports = { ECKey: ECKey, ECPubKey: ECPubKey };
|
||||
|
|
|
@ -22,9 +22,13 @@ var endian = function (n) {
|
|||
return n;
|
||||
}
|
||||
|
||||
var Key = require('./eckey');
|
||||
|
||||
module.exports = {
|
||||
Address: require('./address'),
|
||||
Key: require('./eckey'),
|
||||
Key: Key.ECKey,
|
||||
ECKey: Key.ECKey,
|
||||
ECPubKey: Key.ECPubKey,
|
||||
Message: require('./message'),
|
||||
BigInteger: require('./jsbn/jsbn'),
|
||||
Crypto: require('./crypto-js/crypto'),
|
||||
|
|
|
@ -4,7 +4,7 @@ var util = require('./util');
|
|||
var conv = require('./convert');
|
||||
var Crypto = require('./crypto-js/crypto');
|
||||
var Wallet = require('./wallet');
|
||||
var ECKey = require('./eckey');
|
||||
var ECKey = require('./eckey').ECKey;
|
||||
var ECDSA = require('./ecdsa');
|
||||
var Address = require('./address');
|
||||
|
||||
|
@ -467,7 +467,7 @@ Transaction.deserialize = function(buffer) {
|
|||
Transaction.prototype.sign = function(index, key, type) {
|
||||
type = type || SIGHASH_ALL;
|
||||
key = new ECKey(key);
|
||||
var pub = key.getPub(),
|
||||
var pub = key.getPub().export('bytes'),
|
||||
hash160 = util.sha256ripe160(pub),
|
||||
script = Script.createOutputScript(new Address(hash160)),
|
||||
hash = this.hashTransactionForSignature( script, index, type),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
var Script = require('./script');
|
||||
var ECKey = require('./eckey');
|
||||
var ECKey = require('./eckey').ECKey;
|
||||
var conv = require('./convert');
|
||||
var util = require('./util');
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue