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