diff --git a/crypto/aes-modes/aes-ctr.c b/crypto/aes-modes/aes-ctr.c new file mode 100644 index 00000000..006e2e1c --- /dev/null +++ b/crypto/aes-modes/aes-ctr.c @@ -0,0 +1,304 @@ +/* aes-ctr.c + * + * Copyright (C) 2006-2025 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 + */ + +/* AES-CTR Example + * This example demonstrates: + * - One-shot encryption using wc_AesCtrEncrypt() + * - Streaming decryption using wc_AesCtrEncrypt() in chunks + * Note: CTR mode uses same function for encrypt/decrypt and inherently + * supports streaming (no padding required) + */ + +#include +#include +#include + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include +#include + +#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) + +#define AES_KEY_SIZE AES_256_KEY_SIZE + +/* Small chunk size to demonstrate streaming */ +#define CHUNK_SIZE 64 + +static int read_file(const char* filename, byte** data, word32* dataSz) +{ + FILE* fp; + long fileSz; + + fp = fopen(filename, "rb"); + if (fp == NULL) { + printf("Error: Cannot open file %s\n", filename); + return -1; + } + + fseek(fp, 0, SEEK_END); + fileSz = ftell(fp); + fseek(fp, 0, SEEK_SET); + + *data = (byte*)malloc(fileSz); + if (*data == NULL) { + fclose(fp); + printf("Error: Memory allocation failed\n"); + return -1; + } + + *dataSz = (word32)fread(*data, 1, fileSz, fp); + fclose(fp); + + return 0; +} + +static int write_file(const char* filename, const byte* data, word32 dataSz) +{ + FILE* fp; + + fp = fopen(filename, "wb"); + if (fp == NULL) { + printf("Error: Cannot create file %s\n", filename); + return -1; + } + + fwrite(data, 1, dataSz, fp); + fclose(fp); + + return 0; +} + +/* One-shot encryption */ +static int encrypt_file(const char* inFile, const char* outFile, + const byte* key, word32 keySz) +{ + Aes aes; + WC_RNG rng; + byte iv[AES_BLOCK_SIZE]; + byte* plaintext = NULL; + byte* ciphertext = NULL; + word32 plaintextSz; + int ret; + + ret = read_file(inFile, &plaintext, &plaintextSz); + if (ret != 0) return ret; + + /* IV + ciphertext */ + ciphertext = (byte*)malloc(plaintextSz + AES_BLOCK_SIZE); + if (ciphertext == NULL) { + free(plaintext); + return -1; + } + + /* Generate random IV/nonce */ + ret = wc_InitRng(&rng); + if (ret != 0) { + free(plaintext); + free(ciphertext); + return ret; + } + + ret = wc_RNG_GenerateBlock(&rng, iv, AES_BLOCK_SIZE); + wc_FreeRng(&rng); + if (ret != 0) { + free(plaintext); + free(ciphertext); + return ret; + } + + /* Initialize AES */ + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret != 0) { + free(plaintext); + free(ciphertext); + return ret; + } + + ret = wc_AesSetKey(&aes, key, keySz, iv, AES_ENCRYPTION); + if (ret != 0) { + wc_AesFree(&aes); + free(plaintext); + free(ciphertext); + return ret; + } + + /* One-shot encrypt */ + ret = wc_AesCtrEncrypt(&aes, ciphertext + AES_BLOCK_SIZE, plaintext, + plaintextSz); + wc_AesFree(&aes); + + if (ret != 0) { + free(plaintext); + free(ciphertext); + return ret; + } + + /* Prepend IV to output */ + memcpy(ciphertext, iv, AES_BLOCK_SIZE); + + ret = write_file(outFile, ciphertext, plaintextSz + AES_BLOCK_SIZE); + + free(plaintext); + free(ciphertext); + + printf("AES-CTR encryption complete (one-shot)\n"); + return ret; +} + +/* Streaming decryption - process in chunks */ +static int decrypt_file(const char* inFile, const char* outFile, + const byte* key, word32 keySz) +{ + Aes aes; + byte iv[AES_BLOCK_SIZE]; + byte* ciphertext = NULL; + byte* plaintext = NULL; + word32 ciphertextSz; + word32 plaintextSz; + word32 offset; + word32 chunkSz; + int ret; + + ret = read_file(inFile, &ciphertext, &ciphertextSz); + if (ret != 0) return ret; + + if (ciphertextSz < AES_BLOCK_SIZE) { + free(ciphertext); + printf("Error: File too small\n"); + return -1; + } + + /* Extract IV from beginning */ + memcpy(iv, ciphertext, AES_BLOCK_SIZE); + + plaintextSz = ciphertextSz - AES_BLOCK_SIZE; + plaintext = (byte*)malloc(plaintextSz); + if (plaintext == NULL) { + free(ciphertext); + return -1; + } + + /* Initialize AES */ + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret != 0) { + free(ciphertext); + free(plaintext); + return ret; + } + + ret = wc_AesSetKey(&aes, key, keySz, iv, AES_ENCRYPTION); + if (ret != 0) { + wc_AesFree(&aes); + free(ciphertext); + free(plaintext); + return ret; + } + + /* Streaming decrypt - process in chunks + * Note: CTR mode uses same function for encrypt and decrypt */ + printf("AES-CTR streaming decryption:\n"); + offset = 0; + while (offset < plaintextSz) { + chunkSz = plaintextSz - offset; + if (chunkSz > CHUNK_SIZE) { + chunkSz = CHUNK_SIZE; + } + + ret = wc_AesCtrEncrypt(&aes, plaintext + offset, + ciphertext + AES_BLOCK_SIZE + offset, chunkSz); + if (ret != 0) { + wc_AesFree(&aes); + free(ciphertext); + free(plaintext); + return ret; + } + + printf(" Decrypted chunk: offset=%u, size=%u\n", offset, chunkSz); + offset += chunkSz; + } + + wc_AesFree(&aes); + + ret = write_file(outFile, plaintext, plaintextSz); + + free(ciphertext); + free(plaintext); + + printf("AES-CTR decryption complete (streaming)\n"); + return ret; +} + +int main(int argc, char** argv) +{ + byte key[AES_KEY_SIZE]; + int ret; + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + printf("Encrypts input file (one-shot), then decrypts to output file (streaming)\n"); + return 1; + } + + wolfCrypt_Init(); + + /* Use a fixed key for demonstration */ + memset(key, 0x07, AES_KEY_SIZE); + + /* Encrypt to temporary file */ + ret = encrypt_file(argv[1], "temp_encrypted.bin", key, AES_KEY_SIZE); + if (ret != 0) { + printf("Encryption failed: %d\n", ret); + wolfCrypt_Cleanup(); + return 1; + } + + /* Decrypt to output file */ + ret = decrypt_file("temp_encrypted.bin", argv[2], key, AES_KEY_SIZE); + if (ret != 0) { + printf("Decryption failed: %d\n", ret); + wolfCrypt_Cleanup(); + return 1; + } + + /* Clean up temp file */ + remove("temp_encrypted.bin"); + + printf("Success! Decrypted file written to %s\n", argv[2]); + + wolfCrypt_Cleanup(); + return 0; +} + +#else + +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + printf("AES-CTR not compiled in. Enable with WOLFSSL_AES_COUNTER\n"); + return 0; +} + +#endif