-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
executable file
·2186 lines (1963 loc) · 72.7 KB
/
main.c
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
// Copyright <Pierre-François Monville>
// ===========================================================================
// enigmaX
// Permet de chiffrer et de déchiffrer toutes les données entrées en paramètre.
// Le mot de passe demandé au début est hashé puis sert de graine pour
// le PRNG(générateur de nombre aléatoire). Le PRNG permet de fournir une clé unique
// égale à la longueur du fichier à coder. La clé unique subit un xor avec
// le mot de passe (le mot de passe est répété autant de fois que nécéssaire).
// Le fichier subit un xor avec cette nouvelle clé, puis un brouilleur est utilisé.
// Il mélange la table des caractères (ascii) en utilisant le PRNG et en utilisant
// le keyfile s'il est fourni. 256 tables de brouillages sont utilisées au total dans
// un ordre non prédictible donné par la clé unique combiné avec le keyfile s'il est fournit.
//
// Can crypt and decrypt any data given in argument.
// The password asked is hashed to be used as a seed for the PRNG(pseudo random number generator).
// The PRNG gives a unique key which has the same length as the source file.
// The key is xored with the password (the password is repeated as long as necessary).
// The file is then xored with this new key, then a scrambler is used.
// It scrambles the ascii table using the PRNG and the keyfile if it is given.
// 256 scramble's tables are used in an unpredictible order given by the unique key combined with
// the keyfile if present.
//
// USAGE :
// enigmax [options] FILE|DIRECTORY [KEYFILE]
//
// code or decode the given file
//
// FILE|DIRECTORY
// path to the file or directory that will be crypted/decrypted
//
// KEYFILE:
// path to a keyfile that is used to generate the scrambler instead of the password
//
// -s (simple) :
// put the scrambler off
//
// -i (inverted) :
// invert the coding/decoding process, first it xors then it scrambles
//
// -n (normalised) :
// normalise the size of the keyFile, make its length matching one scrambling cycle
//
// -d (destroy) :
// write on top of the source file (except folder they are deleted at the end)
//
// -f (force) :
// never ask, overwrites existing files
//
// -r (randomize) :
// randomize the name of the output file, keeping extension
//
// -R (full randomize) :
// randomize the name of the output file, no extension
//
// -k --keyfile :
// generate a keyfile
//
// -h --help :
// further help
//
//
// ===========================================================================
/*
TODO:
crypted folders explorer
graphical interface
special option (multi layer's password)
*/
/*
Installation
MAC:
clang -Ofast -fno-unroll-loops main.c -std='c11' -o enigmax
LINUX:
gcc -fno-move-loop-invariants -fno-unroll-loops main.c -std='c11' -o enigmax
WINDOWS (with bash for windows only):
gcc -fno-move-loop-invariants -fno-unroll-loops main.c -std='c11' -o enigmax
you can put the compiled file "enigmax" in your path to use it everywhere
export PATH=$PATH:/PATH/TO/enigmax
write it in your ~/.bashrc if you want it to stay after a reboot
*/
/*
constants
*/
#define BUFFER_SIZE 16384 //16384 //8192
#define _XOPEN_SOURCE 500 //to use extra function used by X/OPEN and POSIX, here nftw, "500 - X/Open 5, incorporating POSIX 1995"
/*
includes
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <time.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>
#include <ftw.h>
#include <errno.h>
/*
global variables
*/
static const char *progName;
static const char *fileName;
static char pathToMainFile[PATH_MAX] = "./";
static char outputFileName[2*PATH_MAX];
static char _isADirectory;
static uint64_t seed[16];
static int seedIndex = 0;
static unsigned char scrambleAsciiTables[256][256];
static unsigned char unscrambleAsciiTables[256][256];
static char isCrypting = 1;
static char scrambling = 1;
static char usingKeyFile = 0;
static char isCodingInverted = 0;
static char normalised = 0;
static char keyfileGeneration = 0;
static char force = 0;
static long numberOfBuffer;
static char scramblingTablesOrder[BUFFER_SIZE];
static char passPhrase[16384];
static uint64_t passIndex = 0;
static int passPhraseSize = 0;
static int keyFileSize = 0;
// _set_output_format(_TWO_DIGIT_EXPONENT);
/*
-static void usage(int status)
status : expect EXIT_FAILURE or EXIT_SUCCESS code to choose the output stream
when the program is typed without arguments in terminal it shows the usage
*/
static void usage(int status)
{
FILE *dest = (status == 0) ? stdout : stderr;
if(status == 0){
fprintf(dest,
"NAME\n\t%s -- crypt or decrypt any data\n\nSYNOPSIS\n\t%s [options] FILE|DIRECTORY [KEYFILE]\n\nDESCRIPTION\n\t(FR) Permet de chiffrer et de déchiffrer toutes les données entrées en paramètre. Le mot de passe demandé au début est hashé puis sert de graine pour le PRNG(générateur de nombre aléatoire). Le PRNG permet de fournir une clé unique égale à la longueur du fichier à coder. La clé unique subit un xor avec le mot de passe (le mot de passe est répété autant de fois que nécéssaire). Le fichier subit un xor avec cette nouvelle clé, puis un brouilleur est utilisé. Il mélange la table des caractères (ascii) en utilisant le PRNG et en utilisant le keyfile s'il est fourni. 256 tables de brouillages sont utilisées au total dans un ordre non prédictible donné par la clé unique combiné avec le keyfile s'il est fournit.\n\t(EN) Can crypt and decrypt any data given in argument. The password asked is hashed to be used as a seed for the PRNG(pseudo random number generator). The PRNG gives a unique key which has the same length as the source file. The key is xored with the password (the password is repeated as long as necessary). The file is then xored with this new key, then a scrambler is used. It scrambles the ascii table using the PRNG and the keyfile if it is given. 256 scramble's tables are used in an unpredictible order given by the unique key combined with the keyfile if present.\n\nOPTIONS\n\toptions are as follows:\n\n\t-h | --help\tfurther help.\n\n\t-k | --keyfile\tgenerate keyfile.\n\n\t-s (simple)\tput the scrambler on off.\n\n\t-i (inverted)\tinvert the coding/decoding process, for coding it xors then scrambles and for decoding it scrambles then xors.\n\n\t-n (normalised)\tnormalise the size of the keyfile, if the keyfile is too long (over 1 cycle in the Yates and Fisher algorithm) it will be croped to complete 1 cycle\n\n\t-d (destroy)\twrite on top of the source file (securely erase source data), except when the source is a folder where it's just deleted by the system at the end)\n\n\t-f (force)\tnever ask something to the user after entering password (overwrite the output file if it already exists and treat the second argument as a file if it looks like a set of options)\n\n\t-r (randomize)\trandomize the name of the output file but keeping the extension intact\n\n\t-R (randomize)\trandomize the name of the output file included the extension\n\n\tFILE|DIRECTORY\tthe path to the file or directory to crypt/decrypt\n\n\tKEYFILE \tthe path to a file which will be used to scramble the substitution's tables and choose in which order they will be used instead of the PRNG only (starting at 16 ko for the keyfile is great, however not interesting to be too heavy) \n\nEXIT STATUS\n\tthe %s program exits 0 on success, and anything else if an error occurs.\n\nEXAMPLES\n\tthe command :\t%s file1\n\n\tlets you choose between crypting or decrypting then it will prompt for a password that crypt/decrypt file1 as xfile1 in the same folder, file1 is not modified.\n\n\tthe command :\t%s file2 keyfile1\n\n\tlets you choose between crypting or decrypting, will prompt for the password that crypt/decrypt file2, uses keyfile1 to generate the scrambler then crypt/decrypt file2 as xfile2 in the same folder, file2 is not modified.\n\n\tthe command :\t%s -s file3\n\n\tlets you choose between crypting or decrypting, will prompt for a password that crypt/decrypt the file without using the scrambler(option 's'), resulting in using the unique key only.\n\n\tthe command :\t%s -i file4 keyfile2\n\n\tlets you choose between crypting or decrypting, uses keyfile2 to generate the scramble table and will prompt for a password that crypt/decrypt the file but will inverts the process(option 'i'): first it xors then it scrambles for the coding process or first it unscrambles then it xors for the decoding process\n\n\tthe command :\t%s -dni file5 keyfile2\n\n\tlets you choose between crypting or decrypting, will prompt for a password that crypt/decrypt the file but generates the substitution's tables with the keyfile passing only one cycle of the Fisher & Yates algorythm(option 'n', so it's shorter in time), inverts the scrambling phase with the xoring phase(option 'i') and write on top of the source file(option 'd')\n\n\tthe command :\t%s -k file6\n\n\tgenerate a keyfile and use it to crypt/decrypt the file\n\n\tthe command :\t%s --keyfile\n\n\tonly generate a keyfile and put it in the current directory\n\nBUGS\n\tIn rare cases, when crypting/decrypting from a directory, the system cannot open the tarfile it created from the directory (possibly the file system is too slow to register it). That's why the program waits one second after the creation of the tarfile when sourcefile is a directory. If it is not enough, the tarfile will not be deleted and you just have to redo the same command with the tarfile as the source file instead of the directory (you can use the d option to securely delete the tarfile to produce the same steps as the standard case).\n\nAUTHOR\n\tPierre-François MONVILLE\n\nCOPYRIGHT\n\tMIT <12 september 2015> <Pierre-François MONVILLE>\n\n", progName, progName, progName, progName, progName, progName, progName, progName, progName, progName);
} else{
fprintf(dest,
"\n%s -- crypt or decrypt any data\n\nVersion : 3.6.6\n\nUsage : %s [options] FILE|DIRECTORY [KEYFILE]\n\nFILE|DIRECTORY :\tpath to the file or directory to crypt/decrypt\n\nKEYFILE :\t\tpath to a keyfile for the substitution's table\n\nOptions :\n -h | --help :\t\tfurther help\n -k | --keyfile :\tgenerate keyfile\n -s (simple) :\t\tput the scrambler off\n -i (inverted) :\tinvert the process, swapping xor with scramble\n -n (normalised) :\tnormalise the size of the keyfile\n -d (destroy) :\toverwrite source file or delete source folder afterwards\n -f (force) :\t\tnever ask, overwrite existing files\n -r (randomize) :\trandomize the name of the output file, keeping extension\n -R (full randomize) : randomize the name of the output file, no extension\n\n", progName, progName);
}
exit(status);
}
/*
-long ceilRound(float numberToBeRounded)
returned value : the number rounded (ceil form)
to prevent from importing all math.h for just one function
I had to add it myself
*/
long ceilRound(float numberToBeRounded){
if (numberToBeRounded - (long) numberToBeRounded > 0)
{
return (long) numberToBeRounded + 1;
}
return (long) numberToBeRounded;
}
/*
void clearBuffer()
empty the buffer
*/
void clearBuffer()
{
int charInBuffer = 0;
while (charInBuffer != '\n' && charInBuffer != EOF)
{
charInBuffer = getchar();
}
}
/*
-int readStr(char *str, unsigned long size)
returned value : 1 on success, 0 on failure
basicaly, it's doing a fgets but take care of the buffer
*/
int readString(char *string, unsigned long size)
{
char *EOFPos = NULL;
if(fgets(string, size, stdin) != NULL)
{
EOFPos = strchr(string, '\n');
if(EOFPos != NULL)
{
*EOFPos = '\0';
}
else
{
clearBuffer();
}
return 1;
}
else
{
clearBuffer();
return 0;
}
}
/*
-void getPassword
prompt for the user's password
the echo is disable so nothing can be seen
*/
void getPassword(){
long size;
struct termios tty_attr;
char NOT_TERMINAL = 0;
// disable echoing in console
// ---------------------------
if (tcgetattr(STDIN_FILENO, &tty_attr) < 0){
if(errno != ENOTTY){
printf("Error while trying to change the visibility of the console, error code: %d, exiting...",errno);
exit(EXIT_FAILURE);
}else{
NOT_TERMINAL = 1;
}
}
// ---------------------------
// save old state
// ---------------------------
const tcflag_t c_lflag = tty_attr.c_lflag;
if (!NOT_TERMINAL){
tty_attr.c_lflag &= ~ECHO;
// uncomment the line below to allow some special caracters to be capture in the password (including delete, suppr,...) causes weird glitches for page up, page down...
// tty_attr.c_lflag &= ~ICANON;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0){
printf("Error while trying to change the visibility of the console, error code: %d, exiting...",errno);
exit(EXIT_FAILURE);
}
}
// ---------------------------
// Get password
// ---------------------------
do{
printf("Password:");
readString(passPhrase, 16384);
size = strlen(passPhrase);
if(size <= 0){
printf("\rThe password can't be empty\n");
continue;
}
// printf("\033[F\033[J");
printf("\r");
}while(size <= 0);
// ---------------------------
// enable echoing again
// ---------------------------
if(!NOT_TERMINAL){
tty_attr.c_lflag = c_lflag;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0){
printf("Error while trying to change the visibility of the console, exiting...");
exit(EXIT_FAILURE);
}
}
// ---------------------------
}
/*
-char* processTarString(char* string)
change string placing '\' just before every spaces in order to
the tar command to work with files/directories with spaces in their names
*/
char* processTarString(char* string){
int numberOfSpace = 0;
char* resultString;
for (int i = 0; i < (int)strlen(string); ++i)
{
if (string[i] == ' ')
{
numberOfSpace++;
}
}
if (numberOfSpace == 0) //just returns the same string basicaly
{
resultString = (char*) calloc(1, sizeof(char)* (strlen(string)));
strcat(resultString, string);
return resultString;
}
resultString = (char*) calloc(1, sizeof(char)* (strlen(string) + numberOfSpace + 1));
for (int i = 0, j = 0; i < (int)strlen(string); ++i, ++j)
{
if (string[i] == ' ')
{
resultString[j] = '\\';
j++;
}
resultString[j] = string[i];
}
return resultString;
}
/*
-void fillWithSpaces(char* string, int n)
string : a string to fill with spaces
size : the max size of the string given
numberOfSpaces : the number of desired spaces
*/
void fillWithSpaces(char* string, int size, int numberOfSpaces){
struct winsize windowSize;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize);
int toFill;
if(numberOfSpaces - (windowSize.ws_col - strlen(string)) > 0){
toFill = windowSize.ws_col - strlen(string) -1;
}else{
toFill = numberOfSpaces;
}
int len = strlen(string);
// string contains a valid '\0' terminated string, so len is smaller than size
if( len + toFill >= size ) {
toFill = size - len - 1;
}
memset( string+len, ' ', toFill );
string[len + toFill] = '\0';
}
/*
-uint64_t generateNumber(void)
returned value : uint64_t number (equivalent to long long but on all OS)
random number generator
with the xorshift1024* algorythm
represents 2^1024 states which is equal to 95^x with x the maximum number of characters before looping to an already known state and 95 equals the number of different symbols that can be used for the password. Here x = 156 characters
it passes the BigCrush test :
http://xoroshiro.di.unimi.it/xorshift1024star.c
*/
uint64_t generateNumber(void) {
const uint64_t seed0 = seed[seedIndex];
uint64_t seed1 = seed[seedIndex = (seedIndex + 1) & 15];
seed1 ^= seed1 << 31; // a
seed[seedIndex] = seed1 ^ seed0 ^ (seed1 >> 11) ^ (seed0 >> 30); // b,c
return seed[seedIndex] * 0x9e3779b97f4a7c13;
}
//**********************************************************
//xoroshiro 128+ to get a random string to fill the password at the end to clean it
//from this implementation : http://xoroshiro.di.unimi.it/xoroshiro128plus.c
//**********************************************************
uint64_t secondarySeed[2];
static inline uint64_t rotl(const uint64_t x, int k) {
return (x << k) | (x >> (64 - k));
}
uint64_t xoroshiro128(void) {
const uint64_t s0 = secondarySeed[0];
uint64_t s1 = secondarySeed[1];
const uint64_t result = s0 + s1;
s1 ^= s0;
secondarySeed[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b
secondarySeed[1] = rotl(s1, 37); // c
return result;
}
void jumpForXoroshiro128(void) {
static const uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc };
uint64_t s0 = 0;
uint64_t s1 = 0;
for(int i = 0; i < (int)(sizeof JUMP / sizeof *JUMP); i++)
for(int b = 0; b < 64; b++) {
if (JUMP[i] & UINT64_C(1) << b) {
s0 ^= secondarySeed[0];
s1 ^= secondarySeed[1];
}
xoroshiro128();
}
secondarySeed[0] = s0;
secondarySeed[1] = s1;
}
//**********************************************************
/*
-void writeKeyFile(char* pathToKeyFile)
pathToKeyFile : a string representing the path where to write the keyFile
writes the keyFile in the current directory
*/
void writeKeyFile(char* pathToKeyFile){
srand(time(NULL));
int start = 10;
int end = 21;
int length = rand()%(end-start)+start;
FILE* keyFile;
if ((keyFile = fopen(pathToKeyFile, "wb")) == NULL) {
perror(pathToKeyFile);
printf("Exiting...\n");
exit(EXIT_FAILURE);
}
numberOfBuffer = length;
for (int i = 0; i < numberOfBuffer; ++i)
{
char buffer[BUFFER_SIZE];
for(int j = 0; j < BUFFER_SIZE; j++){
buffer[j] = (char) xoroshiro128();
}
fwrite(buffer, sizeof(char), BUFFER_SIZE, keyFile);
// random number of jump
int numberOfJump = rand()%11;
for (int i = 0; i < numberOfJump; ++i)
{
jumpForXoroshiro128();
}
}
fclose(keyFile);
}
/*
-char* generateKeyFile(char* keyFileName, char* directory)
keyFileName : a string that will contain the name of the keyfile
directory (OPTIONAL) : the path of the directory where to save the keyFile
asks for a name for the keyFile then writes the keyFile in the current directory
*/
void generateKeyFile(char* keyFileName, char* directory){
FILE* keyFile;
char loop = 0;
char procedureResponse1[50];
if(force){
if(directory == NULL){
sprintf(keyFileName, "key.bin");
}else{
sprintf(keyFileName, "%skey.bin", directory);
}
printf("The keyFile will be named key.bin in the current working directory\n");
}
else{
char hasConflict = 0;
do{
keyFileName[0] = '\0';
if(loop){
printf("An error occured while trying to create keyFile: ");
perror(procedureResponse1);
printf("\n");
}
printf("Enter the name of the keyFile [key]:");
readString(procedureResponse1, 49);
if(strlen(procedureResponse1) == 0){
sprintf(procedureResponse1, "key");
}
if(directory == NULL){
sprintf(keyFileName, "%s.bin", procedureResponse1);
}else{
sprintf(keyFileName, "%s%s.bin", directory, procedureResponse1);
}
if((keyFile = fopen(keyFileName, "rb")) != NULL){
hasConflict = 1;
char hasAnswered = 0;
char loop = 0;
do{
char procedureResponse2[2];
if (loop == 1){
printf("\033[F\033[J");
}
printf("A file named %s already exists, do you want to overwrite it ? [y|N]:", keyFileName);
readString(procedureResponse2, 2);
if(procedureResponse2[0] == 'Y' || procedureResponse2[0] == 'y'){
fclose(keyFile);
keyFile = NULL;
hasAnswered = 1;
}else if(procedureResponse2[0] == 'N' || procedureResponse2[0] == 'n' || strlen(procedureResponse2) == 0){
fclose(keyFile);
keyFile = NULL;
hasAnswered = 2;
}
loop = 1;
}while(hasAnswered == 0);
if(hasAnswered == 2){
continue;
}
}
keyFile = fopen(keyFileName, "wb");
loop = 1;
}while(keyFile == NULL);
if(hasConflict){
printf("\033[F\033[J");
printf("The keyFile name will be %s\n", keyFileName);
}
fclose(keyFile);
}
printf("Generating keyFile... ");
fflush(stdout);
writeKeyFile(keyFileName);
printf("Done\n");
fflush(stdout);
}
/*
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
less readable implementation for sha-3 (Keccak) cryptographic hash function
see : https://github.com/gvanas/KeccakCodePackage/blob/master/Standalone/CompactFIPS202/Keccak-more-compact.c
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#define FOR(i,n) for(i=0; i<n; ++i)
typedef unsigned char u8;
typedef unsigned long long int u64;
typedef unsigned int ui;
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen);
void FIPS202_SHAKE128(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1344, 256, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHAKE256(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1088, 512, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHA3_224(const u8 *in, u64 inLen, u8 *out) { Keccak(1152, 448, in, inLen, 0x06, out, 28); }
void FIPS202_SHA3_256(const u8 *in, u64 inLen, u8 *out) { Keccak(1088, 512, in, inLen, 0x06, out, 32); }
void FIPS202_SHA3_384(const u8 *in, u64 inLen, u8 *out) { Keccak(832, 768, in, inLen, 0x06, out, 48); }
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out) { Keccak(576, 1024, in, inLen, 0x06, out, 64); }
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
#define rL(x,y) load64((u8*)s+8*(x+5*y))
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
void KeccakF1600(void *s)
{
ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D;
for(i=0; i<24; i++) {
/*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); }
/*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; }
/*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); }
/*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
}
}
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
{
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
}
/*
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
END of sha-3(keccak) implementation
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
/*
-void getHash(unsigned char* output, char* password)
output : a 128 Byte string to store the output
password : a string which is the password typed by the user
uses sha-3 (Keccak) hash function to hash the password into a 1024 bits flow
*/
void getHash(char* output, char* password){
FIPS202_SHAKE256((unsigned char*)password, strlen(password), (unsigned char*)output, 128);
}
/*
-void getSeed()
password : the string corresponding to the password given by the user
this function is here to populate the seed for the PRNG,
it hashes the password then puts the output into the seed array
*/
void getSeed(){
char hash[128];
getHash(hash, passPhrase);
for (int i = 0; i < 16; ++i)
{
memcpy (&seed[i], hash + (sizeof(uint64_t) * i), sizeof (uint64_t));
}
}
/*
-void setSecondarySeed()
sets the seed of xoroshiro128 so that it is not 0 everywhere
*/
void setSecondarySeed(){
secondarySeed[0] = time(NULL);
secondarySeed[1] = secondarySeed[0] >> 1;
}
/*
-void getNext255StringFromKeyFile(FILE* keyFile, char* extractedString)
keyFile : the keyFile on which we will extract the string
extractedString : the result string containing the 255 characters from keyfile
Get a 255 string from the keyFile it stops at 255 if keyFile is too long
and make it loop if keyFile is too short
*/
void getNext255StringFromKeyFile(FILE* keyFile, char* extractedString){
long charactersRead = 0;
while(charactersRead < 255){
long size = fread(extractedString + charactersRead, 1, 255 - charactersRead, keyFile);
if(size == 0){
rewind(keyFile);
continue;
}
charactersRead += size;
}
}
/*
-char* getRandomFileName()
return a dynamic string representing a random file name
*/
char* getRandomFileName(){
char authorizedChar[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','Y','Z','0','1','2','3','4','5','6','7','8','9','\0'};
unsigned int length = ((unsigned int) xoroshiro128())%15 + 6;
char* randomName;
randomName = malloc(sizeof(char)*length);
for(int i = 0; i < (int)length; ++i){
randomName[i] = authorizedChar[(unsigned int)xoroshiro128()%strlen(authorizedChar)];
}
randomName[length-1] = '\0';
return randomName;
}
/*
-void scramble(FILE* keyFile)
keyFile : can be null, if present it passes through all the keyfile to scramble the ascii table
scramble the ascii table assuring that there is no duplicate
inspired by the Enigma machine; switching letters but without its weeknesses,
here a letter can be switched by itself and it is not possible to know how many letters
have been switched
*/
void scramble(FILE* keyFile){
printf("Scrambling substitution's tables, may be long...");
fflush(stdout);
for (int j = 0; j < 256; ++j)
{
printf("\rScrambling substitution's tables...(%d/256) ", j + 1);
fflush(stdout);
char temp = 0;
for (int i = 0; i < 256; ++i)
{
scrambleAsciiTables[j][i] = i;
}
if(! usingKeyFile) {
unsigned char random256;
for (int i = 0; i < 255; ++i)
{
//generate number between i and 255 according to Fisher and Yates shuffle algorithm
random256 = (char)((float)((char)(generateNumber()) ^ passPhrase[passIndex]) / 255.0 * (255.0 - i) + i);
passIndex++;
passIndex %= passPhraseSize;
temp = scrambleAsciiTables[j][i];
scrambleAsciiTables[j][i] = scrambleAsciiTables[j][random256];
scrambleAsciiTables[j][random256] = temp;
}
}
}
if (usingKeyFile){
//scramble all tables with keyFile
unsigned char random256;
long numberOfCycles = ceilRound((float)keyFileSize/(float)255);
if(normalised){
numberOfCycles = 1;
}
int k = 0;
float progress = 0;
char temp = 0;
// adjust loadbar to display width
struct winsize windowSize;
int spaceLeft;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize);
spaceLeft = windowSize.ws_col - 65;
if(spaceLeft < 3){
printf("\rScrambling sub.'s tables with keyFile...%.0f%%", progress);
}else{
printf("\rScrambling substitution's tables with keyFile, may be long...(%.0f%%)", progress);
}
fflush(stdout);
while(k < numberOfCycles){
if((int)((float)(k)/(float)(numberOfCycles) * 100.0) > progress){
progress = (float)(k)/(float)(numberOfCycles) * 100.0;
// adjust loadbar to display width
ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize);
spaceLeft = windowSize.ws_col - 65;
if(spaceLeft < 3){
printf("\rScrambling sub.'s tables with keyFile...%.0f%%", progress);
}else{
printf("\rScrambling substitution's tables with keyFile, may be long...(%.0f%%)", progress);
}
fflush(stdout);
}
for(int j = 0; j < 256; ++j){
char extractedString[255];
getNext255StringFromKeyFile(keyFile, extractedString);
for (int i = 0; i < 255; ++i)
{
//generate number between i and 255 according to Fisher and Yates shuffle algorithm
random256 = (char)((float)((char)(generateNumber()) ^ extractedString[i]) / 255.0 * (255.0 - i) + i);
temp = scrambleAsciiTables[j][i];
scrambleAsciiTables[j][i] = scrambleAsciiTables[j][random256];
scrambleAsciiTables[j][random256] = temp;
}
k++;
}
}
//get scramble's table order using keyFile
char message[100];
sprintf(message, "Get scramble's table order using keyFile...");
fillWithSpaces(message,100, 24);
printf("\r%s", message);
fflush(stdout);
int j = 0;
char tablesOrder[BUFFER_SIZE];
while(j < BUFFER_SIZE){
int size = fread(tablesOrder, 1, BUFFER_SIZE, keyFile);
if(size == 0){
rewind(keyFile);
continue;
}
for (int i = 0; i < size; ++i)
{
scramblingTablesOrder[j] = tablesOrder[i];
j++;
if(j == BUFFER_SIZE){
break;
}
}
}
}
char message[100];
sprintf(message, "Scrambling substitution's tables... Done");
fillWithSpaces(message,100, 27);
printf("\r%s\n", message);
fflush(stdout);
}
/*
-void unscramble(void)
this function is here only for optimization
it inverses the key/value in the scramble ascii table making the backward process instantaneous
*/
void unscramble(){
for (int j = 0; j < 256; ++j)
{
for (int i = 0; i < 256; ++i)
{
unsigned char c = scrambleAsciiTables[j][i];
unscrambleAsciiTables[j][c] = i;
}
}
}
/*
-void codingXOR...(char* extractedString, char* keyString, char* xoredString, int bufferLength)
extractedString : data taken from the source file in a string format
keyString : a part of the unique key generated by the PRNG in a string format
xoredString : the result of the xor operation between extractedString and keyString
bufferLength : the length of the data on which this function is working on
Every function is split for optimization purposes to limit the amount of conditions
Apply the mathematical xor function to extractedString and keyString
if we are coding (isCrypting == 1) then we switche the character from the source file then xor it
if we are decoding (isCrypting == 0) then we xor the character from the source file then unscramble it
The scramble table is chosed thanks to the key.
it gives a number from 0 to 255 that is used to chose the scrambled table.
It prevents a frequence analysis of the scrambled file in the event where the unique key has been found.
Thus even if you find the seed and by extension, the unique key, you can't apply headers and try to match
them to the scrambled file in order to deduce the scramble table. You absolutely need the password.
we can schemate all the coding/decoding xoring process like this :
coding : original:a -> scramble:x -> xored:?
decoding : xored(?) -> unxored(x) -> unscrambled(a)
*/
void codingXORKeyFileInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = scrambleAsciiTables[(unsigned char)(scramblingTablesOrder[i] ^ keyString[i])][(unsigned char)(extractedString[i] ^ keyString[i])];
}
}
void codingXORKeyFileNotInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = scrambleAsciiTables[(unsigned char) (scramblingTablesOrder[i] ^ keyString[i])][(unsigned char)extractedString[i]] ^ keyString[i];
}
}
void codingXORNoKeyFileInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = scrambleAsciiTables[(unsigned char)keyString[i]][(unsigned char)(extractedString[i] ^ keyString[i])];
}
}
void codingXORNoKeyFileNotInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = scrambleAsciiTables[(unsigned char)keyString[i]][(unsigned char)extractedString[i]] ^ keyString[i];
}
}
/*
-void decodingXOR...(char* extractedString, char* keyString, char* xoredString, int bufferLength)
extractedString : data taken from the source file in a string format
keyString : a part of the unique key generated by the PRNG in a string format
xoredString : the result of the xor operation between extractedString and keyString
bufferLength : the length of the data on which this function is working on
Every function is split for optimization purposes to limit the amount of conditions
Apply the mathematical xor function to extractedString and keyString
if we are coding (isCrypting == 1) then we switche the character from the source file then xor it
if we are decoding (isCrypting == 0) then we xor the character from the source file then unscramble it
we can schemate all the coding/decoding xoring process like this :
coding : original(a) -> scramble(x) -> xored(?)
decoding : xored(?) -> unxored(x) -> unscrambled(a)
*/
void decodingXORKeyFileInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = unscrambleAsciiTables[(unsigned char)(scramblingTablesOrder[i] ^ keyString[i])][(unsigned char)extractedString[i]] ^ keyString[i];
}
}
void decodingXORKeyFileNotInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = unscrambleAsciiTables[(unsigned char)(scramblingTablesOrder[i] ^ keyString[i])][(unsigned char)(extractedString[i] ^ keyString[i])];
}
}
void decodingXORNoKeyFileInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = unscrambleAsciiTables[(unsigned char)keyString[i]][(unsigned char)extractedString[i]] ^ keyString[i];
}
}
void decodingXORNoKeyFileNotInverted(char* extractedString, char* keyString, char* xoredString, int bufferLength){
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = unscrambleAsciiTables[(unsigned char)keyString[i]][(unsigned char)(extractedString[i] ^ keyString[i])];
}
}
/*
-void standardXOR(char* extractedString, char* keyString, char* xoredString, int bufferLength)
extractedString : data taken from the source file in a string format
keyString : a part of the unique key generated by the PRNG in a string format
xoredString : the result of the xor operation between extractedString and keyString
bufferLength : the length of the data on which this function is working on
Here only for optimization purpose so that there is the small amount
of condition possible when encrypt or decrypt
Apply the mathematical xor function to extractedString and keyString
if we are coding (isCrypting == 1) then we switche the character from the source file then xor it
if we are decoding (isCrypting == 0) then we xor the character from the source file then unscramble it
we can schemate all the coding/decoding xoring process like this :
coding : original(a) -> scramble(x) -> xored(?)
decoding : xored(?) -> unxored(x) -> unscrambled(a)
but here we don't scramble so it is:
coding : original(a) -> xored(?)
decoding: xored(?) -> unxored(a)
*/
void standardXOR(char* extractedString, char* keyString, char* xoredString, int bufferLength)
{
int i;
for (i = 0; i < bufferLength; ++i)
{
xoredString[i] = extractedString[i] ^ keyString[i];
}
}
/*
-int fillbuffer(FILE* mainFile, char* extractedString, char* keyString)
mainFile : pointer to the file given by the user
extractedString : will contains the data extracted from the source file in a string format
keyString : will contains a part of the unique key in a string format
returned value : the size of the data read
read a packet of data from the source file
return the length of the packet which is the buffer size (BUFFER_SIZE)
it can be less at the final packet (if the file isn't a multiple of the buffer size)
the keyString is get by generating a random number with the seed and then xoring it
with the password itself allowing the key to be really unique and not only one of the
2^64 possibilities offered by the seed (uint64_t)
the password is xoring this way : generateNumber1 ^ passPhrase[0]
generateNumber2 ^ passPhrase[1]
...
then the index overflows and it returns to 0 again
generataNumberX ^ passPhrase[0]
...
*/
int fillBuffer(FILE* mainFile, char* extractedString, char* keyString)
{
int charactersRead = fread(extractedString, 1, BUFFER_SIZE, mainFile);
for (int i = 0; i < charactersRead; ++i)
{
keyString[i] = (char)(generateNumber()) ^ passPhrase[passIndex];
passIndex++;
passIndex %= passPhraseSize;
}
return charactersRead;
}
/*
-static inline void loadBar(int currentIteration, int maximalIteration, int numberOfSteps, int startMessage)
currentIteration : the current iteration of the thing that is proccessed
maximalIteration : the number which represents 100% of the process
numberOfSteps : number defining how many times the bar updates
startMessage : the message displayed once at the beginning of the process
display a loading bar with current percentage, graphic representation, and time remaining
which update on every new percent by deleting itself to display the updating bar on top
inspired by Ross Hemsley's code : https://www.ross.click/2011/02/creating-a-progress-bar-in-c-or-any-other-console-app/
*/
static inline void loadBar(int currentIteration, int maximalIteration, int numberOfSteps, char* startMessage)
{
static char firstCall = 1;
static double elapsedTime;
double timeTillEnd;
static time_t startingTime;
time_t currentTime;
if(firstCall){
startingTime = time(NULL);
firstCall = 0;
}
// numberOfSteps defines the number of times the bar updates.
if ( currentIteration % (maximalIteration/numberOfSteps + 1) != 0 ) return;
// adjust loadbar to display width
struct winsize windowSize;
int numberOfSegments;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize);
numberOfSegments = windowSize.ws_col - 45;
if(numberOfSegments < 4){
numberOfSegments = windowSize.ws_col - 13;
if (numberOfSegments < 4){