1 /****************************************************************************
2 * *
3 * ASN.1 Write Routines *
4 * Copyright Peter Gutmann 1992-2014 *
5 * *
6 ****************************************************************************/
7
8 #if defined( INC_ALL )
9 #include "crypt.h"
10 #include "bn.h"
11 #include "asn1.h"
12 #else
13 #include "crypt.h"
14 #include "bn/bn.h"
15 #include "enc_dec/asn1.h"
16 #endif /* Compiler-specific includes */
17
18 #ifdef USE_INT_ASN1
19
20 /****************************************************************************
21 * *
22 * Utility Routines *
23 * *
24 ****************************************************************************/
25
26 /* Calculate the size of the encoded length octets */
27
28 CHECK_RETVAL_RANGE( 1, 5 ) \
calculateLengthSize(IN_LENGTH_Z const long length)29 static int calculateLengthSize( IN_LENGTH_Z const long length )
30 {
31 REQUIRES( length >= 0 && length < MAX_INTLENGTH );
32
33 /* Use the short form of the length octets if possible */
34 if( length <= 0x7F )
35 return( 1 );
36
37 /* Use the long form of the length octets, a length-of-length followed
38 by an 8, 16, 24, or 32-bit length. We order the comparisons by
39 likelihood of occurrence, shorter lengths are far more common than
40 longer ones */
41 if( length <= 0xFF )
42 return( 1 + 1 );
43 if( length <= 0xFFFFL )
44 return( 1 + 2 );
45 return( 1 + ( ( length > 0xFFFFFFL ) ? 4 : 3 ) );
46 }
47
48 /* Write the length octets for an ASN.1 item */
49
50 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeLength(INOUT STREAM * stream,IN_LENGTH_Z const long length)51 static int writeLength( INOUT STREAM *stream, IN_LENGTH_Z const long length )
52 {
53 BYTE buffer[ 8 + 8 ];
54 const int noLengthOctets = ( length <= 0xFF ) ? 1 : \
55 ( length <= 0xFFFFL ) ? 2 : \
56 ( length <= 0xFFFFFFL ) ? 3 : 4;
57 int bufPos = 1;
58
59 assert( isWritePtr( stream, sizeof( STREAM ) ) );
60
61 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH );
62
63 /* Use the short form of the length octets if possible */
64 if( length <= 0x7F )
65 return( sputc( stream, length & 0xFF ) );
66
67 /* Encode the number of length octets followed by the octets themselves */
68 buffer[ 0 ] = 0x80 | intToByte( noLengthOctets );
69 if( noLengthOctets > 3 )
70 buffer[ bufPos++ ] = ( length >> 24 ) & 0xFF;
71 if( noLengthOctets > 2 )
72 buffer[ bufPos++ ] = ( length >> 16 ) & 0xFF;
73 if( noLengthOctets > 1 )
74 buffer[ bufPos++ ] = ( length >> 8 ) & 0xFF;
75 buffer[ bufPos++ ] = length & 0xFF;
76 return( swrite( stream, buffer, bufPos ) );
77 }
78
79 /* Write a (non-bignum) numeric value, used by several routines. The
80 easiest way to do this is to encode the bytes starting from the LSB
81 and then output them in reverse order to get a big-endian encoding */
82
83 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeNumeric(INOUT STREAM * stream,IN_INT const long integer)84 static int writeNumeric( INOUT STREAM *stream, IN_INT const long integer )
85 {
86 BYTE buffer[ 16 + 8 ];
87 long intValue = integer;
88 int length = 0, i, iterationCount;
89
90 assert( isWritePtr( stream, sizeof( STREAM ) ) );
91
92 REQUIRES_S( integer >= 0 && integer < MAX_INTLENGTH );
93
94 /* The value 0 is handled specially */
95 if( intValue == 0 )
96 return( swrite( stream, "\x01\x00", 2 ) );
97
98 /* Assemble the encoded value in little-endian order */
99 if( intValue > 0 )
100 {
101 for( iterationCount = 0;
102 intValue > 0 && iterationCount < FAILSAFE_ITERATIONS_SMALL;
103 iterationCount++ )
104 {
105 buffer[ length++ ] = intValue & 0xFF;
106 intValue >>= 8;
107 }
108 ENSURES( iterationCount < FAILSAFE_ITERATIONS_SMALL );
109
110 /* Make sure that we don't inadvertently set the sign bit if the
111 high bit of the value is set */
112 if( buffer[ length - 1 ] & 0x80 )
113 buffer[ length++ ] = 0x00;
114 }
115 else
116 {
117 /* Write a negative integer values. This code is never executed
118 (and is actually checked for by the precondition at the start of
119 this function), it's present only in case it's ever needed in the
120 future */
121 iterationCount = 0;
122 do
123 {
124 buffer[ length++ ] = intValue & 0xFF;
125 intValue >>= 8;
126 }
127 while( intValue != -1 && length < sizeof( int ) && \
128 iterationCount++ < FAILSAFE_ITERATIONS_SMALL );
129 ENSURES( iterationCount < FAILSAFE_ITERATIONS_SMALL );
130
131 /* Make sure that we don't inadvertently clear the sign bit if the
132 high bit of the value is clear */
133 if( !( buffer[ length - 1 ] & 0x80 ) )
134 buffer[ length++ ] = 0xFF;
135 }
136 ENSURES( length > 0 && length <= 8 );
137
138 /* Output the value in reverse (big-endian) order */
139 sputc( stream, length );
140 for( i = length - 1; i > 0; i-- )
141 sputc( stream, buffer[ i ] );
142 return( sputc( stream, buffer[ 0 ] ) );
143 }
144
145 /****************************************************************************
146 * *
147 * Sizeof Routines *
148 * *
149 ****************************************************************************/
150
151 /* Determine the encoded size of an object given only a length. This
152 function is a bit problematic because it's frequently called as part
153 of a complex expression, where in theory it should never be passed a
154 negative value but due to some sort of exceptional circumstances may
155 end up being passed one. Since this is a can't-occur condition, we
156 don't want to go overboard with checking for it (it would require having
157 to check the return value of every single use of sizeofObject() within a
158 complex expression), but also need some means of being able to cope with
159 it. To deal with this we always return a safe length of zero on error */
160
161 RETVAL_LENGTH_NOERROR \
sizeofObject(IN_LENGTH_Z const long length)162 long sizeofObject( IN_LENGTH_Z const long length )
163 {
164 /* If we've been passed an error code as input or we're about to exceed
165 the maximum safe length range, don't try and go any further */
166 if( length < 0 || length > MAX_INTLENGTH - 16 )
167 {
168 DEBUG_DIAG( ( "Invalid value passed to sizeofObject()" ) );
169 assert( DEBUG_WARN );
170 return( 0 );
171 }
172
173 return( 1 + calculateLengthSize( length ) + length );
174 }
175
176 #ifdef USE_PKC
177
178 /* Determine the size of a bignum. When we're writing these we can't use
179 sizeofObject() directly because the internal representation is unsigned
180 whereas the encoded form is signed */
181
182 RETVAL_RANGE_NOERROR( 0, MAX_INTLENGTH_SHORT ) STDC_NONNULL_ARG( ( 1 ) ) \
signedBignumSize(IN TYPECAST (BIGNUM *)const void * bignum)183 int signedBignumSize( IN TYPECAST( BIGNUM * ) const void *bignum )
184 {
185 const int length = BN_num_bytes( bignum );
186
187 assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );
188
189 /* The output from this function is typically used in calculations
190 involving multiple bignums, for which it doesn't make much sense to
191 individually check the return value of each function call for a
192 condition that can only be caused by an internal error, so we throw
193 an exception in debug mode but otherwise convert the condition to
194 a no-op length value */
195 if( cryptStatusError( length ) )
196 retIntError_Ext( 0 );
197
198 /* Return the bignum length plus a leading zero byte if the high bit is
199 set */
200 return( length + ( ( BN_high_bit( ( BIGNUM * ) bignum ) ) ? 1 : 0 ) );
201 }
202 #endif /* USE_PKC */
203
204 /****************************************************************************
205 * *
206 * Write Routines for Primitive Objects *
207 * *
208 ****************************************************************************/
209
210 /* Write a short/large/bignum integer value */
211
212 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeShortInteger(INOUT STREAM * stream,IN_INT_Z const long integer,IN_TAG const int tag)213 int writeShortInteger( INOUT STREAM *stream,
214 IN_INT_Z const long integer,
215 IN_TAG const int tag )
216 {
217 assert( isWritePtr( stream, sizeof( STREAM ) ) );
218
219 REQUIRES_S( integer >= 0 && integer < MAX_INTLENGTH );
220 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
221
222 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
223 BER_INTEGER : MAKE_CTAG_PRIMITIVE( tag ) );
224 return( writeNumeric( stream, integer ) );
225 }
226
227 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeInteger(INOUT STREAM * stream,IN_BUFFER (integerLength)const BYTE * integer,IN_LENGTH_SHORT const int integerLength,IN_TAG const int tag)228 int writeInteger( INOUT STREAM *stream,
229 IN_BUFFER( integerLength ) const BYTE *integer,
230 IN_LENGTH_SHORT const int integerLength,
231 IN_TAG const int tag )
232 {
233 const BOOLEAN leadingZero = ( integerLength > 0 && \
234 ( *integer & 0x80 ) ) ? 1 : 0;
235
236 assert( isWritePtr( stream, sizeof( STREAM ) ) );
237 assert( isReadPtr( integer, integerLength ) );
238
239 REQUIRES_S( integerLength >= 0 && integerLength < MAX_INTLENGTH_SHORT );
240 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
241
242 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
243 BER_INTEGER : MAKE_CTAG_PRIMITIVE( tag ) );
244 writeLength( stream, integerLength + leadingZero );
245 if( leadingZero )
246 sputc( stream, 0 );
247 return( swrite( stream, integer, integerLength ) );
248 }
249
250 #ifdef USE_PKC
251
252 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeBignumTag(INOUT STREAM * stream,IN TYPECAST (BIGNUM *)const void * bignum,IN_TAG const int tag)253 int writeBignumTag( INOUT STREAM *stream,
254 IN TYPECAST( BIGNUM * ) const void *bignum,
255 IN_TAG const int tag )
256 {
257 BYTE buffer[ CRYPT_MAX_PKCSIZE + 8 ];
258 int length, status;
259
260 assert( isWritePtr( stream, sizeof( STREAM ) ) );
261 assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );
262
263 REQUIRES_S( !BN_is_zero( ( BIGNUM * ) bignum ) );
264 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
265
266 /* If it's a dummy write, don't go through the full encoding process.
267 This optimisation both speeds things up and reduces unnecessary
268 writing of key data to memory */
269 if( sIsNullStream( stream ) )
270 {
271 return( sSkip( stream, sizeofBignum( bignum ),
272 MAX_INTLENGTH_SHORT ) );
273 }
274
275 status = exportBignum( buffer, CRYPT_MAX_PKCSIZE, &length, bignum );
276 if( cryptStatusError( status ) )
277 retIntError_Stream( stream );
278 status = writeInteger( stream, buffer, length, tag );
279 zeroise( buffer, CRYPT_MAX_PKCSIZE );
280 return( status );
281 }
282 #endif /* USE_PKC */
283
284 /* Write an enumerated value */
285
286 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
287 int writeEnumerated( INOUT STREAM *stream,
288 IN_RANGE( 0, 999 ) const int enumerated,
289 IN_TAG const int tag )
290 {
291 assert( isWritePtr( stream, sizeof( STREAM ) ) );
292
293 REQUIRES_S( enumerated >= 0 && enumerated < 1000 );
294 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
295
296 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
297 BER_ENUMERATED : MAKE_CTAG_PRIMITIVE( tag ) );
298 return( writeNumeric( stream, ( long ) enumerated ) );
299 }
300
301 /* Write a null value */
302
303 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeNull(INOUT STREAM * stream,IN_TAG const int tag)304 int writeNull( INOUT STREAM *stream, IN_TAG const int tag )
305 {
306 BYTE buffer[ 8 + 8 ];
307
308 assert( isWritePtr( stream, sizeof( STREAM ) ) );
309
310 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
311
312 buffer[ 0 ] = ( tag == DEFAULT_TAG ) ? \
313 BER_NULL : intToByte( MAKE_CTAG_PRIMITIVE( tag ) );
314 buffer[ 1 ] = 0;
315 return( swrite( stream, buffer, 2 ) );
316 }
317
318 /* Write a boolean value */
319
320 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeBoolean(INOUT STREAM * stream,const BOOLEAN boolean,IN_TAG const int tag)321 int writeBoolean( INOUT STREAM *stream, const BOOLEAN boolean,
322 IN_TAG const int tag )
323 {
324 BYTE buffer[ 8 + 8 ];
325
326 assert( isWritePtr( stream, sizeof( STREAM ) ) );
327
328 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
329
330 buffer[ 0 ] = ( tag == DEFAULT_TAG ) ? \
331 BER_BOOLEAN : intToByte( MAKE_CTAG_PRIMITIVE( tag ) );
332 buffer[ 1 ] = 1;
333 buffer[ 2 ] = boolean ? 0xFF : 0;
334 return( swrite( stream, buffer, 3 ) );
335 }
336
337 /* Write an octet string */
338
339 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeOctetString(INOUT STREAM * stream,IN_BUFFER (length)const BYTE * string,IN_LENGTH_SHORT const int length,IN_TAG const int tag)340 int writeOctetString( INOUT STREAM *stream,
341 IN_BUFFER( length ) const BYTE *string,
342 IN_LENGTH_SHORT const int length,
343 IN_TAG const int tag )
344 {
345 assert( isWritePtr( stream, sizeof( STREAM ) ) );
346 assert( isReadPtr( string, length ) );
347
348 REQUIRES_S( length > 0 && length < MAX_INTLENGTH_SHORT );
349 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
350
351 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
352 BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ) );
353 writeLength( stream, length );
354 return( swrite( stream, string, length ) );
355 }
356
357 /* Write a character string. This handles any of the myriad ASN.1 character
358 string types. The handling of the tag works somewhat differently here to
359 the usual manner in that since the function is polymorphic, the tag
360 defines the character string type and is always used (there's no
361 DEFAULT_TAG like the other functions use) */
362
363 RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeCharacterString(INOUT STREAM * stream,IN_BUFFER (length)const void * string,IN_LENGTH_SHORT const int length,IN_TAG_ENCODED const int tag)364 int writeCharacterString( INOUT STREAM *stream,
365 IN_BUFFER( length ) const void *string,
366 IN_LENGTH_SHORT const int length,
367 IN_TAG_ENCODED const int tag )
368 {
369 assert( isWritePtr( stream, sizeof( STREAM ) ) );
370 assert( isReadPtr( string, length ) );
371
372 REQUIRES_S( length > 0 && length < MAX_INTLENGTH_SHORT );
373 REQUIRES_S( ( tag >= BER_STRING_UTF8 && tag <= BER_STRING_BMP ) || \
374 ( tag >= MAKE_CTAG_PRIMITIVE( 0 ) && \
375 tag <= MAKE_CTAG_PRIMITIVE( MAX_CTAG_VALUE ) ) );
376
377 writeTag( stream, tag );
378 writeLength( stream, length );
379 return( swrite( stream, string, length ) );
380 }
381
382 /* Write a bit string */
383
384 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeBitString(INOUT STREAM * stream,IN_INT_Z const int bitString,IN_TAG const int tag)385 int writeBitString( INOUT STREAM *stream, IN_INT_Z const int bitString,
386 IN_TAG const int tag )
387 {
388 BYTE buffer[ 16 + 8 ];
389 #if UINT_MAX > 0xFFFF
390 const int maxIterations = 32;
391 #else
392 const int maxIterations = 16;
393 #endif /* 16 vs.32-bit systems */
394 unsigned int value = 0;
395 int data = bitString, noBits = 0, i;
396
397 assert( isWritePtr( stream, sizeof( STREAM ) ) );
398
399 REQUIRES_S( bitString >= 0 && bitString < INT_MAX );
400 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
401
402 /* ASN.1 bitstrings start at bit 0, so we need to reverse the order of
403 the bits before we write them out */
404 for( i = 0; i < maxIterations; i++ )
405 {
406 /* Update the number of significant bits */
407 if( data > 0 )
408 noBits++;
409
410 /* Reverse the bits */
411 value <<= 1;
412 if( data & 1 )
413 value |= 1;
414 data >>= 1;
415 }
416
417 /* Write the data as an ASN.1 BITSTRING. This has the potential to lose
418 some bits on 16-bit systems, but the only place where bit strings
419 longer than one or two bytes are used is with CMP's bizarre encoding
420 of error subcodes that just provide further information above and
421 beyond the main error code and text message, and it's unlikely that
422 too many people will be running a CMP server on a DOS box */
423 buffer[ 0 ] = ( tag == DEFAULT_TAG ) ? \
424 BER_BITSTRING : intToByte( MAKE_CTAG_PRIMITIVE( tag ) );
425 buffer[ 1 ] = 1 + intToByte( ( ( noBits + 7 ) >> 3 ) );
426 buffer[ 2 ] = ~( ( noBits - 1 ) & 7 ) & 7;
427 #if UINT_MAX > 0xFFFF
428 buffer[ 3 ] = ( value >> 24 ) & 0xFF;
429 buffer[ 4 ] = ( value >> 16 ) & 0xFF;
430 buffer[ 5 ] = ( value >> 8 ) & 0xFF;
431 buffer[ 6 ] = value & 0xFF;
432 #else
433 buffer[ 3 ] = ( value >> 8 ) & 0xFF;
434 buffer[ 4 ] = value & 0xFF;
435 #endif /* 16 vs.32-bit systems */
436 return( swrite( stream, buffer, 3 + ( ( noBits + 7 ) >> 3 ) ) );
437 }
438
439 /* Write a canonical UTCTime and GeneralizedTime value */
440
441 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeTime(INOUT STREAM * stream,const time_t timeVal,IN_TAG const int tag,const BOOLEAN isUTCTime)442 static int writeTime( INOUT STREAM *stream, const time_t timeVal,
443 IN_TAG const int tag, const BOOLEAN isUTCTime )
444 {
445 struct tm timeInfo, *timeInfoPtr = &timeInfo;
446 char buffer[ 20 + 8 ];
447 const int length = isUTCTime ? 13 : 15;
448
449 assert( isWritePtr( stream, sizeof( STREAM ) ) );
450
451 REQUIRES_S( timeVal >= MIN_STORED_TIME_VALUE );
452 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
453
454 timeInfoPtr = gmTime_s( &timeVal, timeInfoPtr );
455 ENSURES_S( timeInfoPtr != NULL && timeInfoPtr->tm_year > 90 );
456 buffer[ 0 ] = ( tag != DEFAULT_TAG ) ? \
457 intToByte( MAKE_CTAG_PRIMITIVE( tag ) ) : \
458 isUTCTime ? BER_TIME_UTC : BER_TIME_GENERALIZED;
459 buffer[ 1 ] = intToByte( length );
460 if( isUTCTime )
461 {
462 sprintf_s( buffer + 2, 16, "%02d%02d%02d%02d%02d%02dZ",
463 timeInfoPtr->tm_year % 100, timeInfoPtr->tm_mon + 1,
464 timeInfoPtr->tm_mday, timeInfoPtr->tm_hour,
465 timeInfoPtr->tm_min, timeInfoPtr->tm_sec );
466 }
467 else
468 {
469 sprintf_s( buffer + 2, 16, "%04d%02d%02d%02d%02d%02dZ",
470 timeInfoPtr->tm_year + 1900, timeInfoPtr->tm_mon + 1,
471 timeInfoPtr->tm_mday, timeInfoPtr->tm_hour,
472 timeInfoPtr->tm_min, timeInfoPtr->tm_sec );
473 }
474 return( swrite( stream, buffer, length + 2 ) );
475 }
476
477 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeUTCTime(INOUT STREAM * stream,const time_t timeVal,IN_TAG const int tag)478 int writeUTCTime( INOUT STREAM *stream, const time_t timeVal,
479 IN_TAG const int tag )
480 {
481 assert( isWritePtr( stream, sizeof( STREAM ) ) );
482
483 REQUIRES_S( timeVal >= MIN_STORED_TIME_VALUE );
484 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
485
486 return( writeTime( stream, timeVal, tag, TRUE ) );
487 }
488
489 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeGeneralizedTime(INOUT STREAM * stream,const time_t timeVal,IN_TAG const int tag)490 int writeGeneralizedTime( INOUT STREAM *stream, const time_t timeVal,
491 IN_TAG const int tag )
492 {
493 assert( isWritePtr( stream, sizeof( STREAM ) ) );
494
495 REQUIRES_S( timeVal >= MIN_STORED_TIME_VALUE );
496 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
497
498 return( writeTime( stream, timeVal, tag, FALSE) );
499 }
500
501 /****************************************************************************
502 * *
503 * Write Routines for Constructed Objects *
504 * *
505 ****************************************************************************/
506
507 /* Write the start of an encapsulating SEQUENCE, SET, or generic tagged
508 constructed object. The difference between writeOctet/BitStringHole() and
509 writeGenericHole() is that the octet/bit-string versions create a normal
510 or context-specific-tagged primitive string while the generic version
511 creates a pure hole with no processing of tags */
512
513 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeSequence(INOUT STREAM * stream,IN_LENGTH_Z const int length)514 int writeSequence( INOUT STREAM *stream,
515 IN_LENGTH_Z const int length )
516 {
517 assert( isWritePtr( stream, sizeof( STREAM ) ) );
518
519 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH );
520
521 writeTag( stream, BER_SEQUENCE );
522 return( writeLength( stream, length ) );
523 }
524
525 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeSet(INOUT STREAM * stream,IN_LENGTH_SHORT_Z const int length)526 int writeSet( INOUT STREAM *stream,
527 IN_LENGTH_SHORT_Z const int length )
528 {
529 assert( isWritePtr( stream, sizeof( STREAM ) ) );
530
531 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH_SHORT );
532
533 writeTag( stream, BER_SET );
534 return( writeLength( stream, length ) );
535 }
536
537 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeConstructed(INOUT STREAM * stream,IN_LENGTH_Z const int length,IN_TAG const int tag)538 int writeConstructed( INOUT STREAM *stream,
539 IN_LENGTH_Z const int length,
540 IN_TAG const int tag )
541 {
542 assert( isWritePtr( stream, sizeof( STREAM ) ) );
543
544 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH );
545 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
546
547 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
548 BER_SEQUENCE : MAKE_CTAG( tag ) );
549 return( writeLength( stream, length ) );
550 }
551
552 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeOctetStringHole(INOUT STREAM * stream,IN_LENGTH_Z const int length,IN_TAG const int tag)553 int writeOctetStringHole( INOUT STREAM *stream,
554 IN_LENGTH_Z const int length,
555 IN_TAG const int tag )
556 {
557 assert( isWritePtr( stream, sizeof( STREAM ) ) );
558
559 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH );
560 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
561
562 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
563 BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ) );
564 return( writeLength( stream, length ) );
565 }
566
567 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeBitStringHole(INOUT STREAM * stream,IN_LENGTH_SHORT_Z const int length,IN_TAG const int tag)568 int writeBitStringHole( INOUT STREAM *stream,
569 IN_LENGTH_SHORT_Z const int length,
570 IN_TAG const int tag )
571 {
572 assert( isWritePtr( stream, sizeof( STREAM ) ) );
573
574 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH_SHORT );
575 REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
576
577 writeTag( stream, ( tag == DEFAULT_TAG ) ? \
578 BER_BITSTRING : MAKE_CTAG_PRIMITIVE( tag ) );
579 writeLength( stream, length + 1 ); /* +1 for bit count */
580 return( sputc( stream, 0 ) );
581 }
582
583 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeGenericHole(INOUT STREAM * stream,IN_LENGTH_Z const int length,IN_TAG const int tag)584 int writeGenericHole( INOUT STREAM *stream,
585 IN_LENGTH_Z const int length,
586 IN_TAG const int tag )
587 {
588 assert( isWritePtr( stream, sizeof( STREAM ) ) );
589
590 REQUIRES_S( length >= 0 && length < MAX_INTLENGTH );
591 REQUIRES_S( tag >= 0 && tag < MAX_TAG_VALUE );
592
593 writeTag( stream, tag );
594 return( writeLength( stream, length ) );
595 }
596 #endif /* USE_INT_ASN1 */
597