v3.0.0 add session log of keypairs

This commit is contained in:
pointbiz 2015-10-25 14:54:59 -04:00
parent 3cd0ae0e8b
commit c26f106f81
17 changed files with 686 additions and 238 deletions

View file

@ -28,6 +28,10 @@ END USER NOTES:
Here is a signed list of file names and version history.
2015-10-25: status ACTIVE
bitaddress.org-v3.0.0-SHA256-4781574ca09c07f65d1966619f37a762aac6decd8732cacc85b2f2f972f82751.html
- add session log icon that shows all the key pairs generated during the current session.
2015-08-16: status ACTIVE
bitaddress.org-v2.9.11-SHA256-40376eddc790a63d9afcfb72c0a45002827da965f3bfe6ba8c330e697bf188b2.html
- add status icons for checking the URI protocol used, support for window.crypto.getRandomValues

View file

@ -31,6 +31,10 @@ END USER NOTES:
Here is a signed list of file names and version history.
2015-10-25: status ACTIVE
bitaddress.org-v3.0.0-SHA256-4781574ca09c07f65d1966619f37a762aac6decd8732cacc85b2f2f972f82751.html
- add session log icon that shows all the key pairs generated during the current session.
2015-08-16: status ACTIVE
bitaddress.org-v2.9.11-SHA256-40376eddc790a63d9afcfb72c0a45002827da965f3bfe6ba8c330e697bf188b2.html
- add status icons for checking the URI protocol used, support for window.crypto.getRandomValues
@ -298,11 +302,11 @@ bitaddress.org-v0.1-SHA1-f40e706490f3eb2be56c31ddbf4c8646cd51ef40.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (MingW32)
iQEcBAEBAgAGBQJV0Pu2AAoJEIdJe5Fjl09avHcH/31ABkos0xmXJ6E1PC2P7SUN
SiX6ehnKVTnMzbu1APhyJ2hlUrpSOBS0bbRhlHc9M6X2z8aqBT5izRba6V3LnxJ7
dxtPPv++ieJR/5s6EIrbn1I1f+eoCxKCtuFabmqq/Pmc+ChMbjsWbTz/Nx1lwjSq
F/a1k5RqEL3qMPHTc2eCwT4UdlcNr5YKm/8bJTbRKFygWGfj7U0ryX87h+xI3D2s
jO69dtU6hiQ490574Hl1w9cDMbAdNkhXP5TXZ9Tl7Lqs1SMgHZ9Shi+8xjiOyUSM
7hnRbGyK+r2DbpyLzFnayvez1Zu3oELa5qKNgIq1rS3XLZzxPXId+0li/MRSn1c=
=5DkH
iQEcBAEBAgAGBQJWLSSBAAoJEIdJe5Fjl09ajBMIAJobpBphyjpCmkt8CIzILevd
YZ4M9wCXq0rDj8oRmHzwHDU/UEqTQtmw0NK4juogBiBdfQ6SKon2woISYw1jg6g7
EaL3n14dykaXxCvRCo8uTqLr3zsK0w9bxBa0GQ9uh+xG61vM9B14bFkVn90zXLsw
njn+wyibt4b4NmymKdklU4Ds1jTfquAB/9+dbi7DT5p6hcCGTcvWqekLyVYzVmzO
fiAmbQyWgGC1GvvRsIhi5vgzhLttXaC/K6s4Ypb74Qd5Uw7DO3qje3pmdZF1V/gQ
HOsL43jhqFSeGz2SbdJZLQOi1l9GA04cODJsa18tyiZ5ItbXfp9XKL/EcIG1mV4=
=8hP9
-----END PGP SIGNATURE-----

View file

