]>
Commit | Line | Data |
---|---|---|
6606c50f IC |
1 | /* |
2 | * Detects entropy from a string. | |
3 | * | |
4 | * Formats include: | |
5 | * binary [0-1] | |
6 | * base 6 [0-5] | |
7 | * dice 6 [1-6] | |
8 | * decimal [0-9] | |
9 | * hexadecimal [0-9A-F] | |
0fdcf2eb | 10 | * card [A2-9TJQK][CDHS] |
6606c50f IC |
11 | * |
12 | * Automatically uses lowest entropy to avoid issues such as interpretting 0101 | |
13 | * as hexadecimal which would be 16 bits when really it's only 4 bits of binary | |
14 | * entropy. | |
15 | */ | |
16 | ||
c6624d51 IC |
17 | window.Entropy = new (function() { |
18 | ||
bf96267f IC |
19 | let eventBits = { |
20 | ||
21 | "binary": { | |
22 | "0": "0", | |
23 | "1": "1", | |
24 | }, | |
25 | ||
26 | // log2(6) = 2.58496 bits per roll, with bias | |
27 | // 4 rolls give 2 bits each | |
28 | // 2 rolls give 1 bit each | |
29 | // Average (4*2 + 2*1) / 6 = 1.66 bits per roll without bias | |
30 | "base 6": { | |
31 | "0": "00", | |
32 | "1": "01", | |
33 | "2": "10", | |
34 | "3": "11", | |
35 | "4": "0", | |
36 | "5": "1", | |
37 | }, | |
38 | ||
39 | // log2(6) = 2.58496 bits per roll, with bias | |
40 | // 4 rolls give 2 bits each | |
41 | // 2 rolls give 1 bit each | |
42 | // Average (4*2 + 2*1) / 6 = 1.66 bits per roll without bias | |
43 | "base 6 (dice)": { | |
44 | "0": "00", // equivalent to 0 in base 6 | |
45 | "1": "01", | |
46 | "2": "10", | |
47 | "3": "11", | |
48 | "4": "0", | |
49 | "5": "1", | |
50 | }, | |
51 | ||
52 | // log2(10) = 3.321928 bits per digit, with bias | |
53 | // 8 digits give 3 bits each | |
54 | // 2 digits give 1 bit each | |
55 | // Average (8*3 + 2*1) / 10 = 2.6 bits per digit without bias | |
56 | "base 10": { | |
57 | "0": "000", | |
58 | "1": "001", | |
59 | "2": "010", | |
60 | "3": "011", | |
61 | "4": "100", | |
62 | "5": "101", | |
63 | "6": "110", | |
64 | "7": "111", | |
65 | "8": "0", | |
66 | "9": "1", | |
67 | }, | |
68 | ||
69 | "hexadecimal": { | |
70 | "0": "0000", | |
71 | "1": "0001", | |
72 | "2": "0010", | |
73 | "3": "0011", | |
74 | "4": "0100", | |
75 | "5": "0101", | |
76 | "6": "0110", | |
77 | "7": "0111", | |
78 | "8": "1000", | |
79 | "9": "1001", | |
80 | "a": "1010", | |
81 | "b": "1011", | |
82 | "c": "1100", | |
83 | "d": "1101", | |
84 | "e": "1110", | |
85 | "f": "1111", | |
86 | }, | |
87 | ||
88 | // log2(52) = 5.7004 bits per card, with bias | |
89 | // 32 cards give 5 bits each | |
90 | // 16 cards give 4 bits each | |
91 | // 4 cards give 2 bits each | |
92 | // Average (32*5 + 16*4 + 4*2) / 52 = 4.46 bits per card without bias | |
93 | "card": { | |
94 | "ac": "00000", | |
95 | "2c": "00001", | |
96 | "3c": "00010", | |
97 | "4c": "00011", | |
98 | "5c": "00100", | |
99 | "6c": "00101", | |
100 | "7c": "00110", | |
101 | "8c": "00111", | |
102 | "9c": "01000", | |
103 | "tc": "01001", | |
104 | "jc": "01010", | |
105 | "qc": "01011", | |
106 | "kc": "01100", | |
107 | "ad": "01101", | |
108 | "2d": "01110", | |
109 | "3d": "01111", | |
110 | "4d": "10000", | |
111 | "5d": "10001", | |
112 | "6d": "10010", | |
113 | "7d": "10011", | |
114 | "8d": "10100", | |
115 | "9d": "10101", | |
116 | "td": "10110", | |
117 | "jd": "10111", | |
118 | "qd": "11000", | |
119 | "kd": "11001", | |
120 | "ah": "11010", | |
121 | "2h": "11011", | |
122 | "3h": "11100", | |
123 | "4h": "11101", | |
124 | "5h": "11110", | |
125 | "6h": "11111", | |
126 | "7h": "0000", | |
127 | "8h": "0001", | |
128 | "9h": "0010", | |
129 | "th": "0011", | |
130 | "jh": "0100", | |
131 | "qh": "0101", | |
132 | "kh": "0110", | |
133 | "as": "0111", | |
134 | "2s": "1000", | |
135 | "3s": "1001", | |
136 | "4s": "1010", | |
137 | "5s": "1011", | |
138 | "6s": "1100", | |
139 | "7s": "1101", | |
140 | "8s": "1110", | |
141 | "9s": "1111", | |
142 | "ts": "00", | |
143 | "js": "01", | |
144 | "qs": "10", | |
145 | "ks": "11", | |
146 | }, | |
147 | ||
148 | } | |
886f06ee | 149 | |
6606c50f IC |
150 | // matchers returns an array of the matched events for each type of entropy. |
151 | // eg | |
152 | // matchers.binary("010") returns ["0", "1", "0"] | |
153 | // matchers.binary("a10") returns ["1", "0"] | |
154 | // matchers.hex("a10") returns ["a", "1", "0"] | |
c6624d51 | 155 | var matchers = { |
6606c50f IC |
156 | binary: function(str) { |
157 | return str.match(/[0-1]/gi) || []; | |
158 | }, | |
159 | base6: function(str) { | |
160 | return str.match(/[0-5]/gi) || []; | |
161 | }, | |
162 | dice: function(str) { | |
163 | return str.match(/[1-6]/gi) || []; // ie dice numbers | |
164 | }, | |
165 | base10: function(str) { | |
166 | return str.match(/[0-9]/gi) || []; | |
167 | }, | |
168 | hex: function(str) { | |
169 | return str.match(/[0-9A-F]/gi) || []; | |
170 | }, | |
adc8ce12 IC |
171 | card: function(str) { |
172 | // Format is NumberSuit, eg | |
173 | // AH ace of hearts | |
174 | // 8C eight of clubs | |
175 | // TD ten of diamonds | |
176 | // JS jack of spades | |
177 | // QH queen of hearts | |
178 | // KC king of clubs | |
179 | return str.match(/([A2-9TJQK][CDHS])/gi) || []; | |
180 | } | |
181 | } | |
182 | ||
516c16d7 | 183 | this.fromString = function(rawEntropyStr, baseStr) { |
c6624d51 | 184 | // Find type of entropy being used (binary, hex, dice etc) |
516c16d7 | 185 | var base = getBase(rawEntropyStr, baseStr); |
c6624d51 | 186 | // Convert dice to base6 entropy (ie 1-6 to 0-5) |
425b75a9 | 187 | // This is done by changing all 6s to 0s |
c6624d51 | 188 | if (base.str == "dice") { |
bf96267f IC |
189 | var newEvents = []; |
190 | for (var i=0; i<base.events.length; i++) { | |
191 | var c = base.events[i]; | |
425b75a9 | 192 | if ("12345".indexOf(c) > -1) { |
bf96267f | 193 | newEvents[i] = base.events[i]; |
c6624d51 IC |
194 | } |
195 | else { | |
bf96267f | 196 | newEvents[i] = "0"; |
c6624d51 IC |
197 | } |
198 | } | |
c6624d51 | 199 | base.str = "base 6 (dice)"; |
bf96267f | 200 | base.events = newEvents; |
c6624d51 IC |
201 | base.matcher = matchers.base6; |
202 | } | |
c6624d51 | 203 | // Detect empty entropy |
bf96267f | 204 | if (base.events.length == 0) { |
c6624d51 IC |
205 | return { |
206 | binaryStr: "", | |
c6624d51 | 207 | cleanStr: "", |
b54c1218 | 208 | cleanHtml: "", |
c6624d51 IC |
209 | base: base, |
210 | }; | |
211 | } | |
bf96267f IC |
212 | // Convert entropy events to binary |
213 | var entropyBin = base.events.map(function(e) { | |
214 | return eventBits[base.str][e.toLowerCase()]; | |
215 | }).join(""); | |
216 | // Get average bits per event | |
217 | // which may be adjusted for bias if log2(base) is fractional | |
218 | var bitsPerEvent = base.bitsPerEvent; | |
1cf1bbaf | 219 | // Supply a 'filtered' entropy string for display purposes |
bf96267f IC |
220 | var entropyClean = base.events.join(""); |
221 | var entropyHtml = base.events.join(""); | |
c193ff67 | 222 | if (base.asInt == 52) { |
bf96267f | 223 | entropyClean = base.events.join(" ").toUpperCase(); |
c193ff67 IC |
224 | entropyClean = entropyClean.replace(/C/g, "\u2663"); |
225 | entropyClean = entropyClean.replace(/D/g, "\u2666"); | |
226 | entropyClean = entropyClean.replace(/H/g, "\u2665"); | |
227 | entropyClean = entropyClean.replace(/S/g, "\u2660"); | |
bf96267f | 228 | entropyHtml = base.events.join(" ").toUpperCase(); |
b54c1218 IC |
229 | entropyHtml = entropyHtml.replace(/C/g, "<span class='card-suit club'>\u2663</span>"); |
230 | entropyHtml = entropyHtml.replace(/D/g, "<span class='card-suit diamond'>\u2666</span>"); | |
231 | entropyHtml = entropyHtml.replace(/H/g, "<span class='card-suit heart'>\u2665</span>"); | |
232 | entropyHtml = entropyHtml.replace(/S/g, "<span class='card-suit spade'>\u2660</span>"); | |
c193ff67 | 233 | } |
0fdcf2eb | 234 | // Return the result |
c6624d51 IC |
235 | var e = { |
236 | binaryStr: entropyBin, | |
c6624d51 | 237 | cleanStr: entropyClean, |
b54c1218 | 238 | cleanHtml: entropyHtml, |
94959756 | 239 | bitsPerEvent: bitsPerEvent, |
c6624d51 IC |
240 | base: base, |
241 | } | |
242 | return e; | |
243 | } | |
244 | ||
516c16d7 | 245 | function getBase(str, baseStr) { |
c6624d51 IC |
246 | // Need to get the lowest base for the supplied entropy. |
247 | // This prevents interpreting, say, dice rolls as hexadecimal. | |
6606c50f IC |
248 | var binaryMatches = matchers.binary(str); |
249 | var hexMatches = matchers.hex(str); | |
516c16d7 | 250 | var autodetect = baseStr === undefined; |
c6624d51 | 251 | // Find the lowest base that can be used, whilst ignoring any irrelevant chars |
516c16d7 | 252 | if ((binaryMatches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "binary") { |
adc8ce12 | 253 | var ints = binaryMatches.map(function(i) { return parseInt(i, 2) }); |
c6624d51 | 254 | return { |
adc8ce12 | 255 | ints: ints, |
bf96267f | 256 | events: binaryMatches, |
c6624d51 IC |
257 | matcher: matchers.binary, |
258 | asInt: 2, | |
bf96267f | 259 | bitsPerEvent: 1, |
c6624d51 IC |
260 | str: "binary", |
261 | } | |
262 | } | |
adc8ce12 | 263 | var cardMatches = matchers.card(str); |
516c16d7 | 264 | if ((cardMatches.length >= hexMatches.length / 2 && autodetect) || baseStr === "card") { |
adc8ce12 IC |
265 | return { |
266 | ints: ints, | |
bf96267f | 267 | events: cardMatches, |
adc8ce12 IC |
268 | matcher: matchers.card, |
269 | asInt: 52, | |
bf96267f | 270 | bitsPerEvent: (32*5 + 16*4 + 4*2) / 52, // see cardBits |
adc8ce12 IC |
271 | str: "card", |
272 | } | |
273 | } | |
6606c50f | 274 | var diceMatches = matchers.dice(str); |
516c16d7 | 275 | if ((diceMatches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "dice") { |
adc8ce12 | 276 | var ints = diceMatches.map(function(i) { return parseInt(i) }); |
c6624d51 | 277 | return { |
adc8ce12 | 278 | ints: ints, |
bf96267f | 279 | events: diceMatches, |
c6624d51 IC |
280 | matcher: matchers.dice, |
281 | asInt: 6, | |
bf96267f | 282 | bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits |
c6624d51 IC |
283 | str: "dice", |
284 | } | |
285 | } | |
6606c50f | 286 | var base6Matches = matchers.base6(str); |
516c16d7 | 287 | if ((base6Matches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "base 6") { |
adc8ce12 | 288 | var ints = base6Matches.map(function(i) { return parseInt(i) }); |
c6624d51 | 289 | return { |
adc8ce12 | 290 | ints: ints, |
bf96267f | 291 | events: base6Matches, |
c6624d51 IC |
292 | matcher: matchers.base6, |
293 | asInt: 6, | |
bf96267f | 294 | bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits |
c6624d51 IC |
295 | str: "base 6", |
296 | } | |
297 | } | |
6606c50f | 298 | var base10Matches = matchers.base10(str); |
516c16d7 | 299 | if ((base10Matches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "base 10") { |
adc8ce12 | 300 | var ints = base10Matches.map(function(i) { return parseInt(i) }); |
c6624d51 | 301 | return { |
adc8ce12 | 302 | ints: ints, |
bf96267f | 303 | events: base10Matches, |
c6624d51 IC |
304 | matcher: matchers.base10, |
305 | asInt: 10, | |
bf96267f | 306 | bitsPerEvent: (8*3 + 2*1) / 10, // see b10Bits |
c6624d51 IC |
307 | str: "base 10", |
308 | } | |
309 | } | |
adc8ce12 | 310 | var ints = hexMatches.map(function(i) { return parseInt(i, 16) }); |
c6624d51 | 311 | return { |
adc8ce12 | 312 | ints: ints, |
bf96267f | 313 | events: hexMatches, |
c6624d51 IC |
314 | matcher: matchers.hex, |
315 | asInt: 16, | |
bf96267f | 316 | bitsPerEvent: 4, |
c6624d51 IC |
317 | str: "hexadecimal", |
318 | } | |
319 | } | |
320 | ||
c6624d51 | 321 | })(); |