2016-04-19 00:37:45 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
|
2017-08-07 00:26:43 -04:00
|
|
|
#define TAG_LENGTH 16
|
2016-04-19 00:37:45 -04:00
|
|
|
#define BYTES_PER_READ 32 * 1024 // 32kb
|
|
|
|
#define INITIAL_BUFFER_SIZE 256 * 1024 // 256kb, must be at least 2*BYTES_PER_READ
|
|
|
|
|
2017-08-07 00:26:43 -04:00
|
|
|
unsigned int hex2string(char *src, unsigned char **dst_p)
|
2016-04-19 00:37:45 -04:00
|
|
|
{
|
2017-08-07 00:26:43 -04:00
|
|
|
unsigned int byte_length = strlen(src)/2;
|
|
|
|
*dst_p = malloc(byte_length);
|
2016-04-19 00:37:45 -04:00
|
|
|
unsigned char *dst = *dst_p;
|
|
|
|
unsigned char *end = dst + (strlen(src)/2);
|
|
|
|
unsigned int u;
|
|
|
|
|
|
|
|
while (dst < end && sscanf(src, "%2x", &u) == 1) {
|
|
|
|
*dst++ = u;
|
|
|
|
src += 2;
|
|
|
|
}
|
2017-08-07 00:26:43 -04:00
|
|
|
return byte_length;
|
2016-04-19 00:37:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
unsigned char *gcm_ivkey, *gcm_ct, *gcm_pt;
|
2017-01-20 01:21:17 -05:00
|
|
|
int outlen, rv = 0, final_outlen, decrypt = 1;
|
2016-04-19 00:37:45 -04:00
|
|
|
size_t read, actual_size = 0, total_size = INITIAL_BUFFER_SIZE;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
2017-01-20 01:21:17 -05:00
|
|
|
fprintf(stderr, "Usage: %s <key> [enc]\n", argv[0]);
|
2016-04-19 00:37:45 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
// this means we want to encrypt, not decrypt
|
|
|
|
if (argc > 2 && strcmp("enc", argv[2]) == 0)
|
|
|
|
decrypt = 0;
|
|
|
|
|
2017-08-07 00:26:43 -04:00
|
|
|
unsigned int byte_length = hex2string(argv[1], &gcm_ivkey);
|
|
|
|
unsigned int iv_length;
|
|
|
|
if(byte_length == 48) {
|
|
|
|
iv_length = 16;
|
|
|
|
} else if(byte_length == 44) {
|
|
|
|
iv_length = 12;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid key length %d only 44 or 48 bytes supported\n", byte_length);
|
|
|
|
return 1;
|
|
|
|
}
|
2016-04-19 00:37:45 -04:00
|
|
|
|
|
|
|
gcm_ct = malloc(total_size);
|
|
|
|
|
|
|
|
while ((read = fread(gcm_ct + actual_size, 1, BYTES_PER_READ, stdin)) > 0) {
|
|
|
|
actual_size += read;
|
|
|
|
if ((actual_size + BYTES_PER_READ) > total_size) {
|
|
|
|
total_size = total_size * 1.5;
|
|
|
|
gcm_ct = realloc(gcm_ct,total_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
if (actual_size < (decrypt ? 17 : 1)) {
|
|
|
|
fprintf(stderr, "File too small for %scryption\n", decrypt ? "de" : "en");
|
2016-04-19 00:37:45 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
if(decrypt)
|
2017-08-07 00:26:43 -04:00
|
|
|
actual_size -= TAG_LENGTH;
|
2016-04-19 00:37:45 -04:00
|
|
|
|
2017-08-07 00:26:43 -04:00
|
|
|
gcm_pt = malloc(decrypt ? actual_size : (actual_size + TAG_LENGTH));
|
2016-04-19 00:37:45 -04:00
|
|
|
|
|
|
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
|
|
|
|
|
|
|
/* Select cipher */
|
2017-01-20 01:21:17 -05:00
|
|
|
if(decrypt)
|
|
|
|
EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
|
|
|
|
else
|
|
|
|
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
|
2016-04-19 00:37:45 -04:00
|
|
|
|
|
|
|
/* Set IV length, omit for 96 bits */
|
2017-08-07 00:26:43 -04:00
|
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_length, NULL);
|
2017-01-20 01:21:17 -05:00
|
|
|
|
|
|
|
if(decrypt) {
|
|
|
|
/* Specify key and IV */
|
2017-08-07 00:26:43 -04:00
|
|
|
EVP_DecryptInit_ex(ctx, NULL, NULL, gcm_ivkey + iv_length, gcm_ivkey);
|
2017-01-20 01:21:17 -05:00
|
|
|
|
|
|
|
/* Set expected tag value. */
|
2017-08-07 00:26:43 -04:00
|
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, TAG_LENGTH, gcm_ct + actual_size);
|
2016-04-19 00:37:45 -04:00
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
/* Decrypt plaintext */
|
|
|
|
EVP_DecryptUpdate(ctx, gcm_pt, &outlen, gcm_ct, actual_size);
|
2016-04-19 00:37:45 -04:00
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
/* Finalise: note get no output for GCM */
|
|
|
|
rv = EVP_DecryptFinal_ex(ctx, gcm_pt, &final_outlen);
|
|
|
|
} else {
|
|
|
|
/* Specify key and IV */
|
2017-08-07 00:26:43 -04:00
|
|
|
EVP_EncryptInit_ex(ctx, NULL, NULL, gcm_ivkey + iv_length, gcm_ivkey);
|
2017-01-20 01:21:17 -05:00
|
|
|
|
|
|
|
/* Encrypt plaintext */
|
|
|
|
EVP_EncryptUpdate(ctx, gcm_pt, &outlen, gcm_ct, actual_size);
|
2016-04-19 00:37:45 -04:00
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
/* Finalise: note get no output for GCM */
|
|
|
|
rv = EVP_EncryptFinal_ex(ctx, gcm_pt, &final_outlen);
|
2016-04-19 00:37:45 -04:00
|
|
|
|
2017-01-20 01:21:17 -05:00
|
|
|
/* Get expected tag value. */
|
2017-08-07 00:26:43 -04:00
|
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_LENGTH, gcm_pt + actual_size);
|
2017-01-20 01:21:17 -05:00
|
|
|
}
|
2016-04-19 00:37:45 -04:00
|
|
|
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(gcm_ivkey);
|
|
|
|
free(gcm_ct);
|
|
|
|
|
|
|
|
if (rv > 0) {
|
|
|
|
// success!
|
2017-08-07 00:26:43 -04:00
|
|
|
fwrite(gcm_pt, 1, decrypt ? outlen : (outlen + TAG_LENGTH), stdout);
|
2016-04-19 00:37:45 -04:00
|
|
|
free(gcm_pt);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "File integrity check failed\n");
|
|
|
|
free(gcm_pt);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// compile with: gcc aesgcm.c -lcrypto -o aesgcm
|