📄 timing.c
字号:
/****************************************************************************
* *
* cryptlib Timing Test Routines *
* Copyright Peter Gutmann 1995-2003 *
* *
****************************************************************************/
/* In order for the DH/DSA timing tests (which use cryptlib-internal
mechanisms) to work, it's necessary to add the following lines of code to
the start of cryptDeviceQueryCapability() in cryptapi.c, before any other
code:
if( cryptAlgo == 1000 )
return( krnlSendMessage( device, IMESSAGE_SETATTRIBUTE_S,
cryptQueryInfo, CRYPT_IATTRIBUTE_KEY_SPKI ) );
if( cryptAlgo == 1001 )
return( krnlSendMessage( device, IMESSAGE_CTX_ENCRYPT,
cryptQueryInfo, sizeof( KEYAGREE_PARAMS ) ) );
if( cryptAlgo == 1002 )
return( krnlSendMessage( device, IMESSAGE_CTX_DECRYPT,
cryptQueryInfo, sizeof( KEYAGREE_PARAMS ) ) );
if( cryptAlgo == 1003 )
return( krnlSendMessage( device, IMESSAGE_CTX_SIGN,
cryptQueryInfo, sizeof( DLP_PARAMS ) ) );
if( cryptAlgo == 1004 )
return( krnlSendMessage( device, IMESSAGE_CTX_SIGCHECK,
cryptQueryInfo, sizeof( DLP_PARAMS ) ) );
This is because the DH/DSA tests are handled by passing magic values into
the function (which is very rarely used normally). Remember to remove
the magic-values check code after use!
Under Unix, the test code can be built with:
cc -c -D__UNIX__ testtime.c
cc -o testtime -lpthread -lresolv testtime.o -L. -lcl
*/
#if defined( _WIN32_WCE ) && _WIN32_WCE < 400
#define assert( x )
#else
#include <assert.h>
#endif /* Systems without assert() */
#include <stdio.h>
#include <stdlib.h>
#include <math.h> /* For sqrt() for standard deviation */
#ifdef _MSC_VER
#include "../cryptlib.h"
#else
#include "cryptlib.h"
#endif /* Braindamaged VC++ include handling */
#ifdef __UNIX__
#include <sys/time.h>
#endif /* __UNIX__ */
/* It's useful to know if we're running under Windows to enable Windows-
specific processing */
#if defined( _WINDOWS ) || defined( WIN32 ) || defined( _WIN32 ) || \
defined( _WIN32_WCE )
#define __WINDOWS__
#endif /* _WINDOWS || WIN32 || _WIN32 || _WIN32_WCE */
/* Various useful types */
#define BOOLEAN int
#define BYTE unsigned char
#ifndef TRUE
#define FALSE 0
#define TRUE !FALSE
#endif /* TRUE */
/* Helper function for systems with no console */
#ifdef _WIN32_WCE
#define printf wcPrintf
#define puts wcPuts
void wcPrintf( const char *format, ... );
void wcPuts( const char *string );
#endif /* Console-less environments */
/* Since high-precision timing is rather OS-dependent, we only enable this
under Windows where we've got guaranteed high-res timer access */
#if defined( __WINDOWS__ ) || defined( __UNIX__ )
/* The number of tests of each algorithm to perform */
#define NO_TESTS 25
/* Normally we use 32-bit time values, however there's also (partial)
support for 64-bit signed values on systems that support this */
#define USE_32BIT_TIME
#if defined( USE_32BIT_TIME )
typedef long HIRES_TIME;
#elif defined( _MSC_VER )
typedef __int64 HIRES_TIME;
#elif defined( __GNUC__ )
typedef long long HIRES_TIME;
#else
typedef unsigned long HIRES_TIME;
#endif /* 64-bit int values */
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Helper functions for systems with no console. These just use the debug
console as stdout */
#ifdef _WIN32_WCE
void wcPrintf( const char *format, ... )
{
wchar_t wcBuffer[ 1024 ];
char buffer[ 1024 ];
va_list argPtr;
va_start( argPtr, format );
vsprintf( buffer, format, argPtr );
va_end( argPtr );
mbstowcs( wcBuffer, buffer, strlen( buffer ) + 1 );
NKDbgPrintfW( wcBuffer );
}
void wcPuts( const char *string )
{
wcPrintf( "%s\n", string );
}
#endif /* Console-less environments */
/* Get high-resolution timing info */
#ifdef USE_32BIT_TIME
static HIRES_TIME timeDiff( HIRES_TIME startTime )
{
HIRES_TIME timeLSB, timeDifference;
#ifdef __WINDOWS__
#if 1
LARGE_INTEGER performanceCount;
/* Sensitive to context switches */
QueryPerformanceCounter( &performanceCount );
timeLSB = performanceCount.LowPart;
#else
FILETIME dummyTime, kernelTime, userTime;
int status;
/* Only accurate to 10ms, returns constant values in VC++ debugger */
GetThreadTimes( GetCurrentThread(), &dummyTime, &dummyTime,
&kernelTime, &userTime );
timeLSB = userTime.dwLowDateTime;
#endif /* 0 */
#else
struct timeval tv;
/* Only accurate to about 1us */
gettimeofday( &tv, NULL );
timeLSB = tv.tv_usec;
#endif /* Windows vs.Unix high-res timing */
/* If we're getting an initial time, return an absolute value */
if( !startTime )
return( timeLSB );
/* We're getting a time difference */
if( startTime < timeLSB )
timeDifference = timeLSB - startTime;
else
#ifdef __WINDOWS__
/* Windows rolls over at INT_MAX */
timeDifference = ( 0xFFFFFFFFUL - startTime ) + 1 + timeLSB;
#else
/* gettimeofday() rolls over at 1M us */
timeDifference = ( 1000000L - startTime ) + timeLSB;
#endif /* __WINDOWS__ */
if( timeDifference <= 0 )
{
printf( "Error: Time difference = %X, startTime = %X, endTime = %X.\n",
timeDifference, startTime, timeLSB );
return( 1 );
}
return( timeDifference );
}
#else
static HIRES_TIME timeDiff( HIRES_TIME startTime )
{
HIRES_TIME timeValue;
#ifdef __WINDOWS__
#if 1
LARGE_INTEGER performanceCount;
/* Sensitive to context switches */
QueryPerformanceCounter( &performanceCount );
timeValue = performanceCount.QuadPart;
#else
FILETIME dummyTime, kernelTime, userTime;
int status;
/* Only accurate to 10ms, returns constant values in VC++ debugger */
GetThreadTimes( GetCurrentThread(), &dummyTime, &dummyTime,
&kernelTime, &userTime );
timeLSB = userTime.dwLowDateTime;
#endif /* 0 */
#else
struct timeval tv;
/* Only accurate to about 1us */
gettimeofday( &tv, NULL );
timeValue = ( ( ( HIRES_TIME ) tv.tv_sec ) << 32 ) | tv.tv_usec;
#endif /* Windows vs.Unix high-res timing */
if( !startTime )
return( timeValue );
return( timeValue - startTime );
}
#endif /* USE_32BIT_TIME */
/* Check for an algorithm/mode */
static BOOLEAN checkLowlevelInfo( const CRYPT_DEVICE cryptDevice,
const CRYPT_ALGO_TYPE cryptAlgo )
{
CRYPT_QUERY_INFO cryptQueryInfo;
const BOOLEAN isDevice = ( cryptDevice != CRYPT_UNUSED ) ? TRUE : FALSE;
int status;
if( isDevice )
status = cryptDeviceQueryCapability( cryptDevice, cryptAlgo,
&cryptQueryInfo );
else
status = cryptQueryCapability( cryptAlgo, &cryptQueryInfo );
if( cryptStatusError( status ) )
{
printf( "crypt%sQueryCapability() reports algorithm %d is not "
"available, status = %d.\n", isDevice ? "Device" : "",
cryptAlgo, status );
return( FALSE );
}
printf( "%s, block size %d bits, keysize %d bits, ", cryptQueryInfo.algoName,
cryptQueryInfo.blockSize << 3, cryptQueryInfo.keySize << 3 );
return( TRUE );
}
/* Load the encryption contexts */
static BOOLEAN loadContexts( CRYPT_CONTEXT *cryptContext, CRYPT_CONTEXT *decryptContext,
const CRYPT_DEVICE cryptDevice,
const CRYPT_ALGO_TYPE cryptAlgo,
const CRYPT_MODE_TYPE cryptMode,
const BYTE *key, const int length )
{
const BOOLEAN isDevice = ( cryptDevice != CRYPT_UNUSED ) ? TRUE : FALSE;
const BOOLEAN hasKey = ( cryptAlgo >= CRYPT_ALGO_FIRST_CONVENTIONAL && \
cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL ) || \
( cryptAlgo >= CRYPT_ALGO_FIRST_MAC && \
cryptAlgo <= CRYPT_ALGO_LAST_MAC );
BOOLEAN adjustKey = FALSE;
int status;
/* Create the encryption context */
if( isDevice )
status = cryptDeviceCreateContext( cryptDevice, cryptContext,
cryptAlgo );
else
status = cryptCreateContext( cryptContext, CRYPT_UNUSED, cryptAlgo );
if( cryptStatusError( status ) )
{
printf( "crypt%sCreateContext() failed with error code %d.\n",
isDevice ? "Device" : "", status );
return( FALSE );
}
if( cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL )
{
status = cryptSetAttribute( *cryptContext, CRYPT_CTXINFO_MODE,
cryptMode );
if( cryptStatusError( status ) )
{
cryptDestroyContext( *cryptContext );
if( status == CRYPT_ERROR_NOTAVAIL )
/* This mode isn't available, return a special-case value to
tell the calling code to continue */
return( status );
printf( "Encryption mode %d selection failed with status %d.\n",
cryptMode, status );
return( FALSE );
}
}
if( hasKey )
{
status = cryptSetAttributeString( *cryptContext, CRYPT_CTXINFO_KEY,
key, length );
if( length > 16 && status == CRYPT_ERROR_PARAM4 )
{
status = cryptSetAttributeString( *cryptContext, CRYPT_CTXINFO_KEY,
key, 16 );
if( cryptStatusOK( status ) )
{
puts( " Load of full-length key failed, using shorter 128-"
"bit key." );
adjustKey = TRUE;
}
}
if( cryptStatusError( status ) )
{
printf( "Key load failed with error code %d.\n", status );
return( FALSE );
}
}
if( decryptContext == NULL )
return( TRUE );
/* Create the decryption context */
if( cryptDevice == CRYPT_UNUSED )
status = cryptCreateContext( decryptContext, CRYPT_UNUSED, cryptAlgo );
else
status = cryptDeviceCreateContext( cryptDevice, decryptContext,
cryptAlgo );
if( cryptStatusError( status ) )
{
printf( "crypt%sCreateContext() failed with error code %d.\n",
( cryptDevice != CRYPT_UNUSED ) ? "Device" : "", status );
return( FALSE );
}
if( cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL )
{
status = cryptSetAttribute( *decryptContext, CRYPT_CTXINFO_MODE,
cryptMode );
if( cryptStatusError( status ) )
{
cryptDestroyContext( *cryptContext );
if( status == CRYPT_ERROR_NOTAVAIL )
/* This mode isn't available, return a special-case value to
tell the calling code to continue */
return( status );
printf( "Encryption mode %d selection failed with status %d.\n",
cryptMode, status );
return( FALSE );
}
}
if( hasKey )
{
status = cryptSetAttributeString( *decryptContext, CRYPT_CTXINFO_KEY,
key, adjustKey ? 16 : length );
if( cryptStatusError( status ) )
{
printf( "Key load failed with error code %d.\n", status );
return( FALSE );
}
}
return( TRUE );
}
/****************************************************************************
* *
* Timing Tests *
* *
****************************************************************************/
/* Print timing info. This gets a bit hairy because we're actually counting
timer ticks rather than thread times, which means we'll be affected by
things like context switches. There are two approaches to this:
1. Take the fastest time, which will be the time least affected by system
overhead.
2. Apply standard statistical techniques to weed out anomalies. Since
this is just for testing purposes all we do is discard any results
out by more than 10%, which is crude but reasonably effective. A
more rigorous approach is to discards results more than n standard
deviations out, but this gets screwed up by the fact that a single
context switch of 20K ticks can throw out results from an execution
time of only 50 ticks. In any case (modulo context switches) the
fastest, 10%-out, and 2 SD out times are all within about 1% of each
other, so all methods are roughly equally accurate */
static void printTimes( HIRES_TIME times[ NO_TESTS + 1 ][ 8 ],
const int noTimes )
{
int i;
for( i = 0; i < noTimes; i++ )
{
HIRES_TIME timeSum = 0, timeAvg, timeDelta;
HIRES_TIME timeMin = 1000000L;
HIRES_TIME timeCorrSum10 = 0, timeCorrSumSD = 0;
#ifdef USE_SD
double stdDev;
#endif /* USE_SD */
int j, timesCount10 = 0, timesCountSD = 0;
/* Find the mean execution time */
for( j = 1; j < NO_TESTS + 1; j++ )
timeSum += times[ j ][ i ];
timeAvg = timeSum / NO_TESTS;
timeDelta = timeSum / 10; /* 10% variation */
if( timeSum == 0 )
{
/* Some ciphers can't provide results for some cases (e.g.
AES for 8-byte blocks) */
printf( " " );
continue;
}
/* Find the fastest overall time */
for( j = 1; j < NO_TESTS + 1; j++ )
if( times[ j ][ i ] < timeMin )
timeMin = times[ j ][ i ];
/* Find the mean time, discarding anomalous results more than 10%
out. We cast the values to longs in order to (portably) print
them, if we want to print the full 64-bit values we have to
use nonstandard extensions like "%I64d" (for Win32) */
for( j = 1; j < NO_TESTS + 1; j++ )
if( times[ j ][ i ] > timeAvg - timeDelta && \
times[ j ][ i ] < timeAvg + timeDelta )
{
timeCorrSum10 += times[ j ][ i ];
timesCount10++;
}
if( timesCount10 <= 0 )
{
printf( "Error: No times within +/-%d of %d.\n",
timeDelta, timeAvg );
}
if( noTimes <= 2 && i == 0 )
printf( "Pub: min.= %d, avg.= %d ", ( long ) timeMin,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -