Refactor CPUID/AES-NI detection into cpu_features module and hide AES-CTR PRNG on unsupported platforms (#695)

* Refactor CPUID/AES-NI detection into cpu_features module and hide AES-CTR PRNG on unsupported platforms

This commit improves CPU feature handling and PRNG selection logic in three ways:

1. Introduces a dedicated cpu_features.c/.h module that encapsulates CPUID and
   AES-NI detection. The previous duplicated inline implementations scattered
   across multiple files have been removed to prevent multiple-definition issues
   and ensure consistent CPU capability probing.

2. The AES-CTR PRNG is now selectable only when the running platform supports
   AES-NI. The ncurses GUI automatically hides the AES-CTR option when AES-NI
   is not available, preventing users from choosing a PRNG that would fall back
   to software mode or incur unnecessary slowdown. CLI selection
   (--prng=aes_ctr_prng) is also blocked on non-AES-NI CPUs with a clear error.

3. All modules (options, GUI, PRNG initialisation) now use the central
   has_aes_ni() function, ensuring uniform and future-proof feature detection.

* cpu_features.* missing in commit

* AES-CTR not removed, but disabled when not available on platform
This commit is contained in:
Fabian Druschke
2025-12-11 23:46:13 +01:00
committed by GitHub
parent 9f6f465230
commit 4defd2235a
5 changed files with 184 additions and 83 deletions

View File

@@ -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 aes/aes_ctr_prng.h aes/aes_ctr_prng.cpp 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.cpp 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 cpu_features.h cpu_features.c
nwipe_LDADD = $(PARTED_LIBS) $(LIBCONFIG)

44
src/cpu_features.c Normal file
View File

@@ -0,0 +1,44 @@
#include "cpu_features.h"
/*
* 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( __i386__ ) || defined( __x86_64__ ) /* only on x86 */
#if defined( _MSC_VER ) /* MSVC */
int r[4];
__cpuid( r, eax );
*eax_out = r[0];
*ebx_out = r[1];
*ecx_out = r[2];
*edx_out = r[3];
#elif defined( __GNUC__ ) /* GCC/Clang */
__asm__ __volatile__( "cpuid"
: "=a"( *eax_out ), "=b"( *ebx_out ), "=c"( *ecx_out ), "=d"( *edx_out )
: "a"( eax ) );
#else
#error "Unsupported compiler"
#endif
#else /* not-x86 */
(void) eax;
*eax_out = *ebx_out = *ecx_out = *edx_out = 0; /* CPUID = 0 */
#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 )
{
#if defined( __i386__ ) || defined( __x86_64__ ) /* only for x86 */
uint32_t eax, ebx, ecx, edx;
cpuid( 1, &eax, &ebx, &ecx, &edx );
return ( ecx & ( 1u << 25 ) ) != 0; /* Bit 25 = AES-NI */
#else /* ARM, RISC-V … */
return 0; /* no AES-NI */
#endif
}

18
src/cpu_features.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef NWIPE_CPU_FEATURES_H
#define NWIPE_CPU_FEATURES_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out );
int has_aes_ni( void );
#ifdef __cplusplus
}
#endif
#endif /* NWIPE_CPU_FEATURES_H */

147
src/gui.c
View File

