1 /*
2  *  \brief  Generic file encryption program using generic wrappers for configured
3  *          security.
4  *
5  *  Copyright (C) 2006-2016, ARM Limited, All Rights Reserved
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  *  This file is part of mbed TLS (https://tls.mbed.org)
21  */
22 
23 #if !defined(MBEDTLS_CONFIG_FILE)
24 #include "mbedtls/config.h"
25 #else
26 #include MBEDTLS_CONFIG_FILE
27 #endif
28 
29 #if defined(MBEDTLS_PLATFORM_C)
30 #include "mbedtls/platform.h"
31 #else
32 #include <stdio.h>
33 #define mbedtls_fprintf    fprintf
34 #define mbedtls_printf     printf
35 #endif
36 
37 #if defined(MBEDTLS_CIPHER_C) && defined(MBEDTLS_MD_C) && \
38  defined(MBEDTLS_FS_IO)
39 #include "mbedtls/cipher.h"
40 #include "mbedtls/md.h"
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #endif
46 
47 #if defined(_WIN32)
48 #include <windows.h>
49 #if !defined(_WIN32_WCE)
50 #include <io.h>
51 #endif
52 #else
53 #include <sys/types.h>
54 #include <unistd.h>
55 #endif
56 
57 #define MODE_ENCRYPT    0
58 #define MODE_DECRYPT    1
59 
60 #define USAGE   \
61     "\n  crypt_and_hash <mode> <input filename> <output filename> <cipher> <mbedtls_md> <key>\n" \
62     "\n   <mode>: 0 = encrypt, 1 = decrypt\n" \
63     "\n  example: crypt_and_hash 0 file file.aes AES-128-CBC SHA1 hex:E76B2413958B00E193\n" \
64     "\n"
65 
66 #if !defined(MBEDTLS_CIPHER_C) || !defined(MBEDTLS_MD_C) || \
67     !defined(MBEDTLS_FS_IO)
main(void)68 int main( void )
69 {
70     mbedtls_printf("MBEDTLS_CIPHER_C and/or MBEDTLS_MD_C and/or MBEDTLS_FS_IO not defined.\n");
71     return( 0 );
72 }
73 #else
main(int argc,char * argv[])74 int main( int argc, char *argv[] )
75 {
76     int ret = 1, i, n;
77     int mode;
78     size_t keylen, ilen, olen;
79     FILE *fkey, *fin = NULL, *fout = NULL;
80 
81     char *p;
82     unsigned char IV[16];
83     unsigned char key[512];
84     unsigned char digest[MBEDTLS_MD_MAX_SIZE];
85     unsigned char buffer[1024];
86     unsigned char output[1024];
87     unsigned char diff;
88 
89     const mbedtls_cipher_info_t *cipher_info;
90     const mbedtls_md_info_t *md_info;
91     mbedtls_cipher_context_t cipher_ctx;
92     mbedtls_md_context_t md_ctx;
93 #if defined(_WIN32_WCE)
94     long filesize, offset;
95 #elif defined(_WIN32)
96        LARGE_INTEGER li_size;
97     __int64 filesize, offset;
98 #else
99       off_t filesize, offset;
100 #endif
101 
102     mbedtls_cipher_init( &cipher_ctx );
103     mbedtls_md_init( &md_ctx );
104 
105     /*
106      * Parse the command-line arguments.
107      */
108     if( argc != 7 )
109     {
110         const int *list;
111 
112         mbedtls_printf( USAGE );
113 
114         mbedtls_printf( "Available ciphers:\n" );
115         list = mbedtls_cipher_list();
116         while( *list )
117         {
118             cipher_info = mbedtls_cipher_info_from_type( *list );
119             mbedtls_printf( "  %s\n", cipher_info->name );
120             list++;
121         }
122 
123         mbedtls_printf( "\nAvailable message digests:\n" );
124         list = mbedtls_md_list();
125         while( *list )
126         {
127             md_info = mbedtls_md_info_from_type( *list );
128             mbedtls_printf( "  %s\n", mbedtls_md_get_name( md_info ) );
129             list++;
130         }
131 
132 #if defined(_WIN32)
133         mbedtls_printf( "\n  Press Enter to exit this program.\n" );
134         fflush( stdout ); getchar();
135 #endif
136 
137         goto exit;
138     }
139 
140     mode = atoi( argv[1] );
141 
142     if( mode != MODE_ENCRYPT && mode != MODE_DECRYPT )
143     {
144         mbedtls_fprintf( stderr, "invalid operation mode\n" );
145         goto exit;
146     }
147 
148     if( strcmp( argv[2], argv[3] ) == 0 )
149     {
150         mbedtls_fprintf( stderr, "input and output filenames must differ\n" );
151         goto exit;
152     }
153 
154     if( ( fin = fopen( argv[2], "rb" ) ) == NULL )
155     {
156         mbedtls_fprintf( stderr, "fopen(%s,rb) failed\n", argv[2] );
157         goto exit;
158     }
159 
160     if( ( fout = fopen( argv[3], "wb+" ) ) == NULL )
161     {
162         mbedtls_fprintf( stderr, "fopen(%s,wb+) failed\n", argv[3] );
163         goto exit;
164     }
165 
166     /*
167      * Read the Cipher and MD from the command line
168      */
169     cipher_info = mbedtls_cipher_info_from_string( argv[4] );
170     if( cipher_info == NULL )
171     {
172         mbedtls_fprintf( stderr, "Cipher '%s' not found\n", argv[4] );
173         goto exit;
174     }
175     if( ( ret = mbedtls_cipher_setup( &cipher_ctx, cipher_info) ) != 0 )
176     {
177         mbedtls_fprintf( stderr, "mbedtls_cipher_setup failed\n" );
178         goto exit;
179     }
180 
181     md_info = mbedtls_md_info_from_string( argv[5] );
182     if( md_info == NULL )
183     {
184         mbedtls_fprintf( stderr, "Message Digest '%s' not found\n", argv[5] );
185         goto exit;
186     }
187 
188     if( mbedtls_md_setup( &md_ctx, md_info, 1 ) != 0 )
189     {
190         mbedtls_fprintf( stderr, "mbedtls_md_setup failed\n" );
191         goto exit;
192     }
193 
194     /*
195      * Read the secret key and clean the command line.
196      */
197     if( ( fkey = fopen( argv[6], "rb" ) ) != NULL )
198     {
199         keylen = fread( key, 1, sizeof( key ), fkey );
200         fclose( fkey );
201     }
202     else
203     {
204         if( memcmp( argv[6], "hex:", 4 ) == 0 )
205         {
206             p = &argv[6][4];
207             keylen = 0;
208 
209             while( sscanf( p, "%02X", &n ) > 0 &&
210                    keylen < (int) sizeof( key ) )
211             {
212                 key[keylen++] = (unsigned char) n;
213                 p += 2;
214             }
215         }
216         else
217         {
218             keylen = strlen( argv[6] );
219 
220             if( keylen > (int) sizeof( key ) )
221                 keylen = (int) sizeof( key );
222 
223             memcpy( key, argv[6], keylen );
224         }
225     }
226 
227     memset( argv[6], 0, strlen( argv[6] ) );
228 
229 #if defined(_WIN32_WCE)
230     filesize = fseek( fin, 0L, SEEK_END );
231 #else
232 #if defined(_WIN32)
233     /*
234      * Support large files (> 2Gb) on Win32
235      */
236     li_size.QuadPart = 0;
237     li_size.LowPart  =
238         SetFilePointer( (HANDLE) _get_osfhandle( _fileno( fin ) ),
239                         li_size.LowPart, &li_size.HighPart, FILE_END );
240 
241     if( li_size.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
242     {
243         mbedtls_fprintf( stderr, "SetFilePointer(0,FILE_END) failed\n" );
244         goto exit;
245     }
246 
247     filesize = li_size.QuadPart;
248 #else
249     if( ( filesize = lseek( fileno( fin ), 0, SEEK_END ) ) < 0 )
250     {
251         perror( "lseek" );
252         goto exit;
253     }
254 #endif
255 #endif
256 
257     if( fseek( fin, 0, SEEK_SET ) < 0 )
258     {
259         mbedtls_fprintf( stderr, "fseek(0,SEEK_SET) failed\n" );
260         goto exit;
261     }
262 
263     if( mode == MODE_ENCRYPT )
264     {
265         /*
266          * Generate the initialization vector as:
267          * IV = MD( filesize || filename )[0..15]
268          */
269         for( i = 0; i < 8; i++ )
270             buffer[i] = (unsigned char)( filesize >> ( i << 3 ) );
271 
272         p = argv[2];
273 
274         mbedtls_md_starts( &md_ctx );
275         mbedtls_md_update( &md_ctx, buffer, 8 );
276         mbedtls_md_update( &md_ctx, (unsigned char *) p, strlen( p ) );
277         mbedtls_md_finish( &md_ctx, digest );
278 
279         memcpy( IV, digest, 16 );
280 
281         /*
282          * Append the IV at the beginning of the output.
283          */
284         if( fwrite( IV, 1, 16, fout ) != 16 )
285         {
286             mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", 16 );
287             goto exit;
288         }
289 
290         /*
291          * Hash the IV and the secret key together 8192 times
292          * using the result to setup the AES context and HMAC.
293          */
294         memset( digest, 0,  32 );
295         memcpy( digest, IV, 16 );
296 
297         for( i = 0; i < 8192; i++ )
298         {
299             mbedtls_md_starts( &md_ctx );
300             mbedtls_md_update( &md_ctx, digest, 32 );
301             mbedtls_md_update( &md_ctx, key, keylen );
302             mbedtls_md_finish( &md_ctx, digest );
303 
304         }
305 
306         memset( key, 0, sizeof( key ) );
307 
308         if( mbedtls_cipher_setkey( &cipher_ctx, digest, cipher_info->key_bitlen,
309                            MBEDTLS_ENCRYPT ) != 0 )
310         {
311             mbedtls_fprintf( stderr, "mbedtls_cipher_setkey() returned error\n");
312             goto exit;
313         }
314         if( mbedtls_cipher_set_iv( &cipher_ctx, IV, 16 ) != 0 )
315         {
316             mbedtls_fprintf( stderr, "mbedtls_cipher_set_iv() returned error\n");
317             goto exit;
318         }
319         if( mbedtls_cipher_reset( &cipher_ctx ) != 0 )
320         {
321             mbedtls_fprintf( stderr, "mbedtls_cipher_reset() returned error\n");
322             goto exit;
323         }
324 
325         mbedtls_md_hmac_starts( &md_ctx, digest, 32 );
326 
327         /*
328          * Encrypt and write the ciphertext.
329          */
330         for( offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size( &cipher_ctx ) )
331         {
332             ilen = ( (unsigned int) filesize - offset > mbedtls_cipher_get_block_size( &cipher_ctx ) ) ?
333                 mbedtls_cipher_get_block_size( &cipher_ctx ) : (unsigned int) ( filesize - offset );
334 
335             if( fread( buffer, 1, ilen, fin ) != ilen )
336             {
337                 mbedtls_fprintf( stderr, "fread(%ld bytes) failed\n", (long) ilen );
338                 goto exit;
339             }
340 
341             if( mbedtls_cipher_update( &cipher_ctx, buffer, ilen, output, &olen ) != 0 )
342             {
343                 mbedtls_fprintf( stderr, "mbedtls_cipher_update() returned error\n");
344                 goto exit;
345             }
346 
347             mbedtls_md_hmac_update( &md_ctx, output, olen );
348 
349             if( fwrite( output, 1, olen, fout ) != olen )
350             {
351                 mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
352                 goto exit;
353             }
354         }
355 
356         if( mbedtls_cipher_finish( &cipher_ctx, output, &olen ) != 0 )
357         {
358             mbedtls_fprintf( stderr, "mbedtls_cipher_finish() returned error\n" );
359             goto exit;
360         }
361         mbedtls_md_hmac_update( &md_ctx, output, olen );
362 
363         if( fwrite( output, 1, olen, fout ) != olen )
364         {
365             mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
366             goto exit;
367         }
368 
369         /*
370          * Finally write the HMAC.
371          */
372         mbedtls_md_hmac_finish( &md_ctx, digest );
373 
374         if( fwrite( digest, 1, mbedtls_md_get_size( md_info ), fout ) != mbedtls_md_get_size( md_info ) )
375         {
376             mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", mbedtls_md_get_size( md_info ) );
377             goto exit;
378         }
379     }
380 
381     if( mode == MODE_DECRYPT )
382     {
383         /*
384          *  The encrypted file must be structured as follows:
385          *
386          *        00 .. 15              Initialization Vector
387          *        16 .. 31              Encrypted Block #1
388          *           ..
389          *      N*16 .. (N+1)*16 - 1    Encrypted Block #N
390          *  (N+1)*16 .. (N+1)*16 + n    Hash(ciphertext)
391          */
392         if( filesize < 16 + mbedtls_md_get_size( md_info ) )
393         {
394             mbedtls_fprintf( stderr, "File too short to be encrypted.\n" );
395             goto exit;
396         }
397 
398         if( mbedtls_cipher_get_block_size( &cipher_ctx ) == 0 )
399         {
400             mbedtls_fprintf( stderr, "Invalid cipher block size: 0. \n" );
401             goto exit;
402         }
403 
404         /*
405          * Check the file size.
406          */
407         if( cipher_info->mode != MBEDTLS_MODE_GCM &&
408             ( ( filesize - mbedtls_md_get_size( md_info ) ) %
409                 mbedtls_cipher_get_block_size( &cipher_ctx ) ) != 0 )
410         {
411             mbedtls_fprintf( stderr, "File content not a multiple of the block size (%d).\n",
412                      mbedtls_cipher_get_block_size( &cipher_ctx ));
413             goto exit;
414         }
415 
416         /*
417          * Subtract the IV + HMAC length.
418          */
419         filesize -= ( 16 + mbedtls_md_get_size( md_info ) );
420 
421         /*
422          * Read the IV and original filesize modulo 16.
423          */
424         if( fread( buffer, 1, 16, fin ) != 16 )
425         {
426             mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", 16 );
427             goto exit;
428         }
429 
430         memcpy( IV, buffer, 16 );
431 
432         /*
433          * Hash the IV and the secret key together 8192 times
434          * using the result to setup the AES context and HMAC.
435          */
436         memset( digest, 0,  32 );
437         memcpy( digest, IV, 16 );
438 
439         for( i = 0; i < 8192; i++ )
440         {
441             mbedtls_md_starts( &md_ctx );
442             mbedtls_md_update( &md_ctx, digest, 32 );
443             mbedtls_md_update( &md_ctx, key, keylen );
444             mbedtls_md_finish( &md_ctx, digest );
445         }
446 
447         memset( key, 0, sizeof( key ) );
448 
449         if( mbedtls_cipher_setkey( &cipher_ctx, digest, cipher_info->key_bitlen,
450                            MBEDTLS_DECRYPT ) != 0 )
451         {
452             mbedtls_fprintf( stderr, "mbedtls_cipher_setkey() returned error\n" );
453             goto exit;
454         }
455 
456         if( mbedtls_cipher_set_iv( &cipher_ctx, IV, 16 ) != 0 )
457         {
458             mbedtls_fprintf( stderr, "mbedtls_cipher_set_iv() returned error\n" );
459             goto exit;
460         }
461 
462         if( mbedtls_cipher_reset( &cipher_ctx ) != 0 )
463         {
464             mbedtls_fprintf( stderr, "mbedtls_cipher_reset() returned error\n" );
465             goto exit;
466         }
467 
468         mbedtls_md_hmac_starts( &md_ctx, digest, 32 );
469 
470         /*
471          * Decrypt and write the plaintext.
472          */
473         for( offset = 0; offset < filesize; offset += mbedtls_cipher_get_block_size( &cipher_ctx ) )
474         {
475             ilen = ( (unsigned int) filesize - offset > mbedtls_cipher_get_block_size( &cipher_ctx ) ) ?
476                 mbedtls_cipher_get_block_size( &cipher_ctx ) : (unsigned int) ( filesize - offset );
477 
478             if( fread( buffer, 1, ilen, fin ) != ilen )
479             {
480                 mbedtls_fprintf( stderr, "fread(%d bytes) failed\n",
481                     mbedtls_cipher_get_block_size( &cipher_ctx ) );
482                 goto exit;
483             }
484 
485             mbedtls_md_hmac_update( &md_ctx, buffer, ilen );
486             if( mbedtls_cipher_update( &cipher_ctx, buffer, ilen, output,
487                                        &olen ) != 0 )
488             {
489                 mbedtls_fprintf( stderr, "mbedtls_cipher_update() returned error\n" );
490                 goto exit;
491             }
492 
493             if( fwrite( output, 1, olen, fout ) != olen )
494             {
495                 mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
496                 goto exit;
497             }
498         }
499 
500         /*
501          * Verify the message authentication code.
502          */
503         mbedtls_md_hmac_finish( &md_ctx, digest );
504 
505         if( fread( buffer, 1, mbedtls_md_get_size( md_info ), fin ) != mbedtls_md_get_size( md_info ) )
506         {
507             mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", mbedtls_md_get_size( md_info ) );
508             goto exit;
509         }
510 
511         /* Use constant-time buffer comparison */
512         diff = 0;
513         for( i = 0; i < mbedtls_md_get_size( md_info ); i++ )
514             diff |= digest[i] ^ buffer[i];
515 
516         if( diff != 0 )
517         {
518             mbedtls_fprintf( stderr, "HMAC check failed: wrong key, "
519                              "or file corrupted.\n" );
520             goto exit;
521         }
522 
523         /*
524          * Write the final block of data
525          */
526         mbedtls_cipher_finish( &cipher_ctx, output, &olen );
527 
528         if( fwrite( output, 1, olen, fout ) != olen )
529         {
530             mbedtls_fprintf( stderr, "fwrite(%ld bytes) failed\n", (long) olen );
531             goto exit;
532         }
533     }
534 
535     ret = 0;
536 
537 exit:
538     if( fin )
539         fclose( fin );
540     if( fout )
541         fclose( fout );
542 
543     memset( buffer, 0, sizeof( buffer ) );
544     memset( digest, 0, sizeof( digest ) );
545 
546     mbedtls_cipher_free( &cipher_ctx );
547     mbedtls_md_free( &md_ctx );
548 
549     return( ret );
550 }
551 #endif /* MBEDTLS_CIPHER_C && MBEDTLS_MD_C && MBEDTLS_FS_IO */
552