wolfssl-examples/crypto/ascon/ascon-file-encrypt.c

405 lines
11 KiB
C

/* ascon-file-encrypt.c
*
* Copyright (C) 2006-2020 wolfSSL Inc.
*
* This file is part of wolfSSL. (formerly known as CyaSSL)
*
* wolfSSL 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 2 of the License, or
* (at your option) any later version.
*
* wolfSSL 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#endif
#include <wolfssl/options.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <wolfssl/wolfcrypt/ascon.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
#define ASCON_AEAD128_RATE 16
#define SALT_SIZE 8
#define AD_SIZE 32
static const byte nonce[ASCON_AEAD128_NONCE_SZ] = {
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F
};
static const byte ad[AD_SIZE] = {
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x01A, 0x1B,
0x1C, 0x1D, 0x1E, 0x1F,
};
void MemFree(wc_AsconAEAD128 *ascon, byte *key, int size, FILE *inFile, FILE *outFile, WC_RNG rng, byte *input, byte *output, int length) {
memset(input, 0, length);
memset(output, 0, length);
memset(key, 0, size);
free(input);
free(output);
free(key);
fclose(inFile);
fclose(outFile);
wc_FreeRng(&rng);
wc_AsconAEAD128_Free(ascon);
}
/*
* Makes a cryptographically secure key by stretching a user entered key
*/
int GenerateKey(WC_RNG* rng, byte* key, int size, byte* salt)
{
int ret = wc_RNG_GenerateBlock(rng, salt, SALT_SIZE);
if (ret != 0)
return -1020;
/* stretches key */
ret = wc_PBKDF2(key, key, strlen((const char*)key), salt, SALT_SIZE, 4096,
size, WC_SHA256);
if (ret != 0)
return -1030;
return 0;
}
/*
* Encrypts a file using Ascon
*/
int AsconEncrypt(wc_AsconAEAD128* ascon, byte* key, int size, FILE* inFile, FILE* outFile)
{
WC_RNG rng;
byte salt[SALT_SIZE] = {0};
byte tag[ASCON_AEAD128_TAG_SZ] = {0};
int ret = 0;
fseek(inFile, 0, SEEK_END);
const int inputLength = ftell(inFile);
fseek(inFile, 0, SEEK_SET);
byte* input = malloc(inputLength);
byte* output = malloc(inputLength);
while (ret == 0) { // to avoid memory leakage in case of an error
ret = wc_InitRng(&rng);
if (ret != 0) {
printf("Failed to initialize random number generator\n");
ret = -1030;
continue;
}
/* reads from inFile and writes whatever is there to the input array */
ret = fread(input, 1, inputLength, inFile);
if (ret == 0) {
printf("Input file does not exist.\n");
ret = -1010;
continue;
}
/* stretches key to fit size */
ret = GenerateKey(&rng, key, size, salt);
if (ret != 0) {
ret = -1040;
continue;
}
/* sets key */
ret = wc_AsconAEAD128_SetKey(ascon, key);
if (ret != 0) {
ret = -1001;
continue;
}
/* sets nonce */
ret = wc_AsconAEAD128_SetNonce(ascon, nonce);
if (ret != 0) {
ret = -1001;
continue;
}
/* sets additional data */
ret = wc_AsconAEAD128_SetAD(ascon, ad, AD_SIZE);
if (ret != 0) {
ret = -1001;
continue;
}
/* encrypts the message to the output based on input length */
ret = wc_AsconAEAD128_EncryptUpdate(ascon, output, input, inputLength);
if (ret != 0) {
ret = -1005;
continue;
}
/* Finalize encryption and generate tag */
ret = wc_AsconAEAD128_EncryptFinal(ascon, tag);
if (ret != 0) {
ret = -1005;
continue;
}
/* writes to outFile */
fwrite(salt, 1, SALT_SIZE, outFile);
fwrite(tag, 1, ASCON_AEAD128_TAG_SZ, outFile);
fwrite(output, 1, inputLength, outFile);
break;
}
/* closes the opened files and frees the memory*/
MemFree(ascon, key, size, inFile, outFile, rng, input, output, inputLength);
return ret;
}
/*
* Decrypts a file using Ascon
*/
int AsconDecrypt(wc_AsconAEAD128* ascon, byte* key, int size, FILE* inFile, FILE* outFile)
{
WC_RNG rng;
byte salt[SALT_SIZE] = {0};
byte tag[ASCON_AEAD128_TAG_SZ] = {0};
int i = 0;
int ret = 0;
fseek(inFile, 0, SEEK_END);
int length = ftell(inFile);
fseek(inFile, 0, SEEK_SET);
const int aSize = length;
byte* input = malloc(aSize);
byte* output = malloc(aSize);
while (ret == 0) {
ret = wc_InitRng(&rng);
if (ret != 0) {
printf("Failed to initialize random number generator\n");
ret = -1030;
continue;
}
/* reads from inFile and writes whatever is there to the input array */
ret = fread(input, 1, length, inFile);
if (ret == 0) {
printf("Input file does not exist.\n");
ret = -1010;
continue;
}
/* finds salt from input message */
for (i = 0; i < SALT_SIZE; i++) {
salt[i] = input[i];
}
/* finds tag from input message */
for (i = SALT_SIZE; i < ASCON_AEAD128_TAG_SZ + SALT_SIZE; i++) {
tag[i - SALT_SIZE] = input[i];
}
/* replicates old key if keys match */
ret = wc_PBKDF2(key, key, strlen((const char*)key), salt, SALT_SIZE, 4096,
size, WC_SHA256);
if (ret != 0) {
ret = -1050;
continue;
}
/* sets key */
ret = wc_AsconAEAD128_SetKey(ascon, key);
if (ret != 0) {
ret = -1001;
continue;
}
/* sets nonce */
ret = wc_AsconAEAD128_SetNonce(ascon, nonce);
if (ret != 0) {
ret = -1001;
continue;
}
/* sets additional data */
ret = wc_AsconAEAD128_SetAD(ascon, ad, AD_SIZE);
if (ret != 0) {
ret = -1001;
continue;
}
/* change length to remove salt/tag block from being decrypted */
length -= (ASCON_AEAD128_TAG_SZ + SALT_SIZE);
for (i = 0; i < length; i++) {
/* shifts message: ignores salt/tag on message*/
input[i] = input[i + (ASCON_AEAD128_TAG_SZ + SALT_SIZE)];
}
/* decrypts the message to output based on input length */
ret = wc_AsconAEAD128_DecryptUpdate(ascon, output, input, length);
if (ret != 0) {
ret = -1006;
continue;
}
/* Finalize decryption and verify tag */
ret = wc_AsconAEAD128_DecryptFinal(ascon, tag);
if (ret != 0) {
ret = -1001; // ASCON_AUTH_E
continue;
}
/* writes output to the outFile based on shortened length */
fwrite(output, 1, length, outFile);
break;
}
MemFree(ascon, key, size, inFile, outFile, rng, input, output, aSize);
return ret;
}
/*
* help message
*/
void help()
{
printf("\n~~~~~~~~~~~~~~~~~~~~|Help|~~~~~~~~~~~~~~~~~~~~~\n\n");
printf("Usage: ./ascon-encrypt <-option> <file.in> "
"<file.out>\n\n");
printf("Options\n");
printf("-d Decryption\n-e Encryption\n-h Help\n");
}
/*
* temporarily disables echoing in terminal for secure key input
*/
int NoEcho(char* key)
{
struct termios oflags, nflags;
/* disabling echo */
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
printf("Error: tcsetattr failed to disable terminal echo\n");
return -1060;
}
printf("Unique Password: ");
if (fgets(key, ASCON_AEAD128_KEY_SZ, stdin) == NULL) {
printf("Error: fgets failed to retrieve secure key input\n");
return -1070;
}
key[strlen(key) - 1] = 0;
/* restore terminal */
if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
printf("Error: tcsetattr failed to enable terminal echo\n");
return -1080;
}
return 0;
}
int main(int argc, char** argv)
{
wc_AsconAEAD128* ascon = NULL;
byte* key = NULL; /* user entered key */
FILE* inFile = NULL;
FILE* outFile = NULL;
const char* in;
const char* out;
int option; /* choice of how to run program */
int ret = 0; /* return value */
int inCheck = 0;
int outCheck = 0;
char choice = 'n';
while ((option = getopt(argc, argv, "dei:o:h")) != -1) {
switch (option) {
case 'd': /* if entered decrypt */
choice = 'd';
break;
case 'e': /* if entered encrypt */
choice = 'e';
break;
case 'h': /* if entered 'help' */
help();
break;
case 'i': /* input file */
in = optarg;
inCheck = 1;
inFile = fopen(in, "r");
break;
case 'o': /* output file */
out = optarg;
outCheck = 1;
outFile = fopen(out, "w");
break;
case '?':
if (optopt) {
printf("Ending Session\n");
return -111;
}
default:
abort();
}
}
if (inCheck == 0 || outCheck == 0) {
printf("Must have both input and output file");
printf(": -i filename -o filename\n");
}
else if (choice != 'n') {
key = malloc(ASCON_AEAD128_KEY_SZ); /* sets size memory of key */
ret = NoEcho((char*)key);
ascon = wc_AsconAEAD128_New();
if (ascon == NULL) {
free(key);
printf("Error: initiating Ascon object failed\n");
return -1030;
}
if (choice == 'e')
AsconEncrypt(ascon, key, ASCON_AEAD128_KEY_SZ, inFile, outFile);
else if (choice == 'd')
AsconDecrypt(ascon, key, ASCON_AEAD128_KEY_SZ, inFile, outFile);
}
else if (choice == 'n') {
printf("Must select either -e[16] or -d[16] for encryption and decryption\n");
}
return ret;
}