diff options
-rw-r--r-- | bip39-standalone.html | 128 |
1 files changed, 84 insertions, 44 deletions
diff --git a/bip39-standalone.html b/bip39-standalone.html index 5fd3517..8476014 100644 --- a/bip39-standalone.html +++ b/bip39-standalone.html | |||
@@ -17940,6 +17940,7 @@ var time_estimates;time_estimates={estimate_attack_times:function(e){var t,n,s,o | |||
17940 | * dice 6 [1-6] | 17940 | * dice 6 [1-6] |
17941 | * decimal [0-9] | 17941 | * decimal [0-9] |
17942 | * hexadecimal [0-9A-F] | 17942 | * hexadecimal [0-9A-F] |
17943 | * card [A2-9TJQK][CDHS] | ||
17943 | * | 17944 | * |
17944 | * Automatically uses lowest entropy to avoid issues such as interpretting 0101 | 17945 | * Automatically uses lowest entropy to avoid issues such as interpretting 0101 |
17945 | * as hexadecimal which would be 16 bits when really it's only 4 bits of binary | 17946 | * as hexadecimal which would be 16 bits when really it's only 4 bits of binary |
@@ -18050,6 +18051,40 @@ window.Entropy = new (function() { | |||
18050 | while (entropyBin.length < expectedBits) { | 18051 | while (entropyBin.length < expectedBits) { |
18051 | entropyBin = "0" + entropyBin; | 18052 | entropyBin = "0" + entropyBin; |
18052 | } | 18053 | } |
18054 | // Assume cards are NOT replaced. | ||
18055 | // Additional entropy decreases as more cards are used. This means | ||
18056 | // total possible entropy is measured using n!, not base^n. | ||
18057 | // eg the second last card can be only one of two, not one of fifty two | ||
18058 | // so the added entropy for that card is only one bit at most | ||
18059 | if (base.asInt == 52) { | ||
18060 | // Get the maximum value WITHOUT replacement | ||
18061 | var totalDecks = Math.ceil(base.parts.length / 52); | ||
18062 | var totalCards = totalDecks * 52; | ||
18063 | var totalCombos = factorial(52).pow(totalDecks); | ||
18064 | var totalRemainingCards = totalCards - base.parts.length; | ||
18065 | var remainingDecks = Math.floor(totalRemainingCards / 52); | ||
18066 | var remainingCards = totalRemainingCards % 52; | ||
18067 | var remainingCombos = factorial(52).pow(remainingDecks).multiply(factorial(remainingCards)); | ||
18068 | var currentCombos = totalCombos.divide(remainingCombos); | ||
18069 | var numberOfBits = Math.log2(currentCombos); | ||
18070 | var maxWithoutReplace = BigInteger.pow(2, numberOfBits); | ||
18071 | // aggresive flooring of numberOfBits by BigInteger.pow means a | ||
18072 | // more accurate result can be had for small numbers using the | ||
18073 | // built-in Math.pow function. | ||
18074 | if (numberOfBits < 32) { | ||
18075 | maxWithoutReplace = BigInteger(Math.round(Math.pow(2, numberOfBits))); | ||
18076 | } | ||
18077 | // Get the maximum value WITH replacement | ||
18078 | var maxWithReplace = BigInteger.pow(52, base.parts.length); | ||
18079 | // Calculate the new value by scaling the original value down | ||
18080 | var withoutReplace = entropyInt.multiply(maxWithoutReplace).divide(maxWithReplace); | ||
18081 | // Left pad with zeros based on number of bits | ||
18082 | var entropyBin = withoutReplace.toString(2); | ||
18083 | var numberOfBitsInt = Math.floor(numberOfBits); | ||
18084 | while (entropyBin.length < numberOfBitsInt) { | ||
18085 | entropyBin = "0" + entropyBin; | ||
18086 | } | ||
18087 | } | ||
18053 | // Supply a 'filtered' entropy string for display purposes | 18088 | // Supply a 'filtered' entropy string for display purposes |
18054 | var entropyClean = base.parts.join(""); | 18089 | var entropyClean = base.parts.join(""); |
18055 | var entropyHtml = base.parts.join(""); | 18090 | var entropyHtml = base.parts.join(""); |
@@ -18065,6 +18100,7 @@ window.Entropy = new (function() { | |||
18065 | entropyHtml = entropyHtml.replace(/H/g, "<span class='card-suit heart'>\u2665</span>"); | 18100 | entropyHtml = entropyHtml.replace(/H/g, "<span class='card-suit heart'>\u2665</span>"); |
18066 | entropyHtml = entropyHtml.replace(/S/g, "<span class='card-suit spade'>\u2660</span>"); | 18101 | entropyHtml = entropyHtml.replace(/S/g, "<span class='card-suit spade'>\u2660</span>"); |
18067 | } | 18102 | } |
18103 | // Return the result | ||
18068 | var e = { | 18104 | var e = { |
18069 | binaryStr: entropyBin, | 18105 | binaryStr: entropyBin, |
18070 | cleanStr: entropyClean, | 18106 | cleanStr: entropyClean, |
@@ -18154,6 +18190,18 @@ window.Entropy = new (function() { | |||
18154 | return BigInteger.log(x) / BigInteger.log(2); | 18190 | return BigInteger.log(x) / BigInteger.log(2); |
18155 | }; | 18191 | }; |
18156 | 18192 | ||
18193 | // Depends on BigInteger | ||
18194 | function factorial(n) { | ||
18195 | if (n == 0) { | ||
18196 | return 1; | ||
18197 | } | ||
18198 | f = BigInteger.ONE; | ||
18199 | for (var i=1; i<=n; i++) { | ||
18200 | f = f.multiply(new BigInteger(i)); | ||
18201 | } | ||
18202 | return f; | ||
18203 | } | ||
18204 | |||
18157 | })(); | 18205 | })(); |
18158 | </script> | 18206 | </script> |
18159 | <script>(function() { | 18207 | <script>(function() { |
@@ -18949,20 +18997,21 @@ window.Entropy = new (function() { | |||
18949 | } | 18997 | } |
18950 | 18998 | ||
18951 | function showEntropyFeedback(entropy) { | 18999 | function showEntropyFeedback(entropy) { |
19000 | var numberOfBits = entropy.binaryStr.length; | ||
18952 | var strength = "extremely weak"; | 19001 | var strength = "extremely weak"; |
18953 | if (entropy.binaryStr.length >= 64) { | 19002 | if (numberOfBits >= 64) { |
18954 | strength = "very weak"; | 19003 | strength = "very weak"; |
18955 | } | 19004 | } |
18956 | if (entropy.binaryStr.length >= 96) { | 19005 | if (numberOfBits >= 96) { |
18957 | strength = "weak"; | 19006 | strength = "weak"; |
18958 | } | 19007 | } |
18959 | if (entropy.binaryStr.length >= 128) { | 19008 | if (numberOfBits >= 128) { |
18960 | strength = "strong"; | 19009 | strength = "strong"; |
18961 | } | 19010 | } |
18962 | if (entropy.binaryStr.length >= 160) { | 19011 | if (numberOfBits >= 160) { |
18963 | strength = "very strong"; | 19012 | strength = "very strong"; |
18964 | } | 19013 | } |
18965 | if (entropy.binaryStr.length >= 192) { | 19014 | if (numberOfBits >= 192) { |
18966 | strength = "extremely strong"; | 19015 | strength = "extremely strong"; |
18967 | } | 19016 | } |
18968 | // If time to crack is less than one day, and password is considered | 19017 | // If time to crack is less than one day, and password is considered |
@@ -18983,38 +19032,20 @@ window.Entropy = new (function() { | |||
18983 | console.log("Error detecting entropy strength with zxcvbn:"); | 19032 | console.log("Error detecting entropy strength with zxcvbn:"); |
18984 | console.log(e); | 19033 | console.log(e); |
18985 | } | 19034 | } |
18986 | var bitsStr = getNumberOfEntropyBits(entropy); | ||
18987 | var wordCount = Math.floor(entropy.binaryStr.length / 32) * 3; | ||
18988 | var entropyTypeStr = getEntropyTypeStr(entropy); | 19035 | var entropyTypeStr = getEntropyTypeStr(entropy); |
19036 | var wordCount = Math.floor(numberOfBits / 32) * 3; | ||
19037 | var bitsPerEvent = Math.log2(entropy.base.asInt).toFixed(2); | ||
19038 | if (entropy.base.asInt == 52) { | ||
19039 | bitsPerEvent = bitsPerEvent + " (or less)"; | ||
19040 | } | ||
18989 | DOM.entropyFiltered.html(entropy.cleanHtml); | 19041 | DOM.entropyFiltered.html(entropy.cleanHtml); |
18990 | DOM.entropyType.text(entropyTypeStr); | 19042 | DOM.entropyType.text(entropyTypeStr); |
18991 | DOM.entropyStrength.text(strength); | 19043 | DOM.entropyStrength.text(strength); |
18992 | DOM.entropyEventCount.text(entropy.base.ints.length); | 19044 | DOM.entropyEventCount.text(entropy.base.ints.length); |
18993 | DOM.entropyBits.text(bitsStr); | 19045 | DOM.entropyBits.text(numberOfBits); |
18994 | DOM.entropyWordCount.text(wordCount); | 19046 | DOM.entropyWordCount.text(wordCount); |
18995 | DOM.entropyBinary.text(entropy.binaryStr); | 19047 | DOM.entropyBinary.text(entropy.binaryStr); |
18996 | DOM.entropyBitsPerEvent.text(Math.log2(entropy.base.asInt).toFixed(2)); | 19048 | DOM.entropyBitsPerEvent.text(bitsPerEvent); |
18997 | } | ||
18998 | |||
18999 | function getNumberOfEntropyBits(entropy) { | ||
19000 | var bitsStr = entropy.binaryStr.length.toString(); | ||
19001 | // If using cards, assume they are not reused, thus additional entropy | ||
19002 | // decreases as more cards are used. This means entropy is measured | ||
19003 | // using n!, not base^n. | ||
19004 | // eg the second last card can be only one of two, not one of fifty two | ||
19005 | // so the added entropy for that card is only one bit at most | ||
19006 | if (entropy.base.asInt == 52) { | ||
19007 | var totalDecks = Math.ceil(entropy.base.parts.length / 52); | ||
19008 | var totalCards = totalDecks * 52; | ||
19009 | var totalCombos = factorial(52).pow(totalDecks); | ||
19010 | var totalRemainingCards = totalCards - entropy.base.parts.length; | ||
19011 | var remainingDecks = Math.floor(totalRemainingCards / 52); | ||
19012 | var remainingCards = totalRemainingCards % 52; | ||
19013 | var remainingCombos = factorial(52).pow(remainingDecks) * factorial(remainingCards); | ||
19014 | var currentCombos = totalCombos.divide(remainingCombos); | ||
19015 | bitsStr = currentCombos.toString(2).length.toString(); | ||
19016 | } | ||
19017 | return bitsStr | ||
19018 | } | 19049 | } |
19019 | 19050 | ||
19020 | function getEntropyTypeStr(entropy) { | 19051 | function getEntropyTypeStr(entropy) { |
@@ -19027,10 +19058,11 @@ window.Entropy = new (function() { | |||
19027 | var dupeTracker = {}; | 19058 | var dupeTracker = {}; |
19028 | for (var i=0; i<entropy.base.parts.length; i++) { | 19059 | for (var i=0; i<entropy.base.parts.length; i++) { |
19029 | var card = entropy.base.parts[i]; | 19060 | var card = entropy.base.parts[i]; |
19030 | if (card in dupeTracker) { | 19061 | var cardUpper = card.toUpperCase(); |
19062 | if (cardUpper in dupeTracker) { | ||
19031 | dupes.push(card); | 19063 | dupes.push(card); |
19032 | } | 19064 | } |
19033 | dupeTracker[card] = true; | 19065 | dupeTracker[cardUpper] = true; |
19034 | } | 19066 | } |
19035 | if (dupes.length > 0) { | 19067 | if (dupes.length > 0) { |
19036 | var dupeWord = "duplicates"; | 19068 | var dupeWord = "duplicates"; |
@@ -19051,6 +19083,26 @@ window.Entropy = new (function() { | |||
19051 | if (uniqueCards.length == 52) { | 19083 | if (uniqueCards.length == 52) { |
19052 | cardDetail.unshift("full deck"); | 19084 | cardDetail.unshift("full deck"); |
19053 | } | 19085 | } |
19086 | // Detect missing cards | ||
19087 | var values = "A23456789TJQK"; | ||
19088 | var suits = "CDHS"; | ||
19089 | var missingCards = []; | ||
19090 | for (var i=0; i<suits.length; i++) { | ||
19091 | for (var j=0; j<values.length; j++) { | ||
19092 | var card = values[j] + suits[i]; | ||
19093 | if (!(card in dupeTracker)) { | ||
19094 | missingCards.push(card); | ||
19095 | } | ||
19096 | } | ||
19097 | } | ||
19098 | // Display missing cards if six or less, ie clearly going for full deck | ||
19099 | if (missingCards.length > 0 && missingCards.length <= 6) { | ||
19100 | var msg = missingCards.length + " missing: " + missingCards.slice(0,3).join(" "); | ||
19101 | if (missingCards.length > 3) { | ||
19102 | msg += "..."; | ||
19103 | } | ||
19104 | cardDetail.push(msg); | ||
19105 | } | ||
19054 | // Add card details to typeStr | 19106 | // Add card details to typeStr |
19055 | if (cardDetail.length > 0) { | 19107 | if (cardDetail.length > 0) { |
19056 | typeStr += " (" + cardDetail.join(", ") + ")"; | 19108 | typeStr += " (" + cardDetail.join(", ") + ")"; |
@@ -19059,18 +19111,6 @@ window.Entropy = new (function() { | |||
19059 | return typeStr; | 19111 | return typeStr; |
19060 | } | 19112 | } |
19061 | 19113 | ||
19062 | // Depends on BigInteger | ||
19063 | function factorial(n) { | ||
19064 | if (n == 0) { | ||
19065 | return 1; | ||
19066 | } | ||
19067 | f = BigInteger.ONE; | ||
19068 | for (var i=1; i<=n; i++) { | ||
19069 | f = f.multiply(new BigInteger(i)); | ||
19070 | } | ||
19071 | return f; | ||
19072 | } | ||
19073 | |||
19074 | var networks = [ | 19114 | var networks = [ |
19075 | { | 19115 | { |
19076 | name: "Bitcoin", | 19116 | name: "Bitcoin", |