mirror of
https://github.com/martijnvanbrummelen/nwipe.git
synced 2026-03-04 11:52:10 +00:00
Merge pull request #700 from Knogle/auto-selection-for-prng-and-benchmark-no-gui-option
prng, gui: add PRNG benchmarking, auto-selection and CLI support
This commit is contained in:
@@ -209,15 +209,6 @@ static void nwipe_gui_draw_acs_prefix( WINDOW* w, int y, int x )
|
||||
mvwaddch( w, y, x + 3, ' ' );
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const nwipe_prng_t* prng;
|
||||
double mbps;
|
||||
double seconds;
|
||||
unsigned long long bytes;
|
||||
int rc;
|
||||
} nwipe_prng_bench_result_t;
|
||||
|
||||
static double nwipe_gui_monotonic_seconds( void )
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
119
src/nwipe.c
119
src/nwipe.c
@@ -96,6 +96,25 @@ int devnamecmp( const void* a, const void* b )
|
||||
return ( ret );
|
||||
}
|
||||
|
||||
static int nwipe_prng_bench_cmp_desc( const void* a, const void* b )
|
||||
{
|
||||
const nwipe_prng_bench_result_t* A = (const nwipe_prng_bench_result_t*) a;
|
||||
const nwipe_prng_bench_result_t* B = (const nwipe_prng_bench_result_t*) b;
|
||||
|
||||
/* successful results first */
|
||||
if( A->rc != 0 && B->rc == 0 )
|
||||
return 1;
|
||||
if( A->rc == 0 && B->rc != 0 )
|
||||
return -1;
|
||||
|
||||
/* then sort by MB/s */
|
||||
if( A->mbps < B->mbps )
|
||||
return 1;
|
||||
if( A->mbps > B->mbps )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NWIPE_PDF_DIR_MODE ( S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH )
|
||||
/* -> 0755: rwx for owner, r-x for group and others */
|
||||
|
||||
@@ -339,6 +358,106 @@ int main( int argc, char** argv )
|
||||
/* Log OS info */
|
||||
nwipe_log_OSinfo();
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* PRNG benchmark / auto-select (runs before device scan)
|
||||
* ------------------------------------------------------------ */
|
||||
if( nwipe_options.prng_benchmark_only || nwipe_options.prng_auto )
|
||||
{
|
||||
/* tune defaults */
|
||||
const size_t io_block = 4 * 1024 * 1024; /* 4 MiB RAM buffer blocks */
|
||||
double seconds = nwipe_options.prng_bench_seconds;
|
||||
|
||||
/* If user requested auto-select and didn't override seconds, keep it short */
|
||||
if( nwipe_options.prng_auto )
|
||||
{
|
||||
if( seconds <= 0.0 )
|
||||
seconds = 0.25;
|
||||
/* Optional: if you consider "1.0" to be the default and want auto shorter:
|
||||
* if( seconds == 1.0 ) seconds = 0.25;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if( seconds <= 0.0 )
|
||||
seconds = 1.0;
|
||||
}
|
||||
|
||||
nwipe_prng_bench_result_t results[16];
|
||||
memset( results, 0, sizeof( results ) );
|
||||
|
||||
if( nwipe_options.prng_benchmark_only )
|
||||
{
|
||||
int n = nwipe_prng_benchmark_all(
|
||||
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ) );
|
||||
if( n <= 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark failed (no results)." );
|
||||
printf( "PRNG benchmark failed (no results).\n" );
|
||||
cleanup();
|
||||
exit( 3 );
|
||||
}
|
||||
|
||||
qsort( results, (size_t) n, sizeof( results[0] ), nwipe_prng_bench_cmp_desc );
|
||||
|
||||
/* Print to console + log */
|
||||
printf( "\nPRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB\n", seconds, io_block / ( 1024 * 1024 ) );
|
||||
printf( "---------------------------------------------------\n" );
|
||||
|
||||
nwipe_log( NWIPE_LOG_INFO,
|
||||
"PRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB",
|
||||
seconds,
|
||||
io_block / ( 1024 * 1024 ) );
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
if( results[i].rc == 0 )
|
||||
{
|
||||
printf( "%2d) %-40s %10.1f MB/s\n", i + 1, results[i].prng->label, results[i].mbps );
|
||||
nwipe_log( NWIPE_LOG_INFO,
|
||||
"PRNG bench %2d) %-40s %10.1f MB/s",
|
||||
i + 1,
|
||||
results[i].prng->label,
|
||||
results[i].mbps );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%2d) %-40s (failed: rc=%d)\n", i + 1, results[i].prng->label, results[i].rc );
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"PRNG bench %2d) %-40s (failed: rc=%d)",
|
||||
i + 1,
|
||||
results[i].prng->label,
|
||||
results[i].rc );
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\n" );
|
||||
cleanup();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/* --prng=auto path */
|
||||
if( nwipe_options.prng_auto )
|
||||
{
|
||||
const nwipe_prng_t* best = nwipe_prng_select_fastest(
|
||||
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ) );
|
||||
|
||||
if( best != NULL )
|
||||
{
|
||||
/* Apply selection */
|
||||
nwipe_options.prng = (nwipe_prng_t*) best;
|
||||
|
||||
nwipe_log( NWIPE_LOG_INFO, "Auto-selected fastest PRNG: %s", best->label );
|
||||
printf( "Auto-selected fastest PRNG: %s\n", best->label );
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"Auto PRNG selection: no working PRNG found, keeping configured default." );
|
||||
printf( "Auto PRNG selection: no working PRNG found, keeping configured default.\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that hdparm exists, we use hdparm for some HPA/DCO detection etc, if not
|
||||
* exit nwipe. These checks are required if the PATH environment is not setup !
|
||||
* Example: Debian sid 'su' as opposed to 'su -'
|
||||
|
||||
@@ -91,6 +91,8 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
|
||||
/* The Pseudo Random Number Generator. */
|
||||
{ "prng", required_argument, 0, 'p' },
|
||||
{ "prng-benchmark", no_argument, 0, 0 },
|
||||
{ "prng-bench-seconds", required_argument, 0, 0 },
|
||||
|
||||
/* The number of times to run the method. */
|
||||
{ "rounds", required_argument, 0, 'r' },
|
||||
@@ -139,6 +141,10 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
nwipe_options.autonuke = 0;
|
||||
nwipe_options.autopoweroff = 0;
|
||||
nwipe_options.method = &nwipe_random;
|
||||
nwipe_options.prng_auto = 0;
|
||||
nwipe_options.prng_benchmark_only = 0;
|
||||
nwipe_options.prng_bench_seconds = 1.0; /* default for interactive / manual */
|
||||
|
||||
/*
|
||||
* 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,
|
||||
@@ -571,6 +577,21 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
break;
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "prng-benchmark" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_benchmark_only = 1;
|
||||
break;
|
||||
}
|
||||
if( strcmp( nwipe_options_long[i].name, "prng-bench-seconds" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_bench_seconds = atof( optarg );
|
||||
if( nwipe_options.prng_bench_seconds < 0.05 )
|
||||
nwipe_options.prng_bench_seconds = 0.05;
|
||||
if( nwipe_options.prng_bench_seconds > 10.0 )
|
||||
nwipe_options.prng_bench_seconds = 10.0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* getopt_long should raise on invalid option, so we should never get here. */
|
||||
exit( EINVAL );
|
||||
|
||||
@@ -737,6 +758,12 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
|
||||
case 'p': /* PRNG option. */
|
||||
|
||||
if( strcmp( optarg, "auto" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_auto = 1;
|
||||
/* keep current default as fallback until autoselect runs */
|
||||
break;
|
||||
}
|
||||
if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_twister;
|
||||
@@ -996,6 +1023,18 @@ void display_help()
|
||||
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|xoroshiro256_prng|aes_ctr_prng)\n" );
|
||||
puts( " --prng=auto" );
|
||||
puts( " Automatically benchmark all available PRNGs at startup and" );
|
||||
puts( " select the fastest one for the current hardware." );
|
||||
puts( "" );
|
||||
puts( " --prng-benchmark" );
|
||||
puts( " Run a RAM-only PRNG throughput benchmark and exit." );
|
||||
puts( " Prints a sorted leaderboard (MB/s). No wipe is performed." );
|
||||
puts( "" );
|
||||
puts( " --prng-bench-seconds=N" );
|
||||
puts( " Seconds per PRNG during benchmarking (default: 1.0)." );
|
||||
puts( " For --prng=auto this is automatically reduced unless set." );
|
||||
|
||||
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" );
|
||||
|
||||
@@ -58,6 +58,9 @@ typedef struct
|
||||
int nowait; // Do not wait for a final key before exiting.
|
||||
int nosignals; // Do not allow signals to interrupt a wipe.
|
||||
int nogui; // Do not show the GUI.
|
||||
int prng_auto; /* 1 = auto-select fastest PRNG at startup */
|
||||
int prng_benchmark_only; /* 1 = run PRNG benchmark and exit (nogui-friendly) */
|
||||
double prng_bench_seconds; /* seconds per PRNG (default e.g. 0.25 for auto, 1.0 for manual) */
|
||||
char* banner; // The product banner shown on the top line of the screen.
|
||||
void* method; // A function pointer to the wipe method that will be used.
|
||||
char logfile[FILENAME_MAX]; // The filename to log the output to.
|
||||
|
||||
179
src/prng.c
179
src/prng.c
@@ -44,6 +44,15 @@ nwipe_prng_t nwipe_xoroshiro256_prng = { "XORoshiro-256", nwipe_xoroshiro256_prn
|
||||
/* AES-CTR-NI PRNG Structure */
|
||||
nwipe_prng_t nwipe_aes_ctr_prng = { "AES-CTR (Kernel)", nwipe_aes_ctr_prng_init, nwipe_aes_ctr_prng_read };
|
||||
|
||||
static const nwipe_prng_t* all_prngs[] = {
|
||||
&nwipe_twister,
|
||||
&nwipe_isaac,
|
||||
&nwipe_isaac64,
|
||||
&nwipe_add_lagg_fibonacci_prng,
|
||||
&nwipe_xoroshiro256_prng,
|
||||
&nwipe_aes_ctr_prng,
|
||||
};
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
@@ -681,3 +690,173 @@ int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE )
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* PRNG benchmark / auto-selection core
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
static double nwipe_prng_monotonic_seconds( void )
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime( CLOCK_MONOTONIC, &ts );
|
||||
return (double) ts.tv_sec + (double) ts.tv_nsec / 1000000000.0;
|
||||
}
|
||||
|
||||
/* einfacher LCG zum Seed-Befüllen – nur für Benchmark, kein Kryptokram */
|
||||
static void nwipe_prng_make_seed( unsigned char* seed, size_t len )
|
||||
{
|
||||
unsigned long t = (unsigned long) time( NULL );
|
||||
unsigned long x = ( t ^ 0xA5A5A5A5UL ) + (unsigned long) (uintptr_t) seed;
|
||||
|
||||
for( size_t i = 0; i < len; i++ )
|
||||
{
|
||||
x = x * 1664525UL + 1013904223UL;
|
||||
seed[i] = (unsigned char) ( ( x >> 16 ) & 0xFF );
|
||||
}
|
||||
}
|
||||
|
||||
static void* nwipe_prng_alloc_aligned( size_t alignment, size_t size )
|
||||
{
|
||||
void* p = NULL;
|
||||
if( posix_memalign( &p, alignment, size ) != 0 )
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void nwipe_prng_bench_one( const nwipe_prng_t* prng,
|
||||
nwipe_prng_bench_result_t* out,
|
||||
void* io_buf,
|
||||
size_t io_block,
|
||||
double seconds_per_prng )
|
||||
{
|
||||
void* state = NULL;
|
||||
|
||||
unsigned char seedbuf[4096];
|
||||
nwipe_entropy_t seed;
|
||||
seed.s = (u8*) seedbuf;
|
||||
seed.length = sizeof( seedbuf );
|
||||
nwipe_prng_make_seed( seedbuf, sizeof( seedbuf ) );
|
||||
|
||||
out->prng = prng;
|
||||
out->mbps = 0.0;
|
||||
out->seconds = 0.0;
|
||||
out->bytes = 0;
|
||||
out->rc = 0;
|
||||
|
||||
int rc = prng->init( &state, &seed );
|
||||
if( rc != 0 )
|
||||
{
|
||||
out->rc = rc;
|
||||
if( state )
|
||||
free( state );
|
||||
return;
|
||||
}
|
||||
|
||||
const double t0 = nwipe_prng_monotonic_seconds();
|
||||
double now = t0;
|
||||
|
||||
while( ( now - t0 ) < seconds_per_prng )
|
||||
{
|
||||
rc = prng->read( &state, io_buf, io_block );
|
||||
if( rc != 0 )
|
||||
{
|
||||
out->rc = rc;
|
||||
break;
|
||||
}
|
||||
|
||||
out->bytes += (unsigned long long) io_block;
|
||||
now = nwipe_prng_monotonic_seconds();
|
||||
}
|
||||
|
||||
out->seconds = now - t0;
|
||||
if( out->rc == 0 && out->seconds > 0.0 )
|
||||
{
|
||||
out->mbps = ( (double) out->bytes / ( 1024.0 * 1024.0 ) ) / out->seconds;
|
||||
}
|
||||
|
||||
if( state )
|
||||
free( state );
|
||||
}
|
||||
|
||||
int nwipe_prng_benchmark_all( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count )
|
||||
{
|
||||
if( results == NULL || results_count == 0 )
|
||||
return 0;
|
||||
|
||||
/* Anzahl PRNGs begrenzen auf results_count */
|
||||
size_t max = sizeof( all_prngs ) / sizeof( all_prngs[0] );
|
||||
if( results_count < max )
|
||||
max = results_count;
|
||||
|
||||
void* io_buf = nwipe_prng_alloc_aligned( 4096, io_block_bytes );
|
||||
if( !io_buf )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark: unable to allocate %zu bytes buffer", io_block_bytes );
|
||||
return -1;
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < max; i++ )
|
||||
{
|
||||
nwipe_prng_bench_result_t* r = &results[i];
|
||||
memset( r, 0, sizeof( *r ) );
|
||||
nwipe_prng_bench_one( all_prngs[i], r, io_buf, io_block_bytes, seconds_per_prng );
|
||||
}
|
||||
|
||||
free( io_buf );
|
||||
return (int) max;
|
||||
}
|
||||
|
||||
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count )
|
||||
{
|
||||
int n = nwipe_prng_benchmark_all( seconds_per_prng, io_block_bytes, results, results_count );
|
||||
if( n <= 0 )
|
||||
return NULL;
|
||||
|
||||
printf( "Analysing PRNG performance:\n" );
|
||||
fflush( stdout );
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
if( results[i].prng == NULL )
|
||||
continue;
|
||||
|
||||
if( results[i].rc == 0 )
|
||||
printf( "%-22s -> %8.1f MB/s\n", results[i].prng->label, results[i].mbps );
|
||||
else
|
||||
printf( "%-22s -> (failed: rc=%d)\n", results[i].prng->label, results[i].rc );
|
||||
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
const nwipe_prng_t* best = NULL;
|
||||
double best_mbps = 0.0;
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
if( results[i].rc == 0 && results[i].mbps > best_mbps )
|
||||
{
|
||||
best_mbps = results[i].mbps;
|
||||
best = results[i].prng;
|
||||
}
|
||||
}
|
||||
|
||||
if( best == NULL )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_WARNING, "Auto PRNG selection: no successful PRNG benchmark" );
|
||||
printf( "Auto PRNG selection: no successful PRNG benchmark\n" );
|
||||
fflush( stdout );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Selected PRNG: %s\n", best->label );
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
19
src/prng.h
19
src/prng.h
@@ -45,6 +45,25 @@ typedef struct
|
||||
nwipe_prng_read_t read; // Read data from the prng.
|
||||
} nwipe_prng_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const nwipe_prng_t* prng;
|
||||
double mbps;
|
||||
double seconds;
|
||||
unsigned long long bytes;
|
||||
int rc;
|
||||
} nwipe_prng_bench_result_t;
|
||||
|
||||
int nwipe_prng_benchmark_all( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count );
|
||||
|
||||
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count );
|
||||
|
||||
/* Mersenne Twister prototypes. */
|
||||
int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE );
|
||||
int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
|
||||
Reference in New Issue
Block a user