1 /****************************************************************************
2 * *
3 * Memory Stream I/O Functions *
4 * Copyright Peter Gutmann 1993-2008 *
5 * *
6 ****************************************************************************/
7
8 #if defined( INC_ALL )
9 #include "stream_int.h"
10 #else
11 #include "io/stream_int.h"
12 #endif /* Compiler-specific includes */
13
14 /****************************************************************************
15 * *
16 * Utility Functions *
17 * *
18 ****************************************************************************/
19
20 /* Sanity-check the stream state */
21
22 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
sanityCheck(const STREAM * stream)23 static BOOLEAN sanityCheck( const STREAM *stream )
24 {
25 assert( isReadPtr( stream, sizeof( STREAM ) ) );
26
27 /* Null streams have no internal buffer so the buffer position
28 indicators aren't used */
29 if( stream->type == STREAM_TYPE_NULL )
30 {
31 /* Null streams, which act as data sinks, have a content-size
32 indicator so although the buffer size is zero the buffer
33 position values can be nonzero */
34 if( stream->bufSize != 0 )
35 return( FALSE );
36 if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd ||
37 stream->bufEnd < 0 || stream->bufEnd >= MAX_BUFFER_SIZE )
38 return( FALSE );
39
40 return( TRUE );
41 }
42
43 /* If it's not a null stream it has to be a memory stream */
44 if( stream->type != STREAM_TYPE_MEMORY )
45 return( FALSE );
46
47 /* Make sure that the buffer position is within bounds:
48
49 bufEnd
50 |
51 <------ buffer ------> v
52 +---------------------------+
53 | | |
54 +---------------------------+
55 ^ ^
56 | |
57 bufPos bufEnd */
58 if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || \
59 stream->bufEnd < 0 || stream->bufEnd > stream->bufSize || \
60 stream->bufSize <= 0 || stream->bufSize >= MAX_BUFFER_SIZE )
61 return( FALSE );
62
63 return( TRUE );
64 }
65
66 /****************************************************************************
67 * *
68 * Open/Close Functions *
69 * *
70 ****************************************************************************/
71
72 /* Initialise and shut down a memory stream. Since the return value for the
73 memory stream open functions is rarely (if ever) checked we validate the
74 buffer and length parameters later and create a read-only null stream if
75 they're invalid, so that reads and writes return error conditions if
76 they're attempted. For the same reason we just use a basic assert()
77 rather than the stronger REQUIRES() so that we can explicitly handle any
78 parameter errors later in the code */
79
80 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
initMemoryStream(OUT STREAM * stream,const BOOLEAN isNullStream)81 static int initMemoryStream( OUT STREAM *stream,
82 const BOOLEAN isNullStream )
83 {
84 /* We don't use a REQUIRES() predicate here for the reasons given in the
85 comments above */
86 assert( isWritePtr( stream, sizeof( STREAM ) ) );
87
88 /* Check that the input parameters are in order */
89 if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
90 retIntError();
91
92 /* Clear the stream data and initialise the stream structure. Further
93 initialisation of stream buffer parameters will be done by the
94 caller */
95 memset( stream, 0, sizeof( STREAM ) );
96 stream->type = ( isNullStream ) ? STREAM_TYPE_NULL : STREAM_TYPE_MEMORY;
97
98 return( CRYPT_OK );
99 }
100
101 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
checkMemoryStreamParams(INOUT STREAM * stream,IN const void * buffer,IN_LENGTH_Z const int length)102 static int checkMemoryStreamParams( INOUT STREAM *stream,
103 IN const void *buffer,
104 /* May be unintialised for sMemOpen()
105 so we can't use IN_BUFFER */
106 IN_LENGTH_Z const int length )
107 {
108 /* We don't use a REQUIRES() predicate here for the reasons given in the
109 comments above */
110 assert( isWritePtr( stream, sizeof( STREAM ) ) );
111 assert( length > 0 && length < MAX_BUFFER_SIZE );
112 assert( isReadPtr( buffer, length ) );
113
114 /* If there's a problem with the parameters, return an error code but
115 also make it a (non-readable, non-writeable) null stream with the
116 error state set via retIntError_Stream() so that it can be safely
117 used */
118 if( length < 1 || length >= MAX_BUFFER_SIZE || \
119 !isReadPtr( buffer, length ) )
120 {
121 stream->type = STREAM_TYPE_NULL;
122 stream->flags = STREAM_FLAG_READONLY;
123 retIntError_Stream( stream );
124 }
125
126 return( CRYPT_OK );
127 }
128
129 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
shutdownMemoryStream(INOUT STREAM * stream,const BOOLEAN clearStreamBuffer)130 static int shutdownMemoryStream( INOUT STREAM *stream,
131 const BOOLEAN clearStreamBuffer )
132 {
133 assert( isWritePtr( stream, sizeof( STREAM ) ) );
134
135 /* Check that the input parameters are in order */
136 if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
137 retIntError();
138
139 REQUIRES( stream->type == STREAM_TYPE_NULL || \
140 stream->type == STREAM_TYPE_MEMORY );
141
142 /* Clear the stream structure */
143 if( clearStreamBuffer && stream->buffer != NULL && stream->bufEnd > 0 )
144 zeroise( stream->buffer, stream->bufEnd );
145 zeroise( stream, sizeof( STREAM ) );
146
147 return( CRYPT_OK );
148 }
149
150 /* Open/close a memory stream or a null stream that serves as a data sink,
151 which is useful for implementing sizeof() functions by writing data to
152 null streams. If calling sMemOpenOpt() and the buffer parameter is NULL
153 and the length is zero this creates a null stream, otherwise is creates
154 a standard memory stream. This is useful for functions that follow the
155 convention of being passed a null buffer for a length check and a non-
156 null buffer to produce output.
157
158 We don't use REQUIRES() predicates for these functions for the reasons
159 given in the comments in initMemoryStream().
160
161 Note that the open/connect functions are declared with a void return
162 type, this is because they're used in hundreds of locations and the only
163 situation in which they can fail is a programming error. Because of
164 this, problems are caught by throwing exceptions in debug builds rather
165 than having to add error handling for every case where they're used. In
166 addition the functions always initialise the stream, setting it to an
167 invalid stream if there's an error, so there's no real need to check a
168 return value */
169
170 STDC_NONNULL_ARG( ( 1, 2 ) ) \
sMemOpen(OUT STREAM * stream,OUT_BUFFER_FIXED (length)void * buffer,IN_LENGTH const int length)171 void sMemOpen( OUT STREAM *stream,
172 OUT_BUFFER_FIXED( length ) void *buffer,
173 IN_LENGTH const int length )
174 {
175 int status;
176
177 /* REQUIRES() checking done in initMemoryStream() */
178 assert( isWritePtr( stream, sizeof( STREAM ) ) );
179 assert( isWritePtr( buffer, length ) );
180 assert( length > 0 && length < MAX_BUFFER_SIZE );
181
182 /* Initialise the memory stream */
183 status = initMemoryStream( stream, FALSE );
184 ENSURES_V( cryptStatusOK( status ) );
185 status = checkMemoryStreamParams( stream, buffer, length );
186 ENSURES_V( cryptStatusOK( status ) );
187 stream->buffer = buffer;
188 stream->bufSize = length;
189
190 /* Clear the stream buffer. Since this can be arbitrarily large we only
191 clear the entire buffer in the debug version */
192 #ifdef NDEBUG
193 memset( stream->buffer, 0, min( 16, stream->bufSize ) );
194 #else
195 memset( stream->buffer, 0, stream->bufSize );
196 #endif /* NDEBUG */
197 }
198
199 STDC_NONNULL_ARG( ( 1 ) ) \
sMemNullOpen(OUT STREAM * stream)200 void sMemNullOpen( OUT STREAM *stream )
201 {
202 int status;
203
204 assert( isWritePtr( stream, sizeof( STREAM ) ) );
205
206 /* Initialise the memory stream */
207 status = initMemoryStream( stream, TRUE );
208 ENSURES_V( cryptStatusOK( status ) );
209 }
210
211 STDC_NONNULL_ARG( ( 1 ) ) \
sMemOpenOpt(OUT STREAM * stream,OUT_BUFFER_OPT_FIXED (length)void * buffer,IN_LENGTH_Z const int length)212 void sMemOpenOpt( OUT STREAM *stream,
213 OUT_BUFFER_OPT_FIXED( length ) void *buffer,
214 IN_LENGTH_Z const int length )
215 {
216 assert( isWritePtr( stream, sizeof( STREAM ) ) );
217 assert( ( buffer == NULL && length == 0 ) || \
218 isReadPtr( buffer, length ) );
219
220 /* Note that the following must be given as 'buffer == NULL' without an
221 additional 'length == 0' because static-analysis tools can't make the
222 connection between 'buffer' and 'length' and will warn that the value
223 passed to sMemOpen() may be NULL */
224 if( buffer == NULL )
225 {
226 sMemNullOpen( stream );
227 return;
228 }
229 sMemOpen( stream, buffer, length );
230 }
231
232 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sMemClose(INOUT STREAM * stream)233 int sMemClose( INOUT STREAM *stream )
234 {
235 assert( isWritePtr( stream, sizeof( STREAM ) ) );
236
237 REQUIRES( sanityCheck( stream ) );
238 REQUIRES( !( stream->flags & STREAM_FLAG_READONLY ) );
239 #ifdef CONFIG_FUZZ
240 REQUIRES( !( stream->flags & STREAM_MFLAG_PSEUDO ) );
241 #endif /* CONFIG_FUZZ */
242
243 return( shutdownMemoryStream( stream, TRUE ) );
244 }
245
246 /* Connect/disconnect a memory stream without destroying the buffer
247 contents */
248
249 STDC_NONNULL_ARG( ( 1, 2 ) ) \
sMemConnect(OUT STREAM * stream,IN_BUFFER (length)const void * buffer,IN_LENGTH const int length)250 void sMemConnect( OUT STREAM *stream,
251 IN_BUFFER( length ) const void *buffer,
252 IN_LENGTH const int length )
253 {
254 int status;
255
256 assert( isWritePtr( stream, sizeof( STREAM ) ) );
257 assert( length > 0 && length < MAX_BUFFER_SIZE );
258 assert( isReadPtr( buffer, length ) );
259
260 /* Initialise the memory stream. We don't use a REQUIRES() predicate
261 for the reasons given in the comments in initMemoryStream() */
262 status = initMemoryStream( stream, FALSE );
263 ENSURES_V( cryptStatusOK( status ) );
264 status = checkMemoryStreamParams( stream, buffer, length );
265 ENSURES_V( cryptStatusOK( status ) );
266 stream->buffer = ( void * ) buffer;
267 stream->bufSize = length;
268
269 /* Initialise further portions of the stream structure. This is a read-
270 only stream so what's in the buffer at the start is all we'll ever
271 get */
272 stream->bufEnd = length;
273 stream->flags = STREAM_FLAG_READONLY;
274 }
275
276 #ifndef NDEBUG
277
278 STDC_NONNULL_ARG( ( 1, 2 ) ) \
sMemPseudoConnect(OUT STREAM * stream,IN_BUFFER (length)const void * buffer,IN_LENGTH const int length)279 void sMemPseudoConnect( OUT STREAM *stream,
280 IN_BUFFER( length ) const void *buffer,
281 IN_LENGTH const int length )
282 {
283 assert( isWritePtr( stream, sizeof( STREAM ) ) );
284 assert( length > 0 && length < MAX_BUFFER_SIZE );
285 assert( isReadPtr( buffer, length ) );
286
287 /* Open the stream as a standard memory stream */
288 sMemConnect( stream, buffer, length );
289
290 /* We've now got a standard memory stream, modify it to make it pseudo-
291 writeable, in the sense that written data is discarded (this also
292 removes the read-only flag from the standard memory stream) */
293 stream->flags = STREAM_MFLAG_PSEUDO;
294 }
295 #endif /* !NDEBUG */
296
297 RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
sMemDisconnect(INOUT STREAM * stream)298 int sMemDisconnect( INOUT STREAM *stream )
299 {
300 assert( isWritePtr( stream, sizeof( STREAM ) ) );
301
302 REQUIRES( sanityCheck( stream ) );
303
304 return( shutdownMemoryStream( stream, FALSE ) );
305 }
306
307 /****************************************************************************
308 * *
309 * Direct Access Functions *
310 * *
311 ****************************************************************************/
312
313 /* Memory stream direct-access functions, used when the contents of a memory
314 stream need to be encrypted/decrypted/signed/MACd. The basic
315 sMemGetDataBlock() returns a data block of a given size from the current
316 stream position, sMemGetDataBlockAbs() returns a data block from the
317 given stream position, and sMemGetDataBlockRemaining() returns a data
318 block containing all remaining data available in the stream */
319
320 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getMemoryBlock(INOUT STREAM * stream,OUT_BUFFER_ALLOC_OPT (length)void ** dataPtrPtr,IN_LENGTH_Z const int position,IN_DATALENGTH const int length)321 static int getMemoryBlock( INOUT STREAM *stream,
322 OUT_BUFFER_ALLOC_OPT( length ) void **dataPtrPtr,
323 IN_LENGTH_Z const int position,
324 IN_DATALENGTH const int length )
325 {
326 assert( isWritePtr( stream, sizeof( STREAM ) ) );
327 assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
328
329 /* Check that the input parameters are in order */
330 if( !isWritePtrConst( stream, sizeof( STREAM ) ) )
331 retIntError();
332
333 REQUIRES( sanityCheck( stream ) && \
334 stream->type == STREAM_TYPE_MEMORY );
335 REQUIRES_S( position >= 0 && position <= stream->bufSize && \
336 position < MAX_BUFFER_SIZE );
337 REQUIRES_S( length > 0 && length < MAX_BUFFER_SIZE );
338
339 /* Clear return value */
340 *dataPtrPtr = NULL;
341
342 /* If there's a problem with the stream don't try to do anything */
343 if( cryptStatusError( stream->status ) )
344 return( stream->status );
345
346 /* Make sure that there's enough data available in the stream to satisfy
347 the request. We check against bufSize rather than bufEnd since the
348 caller may be asking for access to all remaining data space in the
349 stream rather than just all data read/written so far */
350 if( position + length < 0 || \
351 position + length > stream->bufSize )
352 return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
353
354 /* Return a pointer to the stream-internal buffer starting at location
355 'position' of length 'length' bytes */
356 *dataPtrPtr = stream->buffer + position;
357
358 return( CRYPT_OK );
359 }
360
361 CHECK_RETVAL_RANGE_NOERROR( 0, MAX_BUFFER_SIZE ) STDC_NONNULL_ARG( ( 1 ) ) \
sMemDataLeft(const STREAM * stream)362 int sMemDataLeft( const STREAM *stream )
363 {
364 assert( isReadPtr( stream, sizeof( STREAM ) ) && \
365 stream->type == STREAM_TYPE_MEMORY );
366
367 /* Check that the input parameters are in order */
368 if( !isReadPtrConst( stream, sizeof( STREAM ) ) )
369 retIntError();
370
371 /* We can't use REQUIRES_S() in this case because the stream is a const
372 parameter so instead we return a data-left size of zero */
373 REQUIRES_EXT( ( sanityCheck( stream ) && \
374 stream->type == STREAM_TYPE_MEMORY ), 0 );
375
376 /* If there's a problem with the stream don't try to do anything.
377 Unlike the standard stream read/write functions this function simply
378 returns a record of internal stream state rather than reporting the
379 status of a stream operation, so it's not generally checked by the
380 caller. To indicate an error state the best that we can do is to
381 report zero bytes available, which will result in an underflow error
382 in the caller */
383 if( cryptStatusError( stream->status ) )
384 return( 0 );
385
386 return( stream->bufSize - stream->bufPos );
387 }
388
389 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
sMemGetDataBlock(INOUT STREAM * stream,OUT_BUFFER_ALLOC_OPT (dataSize)void ** dataPtrPtr,IN_DATALENGTH const int dataSize)390 int sMemGetDataBlock( INOUT STREAM *stream,
391 OUT_BUFFER_ALLOC_OPT( dataSize ) void **dataPtrPtr,
392 IN_DATALENGTH const int dataSize )
393 {
394 /* REQUIRES() checking done in getMemoryBlock() */
395 assert( isReadPtr( stream, sizeof( STREAM ) ) && \
396 stream->type == STREAM_TYPE_MEMORY );
397 assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
398 assert( dataSize > 0 && dataSize < MAX_BUFFER_SIZE );
399
400 /* Clear return values */
401 *dataPtrPtr = NULL;
402
403 return( getMemoryBlock( stream, dataPtrPtr, stream->bufPos, dataSize ) );
404 }
405
406 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
sMemGetDataBlockAbs(INOUT STREAM * stream,IN_DATALENGTH_Z const int position,OUT_BUFFER_ALLOC_OPT (dataSize)void ** dataPtrPtr,IN_DATALENGTH const int dataSize)407 int sMemGetDataBlockAbs( INOUT STREAM *stream,
408 IN_DATALENGTH_Z const int position,
409 OUT_BUFFER_ALLOC_OPT( dataSize ) void **dataPtrPtr,
410 IN_DATALENGTH const int dataSize )
411 {
412 /* REQUIRES() checking done in getMemoryBlock() */
413 assert( isReadPtr( stream, sizeof( STREAM ) ) && \
414 stream->type == STREAM_TYPE_MEMORY );
415 assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
416 assert( position >= 0 && position <= stream->bufSize && \
417 position < MAX_BUFFER_SIZE );
418 assert( dataSize > 0 && dataSize < MAX_BUFFER_SIZE );
419
420 /* Clear return values */
421 *dataPtrPtr = NULL;
422
423 return( getMemoryBlock( stream, dataPtrPtr, position, dataSize ) );
424 }
425
426 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
sMemGetDataBlockRemaining(INOUT STREAM * stream,OUT_BUFFER_ALLOC_OPT (* length)void ** dataPtrPtr,OUT_DATALENGTH_Z int * length)427 int sMemGetDataBlockRemaining( INOUT STREAM *stream,
428 OUT_BUFFER_ALLOC_OPT( *length ) void **dataPtrPtr,
429 OUT_DATALENGTH_Z int *length )
430 {
431 const int dataLeft = sMemDataLeft( stream );
432 int status;
433
434 assert( isReadPtr( stream, sizeof( STREAM ) ) && \
435 stream->type == STREAM_TYPE_MEMORY );
436 assert( isWritePtr( dataPtrPtr, sizeof( void * ) ) );
437 assert( isWritePtr( length, sizeof( int ) ) );
438 /* REQUIRES() checking done in getMemoryBlock() */
439
440 /* Clear return values */
441 *dataPtrPtr = NULL;
442 *length = 0;
443
444 /* If there's no data remaining, return an underflow error */
445 if( cryptStatusError( dataLeft ) )
446 return( dataLeft );
447 if( dataLeft <= 0 )
448 return( CRYPT_ERROR_UNDERFLOW );
449
450 status = getMemoryBlock( stream, dataPtrPtr, stream->bufPos, dataLeft );
451 if( cryptStatusError( status ) )
452 return( status );
453 *length = dataLeft;
454
455 return( CRYPT_OK );
456 }
457