-
Notifications
You must be signed in to change notification settings - Fork 0
/
vk.js
1949 lines (1760 loc) · 70.7 KB
/
vk.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
//alert ('vk.js load');
var secureKeysArray = {};
var a = 0;
var innerKey = '234234jx';
var startString = 'START:';
var readyString = 'READY:';
var roundString = 'ROUND:';
var tagsToReplace = {
'&&': '&',
'&': '&',
'<': '<',
'>': '>'
};
function replaceTag(tag) {
return tagsToReplace[tag] || tag;
}
function escapeHTML(str) {
return str.replace(/[&<>]/g, replaceTag);
}
function getChatId() {
var chats = document.getElementById('im_tabs').childNodes;
for (i = 0; i < chats.length; i++) {
if (chats[i].className == "im_tab_selected") {
var chatId = chats[i].id;
chatId = chatId.replace('im_tab', '');
//alert ('selected chat id: ' + chatId); //подсказка для определения ID чата
}
}
return chatId;
}
function getMyId() {
var id = null;
id = document.getElementById('l_ph').innerHTML.replace('<a href="/albums','').replace('" onclick', ' ').split(' ')[0];
// alert(id);
return id;
}
function getChatUsersQuantity() {
var string = document.getElementById("im_rcpt").innerHTML;
if (string == ''){
number = 2;
} else {
string = string.replace('<a href="" onclick="IM.showChatMembers(); return false;">', '').replace('</a>', '').split(' ');
var number;
if (string[0] == 'to')
number = string[1];
else
number = string[0];
}
return parseInt(number) + 1;
}
function sendMsg(msg, chatId) {
document.getElementById('im_editable' + chatId).innerHTML = msg;
location.href = "javascript:IM.send(); void 0";
}
function searchMsgsStartsWith(startMessage, chatId) {
var logNode = document.getElementById("im_log" + chatId);
var messageTexts = logNode.getElementsByClassName("im_msg_text");
if (messageTexts.length > 0) {
for (var i = 0; i < messageTexts.length; ++i) {
var item = messageTexts[i].innerHTML;
if (item.startsWith(startMessage)) {
return item;
}
}
return null;
}
return -1;
}
// func sending aes message from secure form to main vk form
function sendAES(chatId) {
//alert (1);
var nonCrypted = document.getElementById('secureForm' + chatId).value;
var crypted = CryptoJS.AES.encrypt(nonCrypted, secureKeysArray[chatId]);
//alert( nonCrypted + ' - aes - ' + crypted); //test of crypting
document.getElementById('im_editable' + chatId).innerHTML = 'AESSTART' + crypted;
//nonCrypted.value=''; //clear secureform
}
function chatByIdDecrypt(chatId) {
var logNode = document.getElementById("im_log" + chatId);
var messageTexts = logNode.getElementsByClassName("im_msg_text");
for (var i = 0; i < messageTexts.length; ++i) {
var item = messageTexts[i];
//decrypt HTML! if message contain 'aesstart' and secret key is entered
if (item.innerHTML.indexOf('AESSTART') != -1 && secureKeysArray[chatId] != null) {
item.innerHTML = item.innerHTML.replace('AESSTART ', '');
item.innerHTML = item.innerHTML.replace('AESSTART', '');
var decrypted = CryptoJS.AES.decrypt(item.innerHTML, secureKeysArray[chatId]);
decryptedHTML = decrypted.toString(CryptoJS.enc.Utf8);
//escaping HTML string
decryptedHTML = escapeHTML(decryptedHTML);
var designKey = "<img align=\"right\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8\/9hAAAA5ElEQVR42s3TPWvCUBTG8cTJSXwp\r\nfgURKoqfQBy7ugquFYpFUAd3cRBBJ3XxI7j5QvfuIpS2oIMijpbSQXSQ\/g9EOGQwBpcc+A259\/Ak\r\n9x5iGneW6ckAH85uA2JoIosA9pihhp1TQAQfiOIda8SRxidSOF0LKKGLCtpqr4NXNKwX2EuO+SYB\r\nPTwjjB\/VkMTc4QqGOiCIX7WZwAJLbG3rcuwvlPURJGSgGuXT68hhpNbHeEIRfQl4sC4rhIm6RJnI\r\nCo84qoACXpDH92WM0tRCBn78YYoqNk5jtD9LwMG4sbz5L7iqf0eHLAe+b17mAAAAAElFTkSuQmCC\">";
var postHTML = '<br><font style="font-size:9px;">Encoded and Decoded by VkCrypt</font>';
if (decryptedHTML == null || decryptedHTML == '') decryptedHTML = '<b>ERROR</b> (this message can be encoded by another Secret Key)';
item.innerHTML = designKey + decryptedHTML + '';
//var bgImage = "R0lGODlhRwBHAIAAANTt3v\/\/\/yH5BAAAAAAALAAAAABHAEcAAAK8hI95we0PY1S0WiWzzrd7tIXh\r\nR1biqZUqhrbNCrsyA9dzW6u3m6\/72Sv9gEHScFT0HJHJzjLVvDyhUctUUjVdsVnt1tFlfR9hyhhc\r\nXpzRafW67X7DDWvavG6\/1+cAfIDf59fm98eHp3douAdHCJjI+DgYmSaoKAe5iJgZ1mh5pnmJ+Ska\r\nKllaVkk65jkK+sW6ChuLmqq65Tpre+W4adqqO8V7SjvM2Ut5TPxZ6Jvb\/Cq7Gx08\/ST8+3xbUwAA\r\nOw==";
item.style.backgroundColor = "#DEFFDE";
//item.style.backgroundImage='url(data:image\/gif;base64,' + bgImage + ')';
item.style.padding = "3px";
}
}
}
function getKey() {
// var a = randBigInt(256, 0); // rand 256 bit secret
if (document.getElementById('im_tabs')) {
userId = getMyId();
chatId = getChatId();
if (secureKeysArray[chatId]) {
return secureKeysArray[chatId];
}
if (chatId != null) {
// if key != null decrypt();
var chatUsersQuantity = getChatUsersQuantity();
var chatUsersIDs = [];
var startMsg = searchMsgsStartsWith(startString, chatId);
if (startMsg == null) {
// sendMsg(startString + '' + bigInt2str(p, 10) + ',' + bigInt2str(g, 10), chatId);
} else if (startMsg != -1) {
// startMsg is found
var pg = startMsg.replace(startString, '').split(',');
p = str2bigInt(pg[0], 10, 80);
g = str2bigInt(pg[1], 10, 80);
var myReadyMsg = searchMsgsStartsWith(readyString + userId, chatId);
if (myReadyMsg == null) {
sendMsg(readyString + userId, chatId);
} else if (myReadyMsg != -1) {
// myReadyMsg is found
var j = 0;
var logNode = document.getElementById("im_log" + chatId);
var messageTexts = logNode.getElementsByClassName("im_msg_text");
if (messageTexts.length > 0) {
for (var i = 0; i < messageTexts.length; ++i) {
var item = messageTexts[i].innerHTML;
if (item.startsWith(readyString)) {
chatUsersIDs[j] = item.replace(readyString, '');
j++;
}
}
if (chatUsersIDs.length == chatUsersQuantity) {
chatUsersIDs = chatUsersIDs.sort();
var nextID = chatUsersIDs.indexOf(userId) + 1;
if (nextID == (chatUsersQuantity))
// last in array
nextID = 0;
console.info("start Diffie-Hellman");
var prev = g;
for (var round = 1; round < chatUsersQuantity; ++round) {
var myRoundMsg = searchMsgsStartsWith(roundString + round.toString() + ',' + chatUsersIDs[nextID].toString() + ',', chatId);
if (myRoundMsg == null) {;
sendMsg(roundString + round.toString() + ',' + chatUsersIDs[nextID].toString() + ',' + bigInt2str(powMod(prev, str2bigInt(a, 10, 80), p), 10), chatId);
break;
}
else if (myRoundMsg != -1) {
var otherRoundMsg = searchMsgsStartsWith(roundString + round.toString() + ',' + userId.toString() + ',', chatId);
if (otherRoundMsg == null) {
// should wait for msg for u
break;
}
else {
if (otherRoundMsg != -1) {
var number = otherRoundMsg.replace(roundString + round.toString() + ',' + userId.toString() + ',', '');
prev = str2bigInt(number, 10, 80);
if (round == (chatUsersQuantity-1)) {
// Successed !!!!!!!!
var secret = bigInt2str(powMod(prev, str2bigInt(a, 10, 80), p),
10);
// console.info("secr = " + secret);
// sendMsg("a = " + a.toString() + "\nkey = " + secret, chatId);
/*chrome.storage.sync.set({
secretKeysArray: secureKeysArray,
secretA: a,
},null);*/
//alert('Secret: ' + secret);
// to DEBUG:
// console.clear();
return secret;
}
}
}
}
}
}
}
}
}
}
}
return null;
}
function vkPageDeCrypt() {
//если открыт чат
if (document.getElementById('im_tabs')) {
var chats = document.getElementById('im_tabs').childNodes;
for (i = 0; i < chats.length; i++) {
if (chats[i].className == "im_tab_selected") {
var chatId = chats[i].id;
chatId = chatId.replace('im_tab', '');
//alert ('selected chat id: ' + chatId); //подсказка для определения ID чата
}
}
if (!(chatId in secureKeysArray)) {
var rettt;
rettt = getKey();
if (rettt != null)
secureKeysArray[chatId] = rettt;
}
// alert(secureKeysArray);
if (chatId in secureKeysArray) {
// alert(secureKeysArray);
// alert("GOGOGO")
}
if (document.getElementById('secureForms')) {} else {
var secureFormsElem = document.createElement("div");
secureFormsElem.setAttribute("id", "secureForms");
secureFormsElem.setAttribute("style", "display: none;");
document.getElementById("im_texts").appendChild(secureFormsElem);
//document.getElementById('im_texts').innerHTML=document.getElementById('im_texts').innerHTML+'<div id="secureForms" style="display: none;"></div>';
}
var secureKeyHtmlButton = '<div id="secureButtonOn"><a href="#" onClick=" return false;">START ENCRYPTED CHAT</a></div>';
//alert(1);
//алерт для определения секретного ключа
//if (secureKeysArray[chatId]!=null) {alert('secret key for'+ chatId + ' is ' + secureKeysArray[chatId]);}
//если в окне не отрисована кнопка и окно не секретно
if (document.getElementById('im_peer_holders').innerHTML.indexOf('START') == -1 && secureKeysArray[chatId] == null) {
document.getElementById('im_peer_holders').innerHTML = document.getElementById('im_peer_holders').innerHTML + secureKeyHtmlButton;
document.getElementById('im_texts').style.opacity = '1';
if (document.getElementById('secureButtonOn')) {
var button = document.getElementById("secureButtonOn");
button.person_name = "Roberto";
button.addEventListener("click", function() {
var p = str2bigInt(
"108838049659940303356103757286832107246140775791152305372594007835539736018383", 10, 80);
var g = str2bigInt("321378932137218", 10, 80); // and on a generator
sendMsg(startString + '' + bigInt2str(p, 10) + ',' + bigInt2str(g, 10), chatId);
//secureKeysArray[chatId] = prompt('ENTER SECRET KEY FOR ID ' + chatId + 'HERE', '');
//alert(innerKey + ".");
}, false);
}
}
var secureForms = document.getElementById('secureForms').childNodes;
//если чат секретный
if (secureKeysArray[chatId] != null) {
//decode log by ID
chatByIdDecrypt(chatId);
document.getElementById('im_texts').style.position = 'relative';
document.getElementById('secureForms').style.display = 'block';
//создаём секретную форму, если её нет
if (document.getElementById('secureForm' + chatId)) {} else document.getElementById('secureForms').innerHTML = document.getElementById('secureForms').innerHTML + '<textarea id="secureForm' + chatId + '" style="height: 40px; width: 348px; padding: 3px 5px 5px 3px; display: block; font: normal normal 400 11px/16px Tahoma; border: 1px solid #C0CAD5; z-index: 140; position: absolute; top:0; resize: none; " ></textarea>'
for (i = 0; i < secureForms.length; i++) {
if (secureForms[i].id != 'secureForm' + chatId) {
secureForms[i].style.display = 'none';
document.getElementById('im_send').onclick = function() {
IM.send();
};
} else {
secureForms[i].style.display = 'block';
document.getElementById('im_send').onclick = function() {
sendAES(chatId);
location.href = "javascript:IM.send(); void 0";
document.getElementById('secureForm' + chatId).value = '';
};
}
}
//если была отрисована кнопка введите ключ - удалить её
if (document.getElementById('secureButtonOn')) {
element = document.getElementById("secureButtonOn");
element.parentNode.removeChild(element);
}
//если кнопка введите ключ не была отрисована
if (document.getElementById('secureOn')) {} else {
var hiddenKey = '';
for (i = 0; i < secureKeysArray[chatId].length; i++) {
hiddenKey = hiddenKey + '*';
if (i == 9) hiddenKey = hiddenKey + '...';
if (i == 9) break;
}
document.getElementById('im_peer_holders').innerHTML = document.getElementById('im_peer_holders').innerHTML + '<div id="secureOn">SECURE KEY ON (********)</div>';
}
} else {
for (i = 0; i < secureForms.length; i++) {
secureForms[i].style.display = 'none';
}
}
}
//запускаем функцию снова
setTimeout(function() {
vkPageDeCrypt();
}, 500);
}
/*function handle
if (item.innerHTML.indexOf('EncryptedMsg')) {
item.innerHTML = item.innerHTML.replace('EncryptedMsg ', '');
item.innerHTML = item.innerHTML.replace('EncryptedMsg', '');
decryptItem(item, key);
}*/
/*
var logNode = document.getElementById("im_log" + chatId);
var messageTexts = logNode.getElementsByClassName("im_msg_text");
for (var i = 0; i < messageTexts.length; ++i) {
var item = messageTexts[i].innerHTML;
if (item.indexOf(startMessage)) {
encriptionStarted = true;
continue;
}
if (item.indexOf(readyMessage) && encriptionStarted && (chatUsersQuantity != 0)) {
chatUsersQuantity -= 1;
chatUsersIDs[chatUsersQuantity] = parseInt(item.replace(readyMessage, ''));
//alert(parseInt(item.replace(readyMessage, '')));
continue;
}
}
if (encriptionStarted == false) {
//alert('need tosend msg');
sendMsg(startMessage, chatId);
}
else
alert(chatUsersIDs); /*if (getChatUsersQuantity != 0) {
//sendMsg(//Найти себя еще надо!)
}*/
//decrypt HTML! if message contain 'EncryptedMsg' and secret key is entered
/*if (item.innerHTML.indexOf('EncryptedMsg') != -1 && secureKeysArray[chatId] != null) {
item.innerHTML = item.innerHTML.replace('EncryptedMsg ', '');
item.innerHTML = item.innerHTML.replace('EncryptedMsg', '');
//alert(item.innerHTML);
//alert(secureKeysArray[chatId]);
var decrypted = CryptoJS.AES.decrypt(item.innerHTML, secureKeysArray[chatId]);
decryptedHTML = decrypted.toString(CryptoJS.enc.Utf8);
//escaping HTML string
decryptedHTML = escapeHTML(decryptedHTML);
var designKey = "<img align=\"right\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8\/9hAAAA5ElEQVR42s3TPWvCUBTG8cTJSXwp\r\nfgURKoqfQBy7ugquFYpFUAd3cRBBJ3XxI7j5QvfuIpS2oIMijpbSQXSQ\/g9EOGQwBpcc+A259\/Ak\r\n9x5iGneW6ckAH85uA2JoIosA9pihhp1TQAQfiOIda8SRxidSOF0LKKGLCtpqr4NXNKwX2EuO+SYB\r\nPTwjjB\/VkMTc4QqGOiCIX7WZwAJLbG3rcuwvlPURJGSgGuXT68hhpNbHeEIRfQl4sC4rhIm6RJnI\r\nCo84qoACXpDH92WM0tRCBn78YYoqNk5jtD9LwMG4sbz5L7iqf0eHLAe+b17mAAAAAElFTkSuQmCC\">";
var postHTML = '<br><font style="font-size:9px;">Encoded and Decoded by VkCrypt</font>';
if (decryptedHTML == null || decryptedHTML == '') decryptedHTML = '<b>ERROR</b> (this message can be encoded by another Secret Key)';
item.innerHTML = designKey + decryptedHTML + '';
//var bgImage = "R0lGODlhRwBHAIAAANTt3v\/\/\/yH5BAAAAAAALAAAAABHAEcAAAK8hI95we0PY1S0WiWzzrd7tIXh\r\nR1biqZUqhrbNCrsyA9dzW6u3m6\/72Sv9gEHScFT0HJHJzjLVvDyhUctUUjVdsVnt1tFlfR9hyhhc\r\nXpzRafW67X7DDWvavG6\/1+cAfIDf59fm98eHp3douAdHCJjI+DgYmSaoKAe5iJgZ1mh5pnmJ+Ska\r\nKllaVkk65jkK+sW6ChuLmqq65Tpre+W4adqqO8V7SjvM2Ut5TPxZ6Jvb\/Cq7Gx08\/ST8+3xbUwAA\r\nOw==";
item.style.backgroundColor = "#DEFFDE";
//item.style.backgroundImage='url(data:image\/gif;base64,' + bgImage + ')';
item.style.padding = "3px";
}*/
/*function d_h()
{
// Alice tries to send to Bob, they agree on a 256 bit prime
var p = str2bigInt(
"108838049659940303356103757286832107246140775791152305372594007835539736018383", 10, 80);
var g = str2bigInt("2", 10, 80); // and on a generator
// Sergey chooses his secret randomly
var a = str2bigInt(
"7303224671992696184944691515791539344527572836100488142539735181889849005491", 10, 80);
// Frankie chooses his secret randomly
var b = str2bigInt(
"71584138129522073809795038784924972838434076792186090164037800870375597114780", 10, 80);
// Dimon chooses his secret randomly
var c = str2bigInt(
"66131678177653645399177244506390485585040936033069481724579538668722385072328", 10, 80);
// calculate the public key
var A = powMod(g,a,p); var B = powMod(g,b,p); var C = powMod(g,c,p);
var AB = powMod(A,b,p); var BC = powMod(B,c,p); var AC = powMod(C,a,p);
var BA = powMod(B,a,p);
// A and B are exchanged publicly
// Alice and Bob can compute the same key from A and B
var a_sec = powMod(BC, a, p);
var b_sec = powMod(AC, b, p);
var c_sec = powMod(AB, c, p);
console.log('a = ' + bigInt2str(a, 10));
console.log('b = ' + bigInt2str(b, 10));
console.log('c = ' + bigInt2str(c, 10));
console.log('A = ' + bigInt2str(A, 10));
console.log('B = ' + bigInt2str(B, 10));
console.log('C = ' + bigInt2str(C, 10));
console.log('AB = ' + bigInt2str(AB, 10));
console.log('BC = ' + bigInt2str(BC, 10));
console.log('AC = ' + bigInt2str(AC, 10));
console.log('Secrets:')
console.log(bigInt2str(a_sec, 10) + '\n' + bigInt2str(b_sec, 10), bigInt2str(c_sec, 10));
}*/
function main()
{
// d_h();
chrome.storage.sync.get({
secretKeysArray: {},
secretA: 0
}, function(items) {
secureKeysArray = items.secretKeysArray;
a = items.secretA;
if (a == 0) {
a = getRandA(); // rand 256 bit secret
chrome.storage.sync.set({
secretKeysArray: secureKeysArray,
secretA: a,
}, null);
}
vkPageDeCrypt();
});
}
////////////////////////////////////////////////////////////////////////////////////////
// Big Integer Library v. 5.5
// Created 2000, last modified 2013
// Leemon Baird
// www.leemon.com
//
// Version history:
// v 5.5 17 Mar 2013
// - two lines of a form like "if (x<0) x+=n" had the "if" changed to "while" to
// handle the case when x<-n. (Thanks to James Ansell for finding that bug)
// v 5.4 3 Oct 2009
// - added "var i" to greaterShift() so i is not global. (Thanks to PŽter Szab— for finding that bug)
//
// v 5.3 21 Sep 2009
// - added randProbPrime(k) for probable primes
// - unrolled loop in mont_ (slightly faster)
// - millerRabin now takes a bigInt parameter rather than an int
//
// v 5.2 15 Sep 2009
// - fixed capitalization in call to int2bigInt in randBigInt
// (thanks to Emili Evripidou, Reinhold Behringer, and Samuel Macaleese for finding that bug)
//
// v 5.1 8 Oct 2007
// - renamed inverseModInt_ to inverseModInt since it doesn't change its parameters
// - added functions GCD and randBigInt, which call GCD_ and randBigInt_
// - fixed a bug found by Rob Visser (see comment with his name below)
// - improved comments
//
// This file is public domain. You can use it for any purpose without restriction.
// I do not guarantee that it is correct, so use it at your own risk. If you use
// it for something interesting, I'd appreciate hearing about it. If you find
// any bugs or make any improvements, I'd appreciate hearing about those too.
// It would also be nice if my name and URL were left in the comments. But none
// of that is required.
//
// This code defines a bigInt library for arbitrary-precision integers.
// A bigInt is an array of integers storing the value in chunks of bpe bits,
// little endian (buff[0] is the least significant word).
// Negative bigInts are stored two's complement. Almost all the functions treat
// bigInts as nonnegative. The few that view them as two's complement say so
// in their comments. Some functions assume their parameters have at least one
// leading zero element. Functions with an underscore at the end of the name put
// their answer into one of the arrays passed in, and have unpredictable behavior
// in case of overflow, so the caller must make sure the arrays are big enough to
// hold the answer. But the average user should never have to call any of the
// underscored functions. Each important underscored function has a wrapper function
// of the same name without the underscore that takes care of the details for you.
// For each underscored function where a parameter is modified, that same variable
// must not be used as another argument too. So, you cannot square x by doing
// multMod_(x,x,n). You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n).
// Or simply use the multMod(x,x,n) function without the underscore, where
// such issues never arise, because non-underscored functions never change
// their parameters; they always allocate new memory for the answer that is returned.
//
// These functions are designed to avoid frequent dynamic memory allocation in the inner loop.
// For most functions, if it needs a BigInt as a local variable it will actually use
// a global, and will only allocate to it only when it's not the right size. This ensures
// that when a function is called repeatedly with same-sized parameters, it only allocates
// memory on the first call.
//
// Note that for cryptographic purposes, the calls to Math.random() must
// be replaced with calls to a better pseudorandom number generator.
//
// In the following, "bigInt" means a bigInt with at least one leading zero element,
// and "integer" means a nonnegative integer less than radix. In some cases, integer
// can be negative. Negative bigInts are 2s complement.
//
// The following functions do not modify their inputs.
// Those returning a bigInt, string, or Array will dynamically allocate memory for that value.
// Those returning a boolean will return the integer 0 (false) or 1 (true).
// Those returning boolean or int will not allocate memory except possibly on the first
// time they're called with a given parameter size.
//
// bigInt add(x,y) //return (x+y) for bigInts x and y.
// bigInt addInt(x,n) //return (x+n) where x is a bigInt and n is an integer.
// string bigInt2str(x,base) //return a string form of bigInt x in a given base, with 2 <= base <= 95
// int bitSize(x) //return how many bits long the bigInt x is, not counting leading zeros
// bigInt dup(x) //return a copy of bigInt x
// boolean equals(x,y) //is the bigInt x equal to the bigint y?
// boolean equalsInt(x,y) //is bigint x equal to integer y?
// bigInt expand(x,n) //return a copy of x with at least n elements, adding leading zeros if needed
// Array findPrimes(n) //return array of all primes less than integer n
// bigInt GCD(x,y) //return greatest common divisor of bigInts x and y (each with same number of elements).
// boolean greater(x,y) //is x>y? (x and y are nonnegative bigInts)
// boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?
// bigInt int2bigInt(t,n,m) //return a bigInt equal to integer t, with at least n bits and m array elements
// bigInt inverseMod(x,n) //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null
// int inverseModInt(x,n) //return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse
// boolean isZero(x) //is the bigInt x equal to zero?
// boolean millerRabin(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is bigInt, 1<b<x)
// boolean millerRabinInt(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is int, 1<b<x)
// bigInt mod(x,n) //return a new bigInt equal to (x mod n) for bigInts x and n.
// int modInt(x,n) //return x mod n for bigInt x and integer n.
// bigInt mult(x,y) //return x*y for bigInts x and y. This is faster when y<x.
// bigInt multMod(x,y,n) //return (x*y mod n) for bigInts x,y,n. For greater speed, let y<x.
// boolean negative(x) //is bigInt x negative?
// bigInt powMod(x,y,n) //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.
// bigInt randBigInt(n,s) //return an n-bit random BigInt (n>=1). If s=1, then the most significant of those n bits is set to 1.
// bigInt randTruePrime(k) //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.
// bigInt randProbPrime(k) //return a new, random, k-bit, probable prime bigInt (probability it's composite less than 2^-80).
// bigInt str2bigInt(s,b,n,m) //return a bigInt for number represented in string s in base b with at least n bits and m array elements
// bigInt sub(x,y) //return (x-y) for bigInts x and y. Negative answers will be 2s complement
// bigInt trim(x,k) //return a copy of x with exactly k leading zero elements
//
//
// The following functions each have a non-underscored version, which most users should call instead.
// These functions each write to a single parameter, and the caller is responsible for ensuring the array
// passed in is large enough to hold the result.
//
// void addInt_(x,n) //do x=x+n where x is a bigInt and n is an integer
// void add_(x,y) //do x=x+y for bigInts x and y
// void copy_(x,y) //do x=y on bigInts x and y
// void copyInt_(x,n) //do x=n on bigInt x and integer n
// void GCD_(x,y) //set x to the greatest common divisor of bigInts x and y, (y is destroyed). (This never overflows its array).
// boolean inverseMod_(x,n) //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist
// void mod_(x,n) //do x=x mod n for bigInts x and n. (This never overflows its array).
// void mult_(x,y) //do x=x*y for bigInts x and y.
// void multMod_(x,y,n) //do x=x*y mod n for bigInts x,y,n.
// void powMod_(x,y,n) //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation. 0**0=1.
// void randBigInt_(b,n,s) //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.
// void randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.
// void sub_(x,y) //do x=x-y for bigInts x and y. Negative answers will be 2s complement.
//
// The following functions do NOT have a non-underscored version.
// They each write a bigInt result to one or more parameters. The caller is responsible for
// ensuring the arrays passed in are large enough to hold the results.
//
// void addShift_(x,y,ys) //do x=x+(y<<(ys*bpe))
// void carry_(x) //do carries and borrows so each element of the bigInt x fits in bpe bits.
// void divide_(x,y,q,r) //divide x by y giving quotient q and remainder r
// int divInt_(x,n) //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).
// int eGCD_(x,y,d,a,b) //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y
// void halve_(x) //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement. (This never overflows its array).
// void leftShift_(x,n) //left shift bigInt x by n bits. n<bpe.
// void linComb_(x,y,a,b) //do x=a*x+b*y for bigInts x and y and integers a and b
// void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys
// void mont_(x,y,n,np) //Montgomery multiplication (see comments where the function is defined)
// void multInt_(x,n) //do x=x*n where x is a bigInt and n is an integer.
// void rightShift_(x,n) //right shift bigInt x by n bits. 0 <= n < bpe. (This never overflows its array).
// void squareMod_(x,n) //do x=x*x mod n for bigInts x,n
// void subShift_(x,y,ys) //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.
//
// The following functions are based on algorithms from the _Handbook of Applied Cryptography_
// powMod_() = algorithm 14.94, Montgomery exponentiation
// eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_
// GCD_() = algorothm 14.57, Lehmer's algorithm
// mont_() = algorithm 14.36, Montgomery multiplication
// divide_() = algorithm 14.20 Multiple-precision division
// squareMod_() = algorithm 14.16 Multiple-precision squaring
// randTruePrime_() = algorithm 4.62, Maurer's algorithm
// millerRabin() = algorithm 4.24, Miller-Rabin algorithm
//
// Profiling shows:
// randTruePrime_() spends:
// 10% of its time in calls to powMod_()
// 85% of its time in calls to millerRabin()
// millerRabin() spends:
// 99% of its time in calls to powMod_() (always with a base of 2)
// powMod_() spends:
// 94% of its time in calls to mont_() (almost always with x==y)
//
// This suggests there are several ways to speed up this library slightly:
// - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)
// -- this should especially focus on being fast when raising 2 to a power mod n
// - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test
// - tune the parameters in randTruePrime_(), including c, m, and recLimit
// - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking
// within the loop when all the parameters are the same length.
//
// There are several ideas that look like they wouldn't help much at all:
// - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)
// - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)
// - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square
// followed by a Montgomery reduction. The intermediate answer will be twice as long as x, so that
// method would be slower. This is unfortunate because the code currently spends almost all of its time
// doing mont_(x,x,...), both for randTruePrime_() and powMod_(). A faster method for Montgomery squaring
// would have a large impact on the speed of randTruePrime_() and powMod_(). HAC has a couple of poorly-worded
// sentences that seem to imply it's faster to do a non-modular square followed by a single
// Montgomery reduction, but that's obviously wrong.
////////////////////////////////////////////////////////////////////////////////////////
//globals
bpe=0; //bits stored per array element
mask=0; //AND this with an array element to chop it down to bpe bits
radix=mask+1; //equals 2^bpe. A single 1 bit to the left of the last bit of mask.
//the digits for converting to different bases
digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-';
//initialize the global variables
for (bpe=0; (1<<(bpe+1)) > (1<<bpe); bpe++); //bpe=number of bits in the mantissa on this platform
bpe>>=1; //bpe=number of bits in one element of the array representing the bigInt
mask=(1<<bpe)-1; //AND the mask with an integer to get its bpe least significant bits
radix=mask+1; //2^bpe. a single 1 bit to the left of the first bit of mask
one=int2bigInt(1,1,1); //constant used in powMod_()
//the following global variables are scratchpad memory to
//reduce dynamic memory allocation in the inner loop
t=new Array(0);
ss=t; //used in mult_()
s0=t; //used in multMod_(), squareMod_()
s1=t; //used in powMod_(), multMod_(), squareMod_()
s2=t; //used in powMod_(), multMod_()
s3=t; //used in powMod_()
s4=t; s5=t; //used in mod_()
s6=t; //used in bigInt2str()
s7=t; //used in powMod_()
T=t; //used in GCD_()
sa=t; //used in mont_()
mr_x1=t; mr_r=t; mr_a=t; //used in millerRabin()
eg_v=t; eg_u=t; eg_A=t; eg_B=t; eg_C=t; eg_D=t; //used in eGCD_(), inverseMod_()
md_q1=t; md_q2=t; md_q3=t; md_r=t; md_r1=t; md_r2=t; md_tt=t; //used in mod_()
primes=t; pows=t; s_i=t; s_i2=t; s_R=t; s_rm=t; s_q=t; s_n1=t;
s_a=t; s_r2=t; s_n=t; s_b=t; s_d=t; s_x1=t; s_x2=t, s_aa=t; //used in randTruePrime_()
rpprb=t; //used in randProbPrimeRounds() (which also uses "primes")
////////////////////////////////////////////////////////////////////////////////////////
//return array of all primes less than integer n
function findPrimes(n) {
var i,s,p,ans;
s=new Array(n);
for (i=0;i<n;i++)
s[i]=0;
s[0]=2;
p=0; //first p elements of s are primes, the rest are a sieve
for(;s[p]<n;) { //s[p] is the pth prime
for(i=s[p]*s[p]; i<n; i+=s[p]) //mark multiples of s[p]
s[i]=1;
p++;
s[p]=s[p-1]+1;
for(; s[p]<n && s[s[p]]; s[p]++); //find next prime (where s[p]==0)
}
ans=new Array(p);
for(i=0;i<p;i++)
ans[i]=s[i];
return ans;
}
//does a single round of Miller-Rabin base b consider x to be a possible prime?
//x is a bigInt, and b is an integer, with b<x
function millerRabinInt(x,b) {
if (mr_x1.length!=x.length) {
mr_x1=dup(x);
mr_r=dup(x);
mr_a=dup(x);
}
copyInt_(mr_a,b);
return millerRabin(x,mr_a);
}
//does a single round of Miller-Rabin base b consider x to be a possible prime?
//x and b are bigInts with b<x
function millerRabin(x,b) {
var i,j,k,s;
if (mr_x1.length!=x.length) {
mr_x1=dup(x);
mr_r=dup(x);
mr_a=dup(x);
}
copy_(mr_a,b);
copy_(mr_r,x);
copy_(mr_x1,x);
addInt_(mr_r,-1);
addInt_(mr_x1,-1);
//s=the highest power of two that divides mr_r
k=0;
for (i=0;i<mr_r.length;i++)
for (j=1;j<mask;j<<=1)
if (x[i] & j) {
s=(k<mr_r.length+bpe ? k : 0);
i=mr_r.length;
j=mask;
} else
k++;
if (s)
rightShift_(mr_r,s);
powMod_(mr_a,mr_r,x);
if (!equalsInt(mr_a,1) && !equals(mr_a,mr_x1)) {
j=1;
while (j<=s-1 && !equals(mr_a,mr_x1)) {
squareMod_(mr_a,x);
if (equalsInt(mr_a,1)) {
return 0;
}
j++;
}
if (!equals(mr_a,mr_x1)) {
return 0;
}
}
return 1;
}
//returns how many bits long the bigInt is, not counting leading zeros.
function bitSize(x) {
var j,z,w;
for (j=x.length-1; (x[j]==0) && (j>0); j--);
for (z=0,w=x[j]; w; (w>>=1),z++);
z+=bpe*j;
return z;
}
//return a copy of x with at least n elements, adding leading zeros if needed
function expand(x,n) {
var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
copy_(ans,x);
return ans;
}
//return a k-bit true random prime using Maurer's algorithm.
function randTruePrime(k) {
var ans=int2bigInt(0,k,0);
randTruePrime_(ans,k);
return trim(ans,1);
}
//return a k-bit random probable prime with probability of error < 2^-80
function randProbPrime(k) {
if (k>=600) return randProbPrimeRounds(k,2); //numbers from HAC table 4.3
if (k>=550) return randProbPrimeRounds(k,4);
if (k>=500) return randProbPrimeRounds(k,5);
if (k>=400) return randProbPrimeRounds(k,6);
if (k>=350) return randProbPrimeRounds(k,7);
if (k>=300) return randProbPrimeRounds(k,9);
if (k>=250) return randProbPrimeRounds(k,12); //numbers from HAC table 4.4
if (k>=200) return randProbPrimeRounds(k,15);
if (k>=150) return randProbPrimeRounds(k,18);
if (k>=100) return randProbPrimeRounds(k,27);
return randProbPrimeRounds(k,40); //number from HAC remark 4.26 (only an estimate)
}
//return a k-bit probable random prime using n rounds of Miller Rabin (after trial division with small primes)
function randProbPrimeRounds(k,n) {
var ans, i, divisible, B;
B=30000; //B is largest prime to use in trial division
ans=int2bigInt(0,k,0);
//optimization: try larger and smaller B to find the best limit.
if (primes.length==0)
primes=findPrimes(30000); //check for divisibility by primes <=30000
if (rpprb.length!=ans.length)
rpprb=dup(ans);
for (;;) { //keep trying random values for ans until one appears to be prime
//optimization: pick a random number times L=2*3*5*...*p, plus a
// random element of the list of all numbers in [0,L) not divisible by any prime up to p.
// This can reduce the amount of random number generation.
randBigInt_(ans,k,0); //ans = a random odd number to check
ans[0] |= 1;
divisible=0;
//check ans for divisibility by small primes up to B
for (i=0; (i<primes.length) && (primes[i]<=B); i++)
if (modInt(ans,primes[i])==0 && !equalsInt(ans,primes[i])) {
divisible=1;
break;
}
//optimization: change millerRabin so the base can be bigger than the number being checked, then eliminate the while here.
//do n rounds of Miller Rabin, with random bases less than ans
for (i=0; i<n && !divisible; i++) {
randBigInt_(rpprb,k,0);
while(!greater(ans,rpprb)) //pick a random rpprb that's < ans
randBigInt_(rpprb,k,0);
if (!millerRabin(ans,rpprb))
divisible=1;
}
if(!divisible)
return ans;
}
}
//return a new bigInt equal to (x mod n) for bigInts x and n.
function mod(x,n) {
var ans=dup(x);
mod_(ans,n);
return trim(ans,1);
}
//return (x+n) where x is a bigInt and n is an integer.
function addInt(x,n) {
var ans=expand(x,x.length+1);
addInt_(ans,n);
return trim(ans,1);
}
//return x*y for bigInts x and y. This is faster when y<x.
function mult(x,y) {
var ans=expand(x,x.length+y.length);
mult_(ans,y);
return trim(ans,1);
}
//return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.
function powMod(x,y,n) {
var ans=expand(x,n.length);
powMod_(ans,trim(y,2),trim(n,2),0); //this should work without the trim, but doesn't
return trim(ans,1);
}
//return (x-y) for bigInts x and y. Negative answers will be 2s complement
function sub(x,y) {
var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1));
sub_(ans,y);
return trim(ans,1);
}
//return (x+y) for bigInts x and y.
function add(x,y) {
var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1));
add_(ans,y);
return trim(ans,1);
}
//return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null
function inverseMod(x,n) {
var ans=expand(x,n.length);
var s;
s=inverseMod_(ans,n);
return s ? trim(ans,1) : null;
}
//return (x*y mod n) for bigInts x,y,n. For greater speed, let y<x.
function multMod(x,y,n) {
var ans=expand(x,n.length);
multMod_(ans,y,n);
return trim(ans,1);
}
//generate a k-bit true random prime using Maurer's algorithm,
//and put it into ans. The bigInt ans must be large enough to hold it.
function randTruePrime_(ans,k) {
var c,m,pm,dd,j,r,B,divisible,z,zz,recSize;
if (primes.length==0)
primes=findPrimes(30000); //check for divisibility by primes <=30000
if (pows.length==0) {
pows=new Array(512);
for (j=0;j<512;j++) {
pows[j]=Math.pow(2,j/511.-1.);
}
}
//c and m should be tuned for a particular machine and value of k, to maximize speed
c=0.1; //c=0.1 in HAC
m=20; //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
recLimit=20; //stop recursion when k <=recLimit. Must have recLimit >= 2
if (s_i2.length!=ans.length) {
s_i2=dup(ans);
s_R =dup(ans);
s_n1=dup(ans);
s_r2=dup(ans);
s_d =dup(ans);
s_x1=dup(ans);
s_x2=dup(ans);
s_b =dup(ans);
s_n =dup(ans);
s_i =dup(ans);
s_rm=dup(ans);
s_q =dup(ans);
s_a =dup(ans);
s_aa=dup(ans);
}
if (k <= recLimit) { //generate small random primes by trial division up to its square root
pm=(1<<((k+2)>>1))-1; //pm is binary number with all ones, just over sqrt(2^k)
copyInt_(ans,0);
for (dd=1;dd;) {
dd=0;
ans[0]= 1 | (1<<(k-1)) | Math.floor(Math.random()*(1<<k)); //random, k-bit, odd integer, with msb 1
for (j=1;(j<primes.length) && ((primes[j]&pm)==primes[j]);j++) { //trial division by all primes 3...sqrt(2^k)
if (0==(ans[0]%primes[j])) {
dd=1;
break;
}
}
}
carry_(ans);
return;
}
B=c*k*k; //try small primes up to B (or all the primes[] array if the largest is less than B).
if (k>2*m) //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
for (r=1; k-k*r<=m; )
r=pows[Math.floor(Math.random()*512)]; //r=Math.pow(2,Math.random()-1);
else
r=.5;
//simulation suggests the more complex algorithm using r=.333 is only slightly faster.
recSize=Math.floor(r*k)+1;
randTruePrime_(s_q,recSize);
copyInt_(s_i2,0);
s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe)); //s_i2=2^(k-2)
divide_(s_i2,s_q,s_i,s_rm); //s_i=floor((2^(k-1))/(2q))
z=bitSize(s_i);
for (;;) {
for (;;) { //generate z-bit numbers until one falls in the range [0,s_i-1]
randBigInt_(s_R,z,0);
if (greater(s_i,s_R))
break;
} //now s_R is in the range [0,s_i-1]
addInt_(s_R,1); //now s_R is in the range [1,s_i]
add_(s_R,s_i); //now s_R is in the range [s_i+1,2*s_i]
copy_(s_n,s_q);
mult_(s_n,s_R);
multInt_(s_n,2);
addInt_(s_n,1); //s_n=2*s_R*s_q+1
copy_(s_r2,s_R);
multInt_(s_r2,2); //s_r2=2*s_R
//check s_n for divisibility by small primes up to B
for (divisible=0,j=0; (j<primes.length) && (primes[j]<B); j++)
if (modInt(s_n,primes[j])==0 && !equalsInt(s_n,primes[j])) {
divisible=1;
break;
}
if (!divisible) //if it passes small primes check, then try a single Miller-Rabin base 2
if (!millerRabinInt(s_n,2)) //this line represents 75% of the total runtime for randTruePrime_
divisible=1;
if (!divisible) { //if it passes that test, continue checking s_n
addInt_(s_n,-3);
for (j=s_n.length-1;(s_n[j]==0) && (j>0); j--); //strip leading zeros
for (zz=0,w=s_n[j]; w; (w>>=1),zz++);
zz+=bpe*j; //zz=number of bits in s_n, ignoring leading zeros
for (;;) { //generate z-bit numbers until one falls in the range [0,s_n-1]
randBigInt_(s_a,zz,0);
if (greater(s_n,s_a))
break;
} //now s_a is in the range [0,s_n-1]
addInt_(s_n,3); //now s_a is in the range [0,s_n-4]
addInt_(s_a,2); //now s_a is in the range [2,s_n-2]
copy_(s_b,s_a);
copy_(s_n1,s_n);
addInt_(s_n1,-1);
powMod_(s_b,s_n1,s_n); //s_b=s_a^(s_n-1) modulo s_n
addInt_(s_b,-1);