cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
certrev.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate Revocation Routines *
4 * Copyright Peter Gutmann 1996-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "asn1.h"
11  #include "asn1_ext.h"
12 #else
13  #include "cert/cert.h"
14  #include "enc_dec/asn1.h"
15  #include "enc_dec/asn1_ext.h"
16 #endif /* Compiler-specific includes */
17 
18 /* The maximum length of ID that can be stored in a REVOCATION_INFO entry.
19  Larger IDs require external storage */
20 
21 #define MAX_ID_SIZE 128
22 
23 /* Usually when we add revocation information we perform various checks such
24  as making sure that we're not adding duplicate information, however when
25  processing the mega-CRLs from some CAs this becomes prohibitively
26  expensive. To solve this problem we perform checking up to a certain
27  number of entries and after that just drop in any further entries as is
28  in order to provide same-day service. The following value defines the
29  number of CRL entries at which we stop performing checks when we add new
30  entries */
31 
32 #define CRL_SORT_LIMIT 1024
33 
34 /* Context-specific tags for OCSP certificate identifier types */
35 
37 
38 /* OCSP certificate status values */
39 
41 
42 #ifdef USE_CERTREV
43 
44 /****************************************************************************
45 * *
46 * Add/Delete/Check Revocation Information *
47 * *
48 ****************************************************************************/
49 
50 /* Find an entry in a revocation list. This is done using a linear search,
51  which isn't very optimal but anyone trying to do anything useful with
52  mega-CRLs (or with CRLs in general) is in more trouble than basic search
53  algorithm choice. In other words it doesn't really make much difference
54  whether we have an optimal or suboptimal implementation of a
55  fundamentally broken mechanism like CRLs.
56 
57  The value is either a serialNumber or a hash of some form (issuerID,
58  certHash), we don't bother distinguishing the exact type since the
59  chances of a hash collision are virtually nonexistant */
60 
61 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
62 static int findRevocationEntry( const REVOCATION_INFO *listPtr,
63  OUT_OPT_PTR REVOCATION_INFO **insertPoint,
64  IN_BUFFER( valueLength ) const void *value,
65  IN_LENGTH_SHORT const int valueLength,
66  const BOOLEAN sortEntries )
67  {
68  const REVOCATION_INFO *prevElement = NULL;
69  const int idCheck = checksumData( value, valueLength );
70  int iterationCount;
71 
72  assert( isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
73  assert( isWritePtr( insertPoint, sizeof( REVOCATION_INFO * ) ) );
74  assert( isReadPtr( value, valueLength ) );
75 
76  REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
77 
78  /* Clear return value */
79  *insertPoint = NULL;
80 
81  /* Find the correct place in the list to insert the new element and check
82  for duplicates. If requested we sort the entries by serial number
83  (or more generally data value) for no adequately explored reason
84  (some implementations can optimise the searching of CRLs based on
85  this but since there's no agreement on whether to do it or not you
86  can't tell whether it's safe to rely on it). In addition we bound
87  the loop with FAILSAFE_ITERATIONS_MAX rather than the more usual
88  FAILSAFE_ITERATIONS_LARGE since CRLs can grow enormous */
89  for( iterationCount = 0;
90  listPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
91  listPtr = listPtr->next, iterationCount++ )
92  {
93  if( ( sortEntries || idCheck == listPtr->idCheck ) && \
94  listPtr->idLength == valueLength )
95  {
96  const int compareStatus = memcmp( listPtr->id,
97  value, valueLength );
98 
99  if( !compareStatus )
100  {
101  /* We found a matching entry, tell the caller which one it
102  is */
103  *insertPoint = ( REVOCATION_INFO * ) listPtr;
104  return( CRYPT_OK );
105  }
106  if( sortEntries && compareStatus > 0 )
107  break; /* Insert before this point */
108  }
109  else
110  {
111  if( sortEntries && listPtr->idLength > valueLength )
112  break; /* Insert before this point */
113  }
114 
115  prevElement = listPtr;
116  }
117  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
118 
119  /* We can't find a matching entry, return the revocation entry that we
120  should insert the new value after */
121  *insertPoint = ( REVOCATION_INFO * ) prevElement;
122  return( CRYPT_ERROR_NOTFOUND );
123  }
124 
125 /* Add an entry to a revocation list */
126 
127 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
128 int addRevocationEntry( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
129  OUT_OPT_PTR REVOCATION_INFO **newEntryPosition,
130  IN_KEYID const CRYPT_KEYID_TYPE valueType,
131  IN_BUFFER( valueLength ) const void *value,
132  IN_LENGTH_SHORT const int valueLength,
133  const BOOLEAN noCheck )
134  {
135  REVOCATION_INFO *newElement, *insertPoint;
136 
137  assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
138  assert( isWritePtr( newEntryPosition, sizeof( REVOCATION_INFO * ) ) );
139  assert( isReadPtr( value, valueLength ) );
140 
141  REQUIRES( valueType == CRYPT_KEYID_NONE || \
142  valueType == CRYPT_IKEYID_CERTID || \
143  valueType == CRYPT_IKEYID_ISSUERID || \
144  valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
145  REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
146 
147  /* Clear return value */
148  *newEntryPosition = NULL;
149 
150  /* Find the insertion point for the new entry unless we're reading data
151  from a large pre-encoded CRL (indicated by the caller setting the
152  noCheck flag), in which case we just drop it in at the start. The
153  absence of checking is necessary in order to provide same-day service
154  for large CRLs */
155  if( !noCheck && *listHeadPtrPtr != NULL && \
156  cryptStatusOK( \
157  findRevocationEntry( *listHeadPtrPtr, &insertPoint, value,
158  valueLength, TRUE ) ) )
159  {
160  /* If we get an OK status it means that we've found an existing
161  entry that matches the one being added, we can't add it again */
162  return( CRYPT_ERROR_DUPLICATE );
163  }
164  else
165  {
166  /* Insert the new element at the start */
167  insertPoint = NULL;
168  }
169 
170  /* Allocate memory for the new element and copy the information across */
171  if( ( newElement = ( REVOCATION_INFO * ) \
172  clAlloc( "addRevocationEntry", \
173  sizeof( REVOCATION_INFO ) + valueLength ) ) == NULL )
174  return( CRYPT_ERROR_MEMORY );
175  initVarStruct( newElement, REVOCATION_INFO, valueLength );
176  newElement->id = newElement->value; /* Varstruct expects field name 'value' */
177  newElement->idType = valueType;
178  memcpy( newElement->id, value, valueLength );
179  newElement->idLength = valueLength;
180  newElement->idCheck = checksumData( value, valueLength );
181 
182  /* Insert the new element into the list */
183  insertSingleListElement( listHeadPtrPtr, insertPoint, newElement );
184  *newEntryPosition = newElement;
185 
186  return( CRYPT_OK );
187  }
188 
189 /* Delete a revocation list */
190 
191 STDC_NONNULL_ARG( ( 1 ) ) \
192 void deleteRevocationEntries( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr )
193  {
194  REVOCATION_INFO *entryListPtr = *listHeadPtrPtr;
195  int iterationCount;
196 
197  assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
198 
199  *listHeadPtrPtr = NULL;
200 
201  /* Destroy any remaining list items */
202  for( iterationCount = 0;
203  entryListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
204  iterationCount++ )
205  {
206  REVOCATION_INFO *itemToFree = entryListPtr;
207 
208  entryListPtr = entryListPtr->next;
209  if( itemToFree->attributes != NULL )
210  deleteAttributes( &itemToFree->attributes );
211  zeroise( itemToFree, sizeof( REVOCATION_INFO ) );
212  clFree( "deleteRevocationEntries", itemToFree );
213  }
214  }
215 
216 /* Prepare the entries in a revocation list prior to encoding them */
217 
218 CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5, 6 ) ) \
219 int prepareRevocationEntries( INOUT_OPT REVOCATION_INFO *listPtr,
220  const time_t defaultTime,
221  OUT_OPT_PTR REVOCATION_INFO **errorEntry,
222  const BOOLEAN isSingleEntry,
223  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
224  CRYPT_ATTRIBUTE_TYPE *errorLocus,
225  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
226  CRYPT_ERRTYPE_TYPE *errorType )
227  {
228  REVOCATION_INFO *revocationEntry;
229  const time_t currentTime = ( defaultTime > MIN_TIME_VALUE ) ? \
230  defaultTime : getApproxTime();
231  int value, iterationCount, status;
232 
233  assert( listPtr == NULL || \
234  isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
235  assert( isWritePtr( errorEntry, sizeof( REVOCATION_INFO * ) ) );
236  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
237  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
238 
239  /* Clear return value */
240  *errorEntry = NULL;
241 
242  /* If the revocation list is empty there's nothing to do */
243  if( listPtr == NULL )
244  return( CRYPT_OK );
245 
246  /* Set the revocation time if this hasn't already been set. If there's a
247  default time set we use that otherwise we use the current time */
248  for( revocationEntry = listPtr, iterationCount = 0;
249  revocationEntry != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
250  revocationEntry = revocationEntry->next, iterationCount++ )
251  {
252  if( revocationEntry->revocationTime <= MIN_TIME_VALUE )
253  revocationEntry->revocationTime = currentTime;
254 
255  /* Check whether the certificate was revoked with a reason of
256  neverValid, which requires special handling of dates because
257  X.509 doesn't formally define a neverValid reason, assuming that
258  all CAs are perfect and never issue certificates in error. The
259  general idea is to set the two to the same value with the
260  invalidity date (which should be earlier than the revocation date,
261  at least in a sanely-run CA) taking precedence. A revocation
262  with this reason code will in general only be issued by the
263  cryptlib CA (where it's required to handle problems in the CMP
264  protocol) and this always sets the invalidity date so in almost
265  all cases we'll be setting the revocation date to the
266  (CA-specified) invalidity date, which is the date of issue of the
267  certificate being revoked */
268  status = getAttributeFieldValue( revocationEntry->attributes,
270  CRYPT_ATTRIBUTE_NONE, &value );
271  if( cryptStatusOK( status ) && value == CRYPT_CRLREASON_NEVERVALID )
272  {
273  time_t invalidityDate;
274 
275  /* The certificate was revoked with the neverValid code, see if
276  there's an invalidity date present */
277  status = getAttributeFieldTime( revocationEntry->attributes,
280  &invalidityDate );
281  if( cryptStatusError( status ) )
282  {
283  /* There's no invalidity date present, set it to the same as
284  the revocation date */
285  status = addAttributeFieldString( &revocationEntry->attributes,
288  &revocationEntry->revocationTime,
289  sizeof( time_t ), 0,
290  errorLocus, errorType );
291  if( cryptStatusError( status ) )
292  {
293  /* Remember the entry that caused the problem */
294  *errorEntry = revocationEntry;
295  return( status );
296  }
297  }
298  else
299  {
300  /* There's an invalidity date present, make sure that the
301  revocation date is the same as the invalidity date */
302  revocationEntry->revocationTime = invalidityDate;
303  }
304  }
305 
306  /* If we're only processing a single CRL entry rather than an
307  entire revocation list we're done */
308  if( isSingleEntry )
309  break;
310  }
311  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
312 
313  /* Check the attributes for each entry in a revocation list */
314  for( revocationEntry = listPtr, iterationCount = 0;
315  revocationEntry != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
316  revocationEntry = revocationEntry->next, iterationCount++ )
317  {
318  if( revocationEntry->attributes != NULL )
319  {
320  status = checkAttributes( ATTRIBUTE_CERTIFICATE,
321  revocationEntry->attributes,
322  errorLocus, errorType );
323  if( cryptStatusError( status ) )
324  {
325  /* Remember the entry that caused the problem */
326  *errorEntry = revocationEntry;
327  return( status );
328  }
329  }
330 
331  /* If we're only processing a single CRL entry rather than an
332  entire revocation list we're done */
333  if( isSingleEntry )
334  break;
335  }
336  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
337 
338  return( CRYPT_OK );
339  }
340 
341 /****************************************************************************
342 * *
343 * CRL-specific Functions *
344 * *
345 ****************************************************************************/
346 
347 /* Check whether a certificate has been revoked */
348 
349 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
350 static int checkRevocationCRL( const CERT_INFO *certInfoPtr,
351  INOUT CERT_INFO *revocationInfoPtr )
352  {
353  CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
354  REVOCATION_INFO *revocationEntry;
355  int status;
356 
357  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
358  assert( isWritePtr( revocationInfoPtr, sizeof( CERT_INFO ) ) );
359 
360  /* If there's no revocation information present then the certificate
361  can't have been revoked */
362  if( certRevInfo->revocations == NULL )
363  return( CRYPT_OK );
364 
365  /* If the issuers differ then the certificate can't be in this CRL */
366  if( ( revocationInfoPtr->issuerDNsize != certInfoPtr->issuerDNsize || \
367  memcmp( revocationInfoPtr->issuerDNptr, certInfoPtr->issuerDNptr,
368  revocationInfoPtr->issuerDNsize ) ) )
369  return( CRYPT_OK );
370 
371  /* Check whether there's an entry for this certificate in the list */
372  status = findRevocationEntry( certRevInfo->revocations, &revocationEntry,
373  certInfoPtr->cCertCert->serialNumber,
374  certInfoPtr->cCertCert->serialNumberLength,
375  FALSE );
376  if( cryptStatusError( status ) )
377  {
378  /* No CRL entry, the certificate is OK */
379  return( CRYPT_OK );
380  }
381  ENSURES( revocationEntry != NULL );
382 
383  /* Select the entry that contains the revocation information and return
384  the certificate's status */
385  certRevInfo->currentRevocation = revocationEntry;
386  return( CRYPT_ERROR_INVALID );
387  }
388 
389 /* Check a certificate against a CRL */
390 
392 int checkCRL( INOUT CERT_INFO *certInfoPtr,
394  {
395  CERT_INFO *crlInfoPtr;
396  int i, status;
397 
398  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
399 
400  REQUIRES( isHandleRangeValid( iCryptCRL ) );
401 
402  /* Check that the CRL is a complete signed CRL and not just a
403  newly-created CRL object */
404  status = krnlAcquireObject( iCryptCRL, OBJECT_TYPE_CERTIFICATE,
405  ( void ** ) &crlInfoPtr,
407  if( cryptStatusError( status ) )
408  return( status );
409  if( crlInfoPtr->certificate == NULL )
410  {
411  krnlReleaseObject( crlInfoPtr->objectHandle );
412  return( CRYPT_ERROR_NOTINITED );
413  }
414  ANALYSER_HINT( crlInfoPtr != NULL );
415 
416  /* Check the base certificate against the CRL. If it's been revoked or
417  there's only a single certificate present, exit */
418  status = checkRevocationCRL( certInfoPtr, crlInfoPtr );
419  if( cryptStatusError( status ) || \
420  certInfoPtr->type != CRYPT_CERTTYPE_CERTCHAIN )
421  {
422  krnlReleaseObject( crlInfoPtr->objectHandle );
423  return( status );
424  }
425 
426  /* It's a certificate chain, check every remaining certificate in the
427  chain against the CRL. In theory this is pointless because a CRL can
428  only contain information for a single certificate in the chain,
429  however the caller may have passed us a CRL for an intermediate
430  certificate (in which case the check for the leaf certificate was
431  pointless). In any case it's easier to just do the check for all
432  certificates than to determine which certificate the CRL applies to
433  so we check for all certificates */
434  for( i = 0; i < certInfoPtr->cCertCert->chainEnd && \
435  i < MAX_CHAINLENGTH; i++ )
436  {
437  CERT_INFO *certChainInfoPtr;
438 
439  /* Check this certificate against the CRL */
440  status = krnlAcquireObject( certInfoPtr->cCertCert->chain[ i ],
442  ( void ** ) &certChainInfoPtr,
444  if( cryptStatusOK( status ) )
445  {
446  status = checkRevocationCRL( certChainInfoPtr, crlInfoPtr );
447  krnlReleaseObject( certChainInfoPtr->objectHandle );
448  }
449 
450  /* If the certificate has been revoked remember which one is the
451  revoked certificate and exit */
452  if( cryptStatusError( status ) )
453  {
454  certInfoPtr->cCertCert->chainPos = i;
455  break;
456  }
457  }
458  ENSURES( i < MAX_CHAINLENGTH );
459 
460  krnlReleaseObject( crlInfoPtr->objectHandle );
461  return( status );
462  }
463 
464 /****************************************************************************
465 * *
466 * Read/write CRL Information *
467 * *
468 ****************************************************************************/
469 
470 /* Read/write CRL entries:
471 
472  RevokedCert ::= SEQUENCE {
473  userCertificate CertificalSerialNumber,
474  revocationDate UTCTime,
475  extensions Extensions OPTIONAL
476  } */
477 
479 int sizeofCRLentry( INOUT REVOCATION_INFO *crlEntry )
480  {
481  int status;
482 
483  assert( isWritePtr( crlEntry, sizeof( REVOCATION_INFO ) ) );
484 
485  /* Remember the encoded attribute size for later when we write the
486  attributes */
487  status = \
488  crlEntry->attributeSize = sizeofAttributes( crlEntry->attributes );
489  if( cryptStatusError( status ) )
490  return( status );
491 
492  return( ( int ) sizeofObject( \
493  sizeofInteger( crlEntry->id, crlEntry->idLength ) + \
494  sizeofUTCTime() + \
495  ( ( crlEntry->attributeSize > 0 ) ? \
496  ( int ) sizeofObject( crlEntry->attributeSize ) : 0 ) ) );
497  }
498 
499 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
500 int readCRLentry( INOUT STREAM *stream,
501  INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
502  IN_LENGTH_Z const int entryNo,
503  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
504  CRYPT_ATTRIBUTE_TYPE *errorLocus,
505  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
506  CRYPT_ERRTYPE_TYPE *errorType )
507  {
508  REVOCATION_INFO *currentEntry;
509  BYTE serialNumber[ MAX_SERIALNO_SIZE + 8 ];
511  time_t revocationTime;
512 
513  assert( isWritePtr( stream, sizeof( STREAM ) ) );
514  assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
515  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
516  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
517 
518  REQUIRES( entryNo >= 0 && entryNo < MAX_INTLENGTH );
519 
520  /* Determine the overall size of the entry */
521  status = readSequence( stream, &length );
522  if( cryptStatusError( status ) )
523  return( status );
524  endPos = stell( stream ) + length;
525 
526  /* Read the integer component of the serial number (limited to a sane
527  length) and the revocation time */
528  readInteger( stream, serialNumber, MAX_SERIALNO_SIZE,
529  &serialNumberLength );
530  status = readUTCTime( stream, &revocationTime );
531  if( cryptStatusError( status ) )
532  return( status );
533 
534  /* Add the entry to the revocation information list. The ID type isn't
535  quite an issueAndSerialNumber but the checking code eventually
536  converts it into this form using the supplied issuer certificate DN */
537  status = addRevocationEntry( listHeadPtrPtr, &currentEntry,
538  CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
539  serialNumber, serialNumberLength,
540  ( entryNo > CRL_SORT_LIMIT ) ? TRUE : FALSE );
541  if( cryptStatusError( status ) )
542  return( status );
543  currentEntry->revocationTime = revocationTime;
544 
545  /* Read the extensions if there are any present. Since these are per-
546  entry extensions we read the extensions themselves as
547  CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_CRL to make sure
548  that they're processed as required */
549  if( stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
550  {
551  status = readAttributes( stream, &currentEntry->attributes,
552  CRYPT_CERTTYPE_NONE, length,
553  errorLocus, errorType );
554  if( cryptStatusError( status ) )
555  return( status );
556  }
557 
558  return( CRYPT_OK );
559  }
560 
561 STDC_NONNULL_ARG( ( 1, 2 ) ) \
562 int writeCRLentry( INOUT STREAM *stream,
563  const REVOCATION_INFO *crlEntry )
564  {
565  const int revocationLength = \
566  sizeofInteger( crlEntry->id, crlEntry->idLength ) + \
567  sizeofUTCTime() + \
568  ( ( crlEntry->attributeSize > 0 ) ? \
569  ( int ) sizeofObject( crlEntry->attributeSize ) : 0 );
570  int status;
571 
572  assert( isWritePtr( stream, sizeof( STREAM ) ) );
573  assert( isReadPtr( crlEntry, sizeof( REVOCATION_INFO ) ) );
574 
575  /* Write the CRL entry */
576  writeSequence( stream, revocationLength );
577  writeInteger( stream, crlEntry->id, crlEntry->idLength, DEFAULT_TAG );
578  status = writeUTCTime( stream, crlEntry->revocationTime, DEFAULT_TAG );
579  if( cryptStatusError( status ) || crlEntry->attributeSize <= 0 )
580  return( status );
581 
582  /* Write the per-entry extensions. Since these are per-entry extensions
583  rather than overall CRL extensions we write them as CRYPT_CERTTYPE_NONE
584  rather than CRYPT_CERTTYPE_CRL to make sure that they're processed as
585  required */
586  return( writeAttributes( stream, crlEntry->attributes,
587  CRYPT_CERTTYPE_NONE, crlEntry->attributeSize ) );
588  }
589 
590 /****************************************************************************
591 * *
592 * OCSP-specific Functions *
593 * *
594 ****************************************************************************/
595 
596 #if 0 /* 28/8/08 Doesn't seem to be used */
597 
598 /* Check whether a certificate has been revoked */
599 
600 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
601 int checkRevocationOCSP( const CERT_INFO *certInfoPtr,
602  INOUT CERT_INFO *revocationInfoPtr )
603  {
604  CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
605  REVOCATION_INFO *revocationEntry = DUMMY_INIT_PTR;
606  BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
607  int certHashLength, status;
608 
609  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
610  assert( isWritePtr( revocationInfoPtr, sizeof( CERT_INFO ) ) );
611 
612  /* If there's no revocation information present then the certificate
613  can't have been revoked */
614  if( certRevInfo->revocations == NULL )
615  return( CRYPT_OK );
616 
617  /* Get the certificate hash and use it to check whether there's an entry
618  for this certificate in the list. We read the certificate hash
619  indirectly since it's computed on demand and may not have been
620  evaluated yet */
621  status = getCertComponentString( ( CERT_INFO * ) certInfoPtr,
623  certHash, CRYPT_MAX_HASHSIZE,
624  &certHashLength );
625  if( cryptStatusOK( status ) )
626  {
627  status = findRevocationEntry( certRevInfo->revocations,
628  &revocationEntry, certHash,
629  certHashLength, FALSE );
630  }
631  if( cryptStatusError( status ) )
632  {
633  /* No entry, either good or bad, we can't report anything about the
634  certificate */
635  return( status );
636  }
637  ENSURES( revocationEntry != NULL );
638 
639  /* Select the entry that contains the revocation information and return
640  the certificate's status. The unknown status is a bit difficult to
641  report, the best that we can do is report notfound although the
642  notfound occurred at the responder rather than here */
643  certRevInfo->currentRevocation = revocationEntry;
644  return( ( revocationEntry->status == CRYPT_OCSPSTATUS_NOTREVOKED ) ? \
645  CRYPT_OK : \
646  ( revocationEntry->status == CRYPT_OCSPSTATUS_REVOKED ) ? \
648  }
649 #endif /* 0 */
650 
651 /* Copy a revocation list from an OCSP request to a response */
652 
653 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
654 int copyRevocationEntries( INOUT_PTR REVOCATION_INFO **destListHeadPtrPtr,
655  const REVOCATION_INFO *srcListPtr )
656  {
657  const REVOCATION_INFO *srcListCursor;
658  REVOCATION_INFO *destListCursor = DUMMY_INIT_PTR;
659  int iterationCount;
660 
661  assert( isWritePtr( destListHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
662  assert( isReadPtr( srcListPtr, sizeof( REVOCATION_INFO ) ) );
663 
664  /* Sanity check to make sure that the destination list doesn't already
665  exist, which would cause the copy loop below to fail */
666  REQUIRES( *destListHeadPtrPtr == NULL );
667 
668  /* Copy all revocation entries from source to destination */
669  for( srcListCursor = srcListPtr, iterationCount = 0;
670  srcListCursor != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
671  srcListCursor = srcListCursor->next, iterationCount++ )
672  {
673  REVOCATION_INFO *newElement;
674 
675  /* Allocate the new entry and copy the data from the existing one
676  across. We don't copy the attributes because there aren't any
677  that should be carried from request to response */
678  if( ( newElement = ( REVOCATION_INFO * ) \
679  clAlloc( "copyRevocationEntries",
680  sizeof( REVOCATION_INFO ) + \
681  srcListCursor->idLength ) ) == NULL )
682  return( CRYPT_ERROR_MEMORY );
683  copyVarStruct( newElement, srcListCursor, REVOCATION_INFO );
684  newElement->id = newElement->value; /* Varstruct expects field name 'value' */
685  newElement->attributes = NULL;
686  newElement->next = NULL;
687 
688  /* Set the status to 'unknown' by default, this means that any
689  entries that we can't do anything with automatically get the
690  correct status associated with them */
691  newElement->status = CRYPT_OCSPSTATUS_UNKNOWN;
692 
693  /* Link the new element into the list */
694  insertSingleListElement( destListHeadPtrPtr, destListCursor,
695  newElement );
696  destListCursor = newElement;
697  }
698  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
699 
700  return( CRYPT_OK );
701  }
702 
703 /* Check the entries in an OCSP response object against a certificate store.
704  The semantics for this one are a bit odd, the source information for the
705  check is from a request but the destination information is in a response.
706  Since we don't have a copy-and-verify function we do the checking from
707  the response even though technically it's the request data that's being
708  checked */
709 
711 int checkOCSPResponse( INOUT CERT_INFO *certInfoPtr,
713  {
714  REVOCATION_INFO *revocationInfo;
715  BOOLEAN isRevoked = FALSE;
716  int iterationCount;
717 
718  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
719 
720  REQUIRES( isHandleRangeValid( iCryptKeyset ) );
721 
722  /* Walk down the list of revocation entries fetching status information
723  on each one from the certificate store */
724  for( revocationInfo = certInfoPtr->cCertRev->revocations,
725  iterationCount = 0;
726  revocationInfo != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
727  revocationInfo = revocationInfo->next, iterationCount++ )
728  {
729  CRYPT_KEYID_TYPE idType = revocationInfo->idType;
730  MESSAGE_KEYMGMT_INFO getkeyInfo;
731  CERT_INFO *crlEntryInfoPtr;
732  const BYTE *id = revocationInfo->id;
733  int status;
734 
735  REQUIRES( revocationInfo->idType == CRYPT_KEYID_NONE || \
736  revocationInfo->idType == CRYPT_IKEYID_CERTID || \
737  revocationInfo->idType == CRYPT_IKEYID_ISSUERID );
738 
739  /* If it's an OCSPv1 ID and there's no alternate ID information
740  present we can't really do anything with it because the one-way
741  hashing process required by the standard destroys the certificate
742  identifying information */
743  if( idType == CRYPT_KEYID_NONE )
744  {
745  if( revocationInfo->altIdType == CRYPT_KEYID_NONE )
746  {
747  revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
748  continue;
749  }
750 
751  /* There's an alternate ID present, use that instead */
752  idType = revocationInfo->altIdType;
753  id = revocationInfo->altID;
754  }
755 
756  /* Determine the revocation status of the object. Unfortunately
757  because of the way OCSP returns status information we can't just
758  return a yes/no response but have to perform multiple queries to
759  determine whether a certificate is not revoked, revoked, or
760  unknown. Optimising the query strategy is complicated by the
761  fact that although in theory the most common status will be
762  not-revoked we could also get a large number of status-unknown
763  queries, for example if a widely-deployed implementation which is
764  pointed at a cryptlib-based server gets its ID-hashing wrong and
765  submits huge numbers of queries with IDs that match no known
766  certificate. The best we can do is assume that a not-revoked
767  status will be the most common and if that fails fall back to a
768  revoked status check */
769  setMessageKeymgmtInfo( &getkeyInfo, idType, id, KEYID_SIZE,
770  NULL, 0, KEYMGMT_FLAG_CHECK_ONLY );
771  status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
772  &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
773  if( cryptStatusOK( status ) )
774  {
775  /* The certificate is present and not revoked/OK, we're done */
776  revocationInfo->status = CRYPT_OCSPSTATUS_NOTREVOKED;
777  continue;
778  }
779 
780  /* The certificate isn't a currently active one, if it weren't for
781  the need to return the CRL-based OCSP status values we could just
782  return not-OK now but as it is we have to differentiate between
783  revoked and unknown so we perform a second query, this time of
784  the revocation information */
785  setMessageKeymgmtInfo( &getkeyInfo, idType, id, KEYID_SIZE,
786  NULL, 0, KEYMGMT_FLAG_NONE );
787  status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
788  &getkeyInfo, KEYMGMT_ITEM_REVOCATIONINFO );
789  if( cryptStatusError( status ) )
790  {
791  /* No revocation information found, status is unknown */
792  revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
793  continue;
794  }
795 
796  /* The certificate has been revoked, copy the revocation information
797  across from the CRL entry. Error handling here gets a bit
798  complicated because we're supposed to be a (relatively) reliable
799  server, in the (highly unlikely) event that the call fails it's
800  not clear that we should abort the entire operation just because
801  we can't get the specific details for a single object (we already
802  know its overall status, which is 'revoked') so we skip reporting
803  the low-level details if there's a problem and continue */
804  status = krnlAcquireObject( getkeyInfo.cryptHandle,
806  ( void ** ) &crlEntryInfoPtr,
808  if( cryptStatusOK( status ) )
809  {
810  const REVOCATION_INFO *crlRevocationInfo;
811 
812  crlRevocationInfo = crlEntryInfoPtr->cCertRev->revocations;
813  if( crlRevocationInfo != NULL )
814  {
815  revocationInfo->revocationTime = \
816  crlRevocationInfo->revocationTime;
817  if( crlRevocationInfo->attributes != NULL )
818  {
819  /* We don't check for problems in copying the attributes
820  since bailing out at this late stage is worse than
821  missing a few obscure annotations to the revocation */
822  ( void ) copyRevocationAttributes( &revocationInfo->attributes,
823  crlRevocationInfo->attributes );
824  }
825  }
826  krnlReleaseObject( crlEntryInfoPtr->objectHandle );
827  }
829 
830  /* Record the fact that we've seen at least one revoked certificate */
831  revocationInfo->status = CRYPT_OCSPSTATUS_REVOKED;
832  isRevoked = TRUE;
833  }
834  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
835 
836  /* If at least one certificate was revoked indicate this to the caller.
837  Note that if there are multiple certificates present in the query then
838  it's up to the caller to step through the list to find out which ones
839  were revoked */
840  return( isRevoked ? CRYPT_ERROR_INVALID : CRYPT_OK );
841  }
842 
843 /****************************************************************************
844 * *
845 * Read/write OCSP Information *
846 * *
847 ****************************************************************************/
848 
849 /* Read/write an OCSP certificate ID:
850 
851  CertID ::= CHOICE {
852  certID SEQUENCE {
853  hashAlgo AlgorithmIdentifier,
854  iNameHash OCTET STRING, -- Hash of issuerName
855  iKeyHash OCTET STRING, -- Hash of issuer SPKI w/o tag+len
856  serialNo INTEGER
857  },
858  certificate [0] EXPLICIT [0] EXPLICIT Certificate,
859  certIdWithSignature
860  [1] EXPLICIT SEQUENCE {
861  iAndS IssuerAndSerialNumber,
862  tbsCertHash BIT STRING,
863  certSig SEQUENCE {
864  sigAlgo AlgorithmIdentifier,
865  sigVal BIT STRING
866  }
867  }
868  } */
869 
871 static int sizeofOcspID( const REVOCATION_INFO *ocspEntry )
872  {
873  assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
874 
875  REQUIRES( ocspEntry->idType == CRYPT_KEYID_NONE );
876 
877  /* For now we don't try and handle anything except the v1 ID since the
878  status of v2 is uncertain (it doesn't add anything to v1 except even
879  more broken IDs) */
880  return( ocspEntry->idLength );
881  }
882 
883 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
884 static int readOcspID( INOUT STREAM *stream,
885  OUT_ENUM_OPT( CRYPT_KEYID ) CRYPT_KEYID_TYPE *idType,
886  OUT_BUFFER( idMaxLen, *idLen ) BYTE *id,
887  IN_LENGTH_SHORT_MIN( 16 ) const int idMaxLen,
888  OUT_LENGTH_SHORT_Z int *idLen )
889  {
890  HASHFUNCTION_ATOMIC hashFunctionAtomic;
891  void *dataPtr = DUMMY_INIT_PTR;
892  int length, tag, status;
893 
894  assert( isWritePtr( stream, sizeof( STREAM ) ) );
895  assert( isWritePtr( idType, sizeof( CRYPT_KEYID_TYPE ) ) );
896  assert( isWritePtr( id, idMaxLen ) );
897  assert( isWritePtr( idLen, sizeof( int ) ) );
898 
899  REQUIRES( idMaxLen >= 16 && idMaxLen < MAX_INTLENGTH_SHORT );
900 
901  getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, NULL );
902 
903  /* Clear return values */
904  *idType = CRYPT_KEYID_NONE;
905  memset( id, 0, min( 16, idMaxLen ) );
906  *idLen = 0;
907 
908  /* Read the ID */
909  tag = peekTag( stream );
910  if( cryptStatusError( tag ) )
911  return( tag );
912  switch( tag )
913  {
914  case BER_SEQUENCE:
915  /* We can't really do anything with v1 IDs since the one-way
916  hashing process destroys any chance of being able to work
917  with them and the fact that no useful certificate information
918  is hashed means that we can't use them to identify a
919  certificate. As a result the following ID type will always
920  produce a result of "unknown" */
921  *idType = CRYPT_KEYID_NONE;
922  status = getStreamObjectLength( stream, &length );
923  if( cryptStatusError( status ) )
924  return( status );
925  if( length < 8 )
926  return( CRYPT_ERROR_UNDERFLOW );
927  if( length > idMaxLen )
928  return( CRYPT_ERROR_OVERFLOW );
929  *idLen = length;
930  return( sread( stream, id, length ) );
931 
933  /* Convert the certificate to a certID */
934  *idType = CRYPT_IKEYID_CERTID;
935  *idLen = KEYID_SIZE;
936  readConstructed( stream, NULL, CTAG_OI_CERTIFICATE );
937  status = readConstructed( stream, &length, 0 );
938  if( cryptStatusOK( status ) )
939  status = sMemGetDataBlock( stream, &dataPtr, length );
940  if( cryptStatusError( status ) )
941  return( status );
942  hashFunctionAtomic( id, KEYID_SIZE, dataPtr, length );
943  return( readUniversal( stream ) );
944 
946  /* A bizarro ID dreamed up by Denis Pinkas that manages to carry
947  over all the problems of the v1 ID without being compatible
948  with it. It's almost as unworkable as the v1 original but we
949  can convert the iAndS to an issuerID and use that */
950  *idType = CRYPT_IKEYID_ISSUERID;
951  *idLen = KEYID_SIZE;
952  readConstructed( stream, NULL, CTAG_OI_CERTIDWITHSIG );
953  readSequence( stream, NULL );
954  status = getStreamObjectLength( stream, &length );
955  if( cryptStatusOK( status ) )
956  status = sMemGetDataBlock( stream, &dataPtr, length );
957  if( cryptStatusError( status ) )
958  return( status );
959  hashFunctionAtomic( id, KEYID_SIZE, dataPtr, length );
960  sSkip( stream, length ); /* issuerAndSerialNumber */
961  readUniversal( stream ); /* tbsCertificateHash */
962  return( readUniversal( stream ) ); /* certSignature */
963  }
964 
965  return( CRYPT_ERROR_BADDATA );
966  }
967 
968 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
969 static int writeOcspID( INOUT STREAM *stream,
970  const REVOCATION_INFO *ocspEntry )
971  {
972  assert( isWritePtr( stream, sizeof( STREAM ) ) );
973  assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
974 
975  return( swrite( stream, ocspEntry->id, ocspEntry->idLength ) );
976  }
977 
978 /* Read/write an OCSP request entry:
979 
980  Entry ::= SEQUENCE { -- Request
981  certID CertID,
982  extensions [0] EXPLICIT Extensions OPTIONAL
983  } */
984 
986 int sizeofOcspRequestEntry( INOUT REVOCATION_INFO *ocspEntry )
987  {
988  int status;
989 
990  assert( isWritePtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
991 
992  REQUIRES( ocspEntry->idType == CRYPT_KEYID_NONE );
993 
994  /* Remember the encoded attribute size for later when we write the
995  attributes */
996  status = \
997  ocspEntry->attributeSize = sizeofAttributes( ocspEntry->attributes );
998  if( cryptStatusError( status ) )
999  return( status );
1000 
1001  return( ( int ) \
1002  sizeofObject( sizeofOcspID( ocspEntry ) + \
1003  ( ( ocspEntry->attributeSize > 0 ) ? \
1004  ( int ) \
1005  sizeofObject( \
1006  sizeofObject( ocspEntry->attributeSize ) ) : 0 ) ) );
1007  }
1008 
1009 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
1010 int readOcspRequestEntry( INOUT STREAM *stream,
1011  INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
1012  INOUT CERT_INFO *certInfoPtr )
1013  {
1014  const ATTRIBUTE_PTR *attributePtr;
1015  REVOCATION_INFO *currentEntry;
1016  STREAM certIdStream;
1017  BYTE idBuffer[ MAX_ID_SIZE + 8 ];
1018  CRYPT_KEYID_TYPE idType;
1019  void *certIdPtr;
1020  int endPos, certIdLength, length, status;
1021 
1022  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1023  assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
1024  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1025 
1026  /* Determine the overall size of the entry */
1027  status = readSequence( stream, &length );
1028  if( cryptStatusError( status ) )
1029  return( status );
1030  endPos = stell( stream ) + length;
1031 
1032  /* Read the ID information */
1033  status = readOcspID( stream, &idType, idBuffer, MAX_ID_SIZE, &length );
1034  if( cryptStatusError( status ) )
1035  return( status );
1036 
1037  /* Add the entry to the revocation information list */
1038  status = addRevocationEntry( listHeadPtrPtr, &currentEntry, idType,
1039  idBuffer, length, FALSE );
1040  if( cryptStatusError( status ) || \
1041  stell( stream ) > endPos - MIN_ATTRIBUTE_SIZE )
1042  return( status );
1043 
1044  /* Read the extensions. Since these are per-entry extensions rather
1045  than overall OCSP extensions we read the wrapper here and read the
1046  extensions themselves as CRYPT_CERTTYPE_NONE rather than
1047  CRYPT_CERTTYPE_OCSP to make sure that they're processed as required.
1048  Note that these are per-request-entry extensions rather than overall
1049  per-request extensions so the tag is CTAG_OR_SR_EXTENSIONS rather
1050  than CTAG_OR_EXTENSIONS */
1051  status = readConstructed( stream, &length, CTAG_OR_SR_EXTENSIONS );
1052  if( cryptStatusOK( status ) )
1053  {
1054  status = readAttributes( stream, &currentEntry->attributes,
1055  CRYPT_CERTTYPE_NONE, length,
1056  &certInfoPtr->errorLocus,
1057  &certInfoPtr->errorType );
1058  }
1059  if( cryptStatusError( status ) )
1060  return( status );
1061 
1062  /* OCSPv1 uses a braindamaged certificate identification method that
1063  breaks the certificate information up into bits and hashes some while
1064  leaving others intact, making it impossible to identify the
1065  certificate from it. To try and fix this, if the request includes an
1066  ESSCertID we use that to make it look like there was a proper ID
1067  present */
1068  if( currentEntry->idType != CRYPT_KEYID_NONE )
1069  return( CRYPT_OK ); /* Proper ID present, we're done */
1070  attributePtr = findAttribute( currentEntry->attributes,
1072  TRUE );
1073  if( attributePtr == NULL )
1074  return( CRYPT_OK ); /* No ESSCertID present, can't continue */
1075 
1076  /* Extract the ID information from the ESSCertID and save it alongside
1077  the OCSP ID, which we need to retain for use in the response */
1078  status = getAttributeDataPtr( attributePtr, &certIdPtr, &certIdLength );
1079  if( cryptStatusError( status ) )
1080  return( status );
1081  sMemConnect( &certIdStream, certIdPtr, certIdLength );
1082  readSequence( &certIdStream, NULL );
1083  status = readOctetString( &certIdStream, idBuffer, &length, KEYID_SIZE,
1084  KEYID_SIZE );
1085  if( cryptStatusOK( status ) )
1086  {
1087  currentEntry->altIdType = CRYPT_IKEYID_CERTID;
1088  memcpy( currentEntry->altID, idBuffer, length );
1089  }
1090  sMemDisconnect( &certIdStream );
1091 
1092  return( CRYPT_OK );
1093  }
1094 
1095 STDC_NONNULL_ARG( ( 1, 2 ) ) \
1096 int writeOcspRequestEntry( INOUT STREAM *stream,
1097  const REVOCATION_INFO *ocspEntry )
1098  {
1099  const int attributeSize = ( ocspEntry->attributeSize > 0 ) ? \
1100  ( int ) sizeofObject( \
1101  sizeofObject( ocspEntry->attributeSize ) ) : 0;
1102  int status;
1103 
1104  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1105  assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1106 
1107  /* Write the header and ID information */
1108  writeSequence( stream, sizeofOcspID( ocspEntry ) + attributeSize );
1109  status = writeOcspID( stream, ocspEntry );
1110  if( cryptStatusError( status ) || ocspEntry->attributeSize <= 0 )
1111  return( status );
1112 
1113  /* Write the per-entry extensions. Since these are per-entry extensions
1114  rather than overall OCSP extensions we write them as
1115  CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_OCSP to make sure that
1116  they're processed as required. Note that these are per-request-entry
1117  extensions rather than overall per-request extensions so the tag is
1118  CTAG_OR_SR_EXTENSIONS rather than CTAG_OR_EXTENSIONS */
1119  writeConstructed( stream, sizeofObject( ocspEntry->attributeSize ),
1121  return( writeAttributes( stream, ocspEntry->attributes,
1122  CRYPT_CERTTYPE_NONE, ocspEntry->attributeSize ) );
1123  }
1124 
1125 /* Read/write an OCSP response entry:
1126 
1127  Entry ::= SEQUENCE {
1128  certID CertID,
1129  certStatus CHOICE {
1130  notRevd [0] IMPLICIT NULL,
1131  revd [1] SEQUENCE {
1132  revTime GeneralizedTime,
1133  revReas [0] EXPLICIT CRLReason Optional
1134  },
1135  unknown [2] IMPLICIT NULL
1136  },
1137  thisUpdate GeneralizedTime,
1138  extensions [1] EXPLICIT Extensions OPTIONAL
1139  } */
1140 
1141 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
1142 int sizeofOcspResponseEntry( INOUT REVOCATION_INFO *ocspEntry )
1143  {
1144  int certStatusSize = 0, status;
1145 
1146  assert( isWritePtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1147 
1148  /* Remember the encoded attribute size for later when we write the
1149  attributes */
1150  status = \
1151  ocspEntry->attributeSize = sizeofAttributes( ocspEntry->attributes );
1152  if( cryptStatusError( status ) )
1153  return( status );
1154 
1155  /* Determine the size of the certificate status field */
1156  certStatusSize = ( ocspEntry->status != CRYPT_OCSPSTATUS_REVOKED ) ? \
1157  sizeofNull() : ( int ) sizeofObject( sizeofGeneralizedTime() );
1158 
1159  return( ( int ) \
1160  sizeofObject( sizeofOcspID( ocspEntry ) + \
1161  certStatusSize + sizeofGeneralizedTime() ) + \
1162  ( ( ocspEntry->attributeSize > 0 ) ? \
1163  ( int ) sizeofObject( ocspEntry->attributeSize ) : 0 ) );
1164  }
1165 
1166 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
1167 int readOcspResponseEntry( INOUT STREAM *stream,
1168  INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
1169  INOUT CERT_INFO *certInfoPtr )
1170  {
1171  REVOCATION_INFO *currentEntry;
1172  BYTE idBuffer[ MAX_ID_SIZE + 8 ];
1173  CRYPT_KEYID_TYPE idType;
1174  int endPos, length, crlReason = 0, tag, status;
1175 
1176  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1177  assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
1178  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
1179 
1180  /* Determine the overall size of the entry */
1181  status = readSequence( stream, &length );
1182  if( cryptStatusError( status ) )
1183  return( status );
1184  endPos = stell( stream ) + length;
1185 
1186  /* Read the ID information */
1187  status = readOcspID( stream, &idType, idBuffer, MAX_ID_SIZE, &length );
1188  if( cryptStatusError( status ) )
1189  return( status );
1190 
1191  /* Add the entry to the revocation information list */
1192  status = addRevocationEntry( listHeadPtrPtr, &currentEntry, idType,
1193  idBuffer, length, FALSE );
1194  if( cryptStatusError( status ) )
1195  return( status );
1196 
1197  /* Read the status information */
1198  status = tag = peekTag( stream );
1199  if( cryptStatusError( status ) )
1200  return( status );
1201  switch( tag )
1202  {
1204  currentEntry->status = CRYPT_OCSPSTATUS_NOTREVOKED;
1205  status = readUniversal( stream );
1206  break;
1207 
1209  currentEntry->status = CRYPT_OCSPSTATUS_REVOKED;
1210  readConstructed( stream, NULL, OCSP_STATUS_REVOKED );
1211  status = readGeneralizedTime( stream,
1212  &currentEntry->revocationTime );
1213  if( cryptStatusOK( status ) && \
1214  peekTag( stream ) == MAKE_CTAG( 0 ) )
1215  {
1216  /* Remember the crlReason for later */
1217  readConstructed( stream, NULL, 0 );
1218  status = readEnumerated( stream, &crlReason );
1219  }
1220  break;
1221 
1223  currentEntry->status = CRYPT_OCSPSTATUS_UNKNOWN;
1224  status = readUniversal( stream );
1225  break;
1226 
1227  default:
1228  return( CRYPT_ERROR_BADDATA );
1229  }
1230  if( cryptStatusError( status ) )
1231  return( status );
1232  status = readGeneralizedTime( stream, &certInfoPtr->startTime );
1233  if( cryptStatusOK( status ) && peekTag( stream ) == MAKE_CTAG( 0 ) )
1234  {
1235  readConstructed( stream, NULL, 0 );
1236  status = readGeneralizedTime( stream, &certInfoPtr->endTime );
1237  }
1238  if( cryptStatusError( status ) )
1239  return( status );
1240 
1241  /* Read the extensions if there are any present. Since these are per-
1242  entry extensions rather than overall OCSP extensions we read the
1243  wrapper here and read the extensions themselves as CRYPT_CERTTYPE_NONE
1244  rather than CRYPT_CERTTYPE_OCSP to make sure that they're processed
1245  as required */
1246  if( stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
1247  {
1248  status = readConstructed( stream, &length, CTAG_OP_EXTENSIONS );
1249  if( cryptStatusOK( status ) )
1250  {
1251  status = readAttributes( stream, &currentEntry->attributes,
1252  CRYPT_CERTTYPE_NONE, length,
1253  &certInfoPtr->errorLocus, &certInfoPtr->errorType );
1254  }
1255  if( cryptStatusError( status ) )
1256  return( status );
1257  }
1258 
1259  /* If there's a crlReason present in the response and none as an
1260  extension add it as an extension (OCSP allows the same information
1261  to be specified in two different places, to make it easier we always
1262  return it as a crlReason extension, however some implementations
1263  return it in both places so we have to make sure that we don't try and
1264  add it a second time) */
1265  if( findAttributeField( currentEntry->attributes,
1267  CRYPT_ATTRIBUTE_NONE ) == NULL )
1268  {
1269  status = addAttributeField( &currentEntry->attributes,
1271  CRYPT_ATTRIBUTE_NONE, crlReason, 0,
1272  &certInfoPtr->errorLocus,
1273  &certInfoPtr->errorType );
1274  }
1275 
1276  return( status );
1277  }
1278 
1279 STDC_NONNULL_ARG( ( 1, 2 ) ) \
1280 int writeOcspResponseEntry( INOUT STREAM *stream,
1281  const REVOCATION_INFO *ocspEntry,
1282  const time_t entryTime )
1283  {
1284  int certStatusSize, status;
1285 
1286  assert( isWritePtr( stream, sizeof( STREAM ) ) );
1287  assert( isReadPtr( ocspEntry, sizeof( REVOCATION_INFO ) ) );
1288 
1289  /* Determine the size of the certificate status field */
1290  certStatusSize = ( ocspEntry->status != CRYPT_OCSPSTATUS_REVOKED ) ? \
1291  sizeofNull() : ( int ) sizeofObject( sizeofGeneralizedTime() );
1292 
1293  /* Write the header and ID information */
1294  writeSequence( stream, sizeofOcspID( ocspEntry ) + \
1295  certStatusSize + sizeofGeneralizedTime() + \
1296  ( ( ocspEntry->attributeSize > 0 ) ? \
1297  ( int ) sizeofObject( ocspEntry->attributeSize ) : 0 ) );
1298  status = writeOcspID( stream, ocspEntry );
1299  if( cryptStatusError( status ) )
1300  return( status );
1301 
1302  /* Write the certificate status */
1303  if( ocspEntry->status == CRYPT_OCSPSTATUS_REVOKED )
1304  {
1305  writeConstructed( stream, sizeofGeneralizedTime(),
1307  writeGeneralizedTime( stream, ocspEntry->revocationTime,
1308  DEFAULT_TAG );
1309  }
1310  else
1311  {
1312  /* An other-than-revoked status is communicated as a tagged NULL
1313  value. For no known reason this portion of OCSP uses implicit
1314  tagging, since it's the one part of the PDU in which an
1315  explicit tag would actually make sense */
1316  writeNull( stream, ocspEntry->status );
1317  }
1318 
1319  /* Write the current update time, which should be the current time.
1320  Since new status information is always available we don't write a
1321  nextUpdate time (in fact there is some disagreement over whether
1322  these times are based on CRL information, responder information, the
1323  response dispatch time, or a mixture of the above, implementations
1324  can be found that return all manner of peculiar values here) */
1325  status = writeGeneralizedTime( stream, entryTime, DEFAULT_TAG );
1326  if( cryptStatusError( status ) || ocspEntry->attributeSize <= 0 )
1327  return( status );
1328 
1329  /* Write the per-entry extensions. Since these are per-entry extensions
1330  rather than overall OCSP extensions we write them as
1331  CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_OCSP to make sure that
1332  they're processed as required */
1333  return( writeAttributes( stream, ocspEntry->attributes,
1334  CRYPT_CERTTYPE_NONE, ocspEntry->attributeSize ) );
1335  }
1336 #endif /* USE_CERTREV */