From 25693a99b3696536eb4bb1457187a73897633a8b Mon Sep 17 00:00:00 2001 From: Fabian Druschke Date: Sun, 8 Sep 2024 16:56:03 +0200 Subject: [PATCH] Implement high-quality random number generation using AES-CTR mode with OpenSSL and AES-NI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces significant improvements to `nwipe`'s random number generation by implementing AES-CTR mode using OpenSSL with AES-NI hardware acceleration. The changes include: - **AES-CTR PRNG Implementation**: A new pseudo-random number generator (PRNG) based on AES-128 in counter (CTR) mode has been added. This PRNG uses OpenSSL’s EVP API for cryptographic operations, ensuring strong random number generation. - **AES-NI Hardware Support**: The PRNG now detects if the system supports AES-NI and uses hardware acceleration when available for improved performance, particularly on 64-bit systems. A new function `has_aes_ni()` checks for AES-NI support, ensuring that the fastest available option is used. - **Default PRNG Selection**: For 64-bit systems, AES-CTR is now the default PRNG due to its higher performance and security. On 32-bit systems, the XORoshiro-256 PRNG remains the default due to performance considerations. - **Improved Error Handling**: Error handling has been extensively revised. The AES-CTR PRNG now provides detailed error messages, including the detection of potential OpenSSL failures, and uses structured logging (`NWIPE_LOG_DEBUG` and `NWIPE_LOG_NOTICE`) to report PRNG initialization and errors. - **Memory Management**: Smart pointers have been introduced to manage memory automatically within the AES-CTR PRNG, preventing memory leaks. All memory used by the OpenSSL contexts (EVP_MD_CTX and EVP_CIPHER_CTX) is now properly cleaned up after use. - **Performance Enhancements**: The PRNG’s performance has been optimized, especially for 64-bit systems. Additionally, the code now checks for edge cases when reading remaining bytes, and memset is used to handle uninitialized memory areas. - **Formatting and Code Cleanup**: Deprecated SHA256 calls have been replaced with OpenSSL’s EVP API. Multiple formatting issues in various source files (`pass.c`, `options.c`, `gui.c`, etc.) have been fixed, improving code readability and compliance with the coding standards. - **Regression Fixes**: Several small regressions from previous changes have been fixed, including uninitialized variables, implicit function declarations, and incorrect function calls that previously led to warnings and potential crashes (e.g., segfaults in `pass.c`). - **Logging and Debugging**: Improved logging messages and detailed debug information have been added, particularly around the AES-CTR initialization and error handling paths. This implementation significantly improves the security, performance, and reliability of random number generation in `nwipe` and ensures that the tool is better equipped for modern cryptographic standards. Squashed commit of the following: commit 03284d738ca3d800638f054e07823fc371d7a027 Author: Fabian Druschke Date: Fri Aug 23 19:17:05 2024 +0200 Fixed formatting commit 31df3ebf1effad14baa9ca51b0a88f2f025857ad Author: Fabian Druschke Date: Fri Aug 23 19:15:01 2024 +0200 Fixed type error on i686 - uint64_t for AES-CTR commit e56a47c69c26888914da6e1b99fec02ea752d57d Merge: 3465260 d1edd05 Author: Fabian Druschke Date: Tue Aug 20 10:39:26 2024 +0100 Merge branch 'master' into aes-ctr commit 34652608e521d79957d8f1880bc762732005a375 Merge: 1bf5ff1 5140f92 Author: Fabian Druschke Date: Fri May 10 01:41:09 2024 +0200 Merge branch 'master' into aes-ctr commit 1bf5ff100d33ca7f49c6d8bd932a9f6e4b8beb7a Author: Fabian Druschke Date: Fri May 10 01:14:11 2024 +0200 Added check for return value in prng.c and nwipe_log accordingly. commit 9015a32fe3da703c0836a27db1b848654037c376 Author: Fabian Druschke Date: Fri May 10 00:11:59 2024 +0200 Removed error handling using goto, now returning instead. Changed from void to int functions, removed cleanup(); on error , instead relying on cleanup routine on exit. commit 10ea2bbd096747b48931004be42c36d646c4ef05 Author: Fabian Druschke Date: Mon Apr 15 13:47:05 2024 +0200 Adapted the aes-ctr-prng accordingly, to report SANITY level errors, in case of failure. commit 71e7f8f49d66d04b12a4905460eead2252aafa64 Author: Fabian Druschke Date: Mon Apr 15 13:46:35 2024 +0200 Added case handling for NWIPE_LOG_LEVEL_SANITY, providing the github issue link, if a SANITY level error occurs. commit 0f3e7f5ba03c0d4694e8536a5675337cb1b40b96 Author: Fabian Druschke Date: Mon Apr 15 00:40:22 2024 +0200 Added check in pass.c wether nwipe_aes_ctr_prng is being used or not, if not a segfault was the result. Now fixed. commit ce09d8e4dcfdb62179c9aa6e01d2aa45d14a5565 Author: Fabian Druschke Date: Sat Apr 13 00:58:34 2024 +0200 Missing NWIPE_LOG_NOTICE for AES-CTR init changed to DEBUG level. commit 8b284f3f24cf7b1ab83c716083145eb28272f312 Author: Fabian Druschke Date: Sat Apr 13 00:50:25 2024 +0200 Changed notification for successful PRNG init for AES-CTR to NWIPE_LOG_DEBUG. commit 8702cc3a57205668c28cf4c4d46e983819ee1102 Author: Fabian Druschke Date: Sat Apr 13 00:13:51 2024 +0200 Added missing cleanup routine in prior commit in aes_ctr_prng.c and header definitions. commit 35cd0559558d74387345e7df94edc9bd3a0ff2b8 Author: Fabian Druschke Date: Sat Apr 13 00:12:01 2024 +0200 Fixed formatting in pass.c commit beff746982065121059cb02ae27b4601fae4ff0e Author: Fabian Druschke Date: Sat Apr 13 00:11:28 2024 +0200 Added cleanup routine aes_ctr_prng_general_cleanup() after nwipe_random_pass and nwipe_random_verify in order to cleanup PRNG state. commit 1a9520234af93602684042be19309357af1a0e1a Author: Fabian Druschke Date: Fri Apr 12 23:25:04 2024 +0200 Part of the comments were missing, fixed. commit a65410a1861a94fee5d652ff1363a22d46698d3f Author: Fabian Druschke Date: Fri Apr 12 23:20:41 2024 +0200 Added extensive error handling, in order to check for OpenSSL library malfunction. commit 65189630660e12b066596b089f88935ce2931a77 Author: Fabian Druschke Date: Thu Apr 11 00:20:54 2024 +0200 Improved PRNG description for AES-CTR-256 in gui.c commit fe493cfbbd76c3c8e03a7237138690d02682fe1e Author: Fabian Druschke Date: Wed Apr 10 23:51:12 2024 +0200 Added function has_aes_ni() in order to check for AES-Ni support, and set the PRNG accordingly. commit adcd442e0efc0063f4b1fc288022e60d97eec72e Author: Fabian Druschke Date: Wed Apr 10 15:40:24 2024 +0200 Handle edge case for remaining bytes in nwipe_aes_ctr_prng_read using memset. commit ce2db63c985481a353c0a6a0f551d98cbe028afc Author: Fabian Druschke Date: Wed Apr 10 15:08:25 2024 +0200 Fixed comments, indicating AES-CTR-128 instead of 256 bit commit da53ee0d7cef01deb96f449e68adf944a6e73b61 Author: Fabian Druschke Date: Wed Apr 10 14:53:45 2024 +0200 Reverted by mistake nwipe_random back to nwipe_dodshort, now compliant with master commit e47923916a20e92ead486447b8085ca5061fd7b4 Author: Fabian Druschke Date: Wed Apr 10 13:23:36 2024 +0200 Fixed formatting commit e0d95844c65ef6cf07edd0ab1688a1a5927fe0d6 Author: Fabian Druschke Date: Wed Apr 10 13:22:25 2024 +0200 Minor changes, added comments for further explanation. commit 7410d21b7111aa42f8d4ad84805e4f7237783166 Author: Fabian Druschke Date: Tue Apr 9 12:49:02 2024 +0200 Fixed uninitialized temp_buffer in aes_ctr_prng_genrand_uint256_to_buf commit cd5f071912a92f8dae7002d9cfecb8c33074e58d Merge: 3cb78ca 2809580 Author: PartialVolume <22084881+PartialVolume@users.noreply.github.com> Date: Sun Apr 7 22:22:55 2024 +0100 Merge branch 'master' into aes-ctr commit 3cb78ca19d50fc876fb11bcaa746fd311a47cfd2 Author: Fabian Druschke Date: Sun Mar 31 14:03:38 2024 +0200 Added error checking and nwipe_log to aes_ctr_prng.c commit 20fea0d80b8bf60b76c1e024d4b966e1333dd09c Author: Fabian Druschke Date: Sun Mar 31 13:54:54 2024 +0200 Only C implementation, removed CPP here. commit d5b39f6fd48176cba1484ba14eec3f5d4fda4ada Author: Fabian Druschke Date: Sun Mar 31 00:43:58 2024 +0100 Introduced smart pointers to manage memory for EVP_MD_CTX and EVP_CIPHER_CTX within the AES CTR PRNG C++ implementation. This ensures automatic resource release, preventing memory leaks and enhancing code safety. commit f6aeae3a15b85cba7988bd15d9d32bb89eefd5a1 Author: Fabian Druschke Date: Sun Mar 31 00:34:15 2024 +0100 Created seamless integrated .cpp AES-CTR-PRNG in order to avoid memory issues. commit eecddb226230d112775d042ef47d0fde92cfe09f Author: Knogle Date: Sun Mar 24 22:48:06 2024 +0000 aes_ctr_prng_init was missing in header, causing implicit declaration warnings. commit 8fe4db46b1ad7466dfdf78f2108c927b2ac3c981 Author: Knogle Date: Sun Mar 24 18:30:52 2024 +0000 Replaced traditional deprecated SHA256 declarations with EVP-API infrastructure commit 55472fb0e85ad5b9ea2ae9eed1f1b38f7508db8f Author: Knogle Date: Sun Mar 24 03:43:36 2024 +0000 To consider, AES-128-CTR as default option for 64-Bit, and Xoroshiro-256 as default option for 32-Bit due to performance and quality reasons. commit 1a964bca7eb27cd96e2329ff80623758e28d88b4 Author: Fabian Druschke Date: Sat Mar 23 19:12:24 2024 -0300 Fixed missing XORoshiro-256 in options.c bottom section. Added AES-128-CTR OpenSSL descriptions. commit cf9822a5462c482b32907af14c0a4c51f02b689c Author: Fabian Druschke Date: Sat Mar 23 18:48:38 2024 -0300 Several changes, adding AES-128 using libssl in CTR mode as new PRNG, in experimental state. Fixed formatting, fixed AES PRNG header. --- configure.ac | 18 +++++ src/Makefile.am | 2 +- src/aes/aes_ctr_prng.c | 160 +++++++++++++++++++++++++++++++++++++++++ src/aes/aes_ctr_prng.h | 51 +++++++++++++ src/gui.c | 55 +++++++++++++- src/logging.c | 4 ++ src/nwipe.h | 2 + src/options.c | 116 +++++++++++++++++++++--------- src/options.h | 14 ++++ src/pass.c | 19 +++++ src/prng.c | 125 ++++++++++++++++++++++++++++---- src/prng.h | 7 ++ 12 files changed, 524 insertions(+), 49 deletions(-) create mode 100644 src/aes/aes_ctr_prng.c create mode 100644 src/aes/aes_ctr_prng.h diff --git a/configure.ac b/configure.ac index 2720d3e9..75103662 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,24 @@ PKG_CHECK_MODULES( )] ) +PKG_CHECK_MODULES( + [OPENSSL], + [openssl], + [ + CFLAGS="${CFLAGS} ${OPENSSL_CFLAGS}" + LIBS="${LIBS} ${OPENSSL_LIBS}" + ], + [AC_CHECK_LIB([ssl], [SSL_library_init], [ + LIBS="-lssl -lcrypto $LIBS" + AC_CHECK_HEADERS(openssl/ssl.h,, [ + AC_CHECK_HEADERS(openssl/crypto.h, [ + AC_DEFINE([OPENSSL_IN_SUBDIR], [openssl/], [Look for openssl headers in subdir]) + ], [AC_MSG_ERROR([openssl headers not found])]) + ]) + ], [AC_MSG_ERROR([OpenSSL development library not found])] + )] +) + PKG_CHECK_MODULES( [LIBCONFIG], [libconfig], diff --git a/src/Makefile.am b/src/Makefile.am index ac652c91..07494fdb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,5 +6,5 @@ AM_LDFLAGS = # this lists the binaries to produce, the (non-PHONY, binary) targets in # the previous manual Makefile bin_PROGRAMS = nwipe -nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h alfg/add_lagg_fibonacci_prng.h alfg/add_lagg_fibonacci_prng.c xor/xoroshiro256_prng.h xor/xoroshiro256_prng.c pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c +nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h alfg/add_lagg_fibonacci_prng.h alfg/add_lagg_fibonacci_prng.c xor/xoroshiro256_prng.h xor/xoroshiro256_prng.c aes/aes_ctr_prng.h aes/aes_ctr_prng.c pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c nwipe_LDADD = $(PARTED_LIBS) $(LIBCONFIG) diff --git a/src/aes/aes_ctr_prng.c b/src/aes/aes_ctr_prng.c new file mode 100644 index 00000000..e244a0a6 --- /dev/null +++ b/src/aes/aes_ctr_prng.c @@ -0,0 +1,160 @@ +/* + * AES CTR PRNG Implementation + * Author: Fabian Druschke + * Date: 2024-03-13 + * + * This header file contains definitions for the AES (Advanced Encryption Standard) + * implementation in CTR (Counter) mode for pseudorandom number generation, utilizing + * OpenSSL for cryptographic functions. + * + * As the author of this work, I, Fabian Druschke, hereby release this work into the public + * domain. I dedicate any and all copyright interest in this work to the public domain, + * making it free to use for anyone for any purpose without any conditions, unless such + * conditions are required by law. + * + * This software is provided "as is", without warranty of any kind, express or implied, + * including but not limited to the warranties of merchantability, fitness for a particular + * purpose and noninfringement. In no event shall the authors be liable for any claim, + * damages or other liability, whether in an action of contract, tort or otherwise, arising + * from, out of or in connection with the software or the use or other dealings in the software. + * + * USAGE OF OPENSSL IN THIS SOFTWARE: + * This software uses OpenSSL for cryptographic operations. Users are responsible for + * ensuring compliance with OpenSSL's licensing terms. + */ + +#include "aes_ctr_prng.h" +#include +#include +#include +#include +#include +#include + +typedef enum { + NWIPE_LOG_NONE = 0, + NWIPE_LOG_DEBUG, // Debugging messages, detailed for troubleshooting + NWIPE_LOG_INFO, // Informative logs, for regular operation insights + NWIPE_LOG_NOTICE, // Notices for significant but non-critical events + NWIPE_LOG_WARNING, // Warnings about potential errors + NWIPE_LOG_ERROR, // Error messages, significant issues that affect operation + NWIPE_LOG_FATAL, // Fatal errors, require immediate termination of the program + NWIPE_LOG_SANITY, // Sanity checks, used primarily in debugging phases + NWIPE_LOG_NOTIMESTAMP // Log entries without timestamp information +} nwipe_log_t; + +extern void nwipe_log( nwipe_log_t level, const char* format, ... ); + +/* Initializes the AES CTR pseudorandom number generator state. + This function sets up the cryptographic context necessary for generating + pseudorandom numbers using AES in CTR mode. It utilizes SHA-256 to derive + a key from the provided seed, ensuring that the PRNG output is unpredictable + and secure, provided the seed is kept secret and is sufficiently random. + - state: Pointer to the AES CTR PRNG state structure. + - init_key: Array containing the seed for key generation. + - key_length: Length of the seed array. */ +int aes_ctr_prng_init( aes_ctr_state_t* state, unsigned long init_key[], unsigned long key_length ) +{ + assert( state != NULL && init_key != NULL && key_length > 0 ); // Validate inputs + + unsigned char key[32]; // Storage for the 256-bit key + memset( state->ivec, 0, AES_BLOCK_SIZE ); // Clear IV buffer + state->num = 0; // Reset the block counter + memset( state->ecount, 0, AES_BLOCK_SIZE ); // Clear encryption count buffer + + nwipe_log( NWIPE_LOG_DEBUG, "Initializing AES CTR PRNG with provided seed." ); // Log initialization + + EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); // Create new SHA-256 context + if( !mdctx ) + { + nwipe_log( NWIPE_LOG_FATAL, + "Failed to allocate EVP_MD_CTX for SHA-256, return code: %d.", + ERR_get_error() ); // Log context allocation failure + return -1; // Handle error + } + + if( EVP_DigestInit_ex( mdctx, EVP_sha256(), NULL ) != 1 ) + { + nwipe_log( NWIPE_LOG_FATAL, + "SHA-256 context initialization failed, return code: %d.", + ERR_get_error() ); // Log init failure + return -1; // Handle error + } + + EVP_DigestUpdate( + mdctx, (const unsigned char*) init_key, key_length * sizeof( unsigned long ) ); // Process the seed + + if( EVP_DigestFinal_ex( mdctx, key, NULL ) != 1 ) + { + nwipe_log( NWIPE_LOG_FATAL, + "SHA-256 hash finalization failed, return code: %d.", + ERR_get_error() ); // Log finalization failure + return -1; // Handle error + } + EVP_MD_CTX_free( mdctx ); + mdctx = NULL; // Clean up SHA-256 context + + state->ctx = EVP_CIPHER_CTX_new(); // Create new AES-256-CTR context + if( !state->ctx ) + { + nwipe_log( NWIPE_LOG_FATAL, + "Failed to allocate EVP_CIPHER_CTX, return code: %d.", + ERR_get_error() ); // Log cipher context failure + return -1; // Handle error + } + + if( EVP_EncryptInit_ex( state->ctx, EVP_aes_256_ctr(), NULL, key, state->ivec ) != 1 ) + { + nwipe_log( NWIPE_LOG_FATAL, + "AES-256-CTR encryption context initialization failed, return code: %d.", + ERR_get_error() ); // Log encryption init failure + return -1; // Handle error + } + + nwipe_log( NWIPE_LOG_DEBUG, "AES CTR PRNG successfully initialized." ); // Log successful initialization + return 0; // Exit successfully +} +/* Generates pseudorandom numbers and writes them to a buffer. + This function performs the core operation of producing pseudorandom data. + It directly updates the buffer provided, filling it with pseudorandom bytes + generated using the AES-256-CTR mode of operation. + - state: Pointer to the initialized AES CTR PRNG state. + - bufpos: Target buffer where the pseudorandom numbers will be written. */ +int aes_ctr_prng_genrand_uint256_to_buf( aes_ctr_state_t* state, unsigned char* bufpos ) +{ + assert( state != NULL && bufpos != NULL ); // Validate inputs + + unsigned char temp_buffer[32]; // Temporary storage for pseudorandom bytes + memset( temp_buffer, 0, sizeof( temp_buffer ) ); // Zero out temporary buffer + int outlen; // Length of data produced by encryption + + if( EVP_EncryptUpdate( state->ctx, temp_buffer, &outlen, temp_buffer, sizeof( temp_buffer ) ) != 1 ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Failed to generate pseudorandom numbers, return code: %d.", + ERR_get_error() ); // Log generation failure + return -1; // Handle error + } + + memcpy( bufpos, temp_buffer, sizeof( temp_buffer ) ); // Copy pseudorandom bytes to buffer + return 0; // Exit successfully +} +// General cleanup function for AES CTR PRNG +int aes_ctr_prng_general_cleanup( aes_ctr_state_t* state ) +{ + if( state != NULL ) + { + // Free the EVP_CIPHER_CTX if it has been allocated + if( state->ctx ) + { + EVP_CIPHER_CTX_free( state->ctx ); + state->ctx = NULL; // Nullify the pointer after free + } + + // Clear sensitive information from the state + memset( state->ivec, 0, AES_BLOCK_SIZE ); + memset( state->ecount, 0, AES_BLOCK_SIZE ); + state->num = 0; + } + return 0; +} diff --git a/src/aes/aes_ctr_prng.h b/src/aes/aes_ctr_prng.h new file mode 100644 index 00000000..a04abc0a --- /dev/null +++ b/src/aes/aes_ctr_prng.h @@ -0,0 +1,51 @@ +/* + * AES CTR PRNG Definitions + * Author: Fabian Druschke + * Date: 2024-03-13 + * + * This header file contains definitions for the AES (Advanced Encryption Standard) + * implementation in CTR (Counter) mode for pseudorandom number generation, utilizing + * OpenSSL for cryptographic functions. + * + * As the author of this work, I, Fabian Druschke, hereby release this work into the public + * domain. I dedicate any and all copyright interest in this work to the public domain, + * making it free to use for anyone for any purpose without any conditions, unless such + * conditions are required by law. + * + * This software is provided "as is", without warranty of any kind, express or implied, + * including but not limited to the warranties of merchantability, fitness for a particular + * purpose and noninfringement. In no event shall the authors be liable for any claim, + * damages or other liability, whether in an action of contract, tort or otherwise, arising + * from, out of or in connection with the software or the use or other dealings in the software. + * + * USAGE OF OPENSSL IN THIS SOFTWARE: + * This software uses OpenSSL for cryptographic operations. Users are responsible for + * ensuring compliance with OpenSSL's licensing terms. + */ + +#ifndef AES_CTR_RNG_H +#define AES_CTR_RNG_H + +#include +#include +#include + +// Structure to store the state of the AES-CTR random number generator +typedef struct +{ + EVP_CIPHER_CTX* ctx; + unsigned char ivec[AES_BLOCK_SIZE]; + unsigned int num; + unsigned char ecount[AES_BLOCK_SIZE]; +} aes_ctr_state_t; + +// Initializes the AES-CTR random number generator +int aes_ctr_prng_init( aes_ctr_state_t* state, unsigned long init_key[], unsigned long key_length ); + +// Generates a 256-bit random number using AES-CTR and stores it directly in the output buffer +int aes_ctr_prng_genrand_uint256_to_buf( aes_ctr_state_t* state, unsigned char* bufpos ); + +// General cleanup function for AES CTR PRNG +int aes_ctr_prng_general_cleanup( aes_ctr_state_t* state ); + +#endif // AES_CTR_RNG_H diff --git a/src/gui.c b/src/gui.c index b8fa735b..93d722ea 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1616,11 +1616,12 @@ void nwipe_gui_prng( void ) extern nwipe_prng_t nwipe_aes_ctr_prng; extern nwipe_prng_t nwipe_xoroshiro256_prng; extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng; + extern nwipe_prng_t nwipe_aes_ctr_prng; extern int terminate_signal; /* The number of implemented PRNGs. */ - const int count = 5; + const int count = 6; /* The first tabstop. */ const int tab1 = 2; @@ -1662,6 +1663,10 @@ void nwipe_gui_prng( void ) { focus = 4; } + if( nwipe_options.prng == &nwipe_aes_ctr_prng ) + { + focus = 5; + } do { /* Clear the main window. */ @@ -1678,6 +1683,7 @@ void nwipe_gui_prng( void ) mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac64.label ); mvwprintw( main_window, yy++, tab1, " %s", nwipe_add_lagg_fibonacci_prng.label ); mvwprintw( main_window, yy++, tab1, " %s", nwipe_xoroshiro256_prng.label ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_aes_ctr_prng.label ); yy++; /* Print the cursor. */ @@ -1852,6 +1858,49 @@ void nwipe_gui_prng( void ) tab1, "especially for legacy systems, due to its efficiency and minimal demands. " ); break; + case 5: + + mvwprintw( + main_window, yy++, tab1, "AES-256 in Counter Mode (CTR), securely implemented by Fabian Druschke" ); + mvwprintw( main_window, + yy++, + tab1, + "within nwipe using OpenSSL, leverages the cryptographic robustness and " ); + mvwprintw( main_window, + yy++, + tab1, + "efficiency of AES for generating high-quality pseudo-random numbers. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( + main_window, yy++, tab1, "This adaptation emphasizes the utilization of AES New Instructions " ); + mvwprintw( main_window, + yy++, + tab1, + "(AES-NI) starting from the Westmere-EP microarchitecture, enhancing the" ); + mvwprintw( main_window, + yy++, + tab1, + "performance and security for data wiping applications. The integration " ); + mvwprintw( main_window, + yy++, + tab1, + "of AES-NI accelerates cryptographic computations, making AES-256 CTR an" ); + mvwprintw( main_window, yy++, tab1, "ideal choice for efficient and secure data erasure in nwipe." ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( main_window, + yy++, + tab1, + "Compliant with NIST SP 800-38A for cryptographic standards, AES-256 CTR" ); + mvwprintw( + main_window, yy++, tab1, "stands as the world gold standard for data encryption techniques." ); + mvwprintw( main_window, yy++, tab1, "Intended to be used only with 64-Bit CPUs, supporting AES-Ni." ); + break; } /* switch */ @@ -1922,6 +1971,10 @@ void nwipe_gui_prng( void ) { nwipe_options.prng = &nwipe_xoroshiro256_prng; } + if( focus == 5 ) + { + nwipe_options.prng = &nwipe_aes_ctr_prng; + } return; case KEY_BACKSPACE: diff --git a/src/logging.c b/src/logging.c index ca103ef8..fd6ccfe0 100644 --- a/src/logging.c +++ b/src/logging.c @@ -411,6 +411,10 @@ void nwipe_log( nwipe_log_t level, const char* format, ... ) { fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); } + if( level == NWIPE_LOG_SANITY ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Please report this bug to %s." NWIPE_GITHUB_ISSUE_URL ); + } return; } /* nwipe_log */ diff --git a/src/nwipe.h b/src/nwipe.h index 4f1c495d..a15993a3 100644 --- a/src/nwipe.h +++ b/src/nwipe.h @@ -108,6 +108,8 @@ typedef unsigned char u8; #define THREAD_CANCELLATION_TIMEOUT 10 +#define NWIPE_GITHUB_ISSUE_URL "https://github.com/martijnvanbrummelen/nwipe/issues" + /* This is required for ioctl FDFLUSH. */ #include diff --git a/src/options.c b/src/options.c index c855d0e2..98e316ae 100644 --- a/src/options.c +++ b/src/options.c @@ -32,6 +32,40 @@ /* The global options struct. */ nwipe_options_t nwipe_options; +/* + * Executes the CPUID instruction and fills out the provided variables with the results. + * eax: The function/subfunction number to query with CPUID. + * *eax_out, *ebx_out, *ecx_out, *edx_out: Pointers to variables where the CPUID output will be stored. + */ +void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out ) +{ +#if defined( _MSC_VER ) // Microsoft compiler + int registers[4]; + __cpuid( registers, eax ); + *eax_out = registers[0]; + *ebx_out = registers[1]; + *ecx_out = registers[2]; + *edx_out = registers[3]; +#elif defined( __GNUC__ ) // GCC and Clang + __asm__ __volatile__( "cpuid" + : "=a"( *eax_out ), "=b"( *ebx_out ), "=c"( *ecx_out ), "=d"( *edx_out ) + : "a"( eax ) ); +#else +#error "Unsupported compiler" +#endif +} + +/* + * Checks if the AES-NI instruction set is supported by the processor. + * Returns 1 (true) if supported, 0 (false) otherwise. + */ +int has_aes_ni( void ) +{ + uint32_t eax, ebx, ecx, edx; + cpuid( 1, &eax, &ebx, &ecx, &edx ); + return ( ecx & ( 1 << 25 ) ) != 0; // Check if bit 25 in ECX is set +} + int nwipe_options_parse( int argc, char** argv ) { extern char* optarg; // The working getopt option argument. @@ -44,6 +78,7 @@ int nwipe_options_parse( int argc, char** argv ) extern nwipe_prng_t nwipe_isaac64; extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng; extern nwipe_prng_t nwipe_xoroshiro256_prng; + extern nwipe_prng_t nwipe_aes_ctr_prng; /* The getopt() result holder. */ int nwipe_opt; @@ -130,8 +165,26 @@ int nwipe_options_parse( int argc, char** argv ) nwipe_options.autonuke = 0; nwipe_options.autopoweroff = 0; nwipe_options.method = &nwipe_random; - nwipe_options.prng = - ( sizeof( unsigned long int ) >= 8 ) ? &nwipe_xoroshiro256_prng : &nwipe_add_lagg_fibonacci_prng; + /* + * Determines and sets the default PRNG based on AES-NI support and system architecture. + * It selects AES-CTR PRNG if AES-NI is supported, xoroshiro256 for 64-bit systems without AES-NI, + * and add lagged Fibonacci for 32-bit systems. + */ + + if( has_aes_ni() ) + { + nwipe_options.prng = &nwipe_aes_ctr_prng; + } + else if( sizeof( unsigned long int ) >= 8 ) + { + nwipe_options.prng = &nwipe_xoroshiro256_prng; + nwipe_log( NWIPE_LOG_WARNING, "CPU doesn't support AES New Instructions, opting for XORoshiro-256 instead." ); + } + else + { + nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng; + } + nwipe_options.rounds = 1; nwipe_options.noblank = 0; nwipe_options.nousb = 0; @@ -503,6 +556,11 @@ int nwipe_options_parse( int argc, char** argv ) nwipe_options.prng = &nwipe_xoroshiro256_prng; break; } + if( strcmp( optarg, "aes_ctr_prng" ) == 0 ) + { + nwipe_options.prng = &nwipe_aes_ctr_prng; + break; + } /* Else we do not know this PRNG. */ fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg ); @@ -554,6 +612,7 @@ void nwipe_options_log( void ) extern nwipe_prng_t nwipe_isaac64; extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng; extern nwipe_prng_t nwipe_xoroshiro256_prng; + extern nwipe_prng_t nwipe_aes_ctr_prng; /** * Prints a manifest of options to the log. @@ -605,37 +664,29 @@ void nwipe_options_log( void ) { nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" ); } + else if( nwipe_options.prng == &nwipe_add_lagg_fibonacci_prng ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Lagged Fibonacci generator (EXPERIMENTAL!)" ); + } + else if( nwipe_options.prng == &nwipe_xoroshiro256_prng ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = XORoshiro-256 (EXPERIMENTAL!)" ); + } + else if( nwipe_options.prng == &nwipe_aes_ctr_prng ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = AES-CTR New Instructions (EXPERIMENTAL!)" ); + } + else if( nwipe_options.prng == &nwipe_isaac ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" ); + } + else if( nwipe_options.prng == &nwipe_isaac64 ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" ); + } else { - if( nwipe_options.prng == &nwipe_add_lagg_fibonacci_prng ) - { - nwipe_log( NWIPE_LOG_NOTICE, " prng = Lagged Fibonacci generator (EXPERIMENTAL!)" ); - } - else - { - if( nwipe_options.prng == &nwipe_xoroshiro256_prng ) - { - nwipe_log( NWIPE_LOG_NOTICE, " prng = XORoshiro-256 (EXPERIMENTAL!)" ); - } - else - { - if( nwipe_options.prng == &nwipe_isaac ) - { - nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" ); - } - else - { - if( nwipe_options.prng == &nwipe_isaac64 ) - { - nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" ); - } - else - { - nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" ); - } - } - } - } + nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" ); } nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) ); @@ -714,7 +765,8 @@ void display_help() puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" ); puts( " -P, --PDFreportpath=PATH Path to write PDF reports to. Default is \".\"" ); puts( " If set to \"noPDF\" no PDF reports are written.\n" ); - puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng)\n" ); + puts( " -p, --prng=METHOD PRNG option " + "(mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng|xoroshiro256_prng|aes_ctr_prng)\n" ); puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." ); puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" ); puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" ); diff --git a/src/options.h b/src/options.h index 96aeefc3..ffae4d22 100644 --- a/src/options.h +++ b/src/options.h @@ -73,4 +73,18 @@ typedef struct extern nwipe_options_t nwipe_options; +/* + * The cpuid function executes the CPUID instruction and returns the processor feature information. + * Parameters: + * - eax: Input value for the EAX register, specifies the CPUID function to be executed. + * - *eax_out, *ebx_out, *ecx_out, *edx_out: Pointers to variables to store the output of the CPUID instruction. + */ +void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out ); + +/* + * Checks if the AES-NI instruction set is supported on the processor. + * Returns 1 (true) if AES-NI is supported, otherwise 0 (false). + */ +int has_aes_ni( void ); + #endif /* OPTIONS_H_ */ diff --git a/src/pass.c b/src/pass.c index fc48ac13..3da910f8 100644 --- a/src/pass.c +++ b/src/pass.c @@ -31,6 +31,9 @@ #include "pass.h" #include "logging.h" #include "gui.h" +#include "aes/aes_ctr_prng.h" + +extern nwipe_prng_t nwipe_aes_ctr_prng; int nwipe_random_verify( nwipe_context_t* c ) { @@ -215,6 +218,14 @@ int nwipe_random_verify( nwipe_context_t* c ) free( b ); free( d ); + // Cleanup PRNG state at the end of function + // Check before cleaning up AES PRNG state + if( c->prng == &nwipe_aes_ctr_prng ) + { + aes_ctr_prng_general_cleanup( (aes_ctr_state_t*) c->prng_state ); + nwipe_log( NWIPE_LOG_DEBUG, "Called aes_ctr_prng_general_cleanup(), and cleaned up AES context." ); + } + /* We're done. */ return 0; @@ -465,6 +476,14 @@ int nwipe_random_pass( NWIPE_METHOD_SIGNATURE ) return -1; } + // Cleanup PRNG state at the end of function + // Check before cleaning up AES PRNG state + if( c->prng == &nwipe_aes_ctr_prng ) + { + aes_ctr_prng_general_cleanup( (aes_ctr_state_t*) c->prng_state ); + nwipe_log( NWIPE_LOG_DEBUG, "Called aes_ctr_prng_general_cleanup(), and cleaned up AES context." ); + } + /* We're done. */ return 0; diff --git a/src/prng.c b/src/prng.c index abf1b6cc..8ddaa778 100644 --- a/src/prng.c +++ b/src/prng.c @@ -27,6 +27,7 @@ #include "isaac_rand/isaac64.h" #include "alfg/add_lagg_fibonacci_prng.h" //Lagged Fibonacci generator prototype #include "xor/xoroshiro256_prng.h" //XORoshiro-256 prototype +#include "aes/aes_ctr_prng.h" // AES-NI prototype nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read }; @@ -40,6 +41,9 @@ nwipe_prng_t nwipe_add_lagg_fibonacci_prng = { "Lagged Fibonacci generator", /* XOROSHIRO-256 PRNG Structure */ nwipe_prng_t nwipe_xoroshiro256_prng = { "XORoshiro-256", nwipe_xoroshiro256_prng_init, nwipe_xoroshiro256_prng_read }; +/* AES-CTR-NI PRNG Structure */ +nwipe_prng_t nwipe_aes_ctr_prng = { "AES-256-CTR (OpenSSL)", nwipe_aes_ctr_prng_init, nwipe_aes_ctr_prng_read }; + /* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */ static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len ) { @@ -274,21 +278,6 @@ int nwipe_add_lagg_fibonacci_prng_init( NWIPE_PRNG_INIT_SIGNATURE ) return 0; } -/* EXPERIMENTAL implementation of XORoroshiro256 algorithm to provide high-quality, but a lot of random numbers */ -int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE ) -{ - nwipe_log( NWIPE_LOG_NOTICE, "Initialising XORoroshiro-256 PRNG" ); - - if( *state == NULL ) - { - /* This is the first time that we have been called. */ - *state = malloc( sizeof( xoroshiro256_state_t ) ); - } - xoroshiro256_init( (xoroshiro256_state_t*) *state, (uint64_t*) ( seed->s ), seed->length / sizeof( uint64_t ) ); - - return 0; -} - int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE ) { u8* restrict bufpos = buffer; @@ -315,6 +304,22 @@ int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE ) return 0; // Success } +/* EXPERIMENTAL implementation of XORoroshiro256 algorithm to provide high-quality, but a lot of random numbers */ +int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE ) +{ + nwipe_log( NWIPE_LOG_NOTICE, "Initialising XORoroshiro-256 PRNG" ); + + if( *state == NULL ) + { + /* This is the first time that we have been called. */ + *state = malloc( sizeof( xoroshiro256_state_t ) ); + } + xoroshiro256_init( + (xoroshiro256_state_t*) *state, (unsigned long*) ( seed->s ), seed->length / sizeof( unsigned long ) ); + + return 0; +} + int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE ) { u8* restrict bufpos = buffer; @@ -340,3 +345,93 @@ int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE ) return 0; // Success } + +/** + * EPERIMENTAL implementation of AES-256 in counter mode to provide high-quality random numbers. + * Initializes the AES CTR PRNG state. + * @param state A double pointer to the PRNG state structure. If the pointed state is NULL, + * memory will be allocated and initialized for it. + * @param seed A pointer to a seed structure containing the seed data and its length. + * @return int Returns 0 on success, -1 on failure (e.g., memory allocation failure). + */ + +int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE ) +{ + // Log the start of the PRNG initialization process. + nwipe_log( NWIPE_LOG_DEBUG, "Initialising AES CTR PRNG" ); + + // Check if the state pointer is NULL, indicating that it hasn't been allocated yet. + if( *state == NULL ) + { + // Allocate memory for the PRNG state and initialize it to zeros. + *state = calloc( 1, sizeof( aes_ctr_state_t ) ); + + // Check if memory allocation failed. + if( *state == NULL ) + { + // Log the memory allocation failure. + nwipe_log( NWIPE_LOG_FATAL, "Failed to allocate memory for AES CTR PRNG state." ); + return -1; // Return an error code indicating failure. + } + } + + // Initialize the PRNG state with the provided seed. + if( aes_ctr_prng_init( (aes_ctr_state_t*) *state, (uint64_t*) ( seed->s ), seed->length / sizeof( uint64_t ) ) + != 0 ) + { + nwipe_log( NWIPE_LOG_SANITY, "Fatal error occured during PRNG init in OpenSSL." ); + return -1; + } + + return 0; // Indicate success. +} + +/** + * Generates random data using the AES CTR PRNG. + * + * @param state A double pointer to the PRNG state structure. + * @param buffer A pointer to the buffer where the generated random data will be stored. + * @param count The number of bytes of random data to generate. + * @return int Returns 0 on success. + */ +int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE ) +{ + // Pointer to track the current position in the output buffer. + u8* restrict bufpos = buffer; + + // Calculate the number of complete 256-bit blocks to generate. + size_t words = count / SIZE_OF_AES_CTR_PRNG; + + // Loop to fill the buffer with 256-bit blocks of random data. + for( size_t ii = 0; ii < words; ++ii ) + { + // Generate a 256-bit block and write it directly to the buffer. + if( aes_ctr_prng_genrand_uint256_to_buf( (aes_ctr_state_t*) *state, bufpos ) != 0 ) + { + nwipe_log( NWIPE_LOG_SANITY, "Fatal error occured during RNG generation in OpenSSL." ); + return -1; + } + + // Move the buffer position to the start of the next block. + bufpos += SIZE_OF_AES_CTR_PRNG; + } + + // Calculate the number of remaining bytes to generate, if any. + const size_t remain = count % SIZE_OF_AES_CTR_PRNG; + + // Check if there are remaining bytes to generate. + if( remain > 0 ) + { + // Temporary buffer for the last block of random data. + unsigned char temp_output[32]; + memset( temp_output, 0, sizeof( temp_output ) ); + + // Generate one more block of random data. + aes_ctr_prng_genrand_uint256_to_buf( (aes_ctr_state_t*) *state, temp_output ); + + // Copy only the necessary remaining bytes to the output buffer. + memcpy( bufpos, temp_output, remain ); + } + + return 0; // Indicate success. +} diff --git a/src/prng.h b/src/prng.h index a9add099..3e9a7177 100644 --- a/src/prng.h +++ b/src/prng.h @@ -63,6 +63,10 @@ int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE ); int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE ); int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE ); +/* AES-CTR-NI prototypes. */ +int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE ); +int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE ); + /* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */ #define SIZE_OF_TWISTER 4 @@ -76,4 +80,7 @@ int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE ); /* Size of the XOROSHIRO-256 is not derived from the architecture, but it is strictly 32 bytes */ #define SIZE_OF_XOROSHIRO256_PRNG 32 +/* Size of the AES-CTR is not derived from the architecture, but it is strictly 32 bytes */ +#define SIZE_OF_AES_CTR_PRNG 32 + #endif /* PRNG_H_ */