mbedtls/programs/aes/aescrypt2.c
Krzysztof Stachowiak a08652233d Unify the example programs' termination
This is done to account for platforms, for which we want custom behavior
upon the program termination, hence we call `mbedtls_exit()` instead of
returning from `main()`.

For the sake of consistency, introduces the modifications have been made
to the test and utility examples as well. These, while less likely to be
used in the low level environments, won't suffer from such a change.
2020-05-11 22:20:32 +02:00

469 lines
12 KiB
C

/*
* AES-256 file encryption program
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdio.h>
#include <stdlib.h>
#define mbedtls_fprintf fprintf
#define mbedtls_printf printf
#define mbedtls_exit exit
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif /* MBEDTLS_PLATFORM_C */
#include "mbedtls/aes.h"
#include "mbedtls/md.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#if !defined(_WIN32_WCE)
#include <io.h>
#endif
#else
#include <sys/types.h>
#include <unistd.h>
#endif
#define MODE_ENCRYPT 0
#define MODE_DECRYPT 1
#define USAGE \
"\n aescrypt2 <mode> <input filename> <output filename> <key>\n" \
"\n <mode>: 0 = encrypt, 1 = decrypt\n" \
"\n example: aescrypt2 0 file file.aes hex:E76B2413958B00E193\n" \
"\n"
#if !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_SHA256_C) || \
!defined(MBEDTLS_FS_IO) || !defined(MBEDTLS_MD_C)
int main( void )
{
mbedtls_printf("MBEDTLS_AES_C and/or MBEDTLS_SHA256_C "
"and/or MBEDTLS_FS_IO and/or MBEDTLS_MD_C "
"not defined.\n");
mbedtls_exit( 0 );
}
#else
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {
volatile unsigned char *p = v; while( n-- ) *p++ = 0;
}
int main( int argc, char *argv[] )
{
int ret = 0;
int exit_code = MBEDTLS_EXIT_FAILURE;
unsigned int i, n;
int mode, lastn;
size_t keylen;
FILE *fkey, *fin = NULL, *fout = NULL;
char *p;
unsigned char IV[16];
unsigned char tmp[16];
unsigned char key[512];
unsigned char digest[32];
unsigned char buffer[1024];
unsigned char diff;
mbedtls_aes_context aes_ctx;
mbedtls_md_context_t sha_ctx;
#if defined(_WIN32_WCE)
long filesize, offset;
#elif defined(_WIN32)
LARGE_INTEGER li_size;
__int64 filesize, offset;
#else
off_t filesize, offset;
#endif
mbedtls_aes_init( &aes_ctx );
mbedtls_md_init( &sha_ctx );
ret = mbedtls_md_setup( &sha_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 1 );
if( ret != 0 )
{
mbedtls_printf( " ! mbedtls_md_setup() returned -0x%04x\n", -ret );
goto exit;
}
/*
* Parse the command-line arguments.
*/
if( argc != 5 )
{
mbedtls_printf( USAGE );
#if defined(_WIN32)
mbedtls_printf( "\n Press Enter to exit this program.\n" );
fflush( stdout ); getchar();
#endif
goto exit;
}
mode = atoi( argv[1] );
memset( IV, 0, sizeof( IV ) );
memset( key, 0, sizeof( key ) );
memset( digest, 0, sizeof( digest ) );
memset( buffer, 0, sizeof( buffer ) );
if( mode != MODE_ENCRYPT && mode != MODE_DECRYPT )
{
mbedtls_fprintf( stderr, "invalide operation mode\n" );
goto exit;
}
if( strcmp( argv[2], argv[3] ) == 0 )
{
mbedtls_fprintf( stderr, "input and output filenames must differ\n" );
goto exit;
}
if( ( fin = fopen( argv[2], "rb" ) ) == NULL )
{
mbedtls_fprintf( stderr, "fopen(%s,rb) failed\n", argv[2] );
goto exit;
}
if( ( fout = fopen( argv[3], "wb+" ) ) == NULL )
{
mbedtls_fprintf( stderr, "fopen(%s,wb+) failed\n", argv[3] );
goto exit;
}
/*
* Read the secret key from file or command line
*/
if( ( fkey = fopen( argv[4], "rb" ) ) != NULL )
{
keylen = fread( key, 1, sizeof( key ), fkey );
fclose( fkey );
}
else
{
if( memcmp( argv[4], "hex:", 4 ) == 0 )
{
p = &argv[4][4];
keylen = 0;
while( sscanf( p, "%02X", &n ) > 0 &&
keylen < (int) sizeof( key ) )
{
key[keylen++] = (unsigned char) n;
p += 2;
}
}
else
{
keylen = strlen( argv[4] );
if( keylen > (int) sizeof( key ) )
keylen = (int) sizeof( key );
memcpy( key, argv[4], keylen );
}
}
#if defined(_WIN32_WCE)
filesize = fseek( fin, 0L, SEEK_END );
#else
#if defined(_WIN32)
/*
* Support large files (> 2Gb) on Win32
*/
li_size.QuadPart = 0;
li_size.LowPart =
SetFilePointer( (HANDLE) _get_osfhandle( _fileno( fin ) ),
li_size.LowPart, &li_size.HighPart, FILE_END );
if( li_size.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
{
mbedtls_fprintf( stderr, "SetFilePointer(0,FILE_END) failed\n" );
goto exit;
}
filesize = li_size.QuadPart;
#else
if( ( filesize = lseek( fileno( fin ), 0, SEEK_END ) ) < 0 )
{
perror( "lseek" );
goto exit;
}
#endif
#endif
if( fseek( fin, 0, SEEK_SET ) < 0 )
{
mbedtls_fprintf( stderr, "fseek(0,SEEK_SET) failed\n" );
goto exit;
}
if( mode == MODE_ENCRYPT )
{
/*
* Generate the initialization vector as:
* IV = SHA-256( filesize || filename )[0..15]
*/
for( i = 0; i < 8; i++ )
buffer[i] = (unsigned char)( filesize >> ( i << 3 ) );
p = argv[2];
mbedtls_md_starts( &sha_ctx );
mbedtls_md_update( &sha_ctx, buffer, 8 );
mbedtls_md_update( &sha_ctx, (unsigned char *) p, strlen( p ) );
mbedtls_md_finish( &sha_ctx, digest );
memcpy( IV, digest, 16 );
/*
* The last four bits in the IV are actually used
* to store the file size modulo the AES block size.
*/
lastn = (int)( filesize & 0x0F );
IV[15] = (unsigned char)
( ( IV[15] & 0xF0 ) | lastn );
/*
* Append the IV at the beginning of the output.
*/
if( fwrite( IV, 1, 16, fout ) != 16 )
{
mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", 16 );
goto exit;
}
/*
* Hash the IV and the secret key together 8192 times
* using the result to setup the AES context and HMAC.
*/
memset( digest, 0, 32 );
memcpy( digest, IV, 16 );
for( i = 0; i < 8192; i++ )
{
mbedtls_md_starts( &sha_ctx );
mbedtls_md_update( &sha_ctx, digest, 32 );
mbedtls_md_update( &sha_ctx, key, keylen );
mbedtls_md_finish( &sha_ctx, digest );
}
mbedtls_aes_setkey_enc( &aes_ctx, digest, 256 );
mbedtls_md_hmac_starts( &sha_ctx, digest, 32 );
/*
* Encrypt and write the ciphertext.
*/
for( offset = 0; offset < filesize; offset += 16 )
{
n = ( filesize - offset > 16 ) ? 16 : (int)
( filesize - offset );
if( fread( buffer, 1, n, fin ) != (size_t) n )
{
mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", n );
goto exit;
}
for( i = 0; i < 16; i++ )
buffer[i] = (unsigned char)( buffer[i] ^ IV[i] );
mbedtls_aes_crypt_ecb( &aes_ctx, MBEDTLS_AES_ENCRYPT, buffer, buffer );
mbedtls_md_hmac_update( &sha_ctx, buffer, 16 );
if( fwrite( buffer, 1, 16, fout ) != 16 )
{
mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", 16 );
goto exit;
}
memcpy( IV, buffer, 16 );
}
/*
* Finally write the HMAC.
*/
mbedtls_md_hmac_finish( &sha_ctx, digest );
if( fwrite( digest, 1, 32, fout ) != 32 )
{
mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", 16 );
goto exit;
}
}
if( mode == MODE_DECRYPT )
{
/*
* The encrypted file must be structured as follows:
*
* 00 .. 15 Initialization Vector
* 16 .. 31 AES Encrypted Block #1
* ..
* N*16 .. (N+1)*16 - 1 AES Encrypted Block #N
* (N+1)*16 .. (N+1)*16 + 32 HMAC-SHA-256(ciphertext)
*/
if( filesize < 48 )
{
mbedtls_fprintf( stderr, "File too short to be encrypted.\n" );
goto exit;
}
if( ( filesize & 0x0F ) != 0 )
{
mbedtls_fprintf( stderr, "File size not a multiple of 16.\n" );
goto exit;
}
/*
* Subtract the IV + HMAC length.
*/
filesize -= ( 16 + 32 );
/*
* Read the IV and original filesize modulo 16.
*/
if( fread( buffer, 1, 16, fin ) != 16 )
{
mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", 16 );
goto exit;
}
memcpy( IV, buffer, 16 );
lastn = IV[15] & 0x0F;
/*
* Hash the IV and the secret key together 8192 times
* using the result to setup the AES context and HMAC.
*/
memset( digest, 0, 32 );
memcpy( digest, IV, 16 );
for( i = 0; i < 8192; i++ )
{
mbedtls_md_starts( &sha_ctx );
mbedtls_md_update( &sha_ctx, digest, 32 );
mbedtls_md_update( &sha_ctx, key, keylen );
mbedtls_md_finish( &sha_ctx, digest );
}
mbedtls_aes_setkey_dec( &aes_ctx, digest, 256 );
mbedtls_md_hmac_starts( &sha_ctx, digest, 32 );
/*
* Decrypt and write the plaintext.
*/
for( offset = 0; offset < filesize; offset += 16 )
{
if( fread( buffer, 1, 16, fin ) != 16 )
{
mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", 16 );
goto exit;
}
memcpy( tmp, buffer, 16 );
mbedtls_md_hmac_update( &sha_ctx, buffer, 16 );
mbedtls_aes_crypt_ecb( &aes_ctx, MBEDTLS_AES_DECRYPT, buffer, buffer );
for( i = 0; i < 16; i++ )
buffer[i] = (unsigned char)( buffer[i] ^ IV[i] );
memcpy( IV, tmp, 16 );
n = ( lastn > 0 && offset == filesize - 16 )
? lastn : 16;
if( fwrite( buffer, 1, n, fout ) != (size_t) n )
{
mbedtls_fprintf( stderr, "fwrite(%d bytes) failed\n", n );
goto exit;
}
}
/*
* Verify the message authentication code.
*/
mbedtls_md_hmac_finish( &sha_ctx, digest );
if( fread( buffer, 1, 32, fin ) != 32 )
{
mbedtls_fprintf( stderr, "fread(%d bytes) failed\n", 32 );
goto exit;
}
/* Use constant-time buffer comparison */
diff = 0;
for( i = 0; i < 32; i++ )
diff |= digest[i] ^ buffer[i];
if( diff != 0 )
{
mbedtls_fprintf( stderr, "HMAC check failed: wrong key, "
"or file corrupted.\n" );
goto exit;
}
}
exit_code = MBEDTLS_EXIT_SUCCESS;
exit:
if( fin )
fclose( fin );
if( fout )
fclose( fout );
/* Zeroize all command line arguments to also cover
the case when the user has missed or reordered some,
in which case the key might not be in argv[4]. */
for( i = 0; i < (unsigned int) argc; i++ )
mbedtls_zeroize( argv[i], strlen( argv[i] ) );
mbedtls_zeroize( IV, sizeof( IV ) );
mbedtls_zeroize( key, sizeof( key ) );
mbedtls_zeroize( tmp, sizeof( tmp ) );
mbedtls_zeroize( buffer, sizeof( buffer ) );
mbedtls_zeroize( digest, sizeof( digest ) );
mbedtls_aes_free( &aes_ctx );
mbedtls_md_free( &sha_ctx );
mbedtls_exit( exit_code );
}
#endif /* MBEDTLS_AES_C && MBEDTLS_SHA256_C && MBEDTLS_FS_IO */