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