@@ -64,6 +64,7 @@
#include "customers.h"
#include "conf.h"
#include "unistd.h"
#include "cpu_features.h"
#define NWIPE_GUI_PANE 8
@@ -1647,6 +1648,7 @@ void nwipe_gui_prng( void )
extern int terminate_signal;
/* The number of implemented PRNGs. */
const int aes_ctr_available = has_aes_ni();
const int count = 6;
/* The first tabstop. */
@@ -1709,7 +1711,19 @@ 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 );
/* AES-CTR: visually indicate “not available” if no AES-NI */
if( aes_ctr_available )
{
mvwprintw( main_window, yy++, tab1, " %s", nwipe_aes_ctr_prng.label );
}
else
{
/* Dim + “(N/A)” suffix. You can also combine A_REVERSE
* when focused; here only text is dimmed. */
wattron( main_window, A_DIM );
mvwprintw( main_window, yy++, tab1, " %s (N/A)", nwipe_aes_ctr_prng.label );
wattroff( main_window, A_DIM );
}
yy++;
/* Print the cursor. */
@@ -1884,33 +1898,64 @@ 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, "using the Linux kernel's AF_ALG cryptographic API for efficient" );
mvwprintw( main_window, yy++, tab1, "pseudo-random data generation with minimal user-space overhead." );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw(
main_window, yy++, tab1, "This integration leverages potential hardware acceleration via AES-NI," );
mvwprintw(
main_window, yy++, tab1, "making AES-256 CTR ideal for secure and fast data wiping in nwipe." );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"Compliant with NIST SP 800-38A, it is a global standard for encryption." );
mvwprintw(
main_window, yy++, tab1, "Designed for 64-bit Linux systems with kernel CryptoAPI support." );
break;
}
case 5: {
extern int has_aes_ni( void );
const int aes_ctr_available = has_aes_ni();
/* switch */
if( aes_ctr_available )
{
mvwprintw( main_window,
yy++,
tab1,
"AES-256 in Counter Mode (CTR), securely implemented by Fabian Druschke" );
mvwprintw(
main_window, yy++, tab1, "using the Linux kernel's AF_ALG cryptographic API for efficient" );
mvwprintw(
main_window, yy++, tab1, "pseudo-random data generation with minimal user-space overhead." );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This integration leverages potential hardware acceleration via AES-NI," );
mvwprintw(
main_window, yy++, tab1, "making AES-256 CTR ideal for secure and fast data wiping in nwipe." );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"Compliant with NIST SP 800-38A, it is a global standard for encryption." );
mvwprintw(
main_window, yy++, tab1, "Designed for 64-bit Linux systems with kernel CryptoAPI support." );
}
else
{
/* Dimmed, shortened explanation when AES-NI is not available. */
wattron( main_window, A_DIM );
mvwprintw( main_window, yy++, tab1, "AES-256 in Counter Mode (CTR) PRNG (N/A on this system)" );
mvwprintw(
main_window, yy++, tab1, "This PRNG uses AES-NI acceleration via the Linux kernel CryptoAPI." );
mvwprintw( main_window, yy++, tab1, "It is not available because your CPU does not support the" );
mvwprintw( main_window, yy++, tab1, "required AES-NI instruction set." );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw(
main_window, yy++, tab1, "You can still use all other PRNGs (e.g. xoroshiro-256, ISAAC, MT)." );
wattroff( main_window, A_DIM );
}
break;
}
}
/* Add a border. */
box( main_window, 0, 0 );
@@ -1928,9 +1973,9 @@ void nwipe_gui_prng( void )
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get a keystroke.
timeout( -1 ); // Switch back to blocking mode.
timeout( 250 ); /* block getch() for 250ms */
keystroke = getch(); /* Get a keystroke. */
timeout( -1 ); /* Switch back to blocking mode. */
switch( keystroke )
{
@@ -1956,34 +2001,60 @@ void nwipe_gui_prng( void )
case KEY_ENTER:
case ' ':
case 10:
case 10: {
int selection_made = 0;
if( focus == 0 )
{
nwipe_options.prng = &nwipe_twister;
selection_made = 1;
}
if( focus == 1 )
else if( focus == 1 )
{
nwipe_options.prng = &nwipe_isaac;
selection_made = 1;
}
if( focus == 2 )
else if( focus == 2 )
{
nwipe_options.prng = &nwipe_isaac64;
selection_made = 1;
}
if( focus == 3 )
else if( focus == 3 )
{
nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng;
selection_made = 1;
}
if( focus == 4 )
else if( focus == 4 )
{
nwipe_options.prng = &nwipe_xoroshiro256_prng;
selection_made = 1;
}
if( focus == 5 )
else if( focus == 5 )
{
nwipe_options.prng = &nwipe_aes_ctr_prng;
if( aes_ctr_available )
{
/* AES-CTR selectable only when AES-NI is available. */
nwipe_options.prng = &nwipe_aes_ctr_prng;
selection_made = 1;
}
else
{
/* Visible but disabled: do not change selection and
* do not close the dialog. Give feedback only. */
beep();
selection_made = 0;
}
}
return;
if( selection_made )
{
/* Close the dialog only on a valid selection. */
return;
}
/* No valid selection (e.g. AES-CTR without AES-NI): stay in dialog. */
break;
}
case KEY_BACKSPACE:
case KEY_BREAK:

View File

@@ -28,53 +28,11 @@
#include "logging.h"
#include "version.h"
#include "conf.h"
#include "cpu_features.h"
/* 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( __i386__ ) || defined( __x86_64__ ) /* only on x86 */
#if defined( _MSC_VER ) /* MSVC */
int r[4];
__cpuid( r, eax );
*eax_out = r[0];
*ebx_out = r[1];
*ecx_out = r[2];
*edx_out = r[3];
#elif defined( __GNUC__ ) /* GCC/Clang */
__asm__ __volatile__( "cpuid"
: "=a"( *eax_out ), "=b"( *ebx_out ), "=c"( *ecx_out ), "=d"( *edx_out )
: "a"( eax ) );
#else
#error "Unsupported compiler"
#endif
#else /* not-x86 */
(void) eax;
*eax_out = *ebx_out = *ecx_out = *edx_out = 0; /* CPUID = 0 */
#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 )
{
#if defined( __i386__ ) || defined( __x86_64__ ) /* only for x86 */
uint32_t eax, ebx, ecx, edx;
cpuid( 1, &eax, &ebx, &ecx, &edx );
return ( ecx & ( 1u << 25 ) ) != 0; /* Bit 25 = AES-NI */
#else /* ARM, RISC-V … */
return 0; /* no AES-NI */
#endif
}
int nwipe_options_parse( int argc, char** argv )
{
extern char* optarg; // The working getopt option argument.
@@ -661,7 +619,17 @@ int nwipe_options_parse( int argc, char** argv )
}
if( strcmp( optarg, "aes_ctr_prng" ) == 0 )
{
nwipe_options.prng = &nwipe_aes_ctr_prng;
if( has_aes_ni() )
{
nwipe_options.prng = &nwipe_aes_ctr_prng;
}
else
{
fprintf( stderr,
"Error: aes_ctr_prng requires AES-NI on this build, "
"but your CPU does not support AES-NI.\n" );
exit( EINVAL );
}
break;
}