-
Notifications
You must be signed in to change notification settings - Fork 0
/
triops.c
2213 lines (1927 loc) · 73.5 KB
/
triops.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
//
// triops:
// Encrypt/decrypt files using
// CHACHA20 as cipher algorithm and KECCAK-512 as hash algorithm.
// Features:
// * Files are (by default) encrypted/decrypted on-the-fly,
// so content is overwritten. This is interesting from a security
// point of view, as no clear content is left on disk.
// * When decrypting, if password is not the one used for encrypting,
// the process is aborted, so the file cannot be rendered unusable.
// This behaviour is achieved thanks to a password hash stored within
// the encrypted file. This hash can optionally be erased when
// encrypting (in this case the file could end up be decrypted with
// an incorrect password, so content could be irrecoverable).
// This hash is *not* the same used to encrypt!
// * File modification time is maintained. File dates are important!
// * Files can be used as passwords: for example jpg images, etc.
// (do not lose this 'password' file and do not modify it!)
//
// Type './triops -h' to obtain command-line help.
//
// Pure C99 code,
// by circulosmeos, May 2015. June 2015. July 2016. August 2016.
// http://circulosmeos.wordpress.com
// Licensed under GPL v3:
//
// Copyright (C) 2015 circulosmeos (http://circulosmeos.wordpress.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "triops.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#ifdef ANDROID_LIBRARY
#include <jni.h>
//#include <android/log.h>
//#define fprintf (...) __android_log_print(ANDROID_LOG_DEBUG, "TRIOPS", __VA_ARGS__);
#endif
// stat() in FileSize() (and obtainTimestamp(), #ifndef WINDOWS_PLATFORM)
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WINDOWS_PLATFORM
#include <conio.h>
#else
int getch(void);
#endif
#include "ecrypt-sync.h"
#include "crypto_hash.h"
#include "sph_keccak.h"
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// data types:
#define TRIOPS_VERSION "9.0"
#define PROGRAM_NAME "triops"
#define BUFFERSIZE 16384 // for CHACHA20: multiple of 64 bytes to avoid bad implementation (http://goo.gl/DHCLz1)
// v9.0: BUFFERSIZE cannot be smaller than ( IVSIZE_v3 + HASHSIZE_v3 ) = 72 bytes
#define KEYSIZE_v3 32 // KEYSIZE_v3 is for CHACHA20 = 256 bits (256/8=32 bytes)
#define IVSIZE_v3 8 // IVSIZE_v3 is for CHACHA20 = 64 bits ( 64/8= 8 bytes)
#define HASHSIZE_v3 64 // HASHSIZE_v3 is for KECCAK-512=512 bits (512/8=64 bytes)
#define MAX_PASSWORD_LENGTH 261+4 // maximum length of a password introduced with keyboard:
// 260+1(\n) at minimum to make this value (user&code') backwards compatible:
// MAX_PASSWORD_LENGTH must be >= MAX_PATH (260) > HASHSIZE_v3
// v9.0: MAX_PASSWORD_LENGTH must be >= MAX_PATH cause it may temporarily
// contain a password or path if input is stdin.
// +4 'cause MAX_PATH can grow 4 chars for file extension
typedef enum { CheckKeyIsValid_FALSE=0,
CheckKeyIsValid_TRUE=1,
CheckKeyIsValid_TRUE_BUT_EMPTY=2
} CheckKeyIsValid_Constants;
#ifndef WINDOWS_PLATFORM
typedef enum { obtainTimestamp_ST_ATIME=0, // Most recent access (Windows) (or last time modified (DOS))
obtainTimestamp_ST_MTIME=1, // Most recent modify
obtainTimestamp_ST_CTIME=2 // Creation time (NTFS) or most recent change of state (POSIX)
} obtainTimestamp_Constants;
#endif
typedef enum {
TRIOPS_VERSION_UNKNOWN=0, // v9.0
TRIOPS_V3=3,
SIZE_OF_TRIOPS_VERSIONS_ENUM=4
} triops_Versions_Constants;
triops_Versions_Constants triopsVersion;
const char TRIOPS_V3_EXTENSION[] = ".$#3";
const char TRIOPS_GENERIC_EXTENSION[] = ".ooo"; // v9.0
// 2 variables to store different hashes for the hypothetical case when hash is obtained from a file
// so it is not read multiples times: bHashAlreadyObtained and szHashAlreadyObtained
BOOL bHashAlreadyObtained[SIZE_OF_TRIOPS_VERSIONS_ENUM]; //initialize as {FALSE, FALSE, FALSE, FALSE}; @ process_file()
char szHashAlreadyObtained[SIZE_OF_TRIOPS_VERSIONS_ENUM][HASHSIZE_v3];
// optimization for the case when there's no need to obtain
// multiple hashes on the fly from a hashed password file:
// obtain just the needed one:
BOOL bJustOneHashIsNeeded; // initialize as FALSE @ process_file()
union KEY_v3
{
DWORD keyW [KEYSIZE_v3 / sizeof (DWORD)];
BYTE keyB [KEYSIZE_v3];
};
union HASHEDKEY_v3
{
DWORD keyW [HASHSIZE_v3 / sizeof (DWORD)];
BYTE keyB [HASHSIZE_v3];
};
// as IVSIZE_v3==64 bits => 64/8/4= 2 int
// uint32_t in order to assure 32 bits int
typedef struct tagIV_v3
{
uint32_t rand1;
uint32_t fileTime;
} IV_v3, *LPIV_v3;
union unionIV_v3
{
IV_v3 iv;
BYTE byteIV [sizeof (IV_v3)];
};
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// functions
void print_help();
int set_file_position (FILE * hFile, unsigned long long offset, int whence);
unsigned long long read_data_from_file(
BYTE *lpFileBuffer, int iBufferSize, int iBlocks, FILE *hFile,
BOOL bEncrypt, BOOL bUsingHeadMetadata, BOOL bOutputToTheSameFile,
unsigned long long lFileSize, unsigned long long lMetadataSize,
unsigned long long lBlockNumber, unsigned long long lBlockTotal,
unsigned long long lSubtrahend );
int process_file( char *szFile, char *szNewFile, char *szPass, BOOL bEncrypt,
BOOL bExplicitPassword, BOOL bOutputToTheSameFile, BOOL bDoNotStorePasswordHash,
int iUseSelectedMetadata );
int writeMetadata (FILE *hFileMetadata,
BOOL bDoNotStorePasswordHash,
void *IV, int IV_SIZE,
void *HASH, int HASH_SIZE);
void truncateFile (char *);
BOOL obtainPasswordFromKeyboard (char *szPass);
BOOL obtainPassword (char *szFile, char *szPass, BOOL bExplicitPassword);
unsigned long long FileSize(char *);
void EliminatePassword (char *szVariable, int LENGTH);
#ifndef WINDOWS_PLATFORM
time_t obtainTimestampUnix (char *szFile, int iMarcaDeTiempo);
#else
void obtainTimestampWin (char *szFile, LPFILETIME lpLastWriteTime);
void writeTimestampWin (char *szFile, LPFILETIME lpLastWriteTime);
#endif
void truncateFileBySize ( char *, unsigned long long );
BOOL LoadIVandHash_v3 (FILE *, LPBYTE, LPBYTE, char *, BOOL);
int CheckKeyIsValid_v3 (LPSTR, LPBYTE, LPBYTE, LPDWORD, BOOL);
void createIV_v3 (LPIV_v3, char *);
void CreateUniqueKey_v3 (LPDWORD, LPBYTE, LPIV_v3);
#ifdef ANDROID_LIBRARY
//int local_triops(int argc, char* argv[]);
int local_triops(int argc, char* const argv[static 5]);
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// JNI:
jint Java_com_example_triops_MainActivity_triops( JNIEnv* env, jobject thiz, jcharArray jargv )
{
//jargv is a Java array of Java strings
int argc = (*env)->GetArrayLength( env, jargv );
char **argv = malloc(sizeof(char*)*(argc));
int i;
jint result;
for(i=0; i<argc; i++)
{
jstring js = (*env)->GetObjectArrayElement( env, jargv, i ); //A Java string
const char *pjc = (*env)->GetStringUTFChars( env, js, NULL ); //A pointer to a Java-managed char buffer
argv[i] = strdup( pjc ); // Copy to local buffer
(*env)->ReleaseStringUTFChars( env, js, pjc );
(*env)->DeleteLocalRef( env, js );
}
//Call main
result = (jint)local_triops(argc, argv);
//Now free the array
if (argv != NULL) {
for(i=0;i<argc;i++) {
free(argv[i]);
}
free(argv);
}
return result;
}
#endif
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// proper pure C code:
// converted from C main() to JNI function
#ifndef ANDROID_LIBRARY
int main (int argc, char* argv[])
#else
//int local_triops (int argc, char* argv[])
int local_triops (int argc, char* const argv[static 5])
#endif
{
BOOL bOutputToTheSameFile;
// v9.0: from MAX_PATH to MAX_PASSWORD_LENGTH cause it can be used to
// store szPass in some cases:
char szPassFile [MAX_PASSWORD_LENGTH];
BOOL bEncrypt;
BOOL bExplicitPassword;
BOOL bDoNotStorePasswordHash;
BOOL bObligatoryKey;
BOOL bBreakOnFirstError;
BOOL bStdoutOutput; // v9.0
char szFile [MAX_PATH+4]; // +4 'cause MAX_PATH can grow 4 chars for file extension
char szNewFile [MAX_PATH+4]; // +4 'cause MAX_PATH can grow 4 chars for file extension
char szPass [MAX_PASSWORD_LENGTH];
int output;
int i;
int iUseSelectedMetadata = 0; // v9.0: 0 means not explicitely selected, 1: head, 2: tail
// default options:
bOutputToTheSameFile=TRUE; // defined as global above
szFile[0]=0x0;
szNewFile[0]=0x0;
bStdoutOutput=FALSE;
szPassFile[0]=0x0;
bEncrypt=FALSE;
bExplicitPassword=FALSE;
bDoNotStorePasswordHash=FALSE;
bObligatoryKey=FALSE; // it is necessary to indicate a key
bBreakOnFirstError=FALSE;
triopsVersion=TRIOPS_V3;
// this reset of bHashAlreadyObtained and bJustOneHashIsNeeded
// is important #ifdef ANDROID_LIBRARY
// because they must be reset on each run of the library!
bJustOneHashIsNeeded=FALSE;
for (i=0; i<SIZE_OF_TRIOPS_VERSIONS_ENUM; i++)
bHashAlreadyObtained[i]=FALSE;
int opt = 0;
#ifdef ANDROID_LIBRARY
optind = 0; // *must* be reset in order to use getopt() in a .so lib (#ifdef ANDROID_LIBRARY)
#endif
// options:
// * key: from keyboard (k), from cmdline (p), from file (P)
// * output file (o) [optional if decrypting and input is not stdin]
// * encryption (and method chosen) (e) [decryption, if not present]
// * decryption [optional, as it is the defaut action] (d)
// * store (1; or not: 0) the hash to verify the encryted file (H)
// * metadata location selection 'head' or 'tail' (m)
// * file(s) to encrypt/decrypt
while ((opt = getopt(argc, argv, "hkp:P:i:o:Oe:dHbm:")) != -1) {
switch(opt) {
// help
case 'h':
print_help();
return 1;
case 'k':
bObligatoryKey=TRUE;
break;
case 'p':
bObligatoryKey=TRUE;
bExplicitPassword=TRUE;
strcpy (szPassFile, optarg);
break;
case 'P':
bObligatoryKey=TRUE;
strcpy (szPassFile, optarg);
break;
case 'i':
// explicitely indicated input file
strcpy (szFile, optarg);
break;
case 'o':
bOutputToTheSameFile=FALSE;
strcpy (szNewFile, optarg);
break;
case 'O':
bStdoutOutput=TRUE;
break;
case 'e':
bEncrypt=TRUE;
if (strcmp(optarg, "3")!=0) {
fprintf (stderr, "Only '-e 3' is actually accepted ('%s' found)\n", optarg);
return 1;
}
break;
case 'd':
bEncrypt=FALSE;
break;
case 'H':
bDoNotStorePasswordHash=TRUE;
break;
case 'b':
bBreakOnFirstError=TRUE;
break;
case 'm':
if (strcmp(optarg, "head")==0) {
iUseSelectedMetadata=1; // head
break;
} else {
if (strcmp(optarg, "tail")==0) {
iUseSelectedMetadata=2; // tail
break;
}
}
fprintf (stderr, "-m accepts only 'head' and 'tail' options\n");
return 1;
case '?':
#ifndef ANDROID_LIBRARY
if (isprint (optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
//break;
fprintf (stderr, "Command aborted\n");
#endif
return 1;
default:
return 1;
}
}
if (bObligatoryKey==FALSE) {
fprintf (stderr, "ERROR: Key absent: it is obligatory to indicate a key for encryption/decryption.\n");
return 1;
}
// decryption reads file's own metadata, so no choice is possible
if (!bEncrypt && iUseSelectedMetadata!=0) {
fprintf (stderr, "warning: -m option ignored when decrypting.\n");
iUseSelectedMetadata=0;
}
// it is not possible to choose both stdout and file output simultaneously
if (bStdoutOutput==TRUE && strlen(szNewFile)!=0) {
fprintf (stderr, "ERROR: if stdout output is selected, no output file can be indicated.\n");
return 1;
}
// if -i indicated, check here its length:
if ( strlen(szFile) >= MAX_PATH ) {
fprintf (stderr, "\nFile not processed: path is too long for '%s'.\n", szFile);
return 1;
}
// exclude keyboard if stdin or stdout have been selected:
/*if ( ((optind==argc && strlen(szFile)==0) || // stdin
bStdoutOutput==TRUE) && // stdout
strlen(szPassFile)==0 ) // keyboard password indicated
{
fprintf (stderr, "Password cannot be read from keyboard when using stdin or stdout.\n"
"Process aborted.\n");
return 1;
}*/
// set bOutputToTheSameFile=FALSE with stdout selected:
if (bStdoutOutput) {
// with stdin as input, output *cannot* be stdin ...
bOutputToTheSameFile=FALSE;
}
// if stdin, some output must explicitely be indicated
if ((optind==argc && strlen(szFile)==0) && // stdin
bOutputToTheSameFile)
{
fprintf (stderr, "ERROR: with stdin, some output must be indicated ('-o <file>' or '-O').\n");
return 1;
}
// discard simultaneous -i <file> and ... <file> options of input files
if ( ((optind+1)<=argc) && // 1 or more files after options
strlen(szFile)!=0 ) // and -i <file> has been indicated
{
fprintf (stderr, "ERROR: It is not possible to indicate files \n"
"\tsimultaneously with '-i' and after options.\n"
"Process aborted.\n");
return 1;
}
// If multiple file inputs are indicated, no single output file can be present
// (whether it is sdtout or not)
// as output will be the (mangled) concatenation of each one of them.
if (bOutputToTheSameFile==FALSE && (optind+1)<argc) {
fprintf (stderr, "ERROR: When multiple input files are indicated they'll be overwritten\n"
"\tso a single output file is invalid.\n"
"Process aborted.\n");
return 1;
}
// from now on, no more errors from bad combination of parameters are allowed:
// just processing:
// optimization for the case when there's no need to obtain
// multiple hashes on the fly from a hashed password file:
// obtain just the needed one:
if (bEncrypt) { // encrypting, and so triopsVersion is fixed and known
bJustOneHashIsNeeded=TRUE;
}
// note: another optimization with bJustCalculate later with !bEncrypt && !bStdinInput
// obtain password from keyboard:
if (bExplicitPassword==FALSE &&
strlen(szPassFile)==0) {
if (!obtainPasswordFromKeyboard(szPassFile)){
fprintf (stderr, "ERROR: could not obtain password from keyboard.\nProcess aborted.\n");
return 1;
}
bExplicitPassword=TRUE;
}
// from now on only options are file to be hashed or explicit password from comdline
// needed by createIV_v3
srand((unsigned) time(NULL));
// note:
// set output value and break to let the return be done later,
// because an EliminatePassword() call is convenient before exit.
if (optind == argc) {
// ++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++
// if no additional arguments are present
// file input is stdin or just one file (-i)
// ++++++++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++++++++
if (strlen(szFile)==0) { // stdin
// a strlen(szFile)==0 marks input as stdin for process_file()
szFile[0]=0x0; // superfluous
// with stdin as input, output *cannot* be stdin ...
bOutputToTheSameFile=FALSE;
// mark input for type guessing from correct head metadata later:
// as there's no file extension with stdin, file encryption type is unknown
if (!bEncrypt)
triopsVersion=TRIOPS_VERSION_UNKNOWN;
} else {
// there's just one file to process:
// decrypting: so triops Version may be deduced from extension:
if (!bEncrypt) {
if (strlen(szFile)>4 &&
strcmp( szFile+(strlen(szFile)-strlen(TRIOPS_V3_EXTENSION)), TRIOPS_V3_EXTENSION ) == 0)
triopsVersion=TRIOPS_V3;
else
triopsVersion=TRIOPS_VERSION_UNKNOWN;
}
// optimization for the case when there's no need to obtain
// multiple hashes on the fly from a hashed password file:
// obtain just the needed one:
if (!bEncrypt && // decrypting, and
triopsVersion!=TRIOPS_VERSION_UNKNOWN // AND triopsVersion is known
)
bJustOneHashIsNeeded=TRUE;
}
// if (bEncrypt) delay obtainPassword() to group the use of that function on process_file()
// if (!bEncrypt) BUT password cannot be hashed yet because with stdin
// triopsVersion must be dynamically determined from input content
// so let's copy the password/path and delay this to process_file()
memcpy(szPass, szPassFile, MAX_PASSWORD_LENGTH);
// password isn't needed anymore: overwrite variable as a paranoic security measure:
EliminatePassword(szPassFile, MAX_PASSWORD_LENGTH);
if ( process_file( szFile, szNewFile, szPass, bEncrypt,
bExplicitPassword, bOutputToTheSameFile, bDoNotStorePasswordHash,
iUseSelectedMetadata ) != 0
) {
if (strlen(szFile)==0) {
fprintf (stderr, "ERROR processing stdin\n");
} else {
fprintf (stderr, "ERROR processing '%s'\n", szFile);
}
output=1; // error
} else
output=0;
} else { // if (optind == argc)
for (i = optind; i < argc; i++) {
if ( strlen(argv[i]) < MAX_PATH ) {
strcpy (szFile, argv[i]);
// decrypting: so triops Version may be deduced from extension:
if (!bEncrypt) {
if (strlen(szFile)>4 &&
strcmp( szFile+(strlen(szFile)-strlen(TRIOPS_V3_EXTENSION)), TRIOPS_V3_EXTENSION ) == 0)
triopsVersion=TRIOPS_V3;
else {
/*fprintf (stderr, "\nFile not processed:\nDecryption format could not be deduced from file extension: %s\n", szFile);
if (bBreakOnFirstError==TRUE) {
output=1;
break;
} else
continue;*/
triopsVersion=TRIOPS_VERSION_UNKNOWN;
}
}
// optimization for the case when there's no need to obtain
// multiple hashes on the fly from a hashed password file:
// obtain just the needed one:
if (!bEncrypt && // decrypting, and
(optind+1) == argc && // there's just one input file to decrypt (and it is not stdin)
triopsVersion!=TRIOPS_VERSION_UNKNOWN // AND triopsVersion is known
) {
bJustOneHashIsNeeded=TRUE;
}
// delay obtainPassword() to group the use of that function on process_file()
// even if triopsVersion has already been determined
memcpy(szPass, szPassFile, MAX_PASSWORD_LENGTH);
output=process_file( szFile, szNewFile, szPass, bEncrypt,
bExplicitPassword, bOutputToTheSameFile, bDoNotStorePasswordHash,
iUseSelectedMetadata );
} else {
fprintf (stderr, "\nFile not processed: path is too long for '%s'.\n", argv[i]);
if (bBreakOnFirstError==TRUE) {
output=1; // error
break;
} else
continue;
}
if (output!=0) {
if (bBreakOnFirstError==FALSE) {
// print warning, but continue processing next files:
fprintf (stderr, "ERROR processing '%s'.\n", szFile);
output=0; // clear output for next file to process
} else
break;
}
}
}
// password hash isn't needed anymore: overwrite variable as a paranoic security measure:
EliminatePassword(szPass, MAX_PASSWORD_LENGTH);
EliminatePassword(szPassFile, MAX_PASSWORD_LENGTH);
// password isn't needed anymore: overwrite variable as a paranoic security measure:
for (i=0; i<SIZE_OF_TRIOPS_VERSIONS_ENUM; i++) {
EliminatePassword(szHashAlreadyObtained[i], MAX_PASSWORD_LENGTH);
}
return output;
}
int
process_file( char *szFile, char *szNewFile, char *szPass, BOOL bEncrypt,
BOOL bExplicitPassword, BOOL bOutputToTheSameFile, BOOL bDoNotStorePasswordHash,
int iUseSelectedMetadata )
{
//char szFile [MAX_PATH]; // defined as parameter
//char szNewFile [MAX_PATH]; // defined as parameter
//char szPass [MAX_PASSWORD_LENGTH]; // defined as parameter
//BOOL bEncrypt; // defined as parameter
//BOOL bDoNotStorePasswordHash; // defined as parameter
//BOOL bOutputToTheSameFile; // defined as parameter
unsigned long long nBytesSoFar;
unsigned long long nBytesRead;
unsigned long long nBytesRead2; // v9.0
FILE * hFile;
FILE * hFileOut;
BYTE cFileBuffer [BUFFERSIZE]; // v9.0: constant pointer renamed
BYTE cFileBuffer2[BUFFERSIZE]; // v9.0
BYTE * lpFileBuffer; // v9.0: use intermediate pointers to speed buffer swapping
BYTE * lpFileBuffer2; // v9.0
// v9.0: make cMetadata[] size as big as the bigger metadata
// from all encryption methods.
BYTE cMetadata [HASHSIZE_v3+IVSIZE_v3]; // v9.0
int i;
int iError; // to store FSEEK or other function results
unsigned long long lFileSize=0; // show progress bar
BOOL bStdinInput=FALSE; // v9.0
BOOL bStdoutOutput=FALSE; // v9.0
BOOL bProgressBar; // show progress bar
float fBlockSize; // show progress bar
#ifndef WINDOWS_PLATFORM
struct utimbuf stTimes;
#else
FILETIME lLastWriteTime;
#endif
// v9.0: use head or tail metadata: bUsingHeadMetadata
BOOL bUsingHeadMetadata=FALSE;
triops_Versions_Constants triopsVersionOriginal;
BOOL bContinueLoop;
BYTE lpEncrypted [BUFFERSIZE];
unsigned long long lBlockTotal; // counts total number of <=BUFFERSIZE blocks in hFileOut
unsigned long long lBlockNumber; // counts number of <=BUFFERSIZE blocks processed in hFileOut
unsigned long long lSubtrahend; // bytes to delete from last file block, as they're tail, not data.
unsigned long long lMetadataSize; // size of the Metadata (head or tail) added to encrypted files
// CHACHA20 + KECCAK-512
union unionIV_v3 iv_v3; // IV for v3 format (CHACHA20+KECCAK-512)
union KEY_v3 uniqueKey_v3; // KEY for v3 format (CHACHA20+KECCAK-512)
union HASHEDKEY_v3 hashedKey_v3, key_v3;//HASH for v3 format (CHACHA20+KECCAK-512)
ECRYPT_ctx chacha_ctx; // CHACHA20
#ifdef ANDROID_LIBRARY
if (szFile[0] != '/') { // security measure
fprintf (stderr, "\nPath to file not valid: '%s'.\n\n", szFile);
return 2;
}
#endif
// if output is to the same file, modification timestamp is preserved
if (bOutputToTheSameFile) {
#ifndef WINDOWS_PLATFORM
stTimes.modtime=obtainTimestampUnix(szFile, obtainTimestamp_ST_MTIME);
//stTimes.actime=time(NULL); // access time, to actual date-time: done at the end.
#else
obtainTimestampWin(szFile, &lLastWriteTime);
#endif
}
// open the file
if (bOutputToTheSameFile) {
hFile = fopen(szFile, "r+b" );
} else {
if (strlen(szFile)==0) { // v9.0
// input is stdin
bStdinInput=TRUE;
SET_BINARY_MODE(STDIN); // sets binary mode for stdin in Windows
hFile = stdin;
} else {
hFile = fopen(szFile, "rb" );
}
}
if (hFile == NULL)
{
if (!bStdinInput) { // v9.0
fprintf (stderr, "\nError opening '%s'.\n\n", szFile);
} else {
fprintf (stderr, "\nError opening stdin.\n\n");
}
return 1;
}
// v9.0:
if (strlen(szNewFile)==0) {
bStdoutOutput=TRUE;
}
// encrypting:
// Add encrypted file extension to file's name if we're written to another file.
// If we're written to the same file, this process is made at the end.
if (bEncrypt && !bOutputToTheSameFile) {
if (!bStdoutOutput) {
szNewFile[strlen(szNewFile)+strlen(TRIOPS_GENERIC_EXTENSION)]=0x0; // the end of string after the extension addition
/*if (triopsVersion==TRIOPS_V3)
memcpy(szNewFile+strlen(szNewFile), TRIOPS_V3_EXTENSION, 4);*/
memcpy(szNewFile+strlen(szNewFile), TRIOPS_GENERIC_EXTENSION, strlen(TRIOPS_GENERIC_EXTENSION));
}
}
// encrypting/decrypting to a new file:
// check that destination file does not exist yet (do not overwrite in that case):
if (!bOutputToTheSameFile) {
if (!bStdoutOutput) {
hFileOut = fopen(szNewFile, "rb" );
if (hFileOut != NULL)
{
fprintf (stderr, "\nError: Destination file already exists: '%s'\n"
"\tProcess aborted (nothing has been done).\n\n", szNewFile);
fclose(hFileOut);
return 1;
}
// once checked that destination file doesn't exist, open said destination file:
// moved AFTER password has been checked, not to create a superfluous empty file.
/*hFileOut = fopen(szNewFile, "wb" );
if (hFileOut == NULL)
{
fprintf (stderr, "\nError opening %s\n\n", szNewFile);
return 1;
}*/
}
}
else
{
// encrypting/decrypting to the "same" file:
// check that destination file does not exist yet (do not overwrite in that case):
char szDestinationFile [MAX_PATH];
strcpy(szDestinationFile, szFile);
if (!bEncrypt) {
// !bEncrypt && bOutputToTheSameFile
// well, here, the subtraction should depend on original file extension:
// nonetheless, as all extensions used by now have the same length (3+1=4), I
// use -strlen(TRIOPS_GENERIC_EXTENSION) here:
szDestinationFile[strlen(szDestinationFile)-strlen(TRIOPS_GENERIC_EXTENSION)]=0x0;
} else {
// bEncrypt && bOutputToTheSameFile
szDestinationFile[strlen(szDestinationFile)+strlen(TRIOPS_GENERIC_EXTENSION)]=0x0;
/*if (triopsVersion==TRIOPS_V3)
memcpy(szDestinationFile+strlen(szDestinationFile), TRIOPS_V3_EXTENSION, 4);*/
memcpy(szDestinationFile+strlen(szDestinationFile), TRIOPS_GENERIC_EXTENSION, strlen(TRIOPS_GENERIC_EXTENSION));
}
// check that destination file does not exist yet (do not overwrite in that case):
hFileOut = fopen(szDestinationFile, "rb" );
if (hFileOut != NULL)
{
fprintf (stderr, "\nError: Destination file exists: '%s'\n"
"\tProcess aborted (nothing has been done).\n\n", szDestinationFile );
fclose(hFileOut);
return 1;
}
}
// this is not needed because a hFileOut!=NULL would have trigger a fclose && return
//fclose(hFileOut);
// Load the IV and Hash from file
// using bUsingHeadMetadata to point to the right location of both:
// This is needed from v9.0 on, as tail or head can be used to store metadata,
// and will be the checking of the password hash hint the only way to determine
// which of them were used to store the metadata.
triopsVersionOriginal=triopsVersion;
if (triopsVersion==TRIOPS_VERSION_UNKNOWN) {
// tentatively check versions
triopsVersion=TRIOPS_V3;
}
if (!bEncrypt) {
// first check Head just to be able to use stdin as input
bContinueLoop=TRUE;
bUsingHeadMetadata=TRUE;
do {
// Load the IV and Hash from file
if (bStdinInput) {
unsigned long long nBytesRead;
unsigned long long nActualMetadataSize;
// input is stdin, so file type must be tentatively guessed
// from smaller to bigger metadata sizes whilst not moving fseek too far away...
if (triopsVersion==TRIOPS_V3) {
nActualMetadataSize = HASHSIZE_v3 + IVSIZE_v3;
nBytesRead = fread (cMetadata, 1, nActualMetadataSize, hFile);
}
// error checking:
// we're not going to raise error, as this cases can occur with small files
// when checking first for bUsingHeadMetadata, but metadata in in tail...
/*if (nBytesRead < nActualMetadataSize) {
fprintf(stderr, "Error while processing encrypted stdin: input size too small.\n"
"Process aborted.\n");
return EXIT_FAILURE;
}*/
// now, point IV and hashedKey to each proper data
if (triopsVersion==TRIOPS_V3) {
memcpy(iv_v3.byteIV,((union unionIV_v3 *)cMetadata)->byteIV,IVSIZE_v3);
memcpy(hashedKey_v3.keyB,((union HASHEDKEY_v3 *)(cMetadata+IVSIZE_v3))->keyB,HASHSIZE_v3);
}
} else { // if (bStdinInput)
if (triopsVersion==TRIOPS_V3)
LoadIVandHash_v3 (hFile, iv_v3.byteIV, hashedKey_v3.keyB, szFile, bUsingHeadMetadata);
}
// calculate hash password with actual triopsVersion,
if (!bHashAlreadyObtained[triopsVersion]) {
if (
!obtainPassword(szPass, szHashAlreadyObtained[triopsVersion], bExplicitPassword)
) {
fprintf(stderr, "ERROR: Could not obtain password.\nProcess aborted.\n\n");
// close input file:
fclose(hFile);
// and say goodbye :-(
return 1;
}
}
// check if password is valid for the head|tail read
if (triopsVersion==TRIOPS_V3)
// IN: szPass, lpIV, lpHashedKey (read from file); OUT: lpKey (for decrypting)
iError=CheckKeyIsValid_v3 ( szHashAlreadyObtained[triopsVersion],
key_v3.keyB, iv_v3.byteIV, hashedKey_v3.keyW, FALSE );
// if check is ok, continue; if not, read the other Metadata location and end here again:
if (iError == CheckKeyIsValid_FALSE) {
if (!bStdinInput) {
if (triopsVersionOriginal==TRIOPS_VERSION_UNKNOWN) {
if (triopsVersion==TRIOPS_V3) {
bUsingHeadMetadata=!bUsingHeadMetadata;
if (bUsingHeadMetadata==TRUE)
// TRIOPS_V3 is the last version type to check and head and tail were already checked:
bContinueLoop=FALSE;
}
} else {
bUsingHeadMetadata=!bUsingHeadMetadata;
if (bUsingHeadMetadata==TRUE)
bContinueLoop=FALSE;
}
} else {
// bStdinInput:
// note that only bUsingHeadMetadata==TRUE is possible with bStdinInput
if (triopsVersion==TRIOPS_V3)
// TRIOPS_V3 is the last version type to check
bContinueLoop=FALSE;
}
}
if (iError == CheckKeyIsValid_TRUE_BUT_EMPTY ||
iError == CheckKeyIsValid_TRUE)
break; // bUsingHeadMetadata value MUST be conserved!
} while (bContinueLoop);
switch (iError) {
case CheckKeyIsValid_FALSE:
if (!bStdinInput)
fprintf (stderr, "\nerror: file '%s' didn't pass password hint checking.\n\n", szFile);
else
fprintf (stderr, "\nerror: stdin input didn't pass password hint checking\n"
"or input is not suitable for decrypting from stdin.\n");
// close input file:
fclose(hFile);
return 1;
case CheckKeyIsValid_TRUE_BUT_EMPTY:
if (!bStdinInput)
fprintf (stderr, "\nwarning: file '%s' decrypted without password hint checking.\n", szFile);
else
fprintf (stderr, "\nwarning: decrypting stdin input without password hint checking.\n");
// correct, continue
//break; // NO!: check first also next case for bStdinInput, triopsVersion==TRIOPS_V2
case CheckKeyIsValid_TRUE:
if (bStdinInput) {
// reestablish correct value with stdin: *must* be Head metadata always... (if encrypting)
bUsingHeadMetadata=TRUE;
}
break;
}
} else { // if (!bEncrypt) {
// calculate hash password with actual triopsVersion,
if (!bHashAlreadyObtained[triopsVersion]) {
if (
!obtainPassword(szPass, szHashAlreadyObtained[triopsVersion], bExplicitPassword)
) {
fprintf(stderr, "ERROR: Could not obtain password.\nProcess aborted.\n\n");
// close input file:
fclose(hFile);
// and say goodbye :-(
return 1;
}
}
// if encrypting, then password hash and IV must be created:
// CheckKeyIsValid returns in hashedKey.keyW the hash, if TRUE is passed as last argument:
if (triopsVersion==TRIOPS_V3) {
createIV_v3 (&iv_v3.iv, szFile);
/* DEBUG: check value:*/
/*fprintf (stderr, "IV: ");
for (i=0; i<2; i++) fprintf (stderr, " %08lx",((DWORD *)&iv_v3.iv)[i]);*/
// IN: szPass, lpIV; OUT: lpKey (for encrypting), lpHashedKey (for writing to file)
CheckKeyIsValid_v3 ( szHashAlreadyObtained[triopsVersion],
key_v3.keyB, iv_v3.byteIV, hashedKey_v3.keyW, TRUE );
}
// in v9.0 metadata is in head by default
bUsingHeadMetadata=TRUE;
if (iUseSelectedMetadata != 0) {
if (iUseSelectedMetadata==2) {
bUsingHeadMetadata=FALSE;
}
}
} // if (!bEncrypt)
// AFTER password has been checked, (not to create a superfluous empty file), and
// once checked that destination file doesn't exist (upper code), open said destination file:
if (!bOutputToTheSameFile) {
if (!bStdoutOutput) {
hFileOut = fopen(szNewFile, "wb" );
} else {
SET_BINARY_MODE(STDOUT); // sets binary mode for stdout in Windows
hFileOut = stdout;
}
if (hFileOut == NULL)
{
fprintf (stderr, "\nError opening '%s'.\n\n", szNewFile);
return 1;
}
}
// use the IV to create a unique key for this file
if (triopsVersion==TRIOPS_V3) {
CreateUniqueKey_v3 (uniqueKey_v3.keyW, key_v3.keyB, &(iv_v3.iv));
// it is not necessary to make a copy of the original IV, as CHACHA20 uses it as const *
// memcpy(chacha20_iv, iv_v3.byteIV, IVSIZE_v3);
/*fprintf (stderr, "\ncalculated key: ");
for (i=0; i<KEYSIZE_v3/4; i++) fprintf (stderr, " %08lx",uniqueKey_v3.keyW[i]);*/
}
// .................................................
// do CHACHA20 setup:
// .................................................
if (triopsVersion==TRIOPS_V3) {
//ECRYPT_init();
/*
* Key setup. It is the user's responsibility to select the values of
* keysize and ivsize from the set of supported values specified
* above. */
/*
void ECRYPT_keysetup(
ECRYPT_ctx* ctx,
const u8* key,
u32 keysize, // Key size in bits.
u32 ivsize); // IV size in bits.
*/
ECRYPT_keysetup( &chacha_ctx, (u8 *)uniqueKey_v3.keyB, (u32)(KEYSIZE_v3*8), (u32)(IVSIZE_v3*8) );
/*
* IV setup. After having called ECRYPT_keysetup(), the user is
* allowed to call ECRYPT_ivsetup() different times in order to
* encrypt/decrypt different messages with the same key but different
* IV's. */
/*
void ECRYPT_ivsetup(
ECRYPT_ctx* ctx,
const u8* iv);
*/
ECRYPT_ivsetup( &chacha_ctx, (u8 *)iv_v3.byteIV );
}
// .................................................
// .................................................
// preparations to encrypt/decrypt the file
i = 0; // it'll be used as block counter, to show the progress bar.
bProgressBar=FALSE;
// show progress bar only if !bStdinInput: with stdin it's best to remain quiet