cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
utils.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Self-test Utility Routines *
4 * Copyright Peter Gutmann 1997-2009 *
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 
20 #ifdef HAS_WIDECHAR
21  #include <wchar.h>
22 #endif /* HAS_WIDECHAR */
23 
24 /* The keys used with the test code have associated certs that expire at
25  some point. The following value defines the number of days before the
26  expiry at which we start printing warnings */
27 
28 #if defined( _MSC_VER ) && ( _MSC_VER == 1200 ) && !defined( NDEBUG )
29  #define EXPIRY_WARN_DAYS 90
30 #else
31  #define EXPIRY_WARN_DAYS 30
32 #endif /* VC 6 debug/development, give some advance warning */
33 
34 /****************************************************************************
35 * *
36 * Utility Functions *
37 * *
38 ****************************************************************************/
39 
40 #ifndef _WIN32_WCE
41 
42 /* Since ctime() adds a '\n' to the string and may return NULL, we wrap it
43  in something that behaves as required */
44 
45 static char *getTimeString( const time_t theTime, const int bufNo )
46  {
47  static char timeString[ 2 ][ 64 ], *timeStringPtr;
48 
49  assert( bufNo == 0 || bufNo == 1 );
50 
51  timeStringPtr = ctime( &theTime );
52  if( timeStringPtr == NULL )
53  return( "(Not available)" );
54  strcpy( timeString[ bufNo ], timeStringPtr );
55  timeString[ bufNo ][ strlen( timeStringPtr ) - 1 ] = '\0'; /* Stomp '\n' */
56 
57  return( timeString[ bufNo ] );
58  }
59 #else
60  #define getTimeString( theTime, bufNo ) "(No time data available)"
61 #endif /* _WIN32_WCE */
62 
63 #if defined( _WIN32_WCE ) && _WIN32_WCE < 500
64 
65 int remove( const char *pathname )
66  {
67  wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
68 
69  mbstowcs( wcBuffer, pathname, strlen( pathname ) + 1 );
70  DeleteFile( wcBuffer );
71 
72  return( 0 );
73  }
74 #endif /* WinCE < 5.x */
75 
76 /****************************************************************************
77 * *
78 * General Checking Functions *
79 * *
80 ****************************************************************************/
81 
82 /* Check that a file is accessible. This is a generic sanity check to make
83  sure that access to keyset files is functioning */
84 
85 int checkFileAccess( void )
86  {
87  CRYPT_KEYSET cryptKeyset;
88  FILE *filePtr;
90  int length, status;
91 
92  /* First, check that the file actually exists so that we can return an
93  appropriate error message */
94  if( ( filePtr = fopen( convertFileName( CA_PRIVKEY_FILE ),
95  "rb" ) ) == NULL )
96  {
97  printf( "Couldn't access cryptlib keyset file %s. Please make "
98  "sure\nthat all the cryptlib files have been installed "
99  "correctly, and the cryptlib\nself-test is being run from "
100  "the correct directory.\n", CA_PRIVKEY_FILE );
101  return( FALSE );
102  }
103  fclose( filePtr );
104 
105  /* Now read the test files and see if there's any problem due to data
106  conversion evident */
108  if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
109  {
110  puts( "Couldn't open binary data test file to check for data "
111  "conversion problems." );
112  return( FALSE );
113  }
114  length = fread( buffer, 1, BUFFER_SIZE, filePtr );
115  fclose( filePtr );
116  if( length != 16 || \
117  memcmp( buffer, "\x30\x82\x02\x56\x30\x82\x02\x52\r\n\x08\x40\n" "tqz", 16 ) )
118  {
119  puts( "Binary data is corrupt, probably due to being unzipped or "
120  "copied onto the\nsystem in a mode that tries to translate "
121  "text data during processing/copying." );
122  return( FALSE );
123  }
124 #ifdef __UNIX__
126  if( ( filePtr = fopen( buffer, "rb" ) ) == NULL )
127  {
128  puts( "Couldn't open text data test file to check for data "
129  "conversion problems." );
130  return( FALSE );
131  }
132  length = fread( buffer, 1, BUFFER_SIZE, filePtr );
133  fclose( filePtr );
134  if( length != 10 || memcmp( buffer, "test\ntest\n" , 10 ) )
135  {
136  puts( "Text data is still in CRLF-delimited format, probably due "
137  "to being unzipped\nwithout the '-a' option to translate "
138  "text files for Unix systems." );
139  return( FALSE );
140  }
141 #endif /* __UNIX__ */
142 
143  /* The file exists and is accessible and was copied/installed correctly,
144  now try and open it using the cryptlib file access functions */
145  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
147  if( cryptStatusError( status ) )
148  {
149  /* If file keyset access isn't available, the inability to access
150  the keyset isn't an error */
151  if( status == CRYPT_ERROR_NOTAVAIL )
152  return( TRUE );
153 
154  printf( "Couldn't access cryptlib keyset file %s even though the "
155  "file\nexists and is readable. Please make sure that the "
156  "cryptlib self-test is\nbeing run from the correct "
157  "directory.\n", CA_PRIVKEY_FILE );
158  return( FALSE );
159  }
160  cryptKeysetClose( cryptKeyset );
161 
162  return( TRUE );
163  }
164 
165 /* Check that external network sites are accessible, used to detect
166  potential problems with machines stuck behind firewalls */
167 
169  {
170  CRYPT_KEYSET cryptKeyset;
171  int status;
172 
173  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_HTTP,
174  TEXT( "www.amazon.com" ), CRYPT_KEYOPT_READONLY );
175  if( cryptStatusError( status ) )
176  return( FALSE );
177  cryptKeysetClose( cryptKeyset );
178 
179  return( TRUE );
180  }
181 
182 /****************************************************************************
183 * *
184 * Import/Export Functions *
185 * *
186 ****************************************************************************/
187 
188 /* Import a certificate object */
189 
191  {
192  FILE *filePtr;
194  int count;
195 
196  if( ( filePtr = fopen( convertFileName( fileName ), "rb" ) ) == NULL )
197  return( CRYPT_ERROR_OPEN );
198  count = fread( buffer, 1, BUFFER_SIZE, filePtr );
199  fclose( filePtr );
200  if( count == BUFFER_SIZE ) /* Item too large for buffer */
201  return( CRYPT_ERROR_OVERFLOW );
202 
203  /* Import the certificate */
204  return( cryptImportCert( buffer, count, CRYPT_UNUSED, cryptCert ) );
205  }
206 
208  const C_STR fileTemplate, const int number )
209  {
210  BYTE filenameBuffer[ FILENAME_BUFFER_SIZE ];
211 #ifdef UNICODE_STRINGS
212  wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
213 #endif /* UNICODE_STRINGS */
214 
215  filenameFromTemplate( filenameBuffer, fileTemplate, number );
216 #ifdef UNICODE_STRINGS
217  mbstowcs( wcBuffer, filenameBuffer, strlen( filenameBuffer ) + 1 );
218  return( importCertFile( cryptCert, wcBuffer ) );
219 #else
220  return( importCertFile( cryptCert, filenameBuffer ) );
221 #endif /* UNICODE_STRINGS */
222  }
223 
224 /* Read a key from a key file */
225 
227  const C_STR keyName )
228  {
229  CRYPT_KEYSET cryptKeyset;
230  int status;
231 
232  /* Read the key from the keyset */
233  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
234  keysetName, CRYPT_KEYOPT_READONLY );
235  if( cryptStatusError( status ) )
236  return( status );
237  status = cryptGetPublicKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
238  keyName );
239  if( cryptStatusError( status ) )
240  printExtError( cryptKeyset, "cryptGetPublicKey", status, __LINE__ );
241  cryptKeysetClose( cryptKeyset );
242  return( status );
243  }
244 
246  const C_STR keyName, const C_STR password )
247  {
248  CRYPT_KEYSET cryptKeyset;
249  time_t validFrom, validTo;
250  int dummy, status;
251 
252  /* Read the key from the keyset */
253  status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE,
254  keysetName, CRYPT_KEYOPT_READONLY );
255  if( cryptStatusError( status ) )
256  return( status );
257  status = cryptGetPrivateKey( cryptKeyset, cryptContext, CRYPT_KEYID_NAME,
258  keyName, password );
259  if( cryptStatusError( status ) )
260  printExtError( cryptKeyset, "cryptGetPrivateKey", status, __LINE__ );
261  cryptKeysetClose( cryptKeyset );
262  if( cryptStatusError( status ) )
263  return( status );
264 
265  /* If the key has a certificate attached, make sure it's still valid
266  before we hand it back to the self-test functions that will report
267  the problem as being with the self-test rather than with the
268  certificate. We check not just the expiry date but also the expiry
269  interval to make sure that we don't get false positives on short-
270  validity certificates */
271  status = cryptGetAttributeString( *cryptContext,
272  CRYPT_CERTINFO_VALIDFROM, &validFrom, &dummy );
273  if( cryptStatusError( status ) )
274  {
275  /* There's no certificate there, this isn't an error */
276  return( CRYPT_OK );
277  }
278  cryptGetAttributeString( *cryptContext,
279  CRYPT_CERTINFO_VALIDTO, &validTo, &dummy );
280 #ifndef _WIN32_WCE
281  if( ( validTo - validFrom > ( 86400 * EXPIRY_WARN_DAYS ) ) && \
282  validTo - time( NULL ) <= ( 86400 * EXPIRY_WARN_DAYS ) )
283  {
284  const time_t currentTime = time( NULL );
285 
286  puts( " ********************" );
287  if( validTo <= currentTime )
288  {
289  puts( "Warning: This key has expired. Certificate-related "
290  "operations will fail or\n result in error "
291  "messages from the test code." );
292  }
293  else
294  {
295  if( validTo - currentTime <= 86400 )
296  {
297  puts( "Warning: This key expires today. Certificate-"
298  "related operations may fail\n or result in "
299  "error messages from the test code." );
300  }
301  else
302  {
303  printf( "Warning: This key will expire in %ld days. "
304  "Certificate-related operations\n may fail "
305  "or result in error messages from the test code.\n",
306  ( validTo - currentTime ) / 86400 );
307  }
308  }
309  puts( " ********************" );
310  printf( "Hit a key..." );
311  getchar();
312  putchar( '\r' );
313  }
314 #endif /* _WIN32_WCE */
315  return( CRYPT_OK );
316  }
317 
318 /****************************************************************************
319 * *
320 * Key File Access Routines *
321 * *
322 ****************************************************************************/
323 
324 /* Key file and password-handling access routines */
325 
327  const BOOLEAN isPrivKey )
328  {
329  switch( type )
330  {
331  case KEYFILE_X509:
332  return( USER_PRIVKEY_FILE );
333  case KEYFILE_PGP:
334  case KEYFILE_PGP_SPECIAL:
335  return( isPrivKey ? PGP_PRIVKEY_FILE : PGP_PUBKEY_FILE );
337  return( isPrivKey ? OPENPGP_PRIVKEY_HASH_FILE : OPENPGP_PUBKEY_HASH_FILE );
338  case KEYFILE_OPENPGP_AES:
339  return( isPrivKey ? OPENPGP_PRIVKEY_AES_FILE : OPENPGP_PUBKEY_AES_FILE );
340  case KEYFILE_OPENPGP_RSA:
341  return( isPrivKey ? OPENPGP_PRIVKEY_RSA_FILE : OPENPGP_PUBKEY_RSA_FILE );
343  return( OPENPGP_PRIVKEY_PART_FILE );
344  case KEYFILE_NAIPGP:
345  return( isPrivKey ? NAIPGP_PRIVKEY_FILE : NAIPGP_PUBKEY_FILE );
346  }
347  assert( 0 );
348  return( TEXT( "notfound" ) );
349  }
350 
352  {
353  switch( type )
354  {
355  case KEYFILE_X509:
356  return( TEST_PRIVKEY_PASSWORD );
357  case KEYFILE_PGP:
359  case KEYFILE_OPENPGP_RSA:
360  return( TEXT( "test1" ) );
361  case KEYFILE_NAIPGP:
362  return( TEXT( "test10" ) );
363  case KEYFILE_OPENPGP_AES:
364  return( TEXT( "testkey" ) );
366  return( TEXT( "def" ) );
367  }
368  assert( 0 );
369  return( TEXT( "notfound" ) );
370  }
371 
373  const BOOLEAN isPrivKey )
374  {
375  /* If possible we specify user IDs for keys in the middle of the keyring
376  to make sure that we test the ability to correctly handle multiple
377  keys */
378  switch( type )
379  {
380  case KEYFILE_X509:
381  return( USER_PRIVKEY_LABEL );
382  case KEYFILE_PGP:
383  return( TEXT( "test" ) );
384  case KEYFILE_PGP_SPECIAL:
385  return( TEXT( "suzuki" ) );
386  case KEYFILE_NAIPGP:
387  return( isPrivKey ? TEXT( "test" ) : TEXT( "test cryptlib" ) );
389  case KEYFILE_OPENPGP_RSA:
390  return( TEXT( "test1" ) );
391  case KEYFILE_OPENPGP_AES:
392  return( TEXT( "Max Mustermann" ) );
393  }
394  assert( 0 );
395  return( TEXT( "notfound" ) );
396  }
397 
398 /****************************************************************************
399 * *
400 * OS Helper Functions *
401 * *
402 ****************************************************************************/
403 
404 #if defined( __BORLANDC__ ) && ( __BORLANDC__ <= 0x310 )
405 
406 /* BC++ 3.x doesn't have mbstowcs() in the default library, and also defines
407  wchar_t as char (!!) so we fake it here */
408 
409 size_t mbstowcs( char *pwcs, const char *s, size_t n )
410  {
411  memcpy( pwcs, s, n );
412  return( n );
413  }
414 #endif /* BC++ 3.1 or lower */
415 
416 /* When using multiple threads we need to delay one thread for a small
417  amount of time, unfortunately there's no easy way to do this with pthreads
418  so we have to provide the following wrapper function that makes an
419  (implementation-specific) attempt at it */
420 
421 #if defined( UNIX_THREADS ) || defined( WINDOWS_THREADS ) || defined( OS2_THREADS )
422 
423 #if defined( UNIX_THREADS )
424  /* This include must be outside the function to avoid weird compiler errors
425  on some systems */
426  #include <sys/time.h>
427 #endif /* UNIX_THREADS */
428 
429 void delayThread( const int seconds )
430  {
431 #if defined( UNIX_THREADS )
432  struct timeval tv = { 0 };
433 
434  /* The following should put a thread to sleep for a second on most
435  systems since the select() should be a thread-safe one in the
436  presence of pthreads */
437  tv.tv_sec = seconds;
438  select( 1, NULL, NULL, NULL, &tv );
439 #elif defined( WINDOWS_THREADS )
440  Sleep( seconds * 1000 );
441 #endif /* Threading system-specific delay functions */
442  }
443 #endif /* Systems with threading support */
444 
445 /* Helper functions to make tracking down errors on systems with no console
446  a bit less painful. These just use the debug console as stdout */
447 
448 #ifdef _WIN32_WCE
449 
450 void wcPrintf( const char *format, ... )
451  {
452  wchar_t wcBuffer[ 1024 ];
453  char buffer[ 1024 ];
454  va_list argPtr;
455 
456  va_start( argPtr, format );
457  vsprintf( buffer, format, argPtr );
458  va_end( argPtr );
459  mbstowcs( wcBuffer, buffer, strlen( buffer ) + 1 );
460  NKDbgPrintfW( wcBuffer );
461  }
462 
463 void wcPuts( const char *string )
464  {
465  wcPrintf( "%s\n", string );
466  }
467 #endif /* Console-less environments */
468 
469 /* Conversion functions used to get Unicode input into generic ASCII
470  output */
471 
472 #ifdef UNICODE_STRINGS
473 
474 /* Get a filename in an appropriate format for the C runtime library */
475 
476 const char *convertFileName( const C_STR fileName )
477  {
478  static char fileNameBuffer[ FILENAME_BUFFER_SIZE ];
479 
480  wcstombs( fileNameBuffer, fileName, wcslen( fileName ) + 1 );
481  return( fileNameBuffer );
482  }
483 
484 /* Map a filename template to an actual filename, input in Unicode, output in
485  ASCII */
486 
487 void filenameFromTemplate( char *buffer, const wchar_t *fileTemplate,
488  const int count )
489  {
490  wchar_t wcBuffer[ FILENAME_BUFFER_SIZE ];
491  int length;
492 
493  length = _snwprintf( wcBuffer, FILENAME_BUFFER_SIZE, fileTemplate,
494  count );
495  wcstombs( buffer, wcBuffer, length + 1 );
496  }
497 
498 void filenameParamFromTemplate( wchar_t *buffer,
499  const wchar_t *fileTemplate,
500  const int count )
501  {
502  _snwprintf( buffer, FILENAME_BUFFER_SIZE, fileTemplate, count );
503  }
504 #endif /* UNICODE_STRINGS */
505 
506 /****************************************************************************
507 * *
508 * Thread Support Functions *
509 * *
510 ****************************************************************************/
511 
512 #if defined( WINDOWS_THREADS )
513 
514 static HANDLE hMutex;
515 
516 void createMutex( void )
517  {
518  hMutex = CreateMutex( NULL, FALSE, NULL );
519  }
520 void acquireMutex( void )
521  {
522  if( WaitForSingleObject( hMutex, 30000 ) == WAIT_TIMEOUT )
523  {
524  puts( "Warning: Couldn't acquire mutex after 30s wait. Press a "
525  "key to continue." );
526  getchar();
527  }
528  }
529 int waitMutex( void )
530  {
531  if( WaitForSingleObject( hMutex, 30000 ) == WAIT_TIMEOUT )
532  return( CRYPT_ERROR_TIMEOUT );
533 
534  /* Since this is merely a synchronisation operation in which a later
535  thread waits to catch up to an earlier one, we release the mutex again
536  so other threads can get in */
537  releaseMutex();
538  return( CRYPT_OK );
539  }
540 void releaseMutex( void )
541  {
542  if( !ReleaseMutex( hMutex ) )
543  {
544  puts( "Warning: Couldn't release mutex. Press a key to continue." );
545  getchar();
546  }
547  }
548 void destroyMutex( void )
549  {
550  CloseHandle( hMutex );
551  }
552 
553 void waitForThread( const HANDLE hThread )
554  {
555  if( WaitForSingleObject( hThread, 15000 ) == WAIT_TIMEOUT )
556  {
557  puts( "Warning: Server thread is still active due to session "
558  "negotiation failure,\n this will cause an error "
559  "condition when cryptEnd() is called due\n to "
560  "resources remaining allocated. Press a key to continue." );
561  getchar();
562  }
563  CloseHandle( hThread );
564  }
565 #elif defined( UNIX_THREADS )
566 
567 static pthread_mutex_t mutex;
568 
569 void createMutex( void )
570  {
571  pthread_mutex_init( &mutex, NULL );
572  }
573 void acquireMutex( void )
574  {
575  pthread_mutex_lock( &mutex );
576  }
577 int waitMutex( void )
578  {
579  pthread_mutex_lock( &mutex );
580 
581  /* Since this is merely a synchronisation operation in which a later
582  thread waits to catch up to an earlier one, we release the mutex again
583  so other threads can get in */
584  releaseMutex();
585  return( CRYPT_OK );
586  }
587 void releaseMutex( void )
588  {
589  pthread_mutex_unlock( &mutex );
590  }
591 void destroyMutex( void )
592  {
593  pthread_mutex_destroy( &mutex );
594  }
595 
596 void waitForThread( const pthread_t hThread )
597  {
598  if( pthread_join( hThread, NULL ) < 0 )
599  {
600  puts( "Warning: Server thread is still active due to session "
601  "negotiation failure,\n this will cause an error "
602  "condition when cryptEnd() is called due\n to "
603  "resources remaining allocated. Press a key to continue." );
604  getchar();
605  }
606  }
607 
608 #else
609 
610 void createMutex( void )
611  {
612  }
613 void acquireMutex( void )
614  {
615  }
616 void releaseMutex( void )
617  {
618  }
619 int waitMutex( void )
620  {
621  return( CRYPT_OK );
622  }
623 void destroyMutex( void )
624  {
625  }
626 #endif /* WINDOWS_THREADS */
627 
628 #if defined( WINDOWS_THREADS ) || defined( UNIX_THREADS )
629 
630 /* Dispatch multiple client and server threads and wait for them to exit */
631 
632 int multiThreadDispatch( THREAD_FUNC clientFunction,
633  THREAD_FUNC serverFunction, const int noThreads )
634  {
635  THREAD_HANDLE hClientThreads[ MAX_NO_THREADS ];
636  THREAD_HANDLE hServerThreads[ MAX_NO_THREADS ];
637  int sessionID[ MAX_NO_THREADS ];
638  int i;
639 
640  assert( noThreads <= MAX_NO_THREADS );
641 
642  /* Set up the session ID values */
643  for( i = 0; i < MAX_NO_THREADS; i++ )
644  sessionID[ i ] = i;
645 
646  /* Start the sessions and wait for them initialise. We have to wait for
647  some time since the multiple private key reads can take awhile */
648  for( i = 0; i < noThreads; i++ )
649  {
650 #ifdef WINDOWS_THREADS
651  unsigned int threadID;
652 
653  hServerThreads[ i ] = ( HANDLE ) \
654  _beginthreadex( NULL, 0, serverFunction,
655  &sessionID[ i ], 0, &threadID );
656 #else
657  pthread_t threadHandle;
658 
659  hServerThreads[ i ] = 0;
660  if( pthread_create( &threadHandle, NULL, serverFunction,
661  &sessionID[ i ] ) == 0 )
662  hServerThreads[ i ] = threadHandle;
663 #endif /* Windows vs. pthreads */
664  }
665  delayThread( 3 );
666 
667  /* Connect to the local server */
668  for( i = 0; i < noThreads; i++ )
669  {
670 #ifdef WINDOWS_THREADS
671  unsigned int threadID;
672 
673  hClientThreads[ i ] = ( HANDLE ) \
674  _beginthreadex( NULL, 0, clientFunction,
675  &sessionID[ i ], 0, &threadID );
676 #else
677  pthread_t threadHandle;
678 
679  hServerThreads[ i ] = 0;
680  if( pthread_create( &threadHandle, NULL, clientFunction,
681  &sessionID[ i ] ) == 0 )
682  hClientThreads[ i ] = threadHandle;
683 #endif /* Windows vs. pthreads */
684  }
685 #ifdef WINDOWS_THREADS
686  if( WaitForMultipleObjects( noThreads, hServerThreads, TRUE,
687  60000 ) == WAIT_TIMEOUT || \
688  WaitForMultipleObjects( noThreads, hClientThreads, TRUE,
689  60000 ) == WAIT_TIMEOUT )
690 #else
691  /* Posix doesn't have an ability to wait for multiple threads for mostly
692  religious reasons ("That's not how we do things around here") so we
693  just wait for two token threads */
694  pthread_join( hServerThreads[ 0 ], NULL );
695  pthread_join( hClientThreads[ 0 ], NULL );
696 #endif /* Windows vs. pthreads */
697  {
698  puts( "Warning: Server threads are still active due to session "
699  "negotiation failure,\n this will cause an error "
700  "condition when cryptEnd() is called due\n to "
701  "resources remaining allocated. Press a key to continue." );
702  getchar();
703  }
704 #ifdef WINDOWS_THREADS
705  for( i = 0; i < noThreads; i++ )
706  if( hServerThreads[ i ] != 0 )
707  CloseHandle( hServerThreads[ i ] );
708  for( i = 0; i < noThreads; i++ )
709  if( hClientThreads[ i ] != 0 )
710  CloseHandle( hClientThreads[ i ] );
711 #endif /* Windows vs. pthreads */
712 
713  return( TRUE );
714  }
715 #endif /* Windows/Unix threads */
716 
717 /****************************************************************************
718 * *
719 * Error-handling Functions *
720 * *
721 ****************************************************************************/
722 
723 /* Print extended error attribute information */
724 
726  {
727  int errorType, errorLocus, status;
728 
729  status = cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_ERRORTYPE,
730  &errorType );
731  cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_ERRORLOCUS, &errorLocus );
732  if( cryptStatusOK( status ) && errorType != CRYPT_ERRTYPE_NONE )
733  {
734  printf( " Error info attributes report locus %d, type %d.\n",
735  errorLocus, errorType );
736  }
737  }
738 
739 /* Print extended object error information */
740 
742  const char *functionName, const int functionStatus,
743  const int lineNo )
744  {
745  char errorMessage[ 512 ];
746  int errorMessageLength, status;
747 
748  printf( "%s failed with error code %d, line %d.\n", functionName,
749  functionStatus, lineNo );
751  errorMessage, &errorMessageLength );
752  if( cryptStatusError( status ) )
753  {
754  puts( " No extended error information available." );
755  printErrorAttributeInfo( cryptHandle );
756  return;
757  }
758  errorMessage[ errorMessageLength ] = '\0';
759  printf( " Error message = %s'%s'.\n",
760  ( errorMessageLength > ( 80 - 21 ) ) ? "\n " : "",
761  errorMessage );
762  printErrorAttributeInfo( cryptHandle );
763  }
764 
765 /* Exit with an error message. attrErrorExit() prints the locus and type,
766  extErrorExit() prints the extended error code and message */
767 
769  const char *functionName, const int errorCode,
770  const int lineNumber )
771  {
772  printf( "%s failed with error code %d, line %d.\n", functionName,
773  errorCode, lineNumber );
774  printErrorAttributeInfo( cryptHandle );
775  return( FALSE );
776  }
777 
779  const char *functionName, const int errorCode,
780  const int lineNumber )
781  {
782  printExtError( cryptHandle, functionName, errorCode, lineNumber );
783  cryptDestroyObject( cryptHandle );
784  return( FALSE );
785  }
786 
787 /****************************************************************************
788 * *
789 * Misc. Functions *
790 * *
791 ****************************************************************************/
792 
793 /* Some algorithms can be disabled to eliminate patent problems or reduce the
794  size of the code. The following functions are used to select generally
795  equivalent alternatives if the required algorithm isn't available. These
796  selections make certain assumptions, namely that at least one of the
797  algorithms in the fallback chain is always available (which is guaranteed,
798  3DES is used internally), and that they have the same general properties
799  as the algorithms they're replacing, which is also usually the case,
800  with Blowfish being a first-instance substitute for IDEA, RC2, or RC5, and
801  then 3DES as the fallback if Blowfish isn't available */
802 
804  {
805  if( cryptStatusOK( cryptQueryCapability( algorithm, NULL ) ) )
806  return( algorithm );
808  return( CRYPT_ALGO_BLOWFISH );
809  return( CRYPT_ALGO_3DES );
810  }
811 
812 /* Add a collection of fields to a certificate */
813 
815  const CERT_DATA *certData, const int lineNo )
816  {
817  int i;
818 
819  for( i = 0; certData[ i ].type != CRYPT_ATTRIBUTE_NONE; i++ )
820  {
821  int status;
822 
823  switch( certData[ i ].componentType )
824  {
825  case IS_NUMERIC:
826  status = cryptSetAttribute( certificate,
827  certData[ i ].type, certData[ i ].numericValue );
828  if( cryptStatusError( status ) )
829  {
830  printf( "cryptSetAttribute() for entry %d, field ID %d,\n"
831  " value %d, failed with error code %d, line %d.\n",
832  i + 1, certData[ i ].type, certData[ i ].numericValue,
833  status, lineNo );
834  }
835  break;
836 
837  case IS_STRING:
838  status = cryptSetAttributeString( certificate,
839  certData[ i ].type, certData[ i ].stringValue,
840  certData[ i ].numericValue ? \
841  certData[ i ].numericValue : \
842  paramStrlen( certData[ i ].stringValue ) );
843  if( cryptStatusError( status ) )
844  {
845 #if defined( _MSC_VER ) && ( _MSC_VER == 1200 ) && !defined( NDEBUG )
846  if( status == CRYPT_ERROR_INVALID && \
847  paramStrlen( certData[ i ].stringValue ) == 2 && \
848  !memcmp( certData[ i ].stringValue, "NZ", 2 ) )
849  {
850  /* Warn about BoundsChecker-induced Heisenbugs */
851  puts( " ********************" );
852  puts( "If you're running this under BoundsChecker "
853  "you need to disable it to complete\nthe test "
854  "since it causes errors in the certificate "
855  "string-checking code. The\nfollowing error "
856  "is caused by BoundsChecker, not by the "
857  "self-test failing." );
858  puts( " ********************" );
859  }
860 #endif /* VC++ 6 */
861  printf( "cryptSetAttributeString() for entry %d, field ID %d,\n"
862  " value '%s', failed with error code %d, line %d.\n",
863  i + 1, certData[ i ].type,
864  ( char * ) certData[ i ].stringValue, status,
865  lineNo );
866  }
867  break;
868 
869 #ifdef HAS_WIDECHAR
870  case IS_WCSTRING:
871  status = cryptSetAttributeString( certificate,
872  certData[ i ].type, certData[ i ].stringValue,
873  wcslen( certData[ i ].stringValue ) * sizeof( wchar_t ) );
874  if( cryptStatusError( status ) )
875  {
876  printf( "cryptSetAttributeString() for entry %d, field ID %d,\n"
877  " value '%s', failed with error code %d, line %d.\n",
878  i + 1, certData[ i ].type,
879  ( char * ) certData[ i ].stringValue, status,
880  lineNo );
881  }
882  break;
883 #endif /* HAS_WIDECHAR */
884 
885  case IS_TIME:
886  status = cryptSetAttributeString( certificate,
887  certData[ i ].type, &certData[ i ].timeValue,
888  sizeof( time_t ) );
889  if( cryptStatusError( status ) )
890  printf( "cryptSetAttributeString() for entry %d, field ID %d,\n"
891  " value 0x%lX, failed with error code %d, line %d.\n",
892  i + 1, certData[ i ].type, certData[ i ].timeValue,
893  status, lineNo );
894  break;
895 
896  default:
897  assert( FALSE );
898  return( FALSE );
899  }
900  if( cryptStatusError( status ) )
901  {
902  printErrorAttributeInfo( certificate );
903  return( FALSE );
904  }
905  }
906 
907  return( TRUE );
908  }
909 
910 /* Populate a key database with the contents of a directory. This is a
911  rather OS-specific utility function for setting up test databases that
912  only works under Win32 (in fact it's not used at all at the moment) */
913 
914 #if defined( _MSC_VER ) && defined( _WIN32 ) && !defined( _WIN32_WCE ) && 0
915 
916 void loadCertificates( void )
917  {
918  WIN32_FIND_DATA findData;
919  HANDLE searchHandle;
920 
921  searchHandle = FindFirstFile( "d:/tmp/certs/*.der", &findData );
922  if( searchHandle == INVALID_HANDLE_VALUE )
923  return;
924  do
925  {
926  CRYPT_CERTIFICATE cryptCert;
927  int status;
928 
929  printf( "Adding certificate %s.\n", findData.cFileName );
930  status = importCertFile( &cryptCert, findData.cFileName );
931  if( cryptStatusOK( status ) )
932  {
933  cryptDestroyCert( cryptCert );
934  }
935  }
936  while( FindNextFile( searchHandle, &findData ) );
937  FindClose( searchHandle );
938  }
939 #endif /* Win32 */
940 
941 /****************************************************************************
942 * *
943 * Debug Functions *
944 * *
945 ****************************************************************************/
946 
947 /* Write an object to a file for debugging purposes */
948 
949 #if defined( _MSC_VER ) && \
950  !( defined( _WIN32_WCE ) || defined( __PALMSOURCE__ ) )
951  #include <direct.h>
952  #include <io.h>
953 #endif /* VC++ Win16/Win32 */
954 
955 void debugDump( const char *fileName, const void *data, const int dataLength )
956  {
957  FILE *filePtr;
958 #ifdef __UNIX__
959  const char *tmpPath = getenv( "TMPDIR" );
960  char fileNameBuffer[ FILENAME_BUFFER_SIZE ];
961  const int tmpPathLen = ( tmpPath != NULL ) ? strlen( tmpPath ) : 0;
962 #else
963  char fileNameBuffer[ 128 ];
964 #endif /* __UNIX__ */
965  const int length = strlen( fileName );
966  int count;
967 
968  fileNameBuffer[ 0 ] = '\0';
969 #if defined( _WIN32_WCE )
970  /* Under WinCE we don't want to scribble a ton of data into flash every
971  time we're run so we don't try and do anything */
972  return;
973 #elif ( defined( _MSC_VER ) && !defined( __PALMSOURCE__ ) )
974  /* If the path isn't absolute, deposit it in a temp directory. Note
975  that we have to use underscores in front of the Posix functions
976  because these were deprecated starting with VS 2005. In addition we
977  have to explicitly exclude oldnames.lib (which usually isn't a
978  included in the libraries installed with VS) from the link, inclusion
979  of this is triggered by the compiler seeing the Posix or underscore-
980  Posix functions */
981  #if defined( _MSC_VER ) && ( _MSC_VER >= 1400 )
982  #pragma comment(linker, "/nodefaultlib:oldnames.lib")
983  #endif /* VC++ 2005 and newer misconfiguration */
984  if( fileName[ 1 ] != ':' )
985  {
986  if( _access( "d:/tmp/", 6 ) == 0 )
987  {
988  /* There's a data partition available, dump the info there */
989  if( _access( "d:/tmp/", 6 ) == -1 && \
990  !CreateDirectory( "d:/tmp", NULL ) )
991  return;
992  strcpy( fileNameBuffer, "d:/tmp/" );
993  }
994  else
995  {
996  /* There's no separate data partition, everything's dumped into
997  the same partition */
998  if( _access( "c:/tmp/", 6 ) == -1 && \
999  !CreateDirectory( "c:/tmp", NULL ) )
1000  return;
1001  strcpy( fileNameBuffer, "c:/tmp/" );
1002  }
1003  }
1004 #elif defined( __UNIX__ )
1005  /* If the path isn't absolute, deposit it in a temp directory */
1006  if( fileName[ 0 ] != '/' )
1007  {
1008  if( tmpPathLen > 3 && tmpPathLen < FILENAME_BUFFER_SIZE - 64 )
1009  {
1010  strcpy( fileNameBuffer, tmpPath );
1011  if( fileNameBuffer[ tmpPathLen - 1 ] != '/' )
1012  strcat( fileNameBuffer + tmpPathLen, "/" );
1013  }
1014  else
1015  strcpy( fileNameBuffer, "/tmp/" );
1016  }
1017 #else
1018  fileNameBuffer[ 0 ] = '\0';
1019 #endif /* OS-specific paths */
1020  strcat( fileNameBuffer, fileName );
1021  if( length <= 3 || fileName[ length - 4 ] != '.' )
1022  strcat( fileNameBuffer, ".der" );
1023 
1024 #if defined( __VMCMS__ )
1025  {
1026  char formatBuffer[ 32 ];
1027 
1028  sprintf( formatBuffer, "wb, recfm=F, lrecl=%d, noseek", dataLength );
1029  filePtr = fopen( fileNameBuffer, formatBuffer );
1030  }
1031  if( filePtr == NULL )
1032 #else
1033  if( ( filePtr = fopen( fileNameBuffer, "wb" ) ) == NULL )
1034 #endif /* __VMCMS__ */
1035  return;
1036  count = fwrite( data, 1, dataLength, filePtr );
1037  fclose( filePtr );
1038  if( count < length )
1039  {
1040  printf( "Warning: Couldn't dump '%s' to disk.\n", fileName );
1041  remove( fileName );
1042  }
1043  }
1044 
1045 /****************************************************************************
1046 * *
1047 * Session Functions *
1048 * *
1049 ****************************************************************************/
1050 
1051 /* Print information on the peer that we're talking to */
1052 
1053 int printConnectInfo( const CRYPT_SESSION cryptSession )
1054  {
1055 #ifndef UNICODE_STRINGS
1056  time_t theTime;
1057 #endif /* UNICODE_STRINGS */
1058  C_CHR serverName[ 128 ];
1059  int serverNameLength, serverPort, status;
1060 
1061  status = cryptGetAttributeString( cryptSession, CRYPT_SESSINFO_CLIENT_NAME,
1062  serverName, &serverNameLength );
1063  if( cryptStatusError( status ) )
1064  return( FALSE );
1065  cryptGetAttribute( cryptSession, CRYPT_SESSINFO_CLIENT_PORT, &serverPort );
1066 #ifdef UNICODE_STRINGS
1067  serverName[ serverNameLength / sizeof( wchar_t ) ] = TEXT( '\0' );
1068  printf( "SVR: Connect attempt from %S, port %d", serverName, serverPort );
1069 #else
1070  serverName[ serverNameLength ] = '\0';
1071  time( &theTime );
1072  printf( "SVR: Connect attempt from %s, port %d, on %s.\n", serverName,
1073  serverPort, getTimeString( theTime, 0 ) );
1074 #endif /* UNICODE_STRINGS */
1075  fflush( stdout );
1076 
1077  /* Display all the attributes that we've got */
1078  status = displayAttributes( cryptSession );
1079  fflush( stdout );
1080  return( status );
1081  }
1082 
1083 /* Print security info for the session */
1084 
1085 int printSecurityInfo( const CRYPT_SESSION cryptSession,
1086  const BOOLEAN isServer,
1087  const BOOLEAN showFingerprint,
1088  const BOOLEAN showServerKeyInfo,
1089  const BOOLEAN showClientCertInfo )
1090  {
1091  int cryptAlgo, keySize = DUMMY_INIT, version = DUMMY_INIT, status;
1092 
1093  /* Print general security info */
1094  status = cryptGetAttribute( cryptSession, CRYPT_CTXINFO_ALGO,
1095  &cryptAlgo );
1096  if( cryptStatusOK( status ) )
1097  status = cryptGetAttribute( cryptSession, CRYPT_CTXINFO_KEYSIZE,
1098  &keySize );
1099  if( cryptStatusOK( status ) )
1100  status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_VERSION,
1101  &version );
1102  if( cryptStatusError( status ) )
1103  {
1104  printf( "Couldn't get session security parameters, status %d, line "
1105  "%d.\n", status, __LINE__ );
1106  return( FALSE );
1107  }
1108  printf( "%sSession is protected using algorithm %d with a %d bit key,\n"
1109  " protocol version %d.\n", isServer ? "SVR: " : "",
1110  cryptAlgo, keySize * 8, version );
1111  if( showServerKeyInfo || showClientCertInfo )
1112  {
1113  CRYPT_CONTEXT serverKey;
1114 
1115  status = cryptGetAttribute( cryptSession, CRYPT_SESSINFO_RESPONSE,
1116  &serverKey );
1117  if( cryptStatusOK( status ) )
1118  {
1119  status = cryptGetAttribute( serverKey, CRYPT_CTXINFO_ALGO,
1120  &cryptAlgo );
1121  if( cryptStatusOK( status ) )
1122  status = cryptGetAttribute( serverKey, CRYPT_CTXINFO_KEYSIZE,
1123  &keySize );
1124  cryptDestroyContext( serverKey );
1125  }
1126  if( cryptStatusError( status ) )
1127  {
1128  printf( "Couldn't get server security parameters, status %d, line "
1129  "%d.\n", status, __LINE__ );
1130  return( FALSE );
1131  }
1132  printf( "%s key uses algorithm %d, key size %d bits.\n",
1133  showClientCertInfo ? "SVR: Client authentication" : "Server",
1134  cryptAlgo, keySize * 8 );
1135  }
1136  fflush( stdout );
1137  if( isServer || !showFingerprint )
1138  return( TRUE );
1139 
1140  status = printFingerprint( cryptSession, FALSE );
1141  fflush( stdout );
1142  return( status );
1143  }
1144 
1145 int printFingerprint( const CRYPT_SESSION cryptSession,
1146  const BOOLEAN isServer )
1147  {
1148  BYTE fingerPrint[ CRYPT_MAX_HASHSIZE ];
1149  int i, length, status;
1150 
1151  /* Print the server key fingerprint */
1152  status = cryptGetAttributeString( cryptSession,
1154  fingerPrint, &length );
1155  if( cryptStatusError( status ) )
1156  {
1157  printf( "cryptGetAttributeString() failed with error code "
1158  "%d, line %d.\n", status, __LINE__ );
1159  return( FALSE );
1160  }
1161  printf( "%sServer key fingerprint =\n ", isServer ? "SVR: " : "" );
1162  for( i = 0; i < length; i++ )
1163  {
1164  if( i > 0 )
1165  putchar( ' ' );
1166  printf( "%02X", fingerPrint[ i ] );
1167  }
1168  puts( "." );
1169  fflush( stdout );
1170 
1171  return( TRUE );
1172  }
1173 
1174 /* Set up a client/server to connect locally. For the client his simply
1175  tells it where to connect, for the server this binds it to the local
1176  (loopback) address so we don't inadvertently open up outside ports
1177  (admittedly they can't do much except run the hardcoded self-test, but
1178  it's better not to do this at all).
1179 
1180  In order to allow testing against outside clients we optionally allow it
1181  to be set to an external interface, but make it a compile-time option to
1182  ensure that it can't be enabled by accident */
1183 
1184 #if 1
1185  #define LOCAL_HOST_NAME "localhost"
1186 #else
1187  #define LOCAL_HOST_NAME "192.168.1.45"
1188 #endif /* 0 */
1189 
1190 BOOLEAN setLocalConnect( const CRYPT_SESSION cryptSession, const int port )
1191  {
1192  int status;
1193 
1194  if( LOCAL_HOST_NAME[ 0 ] != 'l' )
1195  {
1196  puts( "Warning: Enabling server on non-local interface '"
1197  LOCAL_HOST_NAME "'." );
1198  }
1199  status = cryptSetAttributeString( cryptSession,
1201  TEXT( LOCAL_HOST_NAME ),
1203 #ifdef __UNIX__
1204  /* If we're running under Unix, set the port to a nonprivileged one so
1205  that we don't have to run as root. For anything other than very low-
1206  numbered ports (e.g. SSH), the way we determine the port is to repeat
1207  the first digit, so e.g. TSA on 318 becomes 3318, this seems to be
1208  the method most commonly used */
1209  if( cryptStatusOK( status ) && port < 1024 )
1210  {
1211  if( port < 100 )
1212  status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT,
1213  port + 4000 );
1214  else
1215  status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_SERVER_PORT,
1216  ( ( port / 100 ) * 1000 ) + port );
1217  }
1218 #endif /* __UNIX__ */
1219  if( cryptStatusError( status ) )
1220  {
1221  printf( "cryptSetAttribute/AttributeString() failed with error code "
1222  "%d, line %d.\n", status, __LINE__ );
1223  return( FALSE );
1224  }
1225 
1226  return( TRUE );
1227  }
1228 
1229 /* Run a persistent server session, recycling the connection if the client
1230  kept the link open */
1231 
1232 static void printOperationType( const CRYPT_SESSION cryptSession )
1233  {
1234  struct {
1235  const int operation;
1236  const char *name;
1237  } operationTypeTbl[] = {
1238  { CRYPT_REQUESTTYPE_NONE, "(None)" },
1241  { CRYPT_REQUESTTYPE_KEYUPDATE, "kur" },
1242  { CRYPT_REQUESTTYPE_REVOCATION, "rr" },
1243  { CRYPT_REQUESTTYPE_PKIBOOT, "pkiBoot" },
1244  { -1, "(Unknown)" }
1245  };
1246  char userID[ CRYPT_MAX_TEXTSIZE ];
1247  int userIDsize = DUMMY_INIT, requestType, i, status;
1248 
1249  status = cryptGetAttribute( cryptSession,
1251  &requestType );
1252  if( cryptStatusOK( status ) )
1253  status = cryptGetAttributeString( cryptSession,
1255  userID, &userIDsize );
1256  if( cryptStatusError( status ) )
1257  {
1258  printf( "cryptGetAttribute/AttributeString() failed with error "
1259  "code %d, line %d.\n", status, __LINE__ );
1260  return;
1261  }
1262  userID[ userIDsize ] = '\0';
1263  for( i = 0; operationTypeTbl[ i ].operation != requestType && \
1264  operationTypeTbl[ i ].operation != -1; i++ );
1265  printf( "SVR: Operation type was %d = %s, user '%s'.\n",
1266  requestType, operationTypeTbl[ i ].name, userID );
1267  fflush( stdout );
1268  }
1269 
1271  const BOOLEAN showOperationType )
1272  {
1273  BOOLEAN connectionActive = FALSE;
1274  int status;
1275 
1276  do
1277  {
1278  /* Activate the connection */
1279  status = cryptSetAttribute( cryptSession, CRYPT_SESSINFO_ACTIVE,
1280  TRUE );
1281  if( status == CRYPT_ERROR_READ && connectionActive )
1282  {
1283  /* The other side closed the connection after a previous
1284  successful transaction, this isn't an error */
1285  return( CRYPT_OK );
1286  }
1287 
1288  /* Print connection info and check whether the connection is still
1289  active. If it is, we recycle the session so that we can process
1290  another request */
1291  printConnectInfo( cryptSession );
1292  if( cryptStatusOK( status ) && showOperationType )
1293  printOperationType( cryptSession );
1295  &connectionActive );
1296  }
1297  while( cryptStatusOK( status ) && connectionActive );
1298 
1299  return( status );
1300  }
1301 
1302 /****************************************************************************
1303 * *
1304 * Attribute Dump Routines *
1305 * *
1306 ****************************************************************************/
1307 
1308 /* Print a list of all attributes present in an object */
1309 
1311  {
1312  int status;
1313 
1314  if( cryptStatusError( \
1316  CRYPT_CURSOR_FIRST ) ) )
1317  return( TRUE );
1318 
1319  puts( "Attributes present (by cryptlib ID) are:" );
1320  do
1321  {
1322  BOOLEAN firstAttr = TRUE;
1323  int value;
1324 
1325  status = cryptGetAttribute( cryptHandle,
1327  if( cryptStatusError( status ) )
1328  {
1329  printf( "\nCurrent attribute group value read failed with "
1330  "error code %d, line %d.\n", status, __LINE__ );
1331  return( FALSE );
1332  }
1333  printf( " Attribute group %d, values =", value );
1334  do
1335  {
1336  status = cryptGetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT,
1337  &value );
1338  if( cryptStatusError( status ) )
1339  {
1340  printf( "\nCurrent attribute value read failed with error "
1341  "code %d, line %d.\n", status, __LINE__ );
1342  return( FALSE );
1343  }
1344  if( !firstAttr )
1345  putchar( ',' );
1346  printf( " %d", value );
1347  firstAttr = FALSE;
1348  }
1349  while( cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT,
1350  CRYPT_CURSOR_NEXT ) == CRYPT_OK );
1351  puts( "." );
1352  }
1353  while( cryptSetAttribute( cryptHandle, CRYPT_ATTRIBUTE_CURRENT_GROUP,
1354  CRYPT_CURSOR_NEXT ) == CRYPT_OK );
1355 
1356  /* Reset the cursor to the first attribute. This is useful for things
1357  like envelopes and sessions where the cursor points at the first
1358  attribute that needs to be handled */
1361  return( TRUE );
1362  }
1363 
1364 /****************************************************************************
1365 * *
1366 * Certificate Dump Routines *
1367 * *
1368 ****************************************************************************/
1369 
1370 /* Check whether a string may be a Unicode string */
1371 
1372 static BOOLEAN isUnicode( const BYTE *value, const int length )
1373  {
1374  wchar_t wcValue[ 8 + 4 ];
1375 
1376  /* If it's an odd length or too short to reliably guess, report it as
1377  non-Unicode */
1378  if( ( length % sizeof( wchar_t ) ) || length <= sizeof( wchar_t ) * 2 )
1379  return( FALSE );
1380 
1381  /* If the first four characters are ASCII then it's unlikely that it'll
1382  be Unicode */
1383  if( isprint( value[ 0 ] ) && isprint( value[ 1 ] ) && \
1384  isprint( value[ 2 ] ) && isprint( value[ 3 ] ) )
1385  return( FALSE );
1386 
1387  /* Copy the byte-aligned value into a local wchar_t-aligned buffer for
1388  analysis */
1389  memcpy( wcValue, value, min( 8, length ) );
1390 
1391  /* Check whether the first 3 widechars have identical high bytes. This
1392  isn't totally reliable (e.g. "tanaka" will give a false positive,
1393  { 0x0160, 0x0069, 0x006B } will give a false negative) but it's close
1394  enough */
1395  if( ( wcValue[ 0 ] & 0xFF00 ) == ( wcValue[ 1 ] & 0xFF00 ) && \
1396  ( wcValue[ 0 ] & 0xFF00 ) == ( wcValue[ 2 ] & 0xFF00 ) )
1397  return( TRUE );
1398 
1399  return( FALSE );
1400  }
1401 
1402 /* Print a hex string */
1403 
1404 static void printHex( const BYTE *value, const int length )
1405  {
1406  int i;
1407 
1408  for( i = 0; i < length; i++ )
1409  {
1410  if( i )
1411  printf( " " );
1412  printf( "%02X", value[ i ] );
1413  }
1414  puts( "." );
1415  }
1416 
1417 /* The following function performs many attribute accesses, rather than using
1418  huge numbers of status checks we use the following macro to check each
1419  attribute access */
1420 
1421 #define CHK( function ) \
1422  status = function; \
1423  if( cryptStatusError( status ) ) \
1424  return( certInfoErrorExit( #function, status, __LINE__ ) )
1425 
1426 static int certInfoErrorExit( const char *functionCall, const int status,
1427  const int line )
1428  {
1429  printf( "\n%s failed with status %d, line %d.\n", functionCall,
1430  status, line );
1431  return( FALSE );
1432  }
1433 
1434 /* Print a DN or altName */
1435 
1436 static int printComponent( const CRYPT_CERTIFICATE certificate,
1437  const CRYPT_ATTRIBUTE_TYPE component,
1438  const char *prefixString )
1439  {
1440  char buffer[ 1024 + 1 ];
1441  int length, status;
1442 
1443  status = cryptGetAttributeString( certificate, component, NULL,
1444  &length );
1445  if( cryptStatusError( status ) )
1446  {
1447  if( status == CRYPT_ERROR_NOTAVAIL && \
1448  component == CRYPT_CERTINFO_DN )
1449  {
1450  /* Report this special-case condition explicitly */
1451  puts( " (Name contains characters that prevent it from being "
1452  "represented as a\n text string)." );
1453  }
1454  return( FALSE );
1455  }
1456  if( length > 1024 )
1457  {
1458  /* This should never happen since the longest permitted component
1459  string has 128 characters, but we check for it just in case */
1460  puts( " (Name is too long to display, > 1K characters)." );
1461  return( FALSE );
1462  }
1463  status = cryptGetAttributeString( certificate, component, buffer,
1464  &length );
1465  if( cryptStatusError( status ) )
1466  return( FALSE );
1467  if( isUnicode( buffer, length ) )
1468  {
1469  wchar_t wcBuffer[ 1024 + 1 ];
1470 
1471  /* Copy the byte-aligned value into a local wchar_t-aligned buffer
1472  for display */
1473  memcpy( wcBuffer, buffer, length );
1474  wcBuffer[ length / sizeof( wchar_t ) ] = TEXT( '\0' );
1475  printf( " %s = %S.\n", prefixString, wcBuffer );
1476  return( TRUE );
1477  }
1478  buffer[ length ] = '\0';
1479  printf( " %s = %s.\n", prefixString, buffer );
1480 
1481  return( TRUE );
1482  }
1483 
1484 static int printComponents( const CRYPT_CERTIFICATE certificate,
1485  const CRYPT_ATTRIBUTE_TYPE component,
1486  const char *prefixString )
1487  {
1488  int status;
1489 
1490  /* Try and print the component if it's present */
1491  if( !printComponent( certificate, component, prefixString ) )
1492  return( FALSE );
1493 
1494  /* If it's not a DN or altName component, we're done */
1495  if( !( component >= CRYPT_CERTINFO_COUNTRYNAME && \
1496  component <= CRYPT_CERTINFO_COMMONNAME ) && \
1497  !( component >= CRYPT_CERTINFO_OTHERNAME_TYPEID && \
1498  component > CRYPT_CERTINFO_REGISTEREDID ) )
1499  return( TRUE );
1500 
1501  /* Check for further components, for multivalued components in altNames */
1503  component ) );
1504  while( cryptSetAttribute( certificate,
1507  {
1508  char buffer[ 64 ];
1509 
1510  sprintf( buffer, " + %s", prefixString );
1511  if( !printComponent( certificate, component, buffer ) )
1512  return( FALSE );
1513  }
1514  return( TRUE );
1515  }
1516 
1517 static void printDN( const CRYPT_CERTIFICATE certificate )
1518  {
1519  printComponents( certificate, CRYPT_CERTINFO_DN, "DN string" );
1520  printComponents( certificate, CRYPT_CERTINFO_COUNTRYNAME, "C" );
1521  printComponents( certificate, CRYPT_CERTINFO_STATEORPROVINCENAME, "S" );
1522  printComponents( certificate, CRYPT_CERTINFO_LOCALITYNAME, "L" );
1523  printComponents( certificate, CRYPT_CERTINFO_ORGANIZATIONNAME, "O" );
1524  printComponents( certificate, CRYPT_CERTINFO_ORGANIZATIONALUNITNAME, "OU" );
1525  printComponents( certificate, CRYPT_CERTINFO_COMMONNAME, "CN" );
1526  }
1527 
1528 static void printAltName( const CRYPT_CERTIFICATE certificate )
1529  {
1530  int status;
1531 
1532  printComponents( certificate, CRYPT_CERTINFO_RFC822NAME, "Email" );
1533  printComponents( certificate, CRYPT_CERTINFO_DNSNAME, "DNSName" );
1534  printComponents( certificate, CRYPT_CERTINFO_EDIPARTYNAME_NAMEASSIGNER, "EDI Nameassigner" );
1535  printComponents( certificate, CRYPT_CERTINFO_EDIPARTYNAME_PARTYNAME, "EDI Partyname" );
1536  printComponents( certificate, CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER, "URL" );
1537  printComponents( certificate, CRYPT_CERTINFO_IPADDRESS, "IP" );
1538  printComponents( certificate, CRYPT_CERTINFO_REGISTEREDID, "Registered ID" );
1539  status = cryptSetAttribute( certificate, CRYPT_CERTINFO_DIRECTORYNAME,
1540  CRYPT_UNUSED );
1541  if( cryptStatusOK( status ) )
1542  {
1543  printf( " altName DN is:\n" );
1544  printDN( certificate );
1545  }
1546  }
1547 
1548 /* Print information on a certificate */
1549 
1550 int printCertInfo( const CRYPT_CERTIFICATE certificate )
1551  {
1553  char buffer[ 1024 ];
1554  int length, value, status;
1555 
1556  CHK( cryptGetAttribute( certificate, CRYPT_CERTINFO_CERTTYPE, &value ) );
1557  certType = value;
1558 
1559  /* Display the issuer and subject DN */
1560  if( certType != CRYPT_CERTTYPE_CERTREQUEST && \
1561  certType != CRYPT_CERTTYPE_REQUEST_CERT && \
1562  certType != CRYPT_CERTTYPE_REQUEST_REVOCATION && \
1563  certType != CRYPT_CERTTYPE_RTCS_REQUEST && \
1564  certType != CRYPT_CERTTYPE_RTCS_RESPONSE && \
1565  certType != CRYPT_CERTTYPE_OCSP_REQUEST && \
1566  certType != CRYPT_CERTTYPE_CMS_ATTRIBUTES && \
1567  certType != CRYPT_CERTTYPE_PKIUSER )
1568  {
1569  puts( "Certificate object issuer name is:" );
1572  printDN( certificate );
1573  if( cryptStatusOK( \
1574  cryptGetAttribute( certificate,
1575  CRYPT_CERTINFO_ISSUERALTNAME, &value ) ) )
1576  {
1579  printAltName( certificate );
1580  }
1581  }
1582  if( certType != CRYPT_CERTTYPE_CRL && \
1583  certType != CRYPT_CERTTYPE_REQUEST_REVOCATION && \
1584  certType != CRYPT_CERTTYPE_CMS_ATTRIBUTES && \
1585  certType != CRYPT_CERTTYPE_RTCS_REQUEST && \
1586  certType != CRYPT_CERTTYPE_RTCS_RESPONSE && \
1587  certType != CRYPT_CERTTYPE_OCSP_REQUEST && \
1588  certType != CRYPT_CERTTYPE_OCSP_RESPONSE )
1589  {
1590  puts( "Certificate object subject name is:" );
1593  printDN( certificate );
1594  if( cryptStatusOK( \
1595  cryptGetAttribute( certificate,
1596  CRYPT_CERTINFO_SUBJECTALTNAME, &value ) ) )
1597  {
1600  printAltName( certificate );
1601  }
1602  }
1603 
1604  /* Display the validity information */
1605 #ifndef _WIN32_WCE
1606  if( certType == CRYPT_CERTTYPE_CERTCHAIN ||
1607  certType == CRYPT_CERTTYPE_CERTIFICATE || \
1608  certType == CRYPT_CERTTYPE_ATTRIBUTE_CERT )
1609  {
1610  time_t validFrom, validTo;
1611 
1613  &validFrom, &length ) );
1615  &validTo, &length ) );
1616  printf( "Certificate is valid from %s to %s.\n",
1617  getTimeString( validFrom, 0 ),
1618  getTimeString( validTo, 1 ) );
1619  }
1620  if( certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
1621  {
1622  time_t thisUpdate, nextUpdate;
1623 
1624  status = cryptGetAttributeString( certificate, CRYPT_CERTINFO_THISUPDATE,
1625  &thisUpdate, &length );
1626  if( cryptStatusOK( status ) )
1627  {
1628  /* RTCS basic responses only return a minimal valid/not valid
1629  status so failing to find a time isn't an error */
1630  status = cryptGetAttributeString( certificate,
1632  &nextUpdate, &length );
1633  if( cryptStatusOK( status ) )
1634  {
1635  printf( "OCSP source CRL time %s,\n next update %s.\n",
1636  getTimeString( thisUpdate, 0 ),
1637  getTimeString( nextUpdate, 1 ) );
1638  }
1639  else
1640  {
1641  printf( "OCSP source CRL time %s.\n",
1642  getTimeString( thisUpdate, 0 ) );
1643  }
1644  }
1645  }
1646  if( certType == CRYPT_CERTTYPE_CRL )
1647  {
1648  time_t thisUpdate, nextUpdate;
1649 
1651  &thisUpdate, &length ) );
1652  status = cryptGetAttributeString( certificate, CRYPT_CERTINFO_NEXTUPDATE,
1653  &nextUpdate, &length );
1654  if( cryptStatusOK( status ) )
1655  {
1656  printf( "CRL time %s,\n next update %s.\n",
1657  getTimeString( thisUpdate, 0 ),
1658  getTimeString( nextUpdate, 1 ) );
1659  }
1660  else
1661  printf( "CRL time %s.\n", getTimeString( thisUpdate, 0 ) );
1662  }
1663 #endif /* _WIN32_WCE */
1664  if( certType == CRYPT_CERTTYPE_CRL || \
1665  certType == CRYPT_CERTTYPE_RTCS_RESPONSE || \
1666  certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
1667  {
1668  int noEntries = 0;
1669 
1670  /* Count and display the entries */
1673  {
1674  puts( "Revocation/validity list information: " );
1675  do
1676  {
1677 #ifndef _WIN32_WCE
1678  time_t timeStamp = 0;
1679 #endif /* _WIN32_WCE */
1680  int revStatus, certStatus;
1681 
1682  noEntries++;
1683 
1684  /* Extract response-specific status information */
1685  if( certType == CRYPT_CERTTYPE_RTCS_RESPONSE )
1686  {
1687  CHK( cryptGetAttribute( certificate,
1688  CRYPT_CERTINFO_CERTSTATUS, &certStatus ) );
1689  }
1690  if( certType == CRYPT_CERTTYPE_OCSP_RESPONSE )
1691  {
1692  CHK( cryptGetAttribute( certificate,
1693  CRYPT_CERTINFO_REVOCATIONSTATUS, &revStatus ) );
1694  }
1695 #ifndef _WIN32_WCE
1696  if( certType == CRYPT_CERTTYPE_CRL || \
1697  ( certType == CRYPT_CERTTYPE_OCSP_RESPONSE && \
1698  revStatus == CRYPT_OCSPSTATUS_REVOKED ) || \
1699  ( certType == CRYPT_CERTTYPE_RTCS_RESPONSE && \
1700  certStatus == CRYPT_CERTSTATUS_NOTVALID ) )
1701  {
1702  CHK( cryptGetAttributeString( certificate,
1703  CRYPT_CERTINFO_REVOCATIONDATE, &timeStamp,
1704  &length ) );
1705  }
1706 #endif /* _WIN32_WCE */
1707 
1708  /* Make sure we don't print excessive amounts of
1709  information */
1710  if( noEntries >= 20 )
1711  {
1712  if( noEntries == 20 )
1713  puts( " (Further entries exist, but won't be printed)." );
1714  continue;
1715  }
1716 
1717  /* Print details status info */
1718  switch( certType )
1719  {
1721  printf( " Certificate status = %d (%s).\n",
1722  certStatus,
1723  ( certStatus == CRYPT_CERTSTATUS_VALID ) ? \
1724  "valid" : \
1725  ( certStatus == CRYPT_CERTSTATUS_NOTVALID ) ? \
1726  "not valid" : \
1727  ( certStatus == CRYPT_CERTSTATUS_NONAUTHORITATIVE ) ? \
1728  "only non-authoritative response available" : \
1729  "unknown" );
1730  break;
1731 
1733  printf( " Entry %d, rev.status = %d (%s), rev.time "
1734  "%s.\n", noEntries, revStatus,
1735  ( revStatus == CRYPT_OCSPSTATUS_NOTREVOKED ) ? \
1736  "not revoked" : \
1737  ( revStatus == CRYPT_OCSPSTATUS_REVOKED ) ? \
1738  "revoked" : "unknown",
1739  getTimeString( timeStamp, 0 ) );
1740  break;
1741 
1742  case CRYPT_CERTTYPE_CRL:
1743  printf( " Entry %d, revocation time %s.\n", noEntries,
1744  getTimeString( timeStamp, 0 ) );
1745  break;
1746 
1747  default:
1748  assert( 0 );
1749  }
1750  }
1751  while( cryptSetAttribute( certificate,
1753  CRYPT_CURSOR_NEXT ) == CRYPT_OK );
1754  }
1755  printf( "Revocation/validity list has %d entr%s.\n", noEntries,
1756  ( noEntries == 1 ) ? "y" : "ies" );
1757  }
1758 
1759  /* Display the self-signed status and fingerprint */
1760  if( cryptStatusOK( cryptGetAttribute( certificate,
1761  CRYPT_CERTINFO_SELFSIGNED, &value ) ) )
1762  printf( "Certificate object is %sself-signed.\n",
1763  value ? "" : "not " );
1764  if( certType == CRYPT_CERTTYPE_CERTIFICATE || \
1765  certType == CRYPT_CERTTYPE_CERTCHAIN )
1766  {
1768  buffer, &length ) );
1769  printf( "Certificate fingerprint = " );
1770  printHex( buffer, length );
1771  }
1772 
1773  /* List the attribute types */
1774  if( !displayAttributes( certificate ) )
1775  return( FALSE );
1776 
1777  /* Display common attributes */
1778  if( cryptStatusError( \
1780  CRYPT_CURSOR_FIRST ) ) )
1781  {
1782  puts( " (No extensions/attributes)." );
1783  return( TRUE );
1784  }
1785  puts( "Some of the common extensions/attributes are:" );
1786  if( certType == CRYPT_CERTTYPE_CRL )
1787  {
1788  time_t theTime;
1789 
1791  CRYPT_CURSOR_FIRST ) );
1792  status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CRLNUMBER,
1793  &value );
1794  if( cryptStatusOK( status ) && value )
1795  printf( " crlNumber = %d.\n", value );
1796  status = cryptGetAttribute( certificate, CRYPT_CERTINFO_DELTACRLINDICATOR,
1797  &value );
1798  if( cryptStatusOK( status ) && value )
1799  printf( " deltaCRLIndicator = %d.\n", value );
1800  status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CRLREASON,
1801  &value );
1802  if( cryptStatusOK( status ) && value )
1803  printf( " crlReason = %d.\n", value );
1804  status = cryptGetAttributeString( certificate,
1805  CRYPT_CERTINFO_INVALIDITYDATE, &theTime, &length );
1806 #ifndef _WIN32_WCE
1807  if( cryptStatusOK( status ) )
1808  printf( " invalidityDate = %s.\n",
1809  getTimeString( theTime, 0 ) );
1810 #endif /* _WIN32_WCE */
1811  if( cryptStatusOK( \
1812  cryptGetAttribute( certificate,
1814  {
1817  puts( " issuingDistributionPoint is:" );
1818  printDN( certificate );
1819  printAltName( certificate );
1820  }
1821  return( TRUE );
1822  }
1823 #ifndef _WIN32_WCE
1824  if( certType == CRYPT_CERTTYPE_CMS_ATTRIBUTES )
1825  {
1826  time_t signingTime;
1827 
1828  status = cryptGetAttributeString( certificate,
1830  &signingTime, &length );
1831  if( cryptStatusOK( status ) )
1832  printf( "Signing time %s.\n", getTimeString( signingTime, 0 ) );
1833  return( TRUE );
1834  }
1835 #endif /* _WIN32_WCE */
1836  if( certType == CRYPT_CERTTYPE_PKIUSER )
1837  {
1839  buffer, &length ) );
1840  buffer[ length ] ='\0';
1841  printf( " PKI user ID = %s.\n", buffer );
1842  CHK( cryptGetAttributeString( certificate,
1844  buffer, &length ) );
1845  buffer[ length ] ='\0';
1846  printf( " PKI user issue password = %s.\n", buffer );
1847  CHK( cryptGetAttributeString( certificate,
1849  buffer, &length ) );
1850  buffer[ length ] ='\0';
1851  printf( " PKI user revocation password = %s.\n", buffer );
1852  return( TRUE );
1853  }
1854  status = cryptGetAttribute( certificate,
1855  CRYPT_CERTINFO_KEYUSAGE, &value );
1856  if( cryptStatusOK( status ) && value )
1857  {
1858  static const struct { int flag; const char *name; } usageNames[] = {
1859  { CRYPT_KEYUSAGE_DIGITALSIGNATURE, "digSig" },
1860  { CRYPT_KEYUSAGE_NONREPUDIATION, "nonRep" },
1861  { CRYPT_KEYUSAGE_KEYENCIPHERMENT, "keyEnc" },
1862  { CRYPT_KEYUSAGE_DATAENCIPHERMENT, "dataEnc" },
1863  { CRYPT_KEYUSAGE_KEYAGREEMENT, "keyAgree" },
1864  { CRYPT_KEYUSAGE_KEYCERTSIGN, "certSign" },
1865  { CRYPT_KEYUSAGE_CRLSIGN, "crlSign" },
1866  { CRYPT_KEYUSAGE_ENCIPHERONLY, "encOnly" },
1867  { CRYPT_KEYUSAGE_DECIPHERONLY, "decOnly" },
1868  { CRYPT_KEYUSAGE_NONE, NULL }
1869  };
1870  BOOLEAN printedUsage = FALSE;
1871  int i;
1872 
1873  printf( " keyUsage = %02X (", value );
1874  for( i = 0; usageNames[ i ].flag != CRYPT_KEYUSAGE_NONE; i++ )
1875  {
1876  if( usageNames[ i ].flag & value )
1877  {
1878  if( printedUsage )
1879  printf( ", " );
1880  printf( "%s", usageNames[ i ].name );
1881  printedUsage = TRUE;
1882  }
1883  }
1884  puts( ")." );
1885  }
1886  status = cryptGetAttribute( certificate,
1887  CRYPT_CERTINFO_EXTKEYUSAGE, &value );
1888  if( cryptStatusOK( status ) && value )
1889  {
1890  BOOLEAN firstTime = TRUE;
1891 
1892  printf( " extKeyUsage types = " );
1895  do
1896  {
1898  &value ) );
1899  printf( "%s%d", firstTime ? "" : ", ", value );
1900  firstTime = FALSE;
1901  }
1902  while( cryptSetAttribute( certificate, CRYPT_ATTRIBUTE_CURRENT,
1903  CRYPT_CURSOR_NEXT ) == CRYPT_OK );
1904  printf( ".\n" );
1905  }
1906  status = cryptGetAttribute( certificate, CRYPT_CERTINFO_CA, &value );
1907  if( cryptStatusOK( status ) && value )
1908  printf( " basicConstraints.cA = %s.\n", value ? "True" : "False" );
1909  status = cryptGetAttribute( certificate, CRYPT_CERTINFO_PATHLENCONSTRAINT,
1910  &value );
1911  if( cryptStatusOK( status ) && value )
1912  printf( " basicConstraints.pathLenConstraint = %d.\n", value );
1913  status = cryptGetAttributeString( certificate,
1914  CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, buffer, &length );
1915  if( cryptStatusOK( status ) )
1916  {
1917  printf( " subjectKeyIdentifier = " );
1918  printHex( buffer, length );
1919  }
1920  status = cryptGetAttributeString( certificate,
1921  CRYPT_CERTINFO_AUTHORITY_KEYIDENTIFIER, buffer, &length );
1922  if( cryptStatusOK( status ) )
1923  {
1924  printf( " authorityKeyIdentifier = " );
1925  printHex( buffer, length );
1926  }
1927  status = cryptGetAttributeString( certificate,
1928  CRYPT_CERTINFO_CERTPOLICYID, buffer, &length );
1929  if( cryptStatusOK( status ) )
1930  {
1931  buffer[ length ] = '\0';
1932  printf( " certificatePolicies.policyInformation.policyIdentifier = "
1933  "%s.\n", buffer );
1934  status = cryptGetAttributeString( certificate,
1935  CRYPT_CERTINFO_CERTPOLICY_CPSURI, buffer, &length );
1936  if( cryptStatusOK( status ) )
1937  {
1938  buffer[ length ] = '\0';
1939  printf( " certificatePolicies.policyInformation.cpsURI = "
1940  "%s.\n", buffer );
1941  }
1942  status = cryptGetAttributeString( certificate,
1943  CRYPT_CERTINFO_CERTPOLICY_ORGANIZATION, buffer, &length );
1944  if( cryptStatusOK( status ) )
1945  {
1946  buffer[ length ] = '\0';
1947  printf( " certificatePolicies.policyInformation.organisation = "
1948  "%s.\n", buffer );
1949  }
1950  status = cryptGetAttributeString( certificate,
1951  CRYPT_CERTINFO_CERTPOLICY_EXPLICITTEXT, buffer, &length );
1952  if( cryptStatusOK( status ) )
1953  {
1954  buffer[ length ] = '\0';
1955  printf( " certificatePolicies.policyInformation.explicitText = "
1956  "%s.\n", buffer );
1957  }
1958  }
1959  if( cryptStatusOK( \
1960  cryptGetAttribute( certificate,
1961  CRYPT_CERTINFO_CRLDIST_FULLNAME, &value ) ) )
1962  {
1965  puts( " crlDistributionPoint is/are:" );
1966  do
1967  {
1968  printDN( certificate );
1969  printAltName( certificate );
1970  }
1972  CRYPT_CURSOR_NEXT ) == CRYPT_OK );
1973  }
1974 
1975  return( TRUE );
1976  }
1977 
1979  {
1980  int value, count, status;
1981 
1982  /* Make sure it really is a certificate chain */
1983  CHK( cryptGetAttribute( certChain, CRYPT_CERTINFO_CERTTYPE, &value ) );
1984  if( value != CRYPT_CERTTYPE_CERTCHAIN )
1985  {
1986  printCertInfo( certChain );
1987  return( TRUE );
1988  }
1989 
1990  /* Display info on each certificate in the chain. This uses the cursor
1991  mechanism to select successive certificates in the chain from the
1992  leaf up to the root */
1993  count = 0;
1995  CRYPT_CURSOR_FIRST ) );
1996  do
1997  {
1998  printf( "Certificate %d\n-------------\n", count++ );
1999  printCertInfo( certChain );
2000  printf( "\n" );
2001  }
2002  while( cryptSetAttribute( certChain,
2004 
2005  /* Reset the cursor position in the chain */
2007  CRYPT_CURSOR_FIRST ) );
2008 
2009  return( TRUE );
2010  }