1 /*
2  *  \brief  Generic file encryption program using generic wrappers for configured
3  *          security.
4  *
5  *  Copyright The Mbed TLS Contributors
6  *  SPDX-License-Identifier: Apache-2.0
7  *
8  *  Licensed under the Apache License, Version 2.0 (the "License"); you may
9  *  not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  */
20 
21 /* Enable definition of fileno() even when compiling with -std=c99. Must be
22  * set before mbedtls_config.h, which pulls in glibc's features.h indirectly.
23  * Harmless on other platforms. */
24 #define _POSIX_C_SOURCE 200112L
25 
26 #include "mbedtls/build_info.h"
27 
28 #include "mbedtls/platform.h"
29 
30 #if defined(MBEDTLS_CIPHER_C) && defined(MBEDTLS_MD_C) && \
31  defined(MBEDTLS_FS_IO)
32 #include "mbedtls/cipher.h"
33 #include "mbedtls/md.h"
34 #include "mbedtls/platform_util.h"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #endif
40 
41 #if defined(_WIN32)
42 #include <windows.h>
43 #if !defined(_WIN32_WCE)
44 #include <io.h>
45 #endif
46 #else
47 #include <sys/types.h>
48 #include <unistd.h>
49 #endif
50 
51 #define MODE_ENCRYPT    0
52 #define MODE_DECRYPT    1
53 
54 #define USAGE   \
55     "\n  crypt_and_hash <mode> <input filename> <output filename> <cipher> <mbedtls_md> <key>\n" \
56     "\n   <mode>: 0 = encrypt, 1 = decrypt\n" \
57     "\n  example: crypt_and_hash 0 file file.aes AES-128-CBC SHA1 hex:E76B2413958B00E193\n" \
58     "\n"
59 
60 #if !defined(MBEDTLS_CIPHER_C) || !defined(MBEDTLS_MD_C) || \
61     !defined(MBEDTLS_FS_IO)
main(void)62 int main( void )
63 {
64     mbedtls_printf("MBEDTLS_CIPHER_C and/or MBEDTLS_MD_C and/or MBEDTLS_FS_IO not defined.\n");
65     mbedtls_exit( 0 );
66 }
67 #else
68 
69 
main(int argc,char * argv[])70 int main( int argc, char *argv[] )
71 {
72     int ret = 1, i;
73     unsigned n;
74     int exit_code = MBEDTLS_EXIT_FAILURE;
75     int mode;
76     size_t keylen, ilen, olen;
77     FILE *fkey, *fin = NULL, *fout = NULL;
78 
79     char *p;
80     unsigned char IV[16];
81     unsigned char key[512];
82     unsigned char digest[MBEDTLS_MD_MAX_SIZE];
83     unsigned char buffer[1024];
84     unsigned char output[1024];
85     unsigned char diff;
86 
87     const mbedtls_cipher_info_t *cipher_info;
88     const mbedtls_md_info_t *md_info;
89     mbedtls_cipher_context_t cipher_ctx;
90     mbedtls_md_context_t md_ctx;
91 #if defined(_WIN32_WCE)
92     long filesize, offset;
93 #elif defined(_WIN32)
94        LARGE_INTEGER li_size;
95     __int64 filesize, offset;
96 #else
97       off_t filesize, offset;
98 #endif
99 
100     mbedtls_cipher_init( &cipher_ctx );
101     mbedtls_md_init( &md_ctx );
102 
103     /*
104      * Parse the command-line arguments.
105      */
106     if( argc != 7 )
107     {
108         const int *list;
109 
110         mbedtls_printf( USAGE );
111 
112         mbedtls_printf( "Available ciphers:\n" );
113         list = mbedtls_cipher_list();
114         while( *list )
115         {
116             cipher_info = mbedtls_cipher_info_from_type( *list );
117             mbedtls_printf( "  %s\n", mbedtls_cipher_info_get_name( cipher_info ) );
118             list++;
119         }
120 
121         mbedtls_printf( "\nAvailable message digests:\n" );
122         list = mbedtls_md_list();
123         while( *list )
124         {
125             md_info = mbedtls_md_info_from_type( *list );
126             mbedtls_printf( "  %s\n", mbedtls_md_get_name( md_info ) );
127             list++;
128         }
129 
130         goto exit;
131     }
132 
133     mode = atoi( argv[1] );
134 
135     if( mode != MODE_ENCRYPT && mode != MODE_DECRYPT )
136     {
137         mbedtls_fprintf( stderr, "invalid operation mode\n" );
138         goto exit;
139     }
140 
141     if( strcmp( argv[2], argv[3] ) == 0 )
142     {
143         mbedtls_fprintf( stderr, "input and output filenames must differ\n" );
144         goto exit;
145     }
146 
147     if( ( fin = fopen( argv[2], "rb" ) ) == NULL )
148     {
149         mbedtls_fprintf( stderr, "fopen(%s,rb) failed\n", argv[2] );
150         goto exit;
151     }
152 
153     if( ( fout = fopen( argv[3], "wb+" ) ) == NULL )
154     {
155         mbedtls_fprintf( stderr, "fopen(%s,wb+) failed\n", argv[3] );
156         goto exit;
157     }
158 
159     /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
160     mbedtls_setbuf( fin, NULL );
161     mbedtls_setbuf( fout, NULL );
162 
163     /*
164      * Read the Cipher and MD from the command line
165      */
166     cipher_info = mbedtls_cipher_info_from_string( argv[4] );
167     if( cipher_info == NULL )
168     {
169         mbedtls_fprintf( stderr, "Cipher '%s' not found\n", argv[4] );
170         goto exit;
171     }
172     if( ( ret = mbedtls_cipher_setup( &cipher_ctx, cipher_info) ) != 0 )
173     {
174         mbedtls_fprintf( stderr, "mbedtls_cipher_setup failed\n" );
175         goto exit;
176     }
177 
178     md_info = mbedtls_md_info_from_string( argv[5] );
179     if( md_info == NULL )
180     {
181         mbedtls_fprintf( stderr, "Message Digest '%s' not found\n", argv[5] );
182         goto exit;
183     }
184 
185     if( mbedtls_md_setup( &md_ctx, md_info, 1 ) != 0 )
186     {
187         mbedtls_fprintf( stderr, "mbedtls_md_setup failed\n" );
188         goto exit;
189     }
190 
191     /*
192      * Read the secret key from file or command line
193      */
194     if( ( fkey = fopen( argv[6], "rb" ) ) != NULL )
195     {
196         keylen = fread( key, 1, sizeof( key ), fkey );
197         fclose( fkey );
198     }
199     else
200     {
201         if( memcmp( argv[6], "hex:", 4 ) == 0 )
202         {
203             p = &argv[6][4];
204             keylen = 0;
205 
206             while( sscanf( p, "%02X", (unsigned int*) &n ) > 0 &&
207                    keylen < (int) sizeof( key ) )
208             {
209                 key[keylen++] = (unsigned char) n;
210                 p += 2;
211             }
212         }
213         else
214         {
215             keylen = strlen( argv[6] );
216 
217             if( keylen > (int) sizeof( key ) )
218                 keylen = (int) sizeof( key );
219 
220             memcpy( key, argv[6], keylen );
221         }
222     }
223 
224 #if defined(_WIN32_WCE)
225     filesize = fseek( fin, 0L, SEEK_END );
226 #else
227 #if defined(_WIN32)
228     /*
229      * Support large files (> 2Gb) on Win32
230      */
231     li_size.QuadPart = 0;
232     li_size.LowPart  =
233         SetFilePointer( (HANDLE) _get_osfhandle( _fileno( fin ) ),
234                         li_size.LowPart, &li_size.HighPart, FILE_END );
235 
236     if( li_size.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
237     {
238         mbedtls_fprintf( stderr, "SetFilePointer(0,FILE_END) failed\n" );
239         goto exit;
240     }
241 
242     filesize = li_size.QuadPart;
243 #else
244     if( ( filesize = lseek( fileno( fin ), 0, SEEK_END ) ) < 0 )
245     {
246         perror( "lseek" );
247         goto exit;
248     }
249 #endif
250 #endif
251 
252     if( fseek( fin, 0, SEEK_SET ) < 0 )
253     {
254         mbedtls_fprintf( stderr, "fseek(0,SEEK_SET) failed\n" );
255         goto exit;
256     }
257 
258     if( mode == MODE_ENCRYPT )
259     {
260         /*
261          * Generate the initialization vector as:
262          * IV = MD( filesize || filename )[0..15]
263          */
264         for( i = 0; i < 8; i++ )
265             buffer[i] = (unsigned char)( filesize >> ( i << 3 ) );
266 
267         p = argv[2];
268 
269         if( mbedtls_md_starts( &md_ctx ) != 0 )
270         {
271             mbedtls_fprintf( stderr, "mbedtls_md_starts() returned error\n" );
272             goto exit;
273         }
274         if( mbedtls_md_update( &md_ctx, buffer, 8 ) != 0 )
275         {
276             mbedtls_fprintf( stderr, "mbedtls_md_update() returned error\n" );
277             goto exit;
278         }
279         if( mbedtls_md_update( &md_ctx, ( unsigned char * ) p, strlen( p ) )
280             != 0 )
281         {
282             mbedtls_fprintf( stderr, "mbedtls_md_update() returned error\n" );
283             goto exit;
284         }
285         if( mbedtls_md_finish( &md_ctx, digest ) != 0 )
286         {
287             mbedtls_fprintf( stderr, "mbedtls_md_finish() returned error\n" );
288             goto exit;
289         }
290 
291         memcpy( IV, digest, 16 );
292 
293         /*
294          * Append the IV at the beginning of the output.
295          */
296         if( fwrite( IV, 1, 16, fout ) != 16 )
297         {
298             mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", 16 );
299             goto exit;
300         }
301 
302         /*
303          * Hash the IV and the secret key together 8192 times
304          * using the result to setup the AES context and HMAC.
305          */
306         memset( digest, 0,  32 );
307         memcpy( digest, IV, 16 );
308 
309         for( i = 0; i < 8192; i++ )
310         {
311             if( mbedtls_md_starts( &md_ctx ) != 0 )
312             {
313                 mbedtls_fprintf( stderr,
314                                  "mbedtls_md_starts() returned error\n" );
315                 goto exit;
316             }
317             if( mbedtls_md_update( &md_ctx, digest, 32 ) != 0 )
318             {
319                 mbedtls_fprintf( stderr,
320                                  "mbedtls_md_update() returned error\n" );
321                 goto exit;
322             }
323             if( mbedtls_md_update( &md_ctx, key, keylen ) != 0 )
324             {
325                 mbedtls_fprintf( stderr,
326                                  "mbedtls_md_update() returned error\n" );
327                 goto exit;
328             }
329             if( mbedtls_md_finish( &md_ctx, digest ) != 0 )
330             {
331                 mbedtls_fprintf( stderr,
332                                  "mbedtls_md_finish() returned error\n" );
333                 goto exit;
334             }
335 
336         }
337 
338         if( mbedtls_cipher_setkey( &cipher_ctx,
339                                    digest,
340                                    (int) mbedtls_cipher_info_get_key_bitlen( cipher_info ),
341                            MBEDTLS_ENCRYPT ) != 0 )
342         {
343             mbedtls_fprintf( stderr, "mbedtls_cipher_setkey() returned error\n");
344             goto exit;
345         }
346         if( mbedtls_cipher_set_iv( &cipher_ctx, IV, 16 ) != 0 )
347         {
348             mbedtls_fprintf( stderr, "mbedtls_cipher_set_iv() returned error\n");
349             goto exit;
350         }
351         if( mbedtls_cipher_reset( &cipher_ctx ) != 0 )
352         {
353             mbedtls_fprintf( stderr, "mbedtls_cipher_reset() returned error\n");
354             goto exit;
355         }
356 
357         if( mbedtls_md_hmac_starts( &md_ctx, digest, 32 ) != 0 )
358         {
359             mbedtls_fprintf( stderr, "mbedtls_md_hmac_starts() returned error\n" );
360             goto exit;
361         }
362 
363         /*
364          * Encrypt and write the ciphertext.
365          */
366         for( offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size( &cipher_ctx ) )
367         {
368             ilen = ( (unsigned int) filesize - offset > mbedtls_cipher_get_block_size( &cipher_ctx ) ) ?
369                 mbedtls_cipher_get_block_size( &cipher_ctx ) : (unsigned int) ( filesize - offset );
370 
371             if( fread( buffer, 1, ilen, fin ) != ilen )
372             {
373                 mbedtls_fprintf( stderr, "fread(%ld bytes) failed\n", (long) ilen );
374                 goto exit;
375             }
376 
377             if( mbedtls_cipher_update( &cipher_ctx, buffer, ilen, output, &olen ) != 0 )
378             {
379                 mbedtls_fprintf( stderr, "mbedtls_cipher_update() returned error\n");
380                 goto exit;
381             }
382 
383             if( mbedtls_md_hmac_update( &md_ctx, output, olen ) != 0 )
384             {
385                 mbedtls_fprintf( stderr, "mbedtls_md_hmac_update() returned error\n" );
386                 goto exit;
387             }
388 
389             if( fwrite( output, 1, olen, fout ) != olen )
390             {
391                 mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
392                 goto exit;
393             }
394         }
395 
396         if( mbedtls_cipher_finish( &cipher_ctx, output, &olen ) != 0 )
397         {
398             mbedtls_fprintf( stderr, "mbedtls_cipher_finish() returned error\n" );
399             goto exit;
400         }
401         if( mbedtls_md_hmac_update( &md_ctx, output, olen ) != 0 )
402         {
403             mbedtls_fprintf( stderr, "mbedtls_md_hmac_update() returned error\n" );
404             goto exit;
405         }
406 
407         if( fwrite( output, 1, olen, fout ) != olen )
408         {
409             mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
410             goto exit;
411         }
412 
413         /*
414          * Finally write the HMAC.
415          */
416         if( mbedtls_md_hmac_finish( &md_ctx, digest ) != 0 )
417         {
418             mbedtls_fprintf( stderr, "mbedtls_md_hmac_finish() returned error\n" );
419             goto exit;
420         }
421 
422         if( fwrite( digest, 1, mbedtls_md_get_size( md_info ), fout ) != mbedtls_md_get_size( md_info ) )
423         {
424             mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", mbedtls_md_get_size( md_info ) );
425             goto exit;
426         }
427     }
428 
429     if( mode == MODE_DECRYPT )
430     {
431         /*
432          *  The encrypted file must be structured as follows:
433          *
434          *        00 .. 15              Initialization Vector
435          *        16 .. 31              Encrypted Block #1
436          *           ..
437          *      N*16 .. (N+1)*16 - 1    Encrypted Block #N
438          *  (N+1)*16 .. (N+1)*16 + n    Hash(ciphertext)
439          */
440         if( filesize < 16 + mbedtls_md_get_size( md_info ) )
441         {
442             mbedtls_fprintf( stderr, "File too short to be encrypted.\n" );
443             goto exit;
444         }
445 
446         if( mbedtls_cipher_get_block_size( &cipher_ctx ) == 0 )
447         {
448             mbedtls_fprintf( stderr, "Invalid cipher block size: 0. \n" );
449             goto exit;
450         }
451 
452         /*
453          * Check the file size.
454          */
455         if( mbedtls_cipher_info_get_mode( cipher_info ) != MBEDTLS_MODE_GCM &&
456             ( ( filesize - mbedtls_md_get_size( md_info ) ) %
457                 mbedtls_cipher_get_block_size( &cipher_ctx ) ) != 0 )
458         {
459             mbedtls_fprintf( stderr, "File content not a multiple of the block size (%u).\n",
460                      mbedtls_cipher_get_block_size( &cipher_ctx ));
461             goto exit;
462         }
463 
464         /*
465          * Subtract the IV + HMAC length.
466          */
467         filesize -= ( 16 + mbedtls_md_get_size( md_info ) );
468 
469         /*
470          * Read the IV and original filesize modulo 16.
471          */
472         if( fread( buffer, 1, 16, fin ) != 16 )
473         {
474             mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", 16 );
475             goto exit;
476         }
477 
478         memcpy( IV, buffer, 16 );
479 
480         /*
481          * Hash the IV and the secret key together 8192 times
482          * using the result to setup the AES context and HMAC.
483          */
484         memset( digest, 0,  32 );
485         memcpy( digest, IV, 16 );
486 
487         for( i = 0; i < 8192; i++ )
488         {
489             if( mbedtls_md_starts( &md_ctx ) != 0 )
490             {
491                 mbedtls_fprintf( stderr, "mbedtls_md_starts() returned error\n" );
492                 goto exit;
493             }
494             if( mbedtls_md_update( &md_ctx, digest, 32 ) != 0 )
495             {
496                 mbedtls_fprintf( stderr, "mbedtls_md_update() returned error\n" );
497                 goto exit;
498             }
499             if( mbedtls_md_update( &md_ctx, key, keylen ) != 0 )
500             {
501                 mbedtls_fprintf( stderr, "mbedtls_md_update() returned error\n" );
502                 goto exit;
503             }
504             if( mbedtls_md_finish( &md_ctx, digest ) != 0 )
505             {
506                 mbedtls_fprintf( stderr, "mbedtls_md_finish() returned error\n" );
507                 goto exit;
508             }
509         }
510 
511         if( mbedtls_cipher_setkey( &cipher_ctx,
512                                    digest,
513                                    (int) mbedtls_cipher_info_get_key_bitlen( cipher_info ),
514                            MBEDTLS_DECRYPT ) != 0 )
515         {
516             mbedtls_fprintf( stderr, "mbedtls_cipher_setkey() returned error\n" );
517             goto exit;
518         }
519 
520         if( mbedtls_cipher_set_iv( &cipher_ctx, IV, 16 ) != 0 )
521         {
522             mbedtls_fprintf( stderr, "mbedtls_cipher_set_iv() returned error\n" );
523             goto exit;
524         }
525 
526         if( mbedtls_cipher_reset( &cipher_ctx ) != 0 )
527         {
528             mbedtls_fprintf( stderr, "mbedtls_cipher_reset() returned error\n" );
529             goto exit;
530         }
531 
532         if( mbedtls_md_hmac_starts( &md_ctx, digest, 32 ) != 0 )
533         {
534             mbedtls_fprintf( stderr, "mbedtls_md_hmac_starts() returned error\n" );
535             goto exit;
536         }
537 
538         /*
539          * Decrypt and write the plaintext.
540          */
541         for( offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size( &cipher_ctx ) )
542         {
543             ilen = ( (unsigned int) filesize - offset > mbedtls_cipher_get_block_size( &cipher_ctx ) ) ?
544                 mbedtls_cipher_get_block_size( &cipher_ctx ) : (unsigned int) ( filesize - offset );
545 
546             if( fread( buffer, 1, ilen, fin ) != ilen )
547             {
548                 mbedtls_fprintf( stderr, "fread(%u bytes) failed\n",
549                     mbedtls_cipher_get_block_size( &cipher_ctx ) );
550                 goto exit;
551             }
552 
553             if( mbedtls_md_hmac_update( &md_ctx, buffer, ilen ) != 0 )
554             {
555                 mbedtls_fprintf( stderr, "mbedtls_md_hmac_update() returned error\n" );
556                 goto exit;
557             }
558             if( mbedtls_cipher_update( &cipher_ctx, buffer, ilen, output,
559                                        &olen ) != 0 )
560             {
561                 mbedtls_fprintf( stderr, "mbedtls_cipher_update() returned error\n" );
562                 goto exit;
563             }
564 
565             if( fwrite( output, 1, olen, fout ) != olen )
566             {
567                 mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
568                 goto exit;
569             }
570         }
571 
572         /*
573          * Verify the message authentication code.
574          */
575         if( mbedtls_md_hmac_finish( &md_ctx, digest ) != 0 )
576         {
577             mbedtls_fprintf( stderr, "mbedtls_md_hmac_finish() returned error\n" );
578             goto exit;
579         }
580 
581         if( fread( buffer, 1, mbedtls_md_get_size( md_info ), fin ) != mbedtls_md_get_size( md_info ) )
582         {
583             mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", mbedtls_md_get_size( md_info ) );
584             goto exit;
585         }
586 
587         /* Use constant-time buffer comparison */
588         diff = 0;
589         for( i = 0; i < mbedtls_md_get_size( md_info ); i++ )
590             diff |= digest[i] ^ buffer[i];
591 
592         if( diff != 0 )
593         {
594             mbedtls_fprintf( stderr, "HMAC check failed: wrong key, "
595                              "or file corrupted.\n" );
596             goto exit;
597         }
598 
599         /*
600          * Write the final block of data
601          */
602         if( mbedtls_cipher_finish( &cipher_ctx, output, &olen ) != 0 )
603         {
604             mbedtls_fprintf( stderr, "mbedtls_cipher_finish() returned error\n" );
605             goto exit;
606         }
607 
608         if( fwrite( output, 1, olen, fout ) != olen )
609         {
610             mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
611             goto exit;
612         }
613     }
614 
615     exit_code = MBEDTLS_EXIT_SUCCESS;
616 
617 exit:
618     if( fin )
619         fclose( fin );
620     if( fout )
621         fclose( fout );
622 
623     /* Zeroize all command line arguments to also cover
624        the case when the user has missed or reordered some,
625        in which case the key might not be in argv[6]. */
626     for( i = 0; i < argc; i++ )
627         mbedtls_platform_zeroize( argv[i], strlen( argv[i] ) );
628 
629     mbedtls_platform_zeroize( IV,     sizeof( IV ) );
630     mbedtls_platform_zeroize( key,    sizeof( key ) );
631     mbedtls_platform_zeroize( buffer, sizeof( buffer ) );
632     mbedtls_platform_zeroize( output, sizeof( output ) );
633     mbedtls_platform_zeroize( digest, sizeof( digest ) );
634 
635     mbedtls_cipher_free( &cipher_ctx );
636     mbedtls_md_free( &md_ctx );
637 
638     mbedtls_exit( exit_code );
639 }
640 #endif /* MBEDTLS_CIPHER_C && MBEDTLS_MD_C && MBEDTLS_FS_IO */
641