15 Commits

Author SHA1 Message Date
Kiyomichi Kosaka cea94af32f Make custom field visible automatically after refresh. 2021-04-07 17:33:49 +02:00
Kiyomichi Kosaka 3b1e44e4b4 Merge branch 'master' into full_featured 2021-04-05 20:20:13 +02:00
Kiyomichi Kosaka 06054a7000 Merge branch 'hdwallet_bech32' into full_featured 2021-04-05 20:06:40 +02:00
Kiyomichi Kosaka 6a6c2c6beb In verify for HD wallets add field to choose the address format. 2021-04-05 20:04:32 +02:00
Kiyomichi Kosaka 04609947e8 Merge branch 'hdwallet_bech32' into full_featured 2021-04-05 19:58:57 +02:00
Kiyomichi Kosaka 40a235eb04 Fix address generation for HD wallet with different types on simple path scheme. 2021-04-05 19:58:35 +02:00
Kiyomichi Kosaka 20bf365d29 Merge branch 'hdwallet_bech32' into full_featured 2021-04-05 19:37:54 +02:00
Kiyomichi Kosaka 632572fc66 Merge branch 'hd_wallet_seed' into full_featured 2021-04-05 19:37:39 +02:00
Kiyomichi Kosaka 00519cb2ab Add possibility in verify view for HD wallets to choose the address format. 2021-04-05 19:03:02 +02:00
Kiyomichi Kosaka 2b6de3edef Use HMAC SHA-256 with configurable amount of rounds to generate seed for HD wallet, also show the generated seed in WIF format. 2021-04-05 18:15:33 +02:00
Kiyomichi Kosaka f408c9b251 Add the posibility to specify hardened indicies on HD wallets. 2021-04-05 13:48:21 +02:00
Kiyomichi Kosaka bd82cfd76f Add tooltip to custom path field and make names in HD wallet setting a bit more precise. 2021-04-05 13:48:21 +02:00
Kiyomichi Kosaka 27a2d4efb1 Add setting to configure old HD wallet generation with hardened paths. 2021-04-05 13:48:08 +02:00
Kiyomichi Kosaka 704f2c96a3 Fix the derivation of the hd wallet hardened path to be BIP32 compliant. 2021-04-05 13:10:41 +02:00
Kiyomichi Kosaka 29baeff8c8 Fix small typo. 2021-04-05 13:07:27 +02:00
3 changed files with 84 additions and 13 deletions
+27 -3
View File
@@ -582,12 +582,27 @@
</span> </span>
</div> </div>
<label>Seed (WIF)</label>
<div class="input-group">
<input id="newHDseed" type="text" class="form-control" value="" readonly>
<span class="input-group-btn">
<button class="deriveHDbtn btn btn-default" type="button"><span title="Derive from key" class="glyphicon glyphicon-chevron-right"></span></button>
</span>
</div>
<h3>Address Options</h3> <h3>Address Options</h3>
<p>You can use the advanced options below to generate different kinds of master addresses.</p> <p>You can use the advanced options below to generate different kinds of master addresses.</p>
<div class="checkbox"> <div class="checkbox">
<label><input type="checkbox" id="newHDBrainwallet" class="checkbox-inline"> Custom Seed or Brain Wallet</label> <label><input type="checkbox" id="newHDBrainwallet" class="checkbox-inline"> Custom Seed or Brain Wallet</label>
<input type="text" class="form-control hidden" id="HDBrainwallet"> <div class="hidden" id="HDBrainwalletInput">
<input type="text" class="form-control" id="HDBrainwallet">
<span class="text-muted">
Number of HMAC SHA-256 iterations for seed generation, higher value mean's also longer calculation
(use value 0 to calculate just SHA-256, like in previous coinb.in versions):
<input type="text" class="form-control" id="HDBrainwalletIters" value="50000" size="10">
</span>
</div>
</div> </div>
<input type="button" class="btn btn-primary" value="Generate" id="newHDKeysBtn"> <input type="button" class="btn btn-primary" value="Generate" id="newHDKeysBtn">
@@ -1191,7 +1206,7 @@
<p>The path of key derivation</p> <p>The path of key derivation</p>
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-6">
<b>Path</b><br> <b>Path</b><br>
<select class="form-control" id="hdpathtype"> <select class="form-control" id="hdpathtype">
<option value="simple">Simple: m/i</option> <option value="simple">Simple: m/i</option>
@@ -1216,6 +1231,15 @@
<input type="text" class="form-control derivation_index_end" value="1"> <input type="text" class="form-control derivation_index_end" value="1">
</div> </div>
<div class="col-md-2">
<b>Address format</b><br>
<select class="form-control derivation_addr_format">
<option value="bech32">Bech32</option>
<option value="segwit">SegWit</option>
<option value="legacy">Legacy</option>
</select>
</div>
</div> </div>
<hr> <hr>
@@ -1226,7 +1250,7 @@
<div class="derived_data"> <div class="derived_data">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr><td><b>Index</b></td><td><b>Address</b><td><b>Private Key (WIF)</b></td></td><td><b>Extended xPub</b></td><td><b>Extended xPrv</b></td></tr> <tr><td><b>Index</b></td><td><b>Address</b></td><td><b>Redeem script</b></td><td><b>Private Key (WIF)</b></td><td><b>Extended xPub</b></td><td><b>Extended xPrv</b></td></tr>
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
+49 -7
View File
@@ -610,6 +610,7 @@
r.parent_fingerprint = bytes.slice(5, 9); r.parent_fingerprint = bytes.slice(5, 9);
r.child_index = coinjs.uint(bytes.slice(9, 13), 4); r.child_index = coinjs.uint(bytes.slice(9, 13), 4);
r.chain_code = bytes.slice(13, 45); r.chain_code = bytes.slice(13, 45);
r.seed_wif = '';
r.key_bytes = bytes.slice(45, 78); r.key_bytes = bytes.slice(45, 78);
var c = coinjs.compressed; // get current default var c = coinjs.compressed; // get current default
@@ -620,10 +621,20 @@
var privkey = (r.key_bytes).slice(1, 33); var privkey = (r.key_bytes).slice(1, 33);
var privkeyHex = Crypto.util.bytesToHex(privkey); var privkeyHex = Crypto.util.bytesToHex(privkey);
var pubkey = coinjs.newPubkey(privkeyHex); var pubkey = coinjs.newPubkey(privkeyHex);
var addr_format = $("#verifyHDaddress .derivation_addr_format").val();
if (addr_format == "bech32") {
var address = coinjs.bech32Address(pubkey);
} else if (addr_format == "segwit") {
var address = coinjs.segwitAddress(pubkey);
} else {
var address = {'address': coinjs.pubkey2address(pubkey),
'redeemscript': ''};
}
r.keys = {'privkey':privkeyHex, r.keys = {'privkey':privkeyHex,
'pubkey':pubkey, 'pubkey':pubkey,
'address':coinjs.pubkey2address(pubkey), 'address':address.address,
'script':address.redeemscript,
'wif':coinjs.privkey2wif(privkeyHex)}; 'wif':coinjs.privkey2wif(privkeyHex)};
} else if(r.key_bytes[0] == 0x02 || r.key_bytes[0] == 0x03) { } else if(r.key_bytes[0] == 0x02 || r.key_bytes[0] == 0x03) {
@@ -702,23 +713,34 @@
var ecparams = EllipticCurve.getSECCurveByName("secp256k1"); var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
var curve = ecparams.getCurve(); var curve = ecparams.getCurve();
var k, key, pubkey, o; var k, key, pubkey, o, addr_format, address_fun, address;
o = coinjs.clone(this); o = coinjs.clone(this);
o.chain_code = ir; o.chain_code = ir;
o.child_index = i; o.child_index = i;
addr_format = $("#verifyHDaddress .derivation_addr_format").val();
if (addr_format == "bech32") {
address_fun = function(pk) { return coinjs.bech32Address(pk); };
} else if (addr_format == "segwit") {
address_fun = function(pk) { return coinjs.segwitAddress(pk); };
} else {
address_fun = function(pk) {
return {'address': coinjs.pubkey2address(pk), 'redeemscript': ''};
};
}
if(this.type=='private'){ if(this.type=='private'){
// derive key pair from from a xprv key // derive key pair from from a xprv key
k = il.add(new BigInteger([0].concat(Crypto.util.hexToBytes(this.keys.privkey)))).mod(ecparams.getN()); k = il.add(new BigInteger([0].concat(Crypto.util.hexToBytes(this.keys.privkey)))).mod(ecparams.getN());
key = Crypto.util.bytesToHex(k.toByteArrayUnsigned()); key = Crypto.util.bytesToHex(k.toByteArrayUnsigned());
pubkey = coinjs.newPubkey(key); pubkey = coinjs.newPubkey(key);
address = address_fun(pubkey);
o.keys = {'privkey':key, o.keys = {'privkey':key,
'pubkey':pubkey, 'pubkey':pubkey,
'wif':coinjs.privkey2wif(key), 'wif':coinjs.privkey2wif(key),
'address':coinjs.pubkey2address(pubkey)}; 'address':address.address,
'script':address.redeemscript};
} else if (this.type=='public'){ } else if (this.type=='public'){
// derive xpub key from an xpub key // derive xpub key from an xpub key
@@ -735,9 +757,11 @@
publicKeyBytesCompressed.unshift(0x03) publicKeyBytesCompressed.unshift(0x03)
} }
pubkey = Crypto.util.bytesToHex(publicKeyBytesCompressed); pubkey = Crypto.util.bytesToHex(publicKeyBytesCompressed);
address = address_fun(pubkey);
o.keys = {'pubkey':pubkey, o.keys = {'pubkey':pubkey,
'address':coinjs.pubkey2address(pubkey)} 'address':address.address,
'script':address.redeemscript}
} else { } else {
// fail // fail
} }
@@ -748,8 +772,23 @@
} }
// make a master hd xprv/xpub // make a master hd xprv/xpub
r.master = function(pass) { r.master = function(pass, iters) {
var seed = (pass) ? Crypto.SHA256(pass) : coinjs.newPrivkey(); if (pass) {
var seed_iters = (iters) ? Math.abs(iters * 1) : 0;
if (seed_iters == 0) {
var seed = Crypto.SHA256(pass);
} else {
var seed = Crypto.util.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
for (var i = 0; i < seed_iters; i++) {
seed = Crypto.HMAC(Crypto.SHA256, seed, pass, { asBytes: true });
}
seed = Crypto.util.bytesToHex(seed);
}
} else {
var seed = coinjs.newPrivkey();
}
var seed_wif = coinjs.privkey2wif(seed);
var hasher = new jsSHA(seed, 'HEX'); var hasher = new jsSHA(seed, 'HEX');
var I = hasher.getHMAC("Bitcoin seed", "TEXT", "SHA-512", "HEX"); var I = hasher.getHMAC("Bitcoin seed", "TEXT", "SHA-512", "HEX");
@@ -761,6 +800,7 @@
'parent_fingerprint':[0,0,0,0], 'parent_fingerprint':[0,0,0,0],
'child_index':0, 'child_index':0,
'chain_code':chain, 'chain_code':chain,
'seed_wif':seed_wif,
'privkey':I.slice(0, 64), 'privkey':I.slice(0, 64),
'pubkey':coinjs.newPubkey(I.slice(0, 64))}); 'pubkey':coinjs.newPubkey(I.slice(0, 64))});
} }
@@ -805,6 +845,8 @@
var ret = pub.concat(checksum); var ret = pub.concat(checksum);
o.pubkey = coinjs.base58encode(ret); o.pubkey = coinjs.base58encode(ret);
} }
o.seed_wif = data.seed_wif;
return o; return o;
} }
+8 -3
View File
@@ -570,18 +570,20 @@ $(document).ready(function() {
$("#newHDKeysBtn").click(function(){ $("#newHDKeysBtn").click(function(){
coinjs.compressed = true; coinjs.compressed = true;
var s = ($("#newHDBrainwallet").is(":checked")) ? $("#HDBrainwallet").val() : null; var s = ($("#newHDBrainwallet").is(":checked")) ? $("#HDBrainwallet").val() : null;
var siters = ($("#newHDBrainwallet").is(":checked")) ? $("#HDBrainwalletIters").val()*1 : null;
var hd = coinjs.hd(); var hd = coinjs.hd();
var pair = hd.master(s); var pair = hd.master(s, siters);
$("#newHDxpub").val(pair.pubkey); $("#newHDxpub").val(pair.pubkey);
$("#newHDxprv").val(pair.privkey); $("#newHDxprv").val(pair.privkey);
$("#newHDseed").val(pair.seed_wif);
}); });
$("#newHDBrainwallet").click(function(){ $("#newHDBrainwallet").click(function(){
if($(this).is(":checked")){ if($(this).is(":checked")){
$("#HDBrainwallet").removeClass("hidden"); $("#HDBrainwalletInput").removeClass("hidden");
} else { } else {
$("#HDBrainwallet").addClass("hidden"); $("#HDBrainwalletInput").addClass("hidden");
} }
}); });
@@ -1681,6 +1683,7 @@ $(document).ready(function() {
if(hex == hex_cmp_prv || hex == hex_cmp_pub){ if(hex == hex_cmp_prv || hex == hex_cmp_pub){
var hd = coinjs.hd(s); var hd = coinjs.hd(s);
$("#verifyHDaddress .hdKey").html(s); $("#verifyHDaddress .hdKey").html(s);
$("#verifyHDaddress .seed_wif").val(hd.seed_wif);
$("#verifyHDaddress .chain_code").val(Crypto.util.bytesToHex(hd.chain_code)); $("#verifyHDaddress .chain_code").val(Crypto.util.bytesToHex(hd.chain_code));
$("#verifyHDaddress .depth").val(hd.depth); $("#verifyHDaddress .depth").val(hd.depth);
$("#verifyHDaddress .version").val('0x'+(hd.version).toString(16)); $("#verifyHDaddress .version").val('0x'+(hd.version).toString(16));
@@ -1718,10 +1721,12 @@ $(document).ready(function() {
var derived = hd.derive(i); var derived = hd.derive(i);
} else { } else {
var derived = hd.derive_path(($("#hdpath input").val().replace(/\/+$/, ""))+'/'+i+use_private_index); var derived = hd.derive_path(($("#hdpath input").val().replace(/\/+$/, ""))+'/'+i+use_private_index);
$("#hdpath").removeClass(); // make it visible
} }
html += '<tr>'; html += '<tr>';
html += '<td>'+i+'</td>'; html += '<td>'+i+'</td>';
html += '<td><input type="text" class="form-control" value="'+derived.keys.address+'" readonly></td>'; html += '<td><input type="text" class="form-control" value="'+derived.keys.address+'" readonly></td>';
html += '<td><input type="text" class="form-control" value="'+derived.keys.script+'" readonly></td>';
html += '<td><input type="text" class="form-control" value="'+((derived.keys.wif)?derived.keys.wif:'')+'" readonly></td>'; html += '<td><input type="text" class="form-control" value="'+((derived.keys.wif)?derived.keys.wif:'')+'" readonly></td>';
html += '<td><input type="text" class="form-control" value="'+derived.keys_extended.pubkey+'" readonly></td>'; html += '<td><input type="text" class="form-control" value="'+derived.keys_extended.pubkey+'" readonly></td>';
html += '<td><input type="text" class="form-control" value="'+((derived.keys_extended.privkey)?derived.keys_extended.privkey:'')+'" readonly></td>'; html += '<td><input type="text" class="form-control" value="'+((derived.keys_extended.privkey)?derived.keys_extended.privkey:'')+'" readonly></td>';