@ -5620,11 +5620,83 @@ Bitcoin.ECDSA = (function () {
})();
</script>
<script type="text/javascript">
Bitcoin.KeyPool = (function () {
var KeyPool = function () {
this.keyArray = [];
this.push = function (item) {
if (item == null || item.priv == null) return;
var doAdd = true;
// prevent duplicates from being added to the array
for (var index in this.keyArray) {
var currentItem = this.keyArray[index];
if (currentItem != null && currentItem.priv != null && item.getBitcoinAddress() == currentItem.getBitcoinAddress()) {
doAdd = false;
break;
}
}
if (doAdd) this.keyArray.push(item);
};
this.reset = function () {
this.keyArray = [];
};
this.getArray = function () {
return this.keyArray;
};
this.setArray = function (ka) {
this.keyArray = ka;
};
this.length = function () {
return this.keyArray.length;
};
this.toString = function () {
var keyPoolString = "# = " + this.length() + "\n";
var pool = this.getArray();
for (var index in pool) {
var item = pool[index];
if (Bitcoin.Util.hasMethods(item, 'getBitcoinAddress', 'toString')) {
if (item != null) {
keyPoolString += "\"" + item.getBitcoinAddress() + "\"" + ", \"" + item.toString("wif") + "\"\n";
}
}
}
return keyPoolString;
};
return this;
};
return new KeyPool();
})();
Bitcoin.Bip38Key = (function () {
var Bip38 = function (address, encryptedKey) {
this.address = address;
this.priv = encryptedKey;
};
Bip38.prototype.getBitcoinAddress = function () {
return this.address;
};
Bip38.prototype.toString = function () {
return this.priv;
};
return Bip38;
})();
//https://raw.github.com/pointbiz/bitcoinjs-lib/9b2f94a028a7bc9bed94e0722563e9ff1d8e8db8/src/eckey.js
Bitcoin.ECKey = (function () {
var ECDSA = Bitcoin.ECDSA;
var KeyPool = Bitcoin.KeyPool;
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
var rng = new SecureRandom();
var ECKey = function (input) {
if (!input) {
@ -5663,6 +5735,7 @@ Bitcoin.ECKey = (function () {
}
this.compressed = (this.compressed == undefined) ? !!ECKey.compressByDefault : this.compressed;
KeyPool.push(this);
};
ECKey.privateKeyPrefix = 0x80; // mainnet 0x80 testnet 0xEF
@ -5754,6 +5827,7 @@ Bitcoin.ECKey = (function () {
// Sipa Private Key Wallet Import Format
ECKey.prototype.getBitcoinWalletImportFormat = function () {
var bytes = this.getBitcoinPrivateKeyByteArray();
if (bytes == null) return "";
bytes.unshift(ECKey.privateKeyPrefix); // prepend 0x80 byte
if (this.compressed) bytes.push(0x01); // append 0x01 byte for compressed format
var checksum = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
@ -5773,6 +5847,7 @@ Bitcoin.ECKey = (function () {
};
ECKey.prototype.getBitcoinPrivateKeyByteArray = function () {
if (this.priv == null) return null;
// Get a copy of private key as a byte array
var bytes = this.priv.toByteArrayUnsigned();
// zero pad if private key is less than 32 bytes
@ -5995,6 +6070,16 @@ Bitcoin.Util = {
// double sha256
dsha256: function (data) {
return Crypto.SHA256(Crypto.SHA256(data, { asBytes: true }), { asBytes: true });
},
// duck typing method
hasMethods: function(obj /*, method list as strings */){
var i = 1, methodName;
while((methodName = arguments[i++])){
if(typeof obj[methodName] != 'function') {
return false;
}
}
return true;
}
};
</script>
@ -6321,7 +6406,7 @@ body, html { height: 99%; }
.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 #009900; }
.button {}
.button { margin-left: 5px; margin-right: 5px; }
#wallets { clear: both; }
#btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; }
@ -6502,7 +6587,7 @@ body, html { height: 99%; }
#bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; }
#bulklimit { width: 45px; }
.footer { font-size: 90%; clear: both; width: 750px; padding: 10px 0 10px 0; margin: 50px auto auto auto; }
.footer { font-size: 90%; clear: both; width: 770px; padding: 10px 0 10px 0; margin: 50px auto auto auto; }
.footer div span.item { padding: 10px; }
.footer .authorbtc { float: left; width: 470px; }
.footer .authorbtc span.item { text-align: left; display: block; padding: 0 20px; }
@ -6589,6 +6674,7 @@ body, html { height: 99%; }
<span class="print"><input type="button" name="print" value="Print" id="singleprint" onclick="window.print();" /></span>
</div>
</div>
<div class="body">
<div id="keyarea" class="keyarea">
<div class="public">
<div class="pubaddress">
@ -6620,6 +6706,7 @@ body, html { height: 99%; }
<p id="singletip5"><b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.</p>
</div>
</div>
</div>
<div id="paperarea">
<div class="commands">
@ -6919,10 +7006,18 @@ body, html { height: 99%; }
<div class="tooltip" id="statusprotocolbad">
<span class="statuswarn" id="statuslabelprotocolbad">&#9888; Think twice!</span>
<span id="statuslabelprotocolbad1">You appear to be running this generator online from a live website. For valuable wallets it is recommended to</span>
<a id="statuslabelprotocolbad2" href="https://github.com/pointbiz/bitaddress.org/archive/v2.9.11.zip">download</a>
<a id="statuslabelprotocolbad2" href="https://github.com/pointbiz/bitaddress.org/archive/v3.0.0.zip">download</a>
<span id="statuslabelprotocolbad3">the zip file from GitHub and run this generator offline as a local html file.</span>
<br /><br /><input type="button" value="OK" class="button" id="statusokprotocolbad" onclick="document.getElementById('statusprotocolbad').style.display = 'none';" />
</div>
<div class="tooltip" id="statuskeypoolgood">
<span id="statuslabelkeypool1">This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.</span>
<textarea rows="20" cols="102" id="keypooltextarea"></textarea>
<br /><br />
<input type="button" value="Refresh" class="button" id="statuskeypoolrefresh" onclick="ninja.status.showKeyPool();" />
<input type="button" value="OK" class="button" id="statusokkeypool" onclick="document.getElementById('statuskeypoolgood').style.display = 'none';" />
</div>
</div>
<div class="authorbtc">
@ -6931,22 +7026,24 @@ body, html { height: 99%; }
<span class="statusicon" id="statusprotocol" onclick="ninja.status.showProtocol();">...</span>
<span class="statusicon" id="statuscrypto" onclick="ninja.status.showCrypto();">...</span>
<span class="statusicon" id="statusunittests" onclick="ninja.status.showUnitTests();">...</span>
<span class="statusicon" id="statuskeypool" onclick="ninja.status.showKeyPool();"></span>
</span>
<span class="item"><span id="footerlabeldonations">Donations:</span> <b>1NiNja</b>1bUmhSoTXozBRBEtR8LeF9TGbZBN</span>
<span class="item" id="footerlabeltranslatedby"></span>
<span class="item"><a href="https://github.com/pointbiz/bitaddress.org" target="_blank" id="footerlabelgithub">GitHub Repository</a>
(<a href="https://github.com/pointbiz/bitaddress.org/archive/v2.9.11.zip" target="_blank" id="footerlabelgithubzip">zip</a>)</span>
(<a href="https://github.com/pointbiz/bitaddress.org/archive/v3.0.0.zip" target="_blank" id="footerlabelgithubzip">zip</a>)</span>
</div>
</div>
<div class="authorpgp">
<span class="item">
<a href="CHANGELOG.txt.asc" target="_blank"><span id="footerlabelversion">Version History</span> (2.9.11)</a>
<a href="CHANGELOG.txt.asc" target="_blank"><span id="footerlabelversion">Version History</span> (3.0.0)</a>
</span>
<span class="item">527B 5C82 B1F6 B2DB 72A0<br />ECBF 8749 7B91 6397 4F5A</span>
<span class="item">
(<a href="ninja_bitaddress.org.txt" target="_blank" id="footerlabelpgp">PGP</a>)
(<a href="pointbiz_bitaddress.org.asc" target="_blank" id="footerlabelpgp">PGP</a>)
(<a href="javascript:window.location=window.location.pathname+'.sig';" target="_blank" id="footerlabelsig">sig</a>)
</span>
</div>
<div class="copyright">
<span id="footerlabelcopyright1">Copyright bitaddress.org.</span>
@ -7091,9 +7188,12 @@ ninja.privateKey = {
var prefactorB = prefactorA.concat(ownerentropy); // ownerentropy using closure
passfactor = Bitcoin.Util.dsha256(prefactorB);
}
// remove this ECKey from the pool (because user does not see it)
var userKeyPool = Bitcoin.KeyPool.getArray();
Bitcoin.KeyPool.reset();
var kp = new Bitcoin.ECKey(passfactor);
var passpoint = kp.setCompressed(true).getPub();
Bitcoin.KeyPool.setArray(userKeyPool);
var encryptedpart2 = hex.slice(23, 23 + 16);
var addresshashplusownerentropy = hex.slice(3, 3 + 12);
@ -7557,6 +7657,8 @@ ninja.publicKey = {
document.getElementById("statusunittests").innerHTML = "&times;"; //×
unitTestsCase = "bad";
}
// show session log icon
document.getElementById("statuskeypool").innerHTML = "&#8803;"; //≣
};
var showCrypto = function () {
@ -7571,7 +7673,15 @@ ninja.publicKey = {
if(unitTestsCase != "") document.getElementById('statusunittests' + unitTestsCase).style.display = 'block';
};
return { unitTests: unitTests, showCrypto: showCrypto, showProtocol: showProtocol, showUnitTests: showUnitTests };
var showKeyPool = function () {
document.getElementById('statuskeypoolgood').style.display = 'block';
document.getElementById("keypooltextarea").value = Bitcoin.KeyPool.toString();
};
return {
unitTests: unitTests, showCrypto: showCrypto, showProtocol: showProtocol,
showUnitTests: showUnitTests, showKeyPool: showKeyPool
};
}();
})(ninja);
@ -7806,6 +7916,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Generar dirección",
@ -7967,6 +8080,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Générer Une Nouvelle Adresse",
@ -8128,6 +8244,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Δημιουργία μιας νέας Διεύθυνσης",
@ -8289,6 +8408,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Genera un Nuovo Indirizzo",
@ -8450,6 +8572,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Neues Wallet erstellen",
@ -8611,6 +8736,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Vytvořit novou adresu",
@ -8772,6 +8900,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Új cím előállítása",
@ -8939,6 +9070,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "新アドレス生成",
@ -9107,6 +9241,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Gerar endereço",
@ -9268,6 +9405,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "生成新地址",
@ -9435,6 +9575,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Сгенерировать новый адрес",
@ -9573,14 +9716,15 @@ ninja.translator.showEnglishJson = function () {
</script>
<script type="text/javascript">
ninja.wallets.singlewallet = {
(function (wallets) {
var single = wallets.singlewallet = {
isOpen: function () {
return (document.getElementById("singlewallet").className.indexOf("selected") != -1);
},
open: function () {
if (document.getElementById("btcaddress").innerHTML == "") {
ninja.wallets.singlewallet.generateNewAddressAndKey();
single.generateNewAddressAndKey();
}
document.getElementById("singlearea").style.display = "block";
},
@ -9612,7 +9756,8 @@ ninja.wallets.singlewallet = {
document.getElementById("qrcode_private").innerHTML = "";
}
}
};
};
})(ninja.wallets);
</script>
<script type="text/javascript">
ninja.wallets.paperwallet = {
@ -9718,6 +9863,7 @@ ninja.wallets.paperwallet = {
generateNewWallet: function (idPostFix) {
if (ninja.wallets.paperwallet.encrypt) {
ninja.privateKey.BIP38GenerateECAddressAsync(ninja.wallets.paperwallet.intermediatePoint, false, function (address, encryptedKey) {
Bitcoin.KeyPool.push(new Bitcoin.Bip38Key(address, encryptedKey));
if (ninja.wallets.paperwallet.useArtisticWallet) {
ninja.wallets.paperwallet.showArtisticWallet(idPostFix, address, encryptedKey);
}
@ -10229,7 +10375,10 @@ ninja.wallets.detailwallet = {
},
populateKeyDetails: function (btcKey) {
if (btcKey.priv != null) {
// get the original compression value and set it back later in this function
var originalCompression = btcKey.compressed;
btcKey.setCompressed(false);
document.getElementById("detailprivhex").innerHTML = btcKey.toString().toUpperCase();
document.getElementById("detailprivb64").innerHTML = btcKey.toString("base64");
@ -10244,6 +10393,9 @@ ninja.wallets.detailwallet = {
document.getElementById("detailpubkeycomp").innerHTML = btcKey.getPubKeyHex();
document.getElementById("detailaddresscomp").innerHTML = bitcoinAddressComp;
document.getElementById("detailprivwifcomp").innerHTML = wifComp;
btcKey.setCompressed(originalCompression); // to satisfy the key pool
var pool1 = new Bitcoin.ECKey(wif); // to satisfy the key pool
var pool2 = new Bitcoin.ECKey(wifComp); // to satisfy the key pool
ninja.qrCode.showQrCode({
"detailqrcodepublic": bitcoinAddress,
@ -10438,22 +10590,31 @@ ninja.wallets.splitwallet = {
document.body.appendChild(div);
document.getElementById("busyblock").className = "";
}
Bitcoin.KeyPool.reset(); // reset the key pool so users don't see the test keys
return { passCount: passCount, testCount: testCount };
},
runAsynchronousTests: function () {
runAsynchronousTests: function (showOutput) {
if (showOutput) {
var div = document.createElement("div");
div.setAttribute("class", "unittests");
div.setAttribute("id", "asyncunittests");
div.innerHTML = "<h3>Async Unit Tests</h3><div id=\"asyncunittestresults\"></div><br/><br/><br/><br/>";
document.body.appendChild(div);
}
var userKeyPool = Bitcoin.KeyPool.getArray();
// run the asynchronous tests one after another so we don't crash the browser
ninja.foreachSerialized(ninja.unitTests.asynchronousTests, function (name, cb) {
Bitcoin.KeyPool.reset();
document.getElementById("busyblock").className = "busy";
ninja.unitTests.asynchronousTests[name](cb);
}, function () {
if (showOutput) {
document.getElementById("asyncunittestresults").innerHTML += "running of asynchronous unit tests complete!<br/>";
}
console.log("running of asynchronous unit tests complete!");
Bitcoin.KeyPool.setArray(userKeyPool);
document.getElementById("busyblock").className = "";
});
},
@ -10697,7 +10858,8 @@ ninja.wallets.splitwallet = {
var key = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
var btcKey = new Bitcoin.ECKey(key);
if (btcKey.getBitcoinWalletImportFormat() != "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S"
|| btcKey.getPubPoint().compressed != true) {
|| btcKey.getPubPoint().compressed != true
|| btcKey.compressed != true) {
return false;
}
return true;
@ -10705,7 +10867,8 @@ ninja.wallets.splitwallet = {
testWifToECKey: function () {
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var btcKey = new Bitcoin.ECKey(key);
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb") {
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb"
|| btcKey.compressed == true) {
return false;
}
return true;
@ -10969,6 +11132,46 @@ ninja.wallets.splitwallet = {
return false;
}
return true;
},
//Bitcoin.KeyPool tests
testKeyPoolStoresCompressedAndUncompressedKey: function () {
var keyUncompressed = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var keyCompressed = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
Bitcoin.KeyPool.reset();
var btcKeyUncompressed = new Bitcoin.ECKey(keyUncompressed);
var btcKeyCompressed = new Bitcoin.ECKey(keyCompressed);
var pool = Bitcoin.KeyPool.getArray();
if (pool.length != 2
|| pool[0].getBitcoinWalletImportFormat() != keyUncompressed
|| pool[1].getBitcoinWalletImportFormat() != keyCompressed
) {
return false;
}
return true;
},
testKeyPoolPreventDuplicatesWhenAdding: function () {
var keyUncompressed = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var keyCompressed = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
var keyHex = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B";
Bitcoin.KeyPool.reset();
var btcKeyUncompressed = new Bitcoin.ECKey(keyUncompressed);
var btcKeyCompressed = new Bitcoin.ECKey(keyCompressed);
var btcKeyCompressed2 = new Bitcoin.ECKey(keyCompressed);
var btcKeyUncompressed2 = new Bitcoin.ECKey(keyUncompressed);
var btcKeyHex = new Bitcoin.ECKey(keyHex);
var pool = Bitcoin.KeyPool.getArray();
if (pool.length != 2
|| pool[0].getBitcoinWalletImportFormat() != keyUncompressed
|| pool[1].getBitcoinWalletImportFormat() != keyCompressed
) {
return false;
}
return true;
}
},
@ -10989,43 +11192,50 @@ ninja.wallets.splitwallet = {
["6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j", "MOLON LABE", "5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8"],
["6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH", Crypto.charenc.UTF8.bytesToString([206, 156, 206, 159, 206, 155, 206, 169, 206, 157, 32, 206, 155, 206, 145, 206, 146, 206, 149])/*UTF-8 characters, encoded in source so they don't get corrupted*/, "5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D"]];
var waitTimeMs = 60000;
// running each test uses a lot of memory, which isn't freed
// immediately, so give the VM a little time to reclaim memory
function waitThenCall(callback) {
return function () { setTimeout(callback, 10000); }
return function () { setTimeout(callback, waitTimeMs); }
}
var decryptTest = function (test, i, onComplete) {
function log(str) {
if (document.getElementById("asyncunittestresults")) document.getElementById("asyncunittestresults").innerHTML += str + "<br/>";
console.log(str);
}
var decryptBip38Test = function (test, i, onComplete) {
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(test[0], test[1], function (privBytes) {
if (privBytes.constructor == Error) {
document.getElementById("asyncunittestresults").innerHTML += "fail testDecryptBip38 #" + i + ", error: " + privBytes.message + "<br/>";
log("fail decryptBip38Test #" + i + ", error: " + privBytes.message);
} else {
var btcKey = new Bitcoin.ECKey(privBytes);
var wif = !test[2].substr(0, 1).match(/[LK]/) ? btcKey.setCompressed(false).getBitcoinWalletImportFormat() : btcKey.setCompressed(true).getBitcoinWalletImportFormat();
if (wif != test[2]) {
document.getElementById("asyncunittestresults").innerHTML += "fail testDecryptBip38 #" + i + "<br/>";
log("fail decryptBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "pass testDecryptBip38 #" + i + "<br/>";
log("pass decryptBip38Test #" + i);
}
}
onComplete();
});
};
var encryptTest = function (test, compressed, i, onComplete) {
var encryptBip38Test = function (test, compressed, i, onComplete) {
ninja.privateKey.BIP38PrivateKeyToEncryptedKeyAsync(test[2], test[1], compressed, function (encryptedKey) {
if (encryptedKey === test[0]) {
document.getElementById("asyncunittestresults").innerHTML += "pass testBip38Encrypt #" + i + "<br/>";
log("pass encryptBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Encrypt #" + i + "<br/>";
document.getElementById("asyncunittestresults").innerHTML += "expected " + test[0] + "<br/>received " + encryptedKey + "<br/>";
log("fail encryptBip38Test #" + i);
log("expected " + test[0] + "<br/>received " + encryptedKey);
}
onComplete();
});
};
// test randomly generated encryption-decryption cycle
var cycleTest = function (i, compress, onComplete) {
var cycleBip38Test = function (i, compress, onComplete) {
// create new private key
var privKey = (new Bitcoin.ECKey(false)).getBitcoinWalletImportFormat();
@ -11036,11 +11246,11 @@ ninja.wallets.splitwallet = {
var decryptedKey = (new Bitcoin.ECKey(decryptedBytes)).getBitcoinWalletImportFormat();
if (decryptedKey === privKey) {
document.getElementById("asyncunittestresults").innerHTML += "pass cycleBip38 test #" + i + "<br/>";
log("pass cycleBip38Test #" + i);
}
else {
document.getElementById("asyncunittestresults").innerHTML += "fail cycleBip38 test #" + i + " " + privKey + "<br/>";
document.getElementById("asyncunittestresults").innerHTML += "encrypted key: " + encryptedKey + "<br/>decrypted key: " + decryptedKey;
log("fail cycleBip38Test #" + i + " " + privKey);
log("encrypted key: " + encryptedKey + "<br/>decrypted key: " + decryptedKey);
}
onComplete();
});
@ -11049,20 +11259,20 @@ ninja.wallets.splitwallet = {
// intermediate test - create some encrypted keys from an intermediate
// then decrypt them to check that the private keys are recoverable
var intermediateTest = function (i, onComplete) {
var intermediateBip38Test = function (i, onComplete) {
var pass = Math.random().toString(36).substr(2);
ninja.privateKey.BIP38GenerateIntermediatePointAsync(pass, null, null, function (intermediatePoint) {
ninja.privateKey.BIP38GenerateECAddressAsync(intermediatePoint, false, function (address, encryptedKey) {
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(encryptedKey, pass, function (privBytes) {
if (privBytes.constructor == Error) {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Intermediate #" + i + ", error: " + privBytes.message + "<br/>";
log("fail intermediateBip38Test #" + i + ", error: " + privBytes.message);
} else {
var btcKey = new Bitcoin.ECKey(privBytes);
var btcAddress = btcKey.getBitcoinAddress();
if (address !== btcKey.getBitcoinAddress()) {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Intermediate #" + i + "<br/>";
log("fail intermediateBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "pass testBip38Intermediate #" + i + "<br/>";
log("pass intermediateBip38Test #" + i);
}
}
onComplete();
@ -11071,34 +11281,44 @@ ninja.wallets.splitwallet = {
});
}
document.getElementById("asyncunittestresults").innerHTML += "running " + tests.length + " tests named testDecryptBip38<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 4 tests named testBip38Encrypt<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 2 tests named cycleBip38<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 5 tests named testBip38Intermediate<br/>";
ninja.runSerialized([
var testArray = [
function (cb) {
log("running " + tests.length + " tests named decryptBip38Test");
ninja.forSerialized(0, tests.length, function (i, callback) {
decryptTest(tests[i], i, waitThenCall(callback));
console.log("running decryptBip38Test #" + i + " " + tests[i]);
decryptBip38Test(tests[i], i, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 4 tests named encryptBip38Test");
ninja.forSerialized(0, 4, function (i, callback) {
console.log("running encryptBip38Test #" + i + " " + tests[i]);
// only first 4 test vectors are not EC-multiply,
// compression param false for i = 1,2 and true for i = 3,4
encryptTest(tests[i], i >= 2, i, waitThenCall(callback));
encryptBip38Test(tests[i], i >= 2, i, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 2 tests named cycleBip38Test");
ninja.forSerialized(0, 2, function (i, callback) {
cycleTest(i, i % 2 ? true : false, waitThenCall(callback));
console.log("running cycleBip38Test #" + i);
cycleBip38Test(i, i % 2 ? true : false, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 5 tests named intermediateBip38Test");
ninja.forSerialized(0, 5, function (i, callback) {
intermediateTest(i, waitThenCall(callback));
console.log("running intermediateBip38Test #" + i);
intermediateBip38Test(i, waitThenCall(callback));
}, cb);
}
], done);
];
ninja.runSerialized(testArray, done);
//TODO: ninja.runSerialized([ testArray[0],testArray[1] ], done);
}
}
};
@ -11112,7 +11332,7 @@ if (ninja.getQueryString()["unittests"] == "true" || ninja.getQueryString()["uni
}
// run async unit tests
if (ninja.getQueryString()["asyncunittests"] == "true" || ninja.getQueryString()["asyncunittests"] == "1") {
ninja.unitTests.runAsynchronousTests();
ninja.unitTests.runAsynchronousTests(true);
}
// change language
if (ninja.getQueryString()["culture"] != undefined) {

View file

@ -1,11 +1,11 @@
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (MingW32)
iQEcBAABAgAGBQJV0PvPAAoJEIdJe5Fjl09aUz8IAIen68xsU8MnU56fHx+/KDbx
fiYz88OU7I6PnB+0hlVc5TW5VH4RD8Tzfpc1+31+RyPJ+Va/PUdh68FptcthBsuL
byv3+rrwxRDy/00stIQh1HBd0JJ+mX0DBCqnJ79NdGJJqEHW8D5VgfSnxaqvconi
U5Q1RvzdArb/HcbQ5BvNTXRgHP+TzQBJ3hHyoCxtwOpa3Qd/w5FzkB2TN52mvEiw
U1X0KrT4ntyLYAy4eJb+ecLYHi1dn92BTq8fqkj0mN+inBWb5J0zuZd/aBcqqvRM
qBBPMHBjbXETOunM0PmQ/ENiOugIuN/3JqL0yqVHP1cZxqtLPK5Iw7AVUchPe+E=
=O+Y0
iQEcBAABAgAGBQJWLSSUAAoJEIdJe5Fjl09aOCEIAI4GIeb3WpHoEFzGs0hEaJ/k
UHBlr716hqOyl0bmeTIABYMncJBxXsVtSphtGJChAAw4tdklRmCpXnauSrEnBqMO
bTkjrLXcif5Xot4bJ1+mKJZtDtDj57bpICA/Am7tSchlK/tJNlW5WhV8egd6IF1M
nkcWRz5Jx5VV+sDP0O/WgEYwL3dHZ+M4Q2Be+UCjj8G3GYbFOjocpowtab/2QXRF
Q+3e7XHflMdY04wYc/V/0ZYPx7kUZ456OjNbEIOTkKb0Rb968ERTVGkm0XuDWUuk
4Bay+IaRN6fSsHRtM1Qj6Z6ATSX4ofVh/qBTwjFJESrDz+4tBpbTTNfhvD7av1I=
=HQw+
-----END PGP SIGNATURE-----

View file

@ -1,8 +1,8 @@
{
"name": "bitaddress.org",
"version": "2.9.11",
"sha1sum": "7232900c98e3af634a46374cd7178ce5e33cb96f",
"sha256sum": "40376eddc790a63d9afcfb72c0a45002827da965f3bfe6ba8c330e697bf188b2",
"version": "3.0.0",
"sha1sum": "c3838fd668edd5e51aa262916ac7c286db0ddc51",
"sha256sum": "4781574ca09c07f65d1966619f37a762aac6decd8732cacc85b2f2f972f82751",
"description": "Open Source JavaScript Client-Side Bitcoin Wallet Generator",
"main": "Gruntfile.js",
"dependencies": {

View file

@ -163,6 +163,7 @@
<span class="print"><input type="button" name="print" value="Print" id="singleprint" onclick="window.print();" /></span>
</div>
</div>
<div class="body">
<div id="keyarea" class="keyarea">
<div class="public">
<div class="pubaddress">
@ -194,6 +195,7 @@
<p id="singletip5"><b>Spend your bitcoins</b> by going to blockchain.info and sweep the full balance of your private key into your account at their website. You can also spend your funds by downloading one of the popular bitcoin p2p clients and importing your private key to the p2p client wallet. Keep in mind when you import your single key to a bitcoin p2p client and spend funds your key will be bundled with other private keys in the p2p client wallet. When you perform a transaction your change will be sent to another bitcoin address within the p2p client wallet. You must then backup the p2p client wallet and keep it safe as your remaining bitcoins will be stored there. Satoshi advised that one should never delete a wallet.</p>
</div>
</div>
</div>
<div id="paperarea">
<div class="commands">
@ -497,6 +499,14 @@
<span id="statuslabelprotocolbad3">the zip file from GitHub and run this generator offline as a local html file.</span>
<br /><br /><input type="button" value="OK" class="button" id="statusokprotocolbad" onclick="document.getElementById('statusprotocolbad').style.display = 'none';" />
</div>
<div class="tooltip" id="statuskeypoolgood">
<span id="statuslabelkeypool1">This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.</span>
<textarea rows="20" cols="102" id="keypooltextarea"></textarea>
<br /><br />
<input type="button" value="Refresh" class="button" id="statuskeypoolrefresh" onclick="ninja.status.showKeyPool();" />
<input type="button" value="OK" class="button" id="statusokkeypool" onclick="document.getElementById('statuskeypoolgood').style.display = 'none';" />
</div>
</div>
<div class="authorbtc">
@ -505,6 +515,7 @@
<span class="statusicon" id="statusprotocol" onclick="ninja.status.showProtocol();">...</span>
<span class="statusicon" id="statuscrypto" onclick="ninja.status.showCrypto();">...</span>
<span class="statusicon" id="statusunittests" onclick="ninja.status.showUnitTests();">...</span>
<span class="statusicon" id="statuskeypool" onclick="ninja.status.showKeyPool();"></span>
</span>
<span class="item"><span id="footerlabeldonations">Donations:</span> <b>1NiNja</b>1bUmhSoTXozBRBEtR8LeF9TGbZBN</span>
<span class="item" id="footerlabeltranslatedby"></span>
@ -519,8 +530,9 @@
</span>
<span class="item">527B 5C82 B1F6 B2DB 72A0<br />ECBF 8749 7B91 6397 4F5A</span>
<span class="item">
(<a href="ninja_bitaddress.org.txt" target="_blank" id="footerlabelpgp">PGP</a>)
(<a href="pointbiz_bitaddress.org.asc" target="_blank" id="footerlabelpgp">PGP</a>)
(<a href="javascript:window.location=window.location.pathname+'.sig';" target="_blank" id="footerlabelsig">sig</a>)
</span>
</div>
<div class="copyright">
<span id="footerlabelcopyright1">Copyright bitaddress.org.</span>

View file

@ -1,8 +1,80 @@
Bitcoin.KeyPool = (function () {
var KeyPool = function () {
this.keyArray = [];
this.push = function (item) {
if (item == null || item.priv == null) return;
var doAdd = true;
// prevent duplicates from being added to the array
for (var index in this.keyArray) {
var currentItem = this.keyArray[index];
if (currentItem != null && currentItem.priv != null && item.getBitcoinAddress() == currentItem.getBitcoinAddress()) {
doAdd = false;
break;
}
}
if (doAdd) this.keyArray.push(item);
};
this.reset = function () {
this.keyArray = [];
};
this.getArray = function () {
return this.keyArray;
};
this.setArray = function (ka) {
this.keyArray = ka;
};
this.length = function () {
return this.keyArray.length;
};
this.toString = function () {
var keyPoolString = "# = " + this.length() + "\n";
var pool = this.getArray();
for (var index in pool) {
var item = pool[index];
if (Bitcoin.Util.hasMethods(item, 'getBitcoinAddress', 'toString')) {
if (item != null) {
keyPoolString += "\"" + item.getBitcoinAddress() + "\"" + ", \"" + item.toString("wif") + "\"\n";
}
}
}
return keyPoolString;
};
return this;
};
return new KeyPool();
})();
Bitcoin.Bip38Key = (function () {
var Bip38 = function (address, encryptedKey) {
this.address = address;
this.priv = encryptedKey;
};
Bip38.prototype.getBitcoinAddress = function () {
return this.address;
};
Bip38.prototype.toString = function () {
return this.priv;
};
return Bip38;
})();
//https://raw.github.com/pointbiz/bitcoinjs-lib/9b2f94a028a7bc9bed94e0722563e9ff1d8e8db8/src/eckey.js
Bitcoin.ECKey = (function () {
var ECDSA = Bitcoin.ECDSA;
var KeyPool = Bitcoin.KeyPool;
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
var rng = new SecureRandom();
var ECKey = function (input) {
if (!input) {
@ -41,6 +113,7 @@ Bitcoin.ECKey = (function () {
}
this.compressed = (this.compressed == undefined) ? !!ECKey.compressByDefault : this.compressed;
KeyPool.push(this);
};
ECKey.privateKeyPrefix = 0x80; // mainnet 0x80 testnet 0xEF
@ -132,6 +205,7 @@ Bitcoin.ECKey = (function () {
// Sipa Private Key Wallet Import Format
ECKey.prototype.getBitcoinWalletImportFormat = function () {
var bytes = this.getBitcoinPrivateKeyByteArray();
if (bytes == null) return "";
bytes.unshift(ECKey.privateKeyPrefix); // prepend 0x80 byte
if (this.compressed) bytes.push(0x01); // append 0x01 byte for compressed format
var checksum = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
@ -151,6 +225,7 @@ Bitcoin.ECKey = (function () {
};
ECKey.prototype.getBitcoinPrivateKeyByteArray = function () {
if (this.priv == null) return null;
// Get a copy of private key as a byte array
var bytes = this.priv.toByteArrayUnsigned();
// zero pad if private key is less than 32 bytes

View file

@ -101,5 +101,15 @@ Bitcoin.Util = {
// double sha256
dsha256: function (data) {
return Crypto.SHA256(Crypto.SHA256(data, { asBytes: true }), { asBytes: true });
},
// duck typing method
hasMethods: function(obj /*, method list as strings */){
var i = 1, methodName;
while((methodName = arguments[i++])){
if(typeof obj[methodName] != 'function') {
return false;
}
}
return true;
}
};

View file

@ -23,7 +23,7 @@ body, html { height: 99%; }
.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 #009900; }
.button {}
.button { margin-left: 5px; margin-right: 5px; }
#wallets { clear: both; }
#btcaddress, #btcprivwif, #detailaddress, #detailaddresscomp, #detailprivwif, #detailprivwifcomp { font-family: monospace; font-size: 1.25em; }
@ -204,7 +204,7 @@ body, html { height: 99%; }
#bulkstartindex, #paperlimit, #paperlimitperpage { width: 35px; }
#bulklimit { width: 45px; }
.footer { font-size: 90%; clear: both; width: 750px; padding: 10px 0 10px 0; margin: 50px auto auto auto; }
.footer { font-size: 90%; clear: both; width: 770px; padding: 10px 0 10px 0; margin: 50px auto auto auto; }
.footer div span.item { padding: 10px; }
.footer .authorbtc { float: left; width: 470px; }
.footer .authorbtc span.item { text-align: left; display: block; padding: 0 20px; }

View file

@ -95,7 +95,10 @@ ninja.wallets.detailwallet = {
},
populateKeyDetails: function (btcKey) {
if (btcKey.priv != null) {
// get the original compression value and set it back later in this function
var originalCompression = btcKey.compressed;
btcKey.setCompressed(false);
document.getElementById("detailprivhex").innerHTML = btcKey.toString().toUpperCase();
document.getElementById("detailprivb64").innerHTML = btcKey.toString("base64");
@ -110,6 +113,9 @@ ninja.wallets.detailwallet = {
document.getElementById("detailpubkeycomp").innerHTML = btcKey.getPubKeyHex();
document.getElementById("detailaddresscomp").innerHTML = bitcoinAddressComp;
document.getElementById("detailprivwifcomp").innerHTML = wifComp;
btcKey.setCompressed(originalCompression); // to satisfy the key pool
var pool1 = new Bitcoin.ECKey(wif); // to satisfy the key pool
var pool2 = new Bitcoin.ECKey(wifComp); // to satisfy the key pool
ninja.qrCode.showQrCode({
"detailqrcodepublic": bitcoinAddress,

View file

@ -132,9 +132,12 @@ ninja.privateKey = {
var prefactorB = prefactorA.concat(ownerentropy); // ownerentropy using closure
passfactor = Bitcoin.Util.dsha256(prefactorB);
}
// remove this ECKey from the pool (because user does not see it)
var userKeyPool = Bitcoin.KeyPool.getArray();
Bitcoin.KeyPool.reset();
var kp = new Bitcoin.ECKey(passfactor);
var passpoint = kp.setCompressed(true).getPub();
Bitcoin.KeyPool.setArray(userKeyPool);
var encryptedpart2 = hex.slice(23, 23 + 16);
var addresshashplusownerentropy = hex.slice(3, 3 + 12);

View file

@ -35,6 +35,8 @@
document.getElementById("statusunittests").innerHTML = "&times;"; //×
unitTestsCase = "bad";
}
// show session log icon
document.getElementById("statuskeypool").innerHTML = "&#8803;"; //≣
};
var showCrypto = function () {
@ -49,7 +51,15 @@
if(unitTestsCase != "") document.getElementById('statusunittests' + unitTestsCase).style.display = 'block';
};
return { unitTests: unitTests, showCrypto: showCrypto, showProtocol: showProtocol, showUnitTests: showUnitTests };
var showKeyPool = function () {
document.getElementById('statuskeypoolgood').style.display = 'block';
document.getElementById("keypooltextarea").value = Bitcoin.KeyPool.toString();
};
return {
unitTests: unitTests, showCrypto: showCrypto, showProtocol: showProtocol,
showUnitTests: showUnitTests, showKeyPool: showKeyPool
};
}();
})(ninja);

View file

@ -5,7 +5,7 @@ if (ninja.getQueryString()["unittests"] == "true" || ninja.getQueryString()["uni
}
// run async unit tests
if (ninja.getQueryString()["asyncunittests"] == "true" || ninja.getQueryString()["asyncunittests"] == "1") {
ninja.unitTests.runAsynchronousTests();
ninja.unitTests.runAsynchronousTests(true);
}
// change language
if (ninja.getQueryString()["culture"] != undefined) {

View file

@ -101,6 +101,7 @@ ninja.wallets.paperwallet = {
generateNewWallet: function (idPostFix) {
if (ninja.wallets.paperwallet.encrypt) {
ninja.privateKey.BIP38GenerateECAddressAsync(ninja.wallets.paperwallet.intermediatePoint, false, function (address, encryptedKey) {
Bitcoin.KeyPool.push(new Bitcoin.Bip38Key(address, encryptedKey));
if (ninja.wallets.paperwallet.useArtisticWallet) {
ninja.wallets.paperwallet.showArtisticWallet(idPostFix, address, encryptedKey);
}

View file

@ -1,11 +1,12 @@
ninja.wallets.singlewallet = {
(function (wallets) {
var single = wallets.singlewallet = {
isOpen: function () {
return (document.getElementById("singlewallet").className.indexOf("selected") != -1);
},
open: function () {
if (document.getElementById("btcaddress").innerHTML == "") {
ninja.wallets.singlewallet.generateNewAddressAndKey();
single.generateNewAddressAndKey();
}
document.getElementById("singlearea").style.display = "block";
},
@ -37,4 +38,5 @@ ninja.wallets.singlewallet = {
document.getElementById("qrcode_private").innerHTML = "";
}
}
};
};
})(ninja.wallets);

View file

@ -145,6 +145,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Generar dirección",
@ -306,6 +309,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Générer Une Nouvelle Adresse",
@ -467,6 +473,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Δημιουργία μιας νέας Διεύθυνσης",
@ -628,6 +637,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Genera un Nuovo Indirizzo",
@ -789,6 +801,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Neues Wallet erstellen",
@ -950,6 +965,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Vytvořit novou adresu",
@ -1111,6 +1129,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Új cím előállítása",
@ -1278,6 +1299,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "新アドレス生成",
@ -1446,6 +1470,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Gerar endereço",
@ -1607,6 +1634,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "生成新地址",
@ -1774,6 +1804,9 @@ ninja.translator = {
"statuslabelprotocolbad2": "download", //TODO: please translate
"statuslabelprotocolbad3": "the zip file from GitHub and run this generator offline as a local html file.", //TODO: please translate
"statusokprotocolbad": "OK", //TODO: please translate
"statuslabelkeypool1": "This is a log of all the Bitcoin Addresses and Private Keys you generated during your current session. Reloading the page will create a new session.", //TODO: please translate
"statuskeypoolrefresh": "Refresh", //TODO: please translate
"statusokkeypool": "OK", //TODO: please translate
// single wallet html
"newaddress": "Сгенерировать новый адрес",

View file

@ -38,22 +38,31 @@
document.body.appendChild(div);
document.getElementById("busyblock").className = "";
}
Bitcoin.KeyPool.reset(); // reset the key pool so users don't see the test keys
return { passCount: passCount, testCount: testCount };
},
runAsynchronousTests: function () {
runAsynchronousTests: function (showOutput) {
if (showOutput) {
var div = document.createElement("div");
div.setAttribute("class", "unittests");
div.setAttribute("id", "asyncunittests");
div.innerHTML = "<h3>Async Unit Tests</h3><div id=\"asyncunittestresults\"></div><br/><br/><br/><br/>";
document.body.appendChild(div);
}
var userKeyPool = Bitcoin.KeyPool.getArray();
// run the asynchronous tests one after another so we don't crash the browser
ninja.foreachSerialized(ninja.unitTests.asynchronousTests, function (name, cb) {
Bitcoin.KeyPool.reset();
document.getElementById("busyblock").className = "busy";
ninja.unitTests.asynchronousTests[name](cb);
}, function () {
if (showOutput) {
document.getElementById("asyncunittestresults").innerHTML += "running of asynchronous unit tests complete!<br/>";
}
console.log("running of asynchronous unit tests complete!");
Bitcoin.KeyPool.setArray(userKeyPool);
document.getElementById("busyblock").className = "";
});
},
@ -297,7 +306,8 @@
var key = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
var btcKey = new Bitcoin.ECKey(key);
if (btcKey.getBitcoinWalletImportFormat() != "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S"
|| btcKey.getPubPoint().compressed != true) {
|| btcKey.getPubPoint().compressed != true
|| btcKey.compressed != true) {
return false;
}
return true;
@ -305,7 +315,8 @@
testWifToECKey: function () {
var key = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var btcKey = new Bitcoin.ECKey(key);
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb") {
if (btcKey.getBitcoinWalletImportFormat() != "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb"
|| btcKey.compressed == true) {
return false;
}
return true;
@ -569,6 +580,46 @@
return false;
}
return true;
},
//Bitcoin.KeyPool tests
testKeyPoolStoresCompressedAndUncompressedKey: function () {
var keyUncompressed = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var keyCompressed = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
Bitcoin.KeyPool.reset();
var btcKeyUncompressed = new Bitcoin.ECKey(keyUncompressed);
var btcKeyCompressed = new Bitcoin.ECKey(keyCompressed);
var pool = Bitcoin.KeyPool.getArray();
if (pool.length != 2
|| pool[0].getBitcoinWalletImportFormat() != keyUncompressed
|| pool[1].getBitcoinWalletImportFormat() != keyCompressed
) {
return false;
}
return true;
},
testKeyPoolPreventDuplicatesWhenAdding: function () {
var keyUncompressed = "5J8QhiQtAiozKwyk3GCycAscg1tNaYhNdiiLey8vaDK8Bzm4znb";
var keyCompressed = "KxbhchnQquYQ2dfSxz7rrEaQTCukF4uCV57TkamyTbLzjFWcdi3S";
var keyHex = "292665C3872418ADF1DA7FFA3A646F2F0602246DA6098A91D229C32150F2718B";
Bitcoin.KeyPool.reset();
var btcKeyUncompressed = new Bitcoin.ECKey(keyUncompressed);
var btcKeyCompressed = new Bitcoin.ECKey(keyCompressed);
var btcKeyCompressed2 = new Bitcoin.ECKey(keyCompressed);
var btcKeyUncompressed2 = new Bitcoin.ECKey(keyUncompressed);
var btcKeyHex = new Bitcoin.ECKey(keyHex);
var pool = Bitcoin.KeyPool.getArray();
if (pool.length != 2
|| pool[0].getBitcoinWalletImportFormat() != keyUncompressed
|| pool[1].getBitcoinWalletImportFormat() != keyCompressed
) {
return false;
}
return true;
}
},
@ -589,43 +640,50 @@
["6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j", "MOLON LABE", "5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8"],
["6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH", Crypto.charenc.UTF8.bytesToString([206, 156, 206, 159, 206, 155, 206, 169, 206, 157, 32, 206, 155, 206, 145, 206, 146, 206, 149])/*UTF-8 characters, encoded in source so they don't get corrupted*/, "5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D"]];
var waitTimeMs = 60000;
// running each test uses a lot of memory, which isn't freed
// immediately, so give the VM a little time to reclaim memory
function waitThenCall(callback) {
return function () { setTimeout(callback, 10000); }
return function () { setTimeout(callback, waitTimeMs); }
}
var decryptTest = function (test, i, onComplete) {
function log(str) {
if (document.getElementById("asyncunittestresults")) document.getElementById("asyncunittestresults").innerHTML += str + "<br/>";
console.log(str);
}
var decryptBip38Test = function (test, i, onComplete) {
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(test[0], test[1], function (privBytes) {
if (privBytes.constructor == Error) {
document.getElementById("asyncunittestresults").innerHTML += "fail testDecryptBip38 #" + i + ", error: " + privBytes.message + "<br/>";
log("fail decryptBip38Test #" + i + ", error: " + privBytes.message);
} else {
var btcKey = new Bitcoin.ECKey(privBytes);
var wif = !test[2].substr(0, 1).match(/[LK]/) ? btcKey.setCompressed(false).getBitcoinWalletImportFormat() : btcKey.setCompressed(true).getBitcoinWalletImportFormat();
if (wif != test[2]) {
document.getElementById("asyncunittestresults").innerHTML += "fail testDecryptBip38 #" + i + "<br/>";
log("fail decryptBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "pass testDecryptBip38 #" + i + "<br/>";
log("pass decryptBip38Test #" + i);
}
}
onComplete();
});
};
var encryptTest = function (test, compressed, i, onComplete) {
var encryptBip38Test = function (test, compressed, i, onComplete) {
ninja.privateKey.BIP38PrivateKeyToEncryptedKeyAsync(test[2], test[1], compressed, function (encryptedKey) {
if (encryptedKey === test[0]) {
document.getElementById("asyncunittestresults").innerHTML += "pass testBip38Encrypt #" + i + "<br/>";
log("pass encryptBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Encrypt #" + i + "<br/>";
document.getElementById("asyncunittestresults").innerHTML += "expected " + test[0] + "<br/>received " + encryptedKey + "<br/>";
log("fail encryptBip38Test #" + i);
log("expected " + test[0] + "<br/>received " + encryptedKey);
}
onComplete();
});
};
// test randomly generated encryption-decryption cycle
var cycleTest = function (i, compress, onComplete) {
var cycleBip38Test = function (i, compress, onComplete) {
// create new private key
var privKey = (new Bitcoin.ECKey(false)).getBitcoinWalletImportFormat();
@ -636,11 +694,11 @@
var decryptedKey = (new Bitcoin.ECKey(decryptedBytes)).getBitcoinWalletImportFormat();
if (decryptedKey === privKey) {
document.getElementById("asyncunittestresults").innerHTML += "pass cycleBip38 test #" + i + "<br/>";
log("pass cycleBip38Test #" + i);
}
else {
document.getElementById("asyncunittestresults").innerHTML += "fail cycleBip38 test #" + i + " " + privKey + "<br/>";
document.getElementById("asyncunittestresults").innerHTML += "encrypted key: " + encryptedKey + "<br/>decrypted key: " + decryptedKey;
log("fail cycleBip38Test #" + i + " " + privKey);
log("encrypted key: " + encryptedKey + "<br/>decrypted key: " + decryptedKey);
}
onComplete();
});
@ -649,20 +707,20 @@
// intermediate test - create some encrypted keys from an intermediate
// then decrypt them to check that the private keys are recoverable
var intermediateTest = function (i, onComplete) {
var intermediateBip38Test = function (i, onComplete) {
var pass = Math.random().toString(36).substr(2);
ninja.privateKey.BIP38GenerateIntermediatePointAsync(pass, null, null, function (intermediatePoint) {
ninja.privateKey.BIP38GenerateECAddressAsync(intermediatePoint, false, function (address, encryptedKey) {
ninja.privateKey.BIP38EncryptedKeyToByteArrayAsync(encryptedKey, pass, function (privBytes) {
if (privBytes.constructor == Error) {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Intermediate #" + i + ", error: " + privBytes.message + "<br/>";
log("fail intermediateBip38Test #" + i + ", error: " + privBytes.message);
} else {
var btcKey = new Bitcoin.ECKey(privBytes);
var btcAddress = btcKey.getBitcoinAddress();
if (address !== btcKey.getBitcoinAddress()) {
document.getElementById("asyncunittestresults").innerHTML += "fail testBip38Intermediate #" + i + "<br/>";
log("fail intermediateBip38Test #" + i);
} else {
document.getElementById("asyncunittestresults").innerHTML += "pass testBip38Intermediate #" + i + "<br/>";
log("pass intermediateBip38Test #" + i);
}
}
onComplete();
@ -671,34 +729,44 @@
});
}
document.getElementById("asyncunittestresults").innerHTML += "running " + tests.length + " tests named testDecryptBip38<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 4 tests named testBip38Encrypt<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 2 tests named cycleBip38<br/>";
document.getElementById("asyncunittestresults").innerHTML += "running 5 tests named testBip38Intermediate<br/>";
ninja.runSerialized([
var testArray = [
function (cb) {
log("running " + tests.length + " tests named decryptBip38Test");
ninja.forSerialized(0, tests.length, function (i, callback) {
decryptTest(tests[i], i, waitThenCall(callback));
console.log("running decryptBip38Test #" + i + " " + tests[i]);
decryptBip38Test(tests[i], i, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 4 tests named encryptBip38Test");
ninja.forSerialized(0, 4, function (i, callback) {
console.log("running encryptBip38Test #" + i + " " + tests[i]);
// only first 4 test vectors are not EC-multiply,
// compression param false for i = 1,2 and true for i = 3,4
encryptTest(tests[i], i >= 2, i, waitThenCall(callback));
encryptBip38Test(tests[i], i >= 2, i, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 2 tests named cycleBip38Test");
ninja.forSerialized(0, 2, function (i, callback) {
cycleTest(i, i % 2 ? true : false, waitThenCall(callback));
console.log("running cycleBip38Test #" + i);
cycleBip38Test(i, i % 2 ? true : false, waitThenCall(callback));
}, waitThenCall(cb));
},
}
,
function (cb) {
log("running 5 tests named intermediateBip38Test");
ninja.forSerialized(0, 5, function (i, callback) {
intermediateTest(i, waitThenCall(callback));
console.log("running intermediateBip38Test #" + i);
intermediateBip38Test(i, waitThenCall(callback));
}, cb);
}
], done);
];
ninja.runSerialized(testArray, done);
//TODO: ninja.runSerialized([ testArray[0],testArray[1] ], done);
}
}
};