diff --git a/bitaddress.org.html b/bitaddress.org.html index edb091a..7f56beb 100644 --- a/bitaddress.org.html +++ b/bitaddress.org.html @@ -6302,7 +6302,7 @@ Bitcoin.Util = { no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; } a { position: relative; z-index: 20; } .right { text-align: right; } -.walletarea { display: none; border: 2px solid #53c100; } +.walletarea { display: none; border: 2px solid #009900; } hr { margin: 20px 0; border-top: 2px dashed #008000; } .keyarea { height: 110px; text-align: left; position: relative; padding: 5px; } .keyarea .public { float: left; } @@ -6319,14 +6319,14 @@ body { font-family: Arial; } .question { padding: 10px 15px; text-align: left; cursor: pointer; } .question:hover, .expandable:hover { color: #77777A; } .answer { padding: 0 15px 10px 25px; text-align: left; display: none; font-size: 80%; } -.faq { border: 0; border-top: 2px solid #53c100; } +.faq { border: 0; border-top: 2px solid #009900; } #wallets { clear: both; } #btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; } #seedpoolarea { display: none; } #seedpooldisplay { font-family: monospace; font-size: 1em; width: 640px; padding: 15px 5px; word-wrap: break-word; } -.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: #53c100; position: absolute; z-index: 10; } -#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid #53c100; clear: both; } +.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: #009900; position: absolute; z-index: 10; } +#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid #009900; clear: both; } #generate span { padding: 5px 5px 0 5px; } #generatekeyinput { position: relative; z-index: 20; } #keyarea { height: 250px; } @@ -6339,14 +6339,14 @@ body { font-family: Arial; } #keyarea .public { width: 30%; display: table-cell; } #singlearea { font-size: 90%; } #singlesecret { position: relative; top: -130px; float: right; right: 200px; color: red; font-weight: bolder; font-size: 200%; } -#singleshare { position: relative; top: -110px; float: left; left: 160px; color: #53c100; font-weight: bolder; font-size: 200%; } -#singlesafety { text-align: left; padding: 5px; border-top: 2px solid #53c100; top: -25px; position: relative; } +#singleshare { position: relative; top: -110px; float: left; left: 160px; color: #009900; font-weight: bolder; font-size: 200%; } +#singlesafety { text-align: left; padding: 5px; border-top: 2px solid #009900; top: -25px; position: relative; } -#main { position: relative; text-align: center; margin: 0px auto; width: 810px; } +#main { position: relative; text-align: center; margin: 0px auto; width: 808px; } #logo { width: 578px; height: 80px; } #paperarea { min-height: 120px; display: none; } -#paperarea .keyarea { border: 2px solid #53c100; border-top: 0; } +#paperarea .keyarea { border: 2px solid #009900; border-top: 0; } #paperarea .keyarea.art { display: block; height: auto; border: 0; font-family: Ubuntu, Arial; padding: 0; margin: 0; } #paperarea .artwallet .papersvg { width: 486px; height: 261px; border: 0; margin: 0; padding: 0; left: 0; } #paperarea .artwallet .qrcode_public { top: 52px; left: 17px; z-index: 100; margin: 0; float: none; display: block; position: absolute; background-color: #FFFFFF; @@ -6410,15 +6410,23 @@ body { font-family: Arial; } #vanityarea .label { text-decoration: underline; } #vanityarea .output { font-family: monospace; font-size: 1.25em; display: block; } #vanityarea .notes { text-align: left; font-size: 80%; padding: 0 0 20px 0; } -#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid #53c100; } +#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid #009900; } #vanitystep1label { padding-left: 5px; } -#vanitystep2area { border-top: 2px solid #53c100; display: block; padding: 15px; } +#vanitystep2area { border-top: 2px solid #009900; display: block; padding: 15px; } #vanitystep2inputs { padding: 0 15px 10px 15px; } #vanitycalc { margin-top: 5px; } +#splitarea { text-align: left; } +#splitarea span { padding: 0; } #splitcommands { padding: 10px 15px; text-align: left; } -#splitstep1area { display: none; text-align: left; position: relative; padding: 0; border-bottom: 2px solid #53c100; } -.splitsharerow { border-bottom: 2px solid #53c100; padding: 15px; } +#combinecommands { padding: 10px 15px; } +#splitstep1area { display: none; text-align: left; position: relative; padding: 0; border-bottom: 2px solid #009900; } +.splitsharerow { border-bottom: 2px solid #009900; padding: 15px; } +.splitsharerow:last-child { border-bottom: 0; } +#combinelabelprivatekey { text-decoration: underline; } +#splitarea .output { display: block; font-family: monospace; font-size: 1.25em; } +#splitarea span.output { display: inline; } +#splitstep2area { padding: 10px 15px; } .englishjson { text-align: center; padding: 40px 0 20px 0; } .unittests { text-align: center; } @@ -6440,14 +6448,14 @@ body { font-family: Arial; } .menu { text-align: left; margin: 0; padding: 0; display: block; - background-color: #53c100; - - height: 72px; border-top-left-radius: 5px; border-top-right-radius: 5px; + background-color: #009900; /* # 009900 # 53c100 */ + border-top-left-radius: 5px; border-top-right-radius: 5px; } .menu .tab { - position: relative; float: left; margin: 0; list-style: none; z-index: 110; cursor: pointer; - border: 0px solid red; top: 1px; padding: 10px 20px; width: 160px; text-align: center; + position: relative; display: inline-block; border: 0px solid red; + margin: 0; list-style: none; z-index: 110; cursor: pointer; + top: 1px; padding: 10px 20px; width: 162px; text-align: center; } .menu .tab.selected { @@ -6461,7 +6469,7 @@ body { font-family: Arial; } .menu .tab.selected:hover { color: #000; } .pagebreak { height: 50px; } - .commands { border-bottom: 2px solid #53c100; padding: 10px 2px; margin-bottom: 0; } + .commands { border-bottom: 2px solid #009900; padding: 10px 2px; margin-bottom: 0; } .commands .row { padding: 0 0; text-align: left; } .commands .row.extra { padding-top: 6px; } .commands span { padding: 0 10px; } @@ -6469,8 +6477,8 @@ body { font-family: Arial; } .commands span.right { float: right; } .expandable { padding: 10px 15px; text-align: left; cursor: pointer; } - #menu { visibility: visible; font-size: 90%; } - #culturemenu { text-align: right; padding: 0 20px; } + #menu { visibility: hidden; font-size: 90%; } + #culturemenu { text-align: right; padding: 0 20px; margin-bottom: 3px; } #culturemenu span { padding: 3px; } #culturemenu .selected { text-decoration: none; color: #000000; } @@ -6483,7 +6491,7 @@ body { font-family: Arial; } #detailcommands span { padding: 0 10px; } #detailprivkey { width: 250px; } #detailprivkeypassphrase { width: 250px; } - .paper .commands { border: 2px solid #53c100; } + .paper .commands { border: 2px solid #009900; } #bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; } #bulklimit { width: 45px; } @@ -6502,7 +6510,7 @@ body { font-family: Arial; } #main { width: auto; } #singlearea { border: 0; } #singlesafety { border: 0; } - #paperarea .keyarea:first-child { border-top: 2px solid #53c100; } + #paperarea .keyarea:first-child { border-top: 2px solid #009900; } #paperarea .keyarea.art:first-child { border: 0; } .pagebreak { height: 1px; } .paper #logo { display: none; } @@ -6534,13 +6542,13 @@ body { font-family: Arial; }
@@ -6742,17 +6750,13 @@ body { font-family: Arial; }
-
- - - + +
- - - + + -
@@ -6762,10 +6766,13 @@ body { font-family: Arial; }
- - - - + +
+
+
+ +
+
@@ -9066,7 +9073,7 @@ ninja.wallets.splitwallet = { open: function () { document.getElementById("splitarea").style.display = "block"; secrets.setRNG(); - secrets.init(7); + secrets.init(7); // 7 bits allows for up to 127 shares }, close: function () { @@ -9076,12 +9083,16 @@ ninja.wallets.splitwallet = { mkOutputRow: function (s, id, lbltxt) { var row = document.createElement("div"); var label = document.createElement("label"); - label.innerHTML = lbltxt + s; + label.innerHTML = lbltxt; var qr = document.createElement("div"); + var output = document.createElement("span"); + output.setAttribute("class", "output"); + output.innerHTML = s; qr.setAttribute("id", id); row.setAttribute("class", "splitsharerow"); row.appendChild(label); + row.appendChild(output); row.appendChild(qr); row.appendChild(document.createElement("br")); @@ -9102,13 +9113,12 @@ ninja.wallets.splitwallet = { // Split a private key and update information in the HTML splitKey: function () { try { - var key = new Bitcoin.ECKey(false); - var bitcoinAddress = key.getBitcoinAddress(); - var numshares = parseInt(document.getElementById('splitshares').value); var threshold = parseInt(document.getElementById('splitthreshold').value); - var hexKey = Crypto.util.bytesToHex(key.getBitcoinPrivateKeyByteArray()); - var shares = secrets.share(hexKey, numshares, threshold).map(this.hexToBytes).map(Bitcoin.Base58.encode); + var key = new Bitcoin.ECKey(false); + var bitcoinAddress = key.getBitcoinAddress(); + var shares = ninja.wallets.splitwallet.getFormattedShares(key.getBitcoinHexFormat(), numshares, threshold); + var output = document.createElement("div"); output.setAttribute("id", "splitoutput"); var m = {}; @@ -9130,49 +9140,33 @@ ninja.wallets.splitwallet = { catch (e) { // browser does not have sufficient JavaScript support to generate a bitcoin address alert(e); - document.getElementById("btcaddress").innerHTML = "error"; - document.getElementById("btcprivwif").innerHTML = "error"; - document.getElementById("qrcode_public").innerHTML = ""; - document.getElementById("qrcode_private").innerHTML = ""; } }, // Combine shares of a private key to retrieve the key combineShares: function () { try { - var element = document.getElementById("combineoutput"); - if (element != null) element.parentNode.removeChild(element); - + document.getElementById("combinedprivatekey").innerHTML = ""; var shares = document.getElementById("combineinput").value.trim().split(/\W+/); - - var combined = secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(this.stripLeadZeros)); - - var privkeyBase58 = new Bitcoin.ECKey(this.hexToBytes(combined)).getBitcoinWalletImportFormat(); - var output = document.createElement("div"); - output.setAttribute("id", "combineoutput"); - var txt = document.createElement("input"); - txt.setAttribute("id", "combineoutputtext"); - txt.setAttribute("value", privkeyBase58); - txt.setAttribute("size", 55); - var lbl = document.createElement("label"); - lbl.innerHTML = "Private key"; - output.appendChild(lbl); - output.appendChild(txt); - document.getElementById("combinecommands").appendChild(output); + var combinedBytes = ninja.wallets.splitwallet.combineFormattedShares(shares); + var privkeyBase58 = new Bitcoin.ECKey(combinedBytes).getBitcoinWalletImportFormat(); + document.getElementById("combinedprivatekey").innerHTML = privkeyBase58; } catch (e) { alert(e); } }, + // generate shares and format them in base58 getFormattedShares: function (key, numshares, threshold) { var shares = secrets.share(key, numshares, threshold).map(ninja.wallets.splitwallet.hexToBytes).map(Bitcoin.Base58.encode); return shares; }, + // combine base58 formatted shares and return a bitcoin byte array combineFormattedShares: function (shares) { var combined = secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(ninja.wallets.splitwallet.stripLeadZeros)); - return combined; + return ninja.wallets.splitwallet.hexToBytes(combined); }, openCloseStep: function (num) { @@ -9654,46 +9648,107 @@ ninja.wallets.splitwallet = { }, // test split wallet - testSplitAndCombinePrivateKey: function () { + testSplitAndCombinePrivateKey2of2: function () { + // lowercase hex key + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW + var numshares = 2; + var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); + + if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + return false; + } + return true; + }, + // Example use case #1: + // Division of 3 shares: + // 1 share in a safety deposit box ("Box") + // 1 share at Home + // 1 share at Work + // Threshold of 2 can be redeemed in these permutations + // Home + Box + // Work + Box + // Home + Work + testSplitAndCombinePrivateKey2of3: function () { + // lowercase hex key + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW + var numshares = 3; + var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + shares.shift(); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); + + if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + return false; + } + return true; + }, + testSplitAndCombinePrivateKey2of4: function () { + // uppercase hex key var key = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B"; //5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb var numshares = 4; var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + shares.shift(); + shares.shift(); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); //secrets.share(key, numshares, threshold).map(ninja.wallets.splitwallet.hexToBytes).map(Bitcoin.Base58.encode); - var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); // secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(ninja.wallets.splitwallet.stripLeadZeros)); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); if (btcKey.getBitcoinHexFormat() != key) { return false; } return true; }, - testSplitAndCombinePrivateKey2: function () { - var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da 5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW - var numshares = 4; - var threshold = 2; + // Example use case #2: + // Division of 13 shares: + // 4 shares in a safety deposit box ("Box") + // 3 shares with good friend Angie + // 3 shares with good friend Fred + // 3 shares with Self at home or office + // Threshold of 7 can be redeemed in these permutations + // Self + Box (no trust to spend your coins but your friends are backing up your shares) + // Angie + Box (Angie will send btc to executor of your will) + // Fred + Box (if Angie hasn't already then Fred will send btc to executor of your will) + // Angie + Fred + Self (bank fire/theft then you with both your friends can spend the coins) + testSplitAndCombinePrivateKey7of13: function () { + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; + var numshares = 12; + var threshold = 7; + secrets.setRNG(); + secrets.init(7); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); + var btcKey = new Bitcoin.ECKey(combined); if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { return false; } return true; }, - testSplitAndCombinePrivateKey3: function () { - var key = "004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; - var numshares = 4; - var threshold = 2; + testCombinePrivateKeyFromXofYShares: function () { + var key = "5K9nHKqbwc1xXpa6wV5p3AaCnubvxQDBukKaFkq7ThAkxgMTMEh"; + // these are 4 of 6 shares + var shares = ["3XxjMASmrkk6eXMM9kAJA7qiqViNVBfiwA1GQDLvg4PVScL", "3Y2DkcPuNX8VKZwpnDdxw55wJtcnCvv2nALqe8nBLViHvck", + "3Y6qv7kyGwgRBKVHVbUNtzmLYAZWQtTPztPwR8wc7uf4MXR", "3YD4TowZn6jw5ss8U89vrcPHonFW4vSs9VKq8MupV5kevG4"] + secrets.setRNG(); + secrets.init(7); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); - - if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + var btcKey = new Bitcoin.ECKey(combined); + if (btcKey.getBitcoinWalletImportFormat() != key) { return false; } return true; diff --git a/src/bitaddress-ui.html b/src/bitaddress-ui.html index 123065e..5ff8e89 100644 --- a/src/bitaddress-ui.html +++ b/src/bitaddress-ui.html @@ -130,13 +130,13 @@
@@ -338,17 +338,13 @@
-
- - - + +
- - - + + -
@@ -358,10 +354,13 @@
- - - - + +
+
+
+ +
+
diff --git a/src/main.css b/src/main.css index 64fdbe1..cd9b645 100644 --- a/src/main.css +++ b/src/main.css @@ -4,7 +4,7 @@ no-repeat left center; width: 17px; height: 17px; display: inline-block; float: right; } a { position: relative; z-index: 20; } .right { text-align: right; } -.walletarea { display: none; border: 2px solid #53c100; } +.walletarea { display: none; border: 2px solid #009900; } hr { margin: 20px 0; border-top: 2px dashed #008000; } .keyarea { height: 110px; text-align: left; position: relative; padding: 5px; } .keyarea .public { float: left; } @@ -21,14 +21,14 @@ body { font-family: Arial; } .question { padding: 10px 15px; text-align: left; cursor: pointer; } .question:hover, .expandable:hover { color: #77777A; } .answer { padding: 0 15px 10px 25px; text-align: left; display: none; font-size: 80%; } -.faq { border: 0; border-top: 2px solid #53c100; } +.faq { border: 0; border-top: 2px solid #009900; } #wallets { clear: both; } #btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; } #seedpoolarea { display: none; } #seedpooldisplay { font-family: monospace; font-size: 1em; width: 640px; padding: 15px 5px; word-wrap: break-word; } -.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: #53c100; position: absolute; z-index: 10; } -#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid #53c100; clear: both; } +.seedpoint { width: 6px; height: 6px; display: block; border-radius: 3px; background-color: #009900; position: absolute; z-index: 10; } +#generate { font-family: monospace; font-size: 1.25em; height: 305px; text-align: left; position: relative; padding: 5px; border: 2px solid #009900; clear: both; } #generate span { padding: 5px 5px 0 5px; } #generatekeyinput { position: relative; z-index: 20; } #keyarea { height: 250px; } @@ -41,14 +41,14 @@ body { font-family: Arial; } #keyarea .public { width: 30%; display: table-cell; } #singlearea { font-size: 90%; } #singlesecret { position: relative; top: -130px; float: right; right: 200px; color: red; font-weight: bolder; font-size: 200%; } -#singleshare { position: relative; top: -110px; float: left; left: 160px; color: #53c100; font-weight: bolder; font-size: 200%; } -#singlesafety { text-align: left; padding: 5px; border-top: 2px solid #53c100; top: -25px; position: relative; } +#singleshare { position: relative; top: -110px; float: left; left: 160px; color: #009900; font-weight: bolder; font-size: 200%; } +#singlesafety { text-align: left; padding: 5px; border-top: 2px solid #009900; top: -25px; position: relative; } -#main { position: relative; text-align: center; margin: 0px auto; width: 810px; } +#main { position: relative; text-align: center; margin: 0px auto; width: 808px; } #logo { width: 578px; height: 80px; } #paperarea { min-height: 120px; display: none; } -#paperarea .keyarea { border: 2px solid #53c100; border-top: 0; } +#paperarea .keyarea { border: 2px solid #009900; border-top: 0; } #paperarea .keyarea.art { display: block; height: auto; border: 0; font-family: Ubuntu, Arial; padding: 0; margin: 0; } #paperarea .artwallet .papersvg { width: 486px; height: 261px; border: 0; margin: 0; padding: 0; left: 0; } #paperarea .artwallet .qrcode_public { top: 52px; left: 17px; z-index: 100; margin: 0; float: none; display: block; position: absolute; background-color: #FFFFFF; @@ -112,15 +112,23 @@ body { font-family: Arial; } #vanityarea .label { text-decoration: underline; } #vanityarea .output { font-family: monospace; font-size: 1.25em; display: block; } #vanityarea .notes { text-align: left; font-size: 80%; padding: 0 0 20px 0; } -#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid #53c100; } +#vanitystep1area { display: none; text-align: left; position: relative; padding: 15px; border-bottom: 2px solid #009900; } #vanitystep1label { padding-left: 5px; } -#vanitystep2area { border-top: 2px solid #53c100; display: block; padding: 15px; } +#vanitystep2area { border-top: 2px solid #009900; display: block; padding: 15px; } #vanitystep2inputs { padding: 0 15px 10px 15px; } #vanitycalc { margin-top: 5px; } +#splitarea { text-align: left; } +#splitarea span { padding: 0; } #splitcommands { padding: 10px 15px; text-align: left; } -#splitstep1area { display: none; text-align: left; position: relative; padding: 0; border-bottom: 2px solid #53c100; } -.splitsharerow { border-bottom: 2px solid #53c100; padding: 15px; } +#combinecommands { padding: 10px 15px; } +#splitstep1area { display: none; text-align: left; position: relative; padding: 0; border-bottom: 2px solid #009900; } +.splitsharerow { border-bottom: 2px solid #009900; padding: 15px; } +.splitsharerow:last-child { border-bottom: 0; } +#combinelabelprivatekey { text-decoration: underline; } +#splitarea .output { display: block; font-family: monospace; font-size: 1.25em; } +#splitarea span.output { display: inline; } +#splitstep2area { padding: 10px 15px; } .englishjson { text-align: center; padding: 40px 0 20px 0; } .unittests { text-align: center; } @@ -142,14 +150,14 @@ body { font-family: Arial; } .menu { text-align: left; margin: 0; padding: 0; display: block; - background-color: #53c100; - - height: 72px; border-top-left-radius: 5px; border-top-right-radius: 5px; + background-color: #009900; /* # 009900 # 53c100 */ + border-top-left-radius: 5px; border-top-right-radius: 5px; } .menu .tab { - position: relative; float: left; margin: 0; list-style: none; z-index: 110; cursor: pointer; - border: 0px solid red; top: 1px; padding: 10px 20px; width: 160px; text-align: center; + position: relative; display: inline-block; border: 0px solid red; + margin: 0; list-style: none; z-index: 110; cursor: pointer; + top: 1px; padding: 10px 20px; width: 162px; text-align: center; } .menu .tab.selected { @@ -163,7 +171,7 @@ body { font-family: Arial; } .menu .tab.selected:hover { color: #000; } .pagebreak { height: 50px; } - .commands { border-bottom: 2px solid #53c100; padding: 10px 2px; margin-bottom: 0; } + .commands { border-bottom: 2px solid #009900; padding: 10px 2px; margin-bottom: 0; } .commands .row { padding: 0 0; text-align: left; } .commands .row.extra { padding-top: 6px; } .commands span { padding: 0 10px; } @@ -171,8 +179,8 @@ body { font-family: Arial; } .commands span.right { float: right; } .expandable { padding: 10px 15px; text-align: left; cursor: pointer; } - #menu { visibility: visible; font-size: 90%; } - #culturemenu { text-align: right; padding: 0 20px; } + #menu { visibility: hidden; font-size: 90%; } + #culturemenu { text-align: right; padding: 0 20px; margin-bottom: 3px; } #culturemenu span { padding: 3px; } #culturemenu .selected { text-decoration: none; color: #000000; } @@ -185,7 +193,7 @@ body { font-family: Arial; } #detailcommands span { padding: 0 10px; } #detailprivkey { width: 250px; } #detailprivkeypassphrase { width: 250px; } - .paper .commands { border: 2px solid #53c100; } + .paper .commands { border: 2px solid #009900; } #bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; } #bulklimit { width: 45px; } @@ -204,7 +212,7 @@ body { font-family: Arial; } #main { width: auto; } #singlearea { border: 0; } #singlesafety { border: 0; } - #paperarea .keyarea:first-child { border-top: 2px solid #53c100; } + #paperarea .keyarea:first-child { border-top: 2px solid #009900; } #paperarea .keyarea.art:first-child { border: 0; } .pagebreak { height: 1px; } .paper #logo { display: none; } diff --git a/src/ninja.splitwallet.js b/src/ninja.splitwallet.js index 793eb55..61d8bef 100644 --- a/src/ninja.splitwallet.js +++ b/src/ninja.splitwallet.js @@ -2,7 +2,7 @@ ninja.wallets.splitwallet = { open: function () { document.getElementById("splitarea").style.display = "block"; secrets.setRNG(); - secrets.init(7); + secrets.init(7); // 7 bits allows for up to 127 shares }, close: function () { @@ -12,12 +12,16 @@ ninja.wallets.splitwallet = { mkOutputRow: function (s, id, lbltxt) { var row = document.createElement("div"); var label = document.createElement("label"); - label.innerHTML = lbltxt + s; + label.innerHTML = lbltxt; var qr = document.createElement("div"); + var output = document.createElement("span"); + output.setAttribute("class", "output"); + output.innerHTML = s; qr.setAttribute("id", id); row.setAttribute("class", "splitsharerow"); row.appendChild(label); + row.appendChild(output); row.appendChild(qr); row.appendChild(document.createElement("br")); @@ -38,13 +42,12 @@ ninja.wallets.splitwallet = { // Split a private key and update information in the HTML splitKey: function () { try { - var key = new Bitcoin.ECKey(false); - var bitcoinAddress = key.getBitcoinAddress(); - var numshares = parseInt(document.getElementById('splitshares').value); var threshold = parseInt(document.getElementById('splitthreshold').value); - var hexKey = Crypto.util.bytesToHex(key.getBitcoinPrivateKeyByteArray()); - var shares = secrets.share(hexKey, numshares, threshold).map(this.hexToBytes).map(Bitcoin.Base58.encode); + var key = new Bitcoin.ECKey(false); + var bitcoinAddress = key.getBitcoinAddress(); + var shares = ninja.wallets.splitwallet.getFormattedShares(key.getBitcoinHexFormat(), numshares, threshold); + var output = document.createElement("div"); output.setAttribute("id", "splitoutput"); var m = {}; @@ -66,49 +69,33 @@ ninja.wallets.splitwallet = { catch (e) { // browser does not have sufficient JavaScript support to generate a bitcoin address alert(e); - document.getElementById("btcaddress").innerHTML = "error"; - document.getElementById("btcprivwif").innerHTML = "error"; - document.getElementById("qrcode_public").innerHTML = ""; - document.getElementById("qrcode_private").innerHTML = ""; } }, // Combine shares of a private key to retrieve the key combineShares: function () { try { - var element = document.getElementById("combineoutput"); - if (element != null) element.parentNode.removeChild(element); - + document.getElementById("combinedprivatekey").innerHTML = ""; var shares = document.getElementById("combineinput").value.trim().split(/\W+/); - - var combined = secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(this.stripLeadZeros)); - - var privkeyBase58 = new Bitcoin.ECKey(this.hexToBytes(combined)).getBitcoinWalletImportFormat(); - var output = document.createElement("div"); - output.setAttribute("id", "combineoutput"); - var txt = document.createElement("input"); - txt.setAttribute("id", "combineoutputtext"); - txt.setAttribute("value", privkeyBase58); - txt.setAttribute("size", 55); - var lbl = document.createElement("label"); - lbl.innerHTML = "Private key"; - output.appendChild(lbl); - output.appendChild(txt); - document.getElementById("combinecommands").appendChild(output); + var combinedBytes = ninja.wallets.splitwallet.combineFormattedShares(shares); + var privkeyBase58 = new Bitcoin.ECKey(combinedBytes).getBitcoinWalletImportFormat(); + document.getElementById("combinedprivatekey").innerHTML = privkeyBase58; } catch (e) { alert(e); } }, + // generate shares and format them in base58 getFormattedShares: function (key, numshares, threshold) { var shares = secrets.share(key, numshares, threshold).map(ninja.wallets.splitwallet.hexToBytes).map(Bitcoin.Base58.encode); return shares; }, + // combine base58 formatted shares and return a bitcoin byte array combineFormattedShares: function (shares) { var combined = secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(ninja.wallets.splitwallet.stripLeadZeros)); - return combined; + return ninja.wallets.splitwallet.hexToBytes(combined); }, openCloseStep: function (num) { diff --git a/src/ninja.unittests.js b/src/ninja.unittests.js index 4bdb20b..b97f38f 100644 --- a/src/ninja.unittests.js +++ b/src/ninja.unittests.js @@ -461,46 +461,107 @@ }, // test split wallet - testSplitAndCombinePrivateKey: function () { + testSplitAndCombinePrivateKey2of2: function () { + // lowercase hex key + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW + var numshares = 2; + var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); + + if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + return false; + } + return true; + }, + // Example use case #1: + // Division of 3 shares: + // 1 share in a safety deposit box ("Box") + // 1 share at Home + // 1 share at Work + // Threshold of 2 can be redeemed in these permutations + // Home + Box + // Work + Box + // Home + Work + testSplitAndCombinePrivateKey2of3: function () { + // lowercase hex key + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW + var numshares = 3; + var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + shares.shift(); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); + + if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + return false; + } + return true; + }, + testSplitAndCombinePrivateKey2of4: function () { + // uppercase hex key var key = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B"; //5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb var numshares = 4; var threshold = 2; + secrets.setRNG(); + secrets.init(7); + + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + shares.shift(); + shares.shift(); + var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); + var btcKey = new Bitcoin.ECKey(combined); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); //secrets.share(key, numshares, threshold).map(ninja.wallets.splitwallet.hexToBytes).map(Bitcoin.Base58.encode); - var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); // secrets.combine(shares.map(Bitcoin.Base58.decode).map(Crypto.util.bytesToHex).map(ninja.wallets.splitwallet.stripLeadZeros)); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); if (btcKey.getBitcoinHexFormat() != key) { return false; } return true; }, - testSplitAndCombinePrivateKey2: function () { - var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; //0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da 5HpJ4bpHFEMWYwCidjtZHwM2rsMh4PRfmZKV8Y21i7msiUkQKUW - var numshares = 4; - var threshold = 2; + // Example use case #2: + // Division of 13 shares: + // 4 shares in a safety deposit box ("Box") + // 3 shares with good friend Angie + // 3 shares with good friend Fred + // 3 shares with Self at home or office + // Threshold of 7 can be redeemed in these permutations + // Self + Box (no trust to spend your coins but your friends are backing up your shares) + // Angie + Box (Angie will send btc to executor of your will) + // Fred + Box (if Angie hasn't already then Fred will send btc to executor of your will) + // Angie + Fred + Self (bank fire/theft then you with both your friends can spend the coins) + testSplitAndCombinePrivateKey7of13: function () { + var key = "0004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; + var numshares = 12; + var threshold = 7; + secrets.setRNG(); + secrets.init(7); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); + var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); + var btcKey = new Bitcoin.ECKey(combined); if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { return false; } return true; }, - testSplitAndCombinePrivateKey3: function () { - var key = "004d30da67214fa65a41a6493576944c7ea86713b14db437446c7a8df8e13da"; - var numshares = 4; - var threshold = 2; + testCombinePrivateKeyFromXofYShares: function () { + var key = "5K9nHKqbwc1xXpa6wV5p3AaCnubvxQDBukKaFkq7ThAkxgMTMEh"; + // these are 4 of 6 shares + var shares = ["3XxjMASmrkk6eXMM9kAJA7qiqViNVBfiwA1GQDLvg4PVScL", "3Y2DkcPuNX8VKZwpnDdxw55wJtcnCvv2nALqe8nBLViHvck", + "3Y6qv7kyGwgRBKVHVbUNtzmLYAZWQtTPztPwR8wc7uf4MXR", "3YD4TowZn6jw5ss8U89vrcPHonFW4vSs9VKq8MupV5kevG4"] + secrets.setRNG(); + secrets.init(7); - var shares = ninja.wallets.splitwallet.getFormattedShares(key, numshares, threshold); var combined = ninja.wallets.splitwallet.combineFormattedShares(shares); - - var btcKey = new Bitcoin.ECKey(ninja.wallets.splitwallet.hexToBytes(combined)); - - if (btcKey.getBitcoinHexFormat() != key.toUpperCase()) { + var btcKey = new Bitcoin.ECKey(combined); + if (btcKey.getBitcoinWalletImportFormat() != key) { return false; } return true;