diff options
author | Ian Coleman <coleman.ian@gmail.com> | 2016-11-30 15:30:19 +1100 |
---|---|---|
committer | Ian Coleman <coleman.ian@gmail.com> | 2016-11-30 15:30:19 +1100 |
commit | 87ad2c6e4c7987320d8ce147fe9310b702c5deea (patch) | |
tree | 6dc821981e0fcb86371b52d4a8b1b1ba528346b3 | |
parent | c3c3df473e0e5114b75dbe835ada59a0e8066543 (diff) | |
download | BIP39-87ad2c6e4c7987320d8ce147fe9310b702c5deea.tar.gz BIP39-87ad2c6e4c7987320d8ce147fe9310b702c5deea.tar.zst BIP39-87ad2c6e4c7987320d8ce147fe9310b702c5deea.zip |
Card entropy has improved conversion to binary
See http://crypto.stackexchange.com/q/41886
and https://github.com/iancoleman/bip39/issues/33#issuecomment-263021856
-rw-r--r-- | src/js/entropy.js | 169 | ||||
-rw-r--r-- | tests.js | 215 |
2 files changed, 117 insertions, 267 deletions
diff --git a/src/js/entropy.js b/src/js/entropy.js index 8a0c799..d04d861 100644 --- a/src/js/entropy.js +++ b/src/js/entropy.js | |||
@@ -120,97 +120,9 @@ window.Entropy = new (function() { | |||
120 | while (entropyBin.length < expectedBits) { | 120 | while (entropyBin.length < expectedBits) { |
121 | entropyBin = "0" + entropyBin; | 121 | entropyBin = "0" + entropyBin; |
122 | } | 122 | } |
123 | // Assume cards are NOT replaced. | 123 | // Cards binary must be handled differently, since they're not replaced |
124 | // Additional entropy decreases as more cards are used. This means | ||
125 | // total possible entropy is measured using n!, not base^n. | ||
126 | // eg the second last card can be only one of two, not one of fifty two | ||
127 | // so the added entropy for that card is only one bit at most | ||
128 | if (base.asInt == 52) { | 124 | if (base.asInt == 52) { |
129 | var totalDecks = Math.ceil(base.parts.length / 52); | 125 | entropyBin = getCardBinary(base.parts); |
130 | var totalCards = totalDecks * 52; | ||
131 | var totalCombos = factorial(52).pow(totalDecks); | ||
132 | var totalRemainingCards = totalCards - base.parts.length; | ||
133 | var remainingDecks = Math.floor(totalRemainingCards / 52); | ||
134 | var remainingCards = totalRemainingCards % 52; | ||
135 | var remainingCombos = factorial(52).pow(remainingDecks).multiply(factorial(remainingCards)); | ||
136 | var currentCombos = totalCombos.divide(remainingCombos); | ||
137 | var numberOfBits = Math.log2(currentCombos); | ||
138 | var maxWithoutReplace = BigInteger.pow(2, numberOfBits); | ||
139 | // Use a bunch of sorted decks to measure entropy from, populated | ||
140 | // as needed. | ||
141 | var sortedDecks = []; | ||
142 | // Initialize the final entropy value for these cards | ||
143 | var entropyInt = BigInteger.ZERO; | ||
144 | // Track how many instances of each card have been used, and thus | ||
145 | // how many decks are in use. | ||
146 | var cardCounts = {}; | ||
147 | // Track the total bits of entropy that remain, which diminishes as | ||
148 | // each card is drawn. | ||
149 | var totalBitsLeft = numberOfBits; | ||
150 | // Work out entropy contribution of each card drawn | ||
151 | for (var i=0; i<base.parts.length; i++) { | ||
152 | // Get the card that was drawn | ||
153 | var cardLower = base.parts[i]; | ||
154 | var card = cardLower.toUpperCase(); | ||
155 | // Initialize the deck for this card if needed, to track how | ||
156 | // much entropy it adds. | ||
157 | if (!(card in cardCounts)) { | ||
158 | cardCounts[card] = 0; | ||
159 | } | ||
160 | // Get the deck this card is from | ||
161 | var deckIndex = cardCounts[card]; | ||
162 | while (deckIndex > sortedDecks.length-1) { | ||
163 | sortedDecks.push(getSortedDeck()); | ||
164 | } | ||
165 | // See how many bits this card contributes (depends on how many | ||
166 | // are left in the deck it's from) | ||
167 | var deckForCard = sortedDecks[deckIndex]; | ||
168 | var cardsLeftInDeck = deckForCard.length; | ||
169 | var additionalBits = Math.log2(cardsLeftInDeck); | ||
170 | // Work out the min and max value for this card | ||
171 | var nextTotalBitsLeft = totalBitsLeft - additionalBits; | ||
172 | var minPossibleNewEntropy = TWO.pow(nextTotalBitsLeft).subtract(1); | ||
173 | var maxPossibleNewEntropy = TWO.pow(totalBitsLeft).subtract(1); | ||
174 | var diff = maxPossibleNewEntropy.subtract(minPossibleNewEntropy); | ||
175 | // BigInteger aggresively floors numbers which greatly affects | ||
176 | // the small numbers. In that case, use native Math library | ||
177 | var useBigInt = totalBitsLeft >= 32; | ||
178 | if (!useBigInt) { | ||
179 | minPossibleNewEntropy = Math.round(Math.pow(2, nextTotalBitsLeft)-1); | ||
180 | maxPossibleNewEntropy = Math.round(Math.pow(2, totalBitsLeft)-1); | ||
181 | diff = maxPossibleNewEntropy - minPossibleNewEntropy; | ||
182 | } | ||
183 | // Scale the value between possible min and max depending on | ||
184 | // this card value | ||
185 | var thisCardIndex = deckForCard.indexOf(card); | ||
186 | var toAdd = BigInteger.ZERO; | ||
187 | if (cardsLeftInDeck > 1) { | ||
188 | if (useBigInt) { | ||
189 | toAdd = diff.multiply(thisCardIndex) | ||
190 | .divide(deckForCard.length - 1) | ||
191 | .add(minPossibleNewEntropy); | ||
192 | } | ||
193 | else { | ||
194 | var ratio = thisCardIndex / (deckForCard.length -1); | ||
195 | var f = diff * ratio; | ||
196 | toAdd = new BigInteger(f).add(minPossibleNewEntropy); | ||
197 | } | ||
198 | } | ||
199 | // Add this card entropy to existing entropy | ||
200 | entropyInt = entropyInt.add(toAdd); | ||
201 | // Remove this card from the deck it comes from | ||
202 | deckForCard.splice(thisCardIndex,1); | ||
203 | // Ensure the next insance of this card uses the next deck | ||
204 | cardCounts[card] = cardCounts[card] + 1; | ||
205 | // Next card drawn has less total remaining bits to work with | ||
206 | totalBitsLeft = nextTotalBitsLeft; | ||
207 | } | ||
208 | // Convert to binary | ||
209 | var entropyBin = entropyInt.toString(2); | ||
210 | var numberOfBitsInt = Math.floor(numberOfBits); | ||
211 | while (entropyBin.length < numberOfBitsInt) { | ||
212 | entropyBin = "0" + entropyBin; | ||
213 | } | ||
214 | } | 126 | } |
215 | // Supply a 'filtered' entropy string for display purposes | 127 | // Supply a 'filtered' entropy string for display purposes |
216 | var entropyClean = base.parts.join(""); | 128 | var entropyClean = base.parts.join(""); |
@@ -319,6 +231,83 @@ window.Entropy = new (function() { | |||
319 | } | 231 | } |
320 | } | 232 | } |
321 | 233 | ||
234 | // Assume cards are NOT replaced. | ||
235 | // Additional entropy decreases as more cards are used. This means | ||
236 | // total possible entropy is measured using n!, not base^n. | ||
237 | // eg the second last card can be only one of two, not one of fifty two | ||
238 | // so the added entropy for that card is only one bit at most | ||
239 | function getCardBinary(cards) { | ||
240 | // Track how many instances of each card have been used, and thus | ||
241 | // how many decks are in use. | ||
242 | var cardCounts = {}; | ||
243 | var numberOfDecks = 0; | ||
244 | // Work out number of decks by max(duplicates) | ||
245 | for (var i=0; i<cards.length; i++) { | ||
246 | // Get the card that was drawn | ||
247 | var cardLower = cards[i]; | ||
248 | var card = cardLower.toUpperCase(); | ||
249 | // Initialize the count for this card if needed | ||
250 | if (!(card in cardCounts)) { | ||
251 | cardCounts[card] = 0; | ||
252 | } | ||
253 | cardCounts[card] += 1; | ||
254 | // See if this is max(duplicates) | ||
255 | if (cardCounts[card] > numberOfDecks) { | ||
256 | numberOfDecks = cardCounts[card]; | ||
257 | } | ||
258 | } | ||
259 | // Work out the total number of bits for this many decks | ||
260 | // See http://crypto.stackexchange.com/q/41886 | ||
261 | var gainedBits = Math.log2(factorial(52 * numberOfDecks)); | ||
262 | var lostBits = 52 * Math.log2(factorial(numberOfDecks)); | ||
263 | var maxBits = gainedBits - lostBits; | ||
264 | // Convert the drawn cards to a binary representation. | ||
265 | // The exact technique for doing this is unclear. | ||
266 | // See | ||
267 | // http://crypto.stackexchange.com/a/41896 | ||
268 | // "I even doubt that this is well defined (only the average entropy | ||
269 | // is, I believe)." | ||
270 | // See | ||
271 | // https://github.com/iancoleman/bip39/issues/33#issuecomment-263021856 | ||
272 | // "The binary representation can be the first log(permutations,2) bits | ||
273 | // of the sha-2 hash of the normalized deck string." | ||
274 | // | ||
275 | // In this specific implementation, the first N bits of the hash of the | ||
276 | // normalized cards string is being used. Uppercase, no spaces; eg | ||
277 | // sha256("AH8DQSTC2H") | ||
278 | var totalCards = numberOfDecks * 52; | ||
279 | var percentUsed = cards.length / totalCards; | ||
280 | // Calculate the average number of bits of entropy for the number of | ||
281 | // cards drawn. | ||
282 | var numberOfBits = Math.floor(maxBits * percentUsed); | ||
283 | // Create a normalized string of the selected cards | ||
284 | var normalizedCards = cards.join("").toUpperCase(); | ||
285 | // Convert to binary using the SHA256 hash of the normalized cards. | ||
286 | // If the number of bits is more than 256, multiple rounds of hashing | ||
287 | // are used until the required number of bits is reached. | ||
288 | var entropyBin = ""; | ||
289 | var iterations = 0; | ||
290 | while (entropyBin.length < numberOfBits) { | ||
291 | var hashedCards = sjcl.hash.sha256.hash(normalizedCards); | ||
292 | for (var j=0; j<iterations; j++) { | ||
293 | hashedCards = sjcl.hash.sha256.hash(hashedCards); | ||
294 | } | ||
295 | var hashHex = sjcl.codec.hex.fromBits(hashedCards); | ||
296 | for (var i=0; i<hashHex.length; i++) { | ||
297 | var decimal = parseInt(hashHex[i], 16); | ||
298 | var binary = decimal.toString(2); | ||
299 | while (binary.length < 4) { | ||
300 | binary = "0" + binary; | ||
301 | } | ||
302 | entropyBin = entropyBin + binary; | ||
303 | } | ||
304 | iterations = iterations + 1; | ||
305 | } | ||
306 | // Truncate to the appropriate number of bits. | ||
307 | entropyBin = entropyBin.substring(0, numberOfBits); | ||
308 | return entropyBin; | ||
309 | } | ||
310 | |||
322 | // Polyfill for Math.log2 | 311 | // Polyfill for Math.log2 |
323 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill | 312 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill |
324 | Math.log2 = Math.log2 || function(x) { | 313 | Math.log2 = Math.log2 || function(x) { |
@@ -2149,10 +2149,11 @@ page.open(url, function(status) { | |||
2149 | catch (e) { | 2149 | catch (e) { |
2150 | return e.message; | 2150 | return e.message; |
2151 | } | 2151 | } |
2152 | // Leading zeros for card entropy as binary string | 2152 | // Leading zeros for card entropy as binary string. |
2153 | // Card entropy is hashed so 2c does not produce leading zeros. | ||
2153 | try { | 2154 | try { |
2154 | e = Entropy.fromString("2c"); | 2155 | e = Entropy.fromString("4c"); |
2155 | if (e.binaryStr != "00001") { | 2156 | if (e.binaryStr != "0001") { |
2156 | return "Card entropy as binary has leading zeros"; | 2157 | return "Card entropy as binary has leading zeros"; |
2157 | } | 2158 | } |
2158 | } | 2159 | } |
@@ -2184,24 +2185,24 @@ page.open(url, function(status) { | |||
2184 | // [ cards, binary ] | 2185 | // [ cards, binary ] |
2185 | try { | 2186 | try { |
2186 | var cards = [ | 2187 | var cards = [ |
2187 | [ "ac", "00000" ], | 2188 | [ "ac", "0100" ], |
2188 | [ "acqs", "00001100011" ], | 2189 | [ "acqs", "10111101" ], |
2189 | [ "acks", "00001100100" ], | 2190 | [ "acks", "11110000" ], |
2190 | [ "2cac", "00001100101" ], | 2191 | [ "2cac", "11000010" ], |
2191 | [ "2c", "00001" ], | 2192 | [ "2c", "1000" ], |
2192 | [ "3d", "01111" ], | 2193 | [ "3d", "1111" ], |
2193 | [ "4h", "11101" ], | 2194 | [ "4h", "0011" ], |
2194 | [ "5s", "101011" ], | 2195 | [ "5s", "1001" ], |
2195 | [ "6c", "00101" ], | 2196 | [ "6c", "1011" ], |
2196 | [ "7d", "10011" ], | 2197 | [ "7d", "1101" ], |
2197 | [ "8h", "100001" ], | 2198 | [ "8h", "1011" ], |
2198 | [ "9s", "101111" ], | 2199 | [ "9s", "1010" ], |
2199 | [ "tc", "01001" ], | 2200 | [ "tc", "1101" ], |
2200 | [ "jd", "10111" ], | 2201 | [ "jd", "1101" ], |
2201 | [ "qh", "100101" ], | 2202 | [ "qh", "1100" ], |
2202 | [ "ks", "110011" ], | 2203 | [ "ks", "1111" ], |
2203 | [ "ks2c", "101001011100" ], | 2204 | [ "ks2c", "10000001" ], |
2204 | [ "KS2C", "101001011100" ], | 2205 | [ "KS2C", "10000001" ], |
2205 | ]; | 2206 | ]; |
2206 | for (var i=0; i<cards.length; i++) { | 2207 | for (var i=0; i<cards.length; i++) { |
2207 | var card = cards[i][0]; | 2208 | var card = cards[i][0]; |
@@ -2644,7 +2645,7 @@ page.open(url, function(status) { | |||
2644 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d", | 2645 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d", |
2645 | type: "card (full deck, 1 duplicate: 3d)", | 2646 | type: "card (full deck, 1 duplicate: 3d)", |
2646 | events: 53, | 2647 | events: 53, |
2647 | bits: 231, | 2648 | bits: 254, |
2648 | words: 21, | 2649 | words: 21, |
2649 | strength: "extremely strong", | 2650 | strength: "extremely strong", |
2650 | }, | 2651 | }, |
@@ -2652,7 +2653,7 @@ page.open(url, function(status) { | |||
2652 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d", | 2653 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d", |
2653 | type: "card (2 duplicates: 3d 4d, 1 missing: KS)", | 2654 | type: "card (2 duplicates: 3d 4d, 1 missing: KS)", |
2654 | events: 53, | 2655 | events: 53, |
2655 | bits: 231, | 2656 | bits: 254, |
2656 | words: 21, | 2657 | words: 21, |
2657 | strength: "extremely strong", | 2658 | strength: "extremely strong", |
2658 | }, | 2659 | }, |
@@ -2660,8 +2661,8 @@ page.open(url, function(status) { | |||
2660 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d", | 2661 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d", |
2661 | type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)", | 2662 | type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)", |
2662 | events: 53, | 2663 | events: 53, |
2663 | bits: 242, | 2664 | bits: 264, |
2664 | words: 21, | 2665 | words: 24, |
2665 | strength: "extremely strong", | 2666 | strength: "extremely strong", |
2666 | }, | 2667 | }, |
2667 | // Next test was throwing uncaught error in zxcvbn | 2668 | // Next test was throwing uncaught error in zxcvbn |
@@ -2670,8 +2671,8 @@ page.open(url, function(status) { | |||
2670 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", | 2671 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", |
2671 | type: "card (full deck, 52 duplicates: ac 2c 3c...)", | 2672 | type: "card (full deck, 52 duplicates: ac 2c 3c...)", |
2672 | events: 104, | 2673 | events: 104, |
2673 | bits: 451, | 2674 | bits: 499, |
2674 | words: 42, | 2675 | words: 45, |
2675 | strength: "extremely strong", | 2676 | strength: "extremely strong", |
2676 | }, | 2677 | }, |
2677 | // Case insensitivity to duplicate cards | 2678 | // Case insensitivity to duplicate cards |
@@ -2679,7 +2680,7 @@ page.open(url, function(status) { | |||
2679 | entropy: "asAS", | 2680 | entropy: "asAS", |
2680 | type: "card (1 duplicate: AS)", | 2681 | type: "card (1 duplicate: AS)", |
2681 | events: 2, | 2682 | events: 2, |
2682 | bits: 12, | 2683 | bits: 9, |
2683 | words: 0, | 2684 | words: 0, |
2684 | strength: "extremely weak", | 2685 | strength: "extremely weak", |
2685 | }, | 2686 | }, |
@@ -2687,7 +2688,7 @@ page.open(url, function(status) { | |||
2687 | entropy: "ASas", | 2688 | entropy: "ASas", |
2688 | type: "card (1 duplicate: as)", | 2689 | type: "card (1 duplicate: as)", |
2689 | events: 2, | 2690 | events: 2, |
2690 | bits: 12, | 2691 | bits: 9, |
2691 | words: 0, | 2692 | words: 0, |
2692 | strength: "extremely weak", | 2693 | strength: "extremely weak", |
2693 | }, | 2694 | }, |
@@ -2696,23 +2697,23 @@ page.open(url, function(status) { | |||
2696 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", | 2697 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", |
2697 | type: "card (1 missing: 9C)", | 2698 | type: "card (1 missing: 9C)", |
2698 | events: 51, | 2699 | events: 51, |
2699 | bits: 225, | 2700 | bits: 221, |
2700 | words: 21, | 2701 | words: 18, |
2701 | strength: "extremely strong", | 2702 | strength: "extremely strong", |
2702 | }, | 2703 | }, |
2703 | { | 2704 | { |
2704 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", | 2705 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", |
2705 | type: "card (2 missing: 9C 5D)", | 2706 | type: "card (2 missing: 9C 5D)", |
2706 | events: 50, | 2707 | events: 50, |
2707 | bits: 224, | 2708 | bits: 216, |
2708 | words: 21, | 2709 | words: 18, |
2709 | strength: "extremely strong", | 2710 | strength: "extremely strong", |
2710 | }, | 2711 | }, |
2711 | { | 2712 | { |
2712 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", | 2713 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", |
2713 | type: "card (4 missing: 9C 5D QD...)", | 2714 | type: "card (4 missing: 9C 5D QD...)", |
2714 | events: 48, | 2715 | events: 48, |
2715 | bits: 220, | 2716 | bits: 208, |
2716 | words: 18, | 2717 | words: 18, |
2717 | strength: "extremely strong", | 2718 | strength: "extremely strong", |
2718 | }, | 2719 | }, |
@@ -2721,140 +2722,10 @@ page.open(url, function(status) { | |||
2721 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks", | 2722 | entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks", |
2722 | type: "card", | 2723 | type: "card", |
2723 | events: 45, | 2724 | events: 45, |
2724 | bits: 213, | 2725 | bits: 195, |
2725 | words: 18, | 2726 | words: 18, |
2726 | strength: "extremely strong", | 2727 | strength: "extremely strong", |
2727 | }, | 2728 | }, |
2728 | // Additional entropy from each card decreases as deck is depleted. | ||
2729 | // Check the boundaries of this depletion | ||
2730 | // See table at https://github.com/iancoleman/bip39/issues/33#issuecomment-262855862 | ||
2731 | // for following values of 'events, bits, words' | ||
2732 | // 2 cards remaining = 21 words | ||
2733 | { | ||
2734 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjs", | ||
2735 | type: "card (2 missing: QS KS)", | ||
2736 | events: 50, | ||
2737 | bits: 224, | ||
2738 | words: 21, | ||
2739 | strength: "extremely strong", | ||
2740 | }, | ||
2741 | // 3 cards remaining = 18 words | ||
2742 | { | ||
2743 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9sts", | ||
2744 | type: "card (3 missing: JS QS KS)", | ||
2745 | events: 49, | ||
2746 | bits: 222, // table uses different rounding - 222.99604 | ||
2747 | words: 18, | ||
2748 | strength: "extremely strong", | ||
2749 | }, | ||
2750 | // 13 cards remaining = 18 words | ||
2751 | { | ||
2752 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkh", | ||
2753 | type: "card", | ||
2754 | events: 39, | ||
2755 | bits: 193, | ||
2756 | words: 18, | ||
2757 | strength: "extremely strong", | ||
2758 | }, | ||
2759 | // 14 cards remaining = 15 words | ||
2760 | { | ||
2761 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqh", | ||
2762 | type: "card", | ||
2763 | events: 38, | ||
2764 | bits: 189, | ||
2765 | words: 15, | ||
2766 | strength: "very strong", | ||
2767 | }, | ||
2768 | // 21 cards remaining = 15 words | ||
2769 | { | ||
2770 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h", | ||
2771 | type: "card", | ||
2772 | events: 31, | ||
2773 | bits: 160, | ||
2774 | words: 15, | ||
2775 | strength: "very strong", | ||
2776 | }, | ||
2777 | // 22 cards remaining = 12 words | ||
2778 | { | ||
2779 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h", | ||
2780 | type: "card", | ||
2781 | events: 30, | ||
2782 | bits: 155, | ||
2783 | words: 12, | ||
2784 | strength: "strong", | ||
2785 | }, | ||
2786 | // 27 cards remaining = 12 words | ||
2787 | { | ||
2788 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqd", | ||
2789 | type: "card", | ||
2790 | events: 25, | ||
2791 | bits: 132, | ||
2792 | words: 12, | ||
2793 | strength: "strong", | ||
2794 | }, | ||
2795 | // 28 cards remaining = 9 words | ||
2796 | { | ||
2797 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjd", | ||
2798 | type: "card", | ||
2799 | events: 24, | ||
2800 | bits: 127, | ||
2801 | words: 9, | ||
2802 | strength: "weak", | ||
2803 | }, | ||
2804 | // 34 cards remaining = 9 words | ||
2805 | { | ||
2806 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d", | ||
2807 | type: "card", | ||
2808 | events: 18, | ||
2809 | bits: 97, | ||
2810 | words: 9, | ||
2811 | strength: "weak", | ||
2812 | }, | ||
2813 | // 35 cards remaining = 6 words | ||
2814 | { | ||
2815 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d", | ||
2816 | type: "card", | ||
2817 | events: 17, | ||
2818 | bits: 92, | ||
2819 | words: 6, | ||
2820 | strength: "very weak", | ||
2821 | }, | ||
2822 | // 40 cards remaining = 6 words | ||
2823 | { | ||
2824 | entropy: "ac2c3c4c5c6c7c8c9ctcjcqc", | ||
2825 | type: "card", | ||
2826 | events: 12, | ||
2827 | bits: 66, | ||
2828 | words: 6, | ||
2829 | strength: "very weak", | ||
2830 | }, | ||
2831 | // 41 cards remaining = 3 words | ||
2832 | { | ||
2833 | entropy: "ac2c3c4c5c6c7c8c9ctcjc", | ||
2834 | type: "card", | ||
2835 | events: 11, | ||
2836 | bits: 61, | ||
2837 | words: 3, | ||
2838 | strength: "extremely weak", | ||
2839 | }, | ||
2840 | // 46 cards remaining = 3 words | ||
2841 | { | ||
2842 | entropy: "ac2c3c4c5c6c", | ||
2843 | type: "card", | ||
2844 | events: 6, | ||
2845 | bits: 33, | ||
2846 | words: 3, | ||
2847 | strength: "extremely weak", | ||
2848 | }, | ||
2849 | // 47 cards remaining = 0 words | ||
2850 | { | ||
2851 | entropy: "ac2c3c4c5c", | ||
2852 | type: "card", | ||
2853 | events: 5, | ||
2854 | bits: 28, | ||
2855 | words: 0, | ||
2856 | strength: "extremely weak", | ||
2857 | }, | ||
2858 | ]; | 2729 | ]; |
2859 | // use entropy | 2730 | // use entropy |
2860 | page.evaluate(function() { | 2731 | page.evaluate(function() { |
@@ -2894,6 +2765,7 @@ page.open(url, function(status) { | |||
2894 | if (test.words == 0) { | 2765 | if (test.words == 0) { |
2895 | if (mnemonic.length > 0) { | 2766 | if (mnemonic.length > 0) { |
2896 | console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); | 2767 | console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); |
2768 | console.log("Entropy: " + test.entropy); | ||
2897 | console.log("Mnemonic: " + mnemonic); | 2769 | console.log("Mnemonic: " + mnemonic); |
2898 | fail(); | 2770 | fail(); |
2899 | } | 2771 | } |
@@ -2901,6 +2773,7 @@ page.open(url, function(status) { | |||
2901 | else { | 2773 | else { |
2902 | if (mnemonic.split(" ").length != test.words) { | 2774 | if (mnemonic.split(" ").length != test.words) { |
2903 | console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); | 2775 | console.log("Mnemonic length for " + test.strength + " strength is not " + test.words); |
2776 | console.log("Entropy: " + test.entropy); | ||
2904 | console.log("Mnemonic: " + mnemonic); | 2777 | console.log("Mnemonic: " + mnemonic); |
2905 | fail(); | 2778 | fail(); |
2906 | } | 2779 | } |
@@ -3107,18 +2980,6 @@ page.open(url, function(status) { | |||
3107 | var newPhrase = page.evaluate(function() { | 2980 | var newPhrase = page.evaluate(function() { |
3108 | return $(".phrase").val(); | 2981 | return $(".phrase").val(); |
3109 | }); | 2982 | }); |
3110 | // check raw entropy is in use, ie the first bits should remain the same | ||
3111 | var startLength = 20; | ||
3112 | var originalPhraseStart = originalPhrase.substring(0,startLength); | ||
3113 | var newPhraseStart = newPhrase.substring(0,startLength); | ||
3114 | if (newPhraseStart != originalPhraseStart) { | ||
3115 | console.log("Changing last 12 cards changed first portion of mnemonic"); | ||
3116 | console.log("Original:"); | ||
3117 | console.log(originalPhrase); | ||
3118 | console.log("New:"); | ||
3119 | console.log(newPhrase); | ||
3120 | fail(); | ||
3121 | } | ||
3122 | // check the phrase has changed | 2983 | // check the phrase has changed |
3123 | if (newPhrase == originalPhrase) { | 2984 | if (newPhrase == originalPhrase) { |
3124 | console.log("Changing last 12 cards does not change mnemonic"); | 2985 | console.log("Changing last 12 cards does not change mnemonic"); |