102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/info.txt:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # #
3 | # Visual private key generator (c) 2019 by MrFreeDragon #
4 | # #
5 | # Donations: 1SoDn3auKHVwmQKRaBgkPk2hMmXzCMcPw #
6 | # #
7 | ###############################################################################
8 |
9 | This project represents the visual bitcoin private key generator.
10 |
11 | Video tutorials:
12 | Private Key generation with a physical coin: https://www.youtube.com/watch?v=WyBdYhwweaE
13 | Genration of funny private key (for educational purposes): https://www.youtube.com/watch?v=0Ug4YBEyRFQ
14 |
15 | [1] General bitcoin information.
16 | A private key in the context of Bitcoin is a secret number that allows bitcoins to be spent. In Bitcoin, a private key is a 256-bit number, which can be represented in several ways (different formats). The "size" of this number is 32 bytes, or 256 bit (256 0/1 characters), or 64 HEX characters in the range 0-9/A-F.
17 | Any 256-bit number from 0x1 to 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 (order of bitcoin) is a valid private key. The private key is transformed to the public address only in one way for each address type. This generator supports Legacy bitcoin addresses of both types - compressed and uncompressed. Recommendation is to use compressed key.
18 |
19 | [2] Visual presentation of the key.
20 | The square 16x16 (=256) is used for generation purposes, where each cell represents one bit. The idea is that the filled cell represents "1" bit in the key, and not filled cell represents "0" bit in the key. Such presentation allows creating visual keys which could be easily memorized by human, but hardly understood by machines. You can draw pictures, logos, figures, favorite symbols and so on. You can also make your own patterns and designs and use them as your key. The benefit from such presentation is that you can "store" this key in your memory, just remembering the way you made the drawings.
21 |
22 | [3] Coin mode.
23 | It is known that the most secure way to generate bitcoin key is to flip a coin 256 times, and write down each outcome as 1 or 0 depending on the coin side. Visual private key generator can assist you in doing it. Just start flipping the coin and filling the cells line by line from 1x1 to 16x16 and after 256 outcomes you will have a nonsense "picture" represented your unique private key. You can be sure that nobody in the world have ever generated the same key or would generate in the future. The probability of such collision is so small that it is really equal to 0 for all of us and many other future generations. See the video tutorial explaining how to generate a private key with the physical coin.
24 |
25 | [4] Random key / Inverse / Rotate - filling features of the tool
26 | (+)Random Key: Click this button for random key generation. The key is generated in random coin mode (the cells are filled randomly by 0 or 1). For more security you can change any cells to the opposite bit after random generation.
27 | (+)Inverse: Inversing all the bits to the opposite bit (changes 0 to 1, and 0 to 1). Visually the colour of every cell will be changed to the opposite one.
28 | (+)Rotate: Rotates the whole patter clockwise. All the bits are rotated clockwise.
29 |
30 | [5] Advanced options (check the tick for access).
31 | (+)Explorer: Select the transactions/balance explorer which will be used to explore your generated address (you should click on "BTC address" header near your generated address to explore the details). In most cases all your generated address will be new, without transaction history and balance. By default the explorer is BTC.com.
32 | (+)Block linkes: If you activate this option you will be ale to block the selected rows/columns of the square, and not use "these bits" in your key. The blocked line (row or column) means that all the bits in a line are preset to 0. The random option will not fill the blocked lines. You can generate the private keys with less bits in use.
33 | (+)Online check: This option uses API to connect to blockchain.info/q server and obtain the total volume of received transactions by the generated address. By activating this option you will see the total transactions volume near the address. Keep in mind, that this option requires the internet connection. THe URL of API server is stored in main JS script in var APIrequestURL. By default the Online check option is disabled.
34 |
35 | [6] Clear All.
36 | Clears all the content.
37 |
38 | [7] Own private key visualization.
39 | Tick the box near "Visualize my own HEX private key" and you will have the form to input your unique private key in HEX format. After input and clicking the "Visualize" button you will see your private key visualization in 16x16 square with corresponding bitcoin addresses and public key calculations below. The private key in HEX format consists from 64 hex symbols, so you can also use this field in order to generate your private key with the help of 16side dice (0-9,A-F) tossing it 64 times.
40 |
41 | [7] Address details in blockchain.
42 | You can easily observe the address history and address details in blockchain by clicking on the BTC address text near the generated bitcoin addresses. However particularly in all cases the generated bitcoin addresses will be new ones without any history and with zero balances. By default the BTC.com explorer is used, howver you can change it to another in advanced options (see advanced options section).
43 |
44 | [8] Make WIF & QR code.
45 | By clicking this button you will receive the addition section with all details you need to keep your private key. Click the button again to change from uncompressed to compressed format and vice versa. That section will include the private key in HEX format, the private key in WIF format, BTC address and QR codes for private key and address. Private Key WIF format is a wallet import format and understood by the most bitcoin wallets today. Print this form to store your generated private key and address with corresponding QR codes.
46 |
47 | [9] Security.
48 | The code is open source. All the private keys are generated on client side, in your browser. This site does not copy or store the information generated by you. Even so we recommend downloading this site (it is available in zip) on your computer, disconnect from the internet and generate the key offline.
49 |
50 | (c) 2019 Visual Private Key Generator by MrFreeDragon
51 |
52 | https://btckeygen.com/
53 |
--------------------------------------------------------------------------------
/js/QRcode.js:
--------------------------------------------------------------------------------
1 | //---------------------------------------------------------------------
2 | //
3 | // QR Code Generator for JavaScript
4 | //
5 | // Copyright (c) 2009 Kazuhiko Arase
6 | //
7 | // URL: http://www.d-project.com/
8 | //
9 | // Licensed under the MIT license:
10 | // http://www.opensource.org/licenses/mit-license.php
11 | //
12 | // The word 'QR Code' is registered trademark of
13 | // DENSO WAVE INCORPORATED
14 | // http://www.denso-wave.com/qrcode/faqpatent-e.html
15 | //
16 | //---------------------------------------------------------------------
17 |
18 | var qrcode = function() {
19 |
20 | //---------------------------------------------------------------------
21 | // qrcode
22 | //---------------------------------------------------------------------
23 |
24 | /**
25 | * qrcode
26 | * @param typeNumber 1 to 40
27 | * @param errorCorrectionLevel 'L','M','Q','H'
28 | */
29 | var qrcode = function(typeNumber, errorCorrectionLevel) {
30 |
31 | var PAD0 = 0xEC;
32 | var PAD1 = 0x11;
33 |
34 | var _typeNumber = typeNumber;
35 | var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
36 | var _modules = null;
37 | var _moduleCount = 0;
38 | var _dataCache = null;
39 | var _dataList = [];
40 |
41 | var _this = {};
42 |
43 | var makeImpl = function(test, maskPattern) {
44 |
45 | _moduleCount = _typeNumber * 4 + 17;
46 | _modules = function(moduleCount) {
47 | var modules = new Array(moduleCount);
48 | for (var row = 0; row < moduleCount; row += 1) {
49 | modules[row] = new Array(moduleCount);
50 | for (var col = 0; col < moduleCount; col += 1) {
51 | modules[row][col] = null;
52 | }
53 | }
54 | return modules;
55 | }(_moduleCount);
56 |
57 | setupPositionProbePattern(0, 0);
58 | setupPositionProbePattern(_moduleCount - 7, 0);
59 | setupPositionProbePattern(0, _moduleCount - 7);
60 | setupPositionAdjustPattern();
61 | setupTimingPattern();
62 | setupTypeInfo(test, maskPattern);
63 |
64 | if (_typeNumber >= 7) {
65 | setupTypeNumber(test);
66 | }
67 |
68 | if (_dataCache == null) {
69 | _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
70 | }
71 |
72 | mapData(_dataCache, maskPattern);
73 | };
74 |
75 | var setupPositionProbePattern = function(row, col) {
76 |
77 | for (var r = -1; r <= 7; r += 1) {
78 |
79 | if (row + r <= -1 || _moduleCount <= row + r) continue;
80 |
81 | for (var c = -1; c <= 7; c += 1) {
82 |
83 | if (col + c <= -1 || _moduleCount <= col + c) continue;
84 |
85 | if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
86 | || (0 <= c && c <= 6 && (r == 0 || r == 6) )
87 | || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
88 | _modules[row + r][col + c] = true;
89 | } else {
90 | _modules[row + r][col + c] = false;
91 | }
92 | }
93 | }
94 | };
95 |
96 | var getBestMaskPattern = function() {
97 |
98 | var minLostPoint = 0;
99 | var pattern = 0;
100 |
101 | for (var i = 0; i < 8; i += 1) {
102 |
103 | makeImpl(true, i);
104 |
105 | var lostPoint = QRUtil.getLostPoint(_this);
106 |
107 | if (i == 0 || minLostPoint > lostPoint) {
108 | minLostPoint = lostPoint;
109 | pattern = i;
110 | }
111 | }
112 |
113 | return pattern;
114 | };
115 |
116 | var setupTimingPattern = function() {
117 |
118 | for (var r = 8; r < _moduleCount - 8; r += 1) {
119 | if (_modules[r][6] != null) {
120 | continue;
121 | }
122 | _modules[r][6] = (r % 2 == 0);
123 | }
124 |
125 | for (var c = 8; c < _moduleCount - 8; c += 1) {
126 | if (_modules[6][c] != null) {
127 | continue;
128 | }
129 | _modules[6][c] = (c % 2 == 0);
130 | }
131 | };
132 |
133 | var setupPositionAdjustPattern = function() {
134 |
135 | var pos = QRUtil.getPatternPosition(_typeNumber);
136 |
137 | for (var i = 0; i < pos.length; i += 1) {
138 |
139 | for (var j = 0; j < pos.length; j += 1) {
140 |
141 | var row = pos[i];
142 | var col = pos[j];
143 |
144 | if (_modules[row][col] != null) {
145 | continue;
146 | }
147 |
148 | for (var r = -2; r <= 2; r += 1) {
149 |
150 | for (var c = -2; c <= 2; c += 1) {
151 |
152 | if (r == -2 || r == 2 || c == -2 || c == 2
153 | || (r == 0 && c == 0) ) {
154 | _modules[row + r][col + c] = true;
155 | } else {
156 | _modules[row + r][col + c] = false;
157 | }
158 | }
159 | }
160 | }
161 | }
162 | };
163 |
164 | var setupTypeNumber = function(test) {
165 |
166 | var bits = QRUtil.getBCHTypeNumber(_typeNumber);
167 |
168 | for (var i = 0; i < 18; i += 1) {
169 | var mod = (!test && ( (bits >> i) & 1) == 1);
170 | _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
171 | }
172 |
173 | for (var i = 0; i < 18; i += 1) {
174 | var mod = (!test && ( (bits >> i) & 1) == 1);
175 | _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
176 | }
177 | };
178 |
179 | var setupTypeInfo = function(test, maskPattern) {
180 |
181 | var data = (_errorCorrectionLevel << 3) | maskPattern;
182 | var bits = QRUtil.getBCHTypeInfo(data);
183 |
184 | // vertical
185 | for (var i = 0; i < 15; i += 1) {
186 |
187 | var mod = (!test && ( (bits >> i) & 1) == 1);
188 |
189 | if (i < 6) {
190 | _modules[i][8] = mod;
191 | } else if (i < 8) {
192 | _modules[i + 1][8] = mod;
193 | } else {
194 | _modules[_moduleCount - 15 + i][8] = mod;
195 | }
196 | }
197 |
198 | // horizontal
199 | for (var i = 0; i < 15; i += 1) {
200 |
201 | var mod = (!test && ( (bits >> i) & 1) == 1);
202 |
203 | if (i < 8) {
204 | _modules[8][_moduleCount - i - 1] = mod;
205 | } else if (i < 9) {
206 | _modules[8][15 - i - 1 + 1] = mod;
207 | } else {
208 | _modules[8][15 - i - 1] = mod;
209 | }
210 | }
211 |
212 | // fixed module
213 | _modules[_moduleCount - 8][8] = (!test);
214 | };
215 |
216 | var mapData = function(data, maskPattern) {
217 |
218 | var inc = -1;
219 | var row = _moduleCount - 1;
220 | var bitIndex = 7;
221 | var byteIndex = 0;
222 | var maskFunc = QRUtil.getMaskFunction(maskPattern);
223 |
224 | for (var col = _moduleCount - 1; col > 0; col -= 2) {
225 |
226 | if (col == 6) col -= 1;
227 |
228 | while (true) {
229 |
230 | for (var c = 0; c < 2; c += 1) {
231 |
232 | if (_modules[row][col - c] == null) {
233 |
234 | var dark = false;
235 |
236 | if (byteIndex < data.length) {
237 | dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
238 | }
239 |
240 | var mask = maskFunc(row, col - c);
241 |
242 | if (mask) {
243 | dark = !dark;
244 | }
245 |
246 | _modules[row][col - c] = dark;
247 | bitIndex -= 1;
248 |
249 | if (bitIndex == -1) {
250 | byteIndex += 1;
251 | bitIndex = 7;
252 | }
253 | }
254 | }
255 |
256 | row += inc;
257 |
258 | if (row < 0 || _moduleCount <= row) {
259 | row -= inc;
260 | inc = -inc;
261 | break;
262 | }
263 | }
264 | }
265 | };
266 |
267 | var createBytes = function(buffer, rsBlocks) {
268 |
269 | var offset = 0;
270 |
271 | var maxDcCount = 0;
272 | var maxEcCount = 0;
273 |
274 | var dcdata = new Array(rsBlocks.length);
275 | var ecdata = new Array(rsBlocks.length);
276 |
277 | for (var r = 0; r < rsBlocks.length; r += 1) {
278 |
279 | var dcCount = rsBlocks[r].dataCount;
280 | var ecCount = rsBlocks[r].totalCount - dcCount;
281 |
282 | maxDcCount = Math.max(maxDcCount, dcCount);
283 | maxEcCount = Math.max(maxEcCount, ecCount);
284 |
285 | dcdata[r] = new Array(dcCount);
286 |
287 | for (var i = 0; i < dcdata[r].length; i += 1) {
288 | dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
289 | }
290 | offset += dcCount;
291 |
292 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
293 | var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
294 |
295 | var modPoly = rawPoly.mod(rsPoly);
296 | ecdata[r] = new Array(rsPoly.getLength() - 1);
297 | for (var i = 0; i < ecdata[r].length; i += 1) {
298 | var modIndex = i + modPoly.getLength() - ecdata[r].length;
299 | ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
300 | }
301 | }
302 |
303 | var totalCodeCount = 0;
304 | for (var i = 0; i < rsBlocks.length; i += 1) {
305 | totalCodeCount += rsBlocks[i].totalCount;
306 | }
307 |
308 | var data = new Array(totalCodeCount);
309 | var index = 0;
310 |
311 | for (var i = 0; i < maxDcCount; i += 1) {
312 | for (var r = 0; r < rsBlocks.length; r += 1) {
313 | if (i < dcdata[r].length) {
314 | data[index] = dcdata[r][i];
315 | index += 1;
316 | }
317 | }
318 | }
319 |
320 | for (var i = 0; i < maxEcCount; i += 1) {
321 | for (var r = 0; r < rsBlocks.length; r += 1) {
322 | if (i < ecdata[r].length) {
323 | data[index] = ecdata[r][i];
324 | index += 1;
325 | }
326 | }
327 | }
328 |
329 | return data;
330 | };
331 |
332 | var createData = function(typeNumber, errorCorrectionLevel, dataList) {
333 |
334 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel);
335 |
336 | var buffer = qrBitBuffer();
337 |
338 | for (var i = 0; i < dataList.length; i += 1) {
339 | var data = dataList[i];
340 | buffer.put(data.getMode(), 4);
341 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
342 | data.write(buffer);
343 | }
344 |
345 | // calc num max data.
346 | var totalDataCount = 0;
347 | for (var i = 0; i < rsBlocks.length; i += 1) {
348 | totalDataCount += rsBlocks[i].dataCount;
349 | }
350 |
351 | if (buffer.getLengthInBits() > totalDataCount * 8) {
352 | throw 'code length overflow. ('
353 | + buffer.getLengthInBits()
354 | + '>'
355 | + totalDataCount * 8
356 | + ')';
357 | }
358 |
359 | // end code
360 | if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
361 | buffer.put(0, 4);
362 | }
363 |
364 | // padding
365 | while (buffer.getLengthInBits() % 8 != 0) {
366 | buffer.putBit(false);
367 | }
368 |
369 | // padding
370 | while (true) {
371 |
372 | if (buffer.getLengthInBits() >= totalDataCount * 8) {
373 | break;
374 | }
375 | buffer.put(PAD0, 8);
376 |
377 | if (buffer.getLengthInBits() >= totalDataCount * 8) {
378 | break;
379 | }
380 | buffer.put(PAD1, 8);
381 | }
382 |
383 | return createBytes(buffer, rsBlocks);
384 | };
385 |
386 | _this.addData = function(data, mode) {
387 |
388 | mode = mode || 'Byte';
389 |
390 | var newData = null;
391 |
392 | switch(mode) {
393 | case 'Numeric' :
394 | newData = qrNumber(data);
395 | break;
396 | case 'Alphanumeric' :
397 | newData = qrAlphaNum(data);
398 | break;
399 | case 'Byte' :
400 | newData = qr8BitByte(data);
401 | break;
402 | case 'Kanji' :
403 | newData = qrKanji(data);
404 | break;
405 | default :
406 | throw 'mode:' + mode;
407 | }
408 |
409 | _dataList.push(newData);
410 | _dataCache = null;
411 | };
412 |
413 | _this.isDark = function(row, col) {
414 | if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
415 | throw row + ',' + col;
416 | }
417 | return _modules[row][col];
418 | };
419 |
420 | _this.getModuleCount = function() {
421 | return _moduleCount;
422 | };
423 |
424 | _this.make = function() {
425 | if (_typeNumber < 1) {
426 | var typeNumber = 1;
427 |
428 | for (; typeNumber < 40; typeNumber++) {
429 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel);
430 | var buffer = qrBitBuffer();
431 |
432 | for (var i = 0; i < _dataList.length; i++) {
433 | var data = _dataList[i];
434 | buffer.put(data.getMode(), 4);
435 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
436 | data.write(buffer);
437 | }
438 |
439 | var totalDataCount = 0;
440 | for (var i = 0; i < rsBlocks.length; i++) {
441 | totalDataCount += rsBlocks[i].dataCount;
442 | }
443 |
444 | if (buffer.getLengthInBits() <= totalDataCount * 8) {
445 | break;
446 | }
447 | }
448 |
449 | _typeNumber = typeNumber;
450 | }
451 |
452 | makeImpl(false, getBestMaskPattern() );
453 | };
454 |
455 | _this.createTableTag = function(cellSize, margin) {
456 |
457 | cellSize = cellSize || 2;
458 | margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
459 |
460 | var qrHtml = '';
461 |
462 | qrHtml += '
';
467 | qrHtml += '';
468 |
469 | for (var r = 0; r < _this.getModuleCount(); r += 1) {
470 |
471 | qrHtml += '
';
472 |
473 | for (var c = 0; c < _this.getModuleCount(); c += 1) {
474 | qrHtml += '