Ryan X. Charles
11 years ago
17 changed files with 973 additions and 310 deletions
@ -1,349 +1,347 @@ |
|||||
var BITCOIN_MAINNET_PUBLIC = 0x0488b21e; |
var imports = require('soop').imports(); |
||||
var BITCOIN_MAINNET_PRIVATE = 0x0488ade4; |
var base58 = imports.base58 || require('base58-native').base58; |
||||
var BITCOIN_TESTNET_PUBLIC = 0x043587cf; |
var coinUtil = imports.coinUtil || require('./util/util'); |
||||
var BITCOIN_TESTNET_PRIVATE = 0x04358394; |
var Key = imports.Key || require('./Key'); |
||||
var DOGECOIN_MAINNET_PUBLIC = 0x02facafd; |
var Point = imports.Point || require('./Point'); |
||||
var DOGECOIN_MAINNET_PRIVATE = 0x02fac398; |
var bignum = imports.bignum || require('bignum'); |
||||
var DOGECOIN_TESTNET_PUBLIC = 0x0432a9a8; |
var crypto = require('crypto'); |
||||
var DOGECOIN_TESTNET_PRIVATE = 0x0432a243; |
var networks = require('./networks'); |
||||
var LITECOIN_MAINNET_PUBLIC = 0x019da462; |
|
||||
var LITECOIN_MAINNET_PRIVATE = 0x019d9cfe; |
var secp256k1_n = new bignum("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); |
||||
var LITECOIN_TESTNET_PUBLIC = 0x0436f6e1; |
var secp256k1_Gx = new bignum("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16); |
||||
var LITECOIN_TESTNET_PRIVATE = 0x0436ef7d; |
|
||||
|
|
||||
var BIP32 = function(bytes) { |
var BIP32 = function(bytes) { |
||||
// decode base58
|
if (bytes == 'mainnet' || bytes == 'livenet') |
||||
if( typeof bytes === "string" ) { |
this.version = networks['livenet'].bip32private; |
||||
var decoded = Bitcoin.Base58.decode(bytes); |
else if (bytes == 'testnet') |
||||
if( decoded.length != 82 ) throw new Error("Not enough data"); |
this.version = networks['testnet'].bip32private; |
||||
var checksum = decoded.slice(78, 82); |
|
||||
bytes = decoded.slice(0, 78); |
if (bytes == 'mainnet' || bytes == 'livenet' || bytes == 'testnet') { |
||||
|
this.depth = 0x00; |
||||
var hash = Crypto.SHA256( Crypto.SHA256( bytes, { asBytes: true } ), { asBytes: true } ); |
this.parentFingerprint = new Buffer([0, 0, 0, 0]); |
||||
|
this.childIndex = new Buffer([0, 0, 0, 0]); |
||||
if( hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3] ) { |
this.chainCode = Key.generateSync().private; |
||||
throw new Error("Invalid checksum"); |
this.eckey = Key.generateSync(); |
||||
} |
this.hasPrivateKey = true; |
||||
|
this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); |
||||
|
this.buildExtendedPublicKey(); |
||||
|
this.buildExtendedPrivateKey(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// decode base58
|
||||
|
if (typeof bytes === "string") { |
||||
|
var decoded = base58.decode(bytes); |
||||
|
if (decoded.length != 82) |
||||
|
throw new Error("Not enough data"); |
||||
|
var checksum = decoded.slice(78, 82); |
||||
|
bytes = decoded.slice(0, 78); |
||||
|
|
||||
|
var hash = coinUtil.sha256(coinUtil.sha256(bytes)); |
||||
|
|
||||
|
if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) { |
||||
|
throw new Error("Invalid checksum"); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
if( bytes !== undefined ) |
if (bytes !== undefined) |
||||
this.init_from_bytes(bytes); |
this.initFromBytes(bytes); |
||||
} |
} |
||||
|
|
||||
BIP32.prototype.init_from_bytes = function(bytes) { |
BIP32.seed = function(bytes, network) { |
||||
// Both pub and private extended keys are 78 bytes
|
if (!network) |
||||
if( bytes.length != 78 ) throw new Error("not enough data"); |
return false; |
||||
|
|
||||
this.version = u32(bytes.slice(0, 4)); |
if (!Buffer.isBuffer(bytes)) |
||||
this.depth = u8 (bytes.slice(4, 5)); |
bytes = new Buffer(bytes, 'hex'); //if not buffer, assume hex
|
||||
this.parent_fingerprint = bytes.slice(5, 9); |
if (bytes.length < 128/8) |
||||
this.child_index = u32(bytes.slice(9, 13)); |
return false; //need more entropy
|
||||
this.chain_code = bytes.slice(13, 45); |
var hash = coinUtil.sha512hmac(bytes, new Buffer("Bitcoin seed")); |
||||
|
|
||||
var key_bytes = bytes.slice(45, 78); |
var bip32 = new BIP32(); |
||||
|
bip32.depth = 0x00; |
||||
var is_private = |
bip32.parentFingerprint = new Buffer([0, 0, 0, 0]); |
||||
(this.version == BITCOIN_MAINNET_PRIVATE || |
bip32.childIndex = new Buffer([0, 0, 0, 0]); |
||||
this.version == BITCOIN_TESTNET_PRIVATE || |
bip32.chainCode = hash.slice(32, 64); |
||||
this.version == DOGECOIN_MAINNET_PRIVATE || |
bip32.version = networks[network].bip32private; |
||||
this.version == DOGECOIN_TESTNET_PRIVATE || |
bip32.eckey = new Key(); |
||||
this.version == LITECOIN_MAINNET_PRIVATE || |
bip32.eckey.private = hash.slice(0, 32); |
||||
this.version == LITECOIN_TESTNET_PRIVATE ); |
bip32.eckey.regenerateSync(); |
||||
|
bip32.hasPrivateKey = true; |
||||
var is_public = |
bip32.pubKeyHash = coinUtil.sha256ripe160(bip32.eckey.public); |
||||
(this.version == BITCOIN_MAINNET_PUBLIC || |
|
||||
this.version == BITCOIN_TESTNET_PUBLIC || |
bip32.buildExtendedPublicKey(); |
||||
this.version == DOGECOIN_MAINNET_PUBLIC || |
bip32.buildExtendedPrivateKey(); |
||||
this.version == DOGECOIN_TESTNET_PUBLIC || |
|
||||
this.version == LITECOIN_MAINNET_PUBLIC || |
return bip32; |
||||
this.version == LITECOIN_TESTNET_PUBLIC ); |
}; |
||||
|
|
||||
if( is_private && key_bytes[0] == 0 ) { |
BIP32.prototype.initFromBytes = function(bytes) { |
||||
this.eckey = new Bitcoin.ECKey(key_bytes.slice(1, 33)); |
// Both pub and private extended keys are 78 bytes
|
||||
this.eckey.setCompressed(true); |
if(bytes.length != 78) throw new Error("not enough data"); |
||||
|
|
||||
var ecparams = getSECCurveByName("secp256k1"); |
this.version = u32(bytes.slice(0, 4)); |
||||
var pt = ecparams.getG().multiply(this.eckey.priv); |
this.depth = u8(bytes.slice(4, 5)); |
||||
this.eckey.pub = pt; |
this.parentFingerprint = bytes.slice(5, 9); |
||||
this.eckey.pubKeyHash = Bitcoin.Util.sha256ripe160(this.eckey.pub.getEncoded(true)); |
this.childIndex = u32(bytes.slice(9, 13)); |
||||
this.has_private_key = true; |
this.chainCode = bytes.slice(13, 45); |
||||
} else if( is_public && (key_bytes[0] == 0x02 || key_bytes[0] == 0x03) ) { |
|
||||
this.eckey = new Bitcoin.ECKey(); |
var keyBytes = bytes.slice(45, 78); |
||||
this.eckey.pub = decompress_pubkey(key_bytes); |
|
||||
this.eckey.pubKeyHash = Bitcoin.Util.sha256ripe160(this.eckey.pub.getEncoded(true)); |
var isPrivate = |
||||
this.eckey.setCompressed(true); |
(this.version == networks['livenet'].bip32private || |
||||
this.has_private_key = false; |
this.version == networks['testnet'].bip32private ); |
||||
} else { |
|
||||
throw new Error("Invalid key"); |
var isPublic = |
||||
} |
(this.version == networks['livenet'].bip32public || |
||||
|
this.version == networks['testnet'].bip32public ); |
||||
this.build_extended_public_key(); |
|
||||
this.build_extended_private_key(); |
if (isPrivate && keyBytes[0] == 0) { |
||||
|
this.eckey = new Key(); |
||||
|
this.eckey.private = keyBytes.slice(1, 33); |
||||
|
this.eckey.compressed = true; |
||||
|
this.eckey.regenerateSync(); |
||||
|
this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); |
||||
|
this.hasPrivateKey = true; |
||||
|
} else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { |
||||
|
this.eckey = new Key(); |
||||
|
this.eckey.public = keyBytes; |
||||
|
this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); |
||||
|
this.hasPrivateKey = false; |
||||
|
} else { |
||||
|
throw new Error("Invalid key"); |
||||
|
} |
||||
|
|
||||
|
this.buildExtendedPublicKey(); |
||||
|
this.buildExtendedPrivateKey(); |
||||
} |
} |
||||
|
|
||||
BIP32.prototype.build_extended_public_key = function() { |
BIP32.prototype.buildExtendedPublicKey = function() { |
||||
this.extended_public_key = []; |
this.extendedPublicKey = new Buffer([]); |
||||
|
|
||||
var v = null; |
var v = null; |
||||
switch(this.version) { |
switch(this.version) { |
||||
case BITCOIN_MAINNET_PUBLIC: |
case networks['livenet'].bip32public: |
||||
case BITCOIN_MAINNET_PRIVATE: |
case networks['livenet'].bip32private: |
||||
v = BITCOIN_MAINNET_PUBLIC; |
v = networks['livenet'].bip32public; |
||||
break; |
break; |
||||
case BITCOIN_TESTNET_PUBLIC: |
case networks['testnet'].bip32public: |
||||
case BITCOIN_TESTNET_PRIVATE: |
case networks['testnet'].bip32private: |
||||
v = BITCOIN_TESTNET_PUBLIC; |
v = networks['testnet'].bip32public; |
||||
break; |
break; |
||||
case DOGECOIN_MAINNET_PUBLIC: |
default: |
||||
case DOGECOIN_MAINNET_PRIVATE: |
throw new Error("Unknown version"); |
||||
v = DOGECOIN_MAINNET_PUBLIC; |
} |
||||
break; |
|
||||
case DOGECOIN_TESTNET_PUBLIC: |
// Version
|
||||
case DOGECOIN_TESTNET_PRIVATE: |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([v >> 24])]); |
||||
v = DOGECOIN_TESTNET_PUBLIC; |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([(v >> 16) & 0xff])]); |
||||
break; |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([(v >> 8) & 0xff])]); |
||||
case LITECOIN_MAINNET_PUBLIC: |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([v & 0xff])]); |
||||
case LITECOIN_MAINNET_PRIVATE: |
|
||||
v = LITECOIN_MAINNET_PUBLIC; |
// Depth
|
||||
break; |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([this.depth])]); |
||||
case LITECOIN_TESTNET_PUBLIC: |
|
||||
case LITECOIN_TESTNET_PRIVATE: |
// Parent fingerprint
|
||||
v = LITECOIN_TESTNET_PUBLIC; |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, this.parentFingerprint]); |
||||
break; |
|
||||
default: |
// Child index
|
||||
throw new Error("Unknown version"); |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([this.childIndex >>> 24])]); |
||||
} |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([(this.childIndex >>> 16) & 0xff])]); |
||||
|
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([(this.childIndex >>> 8) & 0xff])]); |
||||
// Version
|
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, new Buffer([this.childIndex & 0xff])]); |
||||
this.extended_public_key.push(v >> 24); |
|
||||
this.extended_public_key.push((v >> 16) & 0xff); |
// Chain code
|
||||
this.extended_public_key.push((v >> 8) & 0xff); |
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, this.chainCode]); |
||||
this.extended_public_key.push(v & 0xff); |
|
||||
|
// Public key
|
||||
// Depth
|
this.extendedPublicKey = Buffer.concat([this.extendedPublicKey, this.eckey.public]); |
||||
this.extended_public_key.push(this.depth); |
|
||||
|
|
||||
// Parent fingerprint
|
|
||||
this.extended_public_key = this.extended_public_key.concat(this.parent_fingerprint); |
|
||||
|
|
||||
// Child index
|
|
||||
this.extended_public_key.push(this.child_index >>> 24); |
|
||||
this.extended_public_key.push((this.child_index >>> 16) & 0xff); |
|
||||
this.extended_public_key.push((this.child_index >>> 8) & 0xff); |
|
||||
this.extended_public_key.push(this.child_index & 0xff); |
|
||||
|
|
||||
// Chain code
|
|
||||
this.extended_public_key = this.extended_public_key.concat(this.chain_code); |
|
||||
|
|
||||
// Public key
|
|
||||
this.extended_public_key = this.extended_public_key.concat(this.eckey.pub.getEncoded(true)); |
|
||||
} |
} |
||||
|
|
||||
BIP32.prototype.extended_public_key_string = function(format) { |
BIP32.prototype.extendedPublicKeyString = function(format) { |
||||
if( format === undefined || format === "base58" ) { |
if (format === undefined || format === "base58") { |
||||
var hash = Crypto.SHA256( Crypto.SHA256( this.extended_public_key, { asBytes: true } ), { asBytes: true } ); |
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPublicKey)); |
||||
var checksum = hash.slice(0, 4); |
var checksum = hash.slice(0, 4); |
||||
var data = this.extended_public_key.concat(checksum); |
var data = Buffer.concat([this.extendedPublicKey, checksum]); |
||||
return Bitcoin.Base58.encode(data); |
return base58.encode(data); |
||||
} else if( format === "hex" ) { |
} else if (format === "hex") { |
||||
return Crypto.util.bytesToHex(this.extended_public_key); |
return this.extendedPublicKey.toString('hex');; |
||||
} else { |
} else { |
||||
throw new Error("bad format"); |
throw new Error("bad format"); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
BIP32.prototype.build_extended_private_key = function() { |
BIP32.prototype.buildExtendedPrivateKey = function() { |
||||
if( !this.has_private_key ) return; |
if (!this.hasPrivateKey) return; |
||||
this.extended_private_key = []; |
this.extendedPrivateKey = new Buffer([]); |
||||
|
|
||||
var v = this.version; |
var v = this.version; |
||||
|
|
||||
// Version
|
// Version
|
||||
this.extended_private_key.push(v >> 24); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([v >> 24])]); |
||||
this.extended_private_key.push((v >> 16) & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([(v >> 16) & 0xff])]); |
||||
this.extended_private_key.push((v >> 8) & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([(v >> 8) & 0xff])]); |
||||
this.extended_private_key.push(v & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([v & 0xff])]); |
||||
|
|
||||
// Depth
|
// Depth
|
||||
this.extended_private_key.push(this.depth); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([this.depth])]); |
||||
|
|
||||
// Parent fingerprint
|
// Parent fingerprint
|
||||
this.extended_private_key = this.extended_private_key.concat(this.parent_fingerprint); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, this.parentFingerprint]); |
||||
|
|
||||
// Child index
|
// Child index
|
||||
this.extended_private_key.push(this.child_index >>> 24); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([this.childIndex >>> 24])]); |
||||
this.extended_private_key.push((this.child_index >>> 16) & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([(this.childIndex >>> 16) & 0xff])]); |
||||
this.extended_private_key.push((this.child_index >>> 8) & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([(this.childIndex >>> 8) & 0xff])]); |
||||
this.extended_private_key.push(this.child_index & 0xff); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([this.childIndex & 0xff])]); |
||||
|
|
||||
// Chain code
|
// Chain code
|
||||
this.extended_private_key = this.extended_private_key.concat(this.chain_code); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, this.chainCode]); |
||||
|
|
||||
// Private key
|
// Private key
|
||||
this.extended_private_key.push(0); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, new Buffer([0])]); |
||||
this.extended_private_key = this.extended_private_key.concat(this.eckey.priv.toByteArrayUnsigned()); |
this.extendedPrivateKey = Buffer.concat([this.extendedPrivateKey, this.eckey.private]); |
||||
} |
} |
||||
|
|
||||
BIP32.prototype.extended_private_key_string = function(format) { |
BIP32.prototype.extendedPrivateKeyString = function(format) { |
||||
if( format === undefined || format === "base58" ) { |
if (format === undefined || format === "base58") { |
||||
var hash = Crypto.SHA256( Crypto.SHA256( this.extended_private_key, { asBytes: true } ), { asBytes: true } ); |
var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPrivateKey)); |
||||
var checksum = hash.slice(0, 4); |
var checksum = hash.slice(0, 4); |
||||
var data = this.extended_private_key.concat(checksum); |
var data = Buffer.concat([this.extendedPrivateKey, checksum]); |
||||
return Bitcoin.Base58.encode(data); |
return base58.encode(data); |
||||
} else if( format === "hex" ) { |
} else if (format === "hex") { |
||||
return Crypto.util.bytesToHex(this.extended_private_key); |
return this.extendedPrivateKey.toString('hex'); |
||||
} else { |
} else { |
||||
throw new Error("bad format"); |
throw new Error("bad format"); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
|
||||
BIP32.prototype.derive = function(path) { |
BIP32.prototype.derive = function(path) { |
||||
var e = path.split('/'); |
var e = path.split('/'); |
||||
|
|
||||
// Special cases:
|
// Special cases:
|
||||
if( path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'' ) return this; |
if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') |
||||
|
return this; |
||||
|
|
||||
var bip32 = this; |
var bip32 = this; |
||||
for( var i in e ) { |
for (var i in e) { |
||||
var c = e[i]; |
var c = e[i]; |
||||
|
|
||||
if( i == 0 ) { |
if (i == 0 ) { |
||||
if( c != 'm' ) throw new Error("invalid path"); |
if (c != 'm') throw new Error("invalid path"); |
||||
continue; |
continue; |
||||
} |
|
||||
|
|
||||
var use_private = (c.length > 1) && (c[c.length-1] == '\''); |
|
||||
var child_index = parseInt(use_private ? c.slice(0, c.length - 1) : c) & 0x7fffffff; |
|
||||
|
|
||||
if( use_private ) |
|
||||
child_index += 0x80000000; |
|
||||
|
|
||||
bip32 = bip32.derive_child(child_index); |
|
||||
} |
} |
||||
|
|
||||
return bip32; |
var usePrivate = (c.length > 1) && (c[c.length-1] == '\''); |
||||
} |
var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; |
||||
|
|
||||
BIP32.prototype.derive_child = function(i) { |
|
||||
var ib = []; |
|
||||
ib.push( (i >> 24) & 0xff ); |
|
||||
ib.push( (i >> 16) & 0xff ); |
|
||||
ib.push( (i >> 8) & 0xff ); |
|
||||
ib.push( i & 0xff ); |
|
||||
|
|
||||
var use_private = (i & 0x80000000) != 0; |
|
||||
var ecparams = getSECCurveByName("secp256k1"); |
|
||||
|
|
||||
var is_private = |
if (usePrivate) |
||||
(this.version == BITCOIN_MAINNET_PRIVATE || |
childIndex += 0x80000000; |
||||
this.version == BITCOIN_TESTNET_PRIVATE || |
|
||||
this.version == DOGECOIN_MAINNET_PRIVATE || |
|
||||
this.version == DOGECOIN_TESTNET_PRIVATE || |
|
||||
this.version == LITECOIN_MAINNET_PRIVATE || |
|
||||
this.version == LITECOIN_TESTNET_PRIVATE); |
|
||||
|
|
||||
if( use_private && (!this.has_private_key || !is_private) ) throw new Error("Cannot do private key derivation without private key"); |
bip32 = bip32.deriveChild(childIndex); |
||||
|
} |
||||
|
|
||||
var ret = null; |
return bip32; |
||||
if( this.has_private_key ) { |
} |
||||
var data = null; |
|
||||
|
|
||||
if( use_private ) { |
BIP32.prototype.deriveChild = function(i) { |
||||
data = [0].concat(this.eckey.priv.toByteArrayUnsigned()).concat(ib); |
var ib = []; |
||||
} else { |
ib.push((i >> 24) & 0xff); |
||||
data = this.eckey.pub.getEncoded(true).concat(ib); |
ib.push((i >> 16) & 0xff); |
||||
} |
ib.push((i >> 8) & 0xff); |
||||
|
ib.push(i & 0xff); |
||||
|
ib = new Buffer(ib); |
||||
|
|
||||
var j = new jsSHA(Crypto.util.bytesToHex(data), 'HEX'); |
var usePrivate = (i & 0x80000000) != 0; |
||||
var hash = j.getHMAC(Crypto.util.bytesToHex(this.chain_code), "HEX", "SHA-512", "HEX"); |
|
||||
var il = new BigInteger(hash.slice(0, 64), 16); |
|
||||
var ir = Crypto.util.hexToBytes(hash.slice(64, 128)); |
|
||||
|
|
||||
// ki = IL + kpar (mod n).
|
var isPrivate = |
||||
var curve = ecparams.getCurve(); |
(this.version == networks['livenet'].bip32private || |
||||
var k = il.add(this.eckey.priv).mod(ecparams.getN()); |
this.version == networks['testnet'].bip32private ); |
||||
|
|
||||
ret = new BIP32(); |
if (usePrivate && (!this.hasPrivateKey || !isPrivate)) |
||||
ret.chain_code = ir; |
throw new Error("Cannot do private key derivation without private key"); |
||||
|
|
||||
ret.eckey = new Bitcoin.ECKey(k.toByteArrayUnsigned()); |
var ret = null; |
||||
ret.eckey.pub = ret.eckey.getPubPoint(); |
if (this.hasPrivateKey) { |
||||
ret.has_private_key = true; |
var data = null; |
||||
|
|
||||
|
if (usePrivate) { |
||||
|
data = Buffer.concat([new Buffer([0]), this.eckey.private, ib]); |
||||
} else { |
} else { |
||||
var data = this.eckey.pub.getEncoded(true).concat(ib); |
data = Buffer.concat([this.eckey.public, ib]); |
||||
var j = new jsSHA(Crypto.util.bytesToHex(data), 'HEX'); |
|
||||
var hash = j.getHMAC(Crypto.util.bytesToHex(this.chain_code), "HEX", "SHA-512", "HEX"); |
|
||||
var il = new BigInteger(hash.slice(0, 64), 16); |
|
||||
var ir = Crypto.util.hexToBytes(hash.slice(64, 128)); |
|
||||
|
|
||||
// Ki = (IL + kpar)*G = IL*G + Kpar
|
|
||||
var k = ecparams.getG().multiply(il).add(this.eckey.pub); |
|
||||
|
|
||||
ret = new BIP32(); |
|
||||
ret.chain_code = ir; |
|
||||
|
|
||||
ret.eckey = new Bitcoin.ECKey(); |
|
||||
ret.eckey.pub = k; |
|
||||
ret.has_private_key = false; |
|
||||
} |
} |
||||
|
|
||||
ret.child_index = i; |
var hash = coinUtil.sha512hmac(data, this.chainCode); |
||||
ret.parent_fingerprint = this.eckey.pubKeyHash.slice(0,4); |
var il = bignum.fromBuffer(hash.slice(0, 32), {size: 32}); |
||||
ret.version = this.version; |
var ir = hash.slice(32, 64); |
||||
ret.depth = this.depth + 1; |
|
||||
|
// ki = IL + kpar (mod n).
|
||||
ret.eckey.setCompressed(true); |
var priv = bignum.fromBuffer(this.eckey.private, {size: 32}); |
||||
ret.eckey.pubKeyHash = Bitcoin.Util.sha256ripe160(ret.eckey.pub.getEncoded(true)); |
var k = il.add(priv).mod(secp256k1_n); |
||||
|
|
||||
ret.build_extended_public_key(); |
ret = new BIP32(); |
||||
ret.build_extended_private_key(); |
ret.chainCode = ir; |
||||
|
|
||||
return ret; |
ret.eckey = new Key(); |
||||
|
ret.eckey.private = k.toBuffer({size: 32}); |
||||
|
ret.eckey.regenerateSync(); |
||||
|
ret.hasPrivateKey = true; |
||||
|
|
||||
|
} else { |
||||
|
var data = Buffer.concat([this.eckey.public, ib]); |
||||
|
var hash = coinUtil.sha512hmac(data, this.chainCode); |
||||
|
var il = bignum.fromBuffer(hash.slice(0, 32), {size: 32}); |
||||
|
var ir = hash.slice(32, 64); |
||||
|
|
||||
|
// Ki = (IL + kpar)*G = IL*G + Kpar
|
||||
|
var ilGkey = new Key(); |
||||
|
ilGkey.private = il.toBuffer({size: 32}); |
||||
|
ilGkey.regenerateSync(); |
||||
|
var ilG = Point.fromKey(ilGkey); |
||||
|
var oldkey = new Key(); |
||||
|
oldkey.public = this.eckey.public; |
||||
|
var Kpar = Point.fromKey(oldkey); |
||||
|
var newpub = Point.add(ilG, Kpar).toKey().public; |
||||
|
|
||||
|
ret = new BIP32(); |
||||
|
ret.chainCode = new Buffer(ir); |
||||
|
|
||||
|
var eckey = new Key(); |
||||
|
eckey.public = newpub; |
||||
|
ret.eckey = eckey; |
||||
|
ret.hasPrivateKey = false; |
||||
|
} |
||||
|
|
||||
|
ret.childIndex = i; |
||||
|
ret.parentFingerprint = this.pubKeyHash.slice(0,4); |
||||
|
ret.version = this.version; |
||||
|
ret.depth = this.depth + 1; |
||||
|
|
||||
|
ret.eckey.compressed = true; |
||||
|
ret.pubKeyHash = coinUtil.sha256ripe160(ret.eckey.public); |
||||
|
|
||||
|
ret.buildExtendedPublicKey(); |
||||
|
ret.buildExtendedPrivateKey(); |
||||
|
|
||||
|
return ret; |
||||
} |
} |
||||
|
|
||||
|
|
||||
function uint(f, size) { |
function uint(f, size) { |
||||
if (f.length < size) |
if (f.length < size) |
||||
throw new Error("not enough data"); |
throw new Error("not enough data"); |
||||
var n = 0; |
var n = 0; |
||||
for (var i = 0; i < size; i++) { |
for (var i = 0; i < size; i++) { |
||||
n *= 256; |
n *= 256; |
||||
n += f[i]; |
n += f[i]; |
||||
} |
} |
||||
return n; |
return n; |
||||
} |
} |
||||
|
|
||||
function u8(f) { return uint(f,1); } |
function u8(f) {return uint(f,1);} |
||||
function u16(f) { return uint(f,2); } |
function u16(f) {return uint(f,2);} |
||||
function u32(f) { return uint(f,4); } |
function u32(f) {return uint(f,4);} |
||||
function u64(f) { return uint(f,8); } |
function u64(f) {return uint(f,8);} |
||||
|
|
||||
function decompress_pubkey(key_bytes) { |
|
||||
var y_bit = u8(key_bytes.slice(0, 1)) & 0x01; |
|
||||
var ecparams = getSECCurveByName("secp256k1"); |
|
||||
|
|
||||
// build X
|
|
||||
var x = BigInteger.ZERO.clone(); |
|
||||
x.fromString(Crypto.util.bytesToHex(key_bytes.slice(1, 33)), 16); |
|
||||
|
|
||||
// get curve
|
|
||||
var curve = ecparams.getCurve(); |
|
||||
var a = curve.getA().toBigInteger(); |
|
||||
var b = curve.getB().toBigInteger(); |
|
||||
var p = curve.getQ(); |
|
||||
|
|
||||
// compute y^2 = x^3 + a*x + b
|
|
||||
var tmp = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); |
|
||||
|
|
||||
// compute modular square root of y (mod p)
|
module.exports = require('soop')(BIP32); |
||||
var y = tmp.modSqrt(p); |
|
||||
|
|
||||
// flip sign if we need to
|
|
||||
if( (y[0] & 0x01) != y_bit ) { |
|
||||
y = y.multiply(new BigInteger("-1")).mod(p); |
|
||||
} |
|
||||
|
|
||||
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); |
|
||||
} |
|
||||
|
@ -0,0 +1,138 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
var imports = require('soop').imports(); |
||||
|
var Key = imports.Key || require('./Key'); |
||||
|
var bignum = imports.bignum || require('bignum'); |
||||
|
var assert = require('assert'); |
||||
|
|
||||
|
//browser
|
||||
|
if (!process.versions) { |
||||
|
var ECKey = require('./browser/vendor-bundle.js').ECKey; |
||||
|
var ECPointFp = require('./browser/vendor-bundle.js').ECPointFp; |
||||
|
var ECFieldElementFp = require('./browser/vendor-bundle.js').ECFieldElementFp; |
||||
|
var getSECCurveByName = require('./browser/vendor-bundle.js').getSECCurveByName; |
||||
|
var BigInteger = require('./browser/vendor-bundle.js').BigInteger; |
||||
|
var should = require('chai').should(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//a point on the secp256k1 curve
|
||||
|
//x and y are bignums
|
||||
|
var Point = function(x, y) { |
||||
|
this.x = x; |
||||
|
this.y = y; |
||||
|
}; |
||||
|
|
||||
|
Point.add = function(p1, p2) { |
||||
|
|
||||
|
//node
|
||||
|
if (process.versions) { |
||||
|
var key1 = p1.toKey(); |
||||
|
key1.compressed = false; |
||||
|
var key2 = p2.toKey(); |
||||
|
key2.compressed = false; |
||||
|
var pubKey = Key.addUncompressed(key1.public, key2.public); |
||||
|
var key = new Key(); |
||||
|
key.compressed = false; |
||||
|
key.public = pubKey; |
||||
|
key.compressed = true; |
||||
|
return Point.fromKey(key); |
||||
|
} |
||||
|
|
||||
|
//browser
|
||||
|
else { |
||||
|
var ecparams = getSECCurveByName('secp256k1'); |
||||
|
|
||||
|
var p1xhex = p1.x.toBuffer({size: 32}).toString('hex'); |
||||
|
var p1x = new BigInteger(p1xhex, 16); |
||||
|
var p1yhex = p1.y.toBuffer({size: 32}).toString('hex'); |
||||
|
var p1y = new BigInteger(p1yhex, 16); |
||||
|
var p1px = new ECFieldElementFp(ecparams.getCurve().getQ(), p1x); |
||||
|
var p1py = new ECFieldElementFp(ecparams.getCurve().getQ(), p1y); |
||||
|
var p1p = new ECPointFp(ecparams.getCurve(), p1px, p1py); |
||||
|
|
||||
|
var p2xhex = p2.x.toBuffer({size: 32}).toString('hex'); |
||||
|
var p2x = new BigInteger(p2xhex, 16); |
||||
|
var p2yhex = p2.y.toBuffer({size: 32}).toString('hex'); |
||||
|
var p2y = new BigInteger(p2yhex, 16); |
||||
|
var p2px = new ECFieldElementFp(ecparams.getCurve().getQ(), p2x); |
||||
|
var p2py = new ECFieldElementFp(ecparams.getCurve().getQ(), p2y); |
||||
|
var p2p = new ECPointFp(ecparams.getCurve(), p2px, p2py); |
||||
|
|
||||
|
var p = p1p.add(p2p); |
||||
|
|
||||
|
var point = new Point(); |
||||
|
var pointxbuf = new Buffer(p.getX().toBigInteger().toByteArrayUnsigned()); |
||||
|
point.x = bignum.fromBuffer(pointxbuf, {size: pointxbuf.length}); |
||||
|
assert(pointxbuf.length <= 32); |
||||
|
var pointybuf = new Buffer(p.getY().toBigInteger().toByteArrayUnsigned()); |
||||
|
assert(pointybuf.length <= 32); |
||||
|
point.y = bignum.fromBuffer(pointybuf, {size: pointybuf.length}); |
||||
|
|
||||
|
return point; |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
//convert the public key of a Key into a Point
|
||||
|
Point.fromKey = function(key) { |
||||
|
|
||||
|
//node
|
||||
|
if (process.versions) { |
||||
|
var point = new Point(); |
||||
|
var pubKeyBuf = new Buffer(key.public); |
||||
|
var key2 = new Key(); |
||||
|
key2.compressed = key.compressed; |
||||
|
key2.public = pubKeyBuf; |
||||
|
key2.compressed = false; |
||||
|
point.x = bignum.fromBuffer(key2.public.slice(1, 33), {size: 32}); |
||||
|
point.y = bignum.fromBuffer(key2.public.slice(33, 65), {size: 32}); |
||||
|
return point; |
||||
|
} |
||||
|
|
||||
|
//browser
|
||||
|
else { |
||||
|
var point = new Point(); |
||||
|
var pubKeyBuf = new Buffer(key.public); |
||||
|
var key2 = new ECKey(); |
||||
|
key2.setCompressed(key.compressed); |
||||
|
key2.setPub(Key.bufferToArray(pubKeyBuf)); |
||||
|
key2.setCompressed(false); |
||||
|
point.x = bignum.fromBuffer((new Buffer(key2.getPub())).slice(1, 33), {size: 32}); |
||||
|
point.y = bignum.fromBuffer((new Buffer(key2.getPub())).slice(33, 65), {size: 32}); |
||||
|
return point; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
//convert the Point into the Key containing a compressed public key
|
||||
|
Point.prototype.toKey = function() { |
||||
|
|
||||
|
//node
|
||||
|
if (process.versions) { |
||||
|
var xbuf = this.x.toBuffer({size: 32}); |
||||
|
var ybuf = this.y.toBuffer({size: 32}); |
||||
|
var key = new Key(); |
||||
|
key.compressed = false; |
||||
|
var prefix = new Buffer([0x04]); |
||||
|
key.public = Buffer.concat([prefix, xbuf, ybuf]); //this might be wrong
|
||||
|
key.compressed = true; |
||||
|
return key; |
||||
|
} |
||||
|
|
||||
|
//browser
|
||||
|
else { |
||||
|
var xbuf = this.x.toBuffer({size: 32}); |
||||
|
var ybuf = this.y.toBuffer({size: 32}); |
||||
|
var key = new ECKey(); |
||||
|
key.setCompressed(false); |
||||
|
var prefix = new Buffer([0x04]); |
||||
|
var pub = Buffer.concat([prefix, xbuf, ybuf]); //this might be wrong
|
||||
|
key.setPub(Key.bufferToArray(pub)); |
||||
|
key.setCompressed(true); |
||||
|
var key2 = new Key(); |
||||
|
key2.public = new Buffer(key.getPub()); |
||||
|
return key2; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
module.exports = require('soop')(Point); |
@ -0,0 +1,297 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
var chai = chai || require('chai'); |
||||
|
var should = chai.should(); |
||||
|
var bitcore = bitcore || require('../bitcore'); |
||||
|
var BIP32 = bitcore.BIP32; |
||||
|
|
||||
|
describe('BIP32', function() { |
||||
|
|
||||
|
//test vectors: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
|
var vector1_master = '000102030405060708090a0b0c0d0e0f'; |
||||
|
var vector1_m_public = 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8' |
||||
|
var vector1_m_private = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; |
||||
|
var vector1_m0h_public = 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw'; |
||||
|
var vector1_m0h_private = 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7'; |
||||
|
var vector1_m0h1_public = 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ'; |
||||
|
var vector1_m0h1_private = 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs'; |
||||
|
var vector1_m0h12h_public = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5'; |
||||
|
var vector1_m0h12h_private = 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM'; |
||||
|
var vector1_m0h12h2_public = 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV'; |
||||
|
var vector1_m0h12h2_private = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334'; |
||||
|
var vector1_m0h12h21000000000_public = 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy'; |
||||
|
var vector1_m0h12h21000000000_private = 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76'; |
||||
|
var vector2_master = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'; |
||||
|
var vector2_m_public = 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB'; |
||||
|
var vector2_m_private = 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U'; |
||||
|
var vector2_m0_public = 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH'; |
||||
|
var vector2_m0_private = 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt'; |
||||
|
var vector2_m02147483647h_public = 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a'; |
||||
|
var vector2_m02147483647h_private = 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9'; |
||||
|
var vector2_m02147483647h1_public = 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon'; |
||||
|
var vector2_m02147483647h1_private = 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef'; |
||||
|
var vector2_m02147483647h12147483646h_public = 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL'; |
||||
|
var vector2_m02147483647h12147483646h_private = 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc'; |
||||
|
var vector2_m02147483647h12147483646h2_public = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt'; |
||||
|
var vector2_m02147483647h12147483646h2_private = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j'; |
||||
|
|
||||
|
|
||||
|
it('should initialize the class', function() { |
||||
|
should.exist(BIP32); |
||||
|
}); |
||||
|
|
||||
|
it('should create a mainnet bip32', function() { |
||||
|
var bip32 = new BIP32('mainnet'); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should create a testnet bip32', function() { |
||||
|
var bip32 = new BIP32('testnet'); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should initialize test vector 1 from the extended public key', function() { |
||||
|
var bip32 = new BIP32(vector1_m_public); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should initialize test vector 1 from the extended private key', function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should get the extended public key from the extended private key for test vector 1', function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
bip32.extendedPublicKeyString().should.equal(vector1_m_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0' ext. private key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector1_m0h_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0' ext. public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector1_m0h_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1 ext. private key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector1_m0h1_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1 ext. public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector1_m0h1_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/1"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector1_m0h1_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h ext. private key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector1_m0h12h_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h ext. public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector1_m0h12h_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h/2 ext. private key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'/2"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector1_m0h12h2_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/2"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'/2"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'/2/1000000000"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector1_m0h12h21000000000_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'/2/1000000000"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { |
||||
|
var bip32 = new BIP32(vector1_m_private); |
||||
|
var child = bip32.derive("m/0'/1/2'/2"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/1000000000"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); |
||||
|
}); |
||||
|
|
||||
|
it('should initialize test vector 2 from the extended public key', function() { |
||||
|
var bip32 = new BIP32(vector2_m_public); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should initialize test vector 2 from the extended private key', function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
should.exist(bip32); |
||||
|
}); |
||||
|
|
||||
|
it('should get the extended public key from the extended private key for test vector 2', function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
bip32.extendedPublicKeyString().should.equal(vector2_m_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0 ext. private key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector2_m0_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0 ext. public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector2_m0_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0 ext. public key from m public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/0"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector2_m0_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h ext. private key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector2_m02147483647h_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h ext. public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector2_m02147483647h_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector2_m02147483647h1_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/1"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1/2147483646'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1/2147483646'"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); |
||||
|
should.exist(child); |
||||
|
child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h2_private); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); |
||||
|
should.exist(child); |
||||
|
child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); |
||||
|
}); |
||||
|
|
||||
|
it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { |
||||
|
var bip32 = new BIP32(vector2_m_private); |
||||
|
var child = bip32.derive("m/0/2147483647'/1/2147483646'"); |
||||
|
var child_pub = new BIP32(child.extendedPublicKeyString()); |
||||
|
var child2 = child_pub.derive("m/2"); |
||||
|
should.exist(child2); |
||||
|
child2.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); |
||||
|
}); |
||||
|
|
||||
|
describe('#seed', function() { |
||||
|
|
||||
|
it('should initialize a new BIP32 correctly from test vector 1 seed', function() { |
||||
|
var hex = vector1_master; |
||||
|
var bip32 = BIP32.seed(hex, 'livenet'); |
||||
|
should.exist(bip32); |
||||
|
bip32.extendedPrivateKeyString().should.equal(vector1_m_private); |
||||
|
bip32.extendedPublicKeyString().should.equal(vector1_m_public); |
||||
|
}); |
||||
|
|
||||
|
it('should initialize a new BIP32 correctly from test vector 2 seed', function() { |
||||
|
var hex = vector2_master; |
||||
|
var bip32 = BIP32.seed(hex, 'livenet'); |
||||
|
should.exist(bip32); |
||||
|
bip32.extendedPrivateKeyString().should.equal(vector2_m_private); |
||||
|
bip32.extendedPublicKeyString().should.equal(vector2_m_public); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
@ -0,0 +1,69 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
var assert = require('assert'); |
||||
|
var chai = chai || require('chai'); |
||||
|
var bitcore = bitcore || require('../bitcore'); |
||||
|
var coinUtil = coinUtil || require('../util/util'); |
||||
|
var buffertools = require('buffertools'); |
||||
|
var bignum = require('bignum'); |
||||
|
|
||||
|
var should = chai.should(); |
||||
|
|
||||
|
var Point = bitcore.Point; |
||||
|
var Key = bitcore.Key; |
||||
|
|
||||
|
describe('Key', function() { |
||||
|
|
||||
|
it('should initialize the main object', function() { |
||||
|
should.exist(Point); |
||||
|
}); |
||||
|
|
||||
|
it('should be able to create instance', function() { |
||||
|
var p = new Point(); |
||||
|
should.exist(p); |
||||
|
}); |
||||
|
|
||||
|
it('should add these two points correctly', function() { |
||||
|
//these points are from one of the BIP32 test vectors
|
||||
|
var axhex = "69b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d"; |
||||
|
var ayhex = "eeedc91342b3c8982c1e676435780fe5f9d62f3f692e8d1512485d77fab35997"; |
||||
|
var a = new Point(bignum.fromBuffer((new Buffer(axhex, 'hex')), {size: 32}), bignum.fromBuffer((new Buffer(ayhex, 'hex')), {size: 32})); |
||||
|
var bxhex = "5a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"; |
||||
|
var byhex = "7f717885be239daadce76b568958305183ad616ff74ed4dc219a74c26d35f839"; |
||||
|
var b = new Point(bignum.fromBuffer((new Buffer(bxhex, 'hex')), {size: 32}), bignum.fromBuffer((new Buffer(byhex, 'hex')), {size: 32})); |
||||
|
var sxhex = "501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"; |
||||
|
var syhex = "008794c1df8131b9ad1e1359965b3f3ee2feef0866be693729772be14be881ab"; |
||||
|
var s = new Point(bignum.fromBuffer((new Buffer(sxhex, 'hex')), {size: 32}), bignum.fromBuffer((new Buffer(syhex, 'hex')), {size: 32})); |
||||
|
var sum = Point.add(a, b); |
||||
|
s.x.toBuffer({size: 32}).toString('hex').should.equal(sum.x.toBuffer({size: 32}).toString('hex')); |
||||
|
s.y.toBuffer({size: 32}).toString('hex').should.equal(sum.y.toBuffer({size: 32}).toString('hex')); |
||||
|
}); |
||||
|
|
||||
|
it('should convert a Point into the public key of a Key', function() { |
||||
|
var axhex = "69b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d"; |
||||
|
var axbuf = new Buffer(axhex, 'hex'); |
||||
|
var ayhex = "eeedc91342b3c8982c1e676435780fe5f9d62f3f692e8d1512485d77fab35997"; |
||||
|
var aybuf = new Buffer(ayhex, 'hex'); |
||||
|
var a = new Point(bignum.fromBuffer(axbuf, {size: 32}), bignum.fromBuffer(aybuf, {size: 32})); |
||||
|
|
||||
|
var pubKeyBufCompressedHex = "0369b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d"; |
||||
|
var key = new Key(); |
||||
|
key.public = new Buffer(pubKeyBufCompressedHex, 'hex'); |
||||
|
|
||||
|
key.public.toString('hex').should.equal(a.toKey().public.toString('hex')); |
||||
|
}); |
||||
|
|
||||
|
it('should convert the public key of a Key into a Point', function() { |
||||
|
var axhex = "69b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d"; |
||||
|
var ayhex = "eeedc91342b3c8982c1e676435780fe5f9d62f3f692e8d1512485d77fab35997"; |
||||
|
|
||||
|
var pubKeyBufCompressedHex = "0369b154b42ff9452c31251cb341d7db01ad603dc56d64f9c5fb9e7031b89a241d"; |
||||
|
var key = new Key(); |
||||
|
key.public = new Buffer(pubKeyBufCompressedHex, 'hex'); |
||||
|
|
||||
|
var point = Point.fromKey(key); |
||||
|
point.x.toBuffer({size: 32}).toString('hex').should.equal(axhex); |
||||
|
point.y.toBuffer({size: 32}).toString('hex').should.equal(ayhex); |
||||
|
}); |
||||
|
|
||||
|
}); |
Loading…
Reference in new issue