cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ext_wr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Certificate Attribute Write Routines *
4 * Copyright Peter Gutmann 1996-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "cert.h"
10  #include "certattr.h"
11  #include "asn1.h"
12  #include "asn1_ext.h"
13 #else
14  #include "cert/cert.h"
15  #include "cert/certattr.h"
16  #include "enc_dec/asn1.h"
17  #include "enc_dec/asn1_ext.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_CERTIFICATES
21 
22 /****************************************************************************
23 * *
24 * Utility Routines *
25 * *
26 ****************************************************************************/
27 
28 /* Get the encoding information needed to encode an attribute */
29 
30 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
31 static int getAttributeEncodingInfo( const ATTRIBUTE_LIST *attributeListPtr,
32  OUT_OPT_PTR \
34  OUT_INT_Z int *attributeDataSize )
35  {
36  const ATTRIBUTE_INFO *attributeInfoPtr;
37  BOOLEAN isConstructed = FALSE;
38 
39  assert( isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
40  assert( isWritePtr( attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO * ) ) );
41  assert( isWritePtr( attributeDataSize, sizeof( int ) ) );
42 
43  /* Clear return value */
44  *attributeInfoPtrPtr = NULL;
45 
46  /* If it's a constructed attribute then the encoding information for
47  the outermost wrapper will be recorded in the encoding FIFO,
48  otherwise it's present directly */
49  if( attributeListPtr->fifoEnd > 0 )
50  {
51  attributeInfoPtr = \
52  attributeListPtr->encodingFifo[ attributeListPtr->fifoEnd - 1 ];
53  isConstructed = TRUE;
54  }
55  else
56  attributeInfoPtr = attributeListPtr->attributeInfoPtr;
57  ENSURES( attributeInfoPtr != NULL );
58 
59  /* Determine the size of the attribute payload */
60  if( isConstructed && attributeInfoPtr->fieldType != FIELDTYPE_CHOICE )
61  {
62  *attributeDataSize = ( int ) sizeofObject( \
63  attributeListPtr->sizeFifo[ attributeListPtr->fifoEnd - 1 ] );
64  }
65  else
66  *attributeDataSize = attributeListPtr->encodedSize;
67  *attributeInfoPtrPtr = ( ATTRIBUTE_INFO * ) attributeInfoPtr;
68 
69  return( CRYPT_OK );
70  }
71 
72 #ifdef USE_CMSATTR
73 
74 /* When we write the attributes as a SET OF Attribute (as CMS does) we have
75  to sort them by encoded value. This is an incredible nuisance since it
76  requires that each value be encoded and stored in encoded form and then
77  the encoded forms sorted and emitted in that order. To avoid this hassle
78  we keep a record of the current lowest encoded form and then find the
79  next one by encoding enough information (the SEQUENCE and OID, CMS
80  attributes don't have critical flags) on the fly to distinguish them.
81  This is actually less overhead than storing the encoded form for sorting
82  because there are only a small total number of attributes (usually 3) and
83  we don't have to malloc() storage for each one and manage the stored form
84  if we do things on the fly */
85 
86 #define ATTR_ENCODED_SIZE ( 16 + MAX_OID_SIZE )
87 
89 static ATTRIBUTE_LIST *getNextEncodedAttribute( const ATTRIBUTE_LIST *attributeListPtr,
90  OUT_BUFFER_FIXED( prevEncodedFormLength ) \
91  BYTE *prevEncodedForm,
92  IN_LENGTH_FIXED( ATTR_ENCODED_SIZE ) \
93  const int prevEncodedFormLength )
94  {
95  const ATTRIBUTE_LIST *currentAttributeListPtr = NULL;
96  STREAM stream;
97  BYTE currentEncodedForm[ ATTR_ENCODED_SIZE + 8 ];
98  BYTE buffer[ ATTR_ENCODED_SIZE + 8 ];
99  int iterationCount, status;
100 
101  assert( isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
102  assert( isWritePtr( prevEncodedForm, prevEncodedFormLength ) );
103 
104  REQUIRES_N( prevEncodedFormLength == ATTR_ENCODED_SIZE );
105 
106  /* Give the current encoded form the maximum possible value */
107  memset( buffer, 0, ATTR_ENCODED_SIZE );
108  memset( currentEncodedForm, 0xFF, ATTR_ENCODED_SIZE );
109 
110  sMemOpen( &stream, buffer, ATTR_ENCODED_SIZE );
111 
112  /* Write the known attributes until we reach either the end of the list
113  or the first blob-type attribute */
114  for( iterationCount = 0;
115  attributeListPtr != NULL && \
116  !checkAttributeProperty( attributeListPtr,
118  iterationCount < FAILSAFE_ITERATIONS_LARGE;
119  iterationCount++ )
120  {
122  const ATTRIBUTE_INFO *attributeInfoPtr;
123  int attributeDataSize;
124 
125  /* Get the encoding information for the attribute */
126  status = getAttributeEncodingInfo( attributeListPtr,
127  ( ATTRIBUTE_INFO ** ) &attributeInfoPtr,
128  &attributeDataSize );
129  ENSURES_N( cryptStatusOK( status ) );
130  attributeID = attributeListPtr->attributeID;
131 
132  /* Write the header and OID */
133  sseek( &stream, 0 );
134  writeSequence( &stream, sizeofOID( attributeInfoPtr->oid ) + \
135  ( int ) sizeofObject( attributeDataSize ) );
136  status = swrite( &stream, attributeInfoPtr->oid,
137  sizeofOID( attributeInfoPtr->oid ) );
138  ENSURES_N( cryptStatusOK( status ) );
139 
140  /* Check to see whether this is larger than the previous value but
141  smaller than any other one that we've seen. If it is, remember
142  it. A full-length memcmp() is safe here because no encoded form
143  can be a prefix of another form so we always exit before we get
144  into the leftover data from previous encodings */
145  if( memcmp( prevEncodedForm, buffer, ATTR_ENCODED_SIZE ) < 0 && \
146  memcmp( buffer, currentEncodedForm, ATTR_ENCODED_SIZE ) < 0 )
147  {
148  memcpy( currentEncodedForm, buffer, ATTR_ENCODED_SIZE );
149  currentAttributeListPtr = attributeListPtr;
150  }
151 
152  /* Move on to the next attribute */
153  for( /* Continue iterationCount from previous loop */ ;
154  attributeListPtr != NULL && \
155  attributeListPtr->attributeID == attributeID && \
156  iterationCount < FAILSAFE_ITERATIONS_LARGE;
157  attributeListPtr = attributeListPtr->next, iterationCount++ );
158  }
159  ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_LARGE );
160 
161  /* Write the blob-type attributes */
162  for( /* Continue iterationCount from previous loop */ ;
163  attributeListPtr != NULL && \
164  iterationCount < FAILSAFE_ITERATIONS_LARGE;
165  attributeListPtr = attributeListPtr->next, iterationCount++ )
166  {
167  ENSURES_N( checkAttributeProperty( attributeListPtr,
169 
170  /* Write the header and OID */
171  sseek( &stream, 0 );
172  writeSequence( &stream, sizeofOID( attributeListPtr->oid ) + \
173  ( int ) sizeofObject( attributeListPtr->valueLength ) );
174  status = swrite( &stream, attributeListPtr->oid,
175  sizeofOID( attributeListPtr->oid ) );
176  ENSURES_N( cryptStatusOK( status ) );
177 
178  /* Check to see whether this is larger than the previous value but
179  smaller than any other one that we've seen. If it is, remember
180  it */
181  if( memcmp( prevEncodedForm, buffer, ATTR_ENCODED_SIZE ) < 0 && \
182  memcmp( buffer, currentEncodedForm, ATTR_ENCODED_SIZE ) < 0 )
183  {
184  memcpy( currentEncodedForm, buffer, ATTR_ENCODED_SIZE );
185  currentAttributeListPtr = attributeListPtr;
186  }
187  }
188  ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_LARGE );
189 
190  sMemDisconnect( &stream );
191 
192  /* Remember the encoded form of the attribute and return a pointer to
193  it */
194  memcpy( prevEncodedForm, currentEncodedForm, ATTR_ENCODED_SIZE );
195  return( ( ATTRIBUTE_LIST * ) currentAttributeListPtr );
196  }
197 #endif /* USE_CMSATTR */
198 
199 /* Determine the size of a set of attributes and validate and preprocess the
200  attribute information */
201 
202 CHECK_RETVAL \
204  {
205  const ATTRIBUTE_LIST *attributeListPtr = attributePtr;
206  int signUnrecognised, attributeSize = 0, iterationCount, status;
207 
208  assert( attributePtr == NULL || \
209  isReadPtr( attributePtr, sizeof( ATTRIBUTE_LIST ) ) );
210 
211  /* If there's nothing to write, return now */
212  if( attributeListPtr == NULL )
213  return( 0 );
214 
215  /* Determine the size of the recognised attributes */
216  for( iterationCount = 0;
217  attributeListPtr != NULL && \
218  !checkAttributeProperty( attributeListPtr,
220  iterationCount < FAILSAFE_ITERATIONS_LARGE;
221  iterationCount++ )
222  {
224  const ATTRIBUTE_INFO *attributeInfoPtr;
225  int attributeDataSize;
226 
227  /* Get the encoding information for the attribute */
228  status = getAttributeEncodingInfo( attributeListPtr,
229  ( ATTRIBUTE_INFO ** ) &attributeInfoPtr,
230  &attributeDataSize );
231  ENSURES( cryptStatusOK( status ) );
232  attributeID = attributeListPtr->attributeID;
233  attributeDataSize = ( int ) sizeofObject( attributeDataSize );
234 
235  /* Determine the overall attribute size */
236  attributeDataSize += sizeofOID( attributeInfoPtr->oid );
237  if( ( attributeInfoPtr->typeInfoFlags & FL_ATTR_CRITICAL ) || \
238  ( attributeListPtr->flags & ATTR_FLAG_CRITICAL ) )
239  attributeDataSize += sizeofBoolean();
240  attributeSize += ( int ) sizeofObject( attributeDataSize );
241 
242  /* Skip everything else in the current attribute */
243  for( /* Continue iterationCount from previous loop */ ;
244  attributeListPtr != NULL && \
245  attributeListPtr->attributeID == attributeID && \
246  iterationCount < FAILSAFE_ITERATIONS_LARGE;
247  attributeListPtr = attributeListPtr->next, iterationCount++ );
248  }
249  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
250 
251  /* If we're not going to be signing the blob-type attributes, return */
253  &signUnrecognised,
255  if( !signUnrecognised )
256  return( attributeSize );
257 
258  /* Determine the size of the blob-type attributes */
259  for( ; attributeListPtr != NULL && \
260  iterationCount < FAILSAFE_ITERATIONS_LARGE;
261  attributeListPtr = attributeListPtr->next, iterationCount++ )
262  {
263  ENSURES( checkAttributeProperty( attributeListPtr,
265 
266  attributeSize += ( int ) \
267  sizeofObject( sizeofOID( attributeListPtr->oid ) + \
268  sizeofObject( attributeListPtr->valueLength ) );
269  if( attributeListPtr->flags & ATTR_FLAG_CRITICAL )
270  attributeSize += sizeofBoolean();
271  }
272  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
273 
274  return( attributeSize );
275  }
276 
277 /****************************************************************************
278 * *
279 * Attribute/Attribute Field Write Routines *
280 * *
281 ****************************************************************************/
282 
283 /* Calculate the size of an attribute field */
284 
285 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
286 static int calculateSpecialFieldSize( const ATTRIBUTE_LIST *attributeListPtr,
287  const ATTRIBUTE_INFO *attributeInfoPtr,
288  OUT_LENGTH_SHORT_Z int *payloadSize,
289  const int fieldType )
290  {
291  assert( isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
292  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
293  assert( isWritePtr( payloadSize, sizeof( int ) ) );
294 
295  REQUIRES( isBlobField( fieldType ) || \
296  ( fieldType == FIELDTYPE_IDENTIFIER ) || \
297  ( fieldType > 0 && fieldType < MAX_TAG ) );
298 
299  /* Determine the size of the data payload */
300  *payloadSize = attributeListPtr->sizeFifo[ attributeListPtr->fifoPos ];
301 
302  /* It's a special-case field, the data size is taken from somewhere
303  other than the user-supplied data */
304  switch( fieldType )
305  {
306  case FIELDTYPE_BLOB_ANY:
309  /* Fixed-value blob (as opposed to user-supplied one) */
310  return( ( int ) attributeInfoPtr->defaultValue );
311 
313  return( sizeofOID( attributeInfoPtr->oid ) );
314 
315 #if 0 /* 28/9/08 This shouldn't ever occur, defaultValue is only used for
316  FIELDTYPE_BLOB_ANY and BOOLEAN fields */
317  case BER_INTEGER:
318  return( sizeofShortInteger( attributeInfoPtr->defaultValue ) );
319 #endif /* 0 */
320 
321  case BER_SEQUENCE:
322  case BER_SET:
323  return( ( int ) sizeofObject( *payloadSize ) );
324  }
325 
326  retIntError();
327  }
328 
329 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
330 static int calculateFieldSize( const ATTRIBUTE_LIST *attributeListPtr,
331  const ATTRIBUTE_INFO *attributeInfoPtr,
332  const int fieldType )
333  {
334  assert( isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
335  assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );
336 
337  REQUIRES( fieldType >= FIELDTYPE_TEXTSTRING && fieldType < MAX_TAG );
338  /* The default handler at the end can include fields up to
339  FIELDTYPE_TEXTSTRING */
340 
341  switch( fieldType )
342  {
343  case FIELDTYPE_BLOB_ANY:
347  return( attributeListPtr->valueLength );
348 
349  case FIELDTYPE_DN:
350  return( sizeofDN( attributeListPtr->value ) );
351 
353  return( sizeofOID( attributeInfoPtr->oid ) );
354 
355  case BER_BITSTRING:
356  return( sizeofBitString( attributeListPtr->intValue ) );
357 
358  case BER_BOOLEAN:
359  return( sizeofBoolean() );
360 
361  case BER_ENUMERATED:
362  return( sizeofEnumerated( attributeListPtr->intValue ) );
363 
364  case BER_INTEGER:
365  return( sizeofShortInteger( attributeListPtr->intValue ) );
366 
367  case BER_NULL:
368  /* This is stored as a pseudo-numeric value CRYPT_UNUSED so we
369  can't fall through to the default handler */
370  return( sizeofNull() );
371 
372  case BER_OCTETSTRING:
373  return( ( int ) sizeofObject( attributeListPtr->valueLength ) );
374 
376  return( sizeofGeneralizedTime() );
377 
378  case BER_TIME_UTC:
379  return( sizeofUTCTime() );
380  }
381 
382  return( ( int ) sizeofObject( attributeListPtr->valueLength ) );
383  }
384 
385 /* Write an attribute field */
386 
388 int writeAttributeField( INOUT_OPT STREAM *stream,
389  INOUT ATTRIBUTE_LIST *attributeListPtr,
390  IN_RANGE( 0, 4 ) const int complianceLevel )
391  {
392  const BOOLEAN isSpecial = ( attributeListPtr->fifoPos > 0 ) ? TRUE : FALSE;
393  const ATTRIBUTE_INFO *attributeInfoPtr = ( isSpecial ) ? \
394  attributeListPtr->encodingFifo[ --attributeListPtr->fifoPos ] : \
395  attributeListPtr->attributeInfoPtr;
396  const void *dataPtr = attributeListPtr->value;
397  const int fieldType = attributeInfoPtr->fieldType;
398  int tag, size, payloadSize = DUMMY_INIT;
399 
400  assert( stream == NULL || isWritePtr( stream, sizeof( STREAM ) ) );
401  assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
402 
403  REQUIRES( complianceLevel >= CRYPT_COMPLIANCELEVEL_OBLIVIOUS && \
404  complianceLevel < CRYPT_COMPLIANCELEVEL_LAST );
405 
406  /* If this is just a marker for a series of CHOICE alternatives, return
407  without doing anything */
408  if( fieldType == FIELDTYPE_CHOICE )
409  return( CRYPT_OK );
410 
411  /* Calculate the size of the encoded data */
412  if( isSpecial )
413  {
414  size = calculateSpecialFieldSize( attributeListPtr, attributeInfoPtr,
415  &payloadSize, fieldType );
416  }
417  else
418  {
419  size = calculateFieldSize( attributeListPtr, attributeInfoPtr,
420  fieldType );
421  }
422  if( cryptStatusError( size ) )
423  return( size );
424 
425  /* If we're just calculating the attribute size, don't write any data */
426  if( stream == NULL )
427  {
428  return( ( attributeInfoPtr->encodingFlags & FL_EXPLICIT ) ? \
429  ( int ) sizeofObject( size ) : size );
430  }
431 
432  /* If the field is explicitly tagged, add another layer of wrapping */
433  if( attributeInfoPtr->encodingFlags & FL_EXPLICIT )
434  writeConstructed( stream, size, attributeInfoPtr->fieldEncodedType );
435 
436  /* If the encoded field type differs from the actual field type (because
437  if implicit tagging) and we're not specifically using explicit
438  tagging and it's not a DN in a GeneralName (which is a tagged IMPLICIT
439  SEQUENCE overridden to make it EXPLICIT because of the tagged CHOICE
440  encoding rules) set the tag to the encoded field type rather than the
441  actual field type */
442  if( attributeInfoPtr->fieldEncodedType >= 0 && \
443  !( attributeInfoPtr->encodingFlags & FL_EXPLICIT ) && \
444  attributeInfoPtr->fieldType != FIELDTYPE_DN )
445  tag = attributeInfoPtr->fieldEncodedType;
446  else
447  tag = DEFAULT_TAG;
448 
449  /* Write the data as appropriate */
450  if( isSpecial )
451  {
452  /* If it's a special-case field, the data is taken from somewhere
453  other than the user-supplied data */
454  switch( fieldType )
455  {
456  case FIELDTYPE_BLOB_ANY:
459  /* Fixed-value blob (as opposed to user-supplied one) */
460  return( swrite( stream, attributeInfoPtr->extraData, size ) );
461 
463  return( swrite( stream, attributeInfoPtr->oid, size ) );
464 
465 #if 0 /* 28/9/08 See comment in calculateSpecialFieldSize() */
466  case BER_INTEGER:
467  return( writeShortInteger( stream, attributeInfoPtr->defaultValue,
468  tag ) );
469 #endif /* 0 */
470 
471  case BER_SEQUENCE:
472  case BER_SET:
473  if( tag != DEFAULT_TAG )
474  return( writeConstructed( stream, payloadSize, tag ) );
475  return( ( fieldType == BER_SET ) ? \
476  writeSet( stream, payloadSize ) : \
477  writeSequence( stream, payloadSize ) );
478  }
479 
480  retIntError();
481  }
482 
483  /* It's a standard object, take the data from the user-supplied data */
484  switch( fieldType )
485  {
486  case FIELDTYPE_BLOB_ANY:
489  if( tag != DEFAULT_TAG )
490  {
491  /* This gets a bit messy because the blob is stored in
492  encoded form in the attribute, to write it as a tagged
493  value we have to write a different first byte */
494  sputc( stream, getFieldEncodedTag( attributeInfoPtr ) );
495  return( swrite( stream, ( ( BYTE * ) dataPtr ) + 1,
496  attributeListPtr->valueLength - 1 ) );
497  }
498  return( swrite( stream, dataPtr, attributeListPtr->valueLength ) );
499 
500  case FIELDTYPE_DN:
501  return( writeDN( stream, attributeListPtr->value, tag ) );
502 
504  ENSURES( tag == DEFAULT_TAG );
505  return( swrite( stream, attributeInfoPtr->oid, size ) );
506 
508  if( tag == DEFAULT_TAG )
509  {
510  tag = ( complianceLevel >= CRYPT_COMPLIANCELEVEL_PKIX_PARTIAL ) ? \
512  }
513  return( writeCharacterString( stream, dataPtr,
514  attributeListPtr->valueLength,
515  tag ) );
516 
517  case BER_BITSTRING:
518  return( writeBitString( stream, ( int ) \
519  attributeListPtr->intValue, tag ) );
520 
521  case BER_BOOLEAN:
522  return( writeBoolean( stream, ( BOOLEAN ) \
523  attributeListPtr->intValue, tag ) );
524 
525  case BER_ENUMERATED:
526  return( writeEnumerated( stream, ( int ) \
527  attributeListPtr->intValue, tag ) );
528 
529  case BER_INTEGER:
530  return( writeShortInteger( stream, attributeListPtr->intValue,
531  tag ) );
532 
533  case BER_NULL:
534  return( writeNull( stream, tag ) );
535 
537  if( tag != DEFAULT_TAG )
538  {
539  /* This gets a bit messy because the OID is stored in
540  encoded form in the attribute, to write it as a tagged
541  value we have to write a different first byte */
542  sputc( stream, getFieldEncodedTag( attributeInfoPtr ) );
543  return( swrite( stream, ( ( BYTE * ) dataPtr ) + 1,
544  attributeListPtr->valueLength - 1 ) );
545  }
546  return( swrite( stream, dataPtr,
547  attributeListPtr->valueLength ) );
548 
549  case BER_OCTETSTRING:
550  return( writeOctetString( stream, dataPtr,
551  attributeListPtr->valueLength,
552  tag ) );
553 
554  case BER_STRING_BMP:
555  case BER_STRING_IA5:
556  case BER_STRING_ISO646:
557  case BER_STRING_NUMERIC:
559  case BER_STRING_UTF8:
560  return( writeCharacterString( stream, dataPtr,
561  attributeListPtr->valueLength,
562  ( tag == DEFAULT_TAG ) ? \
563  fieldType : \
564  MAKE_CTAG_PRIMITIVE( tag ) ) );
565 
567  return( writeGeneralizedTime( stream, *( time_t * ) dataPtr,
568  tag ) );
569 
570  case BER_TIME_UTC:
571  return( writeUTCTime( stream, *( time_t * ) dataPtr, tag ) );
572  }
573 
574  retIntError();
575  }
576 
577 /* Write an attribute */
578 
579 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
580 static int writeAttribute( INOUT STREAM *stream,
581  INOUT ATTRIBUTE_LIST **attributeListPtrPtr,
582  const BOOLEAN wrapperTagSet,
583  IN_RANGE( 0, 4 ) const int complianceLevel )
584  {
586  const ATTRIBUTE_INFO *attributeInfoPtr;
587  ATTRIBUTE_LIST *attributeListPtr = *attributeListPtrPtr;
588  BOOLEAN isCritical = FALSE;
589  int attributeDataSize, iterationCount, status;
590 
591  assert( isWritePtr( stream, sizeof( STREAM ) ) );
592  assert( isWritePtr( attributeListPtrPtr, sizeof( ATTRIBUTE_LIST * ) ) );
593  assert( isReadPtr( *attributeListPtrPtr, sizeof( ATTRIBUTE_LIST ) ) );
594 
595  REQUIRES( complianceLevel >= CRYPT_COMPLIANCELEVEL_OBLIVIOUS && \
596  complianceLevel < CRYPT_COMPLIANCELEVEL_LAST );
597 
598  /* Get the encoding information for the attribute */
599  status = getAttributeEncodingInfo( attributeListPtr,
600  ( ATTRIBUTE_INFO ** ) &attributeInfoPtr,
601  &attributeDataSize );
602  ENSURES( cryptStatusOK( status ) );
603  attributeID = attributeListPtr->attributeID;
604  if( ( attributeInfoPtr->typeInfoFlags & FL_ATTR_CRITICAL ) || \
605  ( attributeListPtr->flags & ATTR_FLAG_CRITICAL ) )
606  isCritical = TRUE;
607 
608  /* Write the outer SEQUENCE, OID, critical flag (if it's set) and
609  appropriate wrapper for the attribute payload */
610  writeSequence( stream,
611  sizeofOID( attributeInfoPtr->oid ) + \
612  ( isCritical ? sizeofBoolean() : 0 ) + \
613  ( int ) sizeofObject( attributeDataSize ) );
614  swrite( stream, attributeInfoPtr->oid,
615  sizeofOID( attributeInfoPtr->oid ) );
616  if( isCritical )
617  writeBoolean( stream, TRUE, DEFAULT_TAG );
618  if( wrapperTagSet )
619  status = writeSet( stream, attributeDataSize );
620  else
621  status = writeOctetStringHole( stream, attributeDataSize,
622  DEFAULT_TAG );
623  if( cryptStatusError( status ) )
624  return( status );
625 
626  /* Write the current attribute */
627  for( iterationCount = 0;
628  attributeListPtr != NULL && \
629  attributeListPtr->attributeID == attributeID && \
630  iterationCount < FAILSAFE_ITERATIONS_MED;
631  attributeListPtr = attributeListPtr->next, iterationCount++ )
632  {
633  int innerIterationCount;
634 
635  /* Write any encapsulating SEQUENCEs if necessary, followed by the
636  field itself. In some rare instances we may have a zero-length
637  SEQUENCE (if all the member(s) of the sequence have default
638  values) so we only try to write the member if there's encoding
639  information for it present */
640  for( attributeListPtr->fifoPos = attributeListPtr->fifoEnd, \
641  innerIterationCount = 0;
642  cryptStatusOK( status ) && \
643  attributeListPtr->fifoPos > 0 && \
644  innerIterationCount < ENCODING_FIFO_SIZE;
645  innerIterationCount++ )
646  {
647  status = writeAttributeField( stream,
648  ( ATTRIBUTE_LIST * ) attributeListPtr,
649  complianceLevel );
650  }
651  ENSURES( innerIterationCount < ENCODING_FIFO_SIZE );
652  if( cryptStatusOK( status ) && \
653  attributeListPtr->attributeInfoPtr != NULL )
654  {
655  status = writeAttributeField( stream,
656  ( ATTRIBUTE_LIST * ) attributeListPtr,
657  complianceLevel );
658  }
659  if( cryptStatusError( status ) )
660  return( status );
661  }
662  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
663 
664  *attributeListPtrPtr = attributeListPtr;
665  return( CRYPT_OK );
666  }
667 
668 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
669 static int writeBlobAttribute( INOUT STREAM *stream,
670  INOUT ATTRIBUTE_LIST **attributeListPtrPtr,
671  const BOOLEAN wrapperTagSet,
672  IN_RANGE( 0, 4 ) const int complianceLevel )
673  {
674  ATTRIBUTE_LIST *attributeListPtr = *attributeListPtrPtr;
675  const BOOLEAN isCritical = \
676  ( attributeListPtr->flags & ATTR_FLAG_CRITICAL ) ? TRUE : FALSE;
677  int status;
678 
679  /* Write the header, OID, critical flag (if present), and payload
680  wrapped up as appropriate */
681  writeSequence( stream,
682  sizeofOID( attributeListPtr->oid ) + \
683  ( isCritical ? sizeofBoolean() : 0 ) + \
684  ( int ) sizeofObject( attributeListPtr->valueLength ) );
685  swrite( stream, attributeListPtr->oid,
686  sizeofOID( attributeListPtr->oid ) );
687  if( isCritical )
688  writeBoolean( stream, TRUE, DEFAULT_TAG );
689  if( wrapperTagSet )
690  writeSet( stream, attributeListPtr->valueLength );
691  else
692  {
693  writeOctetStringHole( stream, attributeListPtr->valueLength,
694  DEFAULT_TAG );
695  }
696  status = swrite( stream, attributeListPtr->value,
697  attributeListPtr->valueLength );
698  if( cryptStatusOK( status ) )
699  *attributeListPtrPtr = attributeListPtr->next;
700  return( status );
701  }
702 
703 #ifdef USE_CMSATTR
704 
705 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
706 int writeCmsAttributes( INOUT STREAM *stream,
707  const ATTRIBUTE_LIST *attributeListPtr,
708  IN_ENUM( CRYPT_CERTTYPE ) const CRYPT_CERTTYPE_TYPE type,
709  IN_LENGTH const int attributeSize,
710  IN_RANGE( 0, 4 ) const int complianceLevel )
711  {
712  ATTRIBUTE_LIST *currentAttributePtr;
713  BYTE currentEncodedForm[ ATTR_ENCODED_SIZE + 8 ];
714  int iterationCount, status;
715 
716  assert( isWritePtr( stream, sizeof( STREAM ) ) );
717  assert( isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
718 
720  type == CRYPT_CERTTYPE_RTCS_REQUEST || \
722  REQUIRES( attributeSize > 0 && attributeSize < MAX_INTLENGTH );
723  REQUIRES( complianceLevel >= CRYPT_COMPLIANCELEVEL_OBLIVIOUS && \
724  complianceLevel < CRYPT_COMPLIANCELEVEL_LAST );
725 
726  /* Write the wrapper, depending on the object type */
727  if( type == CRYPT_CERTTYPE_RTCS_REQUEST )
728  status = writeSet( stream, attributeSize );
729  else
730  {
731  status = writeConstructed( stream, attributeSize,
732  ( type == CRYPT_CERTTYPE_CMS_ATTRIBUTES ) ? \
735  }
736  if( cryptStatusError( status ) )
737  return( status );
738 
739  /* Write the attributes in sorted form */
740  memset( currentEncodedForm, 0, ATTR_ENCODED_SIZE ); /* Set lowest encoded form */
741  for( currentAttributePtr = getNextEncodedAttribute( attributeListPtr, \
742  currentEncodedForm,
743  ATTR_ENCODED_SIZE ),
744  iterationCount = 0;
745  currentAttributePtr != NULL && cryptStatusOK( status ) && \
746  iterationCount < FAILSAFE_ITERATIONS_LARGE;
747  currentAttributePtr = getNextEncodedAttribute( attributeListPtr,
748  currentEncodedForm,
749  ATTR_ENCODED_SIZE ),
750  iterationCount++ )
751  {
752  if( checkAttributeProperty( currentAttributePtr,
754  {
755  status = writeBlobAttribute( stream, &currentAttributePtr, TRUE,
756  complianceLevel );
757  }
758  else
759  {
760  status = writeAttribute( stream, &currentAttributePtr, TRUE,
761  complianceLevel );
762  }
763  if( cryptStatusError( status ) )
764  return( status );
765  }
766  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
767 
768  return( CRYPT_OK );
769  }
770 #endif /* USE_CMSATTR */
771 
772 /****************************************************************************
773 * *
774 * Attribute Collection Write Routines *
775 * *
776 ****************************************************************************/
777 
778 /* Write a set of attributes */
779 
780 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
781 int writeAttributes( INOUT STREAM *stream,
782  INOUT ATTRIBUTE_PTR *attributePtr,
783  IN_ENUM_OPT( CRYPT_CERTTYPE ) const CRYPT_CERTTYPE_TYPE type,
784  IN_LENGTH const int attributeSize )
785  {
786  ATTRIBUTE_LIST *attributeListPtr = attributePtr;
787  int signUnrecognised = DUMMY_INIT, complianceLevel, iterationCount;
788  int status;
789 
790  assert( isWritePtr( stream, sizeof( STREAM ) ) );
791  assert( isWritePtr( attributePtr, sizeof( ATTRIBUTE_LIST ) ) );
792 
793  REQUIRES( type >= CRYPT_CERTTYPE_NONE && type < CRYPT_CERTTYPE_LAST );
794  /* Single CRL entries have the special-case type
795  CRYPT_CERTTYPE_NONE */
796  REQUIRES( attributeSize > 0 && attributeSize < MAX_INTLENGTH );
797 
798  /* Some attributes have odd encoding/handling requirements that can
799  cause problems for other software so we only enforce peculiarities
800  required by some standards at higher compliance levels. In addition
801  we only sign unrecognised attributes if we're explicitly asked to do
802  so by the user */
804  IMESSAGE_GETATTRIBUTE, &complianceLevel,
806  if( cryptStatusOK( status ) )
807  {
809  IMESSAGE_GETATTRIBUTE, &signUnrecognised,
811  }
812  if( cryptStatusError( status ) )
813  return( status );
814 
815  /* CMS attributes work somewhat differently from normal attributes in
816  that, since they're encoded as a SET OF Attribute, they have to be
817  sorted according to their encoded form before being written. For
818  this reason we don't write them sorted by OID as with the other
819  attributes but keep writing the next-lowest attribute until they've
820  all been written */
821 #ifdef USE_CMSATTR
822  if( type == CRYPT_CERTTYPE_CMS_ATTRIBUTES || \
823  type == CRYPT_CERTTYPE_RTCS_REQUEST || \
825  {
826  return( writeCmsAttributes( stream, attributeListPtr, type,
827  attributeSize, complianceLevel ) );
828  }
829 #endif /* USE_CMSATTR */
830 
831  /* Write the appropriate extensions tag for the certificate object and
832  determine how far we can read. CRLs and OCSP requests/responses have
833  two extension types that have different tagging, per-entry extensions
834  and entire-CRL/request extensions. To differentiate between the two
835  we write per-entry extensions with a type of CRYPT_CERTTYPE_NONE */
836  switch( type )
837  {
839  case CRYPT_CERTTYPE_CRL:
840  writeConstructed( stream, ( int ) sizeofObject( attributeSize ),
841  ( type == CRYPT_CERTTYPE_CERTIFICATE ) ? \
843  status = writeSequence( stream, attributeSize );
844  break;
845 
847  writeSequence( stream, sizeofOID( OID_PKCS9_EXTREQ ) + \
848  ( int ) sizeofObject( sizeofObject( attributeSize ) ) );
849  swrite( stream, OID_PKCS9_EXTREQ, sizeofOID( OID_PKCS9_EXTREQ ) );
850  writeSet( stream, ( int ) sizeofObject( attributeSize ) );
851  status = writeSequence( stream, attributeSize );
852  break;
853 
856  /* No wrapper, extensions are written directly */
857  break;
858 
861  case CRYPT_CERTTYPE_NONE:
862  status = writeSequence( stream, attributeSize );
863  break;
864 
866  writeConstructed( stream, ( int ) sizeofObject( attributeSize ),
868  status = writeSequence( stream, attributeSize );
869  break;
870 
872  writeConstructed( stream, ( int ) sizeofObject( attributeSize ),
874  status = writeSequence( stream, attributeSize );
875  break;
876 
877  default:
878  retIntError();
879  }
880  if( cryptStatusError( status ) )
881  return( status );
882 
883  /* Write the known attributes until we reach either the end of the list
884  or the first blob-type attribute */
885  for( iterationCount = 0;
886  cryptStatusOK( status ) && attributeListPtr != NULL && \
887  !checkAttributeProperty( attributeListPtr,
889  iterationCount < FAILSAFE_ITERATIONS_LARGE;
890  iterationCount++ )
891  {
892  status = writeAttribute( stream, &attributeListPtr, FALSE,
893  complianceLevel );
894  }
895  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
896  if( cryptStatusError( status ) || !signUnrecognised )
897  return( status );
898 
899  /* Write the blob-type attributes */
900  for( iterationCount = 0;
901  attributeListPtr != NULL && cryptStatusOK( status ) && \
902  iterationCount < FAILSAFE_ITERATIONS_LARGE;
903  iterationCount++ )
904  {
905  status = writeBlobAttribute( stream, &attributeListPtr, FALSE,
906  complianceLevel );
907  }
908  ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
909  return( status );
910  }
911 #endif /* USE_CERTIFICATES */