-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathassets_js_bitrequest_bip39.js
2051 lines (1933 loc) · 76.6 KB
/
assets_js_bitrequest_bip39.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// bip39 (All addresses / xpubs in this app are test addresses derived from the following testphrase, taken from https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch05.asciidoc)
const bip39_const = {
"test_phrase": "army van defense carry jealous true garbage claim echo media make crunch", // random phrase used for test derive
"expected_seed": "5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39a88b76373733891bfaba16ed27a813ceed498804c0570", // expected seed used for test derive
"expected_address": "1HQ3rb7nyLPrjnuW85MUknPekwkn7poAUm", // expected addres used for test derive
"expected_bech32": "bc1qg0azlj4w2lrq8jssrrz6eprt2fe7f7edm4vpd5", // expected bech32 addres used for test derive
"expected_bch_cashaddr": "qp5p0eur784pk8wxy2kzlz3ctnq5whfnuqqpp78u22",
"expected_eth_address": "0x2161DedC3Be05B7Bb5aa16154BcbD254E9e9eb68",
"c_derive": {
"bitcoin": true,
"litecoin": true,
"dogecoin": true,
"dash": true,
"nano": true,
"monero": true,
"ethereum": true,
"bitcoin-cash": true,
"nimiq": false,
"kaspa": false
},
"can_xpub": {
"bitcoin": true,
"litecoin": true,
"dogecoin": true,
"dash": true,
"nano": false,
"monero": false,
"ethereum": true,
"bitcoin-cash": true,
"nimiq": false,
"kaspa": false
}
}
$(document).ready(function() {
// ** Core Initialization & Setup: **
test_bip39();
//disable_bip39_support
//mark_coins_non_derivable
//mark_coins_xpub_incompatible
//is_trial
//validate_trial_status
// ** BIP39 Test Functions: **
//test_derivation
//bech32_check
//cashaddr_check
//nano_check
//xmr_check
//xpub_check
//eth_xpub_check
// ** Derivation Support Functions: **
//check_derivations
//active_xpub
//has_xpub
//is_xpub
//cxpub
//get_bip32dat
//has_bip32
// ** Seed Generation & Management: **
make_seed();
restore_seed();
restore_seed_verify();
//get_seedid
//manage_bip32
submit_disclaimer();
//bip39
// ** Seed Panel Navigation: **
got_it();
seed_back1();
seed_back2();
//seed_nav
//ls_phrase_obj
//ls_phrase_obj_parsed
//seed_decrypt
// ** Phrase Verification & Backup: **
backup_continue();
//check_phrase
//get_mnemonic_phrase
//validate_mnemonic
//find_invalid_word
//verify_phrase
//shuffle_array
verify_words();
//update_address_lists
continue_seed();
skip_verify();
//complete_seed_setup
//seed_callback
//deactivate_xpubs
//encrypt_seed_data
//has_encrypted_data
//pin_to_encryption_key
// ** Key Derivation Core: **
//hmac_encrypt
//mnemonic_to_seed
//parse_seed
//generate_mnemonic
//to_mnemonic
// ** BIP32 Derivation: **
//objectify_extended
//derive_x
//derive_child_key
//keypair_array
//ext_keys
//xpub_obj
//b58c_x_payload
//format_keys
//xpub_prefix
// ** Address Generation: **
//derive_new_address
//get_latest_index
//key_cc
//key_cc_xpub
//get_rootkey
//derive_all_init
//derive_all
//derive_add_address
//derive_data
//derive_obj
//count_unique_elements
// ** UI & Information Display: **
copy_phrase();
toggle_phrase_visibility();
//reveal_mnemonic
delete_phrase_trigger();
//delete_phrase_verify
phrase_info();
//phrase_info_pu
//list_compatible_wallets
//get_wallet_icon_url
phrase_coin_info();
toggle_dpaths();
//display_coin_info
test_derive_next();
test_derive_prev();
//derive_address_batch
phrase_moreinfo();
phrase_showxp();
});
// ** Core Initialization & Setup: **
// Validates BIP39 implementation, crypto support, and address derivation for multiple cryptocurrencies
function test_bip39() {
if (!crypto) { // test for window.crypto
disable_bip39_support();
return
}
if (glob_const.has_bigint === false) { // test for js BigInt
disable_bip39_support();
return
}
const key_string = bip39_const.expected_seed.slice(0, 32),
encrypted_test = aes_enc(bip39_const.test_phrase, key_string),
decrypted_test = aes_dec(encrypted_test, key_string);
if (bip39_const.test_phrase !== decrypted_test) { // test encryption
disable_bip39_support();
return
}
if (mnemonic_to_seed(bip39_const.test_phrase) !== bip39_const.expected_seed || test_derivation() === false) {
disable_bip39_support();
const failed_coins = ["bitcoin", "litecoin", "dogecoin", "dash", "ethereum", "bitcoin-cash", "monero", "nano"];
mark_coins_non_derivable(failed_coins);
failed_coins.forEach(coin => {
bip39_const.c_derive[coin] = false;
});
}
const coin_checks = [{
"check": bech32_check,
"coin": "bitcoin"
},
{
"check": bech32_check,
"coin": "litecoin"
},
{
"check": cashaddr_check,
"coin": "bitcoin-cash"
},
{
"check": nano_check,
"coin": "nano"
},
{
"check": xmr_check,
"coin": "monero"
}
];
coin_checks.forEach(function({
check,
coin
}) {
if (check() === false) {
mark_coins_non_derivable([coin]);
bip39_const.c_derive[coin] = false;
}
});
// check xpub derivation
if (xpub_check() === false) { // test for btc xpub derivation
const xpub_failed = ["bitcoin", "litecoin", "dogecoin", "dash", "bitcoin-cash"];
mark_coins_xpub_incompatible(xpub_failed);
xpub_failed.forEach(coin => {
bip39_const.can_xpub[coin] = false;
});
}
if (eth_xpub_check() === false) { // test for ethereum xpub derivation
mark_coins_xpub_incompatible(["ethereum"]);
bip39_const.can_xpub.ethereum = false;
}
}
// Marks interface as BIP39 incompatible and disables derivation testing
function disable_bip39_support() {
glob_const.body.addClass("nobip");
glob_let.test_derive = false;
}
// Marks specified cryptocurrencies as non-derivable in UI with 500ms DOM ready delay
function mark_coins_non_derivable(arr) {
setTimeout(function() {
arr.forEach(function(coin) {
$("#" + coin + "_settings").addClass("no_derive");
});
}, 500)
}
// Marks specified cryptocurrencies as xpub-incompatible in UI with 500ms DOM ready delay
function mark_coins_xpub_incompatible(arr) {
setTimeout(function() {
arr.forEach(function(coin) {
$("#" + coin + "_settings").addClass("no_xpub");
});
}, 500)
}
// Validates trial status by checking if timestamp in local storage is within 12-hour window
function is_trial() {
const trial_timestamp = br_get_local("tp");
if (trial_timestamp) {
const trial_duration = 43200000;
if ((now() - parseFloat(trial_timestamp)) < trial_duration) {
return true;
}
}
return false;
}
// Reminder to write down secret phrase
// Enforces address usage limits based on trial status (2 max for trial, 0 for non-trial users)
function validate_trial_status() {
if (glob_let.hasbip) {
if (glob_let.bipv) {
return true;
}
const active_addresses = filter_all_addressli("seedid", glob_let.bipid).filter(".used"),
is_trial_active = is_trial();
if (is_trial_active) {
if (active_addresses.length > 1) {
manage_bip32({
"type": "popup"
});
}
if (active_addresses.length > 2) {
return false;
}
} else {
manage_bip32({
"type": "popup"
});
if (active_addresses.length > 0) {
return false;
}
}
}
return true;
}
// ** BIP39 Test Functions: **
// Validates Bitcoin address derivation using BIP44 path against expected test vector
function test_derivation() {
try {
const coin = "bitcoin",
root_key = get_rootkey(bip39_const.expected_seed),
bip32_config = get_bip32dat(coin),
derive_params = {
"dpath": "m/44'/0'/0'/0/0",
"key": root_key.slice(0, 64),
"cc": root_key.slice(64)
},
derived_keys = derive_x(derive_params),
derived_address = format_keys(bip39_const.expected_seed, derived_keys, bip32_config, 0, coin);
return derived_address.address === bip39_const.expected_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates Bech32 address format derivation against known test public key
function bech32_check() {
try {
const test_pubkey = "03bb4a626f63436a64d7cf1e441713cc964c0d53289a5b17acb1b9c262be57cb17",
derived_address = pub_to_address_bech32("bc", test_pubkey);
return bip39_const.expected_bech32 === derived_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates conversion from legacy to CashAddr format for Bitcoin Cash addresses
function cashaddr_check() {
try {
const legacy_address = "1AVPurYZinnctgGPiXziwU6PuyZKX5rYZU",
cash_address = pub_to_cashaddr(legacy_address);
return bip39_const.expected_bch_cashaddr === cash_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates Nano address derivation from seed using NanocurrencyWeb library
function nano_check() {
try {
const target_address = "nano_1mbtirc4x3kixfy5wufxaqakd3gbojpn6gpmk6kjiyngnjwgy6yty3txgztq",
derived_address = NanocurrencyWeb.wallet.accounts(bip39_const.expected_seed, 0, 0)[0].address;
return target_address === derived_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates Monero address derivation from spend key using Coinomi test vector
function xmr_check() { // https://coinomi.github.io/tools/bip39/
try {
const target_address = "477h3C6E6C4VLMR36bQL3yLcA8Aq3jts1AHLzm5QXipDdXVCYPnKEvUKykh2GTYqkkeQoTEhWpzvVQ4rMgLM1YpeD6qdHbS",
spend_key = get_ssk(bip39_const.expected_seed, true),
derived_keys = xmr_getpubs(spend_key, 0);
return derived_keys.address === target_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates Bitcoin xpub derivation against known addresses using both regular and Bech32 formats
function xpub_check() {
try {
const coin = "bitcoin",
xpub_data = key_cc_xpub("xpub6Cy7dUR4ZKF22HEuVq7epRgRsoXfL2MK1RE81CSvp1ZySySoYGXk5PUY9y9Cc5ExpnSwXyimQAsVhyyPDNDrfj4xjDsKZJNYgsHXoEPNCYQ"),
derive_params = {
"dpath": "M/0/0",
"key": xpub_data.key,
"cc": xpub_data.cc,
"vb": xpub_data.version
},
derived_keys = derive_x(derive_params),
bip32_config = get_bip32dat(coin),
derived_address = format_keys(null, derived_keys, bip32_config, 0, coin),
bech32_address = "bc1qk0wlvl4xh3eqe5szqyrlcj4ws8633vz0vhhywl"; // wildcard for bech32 Xpubs (Zpub)
return derived_address.address === bip39_const.expected_address || derived_address.address === bech32_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// Validates Ethereum xpub derivation by checking public key to address conversion
function eth_xpub_check() {
try {
const test_pubkey = "03c026c4b041059c84a187252682b6f80cbbe64eb81497111ab6914b050a8936fd",
derived_address = pub_to_eth_address(test_pubkey);
return bip39_const.expected_eth_address === derived_address;
} catch (e) {
console.error(e.name, e.message);
return false;
}
}
// ** Derivation Support Functions: **
// Returns derivation method ('xpub', 'seed', or false) based on currency's configuration
function check_derivations(coin) {
if (glob_let.test_derive && bip39_const.c_derive[coin]) {
const active_pubkey = active_xpub(coin);
if (cxpub(coin) && active_pubkey) {
return "xpub";
}
if (glob_let.hasbip) {
return "seed";
}
}
return false;
}
// Returns active xpub data if currency has selected xpub, false otherwise
function active_xpub(coin) {
const pubkey_data = has_xpub(coin)
return pubkey_data && pubkey_data.selected === true ? pubkey_data : false;
}
// Returns xpub configuration if currency has valid xpub, false otherwise
function has_xpub(coin) {
const pubkey_config = is_xpub(coin);
return pubkey_config && pubkey_config.key ? pubkey_config : false;
}
// Returns xpub data from currency settings if xpub is supported, false otherwise
function is_xpub(coin) {
if (cxpub(coin)) {
const xpub_settings = cs_node(coin, "Xpub", true);
return xpub_settings || false;
}
return false;
}
// Returns boolean indicating if currency supports xpub functionality
function cxpub(coin) {
return !!bip39_const.can_xpub[coin];
}
// Retrieves BIP32 configuration data from active xpub or coin settings
function get_bip32dat(coin) {
const xpub_settings = cs_node(coin, "Xpub", true);
if (xpub_settings && xpub_settings.active === true) {
return xpub_settings;
}
const coin_config = get_coin_definition(coin);
if (coin_config) {
const xpub_config = q_obj(coin_config, "settings.Xpub");
if (xpub_config && xpub_config.active) {
return xpub_config;
}
}
return false;
}
// Checks if currency has BIP32 support in its configuration
function has_bip32(coin) {
const coin_config = get_coin_definition(coin);
if (coin_config) {
const has_xpub = q_obj(coin_config, "settings.Xpub.active");
if (has_xpub) {
return true;
}
}
return false;
}
// ** Seed Generation & Management: **
// Handles UI interaction for generating new seed phrases
function make_seed() {
$(document).on("click", "#option_makeseed", function() {
const coin = $(this).attr("data-currency");
if (glob_let.hasbip) {
topnotify(tl("alreadyhavesecretphrase"));
return
}
canceldialog();
manage_bip32({
"type": coin,
"edit": true
});
})
}
// Handles UI interaction for restoring existing seed phrases
function restore_seed() {
$(document).on("click", "#rest_seed, .applist.pobox li.seedu .address .srcicon", function() {
if (is_viewonly() === true) {
show_view_only_error();
return false;
}
if (glob_let.hasbip) {
return false;
}
const confirm_restore = confirm(tl("resoresecretphrase") + "?");
if (confirm_restore) {
const seed_id = $(this).attr("data-seedid");
canceloptions();
canceldialog();
bip39({
"type": "restore",
"edit": true,
"seedid": seed_id
});
$("#seed_step2").addClass("restore");
seed_nav(2);
$("#bip39phrase").focus();
}
})
}
// Validates and processes seed phrase restoration attempts
function restore_seed_verify() {
$(document).on("click", "#restore_seed", function() {
if (glob_let.hasbip) {
return false;
}
glob_let.phrasearray = null,
glob_let.phraseverified = false;
const input_phrase = get_mnemonic_phrase(),
is_valid = check_phrase(input_phrase);
if (is_valid) {
const target_id = $(this).attr("data-seedid"),
phrase_words = input_phrase.split(" "),
current_id = get_seedid(phrase_words);
if (target_id === current_id) {
glob_let.phrasearray = phrase_words,
glob_let.phraseverified = true;
$("#seed_steps").addClass("checked");
complete_seed_setup();
return
}
shake($("#bip39phrase"));
topnotify(tl("wrongsecretphrase"));
return
}
topnotify(is_valid);
})
}
// Generates 8-character seed identifier from word array using HMAC-SHA256
function get_seedid(words) {
return hmacsha(btoa(JSON.stringify(words)), "sha256").slice(0, 8);
}
// Controls BIP32 wallet setup flow and disclaimer acceptance
function manage_bip32(dat) {
if (glob_let.hasbip) {
bip39(dat);
return
}
const dialog_data = get_default_object(dat, true),
dialog_elements = [{
"div": {
"class": "popform",
"content": [{
"div": {
"class": "inputwrap",
"content": "<p>" + tl("cannotbespend") + "</p>"
},
}]
}
},
{
"div": {
"id": "pk_confirm",
"class": "noselect",
"content": [{
"div": {
"id": "pk_confirmwrap",
"class": "cb_wrap",
"attr": {
"data-checked": "false"
},
"content": [{
"span": {
"class": "checkbox"
}
}]
},
"span": {
"content": tl("understandandok")
}
}]
}
},
{
"input": {
"class": "submit",
"attr": {
"type": "submit",
"value": "ok"
}
}
}
],
dialog_content = $(template_dialog({
"id": "disclaimer_dialog",
"icon": "icon-warning",
"title": tl("disclaimer"),
"elements": dialog_elements
})).data(dialog_data);
if ($("#option_makeseed").length) {
canceldialog();
setTimeout(function() {
popdialog(dialog_content, "triggersubmit");
}, 1000);
} else {
popdialog(dialog_content, "triggersubmit");
}
}
// Processes disclaimer dialog submission and triggers BIP39 setup if confirmed
function submit_disclaimer() {
$(document).on("click", "#disclaimer_dialog input.submit", function(e) {
e.preventDefault();
const dialog = $("#disclaimer_dialog"),
dialog_data = dialog.data(),
checkbox = dialog.find("#pk_confirmwrap"),
is_confirmed = checkbox.data("checked");
if (is_confirmed) {
canceldialog();
bip39(dialog_data);
} else {
popnotify("error", tl("consent"));
}
})
}
// Sets up BIP39 seed generation UI with steps for backup, verification, and restoration
function bip39(dat) {
glob_let.phraseverified = false;
const dialog_data = get_default_object(dat, true),
saved_phrase = ls_phrase_obj(),
can_edit = dialog_data && dialog_data.edit,
dialog_type = dialog_data.type || null,
is_restore = dialog_type === "restore" && can_edit === true,
ui_state = glob_let.hasbip === true ? (glob_let.bipv === true ? "bipsavedbu" : "bipsaved") : "nobip",
current_step = ui_state === "nobip" ? 1 : (ui_state === "bipsavedbu" ? 2 : 3),
phrase_class = ui_state === "nobip" ? " showphrase" : " hidephrase",
existing_seed = glob_let.hasbip ? (saved_phrase ? saved_phrase.pob.join(" ") : false) : false,
seed_phrase = is_restore ? "" : existing_seed || generate_mnemonic(12),
reminder_text = dialog_type === "restore" ? "<p>" + tl("overwritten") + "</p>" : "<p>" + tl("pleaseverify") + "</p>",
verify_header = dialog_type === "restore" ? tl("verifycurrent") : tl("verifybackup"),
save_prompt = is_restore ? tl("entersecretphrase") : tl("writedownsecretphrase"),
verify_button = is_restore ? "<div id='restore_seed' class='button' data-seedid='" + dialog_data.seedid + "'>" + tl("restorebttn") + "</div>" : "<div id='cfbu2' class='button'>" + tl("ivebackeditup") + "</div>",
markup = $("<div id='seed_steps' class='panel" + current_step + "' data-goal='" + dialog_type + "'>\
<div id='seed_step1' class='seed_step'>\
<div class='ss_header'>\
<div class='icon-cross ssnav'></div>\
</div>\
<div class='ss_content flex'>\
<div class='ss_content_box'>\
<h2 style='color:#eeac57'>" + tl("important") + "</h2>\
<p><strong>" + tl("abouttobecome") + "</strong><br/>" + tl("inthenextscreen") + " <strong style='color:#eeac57'>" + tl("makesure") + "</strong></p>\
<p><strong>" + tl("ifyouloseyourdevice") + "</strong></p>\
<p class='p_warning' style='text-transform:uppercase'><strong>" + tl("ifyouloseyourphrase") + "</strong></p>\
</div>\
</div>\
<div class='ss_footer'>\
<div id='cfbu1' class='button'>" + tl("understand") + "</div>\
</div>\
</div>\
<div id='seed_step2' class='seed_step'>\
<div class='ss_header'>\
<div class='icon-arrow-left2 ssnav'></div>\
<div class='icon-cross ssnav'></div>\
</div>\
<div class='ss_content flex'>\
<div id='phrase_cb' class='ss_content_box" + phrase_class + "'>\
<h2 id='showphrase'><span class='icon-eye-blocked eye'></span><span class='icon-eye eye'></span>" + tl("bip39_passphrase") + ":</h2>\
<p>" + save_prompt + "</p>\
<div id='phrasewrap'>\
<div id='bip39phrase' contenteditable='" + can_edit + "' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' lang='en' class='noselect'>" + seed_phrase + "</div>\
<div id='phrase_actions'>\
<div id='copyphrase' class='button'>" + tl("copy") + "</div>\
<div id='phrase_info' title='seed info'><span class='icon-info'></span></div>\
</div>\
<div id='phraseblur'></div>\
</div>\
</div>\
</div>\
<div class='ss_footer'>" + verify_button + "</div>\
</div>\
<div id='seed_step3' class='seed_step'>\
<div class='ss_header'>\
<div class='icon-arrow-left2 ssnav'></div>\
</div>\
<div class='ss_content flex'>\
<div class='ss_content_box'><h2>" + verify_header + "</h2><p id='reminder_seed_backup'>" + tl("congratulations") + "<br/></p>\
<p id='gpp'>" + tl("withgreatpower") + "<br/><strong>" + tl("remember") + "</strong></p>" + reminder_text + "<div id='seed_verify_box'>\
</div>\
<div id='cfbu3_w'>\
<div id='cfbu3' class='button'>" + tl("idothislater") + "</div>\
</div>\
</div>\
</div>\
<div class='ss_footer'>\
<div id='continue_seed' class='button'>Continue</div>\
</div>\
</div>\
</div>").data(dialog_data);
$("#seed_panel").html(markup).addClass(ui_state);
glob_const.body.addClass("seed_dialog");
if (current_step === 3) {
verify_phrase(seed_phrase.split(" "), 3);
}
prevent_screen_sleep();
}
// ** Seed Panel Navigation: **
// Advances seed generation process from step 1 to step 2
function got_it() {
$(document).on("click", "#cfbu1", function() {
seed_nav(2);
})
}
// Navigates back from step 2 to step 1 in seed generation
function seed_back1() {
$(document).on("click", "#seed_steps #seed_step2 .ss_header .icon-arrow-left2", function() {
seed_nav(1);
})
}
// Navigates back from step 3 to step 2 in seed generation
function seed_back2() {
$(document).on("click", "#seed_steps #seed_step3 .ss_header .icon-arrow-left2, #seed_steps #seed_step3 #toseed", function() {
seed_nav(2);
$("#seed_step3").removeClass("delete verify");
})
}
// Changes active panel in seed generation UI to specified step number
function seed_nav(step_num) {
$("#seed_steps").attr("class", "panel" + step_num);
}
// Retrieves phrase object from global state or returns false
function ls_phrase_obj() {
return glob_let.bipobj ? ls_phrase_obj_parsed(glob_let.bipobj) : false;
}
// Decodes and parses encrypted or plain phrase object from storage
function ls_phrase_obj_parsed(phrase_obj) {
const encrypted_data = phrase_obj.datenc;
let phrase_data = phrase_obj.dat;
if (encrypted_data) {
const decrypted = s_decode(encrypted_data),
decoded_data = decrypted.dat;
if (decoded_data) {
phrase_data = decoded_data;
}
}
if (phrase_data) {
const parsed_data = JSON.parse(atob(phrase_data));
return {
"pid": parsed_data.pid,
"pob": JSON.parse(atob(parsed_data.pob))
}
}
return false;
}
// Decrypts seed data using provided PIN and returns parsed object
function seed_decrypt(pin) {
if (glob_let.bipobj) {
const encrypted_data = glob_let.bipobj.datenc;
if (encrypted_data) {
const decrypted = s_decode(encrypted_data, pin),
decoded_data = decrypted.dat;
if (decoded_data) {
return JSON.parse(atob(decoded_data));
}
}
const plain_data = glob_let.bipobj.dat;
if (plain_data) {
return JSON.parse(atob(plain_data));
}
}
return false;
}
// ** Phrase Verification & Backup: **
// Validates phrase and triggers verification step in backup process
function backup_continue() {
$(document).on("click", "#cfbu2", function() {
glob_let.phrasearray = null,
glob_let.phraseverified = false;
const input_phrase = get_mnemonic_phrase(),
is_valid = check_phrase(input_phrase);
if (is_valid === true) {
const phrase_words = input_phrase.split(" ");
verify_phrase(phrase_words, 3);
glob_let.phrasearray = phrase_words;
$("#seed_steps").removeClass("checked");
$("#seed_step3").addClass("verify");
seed_nav(3);
return
}
topnotify(is_valid);
})
}
// Validates seed phrase format, length, and BIP39 compatibility
function check_phrase(input_phrase) {
const phrase_words = input_phrase.split(" "),
word_count = phrase_words.length;
if (word_count < 2) {
return tl("emptyphrase");
}
if (word_count === 12) {
if (validate_mnemonic(input_phrase) === false) {
const invalid_word = find_invalid_word(phrase_words);
if (invalid_word) {
return tl("notinwordlist", {
"missing_word": invalid_word
});
}
return tl("notbip39compatible");
}
return true;
}
return tl("mustbe12characters");
}
// Returns cleaned seed phrase text from DOM element
function get_mnemonic_phrase() {
return clean_string($("#bip39phrase").text());
}
// Validates BIP39 mnemonic using SHA256 hash comparison
function validate_mnemonic(mnemonic) {
const binary_str = mnemonic_to_binary_string(mnemonic);
if (binary_str === null) {
return false;
}
const str_len = binary_str.length,
data_bits = binary_str.substring(0, str_len / 33 * 32),
hash_bits = binary_str.substring(str_len - str_len / 33, str_len),
data_array = binary_string_to_word_array(data_bits),
hash_result = sjcl.hash.sha256.hash(data_array),
hash_hex = from_bits(hash_result),
hash_binary = pad_binary(hex_string_to_binary_string(hash_hex), 256),
calc_hash = hash_binary.substring(0, str_len / 33);
return hash_bits === calc_hash;
}
// Returns first word from input array not found in BIP39 wordlist
function find_invalid_word(word_list) {
let invalid_word;
$.each(word_list, function(i, word) {
if (wordlist.indexOf(word) === -1) {
invalid_word = word;
return
}
});
return invalid_word;
}
// Creates UI elements for verifying selected words from seed phrase
function verify_phrase(word_list, word_count) {
const word_indices = word_list.map((word, i) => ({
"word": word,
"index": i + 1
})),
randomized_words = shuffle_array(word_indices),
selected_words = randomized_words.slice(0, word_count),
verify_box = $("#seed_verify_box");
verify_box.html("");
$.each(selected_words, function(i, word_data) {
const word = word_data.word,
word_num = word_data.index,
focus_attr = (i === 0) ? " autofocus" : "",
input_element = "<div class='checkword_box uncheck'><input type='text' placeholder='" + tl("word") + " #" + word_num + "' data-word='" + word + "'" + focus_attr + " autocorrect='off' autocapitalize='none'/><span class='icon-checkmark'></span></div>";
verify_box.append(input_element);
});
}
// Implements Fisher-Yates shuffle algorithm for array randomization
function shuffle_array(array) {
for (let i = array.length - 1; i > 0; i--) {
const rand_index = Math.floor(Math.random() * (i + 1));
[array[i], array[rand_index]] = [array[rand_index], array[i]];
}
return array;
}
// Handles word verification input and triggers appropriate callbacks
function verify_words() {
$(document).on("input", "#seed_verify_box input", function(e) {
const input_field = $(this),
word_box = input_field.closest(".checkword_box"),
target_word = input_field.data("word"),
input_value = input_field.val();
if (input_value === target_word) {
input_field.blur();
word_box.removeClass("uncheck");
const remaining_words = $("#seed_verify_box").find(".uncheck"),
words_left = remaining_words.length;
if (words_left > 0) {
const next_input = remaining_words.first().find("input");
setTimeout(function() {
next_input.focus().val("");
}, 500);
return
}
const verification_step = $("#seed_step3");
if (verification_step.hasClass("delete")) {
const confirm_delete = confirm(tl("areyousuredfp"));
if (confirm_delete) {
br_remove_local("bpdat");
const init_data = br_get_local("init", true),
updated_init = get_default_object(init_data, true);
updated_init.bipv = "no";
delete updated_init.bipv;
br_set_local("init", updated_init, true);
glob_let.hasbip = false;
glob_let.bipv = false;
glob_let.bipid = false;
update_address_lists();
hide_seed_panel();
notify(tl("secretphrasedeleted"));
}
return
}
if (verification_step.hasClass("replace")) {
const confirm_restore = confirm(tl("restoresecretphrasefrombackup"));
if (confirm_restore) {
const backup_data = $("#seed_steps").data().dat;
restore_callback(backup_data, true);
}
return
}
glob_let.phraseverified = true;
$("#seed_steps").addClass("checked");
complete_seed_setup();
return
}
word_box.addClass("uncheck");
});
}
// Updates UI and address lists after seed phrase changes
function update_address_lists() {
$.each(glob_config.bitrequest_coin_data, function(i, coin_config) {
const coin = coin_config.currency,
bip32_settings = coin_config.settings.Xpub;
if (bip32_settings.active) {
const address_list = get_addresslist(coin);
address_list.children(".adli").each(function(i) {
const address_item = $(this);
if (address_item.hasClass("seed")) {
const seed_id = address_item.data("seedid");
if (seed_id === glob_let.bipid) {
address_item.removeClass("seedu").addClass("seedv").attr("data-checked", "true").data("checked", true);
} else {
address_item.removeClass("seedv").addClass("seedu").attr("data-checked", "false").data("checked", false);
}
}
});
save_addresses(coin, false);
check_currency(coin);
}
});
}
// Handles continue button click in seed setup flow
function continue_seed() {
$(document).on("click", "#continue_seed", function() {
complete_seed_setup();
})
}
// Shows warning dialog when skipping seed verification
function skip_verify() {
$(document).on("click", "#cfbu3", function() {
const warning_content = "<h2><span class='icon-warning' style='color:#B33A3A'></span>" + tl("continueatownrisk") + "</h2><p><strong>" + tl("ifyouloseyourdevice") + "</strong></p>";
popdialog(warning_content, "complete_seed_setup");
})
}
// Completes seed setup with PIN validation
function complete_seed_setup() {
canceldialog();
if (check_pin_enabled(true)) {
seed_callback();
return
}
const pin_callback = {
"func": seed_callback
},
pin_content = pinpanel("", pin_callback);
showoptions(pin_content, "pin");
}
// Processes final seed setup steps and initializes derivation
function seed_callback() {
if (!glob_let.hasbip) {
const phrase_obj = {},
seed_string = btoa(JSON.stringify(glob_let.phrasearray)),
phrase_id = hmacsha(seed_string, "sha256").slice(0, 8),
storage_data = {