]>
Commit | Line | Data |
---|---|---|
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] | |
10 | * card [A2-9TJQK][CDHS] | |
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 | ||
17 | window.Entropy = new (function() { | |
18 | ||
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 | } | |
149 | ||
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"] | |
155 | var matchers = { | |
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 | }, | |
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 | ||
183 | this.fromString = function(rawEntropyStr, baseStr) { | |
184 | // Find type of entropy being used (binary, hex, dice etc) | |
185 | var base = getBase(rawEntropyStr, baseStr); | |
186 | // Convert dice to base6 entropy (ie 1-6 to 0-5) | |
187 | // This is done by changing all 6s to 0s | |
188 | if (base.str == "dice") { | |
189 | var newEvents = []; | |
190 | for (var i=0; i<base.events.length; i++) { | |
191 | var c = base.events[i]; | |
192 | if ("12345".indexOf(c) > -1) { | |
193 | newEvents[i] = base.events[i]; | |
194 | } | |
195 | else { | |
196 | newEvents[i] = "0"; | |
197 | } | |
198 | } | |
199 | base.str = "base 6 (dice)"; | |
200 | base.events = newEvents; | |
201 | base.matcher = matchers.base6; | |
202 | } | |
203 | // Detect empty entropy | |
204 | if (base.events.length == 0) { | |
205 | return { | |
206 | binaryStr: "", | |
207 | cleanStr: "", | |
208 | cleanHtml: "", | |
209 | base: base, | |
210 | }; | |
211 | } | |
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; | |
219 | // Supply a 'filtered' entropy string for display purposes | |
220 | var entropyClean = base.events.join(""); | |
221 | var entropyHtml = base.events.join(""); | |
222 | if (base.asInt == 52) { | |
223 | entropyClean = base.events.join(" ").toUpperCase(); | |
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"); | |
228 | entropyHtml = base.events.join(" ").toUpperCase(); | |
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>"); | |
233 | } | |
234 | // Return the result | |
235 | var e = { | |
236 | binaryStr: entropyBin, | |
237 | cleanStr: entropyClean, | |
238 | cleanHtml: entropyHtml, | |
239 | bitsPerEvent: bitsPerEvent, | |
240 | base: base, | |
241 | } | |
242 | return e; | |
243 | } | |
244 | ||
245 | function getBase(str, baseStr) { | |
246 | // Need to get the lowest base for the supplied entropy. | |
247 | // This prevents interpreting, say, dice rolls as hexadecimal. | |
248 | var binaryMatches = matchers.binary(str); | |
249 | var hexMatches = matchers.hex(str); | |
250 | var autodetect = baseStr === undefined; | |
251 | // Find the lowest base that can be used, whilst ignoring any irrelevant chars | |
252 | if ((binaryMatches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "binary") { | |
253 | var ints = binaryMatches.map(function(i) { return parseInt(i, 2) }); | |
254 | return { | |
255 | ints: ints, | |
256 | events: binaryMatches, | |
257 | matcher: matchers.binary, | |
258 | asInt: 2, | |
259 | bitsPerEvent: 1, | |
260 | str: "binary", | |
261 | } | |
262 | } | |
263 | var cardMatches = matchers.card(str); | |
264 | if ((cardMatches.length >= hexMatches.length / 2 && autodetect) || baseStr === "card") { | |
265 | return { | |
266 | ints: ints, | |
267 | events: cardMatches, | |
268 | matcher: matchers.card, | |
269 | asInt: 52, | |
270 | bitsPerEvent: (32*5 + 16*4 + 4*2) / 52, // see cardBits | |
271 | str: "card", | |
272 | } | |
273 | } | |
274 | var diceMatches = matchers.dice(str); | |
275 | if ((diceMatches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "dice") { | |
276 | var ints = diceMatches.map(function(i) { return parseInt(i) }); | |
277 | return { | |
278 | ints: ints, | |
279 | events: diceMatches, | |
280 | matcher: matchers.dice, | |
281 | asInt: 6, | |
282 | bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits | |
283 | str: "dice", | |
284 | } | |
285 | } | |
286 | var base6Matches = matchers.base6(str); | |
287 | if ((base6Matches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "base 6") { | |
288 | var ints = base6Matches.map(function(i) { return parseInt(i) }); | |
289 | return { | |
290 | ints: ints, | |
291 | events: base6Matches, | |
292 | matcher: matchers.base6, | |
293 | asInt: 6, | |
294 | bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits | |
295 | str: "base 6", | |
296 | } | |
297 | } | |
298 | var base10Matches = matchers.base10(str); | |
299 | if ((base10Matches.length == hexMatches.length && hexMatches.length > 0 && autodetect) || baseStr === "base 10") { | |
300 | var ints = base10Matches.map(function(i) { return parseInt(i) }); | |
301 | return { | |
302 | ints: ints, | |
303 | events: base10Matches, | |
304 | matcher: matchers.base10, | |
305 | asInt: 10, | |
306 | bitsPerEvent: (8*3 + 2*1) / 10, // see b10Bits | |
307 | str: "base 10", | |
308 | } | |
309 | } | |
310 | var ints = hexMatches.map(function(i) { return parseInt(i, 16) }); | |
311 | return { | |
312 | ints: ints, | |
313 | events: hexMatches, | |
314 | matcher: matchers.hex, | |
315 | asInt: 16, | |
316 | bitsPerEvent: 4, | |
317 | str: "hexadecimal", | |
318 | } | |
319 | } | |
320 | ||
321 | })(); |