aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Coleman <coleman.ian@gmail.com>2016-11-17 14:28:26 +1100
committerIan Coleman <coleman.ian@gmail.com>2016-11-17 14:28:26 +1100
commit6422c1cd57afbb4f0c71b0157ba6ad9598f82bc4 (patch)
tree935e8579a4f71fdd6e6f50ebf2294a46a80357ff
parentbbc29c80f429d7493fdb9cb7987000d633734db5 (diff)
downloadBIP39-6422c1cd57afbb4f0c71b0157ba6ad9598f82bc4.tar.gz
BIP39-6422c1cd57afbb4f0c71b0157ba6ad9598f82bc4.tar.zst
BIP39-6422c1cd57afbb4f0c71b0157ba6ad9598f82bc4.zip
Entropy library assumes cards are discarded
and removed the duplicate logic from the UI logic.
-rw-r--r--src/js/entropy.js46
-rw-r--r--src/js/index.js52
-rw-r--r--tests.js50
3 files changed, 81 insertions, 67 deletions
diff --git a/src/js/entropy.js b/src/js/entropy.js
index c28620a..5900346 100644
--- a/src/js/entropy.js
+++ b/src/js/entropy.js
@@ -117,6 +117,40 @@ window.Entropy = new (function() {
117 while (entropyBin.length < expectedBits) { 117 while (entropyBin.length < expectedBits) {
118 entropyBin = "0" + entropyBin; 118 entropyBin = "0" + entropyBin;
119 } 119 }
120 // Assume cards are NOT replaced.
121 // Additional entropy decreases as more cards are used. This means
122 // entropy is measured using n!, not base^n.
123 // eg the second last card can be only one of two, not one of fifty two
124 // so the added entropy for that card is only one bit at most
125 if (base.asInt == 52) {
126 // Get the maximum value without replacement
127 var totalDecks = Math.ceil(base.parts.length / 52);
128 var totalCards = totalDecks * 52;
129 var totalCombos = factorial(52).pow(totalDecks);
130 var totalRemainingCards = totalCards - base.parts.length;
131 var remainingDecks = Math.floor(totalRemainingCards / 52);
132 var remainingCards = totalRemainingCards % 52;
133 var remainingCombos = factorial(52).pow(remainingDecks).multiply(factorial(remainingCards));
134 var currentCombos = totalCombos.divide(remainingCombos);
135 var numberOfBits = Math.log2(currentCombos);
136 var maxWithoutReplace = BigInteger.pow(2, numberOfBits);
137 // aggresive flooring of numberOfBits by BigInteger.pow means a
138 // more accurate result can be had for small numbers using the
139 // built-in Math.pow function.
140 if (numberOfBits < 32) {
141 maxWithoutReplace = BigInteger(Math.round(Math.pow(2, numberOfBits)));
142 }
143 // Get the maximum value with replacement
144 var maxWithReplace = BigInteger.pow(52, base.parts.length);
145 // Calculate the new value by scaling the original value down
146 var withoutReplace = entropyInt.multiply(maxWithoutReplace).divide(maxWithReplace);
147 // Left pad with zeros based on number of bits
148 var entropyBin = withoutReplace.toString(2);
149 var numberOfBitsInt = Math.floor(numberOfBits);
150 while (entropyBin.length < numberOfBitsInt) {
151 entropyBin = "0" + entropyBin;
152 }
153 }
120 // Supply a 'filtered' entropy string for display purposes 154 // Supply a 'filtered' entropy string for display purposes
121 var entropyClean = base.parts.join(""); 155 var entropyClean = base.parts.join("");
122 var entropyHtml = base.parts.join(""); 156 var entropyHtml = base.parts.join("");
@@ -221,4 +255,16 @@ window.Entropy = new (function() {
221 return BigInteger.log(x) / BigInteger.log(2); 255 return BigInteger.log(x) / BigInteger.log(2);
222 }; 256 };
223 257
258 // Depends on BigInteger
259 function factorial(n) {
260 if (n == 0) {
261 return 1;
262 }
263 f = BigInteger.ONE;
264 for (var i=1; i<=n; i++) {
265 f = f.multiply(new BigInteger(i));
266 }
267 return f;
268 }
269
224})(); 270})();
diff --git a/src/js/index.js b/src/js/index.js
index f4163ee..254b62f 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -791,20 +791,21 @@
791 } 791 }
792 792
793 function showEntropyFeedback(entropy) { 793 function showEntropyFeedback(entropy) {
794 var numberOfBits = entropy.binaryStr.length;
794 var strength = "extremely weak"; 795 var strength = "extremely weak";
795 if (entropy.binaryStr.length >= 64) { 796 if (numberOfBits >= 64) {
796 strength = "very weak"; 797 strength = "very weak";
797 } 798 }
798 if (entropy.binaryStr.length >= 96) { 799 if (numberOfBits >= 96) {
799 strength = "weak"; 800 strength = "weak";
800 } 801 }
801 if (entropy.binaryStr.length >= 128) { 802 if (numberOfBits >= 128) {
802 strength = "strong"; 803 strength = "strong";
803 } 804 }
804 if (entropy.binaryStr.length >= 160) { 805 if (numberOfBits >= 160) {
805 strength = "very strong"; 806 strength = "very strong";
806 } 807 }
807 if (entropy.binaryStr.length >= 192) { 808 if (numberOfBits >= 192) {
808 strength = "extremely strong"; 809 strength = "extremely strong";
809 } 810 }
810 // If time to crack is less than one day, and password is considered 811 // If time to crack is less than one day, and password is considered
@@ -825,38 +826,17 @@
825 console.log("Error detecting entropy strength with zxcvbn:"); 826 console.log("Error detecting entropy strength with zxcvbn:");
826 console.log(e); 827 console.log(e);
827 } 828 }
828 var bitsStr = getNumberOfEntropyBits(entropy);
829 var wordCount = Math.floor(entropy.binaryStr.length / 32) * 3;
830 var entropyTypeStr = getEntropyTypeStr(entropy); 829 var entropyTypeStr = getEntropyTypeStr(entropy);
830 var wordCount = Math.floor(numberOfBits / 32) * 3;
831 var bitsPerEvent = Math.log2(entropy.base.asInt).toFixed(2);
831 DOM.entropyFiltered.html(entropy.cleanHtml); 832 DOM.entropyFiltered.html(entropy.cleanHtml);
832 DOM.entropyType.text(entropyTypeStr); 833 DOM.entropyType.text(entropyTypeStr);
833 DOM.entropyStrength.text(strength); 834 DOM.entropyStrength.text(strength);
834 DOM.entropyEventCount.text(entropy.base.ints.length); 835 DOM.entropyEventCount.text(entropy.base.ints.length);
835 DOM.entropyBits.text(bitsStr); 836 DOM.entropyBits.text(numberOfBits);
836 DOM.entropyWordCount.text(wordCount); 837 DOM.entropyWordCount.text(wordCount);
837 DOM.entropyBinary.text(entropy.binaryStr); 838 DOM.entropyBinary.text(entropy.binaryStr);
838 DOM.entropyBitsPerEvent.text(Math.log2(entropy.base.asInt).toFixed(2)); 839 DOM.entropyBitsPerEvent.text(bitsPerEvent);
839 }
840
841 function getNumberOfEntropyBits(entropy) {
842 var bitsStr = entropy.binaryStr.length.toString();
843 // If using cards, assume they are not reused, thus additional entropy
844 // decreases as more cards are used. This means entropy is measured
845 // using n!, not base^n.
846 // eg the second last card can be only one of two, not one of fifty two
847 // so the added entropy for that card is only one bit at most
848 if (entropy.base.asInt == 52) {
849 var totalDecks = Math.ceil(entropy.base.parts.length / 52);
850 var totalCards = totalDecks * 52;
851 var totalCombos = factorial(52).pow(totalDecks);
852 var totalRemainingCards = totalCards - entropy.base.parts.length;
853 var remainingDecks = Math.floor(totalRemainingCards / 52);
854 var remainingCards = totalRemainingCards % 52;
855 var remainingCombos = factorial(52).pow(remainingDecks) * factorial(remainingCards);
856 var currentCombos = totalCombos.divide(remainingCombos);
857 bitsStr = currentCombos.toString(2).length.toString();
858 }
859 return bitsStr
860 } 840 }
861 841
862 function getEntropyTypeStr(entropy) { 842 function getEntropyTypeStr(entropy) {
@@ -922,18 +902,6 @@
922 return typeStr; 902 return typeStr;
923 } 903 }
924 904
925 // Depends on BigInteger
926 function factorial(n) {
927 if (n == 0) {
928 return 1;
929 }
930 f = BigInteger.ONE;
931 for (var i=1; i<=n; i++) {
932 f = f.multiply(new BigInteger(i));
933 }
934 return f;
935 }
936
937 var networks = [ 905 var networks = [
938 { 906 {
939 name: "Bitcoin", 907 name: "Bitcoin",
diff --git a/tests.js b/tests.js
index 13f2c76..03ce9e1 100644
--- a/tests.js
+++ b/tests.js
@@ -2185,10 +2185,9 @@ page.open(url, function(status) {
2185 try { 2185 try {
2186 var cards = [ 2186 var cards = [
2187 [ "ac", "00000" ], 2187 [ "ac", "00000" ],
2188 [ "acac", "00000000000" ], 2188 [ "acqs", "00000110001" ],
2189 [ "acac2c", "00000000000000001" ], 2189 [ "acks", "00000110010" ],
2190 [ "acks", "00000110011" ], 2190 [ "2cac", "00000110011" ],
2191 [ "acacks", "00000000000110011" ],
2192 [ "2c", "00001" ], 2191 [ "2c", "00001" ],
2193 [ "3d", "01111" ], 2192 [ "3d", "01111" ],
2194 [ "4h", "11101" ], 2193 [ "4h", "11101" ],
@@ -2201,8 +2200,8 @@ page.open(url, function(status) {
2201 [ "jd", "10111" ], 2200 [ "jd", "10111" ],
2202 [ "qh", "100101" ], 2201 [ "qh", "100101" ],
2203 [ "ks", "110011" ], 2202 [ "ks", "110011" ],
2204 [ "ks2c", "101001011101" ], 2203 [ "ks2c", "101000101001" ],
2205 [ "KS2C", "101001011101" ], 2204 [ "KS2C", "101000101001" ],
2206 ]; 2205 ];
2207 for (var i=0; i<cards.length; i++) { 2206 for (var i=0; i<cards.length; i++) {
2208 var card = cards[i][0]; 2207 var card = cards[i][0];
@@ -2503,7 +2502,7 @@ page.open(url, function(status) {
2503 [ "222F", "16" ], 2502 [ "222F", "16" ],
2504 [ "FFFF", "16" ], 2503 [ "FFFF", "16" ],
2505 [ "0000101017", "33" ], // 10 events at 3.32 bits per event 2504 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2506 [ "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "226" ], // cards are not replaced, so a full deck is not 52^52 entropy which is 296 bits, it's 52!, which is 226 bits 2505 [ "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225" ], // cards are not replaced, so a full deck is not 52^52 entropy which is 296 bits, it's 52!, which is 225 bits
2507 ] 2506 ]
2508 // use entropy 2507 // use entropy
2509 page.evaluate(function(e) { 2508 page.evaluate(function(e) {
@@ -2636,41 +2635,42 @@ page.open(url, function(status) {
2636 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", 2635 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2637 type: "card (full deck)", 2636 type: "card (full deck)",
2638 events: 52, 2637 events: 52,
2639 bits: 226, 2638 bits: 225,
2640 words: 27, 2639 words: 21,
2641 strength: "extremely strong", 2640 strength: "extremely strong",
2642 }, 2641 },
2643 { 2642 {
2644 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d", 2643 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2645 type: "card (full deck, 1 duplicate: 3d)", 2644 type: "card (full deck, 1 duplicate: 3d)",
2646 events: 53, 2645 events: 53,
2647 bits: 232, 2646 bits: 231,
2648 words: 27, 2647 words: 21,
2649 strength: "extremely strong", 2648 strength: "extremely strong",
2650 }, 2649 },
2651 { 2650 {
2652 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d", 2651 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2653 type: "card (2 duplicates: 3d 4d, 1 missing: KS)", 2652 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2654 events: 53, 2653 events: 53,
2655 bits: 232, 2654 bits: 231,
2656 words: 27, 2655 words: 21,
2657 strength: "extremely strong", 2656 strength: "extremely strong",
2658 }, 2657 },
2659 { 2658 {
2660 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d", 2659 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2661 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)", 2660 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2662 events: 53, 2661 events: 53,
2663 bits: 243, 2662 bits: 242,
2664 words: 27, 2663 words: 21,
2665 strength: "extremely strong", 2664 strength: "extremely strong",
2666 }, 2665 },
2667 // Next test was throwing uncaught error in zxcvbn 2666 // Next test was throwing uncaught error in zxcvbn
2667 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2668 { 2668 {
2669 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", 2669 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2670 type: "card (full deck, 52 duplicates: ac 2c 3c...)", 2670 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2671 events: 104, 2671 events: 104,
2672 bits: 452, 2672 bits: 451,
2673 words: 54, 2673 words: 42,
2674 strength: "extremely strong", 2674 strength: "extremely strong",
2675 }, 2675 },
2676 // Case insensitivity to duplicate cards 2676 // Case insensitivity to duplicate cards
@@ -2695,24 +2695,24 @@ page.open(url, function(status) {
2695 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", 2695 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2696 type: "card (1 missing: 9C)", 2696 type: "card (1 missing: 9C)",
2697 events: 51, 2697 events: 51,
2698 bits: 226, 2698 bits: 225,
2699 words: 27, 2699 words: 21,
2700 strength: "extremely strong", 2700 strength: "extremely strong",
2701 }, 2701 },
2702 { 2702 {
2703 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", 2703 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2704 type: "card (2 missing: 9C 5D)", 2704 type: "card (2 missing: 9C 5D)",
2705 events: 50, 2705 events: 50,
2706 bits: 225, 2706 bits: 224,
2707 words: 24, 2707 words: 21,
2708 strength: "extremely strong", 2708 strength: "extremely strong",
2709 }, 2709 },
2710 { 2710 {
2711 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", 2711 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2712 type: "card (4 missing: 9C 5D QD...)", 2712 type: "card (4 missing: 9C 5D QD...)",
2713 events: 48, 2713 events: 48,
2714 bits: 221, 2714 bits: 220,
2715 words: 24, 2715 words: 18,
2716 strength: "extremely strong", 2716 strength: "extremely strong",
2717 }, 2717 },
2718 // More than six missing cards does not show message 2718 // More than six missing cards does not show message
@@ -2720,8 +2720,8 @@ page.open(url, function(status) {
2720 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks", 2720 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2721 type: "card", 2721 type: "card",
2722 events: 45, 2722 events: 45,
2723 bits: 214, 2723 bits: 213,
2724 words: 24, 2724 words: 18,
2725 strength: "extremely strong", 2725 strength: "extremely strong",
2726 }, 2726 },
2727 ]; 2727 ];