X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=src%2Fjs%2Findex.js;h=1e8b61602f32e832e94a1cf9e6a790ebcf8a361d;hb=956e44efd75941d7b6613a947de663923d5be7b3;hp=e63c65fd74cfb1382e00f97650c2c67e4ab29720;hpb=4e9b492ca4e1f90b985cfc2da94bddc60a36e386;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FBIP39.git diff --git a/src/js/index.js b/src/js/index.js index e63c65f..1e8b616 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -38,7 +38,9 @@ DOM.entropyWordCount = DOM.entropyContainer.find(".word-count"); DOM.entropyBinary = DOM.entropyContainer.find(".binary"); DOM.entropyWordIndexes = DOM.entropyContainer.find(".word-indexes"); + DOM.entropyChecksum = DOM.entropyContainer.find(".checksum"); DOM.entropyMnemonicLength = DOM.entropyContainer.find(".mnemonic-length"); + DOM.entropyWeakEntropyOverrideWarning = DOM.entropyContainer.find(".weak-entropy-override-warning"); DOM.entropyFilterWarning = DOM.entropyContainer.find(".filter-warning"); DOM.phrase = $(".phrase"); DOM.passphrase = $(".passphrase"); @@ -87,10 +89,15 @@ DOM.bip141path = $("#bip141-path"); DOM.bip141semantics = $(".bip141-semantics"); DOM.generatedStrength = $(".generate-container .strength"); + DOM.generatedStrengthWarning = $(".generate-container .warning"); DOM.hardenedAddresses = $(".hardened-addresses"); DOM.useBitpayAddressesContainer = $(".use-bitpay-addresses-container"); DOM.useBitpayAddresses = $(".use-bitpay-addresses"); + DOM.useBip38 = $(".use-bip38"); + DOM.bip38Password = $(".bip38-password"); DOM.addresses = $(".addresses"); + DOM.csvTab = $("#csv-tab a"); + DOM.csv = $(".csv"); DOM.rowsToAdd = $(".rows-to-add"); DOM.more = $(".more"); DOM.moreRowsStartIndex = $(".more-rows-start-index"); @@ -109,6 +116,7 @@ function init() { // Events + DOM.generatedStrength.on("change", generatedStrengthChanged); DOM.network.on("change", networkChanged); DOM.bip32Client.on("change", bip32ClientChanged); DOM.useEntropy.on("change", setEntropyVisibility); @@ -131,10 +139,13 @@ DOM.bip141semantics.on("change", tabChanged); DOM.tab.on("shown.bs.tab", tabChanged); DOM.hardenedAddresses.on("change", calcForDerivationPath); + DOM.useBip38.on("change", calcForDerivationPath); + DOM.bip38Password.on("change", calcForDerivationPath); DOM.indexToggle.on("click", toggleIndexes); DOM.addressToggle.on("click", toggleAddresses); DOM.publicKeyToggle.on("click", togglePublicKeys); DOM.privateKeyToggle.on("click", togglePrivateKeys); + DOM.csvTab.on("click", updateCsv); DOM.languages.on("click", languageChanged); DOM.useBitpayAddresses.on("change", useBitpayAddressesChange); setQrEvents(DOM.showQrEls); @@ -147,6 +158,16 @@ // Event handlers + function generatedStrengthChanged() { + var strength = parseInt(DOM.generatedStrength.val()); + if (strength < 12) { + DOM.generatedStrengthWarning.removeClass("hidden"); + } + else { + DOM.generatedStrengthWarning.addClass("hidden"); + } + } + function networkChanged(e) { clearDerivedKeys(); clearAddressesList(); @@ -720,7 +741,8 @@ DOM.extendedPubKey.val(extendedPubKey); // Display the addresses and privkeys clearAddressesList(); - displayAddresses(0, 20); + var initialAddressCount = parseInt(DOM.rowsToAdd.val()); + displayAddresses(0, initialAddressCount); } function displayAddresses(start, total) { @@ -763,6 +785,8 @@ var self = this; this.shouldGenerate = true; var useHardenedAddresses = DOM.hardenedAddresses.prop("checked"); + var useBip38 = DOM.useBip38.prop("checked"); + var bip38password = DOM.bip38Password.val(); var isSegwit = segwitSelected(); var segwitAvailable = networkHasSegwit(); var isP2wpkh = p2wpkhSelected(); @@ -777,6 +801,7 @@ if (!self.shouldGenerate) { return; } + // derive HDkey for this row of the table var key = "NA"; if (useHardenedAddresses) { key = bip32ExtendedKey.deriveHardened(index); @@ -784,19 +809,36 @@ else { key = bip32ExtendedKey.derive(index); } - var address = key.getAddress().toString(); + // bip38 requires uncompressed keys + // see https://github.com/iancoleman/bip39/issues/140#issuecomment-352164035 + var keyPair = key.keyPair; + var useUncompressed = useBip38; + if (useUncompressed) { + keyPair = new bitcoinjs.bitcoin.ECPair(keyPair.d, null, { compressed: false }); + } + // get address + var address = keyPair.getAddress().toString(); + // get privkey + var hasPrivkey = !key.isNeutered(); var privkey = "NA"; - if (!key.isNeutered()) { - privkey = key.keyPair.toWIF(network); + if (hasPrivkey) { + privkey = keyPair.toWIF(network); + // BIP38 encode private key if required + if (useBip38) { + privkey = bitcoinjsBip38.encrypt(keyPair.d.toBuffer(), false, bip38password, function(p) { + console.log("Progressed " + p.percent.toFixed(1) + "% for index " + index); + }); + } } - var pubkey = key.getPublicKeyBuffer().toString('hex'); + // get pubkey + var pubkey = keyPair.getPublicKeyBuffer().toString('hex'); var indexText = getDerivationPath() + "/" + index; if (useHardenedAddresses) { indexText = indexText + "'"; } // Ethereum values are different if (networks[DOM.network.val()].name == "ETH - Ethereum") { - var privKeyBuffer = key.keyPair.d.toBuffer(); + var privKeyBuffer = keyPair.d.toBuffer(32); privkey = privKeyBuffer.toString('hex'); var addressBuffer = ethUtil.privateToAddress(privKeyBuffer); var hexAddress = addressBuffer.toString('hex'); @@ -831,6 +873,7 @@ addAddressToList(indexText, address, pubkey, privkey); if (isLast) { hidePending(); + updateCsv(); } }, 50) } @@ -871,6 +914,7 @@ function clearAddressesList() { DOM.addresses.empty(); + DOM.csv.val(""); stopGenerating(); } @@ -1147,6 +1191,17 @@ mnemonicLength = parseInt(mnemonicLength); var numberOfBits = 32 * mnemonicLength / 3; bits = bits.substring(0, numberOfBits); + // show warning for weak entropy override + if (mnemonicLength / 3 * 32 > entropy.binaryStr.length) { + DOM.entropyWeakEntropyOverrideWarning.removeClass("hidden"); + } + else { + DOM.entropyWeakEntropyOverrideWarning.addClass("hidden"); + } + } + else { + // hide warning for weak entropy override + DOM.entropyWeakEntropyOverrideWarning.addClass("hidden"); } // Discard trailing entropy var bitsToUse = Math.floor(bits.length / 32) * 32; @@ -1165,6 +1220,8 @@ DOM.phrase.val(phrase); // Show the word indexes showWordIndexes(); + // Show the checksum + showChecksum(); } function clearEntropyFeedback() { @@ -1195,13 +1252,14 @@ var entropyTypeStr = getEntropyTypeStr(entropy); var wordCount = Math.floor(numberOfBits / 32) * 3; var bitsPerEvent = entropy.bitsPerEvent.toFixed(2); + var spacedBinaryStr = addSpacesEveryElevenBits(entropy.binaryStr); DOM.entropyFiltered.html(entropy.cleanHtml); DOM.entropyType.text(entropyTypeStr); DOM.entropyCrackTime.text(timeToCrack); DOM.entropyEventCount.text(entropy.base.ints.length); DOM.entropyBits.text(numberOfBits); DOM.entropyWordCount.text(wordCount); - DOM.entropyBinary.text(entropy.binaryStr); + DOM.entropyBinary.text(spacedBinaryStr); DOM.entropyBitsPerEvent.text(bitsPerEvent); // detect and warn of filtering var rawNoSpaces = DOM.entropy.val().replace(/\s/g, ""); @@ -1426,7 +1484,100 @@ DOM.entropyWordIndexes.text(wordIndexesStr); } + function showChecksum() { + var phrase = DOM.phrase.val(); + var words = phraseToWordArray(phrase); + var checksumBitlength = words.length / 3; + var checksum = ""; + var binaryStr = ""; + var language = getLanguage(); + for (var i=words.length-1; i>=0; i--) { + var word = words[i]; + var wordIndex = WORDLISTS[language].indexOf(word); + var wordBinary = wordIndex.toString(2); + while (wordBinary.length < 11) { + wordBinary = "0" + wordBinary; + } + var binaryStr = wordBinary + binaryStr; + if (binaryStr.length >= checksumBitlength) { + var start = binaryStr.length - checksumBitlength; + var end = binaryStr.length; + checksum = binaryStr.substring(start, end); + // add spaces so the last group is 11 bits, not the first + checksum = checksum.split("").reverse().join("") + checksum = addSpacesEveryElevenBits(checksum); + checksum = checksum.split("").reverse().join("") + break; + } + } + DOM.entropyChecksum.text(checksum); + } + + function updateCsv() { + var tableCsv = "path,address,public key,private key\n"; + var rows = DOM.addresses.find("tr"); + for (var i=0; i