cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
certval.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate Validity 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 #ifdef USE_CERTVAL
19 
20 /****************************************************************************
21 * *
22 * Add/Delete/Check Validity Information *
23 * *
24 ****************************************************************************/
25 
26 /* Find an entry in a validity information list */
27 
29 static const VALIDITY_INFO *findValidityEntry( const VALIDITY_INFO *listPtr,
31  const void *value,
33  const int valueLength )
34  {
35  const int vCheck = checksumData( value, valueLength );
36  int iterationCount;
37 
38  assert( isReadPtr( listPtr, sizeof( VALIDITY_INFO ) ) );
39  assert( isReadPtr( value, valueLength ) );
40 
41  REQUIRES_N( valueLength == KEYID_SIZE );
42 
43  /* Check whether this entry is present in the list */
44  for( iterationCount = 0;
45  listPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
46  listPtr = listPtr->next, iterationCount++ )
47  {
48  if( listPtr->dCheck == vCheck && \
49  !memcmp( listPtr->data, value, valueLength ) )
50  return( listPtr );
51  }
52  ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_LARGE );
53 
54  return( NULL );
55  }
56 
57 #if 0 /* 30/6/08 Doesn't seem to be used by anything */
58 
59 /* Check whether a certificate is valid */
60 
61 static int checkValidity( const CERT_INFO *certInfoPtr,
62  CERT_INFO *validityInfoPtr )
63  {
64  CERT_VAL_INFO *certValInfo = validityInfoPtr->cCertVal;
65  VALIDITY_INFO *validityEntry;
66  BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
67  int certHashLength, status;
68 
69  assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
70  assert( isWritePtr( validityInfoPtr, sizeof( CERT_INFO ) ) );
71 
72  REQUIRES( validityInfoPtr->type == CRYPT_CERTTYPE_RTCS_RESPONSE );
73 
74  /* If there's no validity information present we can't say anything
75  about the certificate */
76  if( certValInfo->validityInfo == NULL )
77  return( CRYPT_ERROR_NOTFOUND );
78 
79  /* Get the certificate hash and use it to check whether there's an entry
80  for this certificate in the list. We read the certificate hash
81  indirectly since it's computed on demand and may not have been
82  evaluated yet */
83  status = getCertComponent( ( CERT_INFO * ) certInfoPtr,
85  certHash, CRYPT_MAX_HASHSIZE,
86  &certHashLength );
87  if( cryptStatusError( status ) )
88  return( status );
89  validityEntry = findValidityEntry( certValInfo->validityInfo,
90  certHash, certHashLength );
91  if( validityEntry == NULL )
92  return( CRYPT_ERROR_NOTFOUND );
93 
94  /* Select the entry that contains the validity information and return
95  the certificate's status */
96  certValInfo->currentValidity = validityEntry;
97  return( ( validityEntry->status == TRUE ) ? \
99  }
100 #endif /* 0 */
101 
102 /* Add an entry to a validation list */
103 
104 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
105 int addValidityEntry( INOUT_PTR VALIDITY_INFO **listHeadPtrPtr,
106  OUT_OPT_PTR_OPT VALIDITY_INFO **newEntryPosition,
107  IN_BUFFER( valueLength ) const void *value,
108  IN_LENGTH_FIXED( KEYID_SIZE ) const int valueLength )
109  {
110  VALIDITY_INFO *newElement;
111 
112  assert( isWritePtr( listHeadPtrPtr, sizeof( VALIDITY_INFO * ) ) );
113  assert( newEntryPosition == NULL || \
114  isWritePtr( newEntryPosition, sizeof( VALIDITY_INFO * ) ) );
115  assert( isReadPtr( value, valueLength ) );
116 
117  REQUIRES( valueLength == KEYID_SIZE );
118 
119  /* Clear return value */
120  if( newEntryPosition != NULL )
121  *newEntryPosition = NULL;
122 
123  /* Make sure that this entry isn't already present */
124  if( *listHeadPtrPtr != NULL && \
125  findValidityEntry( *listHeadPtrPtr, value, valueLength ) != NULL )
126  {
127  /* If we found an entry that matches the one being added, we can't
128  add it again */
129  return( CRYPT_ERROR_DUPLICATE );
130  }
131 
132  /* Allocate memory for the new element and copy the information across */
133  if( ( newElement = ( VALIDITY_INFO * ) \
134  clAlloc( "addValidityEntry", sizeof( VALIDITY_INFO ) ) ) == NULL )
135  return( CRYPT_ERROR_MEMORY );
136  memset( newElement, 0, sizeof( VALIDITY_INFO ) );
137  memcpy( newElement->data, value, valueLength );
138  newElement->dCheck = checksumData( value, valueLength );
139 
140  /* Insert the new element into the list */
141  insertSingleListElement( listHeadPtrPtr, *listHeadPtrPtr, newElement );
142  if( newEntryPosition != NULL )
143  *newEntryPosition = newElement;
144  return( CRYPT_OK );
145  }
146 
147 /* Delete a validity information list */
148 
149 STDC_NONNULL_ARG( ( 1 ) ) \
150 void deleteValidityEntries( INOUT_PTR VALIDITY_INFO **listHeadPtrPtr )
151  {
152  VALIDITY_INFO *entryListPtr = *listHeadPtrPtr;
153  int iterationCount;
154 
155  assert( isWritePtr( listHeadPtrPtr, sizeof( VALIDITY_INFO * ) ) );
156 
157  *listHeadPtrPtr = NULL;
158 
159  /* Destroy any remaining list items */
160  for( iterationCount = 0;
161  entryListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
162  iterationCount++ )
163  {
164  VALIDITY_INFO *itemToFree = entryListPtr;
165 
166  entryListPtr = entryListPtr->next;
167  if( itemToFree->attributes != NULL )
168  deleteAttributes( &itemToFree->attributes );
169  zeroise( itemToFree, sizeof( VALIDITY_INFO ) );
170  clFree( "deleteValidityEntries", itemToFree );
171  }
172  ENSURES_V( iterationCount < FAILSAFE_ITERATIONS_LARGE );
173  }
174 
175 /* Copy a validity information list */
176 
177 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
178 int copyValidityEntries( INOUT_PTR VALIDITY_INFO **destListHeadPtrPtr,
179  const VALIDITY_INFO *srcListPtr )
180  {
181  const VALIDITY_INFO *srcListCursor;
182  VALIDITY_INFO *destListCursor = DUMMY_INIT_PTR;
183  int iterationCount;
184 
185  assert( isWritePtr( destListHeadPtrPtr, sizeof( VALIDITY_INFO * ) ) );
186  assert( *destListHeadPtrPtr == NULL ); /* Dest.should be empty */
187  assert( isReadPtr( srcListPtr, sizeof( VALIDITY_INFO ) ) );
188 
189  /* Sanity check to make sure that the destination list doesn't already
190  exist, which would cause the copy loop below to fail */
191  REQUIRES( *destListHeadPtrPtr == NULL );
192 
193  /* Copy all validation entries from source to destination */
194  for( srcListCursor = srcListPtr, iterationCount = 0;
195  srcListCursor != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
196  srcListCursor = srcListCursor->next, iterationCount++ )
197  {
198  VALIDITY_INFO *newElement;
199 
200  /* Allocate the new entry and copy the data from the existing one
201  across. We don't copy the attributes because there aren't any
202  that should be carried from request to response */
203  if( ( newElement = ( VALIDITY_INFO * ) \
204  clAlloc( "copyValidityEntries", \
205  sizeof( VALIDITY_INFO ) ) ) == NULL )
206  return( CRYPT_ERROR_MEMORY );
207  memcpy( newElement, srcListCursor, sizeof( VALIDITY_INFO ) );
208  newElement->attributes = NULL;
209  newElement->next = NULL;
210 
211  /* Set the status to invalid/unknown by default, this means that any
212  entries that we can't do anything with automatically get the
213  correct status associated with them */
214  newElement->status = FALSE;
215  newElement->extStatus = CRYPT_CERTSTATUS_UNKNOWN;
216 
217  /* Link the new element into the list */
218  insertSingleListElement( destListHeadPtrPtr, destListCursor,
219  newElement );
220  destListCursor = newElement;
221  }
222  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
223 
224  return( CRYPT_OK );
225  }
226 
227 /* Prepare the entries in a certificate validity list prior to encoding
228  them */
229 
230 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 3, 4 ) ) \
231 int prepareValidityEntries( IN_OPT const VALIDITY_INFO *listPtr,
232  OUT_OPT_PTR VALIDITY_INFO **errorEntry,
233  OUT_ENUM_OPT( CRYPT_ATTRIBUTE ) \
234  CRYPT_ATTRIBUTE_TYPE *errorLocus,
235  OUT_ENUM_OPT( CRYPT_ERRTYPE ) \
236  CRYPT_ERRTYPE_TYPE *errorType )
237  {
238  const VALIDITY_INFO *validityEntry;
239  int iterationCount;
240 
241  assert( listPtr == NULL || \
242  isReadPtr( listPtr, sizeof( VALIDITY_INFO ) ) );
243  assert( isWritePtr( errorEntry, sizeof( VALIDITY_INFO * ) ) );
244  assert( isWritePtr( errorLocus, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
245  assert( isWritePtr( errorType, sizeof( CRYPT_ERRTYPE_TYPE ) ) );
246 
247  /* Clear return value */
248  *errorEntry = NULL;
249 
250  /* If the validity list is empty there's nothing to do */
251  if( listPtr == NULL )
252  return( CRYPT_OK );
253 
254  /* Check the attributes for each entry in a validation list */
255  for( validityEntry = listPtr, iterationCount = 0;
256  validityEntry != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
257  validityEntry = validityEntry->next, iterationCount++ )
258  {
259  int status;
260 
261  /* If there's nothing to check, skip this entry */
262  if( validityEntry->attributes == NULL )
263  continue;
264 
265  status = checkAttributes( ATTRIBUTE_CERTIFICATE,
266  validityEntry->attributes,
267  errorLocus, errorType );
268  if( cryptStatusError( status ) )
269  {
270  /* Remember the entry that caused the problem */
271  *errorEntry = ( VALIDITY_INFO * ) validityEntry;
272  return( status );
273  }
274  }
275  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
276 
277  return( CRYPT_OK );
278  }
279 
280 /* Check the entries in an RTCS response object against a certificate store.
281  The semantics for this one are a bit odd, the source information for the
282  check is from a request but the destination information is in a response.
283  Since we don't have a copy-and-verify function we do the checking from
284  the response even though technically it's the request data that's being
285  checked */
286 
288 int checkRTCSResponse( INOUT CERT_INFO *certInfoPtr,
290  {
291  VALIDITY_INFO *validityInfo;
292  BOOLEAN isInvalid = FALSE;
293  int iterationCount;
294 
295  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
296 
297  REQUIRES( isHandleRangeValid( iCryptKeyset ) );
298 
299  /* Walk down the list of validity entries fetching status information
300  on each one from the certificate store */
301  for( validityInfo = certInfoPtr->cCertVal->validityInfo,
302  iterationCount = 0;
303  validityInfo != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
304  validityInfo = validityInfo->next, iterationCount++ )
305  {
306  MESSAGE_KEYMGMT_INFO getkeyInfo;
307  int status;
308 
309  /* Determine the validity of the object */
310  setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_CERTID,
311  validityInfo->data, KEYID_SIZE, NULL, 0,
313  status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
314  &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
315  if( cryptStatusOK( status ) )
316  {
317  /* The certificate is present and OK */
318  validityInfo->status = TRUE;
319  validityInfo->extStatus = CRYPT_CERTSTATUS_VALID;
320  }
321  else
322  {
323  /* The certificate isn't present/OK, record the fact that we've
324  seen at least one invalid certificate */
325  validityInfo->status = FALSE;
326  validityInfo->extStatus = CRYPT_CERTSTATUS_NOTVALID;
327  isInvalid = TRUE;
328  }
329  }
330  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
331 
332  /* If at least one certificate was invalid indicate this to the caller.
333  Note that if there are multiple certificates present in the query
334  it's up to the caller to step through the list to find out which ones
335  were invalid */
336  return( isInvalid ? CRYPT_ERROR_INVALID : CRYPT_OK );
337  }
338 
339 /****************************************************************************
340 * *
341 * Read/write RTCS Information *
342 * *
343 ****************************************************************************/
344 
345 /* Read/write an RTCS resquest entry:
346 
347  Entry ::= SEQUENCE {
348  certHash OCTET STRING SIZE(20),
349  legacyID IssuerAndSerialNumber OPTIONAL
350  } */
351 
353 int sizeofRtcsRequestEntry( INOUT VALIDITY_INFO *rtcsEntry )
354  {
355  assert( isWritePtr( rtcsEntry, sizeof( VALIDITY_INFO ) ) );
356 
357  return( ( int ) sizeofObject( sizeofObject( KEYID_SIZE ) ) );
358  }
359 
360 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
361 int readRtcsRequestEntry( INOUT STREAM *stream,
362  INOUT_PTR VALIDITY_INFO **listHeadPtrPtr,
363  INOUT CERT_INFO *certInfoPtr )
364  {
365  BYTE idBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
366  int endPos, length, status;
367 
368  assert( isWritePtr( stream, sizeof( STREAM ) ) );
369  assert( isWritePtr( listHeadPtrPtr, sizeof( VALIDITY_INFO * ) ) );
370  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
371 
372  /* Determine the overall size of the entry */
373  status = readSequence( stream, &length );
374  if( cryptStatusError( status ) )
375  return( status );
376  endPos = stell( stream ) + length;
377 
378  /* Read the certificate ID and add it to the validity information list */
379  status = readOctetString( stream, idBuffer, &length,
381  if( cryptStatusOK( status ) && \
382  stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
383  {
384  /* Skip the legacy ID */
385  status = readUniversal( stream );
386  }
387  if( cryptStatusOK( status ) )
388  status = addValidityEntry( listHeadPtrPtr, NULL,
389  idBuffer, KEYID_SIZE );
390  return( status );
391  }
392 
393 STDC_NONNULL_ARG( ( 1, 2 ) ) \
394 int writeRtcsRequestEntry( INOUT STREAM *stream,
395  const VALIDITY_INFO *rtcsEntry )
396  {
397  assert( isWritePtr( stream, sizeof( STREAM ) ) );
398  assert( isReadPtr( rtcsEntry, sizeof( VALIDITY_INFO ) ) );
399 
400  /* Write the header and ID information */
401  writeSequence( stream, sizeofObject( KEYID_SIZE ) );
402  return( writeOctetString( stream, rtcsEntry->data, KEYID_SIZE,
403  DEFAULT_TAG ) );
404  }
405 
406 /* Read/write an RTCS response entry:
407 
408  Entry ::= SEQUENCE { -- Basic response
409  certHash OCTET STRING SIZE(20),
410  status BOOLEAN
411  }
412 
413  Entry ::= SEQUENCE { -- Full response
414  certHash OCTET STRING SIZE(20),
415  status ENUMERATED,
416  statusInfo ANY DEFINED BY status OPTIONAL,
417  extensions [0] Extensions OPTIONAL
418  } */
419 
421 int sizeofRtcsResponseEntry( INOUT VALIDITY_INFO *rtcsEntry,
422  const BOOLEAN isFullResponse )
423  {
424  int status;
425 
426  assert( isWritePtr( rtcsEntry, sizeof( VALIDITY_INFO ) ) );
427 
428  /* If it's a basic response the size is fairly easy to calculate */
429  if( !isFullResponse )
430  {
431  return( ( int ) sizeofObject( sizeofObject( KEYID_SIZE ) + \
432  sizeofBoolean() ) );
433  }
434 
435  /* Remember the encoded attribute size for later when we write the
436  attributes */
437  status = \
438  rtcsEntry->attributeSize = sizeofAttributes( rtcsEntry->attributes );
439  if( cryptStatusError( status ) )
440  return( status );
441 
442  return( ( int ) \
443  sizeofObject( sizeofObject( KEYID_SIZE ) + sizeofEnumerated( 1 ) + \
444  ( ( rtcsEntry->attributeSize ) ? \
445  ( int ) sizeofObject( rtcsEntry->attributeSize ) : 0 ) ) );
446  }
447 
448 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
449 int readRtcsResponseEntry( INOUT STREAM *stream,
450  INOUT_PTR VALIDITY_INFO **listHeadPtrPtr,
451  INOUT CERT_INFO *certInfoPtr,
452  const BOOLEAN isFullResponse )
453  {
454  VALIDITY_INFO *newEntry;
455  BYTE idBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
456  int endPos, length, status;
457 
458  assert( isWritePtr( stream, sizeof( STREAM ) ) );
459  assert( isWritePtr( listHeadPtrPtr, sizeof( VALIDITY_INFO * ) ) );
460  assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
461 
462  /* Determine the overall size of the entry */
463  status = readSequence( stream, &length );
464  if( cryptStatusError( status ) )
465  return( status );
466  endPos = stell( stream ) + length;
467 
468  /* Read the ID information */
469  status = readOctetString( stream, idBuffer, &length, \
471  if( cryptStatusError( status ) )
472  return( status );
473 
474  /* Add the entry to the validity information list */
475  status = addValidityEntry( listHeadPtrPtr, &newEntry,
476  idBuffer, KEYID_SIZE );
477  if( cryptStatusError( status ) )
478  return( status );
479 
480  /* Read the status information and record the valid/not-valid status */
481  if( isFullResponse )
482  {
483  status = readEnumerated( stream, &newEntry->extStatus );
484  if( cryptStatusOK( status ) )
485  {
486  newEntry->status = \
487  ( newEntry->extStatus == CRYPT_CERTSTATUS_VALID ) ? \
488  TRUE : FALSE;
489  }
490  }
491  else
492  {
493  status = readBoolean( stream, &newEntry->status );
494  if( cryptStatusOK( status ) )
495  {
496  newEntry->extStatus = newEntry->status ? \
497  CRYPT_CERTSTATUS_VALID : CRYPT_CERTSTATUS_NOTVALID;
498  }
499  }
500  if( cryptStatusError( status ) || \
501  stell( stream ) > endPos - MIN_ATTRIBUTE_SIZE )
502  return( status );
503 
504  /* Read the extensions. Since these are per-entry extensions we read
505  the wrapper here and read the extensions themselves as
506  CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_RTCS to make sure
507  that they're processed as required */
508  status = readConstructed( stream, &length, 0 );
509  if( cryptStatusError( status ) )
510  return( status );
511  return( readAttributes( stream, &newEntry->attributes,
512  CRYPT_CERTTYPE_NONE, length,
513  &certInfoPtr->errorLocus,
514  &certInfoPtr->errorType ) );
515  }
516 
517 STDC_NONNULL_ARG( ( 1, 2 ) ) \
518 int writeRtcsResponseEntry( INOUT STREAM *stream,
519  const VALIDITY_INFO *rtcsEntry,
520  const BOOLEAN isFullResponse )
521  {
522  int status;
523 
524  assert( isWritePtr( stream, sizeof( STREAM ) ) );
525  assert( isReadPtr( rtcsEntry, sizeof( VALIDITY_INFO ) ) );
526 
527  REQUIRES( rtcsEntry->extStatus >= CRYPT_CERTSTATUS_VALID && \
528  rtcsEntry->extStatus <= CRYPT_CERTSTATUS_UNKNOWN );
529 
530  /* If it's a basic response it's a straightforward fixed-length
531  object */
532  if( !isFullResponse )
533  {
534  writeSequence( stream, sizeofObject( KEYID_SIZE ) +
535  sizeofBoolean() );
536  writeOctetString( stream, rtcsEntry->data, KEYID_SIZE, DEFAULT_TAG );
537  return( writeBoolean( stream, rtcsEntry->status, DEFAULT_TAG ) );
538  }
539 
540  /* Write an extended response */
541  writeSequence( stream, sizeofObject( KEYID_SIZE ) + sizeofEnumerated( 1 ) );
542  writeOctetString( stream, rtcsEntry->data, KEYID_SIZE, DEFAULT_TAG );
543  status = writeEnumerated( stream, rtcsEntry->extStatus, DEFAULT_TAG );
544  if( cryptStatusError( status ) || rtcsEntry->attributeSize <= 0 )
545  return( status );
546 
547  /* Write the per-entry extensions. Since these are per-entry extensions
548  we write them as CRYPT_CERTTYPE_NONE rather than CRYPT_CERTTYPE_RTCS
549  to make sure that they're processed as required */
550  return( writeAttributes( stream, rtcsEntry->attributes,
551  CRYPT_CERTTYPE_NONE, rtcsEntry->attributeSize ) );
552  }
553 #endif /* USE_CERTVAL */