diff --git a/crypto/aes/Makefile b/crypto/aes/Makefile index 8194c257..efb96bfb 100644 --- a/crypto/aes/Makefile +++ b/crypto/aes/Makefile @@ -1,8 +1,8 @@ CC=gcc CFLAGS=-Wall -LIBS= -lwolfssl +LIBS= -lwolfssl -lm -all: aes-file-encrypt aescfb-file-encrypt aesctr-file-encrypt +all: aes-file-encrypt aescfb-file-encrypt aesctr-file-encrypt aesgcm-file-encrypt aes-file-encrypt: aes-file-encrypt.o $(CC) -o $@ $^ $(CFLAGS) $(LIBS) @@ -13,7 +13,10 @@ aescfb-file-encrypt: aescfb-file-encrypt.o aesctr-file-encrypt: aesctr-file-encrypt.o $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +aesgcm-file-encrypt: aesgcm-file-encrypt.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + .PHONY: clean clean: - rm -f *.o aes-file-encrypt aescfb-file-encrypt aesctr-file-encrypt + rm -f *.o aes-file-encrypt aescfb-file-encrypt aesctr-file-encrypt aesgcm-file-encrypt text* diff --git a/crypto/aes/aes-file-encrypt.c b/crypto/aes/aes-file-encrypt.c index d89af672..7367583b 100644 --- a/crypto/aes/aes-file-encrypt.c +++ b/crypto/aes/aes-file-encrypt.c @@ -22,13 +22,18 @@ #include #include #include -#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include #include #include #include #include #if defined(HAVE_PBKDF2) && !defined(NO_PWDBASED) + #define SALT_SIZE 8 /* diff --git a/crypto/aes/aesgcm-file-encrypt.c b/crypto/aes/aesgcm-file-encrypt.c new file mode 100644 index 00000000..c38bd0a7 --- /dev/null +++ b/crypto/aes/aesgcm-file-encrypt.c @@ -0,0 +1,971 @@ +/* aesgcm-file-encrypt.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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-1335, USA + */ + +/* aesgcm-file-encrypt */ + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef WOLFSSL_AESGCM_STREAM + +#ifdef OPENSSL_EXTRA + #include +#endif +#ifndef WOLFCRYPT_MAGIC + #define WOLFCRYPT_MAGIC "WOLFSSL" +#endif +/* +MIN_BUFFER_SIZE of less than +*/ +#ifndef MIN_BUFFER_SIZE + #define MIN_BUFFER_SIZE 1024 +#endif + +/* +For optimum performance, MAX_BUFFER_SIZE should be >= page_size and less than +system memory in order to prevent memory fragmentation,better work with MMU +memory alignments, leverage cache speedup, and minimize OS overhead. +*/ +#ifndef MAX_BUFFER_SIZE + /* Use upto 1 GByte of RAM */ + #define MAX_BUFFER_SIZE (1 << 30) +#endif + +#ifndef AES_KEY_SIZE + /* Use 32 Bytes for the key size */ + #define AES_KEY_SIZE AES_256_KEY_SIZE +#endif + +#ifndef AESGCM_TAG_SIZE + /* Use 16 for the authentication size */ + #define AESGCM_TAG_SIZE AES_BLOCK_SIZE +#endif + +static size_t get_block_sz(int fd) +{ + struct stat st; + if (fstat(fd, &st) == -1) { + perror("fstat"); + exit(EXIT_FAILURE); + } + return st.st_blksize; +} +static size_t get_file_sz(int fd) +{ + struct stat st; + if (fstat(fd, &st) == -1) { + perror("fstat"); + exit(EXIT_FAILURE); + } + return st.st_size; +} +static size_t get_memory_sz() +{ + long page_size = 0; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) { + perror("sysconf"); + exit(EXIT_FAILURE); + } + return page_size * ((size_t)sysconf(_SC_PHYS_PAGES)); +} + +static size_t get_optimal_buffer_sz(int fd, size_t memory_size) +{ + size_t block_size; + size_t file_size; + size_t optimal_size; + + block_size = get_block_sz(fd); + optimal_size = block_size; + file_size = get_file_sz(fd); + + if (optimal_size > MAX_BUFFER_SIZE) { + optimal_size = MAX_BUFFER_SIZE; + } + + while (optimal_size * 2 <= memory_size && + optimal_size * 2 <= MAX_BUFFER_SIZE && + optimal_size <= file_size) { + optimal_size *= 2; + } + return optimal_size; +} + +static int check_file_permission(const char *fileName, uid_t owner, gid_t group) +{ + + uid_t uid; + gid_t gid; + mode_t mode; + struct stat st; + + if (stat(fileName, &st) == -1) { + perror("stat Error: could not get file status"); + exit(EXIT_FAILURE); + } + + mode = st.st_mode; + uid = st.st_uid; + gid = st.st_gid; + + if ((uid != owner || !(mode & S_IRUSR)) + && (gid != group || !(mode & S_IRGRP)) + && !(mode & S_IROTH)) { + /* The owner and group don't have read access + and the file is not readable by others */ + return -1; + } + else { + /* The new owner and group have read access to the file */ + return 0; + } +} +/*! + \ingroup AES + \brief This function encrypts the input file containing plain text + and stores the resulting cipher text in the output file. + A magic label, the computed authentication tag (TAG) and the supplied + IV will be written in the beginning for the cipher file. + + \return 0 on successfully encrypting the file + + \param in_file filename with the plain text + \param out_file file name to hold the cipher text + \param key_str key must be 32 Bytes + \param iv_str IV length must be 16 Bytes +*/ +int encrypt_file_AesGCM(const char *in_file, const char *out_file, + const char *key_str, const char *iv_str) +{ + byte* in_buf; + byte* out_buf; + int in_fd; + int out_fd; + int read_size; + int ret = 0; + size_t buffer_size; + size_t memory_size; + byte iv[AES_IV_SIZE]; + byte key[AES_KEY_SIZE]; + byte tag_enc[AESGCM_TAG_SIZE]; + Aes gcm; + + if (!in_file || !out_file || !key_str || !iv_str) { + return BAD_FUNC_ARG; + } + + if (strlen(key_str) < AES_KEY_SIZE || strlen(iv_str) < AES_IV_SIZE) { + return BAD_LENGTH_E; + } + + if (check_file_permission(in_file, getuid(), getgid()) == -1) { + return -1; + } + + in_fd = open(in_file, O_RDONLY); + if (in_fd == -1) { + perror("open"); + return -1; + } + + out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (out_fd == -1) { + perror("open"); + close(in_fd); + return -1; + } + memory_size = get_memory_sz(); + buffer_size = get_optimal_buffer_sz(in_fd, memory_size); + + if (buffer_size < MIN_BUFFER_SIZE) { + buffer_size = MIN_BUFFER_SIZE; + } + + in_buf = malloc(buffer_size); + if (in_buf == NULL) { + perror("malloc"); + close(in_fd); + close(out_fd); + exit(EXIT_FAILURE); + } + out_buf = malloc(buffer_size); + if (out_buf == NULL) { + perror("malloc"); + close(in_fd); + close(out_fd); + free(in_buf); + exit(EXIT_FAILURE); + } + + memset(&gcm, 0, sizeof(Aes)); + memset(iv, 0, AES_IV_SIZE); + memset(key, 0, AES_KEY_SIZE); + memset(tag_enc, 0, AESGCM_TAG_SIZE); + strncpy((char *)iv, iv_str, AES_IV_SIZE); + strncpy((char *)key, key_str, AES_KEY_SIZE); + + ret = wc_AesGcmEncryptInit(&gcm, key, AES_KEY_SIZE, iv, AES_IV_SIZE); + if (ret == 0) { + /* Write magic label in the beginning of the cipher file */ + if (write(out_fd, WOLFCRYPT_MAGIC, + strlen(WOLFCRYPT_MAGIC)) != strlen(WOLFCRYPT_MAGIC)) { + perror("write"); + ret = -1; + goto exit; + } + /* Add a placeholder TAG and write IV in the cipher file */ + if (write(out_fd, tag_enc, AESGCM_TAG_SIZE) != AESGCM_TAG_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + if (write(out_fd, iv, AES_IV_SIZE) != AES_IV_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + } + + while (ret == 0) { + read_size = read(in_fd, in_buf, buffer_size); + if (read_size <= 0) + break; + + ret = wc_AesGcmEncryptUpdate(&gcm, out_buf, in_buf, read_size, NULL, 0); + if (ret == 0) { + if (write(out_fd, out_buf, read_size) != read_size) { + perror("write"); + ret = -1; + goto exit; + } + } + } + + if (ret == 0) { + ret = wc_AesGcmEncryptFinal(&gcm, tag_enc, AESGCM_TAG_SIZE); + if (ret == 0) { + /* move file pointer to beginning of file after the magic word */ + off_t offset = lseek(out_fd, strlen(WOLFCRYPT_MAGIC), SEEK_SET); + if (offset == -1) { + perror("lseek SEEK_SET"); + ret = -1; + goto exit; + } + if (write(out_fd, tag_enc, AESGCM_TAG_SIZE) != AESGCM_TAG_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + if (lseek(out_fd, 0, SEEK_END) == -1) { + perror("lseek SEEK_END"); + ret = -1; + goto exit; + } + } + } + printf("File encryption with AES GCM complete.\n"); +exit: + free(in_buf); + free(out_buf); + close(in_fd); + close(out_fd); + + + return ret; +} + +/*! + \ingroup AES + \brief This function decrypts the input file containing cipher text + and stores the resulting plain text in the output file. + The function expects a magic label, authentication tag (TAG) and + the IV in the beginning for the cipher file. + + \return 0 on successfully decrypting the file + \return negative number on error + + \param in_file filename with the cipher text + \param out_file file name to hold plain text + \param key_str key must be 32 Bytes +*/ +int decrypt_file_AesGCM(const char *in_file, const char *out_file, + const char *key_str) +{ + byte* in_buf; + byte* out_buf; + int in_fd; + int out_fd; + int read_size; + int ret = 0; + size_t buffer_size; + size_t memory_size; + byte iv[AES_IV_SIZE]; + byte wolf_magic[strlen(WOLFCRYPT_MAGIC)]; + byte key[AES_KEY_SIZE]; + byte tag_dec[AESGCM_TAG_SIZE]; + byte tag_enc[AESGCM_TAG_SIZE]; + Aes gcm; + + if (!in_file || !out_file || !key_str) { + return BAD_FUNC_ARG; + } + + if (strlen(key_str) < AES_KEY_SIZE) { + return BAD_LENGTH_E; + } + + if (check_file_permission(in_file, getuid(), getgid()) == -1) { + return -1; + } + + in_fd = open(in_file, O_RDONLY); + + if (in_fd == -1) { + perror("open"); + return -1; + } + + out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (out_fd == -1) { + perror("open"); + close(in_fd); + return -1; + } + memory_size = get_memory_sz(); + buffer_size = get_optimal_buffer_sz(in_fd, memory_size); + + if (buffer_size < MIN_BUFFER_SIZE) { + buffer_size = MIN_BUFFER_SIZE; + } + + in_buf = malloc(buffer_size); + if (in_buf == NULL) { + perror("malloc"); + close(in_fd); + close(out_fd); + exit(EXIT_FAILURE); + } + out_buf = malloc(buffer_size); + if (out_buf == NULL) { + perror("malloc"); + close(in_fd); + close(out_fd); + free(in_buf); + exit(EXIT_FAILURE); + } + + memset(&gcm, 0, sizeof(Aes)); + memset(iv, 0, AES_IV_SIZE); + memset(key, 0, AES_KEY_SIZE); + memset(tag_dec, 0, AESGCM_TAG_SIZE); + memset(tag_enc, 0, AESGCM_TAG_SIZE); + strncpy((char *)key, key_str, AES_KEY_SIZE); + + /* Extract a WOLFCRYPT MAGIC | TAG | IV from the cipher file */ + if (read(in_fd, wolf_magic, + strlen(WOLFCRYPT_MAGIC)) != strlen(WOLFCRYPT_MAGIC)) { + perror("write"); + ret = -1; + goto exit; + } + if (memcmp(wolf_magic, WOLFCRYPT_MAGIC, strlen(WOLFCRYPT_MAGIC)) != 0) { + perror("WOLFCRYPT_MAGIC didn't match\n"); + ret = AES_GCM_AUTH_E; + goto exit; + } + read_size = read(in_fd, tag_enc, AESGCM_TAG_SIZE); + if (read_size != AESGCM_TAG_SIZE) { + perror("read"); + ret = -1; + goto exit; + } + read_size = read(in_fd, iv, AES_IV_SIZE); + if (read_size != AES_IV_SIZE) { + perror("read"); + ret = -1; + goto exit; + } + + ret = wc_AesGcmDecryptInit(&gcm, key, AES_KEY_SIZE, iv, AES_IV_SIZE); + + while (ret == 0) { + read_size = read(in_fd, in_buf, buffer_size); + if (read_size <= 0) + break; + + ret = wc_AesGcmDecryptUpdate(&gcm, out_buf, in_buf, read_size, NULL, 0); + if (ret == 0) { + if (write(out_fd, out_buf, read_size) != read_size) { + perror("write"); + ret = -1; + goto exit; + } + } + } + + if (ret == 0) { + ret = wc_AesGcmEncryptFinal(&gcm, tag_dec, AESGCM_TAG_SIZE); + if (ret == 0 && (memcmp(tag_enc, tag_dec, AESGCM_TAG_SIZE) != 0)) { + perror("TAG didn't match\n"); + ret = AES_GCM_AUTH_E; + goto exit; + } + } +exit: + free(in_buf); + free(out_buf); + close(in_fd); + close(out_fd); + + printf("File decryption with AES GCM complete.\n"); + return ret; +} + +#ifdef OPENSSL_EXTRA +what +int encrypt_file(const char *in_file, const char *out_file, + const char *key_str, const char *iv_str) +{ + int in_fd; + int in_len; + int out_fd; + int out_len; + int ret = WOLFSSL_SUCCESS; + byte in_buf[AES_BLOCK_SIZE]; + byte iv[AES_IV_SIZE]; + byte key[AES_KEY_SIZE]; + byte out_buf[AES_BLOCK_SIZE]; + byte tag_enc[AESGCM_TAG_SIZE]; + EVP_CIPHER_CTX *ctx; + + if (!in_file || !out_file || !key_str || !iv_str) { + return BAD_FUNC_ARG; + } + + if (strlen(key_str) < AES_KEY_SIZE || strlen(iv_str) < AES_IV_SIZE) { + return BAD_LENGTH_E; + } + + if (check_file_permission(in_file, getuid(), getgid()) == -1) { + return -1; + } + + in_fd = open(in_file, O_RDONLY); + if (in_fd == -1) { + perror("open"); + return -1; + } + out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (out_fd == -1) { + perror("open"); + close(in_fd); + return -1; + } + + memset(iv, 0, AES_IV_SIZE); + memset(key, 0, AES_KEY_SIZE); + memset(tag_enc, 0, AESGCM_TAG_SIZE); + strncpy((char *)iv, iv_str, AES_IV_SIZE); + strncpy((char *)key, key_str, AES_KEY_SIZE); + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + perror("EVP_CIPHER_CTX_new"); + goto exit; + } + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != + WOLFSSL_SUCCESS) { + perror("EVP_EncryptInit_ex"); + goto exit; + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, AES_IV_SIZE, NULL) != + WOLFSSL_SUCCESS) { + perror("EVP_CIPHER_CTX_ctrl"); + goto exit; + } + + /* Write file identifier in the beginning of the cipher file */ + if (write(out_fd, WOLFCRYPT_MAGIC, + strlen(WOLFCRYPT_MAGIC)) != strlen(WOLFCRYPT_MAGIC)) { + perror("write"); + ret = -1; + goto exit; + } + /* Add a placeholder TAG and write IV in the cipher file */ + if (write(out_fd, tag_enc, AESGCM_TAG_SIZE) != AESGCM_TAG_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + if (write(out_fd, iv, AES_IV_SIZE) != AES_IV_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + while (1) { + in_len = read(in_fd, in_buf, AES_BLOCK_SIZE); + if (in_len <= 0) + break; + + if (EVP_EncryptUpdate(ctx, out_buf, &out_len, in_buf, in_len) != + WOLFSSL_SUCCESS) { + perror("EVP_EncryptUpdate"); + ret = -1; + goto exit; + } + if (write(out_fd, out_buf, out_len) != out_len) { + perror("write"); + ret = -1; + goto exit; + } + } + + if (EVP_EncryptFinal_ex(ctx, out_buf, &out_len) != WOLFSSL_SUCCESS) { + perror("EVP_EncryptFinal_ex"); + ret = -1; + goto exit; + } + + if (write(out_fd, out_buf, out_len) != out_len) { + perror("write"); + ret = -1; + goto exit; + } + + ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AES_IV_SIZE, tag_enc); + + if (ret == WOLFSSL_SUCCESS ) { + /* move file pointer to beginning of file after the magic word */ + off_t offset = lseek(out_fd, strlen(WOLFCRYPT_MAGIC), SEEK_SET); + if (offset == -1) { + perror("lseek SEEK_SET"); + ret = -1; + goto exit; + } + if (write(out_fd, tag_enc, AESGCM_TAG_SIZE) != AESGCM_TAG_SIZE) { + perror("write"); + ret = -1; + goto exit; + } + if (lseek(out_fd, 0, SEEK_END) == -1) { + perror("lseek SEEK_END"); + ret = -1; + goto exit; + } + } + printf("File encryption with EVP GCM complete.\n"); +exit: + EVP_CIPHER_CTX_free(ctx); + close(in_fd); + close(out_fd); + return ret; +} + + +int decrypt_file(const char *in_file, const char *out_file, const char *key_str) +{ + int in_fd; + int in_len; + int out_fd; + int out_len; + int ret = WOLFSSL_SUCCESS; + int read_size; + byte in_buf[AES_BLOCK_SIZE]; + byte iv[AES_IV_SIZE]; + byte wolf_magic[strlen(WOLFCRYPT_MAGIC)]; + byte key[AES_KEY_SIZE]; + byte out_buf[AES_BLOCK_SIZE]; + byte tag_dec[AESGCM_TAG_SIZE]; + byte tag_enc[AESGCM_TAG_SIZE]; + EVP_CIPHER_CTX *ctx; + + if (!in_file || !out_file || !key_str) { + return BAD_FUNC_ARG; + } + + if (strlen(key_str) < AES_KEY_SIZE) { + return BAD_LENGTH_E; + } + + if (check_file_permission(in_file, getuid(), getgid()) == -1) { + return -1; + } + + in_fd = open(in_file, O_RDONLY); + + if (in_fd == -1) { + perror("open"); + return -1; + } + + out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (out_fd == -1) { + perror("open"); + close(in_fd); + return -1; + } + + memset(iv, 0, AES_IV_SIZE); + memset(key, 0, AES_KEY_SIZE); + memset(tag_enc, 0, AESGCM_TAG_SIZE); + memset(tag_dec, 0, AESGCM_TAG_SIZE); + strncpy((char *)key, key_str, AES_KEY_SIZE); + + /* Extract a WOLFCRYPT MAGIC | TAG | IV from the cipher file */ + if (read(in_fd, wolf_magic, + strlen(WOLFCRYPT_MAGIC)) != strlen(WOLFCRYPT_MAGIC)) { + perror("write"); + ret = -1; + goto exit; + } + if (memcmp(wolf_magic, WOLFCRYPT_MAGIC, strlen(WOLFCRYPT_MAGIC)) != 0) { + perror("WOLFCRYPT_MAGIC didn't match\n"); + ret = AES_GCM_AUTH_E; + goto exit; + } + read_size = read(in_fd, tag_enc, AESGCM_TAG_SIZE); + if (read_size != AESGCM_TAG_SIZE) { + perror("read"); + ret = -1; + goto exit; + } + read_size = read(in_fd, iv, AES_IV_SIZE); + if (read_size != AES_IV_SIZE) { + perror("read"); + ret = -1; + goto exit; + } + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + perror("EVP_CIPHER_CTX_new"); + goto exit; + } + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != + WOLFSSL_SUCCESS) { + perror("EVP_DecryptInit_ex"); + goto exit; + } + while (1) { + in_len = read(in_fd, in_buf, AES_BLOCK_SIZE); + if (in_len <= 0) + break; + + if (EVP_DecryptUpdate(ctx, out_buf, &out_len, in_buf, in_len) != + WOLFSSL_SUCCESS) { + perror("EVP_DecryptInit_ex"); + goto exit; + } + if (write(out_fd, out_buf, out_len) != out_len) { + perror("write"); + ret = -1; + goto exit; + } + } + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, AES_IV_SIZE, tag_enc) + != WOLFSSL_SUCCESS) { + perror("EVP_CIPHER_CTX_ctrl"); + goto exit; + } + if (EVP_DecryptFinal_ex(ctx, out_buf, &out_len) != WOLFSSL_SUCCESS) { + perror("EVP_DecryptInit_ex"); + goto exit; + } + if (write(out_fd, out_buf, out_len) != out_len) { + perror("write"); + ret = -1; + goto exit; + } + + if (ret == WOLFSSL_SUCCESS) { + ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, + AES_IV_SIZE, tag_dec); + if (ret == WOLFSSL_SUCCESS && + (memcmp(tag_enc, tag_dec, AESGCM_TAG_SIZE) != 0)) { + perror("TAG didn't match\n"); + exit(EXIT_FAILURE); + } + } + printf("File decryption with EVP GCM complete.\n"); + +exit: + EVP_CIPHER_CTX_free(ctx); + close(in_fd); + close(out_fd); + return ret; +} +#endif + +#if defined(__linux__) + +int sanityTest_default(int file_sz) { + + const char *cmd_enc ="./aesgcm-file-encrypt -e 256 -m 1 \ + -k 77CF00EC060192530B5D06B6B426799B \ + -v 77CF00EC060192530B5D06B6B426799B \ + -i text.bin -o text2cipher.bin"; + const char *cmd_dec ="./aesgcm-file-encrypt -d 256 -m 1 \ + -k 77CF00EC060192530B5D06B6B426799B \ + -i text2cipher.bin -o text2cipher2text.bin"; + const char *cmd_diff = "diff -q text.bin text2cipher2text.bin"; + char buffer[1024]; + + sprintf(buffer,"dd if=/dev/urandom bs=1024 count=%d | head -c %d > \ +text.bin", (file_sz/1024)+1, file_sz); + + if (system(buffer) != 0 || system(cmd_enc) != 0 || system(cmd_dec) != 0 ) { + perror("system command"); + return 1; + } + + FILE* pipe = popen(cmd_diff, "r"); + if (!pipe) { + perror("system command"); + return -1; + } + + if (fgets(buffer, sizeof(buffer), pipe)) { + printf("Error: The files are different.\n"); + return -1; + } + else { + printf("Pass: The files are identical.\n"); + } + pclose(pipe); + +#ifdef OPENSSL_EXTRA + const char *cmd_enc_evp ="./aesgcm-file-encrypt -e 256 -m 1 \ + -k 77CF00EC060192530B5D06B6B426799B \ + -v 77CF00EC060192530B5D06B6B426799B \ + -i text.bin -o text2cipher.evp.bin"; + const char *cmd_dec_evp ="./aesgcm-file-encrypt -d 256 -m 1 \ + -k 77CF00EC060192530B5D06B6B426799B \ + -i text2cipher.evp.bin -o text2cipher2text.evp.bin"; + const char *cmd_diff_evp = "diff -q text.bin text2cipher2text.evp.bin"; + const char *cmd_diff_gcm_evp = "diff -q text2cipher2text.bin \ + text2cipher2text.evp.bin"; + + if (system(cmd_enc_evp) != 0 || system(cmd_dec_evp) != 0 ) { + perror("system command"); + return -1; + } + + pipe = popen(cmd_diff_evp, "r"); + if (!pipe) { + perror("system command"); + return -1; + } + if (fgets(buffer, sizeof(buffer), pipe)) { + printf("Error: The files are different.\n"); + } + else { + printf("Pass: The files are identical.\n"); + } + + pclose(pipe); + + pipe = popen(cmd_diff_gcm_evp, "r"); + if (!pipe) { + perror("Error opening pipe to command"); + return -1; + } + if (fgets(buffer, sizeof(buffer), pipe)) { + printf("Error: The EVP and GCM files are different.\n"); + } + else { + printf("Pass: The EVP and GCM files are identical.\n"); + } + + pclose(pipe); +#endif + + return 0; +} + +#endif /* __linux__ */ + +void usage(char *prog_name) { + fprintf(stderr, "Usage: %s [-e | -d] [-m] [-i input filename] \ + [-o output filename]\n", prog_name); + exit(EXIT_FAILURE); +} + +void help(char *prog_name) { + printf("This program accepts several switches:\n"); + printf(" -e encryption. 256, 192, 128 \n"); + printf(" -d decryption. 256, 192, 128\n"); + printf(" -m method to use. GCM(1), EVP GCM (2), \n"); + printf(" -i Set the input filename to 'file'\n"); + printf(" -o Set the output filename to 'file'\n"); +#if defined(__linux__) + printf(" -t Sanity test with the given file size in Bytes. The \ +test will create three files:text.bin, cipher, decrypted plain. \n"); + printf(" Example, ./aesgcm-file-encrypt -t 256"); +#endif + exit(EXIT_SUCCESS); +} + + +int main(int argc, char** argv) +{ + const char *inFile = NULL; + const char *ivStr = NULL; + const char *keyStr = NULL; + const char *outFile = NULL; + int file_sz = 0; + int key_sz = 0; + int method = 0; + int option; /* options of how to run the program */ + char choice = 'n'; + + while ((option = getopt(argc, argv, "e:d:i:o:m:t:k:v:h")) != -1 && choice != 't') { + switch (option) { + case 'e': /* encrypt */ + choice = 'e'; + key_sz = atoi(optarg); + if (!(key_sz == 128 || key_sz == 192 || key_sz == 256 )) { + perror("Wrong key size: use 128, 192 or 256 \n"); + usage(argv[0]); + } + break; + case 'd': /* decrypt */ + key_sz = atoi(optarg); + if (!(key_sz == 128 || key_sz == 192 || key_sz == 256 )) { + perror("Wrong key size: use 128, 192 or 256 \n"); + usage(argv[0]); + } + choice = 'd'; + break; + case 'i': /* input file */ + inFile = optarg; + break; + case 'o': /* output file */ + outFile = optarg; + break; + case 'm': /* options to do enc/dec */ + method = atoi(optarg); + if (method < 1 || method > 2) { + perror("Wrong AES choice: use EVP (1), GCM(2)\n"); + usage(argv[0]); + } + break; + case 'k': /* key */ + keyStr = optarg; + if (strlen(keyStr) < key_sz/8) { + perror("Wrong key string size\n"); + usage(argv[0]); + } + break; + case 'v': /* IV */ + ivStr = optarg; + if (strlen(ivStr) < 12) { + perror("Wrong IV length\n"); + usage(argv[0]); + } + break; + case 't': /* sanity test */ + choice = 't'; + file_sz = atoi(optarg); + break; + case 'h': /* Get help */ + case '?': + help(argv[0]); + break; + default: + usage(argv[0]); + } + } +#if defined(__linux__) + if (choice == 't') { + if (sanityTest_default(file_sz) != 0) { + perror("Error: sanityTest_default\n"); + } + return 0; + } +#endif + if (inFile && outFile && choice != 'n') { + + switch (method) { + case 1: + if (choice == 'e') { + if (encrypt_file_AesGCM(inFile, outFile, keyStr, ivStr) != 0) { + perror("Error: encrypt_file_AesGCM\n"); + } + } + else if (choice == 'd') { + if (decrypt_file_AesGCM(inFile, outFile, keyStr) != 0) { + perror("Error: decrypt_file_AesGCM\n"); + } + else + printf("Passed: decrypt_file_AesGCM\n"); + + } + break; +#ifdef OPENSSL_EXTRA + case 2: + if (choice == 'e') { + if (encrypt_file(inFile, outFile, keyStr, ivStr) != + WOLFSSL_SUCCESS) { + perror("Error: encrypt_file \n"); + } + } + else if (choice == 'd') { + if (decrypt_file(inFile, outFile, keyStr) != + WOLFSSL_SUCCESS) { + perror("Error: decrypt_file\n"); + } + else + printf("Passed: decrypt_file\n"); + } + break; +#endif + default: + abort(); + } + } + return 0; +} + +#else +int main() +{ + printf("This example requires AES GCM streaming. Build wolfSSL with " + "./configure --enable-aesgcm-stream\n"); + return 0; +} +#endif diff --git a/crypto/aes/aesgcm-file-encrypt.sh b/crypto/aes/aesgcm-file-encrypt.sh new file mode 100755 index 00000000..dc09488c --- /dev/null +++ b/crypto/aes/aesgcm-file-encrypt.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# set -x + +echo "aesgcm-file-encrypt tests" +echo "./aesgcm-file-encrypt -b to run benchmark after building wolfSSL with" +echo "./configure --enable-aesgcm-stream && sudo make install" + +# Define the output CSV file +output_file="aesgcm_times.csv" +output_file_header="aesgcm_times_header.csv" +partition="/dev/nvme0n1p8" + +keyStr=$(cat /dev/urandom | base64 | head -c 32) +echo "key = $keyStr" + +ivStr=$(cat /dev/urandom | base64 | head -c 16) +echo "IV = $ivStr" + +if [[ "$1" == "-b" ]] || [[ "$1" == "--bench" ]]; then + cp "$output_file" "$output_file.back" + echo "File-size,Buffer-size,AES-256-GCM-enc,AES-256-GCM-dec" > "$output_file" + + buffer_sizes=("32" "64" "128" "256" "512" "1024" "2048" "4096" "8192" "12288" "20480" "1073741824") + + for b_size in "${buffer_sizes[@]}" + do + rm aesgcm-file-encrypt text* + make clean + make CPPFLAGS="-DMAX_BUFFER_SIZE=$b_size -DMIN_BUFFER_SIZE=32" aesgcm-file-encrypt + + # Define an array of file sizes to test + + file_sizes=("10M" "50M" "100M" "500M" "1G") + + echo "System Information:" > "$output_file_header" + echo "--------------------" >> "$output_file_header" + echo "Hostname: $(hostname)" >> "$output_file_header" + echo "Kernel Version: $(uname -r)" >> "$output_file_header" + echo "Processor Type: $(uname -m)" >> "$output_file_header" + echo "Operating System: $(lsb_release -d | awk '{print $2,$3,$4,$5}')" >> "$output_file_header" + echo "CPU Info: $(lscpu | grep 'Model name' | cut -d':' -f2 | sed 's/^ //')" >> "$output_file_header" + echo "Memory Info: $(free -h | grep 'Mem' | awk '{print $2}')" >> "$output_file_header" + echo "The page size is: $(sudo blockdev --getbsz $partition) bytes." >> "$output_file_header" + echo "The sector size of the disk is: $(sudo blockdev --getss $partition) bytes." >> "$output_file_header" + + # Loop over each file size and measure performance + for size in "${file_sizes[@]}" + do + # Generate a test file of the given size + text_file=text_"$size".bin + dd if=/dev/urandom of=$text_file bs=$size count=1 conv=fsync + + t_aesgcm_e="$( TIMEFORMAT="%R";time (./aesgcm-file-encrypt -e 256 -m 1 -k $keyStr -v $ivStr -i $text_file -o text2cipher.bin > /dev/null 2>&1) 2>&1 )" + t_aesgcm_d="$( TIMEFORMAT="%R";time (./aesgcm-file-encrypt -d 256 -m 1 -k $keyStr -v $ivStr -i text2cipher.bin -o text2cipher2text.bin > /dev/null 2>&1) 2>&1 )" + + diff -s $text_file text2cipher2text.bin > /dev/null + if [ $? -eq 0 ]; then + echo "Passed $t_aesgcm_e $t_aesgcm_d" + else + echo "Failed" + fi + echo "$size,$b_size,$t_aesgcm_e,$t_aesgcm_d" >> "$output_file" + rm text* + done + done +else + rm aesgcm-file-encrypt text* + make clean + make CPPFLAGS="-DMAX_BUFFER_SIZE=4096" aesgcm-file-encrypt + # test a smaller file size (default option) + dd if=/dev/urandom bs=1024 count=1 | head -c 1022 > text.bin + res=$(./aesgcm-file-encrypt -e 256 -m 1 -k $keyStr -v $ivStr -i text.bin -o text2cipher.bin ) + echo $res + res=$(./aesgcm-file-encrypt -d 256 -m 1 -k $keyStr -v $ivStr -i text2cipher.bin -o text2cipher2text.bin ) + echo $res + + diff -s text.bin text2cipher2text.bin > /dev/null + if [ $? -eq 0 ]; then + echo "Passed" + else + echo "Failed" + fi + rm text* +fi + +if true; then + input_file="$output_file" + output_file_sorted="data.csv" + + awk -F, 'function to_bytes(x) { + split(x, a, /[^0-9.]+/); + return a[1] * (index("KMGT", substr(x, length(a[1]) + 1)) * 1024 ^ (index("KMGT", substr(x, length(a[1]) + 1)) - 1)); + } + NR == 1 { gsub(",", " "); print $0; next } + { line = $1; for (i = 2; i <= NF; i++) line = line "," $i; print to_bytes($1) "," line }' "$input_file" | + sort -t, -k2,2n -k3,3n | awk -F, 'NR == 1 { print $0 } NR > 1 { $1=""; $0=substr($0, 2); print }' > "$output_file_sorted" + + sed -i 's/1073741824/1G/g' "$output_file_sorted" + ./plot_data.gp +fi + + +exit 0 diff --git a/crypto/aes/plot_data.gp b/crypto/aes/plot_data.gp new file mode 100755 index 00000000..301aac3b --- /dev/null +++ b/crypto/aes/plot_data.gp @@ -0,0 +1,63 @@ +#!/usr/bin/gnuplot + +# Function to get the number of lines in the file +filelines(file) = system(sprintf("awk 'END {print NR}' %s", file)) + +# Get the number of lines in the data file +datafile = "data.csv" +nlines = int(filelines(datafile)) - 1 +# you can adjust the number of rows you want to ply +chunk_size = 12 + +# Set the terminal type +set term png + +# Set the title, style, and key settings +set title "wolfSSL" +set style fill solid 1.00 border lt -1 +set style histogram clustered gap 4 title textcolor lt -1 +set datafile missing '-' +set style data histograms + +# Set x-axis settings +set xtics border in scale 0,0 nomirror rotate by -45 autojustify +set xtics norangelimit +set xtics () +set format y '%.0s%c' + +set lmargin at screen 0.15 +set rmargin at screen 0.90 + +#set bmargin at screen 0.10 +set tmargin at screen 0.90 + +# Set legend position +#set key outside below +set key autotitle columnhead noenhanced + +# Set grid settings +set grid mxtics mytics +set grid x y + +# Set titles for x and y axes +set xlabel "FileSize-BufferSize (Bytes)" +set ylabel "Total time (Seconds)" + +# Uncomment and set xrange and yrange if necessary +# set xrange [lower:upper] +# set yrange [lower:upper] + +# Loop through the data file, plotting 12 rows at a time + +do for [i=0:int((nlines-1)/chunk_size)] { + # Set the output file name + set output sprintf("output_%03d.png", i + 1) + + # Calculate start and end rows for the current chunk + start = 0 + i * chunk_size + end = start + chunk_size - 1 + + # Plot the data from the CSV file for the current chunk + plot for [col=3:*] for [filename in datafile] filename using col:xticlabels(strcol(1)."-".strcol(2)) every ::start::end with linespoints +} +