diff options
author | Ian Coleman <coleman.ian@gmail.com> | 2016-11-07 16:01:21 +1100 |
---|---|---|
committer | Ian Coleman <coleman.ian@gmail.com> | 2016-11-07 16:01:21 +1100 |
commit | adc8ce127d4f8ea0d7e5ede6a82c2791d60ff4d2 (patch) | |
tree | da8a4bfe57f207d52822cab17ae7a8af150271bf | |
parent | 6606c50fd3af59483a5524170d9d2c3ec213a60f (diff) | |
download | BIP39-adc8ce127d4f8ea0d7e5ede6a82c2791d60ff4d2.tar.gz BIP39-adc8ce127d4f8ea0d7e5ede6a82c2791d60ff4d2.tar.zst BIP39-adc8ce127d4f8ea0d7e5ede6a82c2791d60ff4d2.zip |
Cards can be used for entropy
Format is [A2-9TJQK][CDHS]
-rw-r--r-- | src/js/entropy.js | 123 | ||||
-rw-r--r-- | src/js/index.js | 21 | ||||
-rw-r--r-- | tests.js | 231 |
3 files changed, 278 insertions, 97 deletions
diff --git a/src/js/entropy.js b/src/js/entropy.js index 5b687d8..92300af 100644 --- a/src/js/entropy.js +++ b/src/js/entropy.js | |||
@@ -36,6 +36,32 @@ window.Entropy = new (function() { | |||
36 | hex: function(str) { | 36 | hex: function(str) { |
37 | return str.match(/[0-9A-F]/gi) || []; | 37 | return str.match(/[0-9A-F]/gi) || []; |
38 | }, | 38 | }, |
39 | card: function(str) { | ||
40 | // Format is NumberSuit, eg | ||
41 | // AH ace of hearts | ||
42 | // 8C eight of clubs | ||
43 | // TD ten of diamonds | ||
44 | // JS jack of spades | ||
45 | // QH queen of hearts | ||
46 | // KC king of clubs | ||
47 | return str.match(/([A2-9TJQK][CDHS])/gi) || []; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | // Convert array of cards from ["ac", "4d", "ks"] | ||
52 | // to numbers between 0 and 51 [0, 16, 51] | ||
53 | function convertCardsToInts(cards) { | ||
54 | var ints = []; | ||
55 | var values = "a23456789tjqk"; | ||
56 | var suits = "cdhs"; | ||
57 | for (var i=0; i<cards.length; i++) { | ||
58 | var card = cards[i].toLowerCase(); | ||
59 | var value = card[0]; | ||
60 | var suit = card[1]; | ||
61 | var asInt = 13 * suits.indexOf(suit) + values.indexOf(value); | ||
62 | ints.push(asInt); | ||
63 | } | ||
64 | return ints; | ||
39 | } | 65 | } |
40 | 66 | ||
41 | this.fromString = function(rawEntropyStr) { | 67 | this.fromString = function(rawEntropyStr) { |
@@ -62,61 +88,61 @@ window.Entropy = new (function() { | |||
62 | if (base.parts.length == 0) { | 88 | if (base.parts.length == 0) { |
63 | return { | 89 | return { |
64 | binaryStr: "", | 90 | binaryStr: "", |
65 | hexStr: "", | ||
66 | cleanStr: "", | 91 | cleanStr: "", |
67 | base: base, | 92 | base: base, |
68 | }; | 93 | }; |
69 | } | 94 | } |
70 | // Pull leading zeros off | 95 | // Pull leading zeros off |
71 | var leadingZeros = []; | 96 | var leadingZeros = []; |
72 | while (base.parts[0] == "0") { | 97 | while (base.ints[0] == "0") { |
73 | leadingZeros.push("0"); | 98 | leadingZeros.push("0"); |
74 | base.parts.shift(); | 99 | base.ints.shift(); |
75 | } | 100 | } |
76 | // Convert leading zeros to binary equivalent | 101 | // Convert leading zeros to binary equivalent |
77 | var numBinLeadingZeros = Math.ceil(Math.log2(base.asInt) * leadingZeros.length); | 102 | var numBinLeadingZeros = Math.floor(Math.log2(base.asInt) * leadingZeros.length); |
78 | var binLeadingZeros = ""; | 103 | var binLeadingZeros = ""; |
79 | for (var i=0; i<numBinLeadingZeros; i++) { | 104 | for (var i=0; i<numBinLeadingZeros; i++) { |
80 | binLeadingZeros += "0"; | 105 | binLeadingZeros += "0"; |
81 | } | 106 | } |
82 | // Convert leading zeros to hex equivalent | ||
83 | var numHexLeadingZeros = Math.floor(numBinLeadingZeros / 4); | ||
84 | var hexLeadingZeros = ""; | ||
85 | for (var i=0; i<numHexLeadingZeros; i++) { | ||
86 | hexLeadingZeros += "0"; | ||
87 | } | ||
88 | // Handle entropy of zero | 107 | // Handle entropy of zero |
89 | if (base.parts.length == 0) { | 108 | if (base.ints.length == 0) { |
90 | return { | 109 | return { |
91 | binaryStr: binLeadingZeros, | 110 | binaryStr: binLeadingZeros, |
92 | hexStr: hexLeadingZeros || "0", | ||
93 | cleanStr: leadingZeros, | 111 | cleanStr: leadingZeros, |
94 | base: base, | 112 | base: base, |
95 | } | 113 | } |
96 | } | 114 | } |
97 | // If using hex, should always be multiples of 4 bits, which can get | 115 | // If the first integer is small, it must be padded with zeros. |
98 | // out of sync if first number has leading 0 bits, eg 2 in hex is 0010 | 116 | // Otherwise the chance of the first bit being 1 is 100%, which is |
99 | // which would show up as 10, thus missing 2 bits it should have. | 117 | // obviously incorrect. |
100 | if (base.asInt == 16) { | 118 | // This is not perfect for unusual bases, eg base 6 has 2.6 bits, so is |
101 | var firstDigit = parseInt(base.parts[0], 16); | 119 | // slightly biased toward having leading zeros, but it's still better |
102 | if (firstDigit >= 4 && firstDigit < 8) { | 120 | // than ignoring it completely. |
103 | binLeadingZeros += "0"; | 121 | // TODO: revise this, it seems very fishy. For example, in base 10, there are |
104 | } | 122 | // 8 opportunities to start with 0 but only 2 to start with 1 |
105 | else if (firstDigit >= 2 && firstDigit < 4) { | 123 | var firstInt = base.ints[0]; |
106 | binLeadingZeros += "00"; | 124 | var firstIntBits = Math.floor(Math.log2(firstInt))+1; |
107 | } | 125 | var maxFirstIntBits = Math.floor(Math.log2(base.asInt-1))+1; |
108 | else if (firstDigit >= 1 && firstDigit < 2) { | 126 | var missingFirstIntBits = maxFirstIntBits - firstIntBits; |
109 | binLeadingZeros += "000"; | 127 | var firstIntLeadingZeros = ""; |
110 | } | 128 | for (var i=0; i<missingFirstIntBits; i++) { |
129 | binLeadingZeros += "0"; | ||
130 | } | ||
131 | // Convert base.ints to BigInteger. | ||
132 | // Due to using unusual bases, eg cards of base52, this is not as simple as | ||
133 | // using BigInteger.parse() | ||
134 | var entropyInt = BigInteger.ZERO; | ||
135 | for (var i=base.ints.length-1; i>=0; i--) { | ||
136 | var thisInt = BigInteger.parse(base.ints[i]); | ||
137 | var power = (base.ints.length - 1) - i; | ||
138 | var additionalEntropy = BigInteger.parse(base.asInt).pow(power).multiply(thisInt); | ||
139 | entropyInt = entropyInt.add(additionalEntropy); | ||
111 | } | 140 | } |
112 | // Convert entropy to different foramts | 141 | // Convert entropy to different formats |
113 | var entropyInt = BigInteger.parse(base.parts.join(""), base.asInt); | ||
114 | var entropyBin = binLeadingZeros + entropyInt.toString(2); | 142 | var entropyBin = binLeadingZeros + entropyInt.toString(2); |
115 | var entropyHex = hexLeadingZeros + entropyInt.toString(16); | 143 | var entropyClean = base.parts.join(""); |
116 | var entropyClean = leadingZeros.join("") + base.parts.join(""); | ||
117 | var e = { | 144 | var e = { |
118 | binaryStr: entropyBin, | 145 | binaryStr: entropyBin, |
119 | hexStr: entropyHex, | ||
120 | cleanStr: entropyClean, | 146 | cleanStr: entropyClean, |
121 | base: base, | 147 | base: base, |
122 | } | 148 | } |
@@ -129,17 +155,32 @@ window.Entropy = new (function() { | |||
129 | var binaryMatches = matchers.binary(str); | 155 | var binaryMatches = matchers.binary(str); |
130 | var hexMatches = matchers.hex(str); | 156 | var hexMatches = matchers.hex(str); |
131 | // Find the lowest base that can be used, whilst ignoring any irrelevant chars | 157 | // Find the lowest base that can be used, whilst ignoring any irrelevant chars |
132 | if (binaryMatches.length == hexMatches.length) { | 158 | if (binaryMatches.length == hexMatches.length && hexMatches.length > 0) { |
159 | var ints = binaryMatches.map(function(i) { return parseInt(i, 2) }); | ||
133 | return { | 160 | return { |
161 | ints: ints, | ||
134 | parts: binaryMatches, | 162 | parts: binaryMatches, |
135 | matcher: matchers.binary, | 163 | matcher: matchers.binary, |
136 | asInt: 2, | 164 | asInt: 2, |
137 | str: "binary", | 165 | str: "binary", |
138 | } | 166 | } |
139 | } | 167 | } |
168 | var cardMatches = matchers.card(str); | ||
169 | if (cardMatches.length >= hexMatches.length / 2) { | ||
170 | var ints = convertCardsToInts(cardMatches); | ||
171 | return { | ||
172 | ints: ints, | ||
173 | parts: cardMatches, | ||
174 | matcher: matchers.card, | ||
175 | asInt: 52, | ||
176 | str: "card", | ||
177 | } | ||
178 | } | ||
140 | var diceMatches = matchers.dice(str); | 179 | var diceMatches = matchers.dice(str); |
141 | if (diceMatches.length == hexMatches.length) { | 180 | if (diceMatches.length == hexMatches.length && hexMatches.length > 0) { |
181 | var ints = diceMatches.map(function(i) { return parseInt(i) }); | ||
142 | return { | 182 | return { |
183 | ints: ints, | ||
143 | parts: diceMatches, | 184 | parts: diceMatches, |
144 | matcher: matchers.dice, | 185 | matcher: matchers.dice, |
145 | asInt: 6, | 186 | asInt: 6, |
@@ -147,8 +188,10 @@ window.Entropy = new (function() { | |||
147 | } | 188 | } |
148 | } | 189 | } |
149 | var base6Matches = matchers.base6(str); | 190 | var base6Matches = matchers.base6(str); |
150 | if (base6Matches.length == hexMatches.length) { | 191 | if (base6Matches.length == hexMatches.length && hexMatches.length > 0) { |
192 | var ints = base6Matches.map(function(i) { return parseInt(i) }); | ||
151 | return { | 193 | return { |
194 | ints: ints, | ||
152 | parts: base6Matches, | 195 | parts: base6Matches, |
153 | matcher: matchers.base6, | 196 | matcher: matchers.base6, |
154 | asInt: 6, | 197 | asInt: 6, |
@@ -156,15 +199,19 @@ window.Entropy = new (function() { | |||
156 | } | 199 | } |
157 | } | 200 | } |
158 | var base10Matches = matchers.base10(str); | 201 | var base10Matches = matchers.base10(str); |
159 | if (base10Matches.length == hexMatches.length) { | 202 | if (base10Matches.length == hexMatches.length && hexMatches.length > 0) { |
203 | var ints = base10Matches.map(function(i) { return parseInt(i) }); | ||
160 | return { | 204 | return { |
205 | ints: ints, | ||
161 | parts: base10Matches, | 206 | parts: base10Matches, |
162 | matcher: matchers.base10, | 207 | matcher: matchers.base10, |
163 | asInt: 10, | 208 | asInt: 10, |
164 | str: "base 10", | 209 | str: "base 10", |
165 | } | 210 | } |
166 | } | 211 | } |
212 | var ints = hexMatches.map(function(i) { return parseInt(i, 16) }); | ||
167 | return { | 213 | return { |
214 | ints: ints, | ||
168 | parts: hexMatches, | 215 | parts: hexMatches, |
169 | matcher: matchers.hex, | 216 | matcher: matchers.hex, |
170 | asInt: 16, | 217 | asInt: 16, |
@@ -175,7 +222,11 @@ window.Entropy = new (function() { | |||
175 | // Polyfill for Math.log2 | 222 | // Polyfill for Math.log2 |
176 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill | 223 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill |
177 | Math.log2 = Math.log2 || function(x) { | 224 | Math.log2 = Math.log2 || function(x) { |
178 | return Math.log(x) * Math.LOG2E; | 225 | // The polyfill isn't good enough because of the poor accuracy of |
226 | // Math.LOG2E | ||
227 | // log2(8) gave 2.9999999999999996 which when floored causes issues. | ||
228 | // So instead use the BigInteger library to get it right. | ||
229 | return BigInteger.log(x) / BigInteger.log(2); | ||
179 | }; | 230 | }; |
180 | 231 | ||
181 | })(); | 232 | })(); |
diff --git a/src/js/index.js b/src/js/index.js index 1cc77d3..a717a9e 100644 --- a/src/js/index.js +++ b/src/js/index.js | |||
@@ -738,33 +738,36 @@ | |||
738 | // Show entropy details | 738 | // Show entropy details |
739 | var extraBits = 32 - (entropy.binaryStr.length % 32); | 739 | var extraBits = 32 - (entropy.binaryStr.length % 32); |
740 | var extraChars = Math.ceil(extraBits * Math.log(2) / Math.log(entropy.base.asInt)); | 740 | var extraChars = Math.ceil(extraBits * Math.log(2) / Math.log(entropy.base.asInt)); |
741 | var words = Math.floor(entropy.binaryStr.length / 32) * 3; | ||
741 | var strength = "an extremely weak"; | 742 | var strength = "an extremely weak"; |
742 | if (entropy.hexStr.length >= 8) { | 743 | if (words >= 3) { |
743 | strength = "a very weak"; | 744 | strength = "a very weak"; |
744 | } | 745 | } |
745 | if (entropy.hexStr.length >= 12) { | 746 | if (words >= 6) { |
746 | strength = "a weak"; | 747 | strength = "a weak"; |
747 | } | 748 | } |
748 | if (entropy.hexStr.length >= 24) { | 749 | if (words >= 9) { |
749 | strength = "a strong"; | 750 | strength = "a strong"; |
750 | } | 751 | } |
751 | if (entropy.hexStr.length >= 32) { | 752 | if (words >= 12) { |
752 | strength = "a very strong"; | 753 | strength = "a very strong"; |
753 | } | 754 | } |
754 | if (entropy.hexStr.length >= 40) { | 755 | if (words >= 15) { |
755 | strength = "an extremely strong"; | 756 | strength = "an extremely strong"; |
756 | } | 757 | } |
757 | if (entropy.hexStr.length >=48) { | 758 | if (words >= 18) { |
758 | strength = "an even stronger" | 759 | strength = "an even stronger" |
759 | } | 760 | } |
760 | var msg = "Have " + entropy.binaryStr.length + " bits of entropy, " + extraChars + " more " + entropy.base.str + " chars required to generate " + strength + " mnemonic: " + entropy.cleanStr; | 761 | var msg = "Have " + entropy.binaryStr.length + " bits of entropy, " + extraChars + " more " + entropy.base.str + " chars required to generate " + strength + " mnemonic: " + entropy.cleanStr; |
761 | showEntropyError(msg); | 762 | showEntropyError(msg); |
762 | // Discard trailing entropy | 763 | // Discard trailing entropy |
763 | var hexStr = entropy.hexStr.substring(0, Math.floor(entropy.hexStr.length / 8) * 8); | 764 | var bitsToUse = Math.floor(entropy.binaryStr.length / 32) * 32; |
765 | var binaryStr = entropy.binaryStr.substring(0, bitsToUse); | ||
764 | // Convert entropy string to numeric array | 766 | // Convert entropy string to numeric array |
765 | var entropyArr = []; | 767 | var entropyArr = []; |
766 | for (var i=0; i<hexStr.length / 2; i++) { | 768 | for (var i=0; i<binaryStr.length / 8; i++) { |
767 | var entropyByte = parseInt(hexStr[i*2].concat(hexStr[i*2+1]), 16); | 769 | var byteAsBits = binaryStr.substring(i*8, i*8+8); |
770 | var entropyByte = parseInt(byteAsBits, 2); | ||
768 | entropyArr.push(entropyByte) | 771 | entropyArr.push(entropyByte) |
769 | } | 772 | } |
770 | // Convert entropy array to mnemonic | 773 | // Convert entropy array to mnemonic |
@@ -1997,85 +1997,212 @@ page.open(url, function(status) { | |||
1997 | // Entropy unit tests | 1997 | // Entropy unit tests |
1998 | function() { | 1998 | function() { |
1999 | page.open(url, function(status) { | 1999 | page.open(url, function(status) { |
2000 | var error = page.evaluate(function() { | 2000 | var response = page.evaluate(function() { |
2001 | var e; | 2001 | var e; |
2002 | // binary entropy is detected | 2002 | // binary entropy is detected |
2003 | e = Entropy.fromString("01010101"); | 2003 | try { |
2004 | if (e.base.str != "binary") { | 2004 | e = Entropy.fromString("01010101"); |
2005 | return "Binary entropy not detected correctly"; | 2005 | if (e.base.str != "binary") { |
2006 | return "Binary entropy not detected correctly"; | ||
2007 | } | ||
2008 | } | ||
2009 | catch (e) { | ||
2010 | return e.message; | ||
2006 | } | 2011 | } |
2007 | // base6 entropy is detected | 2012 | // base6 entropy is detected |
2008 | e = Entropy.fromString("012345012345"); | 2013 | try { |
2009 | if (e.base.str != "base 6") { | 2014 | e = Entropy.fromString("012345012345"); |
2010 | return "base6 entropy not detected correctly"; | 2015 | if (e.base.str != "base 6") { |
2016 | return "base6 entropy not detected correctly"; | ||
2017 | } | ||
2018 | } | ||
2019 | catch (e) { | ||
2020 | return e.message; | ||
2011 | } | 2021 | } |
2012 | // dice entropy is detected | 2022 | // dice entropy is detected |
2013 | e = Entropy.fromString("123456123456"); | 2023 | try { |
2014 | if (e.base.str != "base 6 (dice)") { | 2024 | e = Entropy.fromString("123456123456"); |
2015 | return "dice entropy not detected correctly"; | 2025 | if (e.base.str != "base 6 (dice)") { |
2026 | return "dice entropy not detected correctly"; | ||
2027 | } | ||
2028 | } | ||
2029 | catch (e) { | ||
2030 | return e.message; | ||
2016 | } | 2031 | } |
2017 | // base10 entropy is detected | 2032 | // base10 entropy is detected |
2018 | e = Entropy.fromString("0123456789"); | 2033 | try { |
2019 | if (e.base.str != "base 10") { | 2034 | e = Entropy.fromString("0123456789"); |
2020 | return "base10 entropy not detected correctly"; | 2035 | if (e.base.str != "base 10") { |
2036 | return "base10 entropy not detected correctly"; | ||
2037 | } | ||
2038 | } | ||
2039 | catch (e) { | ||
2040 | return e.message; | ||
2021 | } | 2041 | } |
2022 | // hex entropy is detected | 2042 | // hex entropy is detected |
2023 | e = Entropy.fromString("0123456789ABCDEF"); | 2043 | try { |
2024 | if (e.base.str != "hexadecimal") { | 2044 | e = Entropy.fromString("0123456789ABCDEF"); |
2025 | return "hexadecimal entropy not detected correctly"; | 2045 | if (e.base.str != "hexadecimal") { |
2046 | return "hexadecimal entropy not detected correctly"; | ||
2047 | } | ||
2048 | } | ||
2049 | catch (e) { | ||
2050 | return e.message; | ||
2051 | } | ||
2052 | // card entropy is detected | ||
2053 | try { | ||
2054 | e = Entropy.fromString("AC4DTHKS"); | ||
2055 | if (e.base.str != "card") { | ||
2056 | return "card entropy not detected correctly"; | ||
2057 | } | ||
2058 | } | ||
2059 | catch (e) { | ||
2060 | return e.message; | ||
2026 | } | 2061 | } |
2027 | // entropy is case insensitive | 2062 | // entropy is case insensitive |
2028 | e = Entropy.fromString("aBcDeF"); | 2063 | try { |
2029 | if (e.cleanStr != "aBcDeF") { | 2064 | e = Entropy.fromString("aBcDeF"); |
2030 | return "Entropy should not be case sensitive"; | 2065 | if (e.cleanStr != "aBcDeF") { |
2066 | return "Entropy should not be case sensitive"; | ||
2067 | } | ||
2068 | } | ||
2069 | catch (e) { | ||
2070 | return e.message; | ||
2031 | } | 2071 | } |
2032 | // dice entropy is converted to base6 | 2072 | // dice entropy is converted to base6 |
2033 | e = Entropy.fromString("123456"); | 2073 | try { |
2034 | if (e.cleanStr != "012345") { | 2074 | e = Entropy.fromString("123456"); |
2035 | return "Dice entropy is not automatically converted to base6"; | 2075 | if (e.cleanStr != "012345") { |
2076 | return "Dice entropy is not automatically converted to base6"; | ||
2077 | } | ||
2078 | } | ||
2079 | catch (e) { | ||
2080 | return e.message; | ||
2036 | } | 2081 | } |
2037 | // dice entropy is preferred to base6 if ambiguous | 2082 | // dice entropy is preferred to base6 if ambiguous |
2038 | e = Entropy.fromString("12345"); | 2083 | try { |
2039 | if (e.base.str != "base 6 (dice)") { | 2084 | e = Entropy.fromString("12345"); |
2040 | return "dice not used as default over base 6"; | 2085 | if (e.base.str != "base 6 (dice)") { |
2086 | return "dice not used as default over base 6"; | ||
2087 | } | ||
2088 | } | ||
2089 | catch (e) { | ||
2090 | return e.message; | ||
2041 | } | 2091 | } |
2042 | // unused characters are ignored | 2092 | // unused characters are ignored |
2043 | e = Entropy.fromString("fghijkl"); | 2093 | try { |
2044 | if (e.cleanStr != "f") { | 2094 | e = Entropy.fromString("fghijkl"); |
2045 | return "additional characters are not ignored"; | 2095 | if (e.cleanStr != "f") { |
2096 | return "additional characters are not ignored"; | ||
2097 | } | ||
2098 | } | ||
2099 | catch (e) { | ||
2100 | return e.message; | ||
2046 | } | 2101 | } |
2047 | // the lowest base is used by default | 2102 | // the lowest base is used by default |
2048 | // 7 could be decimal or hexadecimal, but should be detected as decimal | 2103 | // 7 could be decimal or hexadecimal, but should be detected as decimal |
2049 | e = Entropy.fromString("7"); | 2104 | try { |
2050 | if (e.base.str != "base 10") { | 2105 | e = Entropy.fromString("7"); |
2051 | return "lowest base is not used"; | 2106 | if (e.base.str != "base 10") { |
2107 | return "lowest base is not used"; | ||
2108 | } | ||
2052 | } | 2109 | } |
2053 | // Hexadecimal representation is returned | 2110 | catch (e) { |
2054 | e = Entropy.fromString("1010"); | 2111 | return e.message; |
2055 | if (e.hexStr != "A") { | ||
2056 | return "Hexadecimal representation not returned"; | ||
2057 | } | 2112 | } |
2058 | // Leading zeros are retained | 2113 | // Leading zeros are retained |
2059 | e = Entropy.fromString("000A"); | 2114 | try { |
2060 | if (e.cleanStr != "000A") { | 2115 | e = Entropy.fromString("000A"); |
2061 | return "Leading zeros are not retained"; | 2116 | if (e.cleanStr != "000A") { |
2117 | return "Leading zeros are not retained"; | ||
2118 | } | ||
2119 | } | ||
2120 | catch (e) { | ||
2121 | return e.message; | ||
2062 | } | 2122 | } |
2063 | // Leading zeros are correctly preserved for hex in binary string | 2123 | // Leading zeros are correctly preserved for hex in binary string |
2064 | e = Entropy.fromString("2A"); | 2124 | try { |
2065 | if (e.binaryStr != "00101010") { | 2125 | e = Entropy.fromString("2A"); |
2066 | return "Hex leading zeros are not correct in binary"; | 2126 | if (e.binaryStr != "00101010") { |
2127 | return "Hex leading zeros are not correct in binary"; | ||
2128 | } | ||
2129 | } | ||
2130 | catch (e) { | ||
2131 | return e.message; | ||
2132 | } | ||
2133 | // Leading zeros are correctly preserved for base 6 in binary string | ||
2134 | try { | ||
2135 | e = Entropy.fromString("2"); | ||
2136 | if (e.binaryStr != "010") { | ||
2137 | return "Base 6 leading zeros are not correct in binary"; | ||
2138 | } | ||
2139 | } | ||
2140 | catch (e) { | ||
2141 | return e.message; | ||
2067 | } | 2142 | } |
2068 | // Keyboard mashing results in weak entropy | 2143 | // Keyboard mashing results in weak entropy |
2069 | // Despite being a long string, it's less than 30 bits of entropy | 2144 | // Despite being a long string, it's less than 30 bits of entropy |
2070 | e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj"); | 2145 | try { |
2071 | if (e.binaryStr.length >= 30) { | 2146 | e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj"); |
2072 | return "Keyboard mashing should produce weak entropy"; | 2147 | if (e.binaryStr.length >= 30) { |
2148 | return "Keyboard mashing should produce weak entropy"; | ||
2149 | } | ||
2073 | } | 2150 | } |
2074 | return false; | 2151 | catch (e) { |
2152 | return e.message; | ||
2153 | } | ||
2154 | // Card entropy is used if every pair could be a card | ||
2155 | try { | ||
2156 | e = Entropy.fromString("4c3c2c"); | ||
2157 | if (e.base.str != "card") { | ||
2158 | return "Card entropy not used if all pairs are cards"; | ||
2159 | } | ||
2160 | } | ||
2161 | catch (e) { | ||
2162 | return e.message; | ||
2163 | } | ||
2164 | // Card entropy uses base 52 | ||
2165 | // [ cards, binary ] | ||
2166 | try { | ||
2167 | var cards = [ | ||
2168 | [ "ac", "00000" ], | ||
2169 | [ "acac", "00000000000" ], | ||
2170 | [ "acac2c", "00000000000000001" ], | ||
2171 | [ "acks", "00000110011" ], | ||
2172 | [ "acacks", "00000000000110011" ], | ||
2173 | [ "2c", "000001" ], | ||
2174 | [ "3d", "001111" ], | ||
2175 | [ "4h", "011101" ], | ||
2176 | [ "5s", "101011" ], | ||
2177 | [ "6c", "000101" ], | ||
2178 | [ "7d", "010011" ], | ||
2179 | [ "8h", "100001" ], | ||
2180 | [ "9s", "101111" ], | ||
2181 | [ "tc", "001001" ], | ||
2182 | [ "jd", "010111" ], | ||
2183 | [ "qh", "100101" ], | ||
2184 | [ "ks", "110011" ], | ||
2185 | [ "ks2c", "101001011101" ], | ||
2186 | [ "KS2C", "101001011101" ], | ||
2187 | ]; | ||
2188 | for (var i=0; i<cards.length; i++) { | ||
2189 | var card = cards[i][0]; | ||
2190 | var result = cards[i][1]; | ||
2191 | e = Entropy.fromString(card); | ||
2192 | console.log(e.binary + " " + result); | ||
2193 | if (e.binaryStr !== result) { | ||
2194 | return "card entropy not parsed correctly: " + result + " != " + e.binaryStr; | ||
2195 | } | ||
2196 | } | ||
2197 | } | ||
2198 | catch (e) { | ||
2199 | return e.message; | ||
2200 | } | ||
2201 | return "PASS"; | ||
2075 | }); | 2202 | }); |
2076 | if (error) { | 2203 | if (response != "PASS") { |
2077 | console.log("Entropy unit tests"); | 2204 | console.log("Entropy unit tests"); |
2078 | console.log(error); | 2205 | console.log(response); |
2079 | fail(); | 2206 | fail(); |
2080 | }; | 2207 | }; |
2081 | next(); | 2208 | next(); |
@@ -2339,10 +2466,10 @@ page.open(url, function(status) { | |||
2339 | [ "0", "1" ], | 2466 | [ "0", "1" ], |
2340 | [ "0000", "4" ], | 2467 | [ "0000", "4" ], |
2341 | [ "6", "3" ], | 2468 | [ "6", "3" ], |
2342 | [ "7", "3" ], | 2469 | [ "7", "4" ], |
2343 | [ "8", "4" ], | 2470 | [ "8", "4" ], |
2344 | [ "F", "4" ], | 2471 | [ "F", "4" ], |
2345 | [ "29", "5" ], | 2472 | [ "29", "7" ], |
2346 | [ "0A", "8" ], | 2473 | [ "0A", "8" ], |
2347 | [ "1A", "8" ], // hex is always multiple of 4 bits of entropy | 2474 | [ "1A", "8" ], // hex is always multiple of 4 bits of entropy |
2348 | [ "2A", "8" ], | 2475 | [ "2A", "8" ], |
@@ -2350,9 +2477,9 @@ page.open(url, function(status) { | |||
2350 | [ "8A", "8" ], | 2477 | [ "8A", "8" ], |
2351 | [ "FA", "8" ], | 2478 | [ "FA", "8" ], |
2352 | [ "000A", "16" ], | 2479 | [ "000A", "16" ], |
2353 | [ "2220", "10" ], | 2480 | [ "2220", "11" ], |
2354 | [ "2221", "9" ], // uses dice, so entropy is actually 1110 | 2481 | [ "2221", "11" ], // uses dice, so entropy is actually 1110 |
2355 | [ "2227", "12" ], | 2482 | [ "2227", "14" ], |
2356 | [ "222F", "16" ], | 2483 | [ "222F", "16" ], |
2357 | [ "FFFF", "16" ], | 2484 | [ "FFFF", "16" ], |
2358 | ] | 2485 | ] |