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