1 /****************************************************************************
2 * *
3 * cryptlib Self-test Utility Routines *
4 * Copyright Peter Gutmann 1997-2011 *
5 * *
6 ****************************************************************************/
7
8 #include <ctype.h>
9 #include "cryptlib.h"
10 #include "test/test.h"
11
12 #if defined( __MVS__ ) || defined( __VMCMS__ )
13 /* Suspend conversion of literals to ASCII. */
14 #pragma convlit( suspend )
15 #endif /* IBM big iron */
16 #if defined( __ILEC400__ )
17 #pragma convert( 0 )
18 #endif /* IBM medium iron */
19 #ifdef HAS_WIDECHAR
20 #include <wchar.h>
21 #endif /* HAS_WIDECHAR */
22 #ifndef NDEBUG
23 #include "misc/config.h"
24 #endif /* NDEBUG */
25
26 /* Define the following if cryptlib has been built without keyset support,
27 this loads a fixed key and attaches a pseudo-certificate to it */
28
29 /* #define USE_PSEUDOCERTIFICATES */
30 #if defined( USE_PSEUDOCERTIFICATES ) && ( defined( _MSC_VER ) || defined( __GNUC__ ) )
31 #pragma message( "Building with pseudocertificate support" )
32 #endif /* USE_PSEUDOCERTIFICATES && VC++/gcc */
33
34 /* The keys used with the test code have associated certs that expire at
35 some point. The following value defines the number of days before the
36 expiry at which we start printing warnings */
37
38 #if defined( _MSC_VER ) && ( _MSC_VER == 1200 ) && !defined( NDEBUG )
39 #define EXPIRY_WARN_DAYS 90
40 #else
41 #define EXPIRY_WARN_DAYS 30
42 #endif /* VC 6 debug/development, give some advance warning */
43
44 /****************************************************************************
45 * *
46 * Utility Functions *
47 * *
48 ****************************************************************************/
49
50 #ifndef _WIN32_WCE
51
52 /* Since ctime() adds a '\n' to the string and may return NULL, we wrap it
53 in something that behaves as required */
54
getTimeString(const time_t theTime,const int bufNo)55 static char *getTimeString( const time_t theTime, const int bufNo )
56 {
57 static char timeString[ 2 ][ 64 ], *timeStringPtr;
58
59 assert( bufNo == 0 || bufNo == 1 );
60
61 timeStringPtr = ctime( &theTime );
62 if( timeStringPtr == NULL )
63 return( "(Not available)" );
64 strcpy( timeString[ bufNo ], timeStringPtr );
65 timeString[ bufNo ][ strlen( timeStringPtr ) - 1 ] = '\0'; /* Stomp '\n' */
66
67 return( timeString[ bufNo ] );
68 }
69 #else
70 #define getTimeString( theTime, bufNo ) "(No time data available)"
71 #endif /* _WIN32_WCE */
72
73 #if defined( _WIN32_WCE ) && _WIN32_WCE < 500
74
remove(const char * pathname)75 int remove( const char *pathname )
76 {
77 wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
78
79 mbstowcs( wcBuffer, pathname, strlen( pathname ) + 1 );
80 DeleteFile( wcBuffer );
81
82 return( 0 );
83 }
84 #endif /* WinCE < 5.x */
85
86 /****************************************************************************
87 * *
88 * General Checking Functions *
89 * *
90 ****************************************************************************/
91
92 /* Windows-specific file accessibility check */
93
94 #if defined( __WINDOWS__ ) && !defined( _WIN32_WCE )
95
96 #pragma comment( lib, "advapi32" )
97
checkFileAccessibleACL(const char * fileName)98 static int checkFileAccessibleACL( const char *fileName )
99 {
100 BYTE sidBuffer[ 1024 ];
101 SECURITY_DESCRIPTOR *pSID = ( void * ) sidBuffer;
102 GENERIC_MAPPING gMapping;
103 PRIVILEGE_SET psPrivilege;
104 HANDLE hThreadToken;
105 DWORD dwPrivilegeLength = sizeof( PRIVILEGE_SET );
106 DWORD cbNeeded, dwGrantedAccess;
107 BOOL fStatus;
108
109 if( !GetFileSecurity( fileName, ( OWNER_SECURITY_INFORMATION | \
110 GROUP_SECURITY_INFORMATION | \
111 DACL_SECURITY_INFORMATION),
112 pSID, 1024, &cbNeeded ) )
113 {
114 /* We can't access file security information (presumably due to
115 insufficient permissions), there's a problem */
116 return( FALSE );
117 }
118 if( !ImpersonateSelf( SecurityImpersonation ) )
119 return( TRUE );
120 if( !OpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hThreadToken ) )
121 {
122 RevertToSelf();
123 return( TRUE );
124 }
125 if( !AccessCheck( pSID, hThreadToken, FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE,
126 &gMapping, &psPrivilege, &dwPrivilegeLength,
127 &dwGrantedAccess, &fStatus ) )
128 {
129 const DWORD dwLastError = GetLastError();
130
131 RevertToSelf();
132 CloseHandle( hThreadToken );
133
134 /* If it's FAT32 then there's nothing further to check */
135 if( dwLastError == ERROR_NO_SECURITY_ON_OBJECT || \
136 dwLastError == ERROR_NOT_SUPPORTED )
137 return( TRUE );
138
139 return( FALSE );
140 }
141 RevertToSelf();
142 CloseHandle( hThreadToken );
143 if( !fStatus )
144 {
145 return( FALSE );
146 }
147
148 return( TRUE );
149 }
150 #endif /* Windows versions with ACLs */
151
152 /* Check that a file is accessible. This is a generic sanity check to make
153 sure that access to keyset files is functioning */
154
checkFileAccess(void)155 int checkFileAccess( void )
156 {
157 CRYPT_KEYSET cryptKeyset;
158 FILE *filePtr;
159 BYTE buffer[ BUFFER_SIZE ];
160 int length, failedFileNo = 0, status;
161
162 /* First, check that the file actually exists so that we can return an
163 appropriate error message */
164 if( ( filePtr = fopen( convertFileName( CA_PRIVKEY_FILE ),
165 "rb" ) ) == NULL )
166 failedFileNo = 1;
167 else
168 fclose( filePtr );
169 if( failedFileNo == 0 )
170 {
171 if( ( filePtr = fopen( convertFileName( TEST_PRIVKEY_FILE ), \
172 "rb" ) ) == NULL )
173 failedFileNo = 2;
174 else
175 fclose( filePtr );
176 }
177 if( failedFileNo > 0 )
178 {
179 printf( "Couldn't access cryptlib keyset file '%s'. Please make "
180 "sure\nthat all the cryptlib files have been installed "
181 "correctly, and the cryptlib\nself-test is being run from "
182 "the correct directory.\n",
183 ( failedFileNo == 1 ) ? CA_PRIVKEY_FILE : TEST_PRIVKEY_FILE );
184 return( FALSE );
185 }
186
187 /* Now check for accessibility problems due to filesystem permissions.
188 This can sometimes occur in odd situations for private-key files
189 (which are set up with fairly restrictive ACLs) when the files have
190 been copied from one filesystem to another with a different user,
191 so the ACLs grant access to the user on the source filesystem rather
192 than the destination filesystem (this requires a somewhat messed-
193 up copy, since the copier will have had access but the current
194 requester won't).
195
196 We check for access to two files, the CA private-key file that ships
197 with cryptlib and the user private-key file that's created when
198 cryptlib is run */
199 #if defined( __WINDOWS__ ) && !defined( _WIN32_WCE )
200 if( !checkFileAccessibleACL( CA_PRIVKEY_FILE ) )
201 failedFileNo = 1;
202 else
203 {
204 if( !checkFileAccessibleACL( TEST_PRIVKEY_FILE ) )
205 failedFileNo = 2;
206 }
207 if( failedFileNo > 0 )
208 {
209 printf( "Couldn't access %s cryptlib keyset file '%s'\nfor "
210 "read/write/delete. This is probably due to a filesystem "
211 "ACL issue\nin which the current user has insufficient "
212 "permissions to perform the\nrequired file access.\n",
213 ( failedFileNo == 1 ) ? "pre-generated" : "test-run generated",
214 ( failedFileNo == 1 ) ? CA_PRIVKEY_FILE : TEST_PRIVKEY_FILE );
215 return( FALSE );
216 }
217 #endif /* Windows versions with ACLs */
218
219 /* Now read the test files and see if there's any problem due to data
220 conversion evident */
221 filenameFromTemplate( buffer, TESTDATA_FILE_TEMPLATE, 1 );
222 if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
223 {
224 puts( "Couldn't open binary data test file to check for data "
225 "conversion problems." );
226 return( FALSE );
227 }
228 length = fread( buffer, 1, BUFFER_SIZE, filePtr );
229 fclose( filePtr );
230 if( length != 16 || \
231 memcmp( buffer, "\x30\x82\x02\x56\x30\x82\x02\x52\r\n\x08\x40\n" "tqz", 16 ) )
232 {
233 puts( "Binary data is corrupt, probably due to being unzipped or "
234 "copied onto the\nsystem in a mode that tries to translate "
235 "text data during processing/copying." );
236 return( FALSE );
237 }
238 #ifdef __UNIX__
239 filenameFromTemplate( buffer, TESTDATA_FILE_TEMPLATE, 2 );
240 if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
241 {
242 puts( "Couldn't open text data test file to check for data "
243 "conversion problems." );
244 return( FALSE );
245 }
246 length = fread( buffer, 1, BUFFER_SIZE, filePtr );
247 fclose( filePtr );
248 if( length != 10 || memcmp( buffer, "test\ntest\n" , 10 ) )
249 {
250 puts( "Text data is still in CRLF-delimited format, probably due "
251 "to being unzipped\nwithout the '-a' option to translate "
252 "text files for Unix systems." );
253 return( FALSE );
254 }
255 #endif /* __UNIX__ */
256
257 /* The file exists and is accessible and was copied/installed correctly,
258 now try and open it using the cryptlib file access functions. This
259 is a bit of a catch-22 because we're trying to at least open a keyset
260 before the self-test has verified the correct functioning of the
261 keyset-access code, but in almost all cases it's working OK and this
262 provides a useful general sanity-check, since the keyset code would
263 fail in any case when we get to it in the self-test */
264 status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
265 CA_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY );
266 if( cryptStatusError( status ) )
267 {
268 /* If file keyset access isn't available, the inability to access
269 the keyset isn't an error */
270 if( status == CRYPT_ERROR_NOTAVAIL )
271 return( TRUE );
272
273 printf( "Couldn't access cryptlib keyset file '%s' even though the "
274 "file\nexists and is readable. Please make sure that the "
275 "cryptlib self-test is\nbeing run from the correct "
276 "directory.\n", CA_PRIVKEY_FILE );
277 return( FALSE );
278 }
279 cryptKeysetClose( cryptKeyset );
280
281 return( TRUE );
282 }
283
284 /* Check that external network sites are accessible, used to detect
285 potential problems with machines stuck behind firewalls */
286
checkNetworkAccess(void)287 int checkNetworkAccess( void )
288 {
289 CRYPT_KEYSET cryptKeyset;
290 int status;
291
292 /* First we try for Microsoft's NCSI (Network Connection Status Icon)
293 site, a canary site used by Windows to check for network connectivity
294 that has high availability and is unlikely to be blocked. If that
295 fails we try for a well-known site, Amazon, as a fallback */
296 status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_HTTP,
297 TEXT( "http://www.msftncsi.com" ),
298 CRYPT_KEYOPT_READONLY );
299 if( cryptStatusOK( status ) )
300 {
301 cryptKeysetClose( cryptKeyset );
302 return( TRUE );
303 }
304 status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_HTTP,
305 TEXT( "www.amazon.com" ), CRYPT_KEYOPT_READONLY );
306 if( cryptStatusOK( status ) )
307 {
308 cryptKeysetClose( cryptKeyset );
309 return( TRUE );
310 }
311
312 return( FALSE );
313 }
314
315 /****************************************************************************
316 * *
317 * Import/Export Functions *
318 * *
319 ****************************************************************************/
320
321 /* Read data from a file */
322
readFileData(const char * fileName,const char * description,BYTE * buffer,const int bufSize,const int minFileSize,const BOOLEAN silent)323 int readFileData( const char *fileName, const char *description,
324 BYTE *buffer, const int bufSize, const int minFileSize,
325 const BOOLEAN silent )
326 {
327 FILE *filePtr;
328 int count;
329
330 if( ( filePtr = fopen( fileName, "rb" ) ) == NULL )
331 {
332 printf( "Couldn't find %s file, skipping test of data import...\n",
333 description );
334 return( 0 );
335 }
336 if( !silent )
337 printf( "Testing %s import...\n", description );
338 count = fread( buffer, 1, bufSize, filePtr );
339 fclose( filePtr );
340 if( count >= bufSize )
341 {
342 puts( "The data buffer size is too small for the data. To fix this, "
343 "either increase\nthe BUFFER_SIZE value in " __FILE__ " and "
344 "recompile the code, or use the\ntest code with dynamically-"
345 "allocated buffers." );
346 return( 0 ); /* Skip this test and continue */
347 }
348 if( count < minFileSize )
349 {
350 printf( "Read failed, only read %d bytes, at least %d required.\n",
351 count, minFileSize );
352 return( 0 ); /* Skip this test and continue */
353 }
354 if( !silent )
355 printf( "%s has size %d bytes.\n", description, count );
356 return( count );
357 }
358
359 /* Import a certificate object */
360
importCertFile(CRYPT_CERTIFICATE * cryptCert,const C_STR fileName)361 int importCertFile( CRYPT_CERTIFICATE *cryptCert, const C_STR fileName )
362 {
363 FILE *filePtr;
364 BYTE buffer[ BUFFER_SIZE ];
365 int count;
366
367 if( ( filePtr = fopen( convertFileName( fileName ), "rb" ) ) == NULL )
368 return( CRYPT_ERROR_OPEN );
369 count = fread( buffer, 1, BUFFER_SIZE, filePtr );
370 fclose( filePtr );
371 if( count == BUFFER_SIZE ) /* Item too large for buffer */
372 return( CRYPT_ERROR_OVERFLOW );
373
374 /* Import the certificate */
375 return( cryptImportCert( buffer, count, CRYPT_UNUSED, cryptCert ) );
376 }
377
importCertFromTemplate(CRYPT_CERTIFICATE * cryptCert,const C_STR fileTemplate,const int number)378 int importCertFromTemplate( CRYPT_CERTIFICATE *cryptCert,
379 const C_STR fileTemplate, const int number )
380 {
381 BYTE filenameBuffer[ FILENAME_BUFFER_SIZE ];
382 #ifdef UNICODE_STRINGS
383 wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
384 #endif /* UNICODE_STRINGS */
385
386 filenameFromTemplate( filenameBuffer, fileTemplate, number );
387 #ifdef UNICODE_STRINGS
388 mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
389 return( importCertFile( cryptCert, wcBuffer ) );
390 #else
391 return( importCertFile( cryptCert, filenameBuffer ) );
392 #endif /* UNICODE_STRINGS */
393 }
394
395 /* Export a certificate to a file in the given format */
396
exportCertFile(const char * fileName,const CRYPT_CERTIFICATE certificate,const CRYPT_CERTFORMAT_TYPE formatType)397 int exportCertFile( const char *fileName,
398 const CRYPT_CERTIFICATE certificate,
399 const CRYPT_CERTFORMAT_TYPE formatType )
400 {
401 FILE *filePtr;
402 BYTE certBuffer[ BUFFER_SIZE ];
403 int certLength, count, status;
404
405 /* Export the certificate in the requested format */
406 status = cryptExportCert( certBuffer, BUFFER_SIZE, &certLength,
407 formatType, certificate );
408 if( cryptStatusError( status ) )
409 return( status );
410
411 /* Write it to a file */
412 filePtr = fopen( fileName, "wb" );
413 if( filePtr == NULL )
414 return( CRYPT_ERROR_OPEN );
415 count = fwrite( certBuffer, 1, certLength, filePtr );
416 fclose( filePtr );
417 if( count != certLength )
418 return( CRYPT_ERROR_WRITE );
419
420 return( CRYPT_OK );
421 }
422
423 /* Read a key from a key file */
424
425 #ifndef USE_PSEUDOCERTIFICATES
426
getPublicKey(CRYPT_CONTEXT * cryptContext,const C_STR keysetName,const C_STR keyName)427 int getPublicKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
428 const C_STR keyName )
429 {
430 CRYPT_KEYSET cryptKeyset;
431 int status;
432
433 /* Read the key from the keyset */
434 status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
435 keysetName, CRYPT_KEYOPT_READONLY );
436 if( cryptStatusError( status ) )
437 {
438 printf( "Couldn't open keyset '%s', status %d, line %d.\n",
439 keysetName, status, __LINE__ );
440 return( FALSE );
441 }
442 status = cryptGetPublicKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
443 keyName );
444 if( cryptStatusError( status ) )
445 printExtError( cryptKeyset, "cryptGetPublicKey", status, __LINE__ );
446 cryptKeysetClose( cryptKeyset );
447 return( status );
448 }
449
getPrivateKey(CRYPT_CONTEXT * cryptContext,const C_STR keysetName,const C_STR keyName,const C_STR password)450 int getPrivateKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
451 const C_STR keyName, const C_STR password )
452 {
453 CRYPT_KEYSET cryptKeyset;
454 time_t validFrom;
455 #ifndef _WIN32_WCE
456 time_t validTo;
457 #endif /* _WIN32_WCE */
458 int dummy, status;
459
460 /* Read the key from the keyset */
461 status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
462 keysetName, CRYPT_KEYOPT_READONLY );
463 if( cryptStatusError( status ) )
464 return( status );
465 status = cryptGetPrivateKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
466 keyName, password );
467 if( cryptStatusError( status ) )
468 printExtError( cryptKeyset, "cryptGetPrivateKey", status, __LINE__ );
469 cryptKeysetClose( cryptKeyset );
470 if( cryptStatusError( status ) )
471 return( status );
472
473 /* If the key has a certificate attached, make sure it's still valid
474 before we hand it back to the self-test functions that will report
475 the problem as being with the self-test rather than with the
476 certificate. We check not just the expiry date but also the expiry
477 interval to make sure that we don't get false positives on short-
478 validity certificates */
479 status = cryptGetAttributeString( *cryptContext,
480 CRYPT_CERTINFO_VALIDFROM, &validFrom, &dummy );
481 if( cryptStatusError( status ) )
482 {
483 /* There's no certificate there, this isn't an error */
484 return( CRYPT_OK );
485 }
486 #ifndef _WIN32_WCE
487 status = cryptGetAttributeString( *cryptContext,
488 CRYPT_CERTINFO_VALIDTO, &validTo, &dummy );
489 if( cryptStatusError( status ) )
490 return( status );
491 if( ( validTo - validFrom > ( 86400 * EXPIRY_WARN_DAYS ) ) && \
492 validTo - time( NULL ) <= ( 86400 * EXPIRY_WARN_DAYS ) )
493 {
494 const time_t currentTime = time( NULL );
495
496 puts( " ********************" );
497 if( validTo <= currentTime )
498 {
499 puts( "Warning: This key has expired. Certificate-related "
500 "operations will fail or\n result in error "
501 "messages from the test code." );
502 }
503 else
504 {
505 if( validTo - currentTime <= 86400 )
506 {
507 puts( "Warning: This key expires today. Certificate-"
508 "related operations may fail\n or result in "
509 "error messages from the test code." );
510 }
511 else
512 {
513 printf( "Warning: This key will expire in " TIMET_FORMAT
514 " days. Certificate-related operations\n "
515 "may fail or result in error messages from the test "
516 "code.\n",
517 ( validTo - currentTime ) / 86400 );
518 }
519 }
520 puts( " ********************" );
521 printf( "Hit a key..." );
522 getchar();
523 putchar( '\r' );
524 }
525 #endif /* _WIN32_WCE */
526 return( CRYPT_OK );
527 }
528 #else
529
530 C_RET cryptCreateAttachedCert( C_IN CRYPT_CONTEXT cryptContext,
531 C_IN void C_PTR certObject,
532 C_IN int certObjectLength );
533
534 /* Import a certificate as a raw data object and attach it to a context as a
535 pseudo-certificate */
536
addPseudoCertificate(const CRYPT_CONTEXT cryptContext,const int certNo)537 static int addPseudoCertificate( const CRYPT_CONTEXT cryptContext,
538 const int certNo )
539 {
540 FILE *filePtr;
541 BYTE certBuffer[ BUFFER_SIZE ];
542 char filenameBuffer[ FILENAME_BUFFER_SIZE ];
543 int length = 0;
544
545 filenameFromTemplate( filenameBuffer, PSEUDOCERT_FILE_TEMPLATE, certNo );
546 if( ( filePtr = fopen( filenameBuffer, "rb" ) ) == NULL )
547 return( CRYPT_ERROR_OPEN );
548 length = fread( certBuffer, 1, BUFFER_SIZE, filePtr );
549 fclose( filePtr );
550 if( length <= 16 || length >= BUFFER_SIZE )
551 return( CRYPT_ERROR_READ );
552 return( cryptCreateAttachedCert( cryptContext, certBuffer, length ) );
553 }
554
getPublicKey(CRYPT_CONTEXT * cryptContext,const C_STR keysetName,const C_STR keyName)555 int getPublicKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
556 const C_STR keyName )
557 {
558 /* Load a fixed RSA public key and attached the pseudo-certificate */
559 if( !loadRSAContexts( CRYPT_UNUSED, cryptContext, NULL ) )
560 return( CRYPT_ERROR_NOTAVAIL );
561 return( addPseudoCertificate( *cryptContext, 1 ) );
562 }
563
getPrivateKey(CRYPT_CONTEXT * cryptContext,const C_STR keysetName,const C_STR keyName,const C_STR password)564 int getPrivateKey( CRYPT_CONTEXT *cryptContext, const C_STR keysetName,
565 const C_STR keyName, const C_STR password )
566 {
567 /* Load a fixed RSA public key and attached the pseudo-certificate */
568 if( !loadRSAContexts( CRYPT_UNUSED, NULL, cryptContext ) )
569 return( CRYPT_ERROR_NOTAVAIL );
570 return( addPseudoCertificate( *cryptContext, 1 ) );
571 }
572 #endif /* USE_PSEUDOCERTIFICATES */
573
574 /****************************************************************************
575 * *
576 * Key File Access Routines *
577 * *
578 ****************************************************************************/
579
580 /* Key file and password-handling access routines */
581
getKeyfileName(const KEYFILE_TYPE type,const BOOLEAN isPrivKey)582 const C_STR getKeyfileName( const KEYFILE_TYPE type,
583 const BOOLEAN isPrivKey )
584 {
585 static char filenameBuffer[ FILENAME_BUFFER_SIZE ];
586
587 switch( type )
588 {
589 case KEYFILE_X509:
590 return( USER_PRIVKEY_FILE );
591 case KEYFILE_X509_ALT:
592 filenameFromTemplate( filenameBuffer, USER_PRIVKEY_FILE_TEMPLATE, 2 );
593 return( filenameBuffer );
594 case KEYFILE_PGP:
595 case KEYFILE_PGP_SPECIAL:
596 return( isPrivKey ? PGP_PRIVKEY_FILE : PGP_PUBKEY_FILE );
597 case KEYFILE_OPENPGP_HASH:
598 return( isPrivKey ? OPENPGP_PRIVKEY_HASH_FILE : OPENPGP_PUBKEY_HASH_FILE );
599 case KEYFILE_OPENPGP_AES:
600 return( isPrivKey ? OPENPGP_PRIVKEY_AES_FILE : OPENPGP_PUBKEY_AES_FILE );
601 case KEYFILE_OPENPGP_CAST:
602 return( OPENPGP_PRIVKEY_CAST_FILE );
603 case KEYFILE_OPENPGP_RSA:
604 return( isPrivKey ? OPENPGP_PRIVKEY_RSA_FILE : OPENPGP_PUBKEY_RSA_FILE );
605 case KEYFILE_OPENPGP_MULT:
606 return( OPENPGP_PUBKEY_MULT_FILE );
607 case KEYFILE_OPENPGP_PARTIAL:
608 return( OPENPGP_PRIVKEY_PART_FILE );
609 case KEYFILE_NAIPGP:
610 return( isPrivKey ? NAIPGP_PRIVKEY_FILE : NAIPGP_PUBKEY_FILE );
611 case KEYFILE_OPENPGP_BOUNCYCASTLE:
612 return( OPENPGP_PRIVKEY_BC_FILE );
613 case KEYFILE_OPENPGP_ECC:
614 return( isPrivKey ? OPENPGP_PRIVKEY_ECC_FILE : OPENPGP_PUBKEY_ECC_FILE );
615 }
616 assert( 0 );
617 return( TEXT( "notfound" ) );
618 }
619
getKeyfilePassword(const KEYFILE_TYPE type)620 const C_STR getKeyfilePassword( const KEYFILE_TYPE type )
621 {
622 switch( type )
623 {
624 case KEYFILE_X509:
625 case KEYFILE_X509_ALT:
626 return( TEST_PRIVKEY_PASSWORD );
627 case KEYFILE_PGP:
628 case KEYFILE_OPENPGP_HASH:
629 case KEYFILE_OPENPGP_RSA:
630 case KEYFILE_OPENPGP_ECC:
631 return( TEXT( "test1" ) );
632 case KEYFILE_NAIPGP:
633 return( TEXT( "test10" ) );
634 case KEYFILE_OPENPGP_AES:
635 return( TEXT( "testkey" ) );
636 case KEYFILE_OPENPGP_CAST:
637 case KEYFILE_OPENPGP_BOUNCYCASTLE:
638 return( TEXT( "test" ) );
639 case KEYFILE_OPENPGP_PARTIAL:
640 return( TEXT( "def" ) );
641 }
642 assert( 0 );
643 return( TEXT( "notfound" ) );
644 }
645
getKeyfileUserID(const KEYFILE_TYPE type,const BOOLEAN isPrivKey)646 const C_STR getKeyfileUserID( const KEYFILE_TYPE type,
647 const BOOLEAN isPrivKey )
648 {
649 /* If possible we specify user IDs for keys in the middle of the keyring
650 to make sure that we test the ability to correctly handle multiple
651 keys */
652 switch( type )
653 {
654 case KEYFILE_X509:
655 case KEYFILE_X509_ALT:
656 return( USER_PRIVKEY_LABEL );
657 case KEYFILE_PGP:
658 case KEYFILE_OPENPGP_BOUNCYCASTLE:
659 return( TEXT( "test" ) );
660 case KEYFILE_PGP_SPECIAL:
661 return( TEXT( "suzuki" ) );
662 case KEYFILE_NAIPGP:
663 return( isPrivKey ? TEXT( "test" ) : TEXT( "test cryptlib" ) );
664 case KEYFILE_OPENPGP_HASH:
665 case KEYFILE_OPENPGP_RSA:
666 case KEYFILE_OPENPGP_ECC:
667 return( TEXT( "test1" ) );
668 case KEYFILE_OPENPGP_MULT:
669 return( TEXT( "NXX2502" ) );
670 case KEYFILE_OPENPGP_AES:
671 return( TEXT( "Max Mustermann" ) );
672 case KEYFILE_OPENPGP_CAST:
673 return( TEXT( "Trond" ) );
674 }
675 assert( 0 );
676 return( TEXT( "notfound" ) );
677 }
678
679 /****************************************************************************
680 * *
681 * OS Helper Functions *
682 * *
683 ****************************************************************************/
684
685 #if defined( __BORLANDC__ ) && ( __BORLANDC__ <= 0x310 )
686
687 /* BC++ 3.x doesn't have mbstowcs() in the default library, and also defines
688 wchar_t as char (!!) so we fake it here */
689
mbstowcs(char * pwcs,const char * s,size_t n)690 size_t mbstowcs( char *pwcs, const char *s, size_t n )
691 {
692 memcpy( pwcs, s, n );
693 return( n );
694 }
695 #endif /* BC++ 3.1 or lower */
696
697 /* When using multiple threads we need to delay one thread for a small
698 amount of time, unfortunately there's no easy way to do this with pthreads
699 so we have to provide the following wrapper function that makes an
700 (implementation-specific) attempt at it */
701
702 #if defined( UNIX_THREADS ) || defined( WINDOWS_THREADS ) || defined( OS2_THREADS )
703
704 #if defined( UNIX_THREADS )
705 /* This include must be outside the function to avoid weird compiler errors
706 on some systems */
707 #include <sys/time.h>
708 #endif /* UNIX_THREADS */
709
delayThread(const int seconds)710 void delayThread( const int seconds )
711 {
712 #if defined( UNIX_THREADS )
713 struct timeval tv = { 0 };
714
715 /* The following should put a thread to sleep for a second on most
716 systems since the select() should be a thread-safe one in the
717 presence of pthreads */
718 tv.tv_sec = seconds;
719 select( 1, NULL, NULL, NULL, &tv );
720 #elif defined( WINDOWS_THREADS )
721 Sleep( seconds * 1000 );
722 #endif /* Threading system-specific delay functions */
723 }
724 #endif /* Systems with threading support */
725
726 /* Helper functions to make tracking down errors on systems with no console
727 a bit less painful. These just use the debug console as stdout */
728
729 #ifdef _WIN32_WCE
730
wcPrintf(const char * format,...)731 void wcPrintf( const char *format, ... )
732 {
733 wchar_t wcBuffer[ 1024 ];
734 char buffer[ 1024 ];
735 va_list argPtr;
736
737 va_start( argPtr, format );
738 vsprintf( buffer, format, argPtr );
739 va_end( argPtr );
740 mbstowcs( wcBuffer, buffer, strlen( buffer ) + 1 );
741 NKDbgPrintfW( wcBuffer );
742 }
743
wcPuts(const char * string)744 void wcPuts( const char *string )
745 {
746 wcPrintf( "%s\n", string );
747 }
748 #endif /* Console-less environments */
749
750 /* Conversion functions used to get Unicode input into generic ASCII
751 output */
752
753 #ifdef UNICODE_STRINGS
754
755 /* Get a filename in an appropriate format for the C runtime library */
756
convertFileName(const C_STR fileName)757 const char *convertFileName( const C_STR fileName )
758 {
759 static char fileNameBuffer[ FILENAME_BUFFER_SIZE ];
760
761 wcstombs( fileNameBuffer, fileName, wcslen( fileName ) + 1 );
762 return( fileNameBuffer );
763 }
764
765 /* Map a filename template to an actual filename, input in Unicode, output in
766 ASCII */
767
filenameFromTemplate(char * buffer,const wchar_t * fileTemplate,const int count)768 void filenameFromTemplate( char *buffer, const wchar_t *fileTemplate,
769 const int count )
770 {
771 wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
772 int length;
773
774 length = _snwprintf( wcBuffer, FILENAME_BUFFER_SIZE, fileTemplate,
775 count );
776 wcstombs( buffer, wcBuffer, length + 1 );
777 }
778
filenameParamFromTemplate(wchar_t * buffer,const wchar_t * fileTemplate,const int count)779 void filenameParamFromTemplate( wchar_t *buffer,
780 const wchar_t *fileTemplate,
781 const int count )
782 {
783 _snwprintf( buffer, FILENAME_BUFFER_SIZE, fileTemplate, count );
784 }
785 #endif /* UNICODE_STRINGS */
786
787 /****************************************************************************
788 * *
789 * Thread Support Functions *
790 * *
791 ****************************************************************************/
792
793 #if defined( WINDOWS_THREADS )
794
795 static HANDLE hMutex;
796
createMutex(void)797 void createMutex( void )
798 {
799 hMutex = CreateMutex( NULL, FALSE, NULL );
800 }
acquireMutex(void)801 void acquireMutex( void )
802 {
803 if( WaitForSingleObject( hMutex, 30000 ) == WAIT_TIMEOUT )
804 {
805 puts( "Warning: Couldn't acquire mutex after 30s wait. Press a "
806 "key to continue." );
807 getchar();
808 }
809 }
waitMutex(void)810 int waitMutex( void )
811 {
812 if( WaitForSingleObject( hMutex, 30000 ) == WAIT_TIMEOUT )
813 return( CRYPT_ERROR_TIMEOUT );
814
815 /* Since this is merely a synchronisation operation in which a later
816 thread waits to catch up to an earlier one, we release the mutex again
817 so other threads can get in */
818 releaseMutex();
819 return( CRYPT_OK );
820 }
releaseMutex(void)821 void releaseMutex( void )
822 {
823 if( !ReleaseMutex( hMutex ) )
824 {
825 puts( "Warning: Couldn't release mutex. Press a key to continue." );
826 getchar();
827 }
828 }
destroyMutex(void)829 void destroyMutex( void )
830 {
831 CloseHandle( hMutex );
832 }
833
waitForThread(const HANDLE hThread)834 void waitForThread( const HANDLE hThread )
835 {
836 if( WaitForSingleObject( hThread, 15000 ) == WAIT_TIMEOUT )
837 {
838 puts( "Warning: Server thread is still active due to session "
839 "negotiation failure,\n this will cause an error "
840 "condition when cryptEnd() is called due\n to "
841 "resources remaining allocated. Press a key to continue." );
842 getchar();
843 }
844 CloseHandle( hThread );
845 }
846 #elif defined( UNIX_THREADS )
847
848 static pthread_mutex_t mutex;
849
createMutex(void)850 void createMutex( void )
851 {
852 pthread_mutex_init( &mutex, NULL );
853 }
acquireMutex(void)854 void acquireMutex( void )
855 {
856 pthread_mutex_lock( &mutex );
857 }
waitMutex(void)858 int waitMutex( void )
859 {
860 pthread_mutex_lock( &mutex );
861
862 /* Since this is merely a synchronisation operation in which a later
863 thread waits to catch up to an earlier one, we release the mutex again
864 so other threads can get in */
865 releaseMutex();
866 return( CRYPT_OK );
867 }
releaseMutex(void)868 void releaseMutex( void )
869 {
870 pthread_mutex_unlock( &mutex );
871 }
destroyMutex(void)872 void destroyMutex( void )
873 {
874 pthread_mutex_destroy( &mutex );
875 }
876
waitForThread(const pthread_t hThread)877 void waitForThread( const pthread_t hThread )
878 {
879 if( pthread_join( hThread, NULL ) < 0 )
880 {
881 puts( "Warning: Server thread is still active due to session "
882 "negotiation failure,\n this will cause an error "
883 "condition when cryptEnd() is called due\n to "
884 "resources remaining allocated. Press a key to continue." );
885 getchar();
886 }
887 }
888
889 #else
890
createMutex(void)891 void createMutex( void )
892 {
893 }
acquireMutex(void)894 void acquireMutex( void )
895 {
896 }
releaseMutex(void)897 void releaseMutex( void )
898 {
899 }
waitMutex(void)900 int waitMutex( void )
901 {
902 return( CRYPT_OK );
903 }
destroyMutex(void)904 void destroyMutex( void )
905 {
906 }
907 #endif /* WINDOWS_THREADS */
908
909 #if defined( WINDOWS_THREADS ) || defined( UNIX_THREADS )
910
911 /* Dispatch multiple client and server threads and wait for them to exit */
912
multiThreadDispatch(THREAD_FUNC clientFunction,THREAD_FUNC serverFunction,const int noThreads)913 int multiThreadDispatch( THREAD_FUNC clientFunction,
914 THREAD_FUNC serverFunction, const int noThreads )
915 {
916 THREAD_HANDLE hClientThreads[ MAX_NO_THREADS ];
917 THREAD_HANDLE hServerThreads[ MAX_NO_THREADS ];
918 int sessionID[ MAX_NO_THREADS ];
919 int i;
920
921 assert( noThreads <= MAX_NO_THREADS );
922
923 /* Set up the session ID values */
924 for( i = 0; i < MAX_NO_THREADS; i++ )
925 sessionID[ i ] = i;
926
927 /* Start the sessions and wait for them initialise. We have to wait for
928 some time since the multiple private key reads can take awhile */
929 for( i = 0; i < noThreads; i++ )
930 {
931 #ifdef WINDOWS_THREADS
932 unsigned int threadID;
933
934 hServerThreads[ i ] = ( HANDLE ) \
935 _beginthreadex( NULL, 0, serverFunction,
936 &sessionID[ i ], 0, &threadID );
937 #else
938 pthread_t threadHandle;
939
940 hServerThreads[ i ] = 0;
941 if( pthread_create( &threadHandle, NULL, serverFunction,
942 &sessionID[ i ] ) == 0 )
943 hServerThreads[ i ] = threadHandle;
944 #endif /* Windows vs. pthreads */
945 }
946 delayThread( 3 );
947
948 /* Connect to the local server */
949 for( i = 0; i < noThreads; i++ )
950 {
951 #ifdef WINDOWS_THREADS
952 unsigned int threadID;
953
954 hClientThreads[ i ] = ( HANDLE ) \
955 _beginthreadex( NULL, 0, clientFunction,
956 &sessionID[ i ], 0, &threadID );
957 #else
958 pthread_t threadHandle;
959
960 hClientThreads[ i ] = 0;
961 if( pthread_create( &threadHandle, NULL, clientFunction,
962 &sessionID[ i ] ) == 0 )
963 hClientThreads[ i ] = threadHandle;
964 #endif /* Windows vs. pthreads */
965 }
966 #ifdef WINDOWS_THREADS
967 if( WaitForMultipleObjects( noThreads, hServerThreads, TRUE,
968 60000 ) == WAIT_TIMEOUT || \
969 WaitForMultipleObjects( noThreads, hClientThreads, TRUE,
970 60000 ) == WAIT_TIMEOUT )
971 #else
972 /* Posix doesn't have an ability to wait for multiple threads for mostly
973 religious reasons ("That's not how we do things around here") so we
974 just wait for two token threads */
975 pthread_join( hServerThreads[ 0 ], NULL );
976 pthread_join( hClientThreads[ 0 ], NULL );
977 #endif /* Windows vs. pthreads */
978 {
979 puts( "Warning: Server threads are still active due to session "
980 "negotiation failure,\n this will cause an error "
981 "condition when cryptEnd() is called due\n to "
982 "resources remaining allocated. Press a key to continue." );
983 getchar();
984 }
985 #ifdef WINDOWS_THREADS
986 for( i = 0; i < noThreads; i++ )
987 if( hServerThreads[ i ] != 0 )
988 CloseHandle( hServerThreads[ i ] );
989 for( i = 0; i < noThreads; i++ )
990 if( hClientThreads[ i ] != 0 )
991 CloseHandle( hClientThreads[ i ] );
992 #endif /* Windows vs. pthreads */
993
994 return( TRUE );
995 }
996 #endif /* Windows/Unix threads */
997
998 /****************************************************************************
999 * *
1000 * Timing Support Functions *
1001 * *
1002 ****************************************************************************/
1003
1004 /* Get high-resolution timing info */
1005
1006 #ifdef USE_TIMING
1007
1008 #ifdef USE_32BIT_TIME
1009
timeDiff(HIRES_TIME startTime)1010 HIRES_TIME timeDiff( HIRES_TIME startTime )
1011 {
1012 HIRES_TIME timeLSB, timeDifference;
1013
1014 #ifdef __WINDOWS__
1015 #if 1
1016 LARGE_INTEGER performanceCount;
1017
1018 /* Sensitive to context switches */
1019 QueryPerformanceCounter( &performanceCount );
1020 timeLSB = performanceCount.LowPart;
1021 #else
1022 FILETIME dummyTime, kernelTime, userTime;
1023 int status;
1024
1025 /* Only accurate to 10ms, returns constant values in VC++ debugger */
1026 GetThreadTimes( GetCurrentThread(), &dummyTime, &dummyTime,
1027 &kernelTime, &userTime );
1028 timeLSB = userTime.dwLowDateTime;
1029 #endif /* 0 */
1030 #else
1031 struct timeval tv;
1032
1033 /* Only accurate to about 1us */
1034 gettimeofday( &tv, NULL );
1035 timeLSB = tv.tv_usec;
1036 #endif /* Windows vs.Unix high-res timing */
1037
1038 /* If we're getting an initial time, return an absolute value */
1039 if( !startTime )
1040 return( timeLSB );
1041
1042 /* We're getting a time difference */
1043 if( startTime < timeLSB )
1044 timeDifference = timeLSB - startTime;
1045 else
1046 {
1047 #ifdef __WINDOWS__
1048 /* Windows rolls over at INT_MAX */
1049 timeDifference = ( 0xFFFFFFFFUL - startTime ) + 1 + timeLSB;
1050 #else
1051 /* gettimeofday() rolls over at 1M us */
1052 timeDifference = ( 1000000L - startTime ) + timeLSB;
1053 #endif /* __WINDOWS__ */
1054 }
1055 if( timeDifference <= 0 )
1056 {
1057 printf( "Error: Time difference = " HIRES_FORMAT_SPECIFIER ", "
1058 "startTime = " HIRES_FORMAT_SPECIFIER ", "
1059 "endTime = " HIRES_FORMAT_SPECIFIER ".\n",
1060 timeDifference, startTime, timeLSB );
1061 return( 1 );
1062 }
1063 return( timeDifference );
1064 }
1065 #else
1066
timeDiff(HIRES_TIME startTime)1067 HIRES_TIME timeDiff( HIRES_TIME startTime )
1068 {
1069 HIRES_TIME timeValue;
1070
1071 #ifdef __WINDOWS__
1072 #if 1
1073 LARGE_INTEGER performanceCount;
1074
1075 /* Sensitive to context switches */
1076 QueryPerformanceCounter( &performanceCount );
1077 timeValue = performanceCount.QuadPart;
1078 #else
1079 FILETIME dummyTime, kernelTime, userTime;
1080 int status;
1081
1082 /* Only accurate to 10ms, returns constant values in VC++ debugger */
1083 GetThreadTimes( GetCurrentThread(), &dummyTime, &dummyTime,
1084 &kernelTime, &userTime );
1085 timeLSB = userTime.dwLowDateTime;
1086 #endif /* 0 */
1087 #else
1088 struct timeval tv;
1089
1090 /* Only accurate to about 1us */
1091 gettimeofday( &tv, NULL );
1092 timeValue = ( ( ( HIRES_TIME ) tv.tv_sec ) << 32 ) | tv.tv_usec;
1093 #endif /* Windows vs.Unix high-res timing */
1094
1095 if( !startTime )
1096 return( timeValue );
1097 return( timeValue - startTime );
1098 }
1099 #endif /* USE_32BIT_TIME */
1100
1101 /* Print timing info. This gets a bit hairy because we're actually counting
1102 low-level timer ticks rather than abstract thread times which means that
1103 we'll be affected by things like context switches. There are two
1104 approaches to this:
1105
1106 1. Take the fastest time, which will be the time least affected by
1107 system overhead.
1108
1109 2. Apply standard statistical techniques to weed out anomalies. Since
1110 this is just for testing purposes all we do is discard any results
1111 out by more than 10%, which is crude but reasonably effective. A
1112 more rigorous approach is to discards results more than n standard
1113 deviations out, but this gets screwed up by the fact that a single
1114 context switch of 20K ticks can throw out results from an execution
1115 time of only 50 ticks. In any case (modulo context switches) the
1116 fastest, 10%-out, and 2 SD out times are all within about 1% of each
1117 other so all methods are roughly equally accurate */
1118
timeDisplayMean(HIRES_TIME * times,const int noTimes)1119 static int timeDisplayMean( HIRES_TIME *times, const int noTimes )
1120 {
1121 HIRES_TIME timeSum = 0, timeAvg, timeDelta;
1122 HIRES_TIME timeMin = 1000000L;
1123 HIRES_TIME timeCorrSum10 = 0;
1124 HIRES_TIME avgTime;
1125 #ifdef __WINDOWS__
1126 LARGE_INTEGER performanceCount;
1127 #endif /* __WINDOWS__ */
1128 #ifdef USE_SD
1129 HIRES_TIME timeCorrSumSD = 0;
1130 double stdDev;
1131 int timesCountSD = 0;
1132 #endif /* USE_SD */
1133 long timeMS, ticksPerSec;
1134 const int startIndex = ( noTimes == 1 ) ? 0 : 1;
1135 /* If we're using a multitude of readings we discard the first one,
1136 which is always unusually high due to startup overhead */
1137 int i, timesCount10 = 0;
1138
1139 /* Try and get the clock frequency */
1140 #ifdef __WINDOWS__
1141 QueryPerformanceFrequency( &performanceCount );
1142 ticksPerSec = performanceCount.LowPart;
1143 #else
1144 ticksPerSec = 1000000;
1145 #endif /* __WINDOWS__ */
1146 if( noTimes > 1 )
1147 {
1148 printf( "Times given in clock ticks of frequency " );
1149 #ifdef __WINDOWS__
1150 printf( "%ld", ticksPerSec );
1151 #else
1152 printf( "~1M" );
1153 #endif /* __WINDOWS__ */
1154 printf( " ticks per second.\n\n" );
1155 }
1156
1157 /* Find the mean execution time */
1158 for( i = startIndex; i < noTimes; i++ )
1159 timeSum += times[ i ];
1160 timeAvg = timeSum / noTimes;
1161 timeDelta = timeAvg / 10; /* 10% variation */
1162
1163 /* Find the fastest overall time */
1164 for( i = startIndex; i < noTimes; i++ )
1165 {
1166 if( times[ i ] < timeMin )
1167 timeMin = times[ i ];
1168 }
1169
1170 /* Find the mean time, discarding anomalous results more than 10% out.
1171 We cast the values to longs in order to (portably) print them, if we
1172 want to print the full 64-bit values we have to use nonstandard
1173 extensions like "%I64d" (for Win32) */
1174 for( i = startIndex; i < noTimes; i++ )
1175 {
1176 if( times[ i ] > timeAvg - timeDelta && \
1177 times[ i ] < timeAvg + timeDelta )
1178 {
1179 timeCorrSum10 += times[ i ];
1180 timesCount10++;
1181 }
1182 }
1183 if( timesCount10 <= 0 )
1184 {
1185 printf( "Error: No times within +/-%ld of %ld.\n",
1186 ( long ) timeDelta, ( long ) timeAvg );
1187 return( -1 );
1188 }
1189 avgTime = timeCorrSum10 / timesCount10;
1190 if( noTimes > 1 )
1191 {
1192 printf( "Time: min.= %ld, avg.= %ld ", ( long ) timeMin,
1193 ( long ) avgTime );
1194 }
1195 timeMS = ( avgTime * 1000 ) / ticksPerSec;
1196 #if 0 /* Print difference to fastest time, usually only around 1% */
1197 printf( "(%4d)", ( timeCorrSum10 / timesCount10 ) - timeMin );
1198 #endif /* 0 */
1199
1200 #ifdef USE_SD
1201 /* Find the standard deviation */
1202 for( i = startIndex; i < noTimes; i++ )
1203 {
1204 const HIRES_TIME timeDev = times[ i ] - timeAvg;
1205
1206 timeCorrSumSD += ( timeDev * timeDev );
1207 }
1208 stdDev = timeCorrSumSD / NO_TESTS;
1209 stdDev = sqrt( stdDev );
1210
1211 /* Find the mean time, discarding anomalous results more than two
1212 standard deviations out */
1213 timeCorrSumSD = 0;
1214 timeDelta = ( HIRES_TIME ) stdDev * 2;
1215 for( i = startIndex; j < noTimes; i++ )
1216 {
1217 if( times[ i ] > timeAvg - timeDelta && \
1218 times[ i ] < timeAvg + timeDelta )
1219 {
1220 timeCorrSumSD += times[ i ];
1221 timesCountSD++;
1222 }
1223 }
1224 if( timesCountSD == 0 )
1225 timesCountSD++; /* Context switch, fudge it */
1226 printf( "%6ld", ( long ) ( timeCorrSumSD / timesCountSD ) );
1227
1228 #if 1 /* Print difference to fastest and mean times, usually only around
1229 1% */
1230 printf( " (dF = %4d, dM = %4d)\n",
1231 ( timeCorrSumSD / timesCountSD ) - timeMin,
1232 abs( ( timeCorrSumSD / timesCountSD ) - \
1233 ( timeCorrSum10 / timesCount10 ) ) );
1234 #endif /* 0 */
1235 #endif /* USE_SD */
1236
1237 /* Print the times in ms */
1238 if( noTimes > 1 )
1239 printf( "\n Per-op time = " );
1240 if( timeMS <= 0 )
1241 printf( "< 1" );
1242 else
1243 printf( "%ld", timeMS );
1244 printf( " ms.\n" );
1245
1246 return( ( timeMS <= 0 ) ? 1 : timeMS );
1247 }
1248
timeDisplay(HIRES_TIME timeValue)1249 int timeDisplay( HIRES_TIME timeValue )
1250 {
1251 return( timeDisplayMean( &timeValue, 1 ) );
1252 }
1253
1254 /* Timing-attack evaluation code */
1255
testTimingAttack(void)1256 int testTimingAttack( void )
1257 {
1258 CRYPT_CONTEXT cryptContext, decryptContext;
1259 CRYPT_CONTEXT sessionKeyContext;
1260 HIRES_TIME times[ 1000 ];
1261 BYTE encryptedKeyBlob[ 1024 ];
1262 int length, i, status;
1263
1264 /* Create the contexts needed for the decryption timing checks */
1265 status = cryptCreateContext( &sessionKeyContext, CRYPT_UNUSED,
1266 CRYPT_ALGO_3DES );
1267 if( cryptStatusError( status ) )
1268 return( FALSE );
1269 status = cryptGenerateKey( sessionKeyContext );
1270 if( cryptStatusError( status ) )
1271 return( FALSE );
1272 status = loadRSAContexts( CRYPT_UNUSED, &cryptContext, &decryptContext );
1273 if( !status )
1274 return( FALSE );
1275
1276 /* Create the encrypted key blob */
1277 status = cryptExportKey( encryptedKeyBlob, 1024, &length, cryptContext,
1278 sessionKeyContext );
1279 if( cryptStatusError( status ) )
1280 {
1281 printf( "cryptExportKeyEx() failed with error code %d, line %d.\n",
1282 status, __LINE__ );
1283 return( FALSE );
1284 }
1285 if( length != 174 )
1286 {
1287 printf( "Encrypted key should be %d bytes, was %d, line %d.\n",
1288 174, length, __LINE__ );
1289 return( FALSE );
1290 }
1291 cryptDestroyContext( sessionKeyContext );
1292
1293 /* Determine the time for the unmodified decrypt */
1294 #if 0
1295 for( i = 0; i < 200; i++ )
1296 {
1297 HIRES_TIME timeVal;
1298
1299 cryptCreateContext( &sessionKeyContext, CRYPT_UNUSED,
1300 CRYPT_ALGO_3DES );
1301 timeVal = timeDiff( 0 );
1302 status = cryptImportKey( encryptedKeyBlob, length, decryptContext,
1303 sessionKeyContext );
1304 timeVal = timeDiff( timeVal );
1305 cryptDestroyContext( sessionKeyContext );
1306 if( cryptStatusError( status ) )
1307 {
1308 printf( "cryptImportKey() failed with status %s, line %d.\n",
1309 status, __LINE__ );
1310 return( FALSE );
1311 }
1312 times[ i ] = timeVal;
1313 }
1314 #if 0
1315 printf( "Time for unmodified decrypt:\n" );
1316 for( i = 0; i < 200; i++ )
1317 {
1318 printf( "%5d ", times[ i ] );
1319 if( ( ( i + 1 ) % 10 ) == 0 )
1320 putchar( '\n' );
1321 }
1322 #endif /* 0 */
1323 timeDisplayMean( times, 200 );
1324 #endif /* 0 */
1325
1326 /* Manipulate the encrypted blob and see what timing effect it has */
1327 for( i = 0; i < 1000; i++ )
1328 {
1329 BYTE buffer[ 1024 ], *encryptedKeyPtr;
1330 HIRES_TIME timeVal;
1331
1332 /* For the 1024-bit key the encrypted value in the blob ranges from
1333 n + 46 to n + 173 (128 bytes, zero-based) */
1334 encryptedKeyPtr = buffer + 173;
1335 memcpy( buffer, encryptedKeyBlob, length );
1336 *encryptedKeyPtr ^= 0x01;
1337 status = cryptCreateContext( &sessionKeyContext, CRYPT_UNUSED,
1338 CRYPT_ALGO_3DES );
1339 if( cryptStatusError( status ) )
1340 return( FALSE );
1341 timeVal = timeDiff( 0 );
1342 status = cryptImportKey( buffer, length, decryptContext,
1343 sessionKeyContext );
1344 timeVal = timeDiff( timeVal );
1345 cryptDestroyContext( sessionKeyContext );
1346 if( !cryptStatusError( status ) )
1347 {
1348 printf( "Corrupted import wasn't detected, line %d.\n",
1349 __LINE__ );
1350 return( FALSE );
1351 }
1352 times[ i ] = timeVal;
1353 }
1354 #if 0
1355 printf( "Time for modified decrypt:\n" );
1356 for( i = 0; i < 1000; i++ )
1357 {
1358 printf( "%5d ", times[ i ] );
1359 if( ( ( i + 1 ) % 10 ) == 0 )
1360 putchar( '\n' );
1361 }
1362 #endif
1363 timeDisplayMean( times, 1000 );
1364
1365 return( TRUE );
1366 }
1367 #endif /* USE_TIMING */
1368
1369 /****************************************************************************
1370 * *
1371 * Error-handling Functions *
1372 * *
1373 ****************************************************************************/
1374
1375 /* Print extended error attribute information */
1376
printErrorAttributeInfo(const CRYPT_HANDLE cryptHandle)1377 void printErrorAttributeInfo( const CRYPT_HANDLE cryptHandle )
1378 {
1379 static const char *errorTypeString[] = {
1380 "CRYPT_ERRTYPE_NONE", "CRYPT_ERRTYPE_ATTR_SIZE",
1381 "CRYPT_ERRTYPE_ATTR_VALUE", "CRYPT_ERRTYPE_ATTR_ABSENT",
1382 "CRYPT_ERRTYPE_ATTR_PRESENT", "CRYPT_ERRTYPE_CONSTRAINT",
1383 "CRYPT_ERRTYPE_ISSUERCONSTRAINT", "NULL", "NULL"
1384 };
1385 const char *typeString = "<<<Unknown>>>";
1386 int errorType, errorLocus, status;
1387
1388 status = cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_ERRORTYPE,
1389 &errorType );
1390 if( cryptStatusOK( status ) )
1391 {
1392 if( errorType >= CRYPT_ERRTYPE_NONE && \
1393 errorType < CRYPT_ERRTYPE_LAST )
1394 typeString = errorTypeString[ errorType ];
1395 }
1396 status = cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_ERRORLOCUS,
1397 &errorLocus );
1398 if( cryptStatusOK( status ) && errorType != CRYPT_ERRTYPE_NONE )
1399 {
1400 fprintf( outputStream, " Error info attributes report locus %d, "
1401 "type %d (%s).\n", errorLocus, errorType, typeString );
1402 }
1403 }
1404
1405 /* Print extended object error information */
1406
printExtError(const CRYPT_HANDLE cryptHandle,const char * functionName,const int functionStatus,const int lineNo)1407 void printExtError( const CRYPT_HANDLE cryptHandle,
1408 const char *functionName, const int functionStatus,
1409 const int lineNo )
1410 {
1411 char errorMessage[ 512 ];
1412 int errorMessageLength, status;
1413
1414 fprintf( outputStream, "%s failed with error code %d, line %d.\n",
1415 functionName, functionStatus, lineNo );
1416 status = cryptGetAttributeString( cryptHandle, CRYPT_ATTRIBUTE_ERRORMESSAGE,
1417 errorMessage, &errorMessageLength );
1418 if( cryptStatusError( status ) )
1419 {
1420 fputs( " No extended error information available.\n",
1421 outputStream );
1422 printErrorAttributeInfo( cryptHandle );
1423 return;
1424 }
1425 errorMessage[ errorMessageLength ] = '\0';
1426 fprintf( outputStream, " Error message = %s'%s'.\n",
1427 ( errorMessageLength > ( 80 - 21 ) ) ? "\n " : "",
1428 errorMessage );
1429 printErrorAttributeInfo( cryptHandle );
1430 }
1431
1432 /* Exit with an error message. attrErrorExit() prints the locus and type,
1433 extErrorExit() prints the extended error code and message */
1434
attrErrorExit(const CRYPT_HANDLE cryptHandle,const char * functionName,const int errorCode,const int lineNumber)1435 BOOLEAN attrErrorExit( const CRYPT_HANDLE cryptHandle,
1436 const char *functionName, const int errorCode,
1437 const int lineNumber )
1438 {
1439 fprintf( outputStream, "%s failed with error code %d, line %d.\n",
1440 functionName, errorCode, lineNumber );
1441 printErrorAttributeInfo( cryptHandle );
1442 return( FALSE );
1443 }
1444
extErrorExit(const CRYPT_HANDLE cryptHandle,const char * functionName,const int errorCode,const int lineNumber)1445 BOOLEAN extErrorExit( const CRYPT_HANDLE cryptHandle,
1446 const char *functionName, const int errorCode,
1447 const int lineNumber )
1448 {
1449 printExtError( cryptHandle, functionName, errorCode, lineNumber );
1450 cryptDestroyObject( cryptHandle );
1451 return( FALSE );
1452 }
1453
1454 /****************************************************************************
1455 * *
1456 * Misc. Functions *
1457 * *
1458 ****************************************************************************/
1459
1460 /* Some algorithms can be disabled to eliminate patent problems or reduce the
1461 size of the code. The following functions are used to select generally
1462 equivalent alternatives if the required algorithm isn't available. These
1463 selections make certain assumptions, namely that at least one of the
1464 algorithms in the fallback chain is always available (which is guaranteed,
1465 3DES is used internally), and that they have the same general properties
1466 as the algorithms they're replacing, which is also usually the case,
1467 with CAST being a first-instance substitute for IDEA or RC2 and
1468 then 3DES as the fallback if CAST isn't available */
1469
selectCipher(const CRYPT_ALGO_TYPE algorithm)1470 CRYPT_ALGO_TYPE selectCipher( const CRYPT_ALGO_TYPE algorithm )
1471 {
1472 if( cryptStatusOK( cryptQueryCapability( algorithm, NULL ) ) )
1473 return( algorithm );
1474 if( cryptStatusOK( cryptQueryCapability( CRYPT_ALGO_CAST, NULL ) ) )
1475 return( CRYPT_ALGO_CAST );
1476 return( CRYPT_ALGO_3DES );
1477 }
1478
1479 /* Print a hex string. We assemble this as a single string and output it
1480 in one go to void it being broken up by output from another thread */
1481
printHex(const char * prefix,const BYTE * value,const int length)1482 void printHex( const char *prefix, const BYTE *value, const int length )
1483 {
1484 char buffer[ 4096 ];
1485 int pos = 0, i;
1486
1487 for( i = 0; i < min( length, 1024 ); i += 16 )
1488 {
1489 const int innerLen = min( length - i, 16 );
1490 int j;
1491
1492 pos += sprintf( buffer + pos, prefix );
1493 for( j = 0; j < innerLen; j++ )
1494 pos += sprintf( buffer + pos, "%02X ", value[ i + j ] );
1495 for( ; j < 16; j++ )
1496 pos += sprintf( buffer + pos, " " );
1497 for( j = 0; j < innerLen; j++ )
1498 {
1499 const BYTE ch = value[ i + j ];
1500
1501 pos += sprintf( buffer + pos, "%c", isprint( ch ) ? ch : '.' );
1502 }
1503 pos += sprintf( buffer + pos, "\n" );
1504 }
1505
1506 fprintf( outputStream, "%s", buffer );
1507 }
1508
1509 /* Add a collection of fields to a certificate */
1510
addCertFields(const CRYPT_CERTIFICATE certificate,const CERT_DATA * certData,const int lineNo)1511 int addCertFields( const CRYPT_CERTIFICATE certificate,
1512 const CERT_DATA *certData, const int lineNo )
1513 {
1514 int i;
1515
1516 for( i = 0; certData[ i ].type != CRYPT_ATTRIBUTE_NONE; i++ )
1517 {
1518 int status;
1519
1520 switch( certData[ i ].componentType )
1521 {
1522 case IS_NUMERIC:
1523 status = cryptSetAttribute( certificate,
1524 certData[ i ].type, certData[ i ].numericValue );
1525 if( cryptStatusError( status ) )
1526 {
1527 fprintf( outputStream, "cryptSetAttribute() for "
1528 "entry %d, field ID %d,\n value %d, failed "
1529 "with error code %d, line %d.\n", i + 1,
1530 certData[ i ].type, certData[ i ].numericValue,
1531 status, lineNo );
1532 }
1533 break;
1534
1535 case IS_STRING:
1536 status = cryptSetAttributeString( certificate,
1537 certData[ i ].type, certData[ i ].stringValue,
1538 certData[ i ].numericValue ? \
1539 certData[ i ].numericValue : \
1540 paramStrlen( certData[ i ].stringValue ) );
1541 if( cryptStatusError( status ) )
1542 {
1543 #if defined( _MSC_VER ) && ( _MSC_VER == 1200 ) && !defined( NDEBUG )
1544 if( status == CRYPT_ERROR_INVALID && \
1545 paramStrlen( certData[ i ].stringValue ) == 2 && \
1546 !memcmp( certData[ i ].stringValue, "NZ", 2 ) )
1547 {
1548 /* Warn about BoundsChecker-induced Heisenbugs */
1549 puts( " ********************" );
1550 puts( "If you're running this under BoundsChecker "
1551 "you need to disable it to complete\nthe test "
1552 "since it causes errors in the certificate "
1553 "string-checking code. The\nfollowing error "
1554 "is caused by BoundsChecker, not by the "
1555 "self-test failing." );
1556 puts( " ********************" );
1557 }
1558 #endif /* VC++ 6 */
1559 fprintf( outputStream, "cryptSetAttributeString() for "
1560 "entry %d, field ID %d,\n value '%s', failed "
1561 "with error code %d, line %d.\n", i + 1,
1562 certData[ i ].type,
1563 ( char * ) certData[ i ].stringValue, status,
1564 lineNo );
1565 }
1566 break;
1567
1568 #ifdef HAS_WIDECHAR
1569 case IS_WCSTRING:
1570 status = cryptSetAttributeString( certificate,
1571 certData[ i ].type, certData[ i ].stringValue,
1572 wcslen( certData[ i ].stringValue ) * sizeof( wchar_t ) );
1573 if( cryptStatusError( status ) )
1574 {
1575 fprintf( outputStream, "cryptSetAttributeString() for "
1576 "entry %d, field ID %d,\n value '%s', failed "
1577 "with error code %d, line %d.\n", i + 1,
1578 certData[ i ].type,
1579 ( char * ) certData[ i ].stringValue, status,
1580 lineNo );
1581 }
1582 break;
1583 #endif /* HAS_WIDECHAR */
1584
1585 case IS_TIME:
1586 status = cryptSetAttributeString( certificate,
1587 certData[ i ].type, &certData[ i ].timeValue,
1588 sizeof( time_t ) );
1589 if( cryptStatusError( status ) )
1590 {
1591 fprintf( outputStream, "cryptSetAttributeString() for "
1592 "entry %d, field ID %d,\n value 0x"
1593 TIMET_FORMAT ", failed with error code %d, "
1594 "line %d.\n", i + 1, certData[ i ].type,
1595 certData[ i ].timeValue, status, lineNo );
1596 }
1597 break;
1598
1599 default:
1600 assert( FALSE );
1601 return( FALSE );
1602 }
1603 if( cryptStatusError( status ) )
1604 {
1605 printErrorAttributeInfo( certificate );
1606 return( FALSE );
1607 }
1608 }
1609
1610 return( TRUE );
1611 }
1612
1613 /* Compare two blocks and data and check whether they're identical */
1614
compareData(const void * origData,const int origDataLength,const void * recovData,const int recovDataLength)1615 int compareData( const void *origData, const int origDataLength,
1616 const void *recovData, const int recovDataLength )
1617 {
1618 if( origDataLength != recovDataLength )
1619 {
1620 printf( "Original length %d doesn't match recovered data "
1621 "length %d.\n", origDataLength, recovDataLength );
1622
1623 return( FALSE );
1624 }
1625 if( memcmp( origData, recovData, origDataLength ) )
1626 {
1627 printf( "Data of length %d doesn't match recovered data:\n",
1628 origDataLength );
1629 printf( "Original data:\n" );
1630 printHex( " ", origData, min( origDataLength, 64 ) );
1631 printf( "Recovered data:\n" );
1632 printHex( " ", recovData, min( origDataLength, 64 ) );
1633
1634 return( FALSE );
1635 }
1636
1637 return( TRUE );
1638 }
1639
1640 /****************************************************************************
1641 * *
1642 * Debug Functions *
1643 * *
1644 ****************************************************************************/
1645
1646 /* Write an object to a file for debugging purposes */
1647
1648 #if defined( _MSC_VER ) && \
1649 !( defined( _WIN32_WCE ) || defined( __PALMSOURCE__ ) )
1650 #include <direct.h>
1651 #include <io.h>
1652 #endif /* VC++ Win16/Win32 */
1653
debugDump(const char * fileName,const void * data,const int dataLength)1654 void debugDump( const char *fileName, const void *data, const int dataLength )
1655 {
1656 FILE *filePtr;
1657 #ifdef __UNIX__
1658 const char *tmpPath = getenv( "TMPDIR" );
1659 char fileNameBuffer[ FILENAME_BUFFER_SIZE ];
1660 const int tmpPathLen = ( tmpPath != NULL ) ? strlen( tmpPath ) : 0;
1661 #else
1662 char fileNameBuffer[ 128 ];
1663 #endif /* __UNIX__ */
1664 const int length = strlen( fileName );
1665 int count;
1666
1667 fileNameBuffer[ 0 ] = '\0';
1668 #if defined( _WIN32_WCE )
1669 /* Under WinCE we don't want to scribble a ton of data into flash every
1670 time we're run so we don't try and do anything */
1671 return;
1672 #elif ( defined( _MSC_VER ) && !defined( __PALMSOURCE__ ) )
1673 /* If the path isn't absolute, deposit it in a temp directory. Note
1674 that we have to use underscores in front of the Posix functions
1675 because these were deprecated starting with VS 2005. In addition we
1676 have to explicitly exclude oldnames.lib (which usually isn't a
1677 included in the libraries installed with VS) from the link, inclusion
1678 of this is triggered by the compiler seeing the Posix or underscore-
1679 Posix functions */
1680 #if defined( _MSC_VER ) && ( _MSC_VER >= 1400 )
1681 #pragma comment(linker, "/nodefaultlib:oldnames.lib")
1682 #endif /* VC++ 2005 and newer misconfiguration */
1683 if( fileName[ 1 ] != ':' )
1684 {
1685 /* It's my code, I can use whatever paths I feel like */
1686 if( _access( "d:/tmp/", 6 ) == 0 )
1687 {
1688 /* There's a data partition available, dump the info there */
1689 if( _access( "d:/tmp/", 6 ) == -1 && \
1690 !CreateDirectory( "d:/tmp", NULL ) )
1691 return;
1692 strcpy( fileNameBuffer, "d:/tmp/" );
1693 }
1694 else
1695 {
1696 /* There's no separate data partition, everything's dumped into
1697 the same partition */
1698 if( _access( "c:/tmp/", 6 ) == -1 && \
1699 !CreateDirectory( "c:/tmp", NULL ) )
1700 return;
1701 strcpy( fileNameBuffer, "c:/tmp/" );
1702 }
1703 }
1704 #elif defined( __UNIX__ )
1705 /* If the path isn't absolute, deposit it in a temp directory */
1706 if( fileName[ 0 ] != '/' )
1707 {
1708 if( tmpPathLen > 3 && tmpPathLen < FILENAME_BUFFER_SIZE - 64 )
1709 {
1710 strcpy( fileNameBuffer, tmpPath );
1711 if( fileNameBuffer[ tmpPathLen - 1 ] != '/' )
1712 strcat( fileNameBuffer + tmpPathLen, "/" );
1713 }
1714 else
1715 strcpy( fileNameBuffer, "/tmp/" );
1716 }
1717 #else
1718 fileNameBuffer[ 0 ] = '\0';
1719 #endif /* OS-specific paths */
1720 strcat( fileNameBuffer, fileName );
1721 if( length <= 3 || fileName[ length - 4 ] != '.' )
1722 strcat( fileNameBuffer, ".der" );
1723
1724 #if defined( __VMCMS__ )
1725 {
1726 char formatBuffer[ 32 ];
1727
1728 sprintf( formatBuffer, "wb, recfm=F, lrecl=%d, noseek", dataLength );
1729 filePtr = fopen( fileNameBuffer, formatBuffer );
1730 }
1731 if( filePtr == NULL )
1732 return;
1733 #else
1734 if( ( filePtr = fopen( fileNameBuffer, "wb" ) ) == NULL )
1735 return;
1736 #endif /* __VMCMS__ */
1737 count = fwrite( data, 1, dataLength, filePtr );
1738 fclose( filePtr );
1739 if( count < length )
1740 {
1741 printf( "Warning: Couldn't dump '%s' to disk.\n", fileName );
1742 remove( fileName );
1743 }
1744 }
1745
1746 /****************************************************************************
1747 * *
1748 * Session Functions *
1749 * *
1750 ****************************************************************************/
1751
1752 /* Print information on the peer that we're talking to */
1753
printConnectInfo(const CRYPT_SESSION cryptSession)1754 int printConnectInfo( const CRYPT_SESSION cryptSession )
1755 {
1756 #ifndef UNICODE_STRINGS
1757 time_t theTime;
1758 #endif /* UNICODE_STRINGS */
1759 C_CHR serverName[ 128 ];
1760 int serverNameLength, serverPort, status;
1761
1762 status = cryptGetAttributeString( cryptSession, CRYPT_SESSINFO_CLIENT_NAME,
1763 serverName, &serverNameLength );
1764 if( cryptStatusOK( status ) )
1765 {
1766 status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_CLIENT_PORT,
1767 &serverPort );
1768 }
1769 if( cryptStatusError( status ) )
1770 return( FALSE );
1771 #ifdef UNICODE_STRINGS
1772 serverName[ serverNameLength / sizeof( wchar_t ) ] = TEXT( '\0' );
1773 printf( "SVR: Connect attempt from %S, port %d", serverName, serverPort );
1774 #else
1775 serverName[ serverNameLength ] = '\0';
1776 time( &theTime );
1777 printf( "SVR: Connect attempt from %s, port %d, on %s.\n", serverName,
1778 serverPort, getTimeString( theTime, 0 ) );
1779 #endif /* UNICODE_STRINGS */
1780 fflush( stdout );
1781
1782 /* Display all the attributes that we've got */
1783 status = displayAttributes( cryptSession );
1784 fflush( stdout );
1785 return( status );
1786 }
1787
1788 /* Print security info for the session */
1789
printSecurityInfo(const CRYPT_SESSION cryptSession,const BOOLEAN isServer,const BOOLEAN showFingerprint,const BOOLEAN showServerKeyInfo,const BOOLEAN showClientCertInfo)1790 int printSecurityInfo( const CRYPT_SESSION cryptSession,
1791 const BOOLEAN isServer,
1792 const BOOLEAN showFingerprint,
1793 const BOOLEAN showServerKeyInfo,
1794 const BOOLEAN showClientCertInfo )
1795 {
1796 int cryptAlgo, keySize DUMMY_INIT, version DUMMY_INIT, status;
1797
1798 /* Print general security info */
1799 status = cryptGetAttribute( cryptSession, CRYPT_CTXINFO_ALGO,
1800 &cryptAlgo );
1801 if( cryptStatusOK( status ) )
1802 status = cryptGetAttribute( cryptSession, CRYPT_CTXINFO_KEYSIZE,
1803 &keySize );
1804 if( cryptStatusOK( status ) )
1805 status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_VERSION,
1806 &version );
1807 if( cryptStatusError( status ) )
1808 {
1809 printf( "Couldn't get session security parameters, status %d, line "
1810 "%d.\n", status, __LINE__ );
1811 return( FALSE );
1812 }
1813 printf( "%sSession is protected using algorithm %d with a %d bit key,\n"
1814 " protocol version %d.\n", isServer ? "SVR: " : "",
1815 cryptAlgo, keySize * 8, version );
1816 if( showServerKeyInfo || showClientCertInfo )
1817 {
1818 CRYPT_CONTEXT serverKey;
1819
1820 status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_RESPONSE,
1821 &serverKey );
1822 if( cryptStatusOK( status ) )
1823 {
1824 status = cryptGetAttribute( serverKey, CRYPT_CTXINFO_ALGO,
1825 &cryptAlgo );
1826 if( cryptStatusOK( status ) )
1827 status = cryptGetAttribute( serverKey, CRYPT_CTXINFO_KEYSIZE,
1828 &keySize );
1829 cryptDestroyContext( serverKey );
1830 }
1831 if( cryptStatusError( status ) )
1832 {
1833 printf( "Couldn't get server security parameters, status %d, line "
1834 "%d.\n", status, __LINE__ );
1835 return( FALSE );
1836 }
1837 printf( "%s key uses algorithm %d, key size %d bits.\n",
1838 showClientCertInfo ? "SVR: Client authentication" : "Server",
1839 cryptAlgo, keySize * 8 );
1840 }
1841 fflush( stdout );
1842 if( isServer || !showFingerprint )
1843 return( TRUE );
1844
1845 status = printFingerprint( cryptSession, FALSE );
1846 fflush( stdout );
1847 return( status );
1848 }
1849
printFingerprint(const CRYPT_SESSION cryptSession,const BOOLEAN isServer)1850 int printFingerprint( const CRYPT_SESSION cryptSession,
1851 const BOOLEAN isServer )
1852 {
1853 BYTE fingerPrint[ CRYPT_MAX_HASHSIZE ];
1854 int length, status;
1855
1856 /* Print the server key fingerprint */
1857 status = cryptGetAttributeString( cryptSession,
1858 CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1,
1859 fingerPrint, &length );
1860 if( cryptStatusError( status ) )
1861 {
1862 printf( "cryptGetAttributeString() failed with error code "
1863 "%d, line %d.\n", status, __LINE__ );
1864 return( FALSE );
1865 }
1866 printf( "%sServer key fingerprint =\n", isServer ? "SVR: " : "" );
1867 printHex( " ", fingerPrint, length );
1868
1869 return( TRUE );
1870 }
1871
1872 /* Set up a client/server to connect locally. For the client this simply
1873 tells it where to connect, for the server this binds it to the local
1874 (loopback) address so that we don't inadvertently open up outside ports
1875 (admittedly they can't do much except run the hardcoded self-test, but
1876 it's better not to do this at all) */
1877
setLocalConnect(const CRYPT_SESSION cryptSession,const int port)1878 BOOLEAN setLocalConnect( const CRYPT_SESSION cryptSession, const int port )
1879 {
1880 int status;
1881
1882 if( LOCAL_HOST_NAME[ 0 ] != 'l' )
1883 {
1884 puts( "Warning: Enabling server on non-local interface '"
1885 LOCAL_HOST_NAME "'." );
1886 }
1887
1888 status = cryptSetAttributeString( cryptSession,
1889 CRYPT_SESSINFO_SERVER_NAME,
1890 NATIVE_LOCAL_HOST_NAME,
1891 paramStrlen( NATIVE_LOCAL_HOST_NAME ) );
1892 #ifdef __UNIX__
1893 /* If we're running under Unix, set the port to a nonprivileged one so
1894 that we don't have to run as root. For anything other than very low-
1895 numbered ports (e.g. SSH), the way we determine the port is to repeat
1896 the first digit, so e.g. TSA on 318 becomes 3318, this seems to be
1897 the method most commonly used */
1898 if( cryptStatusOK( status ) && port < 1024 )
1899 {
1900 if( port < 100 )
1901 status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT,
1902 port + 4000 );
1903 else
1904 status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT,
1905 ( ( port / 100 ) * 1000 ) + port );
1906 }
1907 #endif /* __UNIX__ */
1908 if( cryptStatusError( status ) )
1909 {
1910 printf( "cryptSetAttribute/AttributeString() failed with error code "
1911 "%d, line %d.\n", status, __LINE__ );
1912 return( FALSE );
1913 }
1914
1915 return( TRUE );
1916 }
1917
1918 /* Check whether a remote server might be down, which is treated as a soft
1919 fail rather than a hard-fail error condition */
1920
isServerDown(const CRYPT_SESSION cryptSession,const int errorStatus)1921 BOOLEAN isServerDown( const CRYPT_SESSION cryptSession,
1922 const int errorStatus )
1923 {
1924 /* If we get a straight connect error then we don't treat it as a
1925 serious failure */
1926 if( errorStatus == CRYPT_ERROR_OPEN || \
1927 errorStatus == CRYPT_ERROR_NOTFOUND )
1928 return( TRUE );
1929
1930 /* Under Unix a connection-refused will be reported as a
1931 CRYPT_ERROR_PERMISSION (under Winsock it's just a generic open
1932 error), and a failure to connect may also be reported via a timeout
1933 as CRYPT_ERROR_TIMEOUT, so we check for these as alternatives to an
1934 open error.
1935
1936 Note that some firewalls may allow a connect but then block reads, in
1937 which case we'd need to check for the string "Timeout on read" as
1938 well, however we don't enable this by default because some broken
1939 servers may respond to unexpected PDUs by hanging or closing the
1940 connection, which will also lead to a read timeout for a condition
1941 that's more than just a transient network error */
1942 #ifdef __UNIX__
1943 if( errorStatus == CRYPT_ERROR_PERMISSION || \
1944 errorStatus == CRYPT_ERROR_TIMEOUT )
1945 {
1946 char errorMessage[ 512 ];
1947 int errorMessageLength, status;
1948
1949 status = cryptGetAttributeString( cryptSession,
1950 CRYPT_ATTRIBUTE_ERRORMESSAGE,
1951 errorMessage, &errorMessageLength );
1952 if( cryptStatusOK( status ) )
1953 {
1954 errorMessage[ errorMessageLength ] = '\0';
1955 if( strstr( errorMessage, "ECONNREFUSED" ) != NULL || \
1956 strstr( errorMessage, "ETIMEDOUT" ) != NULL || \
1957 strstr( errorMessage, "Timeout on connect" ) != NULL )
1958 return( TRUE );
1959 }
1960 }
1961 #endif /* __UNX__ */
1962
1963 return( FALSE );
1964 }
1965
1966 /* Run a persistent server session, recycling the connection if the client
1967 kept the link open */
1968
printOperationType(const CRYPT_SESSION cryptSession)1969 static void printOperationType( const CRYPT_SESSION cryptSession )
1970 {
1971 struct {
1972 const int operation;
1973 const char *name;
1974 } operationTypeTbl[] = {
1975 { CRYPT_REQUESTTYPE_NONE, "(None)" },
1976 { CRYPT_REQUESTTYPE_INITIALISATION, "ir" },
1977 { CRYPT_REQUESTTYPE_CERTIFICATE, "cr" },
1978 { CRYPT_REQUESTTYPE_KEYUPDATE, "kur" },
1979 { CRYPT_REQUESTTYPE_REVOCATION, "rr" },
1980 { CRYPT_REQUESTTYPE_PKIBOOT, "pkiBoot" },
1981 { -1, "(Unknown)" }
1982 };
1983 char userID[ CRYPT_MAX_TEXTSIZE ];
1984 int userIDsize DUMMY_INIT, requestType, i, status;
1985
1986 status = cryptGetAttribute( cryptSession,
1987 CRYPT_SESSINFO_CMP_REQUESTTYPE,
1988 &requestType );
1989 if( cryptStatusOK( status ) )
1990 status = cryptGetAttributeString( cryptSession,
1991 CRYPT_SESSINFO_USERNAME,
1992 userID, &userIDsize );
1993 if( cryptStatusError( status ) )
1994 {
1995 printf( "cryptGetAttribute/AttributeString() failed with error "
1996 "code %d, line %d.\n", status, __LINE__ );
1997 return;
1998 }
1999 userID[ userIDsize ] = '\0';
2000 for( i = 0; operationTypeTbl[ i ].operation != requestType && \
2001 operationTypeTbl[ i ].operation != -1; i++ );
2002 printf( "SVR: Operation type was %d = %s, user '%s'.\n",
2003 requestType, operationTypeTbl[ i ].name, userID );
2004 fflush( stdout );
2005 }
2006
activatePersistentServerSession(const CRYPT_SESSION cryptSession,const BOOLEAN showOperationType)2007 int activatePersistentServerSession( const CRYPT_SESSION cryptSession,
2008 const BOOLEAN showOperationType )
2009 {
2010 BOOLEAN connectionActive = FALSE;
2011 int status;
2012
2013 do
2014 {
2015 /* Activate the connection */
2016 status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE,
2017 TRUE );
2018 if( status == CRYPT_ERROR_READ && connectionActive )
2019 {
2020 /* The other side closed the connection after a previous
2021 successful transaction, this isn't an error */
2022 return( CRYPT_OK );
2023 }
2024
2025 /* Print connection info and check whether the connection is still
2026 active. If it is, we recycle the session so that we can process
2027 another request */
2028 printConnectInfo( cryptSession );
2029 if( cryptStatusOK( status ) )
2030 {
2031 if( showOperationType )
2032 printOperationType( cryptSession );
2033 status = cryptGetAttribute( cryptSession,
2034 CRYPT_SESSINFO_CONNECTIONACTIVE,
2035 &connectionActive );
2036 }
2037 }
2038 while( cryptStatusOK( status ) && connectionActive );
2039
2040 return( status );
2041 }
2042
2043 /****************************************************************************
2044 * *
2045 * Attribute Dump Routines *
2046 * *
2047 ****************************************************************************/
2048
2049 /* Print a list of all attributes present in an object. We assemble this as
2050 a single string and output it in one go to void it being broken up by
2051 output from another thread */
2052
displayAttributes(const CRYPT_HANDLE cryptHandle)2053 int displayAttributes( const CRYPT_HANDLE cryptHandle )
2054 {
2055 int status;
2056
2057 if( cryptStatusError( \
2058 cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2059 CRYPT_CURSOR_FIRST ) ) )
2060 return( TRUE );
2061
2062 fputs( "Attributes present (by cryptlib ID) are:\n", outputStream );
2063 do
2064 {
2065 BOOLEAN firstAttr = TRUE;
2066 char buffer[ 4096 ];
2067 int value, pos = 0;
2068
2069 status = cryptGetAttribute( cryptHandle,
2070 CRYPT_ATTRIBUTE_CURRENT_GROUP, &value );
2071 if( cryptStatusError( status ) )
2072 {
2073 printf( "\nCurrent attribute group value read failed with "
2074 "error code %d, line %d.\n", status, __LINE__ );
2075 return( FALSE );
2076 }
2077 pos += sprintf( buffer + pos, " Attribute group %d, values =", value );
2078 do
2079 {
2080 status = cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT,
2081 &value );
2082 if( cryptStatusError( status ) )
2083 {
2084 printf( "\nCurrent attribute value read failed with error "
2085 "code %d, line %d.\n", status, __LINE__ );
2086 return( FALSE );
2087 }
2088 if( !firstAttr )
2089 pos += sprintf( buffer + pos, "," );
2090 pos += sprintf( buffer + pos, " %d", value );
2091 firstAttr = FALSE;
2092 }
2093 while( cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT,
2094 CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2095 sprintf( buffer + pos, ".\n" );
2096 fprintf( outputStream, "%s", buffer );
2097 }
2098 while( cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2099 CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2100
2101 /* Reset the cursor to the first attribute. This is useful for things
2102 like envelopes and sessions where the cursor points at the first
2103 attribute that needs to be handled */
2104 cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2105 CRYPT_CURSOR_FIRST );
2106 return( TRUE );
2107 }
2108
2109 /****************************************************************************
2110 * *
2111 * Certificate Dump Routines *
2112 * *
2113 ****************************************************************************/
2114
2115 /* Check whether a string may be a Unicode string */
2116
isUnicode(const BYTE * value,const int length)2117 static BOOLEAN isUnicode( const BYTE *value, const int length )
2118 {
2119 wchar_t wcValue[ 16 + 4 ];
2120
2121 /* If it's an odd length or too short to reliably guess, report it as
2122 non-Unicode */
2123 if( ( length % sizeof( wchar_t ) ) || length <= sizeof( wchar_t ) * 2 )
2124 return( FALSE );
2125
2126 /* If the first four characters are ASCII then it's unlikely that it'll
2127 be Unicode */
2128 if( isprint( value[ 0 ] ) && isprint( value[ 1 ] ) && \
2129 isprint( value[ 2 ] ) && isprint( value[ 3 ] ) )
2130 return( FALSE );
2131
2132 /* We need at least three widechars for the next check */
2133 if( length <= sizeof( wchar_t ) * 3 )
2134 return( FALSE );
2135
2136 /* Copy the byte-aligned value into a local wchar_t-aligned buffer for
2137 analysis */
2138 memcpy( wcValue, value, min( length, 16 ) );
2139
2140 /* Check whether the first 3 widechars have identical high bytes. This
2141 isn't totally reliable (e.g. "tanaka" will give a false positive,
2142 { 0x0160, 0x0069, 0x006B } will give a false negative) but it's close
2143 enough */
2144 if( ( wcValue[ 0 ] & 0xFF00 ) == ( wcValue[ 1 ] & 0xFF00 ) && \
2145 ( wcValue[ 0 ] & 0xFF00 ) == ( wcValue[ 2 ] & 0xFF00 ) )
2146 return( TRUE );
2147
2148 return( FALSE );
2149 }
2150
2151 /* The following function performs many attribute accesses, rather than using
2152 huge numbers of status checks we use the following macro to check each
2153 attribute access */
2154
2155 #define CHK( function ) \
2156 status = function; \
2157 if( cryptStatusError( status ) ) \
2158 return( certInfoErrorExit( #function, status, __LINE__ ) )
2159
certInfoErrorExit(const char * functionCall,const int status,const int line)2160 static int certInfoErrorExit( const char *functionCall, const int status,
2161 const int line )
2162 {
2163 printf( "\n%s failed with status %d, line %d.\n", functionCall,
2164 status, line );
2165 return( FALSE );
2166 }
2167
2168 /* Print a DN or altName */
2169
printComponent(const CRYPT_CERTIFICATE certificate,const CRYPT_ATTRIBUTE_TYPE component,const char * prefixString)2170 static int printComponent( const CRYPT_CERTIFICATE certificate,
2171 const CRYPT_ATTRIBUTE_TYPE component,
2172 const char *prefixString )
2173 {
2174 char buffer[ 1024 + 1 ];
2175 int length, status;
2176
2177 status = cryptGetAttributeString( certificate, component, NULL,
2178 &length );
2179 if( cryptStatusError( status ) )
2180 {
2181 if( status == CRYPT_ERROR_NOTAVAIL && \
2182 component == CRYPT_CERTINFO_DN )
2183 {
2184 /* Report this special-case condition explicitly */
2185 fputs( " (Name contains characters that prevent it from being "
2186 "represented as a\n text string).\n", outputStream );
2187 }
2188 return( FALSE );
2189 }
2190 if( length > 1024 )
2191 {
2192 /* This should never happen since the longest permitted component
2193 string has 128 characters, but we check for it just in case */
2194 puts( " (Name is too long to display, > 1K characters)." );
2195 return( FALSE );
2196 }
2197 status = cryptGetAttributeString( certificate, component, buffer,
2198 &length );
2199 if( cryptStatusError( status ) )
2200 return( FALSE );
2201 if( isUnicode( buffer, length ) )
2202 {
2203 wchar_t wcBuffer[ 1024 + 1 ];
2204
2205 /* Copy the byte-aligned value into a local wchar_t-aligned buffer
2206 for display */
2207 memcpy( wcBuffer, buffer, length );
2208 wcBuffer[ length / sizeof( wchar_t ) ] = TEXT( '\0' );
2209 fprintf( outputStream, " %s = %S.\n", prefixString, wcBuffer );
2210 return( TRUE );
2211 }
2212 buffer[ length ] = '\0';
2213 fprintf( outputStream, " %s = %s.\n", prefixString, buffer );
2214
2215 return( TRUE );
2216 }
2217
printComponents(const CRYPT_CERTIFICATE certificate,const CRYPT_ATTRIBUTE_TYPE component,const char * prefixString)2218 static int printComponents( const CRYPT_CERTIFICATE certificate,
2219 const CRYPT_ATTRIBUTE_TYPE component,
2220 const char *prefixString )
2221 {
2222 int status;
2223
2224 /* Try and print the component if it's present */
2225 if( !printComponent( certificate, component, prefixString ) )
2226 return( FALSE );
2227
2228 /* If it's not a DN or altName component, we're done */
2229 if( !( component >= CRYPT_CERTINFO_COUNTRYNAME && \
2230 component <= CRYPT_CERTINFO_COMMONNAME ) && \
2231 !( component >= CRYPT_CERTINFO_OTHERNAME_TYPEID && \
2232 component <= CRYPT_CERTINFO_REGISTEREDID ) )
2233 return( TRUE );
2234
2235 /* Check for further components, for multivalued components in altNames */
2236 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT_INSTANCE,
2237 component ) );
2238 while( cryptSetAttribute( certificate,
2239 CRYPT_ATTRIBUTE_CURRENT_INSTANCE,
2240 CRYPT_CURSOR_NEXT ) == CRYPT_OK )
2241 {
2242 char buffer[ 64 ];
2243
2244 sprintf( buffer, " + %s", prefixString );
2245 if( !printComponent( certificate, component, buffer ) )
2246 return( FALSE );
2247 }
2248 return( TRUE );
2249 }
2250
printDN(const CRYPT_CERTIFICATE certificate)2251 static void printDN( const CRYPT_CERTIFICATE certificate )
2252 {
2253 printComponents( certificate, CRYPT_CERTINFO_DN, "DN string" );
2254 printComponents( certificate, CRYPT_CERTINFO_COUNTRYNAME, "C" );
2255 printComponents( certificate, CRYPT_CERTINFO_STATEORPROVINCENAME, "S" );
2256 printComponents( certificate, CRYPT_CERTINFO_LOCALITYNAME, "L" );
2257 printComponents( certificate, CRYPT_CERTINFO_ORGANIZATIONNAME, "O" );
2258 printComponents( certificate, CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, "OU" );
2259 printComponents( certificate, CRYPT_CERTINFO_COMMONNAME, "CN" );
2260 }
2261
printAltName(const CRYPT_CERTIFICATE certificate)2262 static void printAltName( const CRYPT_CERTIFICATE certificate )
2263 {
2264 int status;
2265
2266 printComponents( certificate, CRYPT_CERTINFO_RFC822NAME, "Email" );
2267 printComponents( certificate, CRYPT_CERTINFO_DNSNAME, "DNSName" );
2268 printComponents( certificate, CRYPT_CERTINFO_EDIPARTYNAME_NAMEASSIGNER, "EDI Nameassigner" );
2269 printComponents( certificate, CRYPT_CERTINFO_EDIPARTYNAME_PARTYNAME, "EDI Partyname" );
2270 printComponents( certificate, CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, "URL" );
2271 printComponents( certificate, CRYPT_CERTINFO_IPADDRESS, "IP" );
2272 printComponents( certificate, CRYPT_CERTINFO_REGISTEREDID, "Registered ID" );
2273 status = cryptSetAttribute( certificate, CRYPT_CERTINFO_DIRECTORYNAME,
2274 CRYPT_UNUSED );
2275 if( cryptStatusOK( status ) )
2276 {
2277 fprintf( outputStream, " altName DN is:\n" );
2278 printDN( certificate );
2279 }
2280 }
2281
2282 /* Print information on a certificate */
2283
printCertInfo(const CRYPT_CERTIFICATE certificate)2284 int printCertInfo( const CRYPT_CERTIFICATE certificate )
2285 {
2286 CRYPT_CERTTYPE_TYPE certType;
2287 char buffer[ 1024 ];
2288 int length, value, status;
2289
2290 CHK( cryptGetAttribute( certificate, CRYPT_CERTINFO_CERTTYPE, &value ) );
2291 certType = value;
2292
2293 /* Display the issuer and subject DN */
2294 if( certType != CRYPT_CERTTYPE_CERTREQUEST && \
2295 certType != CRYPT_CERTTYPE_REQUEST_CERT && \
2296 certType != CRYPT_CERTTYPE_REQUEST_REVOCATION && \
2297 certType != CRYPT_CERTTYPE_RTCS_REQUEST && \
2298 certType != CRYPT_CERTTYPE_RTCS_RESPONSE && \
2299 certType != CRYPT_CERTTYPE_OCSP_REQUEST && \
2300 certType != CRYPT_CERTTYPE_CMS_ATTRIBUTES && \
2301 certType != CRYPT_CERTTYPE_PKIUSER )
2302 {
2303 fputs( "Certificate object issuer name is:\n", outputStream );
2304 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2305 CRYPT_CERTINFO_ISSUERNAME ) );
2306 printDN( certificate );
2307 if( cryptStatusOK( \
2308 cryptGetAttribute( certificate,
2309 CRYPT_CERTINFO_ISSUERALTNAME, &value ) ) )
2310 {
2311 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2312 CRYPT_CERTINFO_ISSUERALTNAME ) );
2313 printAltName( certificate );
2314 }
2315 }
2316 if( certType != CRYPT_CERTTYPE_CRL && \
2317 certType != CRYPT_CERTTYPE_REQUEST_REVOCATION && \
2318 certType != CRYPT_CERTTYPE_CMS_ATTRIBUTES && \
2319 certType != CRYPT_CERTTYPE_RTCS_REQUEST && \
2320 certType != CRYPT_CERTTYPE_RTCS_RESPONSE && \
2321 certType != CRYPT_CERTTYPE_OCSP_REQUEST && \
2322 certType != CRYPT_CERTTYPE_OCSP_RESPONSE )
2323 {
2324 fputs( "Certificate object subject name is:\n", outputStream );
2325 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2326 CRYPT_CERTINFO_SUBJECTNAME ) );
2327 printDN( certificate );
2328 if( cryptStatusOK( \
2329 cryptGetAttribute( certificate,
2330 CRYPT_CERTINFO_SUBJECTALTNAME, &value ) ) )
2331 {
2332 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2333 CRYPT_CERTINFO_SUBJECTALTNAME ) );
2334 printAltName( certificate );
2335 }
2336 }
2337
2338 /* Display the validity information */
2339 #ifndef _WIN32_WCE
2340 if( certType == CRYPT_CERTTYPE_CERTCHAIN ||
2341 certType == CRYPT_CERTTYPE_CERTIFICATE || \
2342 certType == CRYPT_CERTTYPE_ATTRIBUTE_CERT )
2343 {
2344 time_t validFrom, validTo;
2345
2346 CHK( cryptGetAttributeString( certificate, CRYPT_CERTINFO_VALIDFROM,
2347 &validFrom, &length ) );
2348 CHK( cryptGetAttributeString( certificate, CRYPT_CERTINFO_VALIDTO,
2349 &validTo, &length ) );
2350 fprintf( outputStream, "Certificate is valid from %s to %s.\n",
2351 getTimeString( validFrom, 0 ),
2352 getTimeString( validTo, 1 ) );
2353 }
2354 if( certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
2355 {
2356 time_t thisUpdate, nextUpdate;
2357
2358 status = cryptGetAttributeString( certificate, CRYPT_CERTINFO_THISUPDATE,
2359 &thisUpdate, &length );
2360 if( cryptStatusOK( status ) )
2361 {
2362 /* RTCS basic responses only return a minimal valid/not valid
2363 status so failing to find a time isn't an error */
2364 status = cryptGetAttributeString( certificate,
2365 CRYPT_CERTINFO_NEXTUPDATE,
2366 &nextUpdate, &length );
2367 if( cryptStatusOK( status ) )
2368 {
2369 fprintf( outputStream, "OCSP source CRL time %s,\n next "
2370 "update %s.\n", getTimeString( thisUpdate, 0 ),
2371 getTimeString( nextUpdate, 1 ) );
2372 }
2373 else
2374 {
2375 fprintf( outputStream, "OCSP source CRL time %s.\n",
2376 getTimeString( thisUpdate, 0 ) );
2377 }
2378 }
2379 }
2380 if( certType == CRYPT_CERTTYPE_CRL )
2381 {
2382 time_t thisUpdate, nextUpdate;
2383
2384 CHK( cryptGetAttributeString( certificate, CRYPT_CERTINFO_THISUPDATE,
2385 &thisUpdate, &length ) );
2386 status = cryptGetAttributeString( certificate, CRYPT_CERTINFO_NEXTUPDATE,
2387 &nextUpdate, &length );
2388 if( cryptStatusOK( status ) )
2389 {
2390 fprintf( outputStream, "CRL time %s,\n next update %s.\n",
2391 getTimeString( thisUpdate, 0 ),
2392 getTimeString( nextUpdate, 1 ) );
2393 }
2394 else
2395 {
2396 fprintf( outputStream, "CRL time %s.\n",
2397 getTimeString( thisUpdate, 0 ) );
2398 }
2399 }
2400 #endif /* _WIN32_WCE */
2401 if( certType == CRYPT_CERTTYPE_CRL || \
2402 certType == CRYPT_CERTTYPE_RTCS_RESPONSE || \
2403 certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
2404 {
2405 int noEntries = 0;
2406
2407 /* Count and display the entries */
2408 if( cryptSetAttribute( certificate, CRYPT_CERTINFO_CURRENT_CERTIFICATE,
2409 CRYPT_CURSOR_FIRST ) == CRYPT_OK )
2410 {
2411 fputs( "Revocation/validity list information:\n", outputStream );
2412 do
2413 {
2414 #ifndef _WIN32_WCE
2415 time_t timeStamp = 0;
2416 #endif /* _WIN32_WCE */
2417 int revStatus, certStatus;
2418
2419 noEntries++;
2420
2421 /* Extract response-specific status information */
2422 if( certType == CRYPT_CERTTYPE_RTCS_RESPONSE )
2423 {
2424 CHK( cryptGetAttribute( certificate,
2425 CRYPT_CERTINFO_CERTSTATUS, &certStatus ) );
2426 }
2427 if( certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
2428 {
2429 CHK( cryptGetAttribute( certificate,
2430 CRYPT_CERTINFO_REVOCATIONSTATUS, &revStatus ) );
2431 }
2432 #ifndef _WIN32_WCE
2433 if( certType == CRYPT_CERTTYPE_CRL || \
2434 ( certType == CRYPT_CERTTYPE_OCSP_RESPONSE && \
2435 revStatus == CRYPT_OCSPSTATUS_REVOKED ) || \
2436 ( certType == CRYPT_CERTTYPE_RTCS_RESPONSE && \
2437 certStatus == CRYPT_CERTSTATUS_NOTVALID ) )
2438 {
2439 CHK( cryptGetAttributeString( certificate,
2440 CRYPT_CERTINFO_REVOCATIONDATE, &timeStamp,
2441 &length ) );
2442 }
2443 #endif /* _WIN32_WCE */
2444
2445 /* Make sure we don't print excessive amounts of
2446 information */
2447 if( noEntries >= 20 )
2448 {
2449 if( noEntries == 20 )
2450 {
2451 fputs( " (Further entries exist, but won't be "
2452 "printed).\n", outputStream );
2453 }
2454 continue;
2455 }
2456
2457 /* Print details status info */
2458 switch( certType )
2459 {
2460 case CRYPT_CERTTYPE_RTCS_RESPONSE:
2461 fprintf( outputStream, " Certificate status = %d (%s).\n",
2462 certStatus,
2463 ( certStatus == CRYPT_CERTSTATUS_VALID ) ? \
2464 "valid" : \
2465 ( certStatus == CRYPT_CERTSTATUS_NOTVALID ) ? \
2466 "not valid" : \
2467 ( certStatus == CRYPT_CERTSTATUS_NONAUTHORITATIVE ) ? \
2468 "only non-authoritative response available" : \
2469 "unknown" );
2470 break;
2471
2472 case CRYPT_CERTTYPE_OCSP_RESPONSE:
2473 fprintf( outputStream, " Entry %d, rev.status = %d "
2474 "(%s), rev.time %s.\n", noEntries, revStatus,
2475 ( revStatus == CRYPT_OCSPSTATUS_NOTREVOKED ) ? \
2476 "not revoked" : \
2477 ( revStatus == CRYPT_OCSPSTATUS_REVOKED ) ? \
2478 "revoked" : "unknown",
2479 getTimeString( timeStamp, 0 ) );
2480 break;
2481
2482 case CRYPT_CERTTYPE_CRL:
2483 fprintf( outputStream, " Entry %d, revocation "
2484 "time %s.\n", noEntries,
2485 getTimeString( timeStamp, 0 ) );
2486 break;
2487
2488 default:
2489 assert( 0 );
2490 }
2491 }
2492 while( cryptSetAttribute( certificate,
2493 CRYPT_CERTINFO_CURRENT_CERTIFICATE,
2494 CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2495 }
2496 fprintf( outputStream, "Revocation/validity list has %d entr%s.\n",
2497 noEntries, ( noEntries == 1 ) ? "y" : "ies" );
2498 }
2499
2500 /* Display the self-signed status and fingerprint */
2501 if( cryptStatusOK( cryptGetAttribute( certificate,
2502 CRYPT_CERTINFO_SELFSIGNED, &value ) ) )
2503 {
2504 fprintf( outputStream, "Certificate object is %sself-signed.\n",
2505 value ? "" : "not " );
2506 }
2507 if( certType == CRYPT_CERTTYPE_CERTIFICATE || \
2508 certType == CRYPT_CERTTYPE_CERTCHAIN )
2509 {
2510 CHK( cryptGetAttributeString( certificate,
2511 CRYPT_CERTINFO_FINGERPRINT_SHA1,
2512 buffer, &length ) );
2513 fprintf( outputStream, "Certificate fingerprint =\n" );
2514 printHex( " ", buffer, length );
2515 }
2516
2517 /* List the attribute types */
2518 if( !displayAttributes( certificate ) )
2519 return( FALSE );
2520
2521 /* Display common attributes */
2522 if( cryptStatusError( \
2523 cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2524 CRYPT_CURSOR_FIRST ) ) )
2525 {
2526 fputs( " (No extensions/attributes).\n", outputStream );
2527 return( TRUE );
2528 }
2529 fputs( "Some of the common extensions/attributes are:\n", outputStream );
2530 if( certType == CRYPT_CERTTYPE_CRL )
2531 {
2532 time_t theTime;
2533
2534 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2535 CRYPT_CURSOR_FIRST ) );
2536 status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CRLNUMBER,
2537 &value );
2538 if( cryptStatusOK( status ) && value )
2539 fprintf( outputStream, " crlNumber = %d.\n", value );
2540 status = cryptGetAttribute( certificate, CRYPT_CERTINFO_DELTACRLINDICATOR,
2541 &value );
2542 if( cryptStatusOK( status ) && value )
2543 fprintf( outputStream, " deltaCRLIndicator = %d.\n", value );
2544 status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CRLREASON,
2545 &value );
2546 if( cryptStatusOK( status ) && value )
2547 fprintf( outputStream, " crlReason = %d.\n", value );
2548 status = cryptGetAttributeString( certificate,
2549 CRYPT_CERTINFO_INVALIDITYDATE, &theTime, &length );
2550 #ifndef _WIN32_WCE
2551 if( cryptStatusOK( status ) )
2552 {
2553 fprintf( outputStream, " invalidityDate = %s.\n",
2554 getTimeString( theTime, 0 ) );
2555 }
2556 #endif /* _WIN32_WCE */
2557 if( cryptStatusOK( \
2558 cryptGetAttribute( certificate,
2559 CRYPT_CERTINFO_ISSUINGDIST_FULLNAME, &value ) ) )
2560 {
2561 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2562 CRYPT_CERTINFO_ISSUINGDIST_FULLNAME ) );
2563 fputs( " issuingDistributionPoint is:\n", outputStream );
2564 printDN( certificate );
2565 printAltName( certificate );
2566 }
2567 return( TRUE );
2568 }
2569 #ifndef _WIN32_WCE
2570 if( certType == CRYPT_CERTTYPE_CMS_ATTRIBUTES )
2571 {
2572 time_t signingTime;
2573
2574 status = cryptGetAttributeString( certificate,
2575 CRYPT_CERTINFO_CMS_SIGNINGTIME,
2576 &signingTime, &length );
2577 if( cryptStatusOK( status ) )
2578 {
2579 fprintf( outputStream, "Signing time %s.\n",
2580 getTimeString( signingTime, 0 ) );
2581 }
2582 return( TRUE );
2583 }
2584 #endif /* _WIN32_WCE */
2585 if( certType == CRYPT_CERTTYPE_PKIUSER )
2586 {
2587 CHK( cryptGetAttributeString( certificate, CRYPT_CERTINFO_PKIUSER_ID,
2588 buffer, &length ) );
2589 buffer[ length ] ='\0';
2590 fprintf( outputStream, " PKI user ID = %s.\n", buffer );
2591 CHK( cryptGetAttributeString( certificate,
2592 CRYPT_CERTINFO_PKIUSER_ISSUEPASSWORD,
2593 buffer, &length ) );
2594 buffer[ length ] ='\0';
2595 fprintf( outputStream, " PKI user issue password = %s.\n",
2596 buffer );
2597 CHK( cryptGetAttributeString( certificate,
2598 CRYPT_CERTINFO_PKIUSER_REVPASSWORD,
2599 buffer, &length ) );
2600 buffer[ length ] ='\0';
2601 fprintf( outputStream, " PKI user revocation password = %s.\n",
2602 buffer );
2603 return( TRUE );
2604 }
2605 status = cryptGetAttribute( certificate,
2606 CRYPT_CERTINFO_KEYUSAGE, &value );
2607 if( cryptStatusOK( status ) && value )
2608 {
2609 static const struct { int flag; const char *name; } usageNames[] = {
2610 { CRYPT_KEYUSAGE_DIGITALSIGNATURE, "digSig" },
2611 { CRYPT_KEYUSAGE_NONREPUDIATION, "nonRep" },
2612 { CRYPT_KEYUSAGE_KEYENCIPHERMENT, "keyEnc" },
2613 { CRYPT_KEYUSAGE_DATAENCIPHERMENT, "dataEnc" },
2614 { CRYPT_KEYUSAGE_KEYAGREEMENT, "keyAgree" },
2615 { CRYPT_KEYUSAGE_KEYCERTSIGN, "certSign" },
2616 { CRYPT_KEYUSAGE_CRLSIGN, "crlSign" },
2617 { CRYPT_KEYUSAGE_ENCIPHERONLY, "encOnly" },
2618 { CRYPT_KEYUSAGE_DECIPHERONLY, "decOnly" },
2619 { CRYPT_KEYUSAGE_NONE, NULL }
2620 };
2621 BOOLEAN printedUsage = FALSE;
2622 int i;
2623
2624 fprintf( outputStream, " keyUsage = %02X (", value );
2625 for( i = 0; usageNames[ i ].flag != CRYPT_KEYUSAGE_NONE; i++ )
2626 {
2627 if( usageNames[ i ].flag & value )
2628 {
2629 if( printedUsage )
2630 fprintf( outputStream, ", " );
2631 fprintf( outputStream, "%s", usageNames[ i ].name );
2632 printedUsage = TRUE;
2633 }
2634 }
2635 fputs( ").\n", outputStream );
2636 }
2637 status = cryptGetAttribute( certificate,
2638 CRYPT_CERTINFO_EXTKEYUSAGE, &value );
2639 if( cryptStatusOK( status ) && value )
2640 {
2641 BOOLEAN firstTime = TRUE;
2642
2643 fprintf( outputStream, " extKeyUsage types = " );
2644 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT_GROUP,
2645 CRYPT_CERTINFO_EXTKEYUSAGE ) );
2646 do
2647 {
2648 CHK( cryptGetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2649 &value ) );
2650 fprintf( outputStream, "%s%d", firstTime ? "" : ", ", value );
2651 firstTime = FALSE;
2652 }
2653 while( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2654 CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2655 fprintf( outputStream, ".\n" );
2656 }
2657 status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CA, &value );
2658 if( cryptStatusOK( status ) && value )
2659 {
2660 fprintf( outputStream, " basicConstraints.cA = %s.\n",
2661 value ? "True" : "False" );
2662 }
2663 status = cryptGetAttribute( certificate, CRYPT_CERTINFO_PATHLENCONSTRAINT,
2664 &value );
2665 if( cryptStatusOK( status ) && value )
2666 {
2667 fprintf( outputStream, " basicConstraints.pathLenConstraint = %d.\n",
2668 value );
2669 }
2670 status = cryptGetAttributeString( certificate,
2671 CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, buffer, &length );
2672 if( cryptStatusOK( status ) )
2673 {
2674 fprintf( outputStream, " subjectKeyIdentifier =\n" );
2675 printHex( " ", buffer, length );
2676 }
2677 status = cryptGetAttributeString( certificate,
2678 CRYPT_CERTINFO_AUTHORITY_KEYIDENTIFIER, buffer, &length );
2679 if( cryptStatusOK( status ) )
2680 {
2681 fprintf( outputStream, " authorityKeyIdentifier =\n" );
2682 printHex( " ", buffer, length );
2683 }
2684 status = cryptGetAttributeString( certificate,
2685 CRYPT_CERTINFO_CERTPOLICYID, buffer, &length );
2686 if( cryptStatusOK( status ) )
2687 {
2688 buffer[ length ] = '\0';
2689 fprintf( outputStream,
2690 " certificatePolicies.policyInformation.policyIdentifier = %s.\n",
2691 buffer );
2692 status = cryptGetAttributeString( certificate,
2693 CRYPT_CERTINFO_CERTPOLICY_CPSURI, buffer, &length );
2694 if( cryptStatusOK( status ) )
2695 {
2696 buffer[ length ] = '\0';
2697 fprintf( outputStream,
2698 " certificatePolicies.policyInformation.cpsURI = %s.\n",
2699 buffer );
2700 }
2701 status = cryptGetAttributeString( certificate,
2702 CRYPT_CERTINFO_CERTPOLICY_ORGANIZATION, buffer, &length );
2703 if( cryptStatusOK( status ) )
2704 {
2705 buffer[ length ] = '\0';
2706 fprintf( outputStream,
2707 " certificatePolicies.policyInformation.organisation = %s.\n",
2708 buffer );
2709 }
2710 status = cryptGetAttributeString( certificate,
2711 CRYPT_CERTINFO_CERTPOLICY_EXPLICITTEXT, buffer, &length );
2712 if( cryptStatusOK( status ) )
2713 {
2714 buffer[ length ] = '\0';
2715 fprintf( outputStream,
2716 " certificatePolicies.policyInformation.explicitText = %s.\n",
2717 buffer );
2718 }
2719 }
2720 if( cryptStatusOK( \
2721 cryptGetAttribute( certificate,
2722 CRYPT_CERTINFO_CRLDIST_FULLNAME, &value ) ) )
2723 {
2724 CHK( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
2725 CRYPT_CERTINFO_CRLDIST_FULLNAME ) );
2726 fputs( " crlDistributionPoint is/are:\n", outputStream );
2727 do
2728 {
2729 printDN( certificate );
2730 printAltName( certificate );
2731 }
2732 while( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT_INSTANCE,
2733 CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2734 }
2735
2736 return( TRUE );
2737 }
2738
printCertChainInfo(const CRYPT_CERTIFICATE certChain)2739 int printCertChainInfo( const CRYPT_CERTIFICATE certChain )
2740 {
2741 int value, count, status;
2742
2743 /* Make sure it really is a certificate chain */
2744 CHK( cryptGetAttribute( certChain, CRYPT_CERTINFO_CERTTYPE, &value ) );
2745 if( value != CRYPT_CERTTYPE_CERTCHAIN )
2746 {
2747 printCertInfo( certChain );
2748 return( TRUE );
2749 }
2750
2751 /* Display info on each certificate in the chain. This uses the cursor
2752 mechanism to select successive certificates in the chain from the
2753 leaf up to the root */
2754 count = 0;
2755 CHK( cryptSetAttribute( certChain, CRYPT_CERTINFO_CURRENT_CERTIFICATE,
2756 CRYPT_CURSOR_FIRST ) );
2757 do
2758 {
2759 fprintf( outputStream, "Certificate %d\n-------------\n", count++ );
2760 printCertInfo( certChain );
2761 fprintf( outputStream, "\n" );
2762 }
2763 while( cryptSetAttribute( certChain,
2764 CRYPT_CERTINFO_CURRENT_CERTIFICATE, CRYPT_CURSOR_NEXT ) == CRYPT_OK );
2765
2766 /* Reset the cursor position in the chain */
2767 CHK( cryptSetAttribute( certChain, CRYPT_CERTINFO_CURRENT_CERTIFICATE,
2768 CRYPT_CURSOR_FIRST ) );
2769
2770 return( TRUE );
2771 }
2772