📄 utils.c
字号:
/****************************************************************************
* *
* cryptlib Self-test Utility Routines *
* Copyright Peter Gutmann 1997-2007 *
* *
****************************************************************************/
#include "cryptlib.h"
#include "test/test.h"
#if defined( __MVS__ ) || defined( __VMCMS__ )
/* Suspend conversion of literals to ASCII. */
#pragma convlit( suspend )
#endif /* IBM big iron */
#if defined( __ILEC400__ )
#pragma convert( 0 )
#endif /* IBM medium iron */
#ifdef HAS_WIDECHAR
#include <wchar.h>
#endif /* HAS_WIDECHAR */
/* The keys used with the test code have associated certs that expire at
some point. The following value defines the number of days before the
expiry at which we start printing warnings */
#if defined( _MSC_VER ) && ( _MSC_VER == 1200 ) && !defined( NDEBUG )
#define EXPIRY_WARN_DAYS 90
#else
#define EXPIRY_WARN_DAYS 30
#endif /* VC 6 debug/development, give some advance warning */
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
#ifndef _WIN32_WCE
/* Since ctime() adds a '\n' to the string and may return NULL, we wrap it
in something that behaves as required */
static char *getTimeString( const time_t theTime, const int bufNo )
{
static char timeString[ 2 ][ 64 ], *timeStringPtr;
assert( bufNo == 0 || bufNo == 1 );
timeStringPtr = ctime( &theTime );
if( timeStringPtr == NULL )
return( "(Not available)" );
strcpy( timeString[ bufNo ], timeStringPtr );
timeString[ bufNo ][ strlen( timeStringPtr ) - 1 ] = '\0'; /* Stomp '\n' */
return( timeString[ bufNo ] );
}
#else
#define getTimeString( theTime, bufNo ) "(No time data available)"
#endif /* _WIN32_WCE */
/****************************************************************************
* *
* General Checking Functions *
* *
****************************************************************************/
/* Check that a file is accessible. This is a generic sanity check to make
sure that access to keyset files is functioning */
int checkFileAccess( void )
{
CRYPT_KEYSET cryptKeyset;
FILE *filePtr;
BYTE buffer[ BUFFER_SIZE ];
int length, status;
/* First, check that the file actually exists so that we can return an
appropriate error message */
if( ( filePtr = fopen( convertFileName( CA_PRIVKEY_FILE ),
"rb" ) ) == NULL )
{
printf( "Couldn't access cryptlib keyset file %s. Please make "
"sure\nthat all the cryptlib files have been installed "
"correctly, and the cryptlib\nself-test is being run from "
"the correct directory.\n", CA_PRIVKEY_FILE );
return( FALSE );
}
fclose( filePtr );
/* Now read the test files and see if there's any problem due to data
conversion evident */
filenameFromTemplate( buffer, TESTDATA_FILE_TEMPLATE, 1 );
if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
{
puts( "Couldn't open binary data test file to check for data "
"conversion problems." );
return( FALSE );
}
length = fread( buffer, 1, BUFFER_SIZE, filePtr );
fclose( filePtr );
if( length != 16 || \
memcmp( buffer, "\x30\x82\x02\x56\x30\x82\x02\x52\r\n\x08\x40\n" "tqz", 16 ) )
{
puts( "Binary data is corrupt, probably due to being unzipped or "
"copied onto the\nsystem in a mode that tries to translate "
"text data during processing/copying." );
return( FALSE );
}
#ifdef __UNIX__
filenameFromTemplate( buffer, TESTDATA_FILE_TEMPLATE, 2 );
if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
{
puts( "Couldn't open text data test file to check for data "
"conversion problems." );
return( FALSE );
}
length = fread( buffer, 1, BUFFER_SIZE, filePtr );
fclose( filePtr );
if( length != 10 || memcmp( buffer, "test\ntest\n" , 10 ) )
{
puts( "Text data is still in CRLF-delimited format, probably due "
"to being unzipped\nwithout the '-a' option to translate "
"text files for Unix systems." );
return( FALSE );
}
#endif /* __UNIX__ */
/* The file exists and is accessible and was copied/installed correctly,
now try and open it using the cryptlib file access functions */
status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
CA_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY );
if( cryptStatusError( status ) )
{
/* If file keyset access isn't available, the inability to access
the keyset isn't an error */
if( status == CRYPT_ERROR_NOTAVAIL )
return( TRUE );
printf( "Couldn't access cryptlib keyset file %s even though the "
"file\nexists and is readable. Please make sure that the "
"cryptlib self-test is\nbeing run from the correct "
"directory.\n", CA_PRIVKEY_FILE );
return( FALSE );
}
cryptKeysetClose( cryptKeyset );
return( TRUE );
}
/* Check that external network sites are accessible, used to detect
potential problems with machines stuck behind firewalls */
int checkNetworkAccess( void )
{
CRYPT_KEYSET cryptKeyset;
int status;
status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_HTTP,
TEXT( "www.amazon.com" ), CRYPT_KEYOPT_READONLY );
if( cryptStatusError( status ) )
return( FALSE );
cryptKeysetClose( cryptKeyset );
return( TRUE );
}
/****************************************************************************
* *
* Import/Export Functions *
* *
****************************************************************************/
/* Import a certificate object */
int importCertFile( CRYPT_CERTIFICATE *cryptCert, const C_STR fileName )
{
FILE *filePtr;
BYTE buffer[ BUFFER_SIZE ];
int count;
if( ( filePtr = fopen( convertFileName( fileName ), "rb" ) ) == NULL )
return( CRYPT_ERROR_OPEN );
count = fread( buffer, 1, BUFFER_SIZE, filePtr );
fclose( filePtr );
if( count == BUFFER_SIZE ) /* Item too large for buffer */
return( CRYPT_ERROR_OVERFLOW );
/* Import the certificate */
return( cryptImportCert( buffer, count, CRYPT_UNUSED, cryptCert ) );
}
int importCertFromTemplate( CRYPT_CERTIFICATE *cryptCert,
const C_STR fileTemplate, const int number )
{
BYTE filenameBuffer[ FILENAME_BUFFER_SIZE ];
#ifdef UNICODE_STRINGS
wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
#endif /* UNICODE_STRINGS */
filenameFromTemplate( filenameBuffer, fileTemplate, number );
#ifdef UNICODE_STRINGS
mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
return( importCertFile( cryptCert, wcBuffer ) );
#else
return( importCertFile( cryptCert, filenameBuffer ) );
#endif /* UNICODE_STRINGS */
}
/* Read a key from a key file */
int getPublicKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
const C_STR keyName )
{
CRYPT_KEYSET cryptKeyset;
int status;
/* Read the key from the keyset */
status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
keysetName, CRYPT_KEYOPT_READONLY );
if( cryptStatusError( status ) )
return( status );
status = cryptGetPublicKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
keyName );
cryptKeysetClose( cryptKeyset );
return( status );
}
int getPrivateKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
const C_STR keyName, const C_STR password )
{
CRYPT_KEYSET cryptKeyset;
time_t validFrom, validTo;
int dummy, status;
/* Read the key from the keyset */
status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
keysetName, CRYPT_KEYOPT_READONLY );
if( cryptStatusError( status ) )
return( status );
status = cryptGetPrivateKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
keyName, password );
cryptKeysetClose( cryptKeyset );
if( cryptStatusError( status ) )
return( status );
/* If the key has a cert attached, make sure it's still valid before we
hand it back to the self-test functions that will report the problem
as being with the self-test rather than with the cert. We check not
just the expiry date but also the expiry interval, to make sure that
we don't get false positives on short-validity certs */
status = cryptGetAttributeString( *cryptContext,
CRYPT_CERTINFO_VALIDFROM, &validFrom, &dummy );
if( cryptStatusError( status ) )
/* There's no cert there, this isn't an error */
return( CRYPT_OK );
cryptGetAttributeString( *cryptContext,
CRYPT_CERTINFO_VALIDTO, &validTo, &dummy );
#ifndef _WIN32_WCE
if( ( validTo - validFrom > ( 86400 * EXPIRY_WARN_DAYS ) ) && \
validTo - time( NULL ) <= ( 86400 * EXPIRY_WARN_DAYS ) )
{
const time_t currentTime = time( NULL );
puts( " ********************" );
if( validTo <= currentTime )
{
puts( "Warning: This key has expired. Certificate-related "
"operations will fail or\n result in error "
"messages from the test code." );
}
else
{
if( validTo - currentTime <= 86400 )
{
puts( "Warning: This key expires today. Certificate-"
"related operations may fail\n or result in "
"error messages from the test code." );
}
else
{
printf( "Warning: This key will expire in %ld days. "
"Certificate-related operations\n may fail "
"or result in error messages from the test code.\n",
( validTo - currentTime ) / 86400 );
}
}
puts( " ********************" );
printf( "Hit a key..." );
getchar();
putchar( '\r' );
}
#endif /* _WIN32_WCE */
return( CRYPT_OK );
}
/****************************************************************************
* *
* Key File Access Routines *
* *
****************************************************************************/
/* Key file and password-handling access routines */
const C_STR getKeyfileName( const KEYFILE_TYPE type,
const BOOLEAN isPrivKey )
{
switch( type )
{
case KEYFILE_X509:
return( USER_PRIVKEY_FILE );
case KEYFILE_PGP:
return( isPrivKey ? PGP_PRIVKEY_FILE : PGP_PUBKEY_FILE );
case KEYFILE_OPENPGP:
return( isPrivKey ? OPENPGP_PRIVKEY_FILE : OPENPGP_PUBKEY_FILE );
case KEYFILE_OPENPGP_HASH:
return( isPrivKey ? OPENPGP_PRIVKEY_HASH_FILE : OPENPGP_PUBKEY_HASH_FILE );
case KEYFILE_OPENPGP_AES:
return( isPrivKey ? OPENPGP_PRIVKEY_AES_FILE : OPENPGP_PUBKEY_AES_FILE );
case KEYFILE_OPENPGP_RSA:
return( isPrivKey ? OPENPGP_PRIVKEY_RSA_FILE : OPENPGP_PUBKEY_RSA_FILE );
case KEYFILE_OPENPGP_PARTIAL:
return( OPENPGP_PRIVKEY_PART_FILE );
case KEYFILE_NAIPGP:
return( isPrivKey ? NAIPGP_PRIVKEY_FILE : NAIPGP_PUBKEY_FILE );
}
assert( 0 );
return( TEXT( "notfound" ) );
}
const C_STR getKeyfilePassword( const KEYFILE_TYPE type )
{
switch( type )
{
case KEYFILE_X509:
return( TEST_PRIVKEY_PASSWORD );
case KEYFILE_PGP:
case KEYFILE_OPENPGP:
case KEYFILE_OPENPGP_HASH:
case KEYFILE_OPENPGP_RSA:
return( TEXT( "test1" ) );
case KEYFILE_NAIPGP:
return( TEXT( "test10" ) );
case KEYFILE_OPENPGP_AES:
return( TEXT( "testkey" ) );
case KEYFILE_OPENPGP_PARTIAL:
return( TEXT( "def" ) );
}
assert( 0 );
return( TEXT( "notfound" ) );
}
const C_STR getKeyfileUserID( const KEYFILE_TYPE type,
const BOOLEAN isPrivKey )
{
/* If possible we specify user IDs for keys in the middle of the keyring
to make sure that we test the ability to correctly handle multiple
keys */
switch( type )
{
case KEYFILE_X509:
return( USER_PRIVKEY_LABEL );
case KEYFILE_PGP:
return( TEXT( "test" ) );
case KEYFILE_NAIPGP:
return( isPrivKey ? TEXT( "test" ) : TEXT( "test cryptlib" ) );
case KEYFILE_OPENPGP:
case KEYFILE_OPENPGP_HASH:
case KEYFILE_OPENPGP_RSA:
return( TEXT( "test1" ) );
case KEYFILE_OPENPGP_AES:
return( TEXT( "Max Mustermann" ) );
}
assert( 0 );
return( TEXT( "notfound" ) );
}
/****************************************************************************
* *
* OS Helper Functions *
* *
****************************************************************************/
#if defined( __BORLANDC__ ) && ( __BORLANDC__ <= 0x310 )
/* BC++ 3.x doesn't have mbstowcs() in the default library, and also defines
wchar_t as char (!!) so we fake it here */
size_t mbstowcs( char *pwcs, const char *s, size_t n )
{
memcpy( pwcs, s, n );
return( n );
}
#endif /* BC++ 3.1 or lower */
/* When using multiple threads we need to delay one thread for a small
amount of time, unfortunately there's no easy way to do this with pthreads
so we have to provide the following wrapper function that makes an
(implementation-specific) attempt at it */
#if defined( UNIX_THREADS ) || defined( WINDOWS_THREADS ) || defined( OS2_THREADS )
#if defined( UNIX_THREADS )
/* This include must be outside the function to avoid weird compiler errors
on some systems */
#include <sys/time.h>
#endif /* UNIX_THREADS */
void delayThread( const int seconds )
{
#if defined( UNIX_THREADS )
struct timeval tv = { 0 };
/* The following should put a thread to sleep for a second on most
systems since the select() should be a thread-safe one in the
presence of pthreads */
tv.tv_sec = seconds;
select( 1, NULL, NULL, NULL, &tv );
#elif defined( WINDOWS_THREADS )
Sleep( seconds * 1000 );
#endif /* Threading system-specific delay functions */
}
#endif /* Systems with threading support */
/* Helper functions to make tracking down errors on systems with no console
a bit less painful. 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 */
/* Conversion functions used to get Unicode input into generic ASCII
output */
#ifdef UNICODE_STRINGS
/* Get a filename in an appropriate format for the C runtime library */
const char *convertFileName( const C_STR fileName )
{
static char fileNameBuffer[ FILENAME_BUFFER_SIZE ];
wcstombs( fileNameBuffer, fileName, wcslen( fileName ) + 1 );
return( fileNameBuffer );
}
/* Map a filename template to an actual filename, input in Unicode, output in
ASCII */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -