1 /****************************************************************************
2 *																			*
3 *						Encoded Object Query Routines						*
4 *					  Copyright Peter Gutmann 1992-2008						*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "asn1.h"
10   #include "asn1_ext.h"
11   #include "misc_rw.h"
12   #include "pgp_rw.h"
13   #include "mech.h"
14 #else
15   #include "enc_dec/asn1.h"
16   #include "enc_dec/asn1_ext.h"
17   #include "enc_dec/misc_rw.h"
18   #include "enc_dec/pgp_rw.h"
19   #include "mechs/mech.h"
20 #endif /* Compiler-specific includes */
21 
22 #ifdef USE_INT_CMS
23 
24 /****************************************************************************
25 *																			*
26 *								Utility Routines							*
27 *																			*
28 ****************************************************************************/
29 
30 /* Get information on an ASN.1 object */
31 
32 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getObjectInfo(INOUT STREAM * stream,OUT QUERY_INFO * queryInfo)33 static int getObjectInfo( INOUT STREAM *stream,
34 						  OUT QUERY_INFO *queryInfo )
35 	{
36 	const long startPos = stell( stream );
37 	long value;
38 	int tag, length, status;
39 
40 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
41 	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );
42 
43 	/* Clear return value */
44 	memset( queryInfo, 0, sizeof( QUERY_INFO ) );
45 
46 	/* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do
47 	   anything */
48 	if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE )
49 		return( CRYPT_ERROR_UNDERFLOW );
50 
51 	/* Get the object's length and make sure that its encoding is valid,
52 	   which also checks that all of the object's data is present */
53 	status = getStreamObjectLength( stream, &length );
54 	if( cryptStatusOK( status ) )
55 		{
56 		void *objectPtr;
57 
58 		status = sMemGetDataBlockAbs( stream, startPos, &objectPtr, length );
59 		if( cryptStatusOK( status ) )
60 			status = checkObjectEncoding( objectPtr, length );
61 		}
62 	if( cryptStatusError( status ) )
63 		return( status );
64 
65 	/* Get the type and version information */
66 	queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
67 	queryInfo->size = length;
68 	status = tag = peekTag( stream );
69 	if( cryptStatusError( status ) )
70 		return( status );
71 	readGenericHole( stream, NULL, 16, tag );
72 	status = readShortInteger( stream, &value );
73 	if( cryptStatusError( status ) )
74 		return( status );
75 	queryInfo->version = value;
76 	switch( tag )
77 		{
78 		case BER_SEQUENCE:
79 			/* This could be a signature or a PKC-encrypted key, see what
80 			   follows */
81 			switch( value )
82 				{
83 				case KEYTRANS_VERSION:
84 				case KEYTRANS_EX_VERSION:
85 					queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
86 					break;
87 
88 				case SIGNATURE_VERSION:
89 				case SIGNATURE_EX_VERSION:
90 					queryInfo->type = CRYPT_OBJECT_SIGNATURE;
91 					break;
92 
93 				default:
94 					return( CRYPT_ERROR_BADDATA );
95 				}
96 			if( value == KEYTRANS_VERSION || value == SIGNATURE_VERSION )
97 				queryInfo->formatType = CRYPT_FORMAT_CMS;
98 			break;
99 
100 		case MAKE_CTAG( CTAG_RI_KEYAGREE ):
101 			/* It's CMS' wierd X9.42-inspired key agreement mechanism, we
102 			   can't do much with this (mind you neither can anyone else)
103 			   so we should probably really treat it as a
104 			   CRYPT_ERROR_BADDATA if we encounter it rather than just
105 			   ignoring it */
106 			queryInfo->type = CRYPT_OBJECT_NONE;
107 			DEBUG_DIAG(( "Found keyAgreeRecipientInfo" ));
108 			assert_nofuzz( DEBUG_WARN );
109 			break;
110 
111 		case MAKE_CTAG( CTAG_RI_PWRI ):
112 			queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
113 			break;
114 
115 		default:
116 			queryInfo->type = CRYPT_OBJECT_NONE;
117 			if( tag > MAKE_CTAG( CTAG_RI_PWRI ) && \
118 				tag <= MAKE_CTAG( CTAG_RI_MAX ) )
119 				{
120 				/* This is probably a new RecipientInfo type, skip it */
121 				DEBUG_DIAG(( "Found unknown RecipientInfo %X", tag ));
122 				assert_nofuzz( DEBUG_WARN );
123 				break;
124 				}
125 			return( CRYPT_ERROR_BADDATA );
126 		}
127 
128 	/* Reset the stream for the caller before we exit */
129 	sseek( stream, startPos );
130 	return( CRYPT_OK );
131 	}
132 
133 #ifdef USE_PGP
134 
135 /* Get information on a PGP data object.  This doesn't reset the stream like
136    the ASN.1 equivalent because the PGP header is complex enough that it
137    can't be read inline like the ASN.1 header */
138 
139 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getPgpPacketInfo(INOUT STREAM * stream,OUT QUERY_INFO * queryInfo)140 int getPgpPacketInfo( INOUT STREAM *stream,
141 					  OUT QUERY_INFO *queryInfo )
142 	{
143 	const long startPos = stell( stream );
144 	long offset, length;
145 	int ctb, status;
146 
147 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
148 	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );
149 
150 	/* Clear return value */
151 	memset( queryInfo, 0, sizeof( QUERY_INFO ) );
152 
153 	/* Read the packet header and extract information from the CTB.  Note
154 	   that the assignment of version numbers is speculative only because
155 	   it's possible to use PGP 2.x packet headers to wrap up OpenPGP
156 	   packets */
157 	status = pgpReadPacketHeader( stream, &ctb, &length, 8,
158 								  MAX_INTLENGTH - 1 );
159 	if( cryptStatusError( status ) )
160 		return( status );
161 	queryInfo->formatType = CRYPT_FORMAT_PGP;
162 	queryInfo->version = pgpGetPacketVersion( ctb );
163 	status = offset = stell( stream );
164 	if( cryptStatusError( status ) )
165 		return( status );
166 	queryInfo->size = ( offset - startPos ) + length;
167 	switch( pgpGetPacketType( ctb ) )
168 		{
169 		case PGP_PACKET_SKE:
170 			queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
171 			break;
172 
173 		case PGP_PACKET_PKE:
174 			queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
175 			break;
176 
177 		case PGP_PACKET_SIGNATURE:
178 			queryInfo->type = CRYPT_OBJECT_SIGNATURE;
179 			break;
180 
181 		case PGP_PACKET_SIGNATURE_ONEPASS:
182 			/* First half of a one-pass signature, this is given a special
183 			   type of 'none' since it's not a normal packet */
184 			queryInfo->type = CRYPT_OBJECT_NONE;
185 			break;
186 
187 		default:
188 			DEBUG_DIAG(( "Found unrecognised ctb %X", ctb ));
189 			assert( DEBUG_WARN );
190 			return( CRYPT_ERROR_BADDATA );
191 		}
192 
193 	/* Make sure that all of the data is present without resetting the
194 	   stream */
195 	return( ( sMemDataLeft( stream ) < length ) ? \
196 			CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
197 	}
198 #endif /* USE_PGP */
199 
200 /****************************************************************************
201 *																			*
202 *								Object Query Routines						*
203 *																			*
204 ****************************************************************************/
205 
206 /* Low-level object query functions */
207 
208 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
queryAsn1Object(INOUT void * streamPtr,OUT QUERY_INFO * queryInfo)209 int queryAsn1Object( INOUT void *streamPtr, OUT QUERY_INFO *queryInfo )
210 	{
211 	QUERY_INFO basicQueryInfo;
212 	STREAM *stream = streamPtr;
213 	const long startPos = stell( stream );
214 	int status;
215 
216 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
217 	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );
218 
219 	/* Clear return value */
220 	memset( queryInfo, 0, sizeof( QUERY_INFO ) );
221 
222 	/* Determine basic object information.  This also verifies that all of
223 	   the object data is present in the stream */
224 	status = getObjectInfo( stream, &basicQueryInfo );
225 	if( cryptStatusError( status ) )
226 		return( status );
227 
228 	/* Call the appropriate routine to find out more about the object */
229 	switch( basicQueryInfo.type )
230 		{
231 		case CRYPT_OBJECT_ENCRYPTED_KEY:
232 			{
233 			const READKEK_FUNCTION readKekFunction = \
234 									getReadKekFunction( KEYEX_CMS );
235 
236 			if( readKekFunction == NULL )
237 				return( CRYPT_ERROR_NOTAVAIL );
238 			status = readKekFunction( stream, queryInfo );
239 			break;
240 			}
241 
242 		case CRYPT_OBJECT_PKCENCRYPTED_KEY:
243 			{
244 			const READKEYTRANS_FUNCTION readKeytransFunction = \
245 				getReadKeytransFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \
246 										 KEYEX_CMS : KEYEX_CRYPTLIB );
247 
248 			if( readKeytransFunction == NULL )
249 				return( CRYPT_ERROR_NOTAVAIL );
250 			status = readKeytransFunction( stream, queryInfo );
251 			break;
252 			}
253 
254 		case CRYPT_OBJECT_SIGNATURE:
255 			{
256 			const READSIG_FUNCTION readSigFunction = \
257 				getReadSigFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \
258 									SIGNATURE_CMS : SIGNATURE_CRYPTLIB );
259 
260 			if( readSigFunction == NULL )
261 				return( CRYPT_ERROR_NOTAVAIL );
262 			status = readSigFunction( stream, queryInfo );
263 			break;
264 			}
265 
266 		case CRYPT_OBJECT_NONE:
267 			/* New, unrecognised RecipientInfo type */
268 			status = readUniversal( stream );
269 			break;
270 
271 		default:
272 			retIntError();
273 		}
274 	sseek( stream, startPos );
275 	if( cryptStatusError( status ) )
276 		{
277 		zeroise( queryInfo, sizeof( QUERY_INFO ) );
278 		return( status );
279 		}
280 
281 	/* Augment the per-object query information with the basic query
282 	   information that we got earlier */
283 	queryInfo->formatType = basicQueryInfo.formatType;
284 	queryInfo->type = basicQueryInfo.type;
285 	queryInfo->size = basicQueryInfo.size;
286 	queryInfo->version = basicQueryInfo.version;
287 
288 	return( CRYPT_OK );
289 	}
290 
291 #ifdef USE_PGP
292 
293 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
queryPgpObject(INOUT void * streamPtr,OUT QUERY_INFO * queryInfo)294 int queryPgpObject( INOUT void *streamPtr, OUT QUERY_INFO *queryInfo )
295 	{
296 	QUERY_INFO basicQueryInfo;
297 	STREAM *stream = streamPtr;
298 	const long startPos = stell( stream );
299 	int status;
300 
301 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
302 	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );
303 
304 	/* Clear return value */
305 	memset( queryInfo, 0, sizeof( QUERY_INFO ) );
306 
307 	/* Determine basic object information.  This also verifies that all of
308 	   the object data is present in the stream */
309 	status = getPgpPacketInfo( stream, &basicQueryInfo );
310 	sseek( stream, startPos );
311 	if( cryptStatusError( status ) )
312 		return( status );
313 
314 	/* Call the appropriate routine to find out more about the object */
315 	switch( basicQueryInfo.type )
316 		{
317 		case CRYPT_OBJECT_ENCRYPTED_KEY:
318 			{
319 			const READKEK_FUNCTION readKekFunction = \
320 									getReadKekFunction( KEYEX_PGP );
321 
322 			if( readKekFunction == NULL )
323 				return( CRYPT_ERROR_NOTAVAIL );
324 			status = readKekFunction( stream, queryInfo );
325 			break;
326 			}
327 
328 		case CRYPT_OBJECT_PKCENCRYPTED_KEY:
329 			{
330 			const READKEYTRANS_FUNCTION readKeytransFunction = \
331 									getReadKeytransFunction( KEYEX_PGP );
332 
333 			if( readKeytransFunction == NULL )
334 				return( CRYPT_ERROR_NOTAVAIL );
335 			status = readKeytransFunction( stream, queryInfo );
336 			break;
337 			}
338 
339 		case CRYPT_OBJECT_SIGNATURE:
340 			{
341 			const READSIG_FUNCTION readSigFunction = \
342 									getReadSigFunction( SIGNATURE_PGP );
343 
344 			if( readSigFunction == NULL )
345 				return( CRYPT_ERROR_NOTAVAIL );
346 			status = readSigFunction( stream, queryInfo );
347 			break;
348 			}
349 
350 		case CRYPT_OBJECT_NONE:
351 			/* First half of a one-pass signature */
352 			status = readPgpOnepassSigPacket( stream, queryInfo );
353 			break;
354 
355 		default:
356 			retIntError();
357 		}
358 	sseek( stream, startPos );
359 	if( cryptStatusError( status ) )
360 		{
361 		zeroise( queryInfo, sizeof( QUERY_INFO ) );
362 		return( status );
363 		}
364 
365 	/* Augment the per-object query information with the basic query
366 	   information that we got earlier */
367 	queryInfo->formatType = basicQueryInfo.formatType;
368 	if( queryInfo->type == CRYPT_OBJECT_NONE )
369 		{
370 		/* The non-type CRYPT_OBJECT_NONE denotes the first half of a one-
371 		   pass signature packet, in which case the actual type is given in
372 		   the packet data */
373 		queryInfo->type = basicQueryInfo.type;
374 		}
375 	queryInfo->size = basicQueryInfo.size;
376 	if( queryInfo->version == 0 )
377 		{
378 		/* PGP has multiple packet version numbers sprayed all over the
379 		   place, and just because an outer version is X doesn't mean that
380 		   a subsequent inner version can't be Y.  The information is really
381 		   only used to control the formatting of what gets read, so we
382 		   just report the first version that we encounter */
383 		queryInfo->version = basicQueryInfo.version;
384 		}
385 
386 	return( CRYPT_OK );
387 	}
388 #endif /* USE_PGP */
389 
390 /****************************************************************************
391 *																			*
392 *						External Object Query Interface						*
393 *																			*
394 ****************************************************************************/
395 
396 /* Query an object.  This is just a wrapper that provides an external
397    interface for the lower-level object-query routines */
398 
399 C_CHECK_RETVAL C_NONNULL_ARG( ( 1, 3 ) ) \
cryptQueryObject(C_IN void C_PTR objectData,C_IN int objectDataLength,C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo)400 C_RET cryptQueryObject( C_IN void C_PTR objectData,
401 						C_IN int objectDataLength,
402 						C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo )
403 	{
404 	QUERY_INFO queryInfo DUMMY_INIT_STRUCT;	/* If USE_PGP undef'd */
405 	STREAM stream;
406 	int value, length = objectDataLength, status;
407 
408 	/* Perform basic error checking and clear the return value */
409 	if( objectDataLength <= MIN_CRYPT_OBJECTSIZE || \
410 		objectDataLength >= MAX_BUFFER_SIZE )
411 		return( CRYPT_ERROR_PARAM2 );
412 	if( !isReadPtr( objectData, objectDataLength ) )
413 		return( CRYPT_ERROR_PARAM1 );
414 	if( !isWritePtrConst( cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) ) )
415 		return( CRYPT_ERROR_PARAM3 );
416 	memset( cryptObjectInfo, 0, sizeof( CRYPT_OBJECT_INFO ) );
417 
418 	/* Query the object.  This is just a wrapper for the lower-level object-
419 	   query functions.  Note that we use sPeek() rather than peekTag()
420 	   because we want to continue processing (or at least checking for) PGP
421 	   data if it's no ASN.1 */
422 	sMemConnect( &stream, ( void * ) objectData, length );
423 	status = value = sPeek( &stream );
424 	if( cryptStatusError( status ) )
425 		{
426 		sMemDisconnect( &stream );
427 		return( status );
428 		}
429 	if( value == BER_SEQUENCE || value == MAKE_CTAG( CTAG_RI_PWRI ) )
430 		status = queryAsn1Object( &stream, &queryInfo );
431 	else
432 		{
433 #ifdef USE_PGP
434 		status = queryPgpObject( &stream, &queryInfo );
435 #else
436 		status = CRYPT_ERROR_BADDATA;
437 #endif /* USE_PGP */
438 		}
439 	sMemDisconnect( &stream );
440 	if( cryptStatusError( status ) )
441 		return( status );
442 
443 	/* Copy the externally-visible fields across */
444 	cryptObjectInfo->objectType = queryInfo.type;
445 	cryptObjectInfo->cryptAlgo = queryInfo.cryptAlgo;
446 	cryptObjectInfo->cryptMode = queryInfo.cryptMode;
447 	if( queryInfo.type == CRYPT_OBJECT_SIGNATURE )
448 		cryptObjectInfo->hashAlgo = queryInfo.hashAlgo;
449 	if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY && \
450 		queryInfo.saltLength > 0 )
451 		{
452 		memcpy( cryptObjectInfo->salt, queryInfo.salt, queryInfo.saltLength );
453 		cryptObjectInfo->saltSize = queryInfo.saltLength;
454 		if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE )
455 			cryptObjectInfo->hashAlgo = queryInfo.keySetupAlgo;
456 		}
457 
458 	return( CRYPT_OK );
459 	}
460 
461 #else
462 
463 /****************************************************************************
464 *																			*
465 *						Stub Functions for non-CMS/PGP Use					*
466 *																			*
467 ****************************************************************************/
468 
cryptQueryObject(C_IN void C_PTR objectData,C_IN int objectDataLength,C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo)469 C_RET cryptQueryObject( C_IN void C_PTR objectData,
470 						C_IN int objectDataLength,
471 						C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo )
472 	{
473 	UNUSED_ARG( objectData );
474 	UNUSED_ARG( cryptObjectInfo );
475 
476 	return( CRYPT_ERROR_NOTAVAIL );
477 	}
478 #endif /* USE_INT_CMS */
479