/****************************************************************************
*																			*
*						Encoded Object Query Routines						*
*					  Copyright Peter Gutmann 1992-2025						*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "asn1.h"
  #include "asn1_ext.h"
  #include "misc_rw.h"
  #include "pgp_rw.h"
  #include "mech.h"
#else
  #include "enc_dec/asn1.h"
  #include "enc_dec/asn1_ext.h"
  #include "enc_dec/misc_rw.h"
  #include "enc_dec/pgp_rw.h"
  #include "mechs/mech.h"
#endif /* Compiler-specific includes */

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

#ifdef USE_INT_CMS

/* Get information on an ASN.1 object.  This can be one of the following:

	RecipientInfo ::= CHOICE {
		keyTransRI		SEQUENCE { version = 0 || 2, ... },
		keyAgreeRI	[1]	SEQUENCE { version = 3, ... },
		kekRI		[2]	SEQUENCE { version = 4, ... },
		passwordRI	[3]	SEQUENCE { version = 0, ... },
		otherRI		[4]	SEQUENCE { OID, data },
		rfuRI		[5..7] ...			-- Reserved for future use
		}

	SignerInfo ::= SEQUENCE {
		version = 1 || 3

   If we've been given a type hint we make sure that it's one of the 
   expected types, if not we use the tag and version number to distinguish
   between RecipientInfo and SignerInfo.  Note the illogical versioning, 
   this was done because it was thought useful to distinguish the record
   types until PWRI came along at which point it was reset to a sensible
   versioning process, however what this means is that it's unclear whether
   something like a new kekRI version should be 0 (since there's only been a 
   4 previously) or 5, and whether a new keyTransRI should continue from 3
   or fill the gap at 1.  For now we assume that the versioning will 
   continue at the current highest-assigned value for each type and no-op
   out objects with the next two version values rather than rejecting them
   as invalid data */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int getObjectInfo( INOUT_PTR STREAM *stream, 
						  OUT_PTR QUERY_INFO *queryInfo,
						  const QUERYOBJECT_TYPE objectTypeHint )
	{
	const int startPos = stell( stream );
	long version;
	int tag, length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	REQUIRES( isIntegerRange( startPos ) );
	REQUIRES( isEnumRange( objectTypeHint, QUERYOBJECT ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do
	   anything */
	if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE )
		return( CRYPT_ERROR_UNDERFLOW );

	/* Get the object's length and make sure that its encoding is valid, 
	   which also checks that all of the object's data is present */
	status = getStreamObjectLength( stream, &length, 16 );
	if( cryptStatusOK( status ) )
		{
		void *objectPtr;
	
		status = sMemGetDataBlockAbs( stream, startPos, &objectPtr, length );
#ifndef CONFIG_FUZZ
		if( cryptStatusOK( status ) )
			status = checkObjectEncoding( objectPtr, length );
#endif /* CONFIG_FUZZ */
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Get the type and version information */
	queryInfo->type = CRYPT_OBJECT_NONE;
	queryInfo->size = length;
	status = tag = peekTag( stream );
	if( cryptStatusError( status ) )
		return( status );
	ANALYSER_HINT( isValidTag( tag ) );	/* Guaranteed by peekTag() */
	readGenericHole( stream, NULL, 16, tag );
	status = readShortInteger( stream, &version );
	if( cryptStatusError( status ) )
		return( status );
	if( !isIntegerRange( version ) )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->version = ( int ) version;

	/* If the caller has specified that a particular type of object is 
	   expected, make sure that it's the right type */
	if( objectTypeHint == QUERYOBJECT_KEYEX )
		{
		/* If it's a RecipientInfo then the tag must be a SEQUENCE or a 
		   context-specific tag */
		switch( tag )
			{
			case BER_SEQUENCE:
				if( version == KEYTRANS_VERSION )
					{
					queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					queryInfo->formatType = CRYPT_FORMAT_CMS;
					break;
					}
				if( version == KEYTRANS_EX_VERSION )
					{
					queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
					break;
					}
				if( version == KEYTRANS_EX_VERSION + 1 || \
					version == KEYTRANS_EX_VERSION + 2 )
					{
					/* Assume that it's a new type of keyTransRI and no-op 
					   out the read */
					queryInfo->optType = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			case MAKE_CTAG( CTAG_RI_PASSWORD ):
				if( version == PWRI_VERSION )
					{
					queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
					queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
					break;
					}
				if( version == PWRI_VERSION + 1 || \
					version == PWRI_VERSION + 2 )
					{
					/* Assume that it's a new type of passwordRI and no-op 
					   out the read */
					queryInfo->optType = CRYPT_OBJECT_ENCRYPTED_KEY;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			case MAKE_CTAG( CTAG_RI_KEYAGREE ):
			case MAKE_CTAG( CTAG_RI_KEK ):
			case MAKE_CTAG( CTAG_RI_OTHER ):
				/* It's a known RecipientInfo type that we can't do anything 
				   with, leave it as a no-op */
				DEBUG_DIAG(( "Found un-processable RecipientInfo type %d", 
							 EXTRACT_CTAG( tag ) ));
				queryInfo->optType = CRYPT_OBJECT_PKCENCRYPTED_KEY;
				break;
			
			case MAKE_CTAG( CTAG_RI_OTHER + 1 ):
			case MAKE_CTAG( CTAG_RI_OTHER + 2 ):
			case MAKE_CTAG( CTAG_RI_OTHER + 3 ):
				/* It's an unknown RecipientInfo type, leave it as a no-op.  
				   Given the addition of the kitchen-sink OtherRecipientInfo 
				   which was the last RecipientInfo update in RFC 3369 in 
				   2002 it's unlikely that further RecipientInfo types will 
				   ever be added, but we leave this no-op handling here so
				   we don't reject an entire message if it happens to 
				   contain a plausibly new value */
				DEBUG_DIAG(( "Found unknown RecipientInfo type %d", 
							 EXTRACT_CTAG( tag ) ));
				queryInfo->optType = CRYPT_OBJECT_PKCENCRYPTED_KEY;
				break;

			default:
				return( CRYPT_ERROR_BADDATA );
			}

		/* Reset the stream for the caller before we exit */
		sseek( stream, startPos );
		return( CRYPT_OK );
		}
	if( objectTypeHint == QUERYOBJECT_SIGNATURE )
		{
		/* If it's a SignerInfo then the tag must be a SEQUENCE */
		if( tag != BER_SEQUENCE )
			return( CRYPT_ERROR_BADDATA );
		switch( version )
			{
			case SIGNATURE_VERSION:
				queryInfo->type = CRYPT_OBJECT_SIGNATURE;
				queryInfo->formatType = CRYPT_FORMAT_CMS;
				break;

			case SIGNATURE_EX_VERSION:
				queryInfo->type = CRYPT_OBJECT_SIGNATURE;
				queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
				break;
				
			case SIGNATURE_EX_VERSION + 1:
			case SIGNATURE_EX_VERSION + 2:
				{
				/* Assume that it's a new type of SignerInfo and no-op out 
				   the read */
				queryInfo->optType = CRYPT_OBJECT_SIGNATURE;
				break;
				}

			default:
				return( CRYPT_ERROR_BADDATA );
			}

		/* Reset the stream for the caller before we exit */
		sseek( stream, startPos );
		return( CRYPT_OK );
		}

	/* We don't know what we're supposed to be seeing, dig into the content 
	   to try and find out.  Unlike the object types with hints where we 
	   convert unknown types to no-ops to allow processing to continue since 
	   it'll be coming from data in an envelope with more objects present, 
	   we reject anything that we can't process since it'll be a standalone 
	   object passed in by the user */
	if( tag != BER_SEQUENCE && tag != MAKE_CTAG( CTAG_RI_PASSWORD ) )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
	if( tag == BER_SEQUENCE )
		{
		/* This could be a SignedInfo or a KeyTransRecipientInfo, see what 
		   follows */
		switch( version )
			{
			case KEYTRANS_VERSION:
				queryInfo->formatType = CRYPT_FORMAT_CMS;
				STDC_FALLTHROUGH;
			case KEYTRANS_EX_VERSION:
				queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
				break;

			case SIGNATURE_VERSION:
				queryInfo->formatType = CRYPT_FORMAT_CMS;
				STDC_FALLTHROUGH;
			case SIGNATURE_EX_VERSION:
				queryInfo->type = CRYPT_OBJECT_SIGNATURE;
				break;

			default:
				return( CRYPT_ERROR_BADDATA );
			}
		}
	else
		{
		/* It's PasswordRecipientInfo */
		if( version != PWRI_VERSION )
			return( CRYPT_ERROR_BADDATA );
		queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
		}

	/* Reset the stream for the caller before we exit */
	sseek( stream, startPos );
	return( CRYPT_OK );
	}
#endif /* USE_INT_CMS */

#if defined( USE_PGP ) || defined( USE_PGPKEYS )

/* Get information on a PGP data object.  This doesn't reset the stream like
   the ASN.1 equivalent because the PGP header is complex enough that it
   can't be read inline like the ASN.1 header */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getPgpPacketInfo( INOUT_PTR STREAM *stream, 
					  OUT_PTR QUERY_INFO *queryInfo,
					  const QUERYOBJECT_TYPE objectTypeHint )
	{
	const int startPos = stell( stream );
	int ctb, version, length, offset, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	REQUIRES( isIntegerRange( startPos ) );
	REQUIRES( isEnumRange( objectTypeHint, QUERYOBJECT ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Read the packet header and extract information from the CTB.  There 
	   are two places to stash version numbers, the PGP 2.x vs. OpenPGP flag 
	   in the CTB and then the actual packet version which may be different 
	   from the CTB version, for example some OpenPGP implementations use 
	   PGP 2.x CTBs to contain OpenPGP packets.  To deal with this we 
	   initially set the version based on the CTB but then override it if 
	   the packet version indicates an OpenPGP packet */
	status = pgpReadPacketHeader( stream, &ctb, &length, 8, 
								  MAX_INTLENGTH - 1 );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->type = CRYPT_OBJECT_NONE;
	queryInfo->formatType = CRYPT_FORMAT_PGP;
	queryInfo->version = pgpGetPacketVersion( ctb );
	status = streamOffsetFromPosition( stream, startPos, &offset );
	if( cryptStatusError( status ) )
		return( status );
	if( checkOverflowAdd( offset, length ) )
		return( CRYPT_ERROR_OVERFLOW );
	queryInfo->size = offset + length;
	status = version = sgetc( stream );
	if( cryptStatusError( status ) )
		return( status );
	REQUIRES( !checkOverflowDec( length ) );
	length--;		/* We've skipped the version number */

	/* If the caller has specified that a particular type of object is 
	   expected, make sure that it's the right type */
	if( objectTypeHint == QUERYOBJECT_KEYEX )
		{
		switch( pgpGetPacketType( ctb ) )
			{
			case PGP_PACKET_SKE:
				if( version == PGP_VERSION_OPENPGP )
					{
					queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
					queryInfo->version = PGP_VERSION_OPENPGP;
					break;
					}
				if( version == PGP_VERSION_OPENPGP + 1 || \
					version == PGP_VERSION_OPENPGP + 2 )
					{
					/* Assume that it's a new type of SKE packet and no-op 
					   out the read */
					queryInfo->optType = CRYPT_OBJECT_ENCRYPTED_KEY;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			case PGP_PACKET_PKE:
				/* In this case the OpenPGP version is version 3, not the 
				   expected VERSION_OPENPGP */
				if( version == PGP_VERSION_2 )
					{
					queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					break;
					}
				if( version == PGP_VERSION_3 )
					{
					queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					queryInfo->version = PGP_VERSION_OPENPGP;
					break;
					}
				if( version == PGP_VERSION_3 + 1 || \
					version == PGP_VERSION_3 + 2 )
					{
					/* Assume that it's a new type of PKE packet and no-op 
					   out the read */
					queryInfo->optType = CRYPT_OBJECT_PKCENCRYPTED_KEY;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			default:
				return( CRYPT_ERROR_BADDATA );
			}

		/* Make sure that all of the data is present without resetting the 
		   stream */
		return( ( sMemDataLeft( stream ) < length ) ? \
				CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
		}
	if( objectTypeHint == QUERYOBJECT_SIGNATURE )
		{
		switch( pgpGetPacketType( ctb ) )
			{
			case PGP_PACKET_SIGNATURE:
				if( version == PGP_VERSION_3 )
					{
					queryInfo->type = CRYPT_OBJECT_SIGNATURE;
					break;
					}
				if( version == PGP_VERSION_OPENPGP )
					{
					queryInfo->type = CRYPT_OBJECT_SIGNATURE;
					queryInfo->version = PGP_VERSION_OPENPGP;
					break;
					}
				if( version == PGP_VERSION_OPENPGP + 1 || \
					version == PGP_VERSION_OPENPGP + 2 )
					{
					/* Assume that it's a new type of signature packet and 
					   no-op out the read */
					queryInfo->optType = CRYPT_OBJECT_SIGNATURE;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			case PGP_PACKET_SIGNATURE_ONEPASS:
				/* First half of a one-pass signature, this is given a 
				   special type since it's not a normal packet.  In this 
				   case the OpenPGP version is version 3, not the expected 
				   VERSION_OPENPGP.
			   
				   Unlike the PKE, SKE, and signature packets we fail if we 
				   find an unrecognised version since this isn't something 
				   that we can skip */
				if( version == PGP_VERSION_3 )
					{
					queryInfo->type = CRYPT_OBJECT_LAST;
					queryInfo->version = PGP_VERSION_OPENPGP;
					break;
					}
				return( CRYPT_ERROR_BADDATA );

			default:
				return( CRYPT_ERROR_BADDATA );
			}

		/* Make sure that all of the data is present without resetting the 
		   stream */
		return( ( sMemDataLeft( stream ) < length ) ? \
				CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
		}

	/* We don't know what we're supposed to be seeing, dig into the content 
	   to try and find out.  Unlike the object types with hints where we 
	   convert unknown types to no-ops to allow processing to continue since 
	   it'll be coming from data in an envelope with more objects present, 
	   we reject anything that we can't process since it'll be a standalone 
	   object passed in by the user */
	switch( pgpGetPacketType( ctb ) )
		{
		case PGP_PACKET_SKE:
			if( version == PGP_VERSION_OPENPGP )
				{
				queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
				queryInfo->version = PGP_VERSION_OPENPGP;
				break;
				}
			return( CRYPT_ERROR_BADDATA );

		case PGP_PACKET_PKE:
			/* In this case the OpenPGP version is version 3, not the 
			   expected VERSION_OPENPGP */
			if( version == PGP_VERSION_2 )
				{
				queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
				break;
				}
			if( version == PGP_VERSION_3 )
				{
				queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
				queryInfo->version = PGP_VERSION_OPENPGP;
				break;
				}
			return( CRYPT_ERROR_BADDATA );

		case PGP_PACKET_SIGNATURE:
			if( version == PGP_VERSION_3 )
				{
				queryInfo->type = CRYPT_OBJECT_SIGNATURE;
				break;
				}
			if( version == PGP_VERSION_OPENPGP )
				{
				queryInfo->type = CRYPT_OBJECT_SIGNATURE;
				queryInfo->version = PGP_VERSION_OPENPGP;
				break;
				}
			return( CRYPT_ERROR_BADDATA );

		default:
			DEBUG_DIAG(( "Found unrecognised ctb %X", ctb ));
			assert( DEBUG_WARN );
			return( CRYPT_ERROR_BADDATA );
		}

	/* Make sure that all of the data is present without resetting the 
	   stream */
	return( ( sMemDataLeft( stream ) < length ) ? \
			CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
	}
#endif /* USE_PGP || USE_PGPKEYS */

/****************************************************************************
*																			*
*								Object Query Routines						*
*																			*
****************************************************************************/

#ifdef USE_INT_CMS

/* Low-level object query functions */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int queryAsn1Object( INOUT_PTR TYPECAST( STREAM * ) struct ST *streamPtr, 
					 OUT_PTR QUERY_INFO *queryInfo,
					 const QUERYOBJECT_TYPE objectTypeHint )
	{
	QUERY_INFO basicQueryInfo;
	STREAM *stream = streamPtr;
	const int startPos = stell( stream );
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	REQUIRES( isIntegerRange( startPos ) );
	REQUIRES( isEnumRange( objectTypeHint, QUERYOBJECT ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Determine basic object information by reading the start of the 
	   object, resetting the stream at the end of the read in preparation 
	   for the full object-type-specific read.  This also verifies that 
	   all of the object data is present in the stream, and, if an 
	   objectTypeHint is given, that we've been fed the correct type of 
	   object.
	   
	   We read the information into a local QUERY_INFO copy because the
	   object-subtype-specific reads clear their output parameter, which 
	   means they'd clear the information that we've just read if we 
	   passed in the main queryInfo */
	status = getObjectInfo( stream, &basicQueryInfo, objectTypeHint );
	if( cryptStatusError( status ) )
		return( status );
	ENSURES( basicQueryInfo.type != CRYPT_OBJECT_NONE || \
			 basicQueryInfo.optType != CRYPT_OBJECT_NONE );

	/* Call the appropriate routine to find out more about the object */
	switch( basicQueryInfo.type )
		{
		case CRYPT_OBJECT_ENCRYPTED_KEY:
			{
			const READKEK_FUNCTION readKekFunction = \
									getReadKekFunction( KEYEX_CMS );

			if( readKekFunction == NULL )
				return( CRYPT_ERROR_NOTAVAIL );
			status = readKekFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_PKCENCRYPTED_KEY:
			{
			const READKEYTRANS_FUNCTION readKeytransFunction = \
				getReadKeytransFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \
										 KEYEX_CMS : KEYEX_CRYPTLIB );

			if( readKeytransFunction == NULL )
				return( CRYPT_ERROR_NOTAVAIL );
			status = readKeytransFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_SIGNATURE:
			{
			const READSIG_FUNCTION readSigFunction = \
				getReadSigFunction( ( basicQueryInfo.formatType == CRYPT_FORMAT_CMS ) ? \
									SIGNATURE_CMS : SIGNATURE_CRYPTLIB );

			if( readSigFunction == NULL )
				return( CRYPT_ERROR_NOTAVAIL );
			status = readSigFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_NONE:
			/* New, unrecognised object type */
			status = readUniversal( stream );
			break;

		default:
			retIntError();
		}
	if( cryptStatusOK( status ) && \
		( checkOverflowAdd( startPos, basicQueryInfo.size ) || \
		  startPos + basicQueryInfo.size != stell( stream ) ) )
		{
		/* Make sure that the given size of the object matches what we've 
		   actually processed */
		status = CRYPT_ERROR_BADDATA;
		}
	if( cryptStatusError( status ) )
		{
		zeroise( queryInfo, sizeof( QUERY_INFO ) );
		return( status );
		}
	sseek( stream, startPos );

	/* Augment the per-object query information with the basic query 
	   information that we got earlier */
	queryInfo->formatType = basicQueryInfo.formatType;
	queryInfo->type = basicQueryInfo.type;
	queryInfo->optType = basicQueryInfo.optType;
	queryInfo->size = basicQueryInfo.size;
	queryInfo->version = basicQueryInfo.version;

	return( CRYPT_OK );
	}
#endif /* USE_INT_CMS */

#if defined( USE_PGP ) || defined( USE_PGPKEYS )

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int queryPgpObject( INOUT_PTR TYPECAST( STREAM * ) struct ST *streamPtr, 
					OUT_PTR QUERY_INFO *queryInfo,
					const QUERYOBJECT_TYPE objectTypeHint )
	{
	STREAM *stream = streamPtr;
	const int startPos = stell( stream );
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	REQUIRES( isIntegerRange( startPos ) );
	REQUIRES( isEnumRange( objectTypeHint, QUERYOBJECT ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Determine basic object information.  This also verifies that all of 
	   the object data is present in the stream and, if an objectTypeHint
	   is given, that we've been fed the correct type of object.

	   Unlike the ASN.1 object read this doesn't reset the stream because 
	   the PGP header is complex enough that it can't be read inline like 
	   the ASN.1 equivalent, so we reset it ourselves here before re-reading 
	   it in the object-subtype-specific read */
	status = getPgpPacketInfo( stream, queryInfo, objectTypeHint );
	if( cryptStatusError( status ) )
		return( status );
	sseek( stream, startPos );
	ENSURES( queryInfo->type != CRYPT_OBJECT_NONE || \
			 queryInfo->optType != CRYPT_OBJECT_NONE );

	/* Call the appropriate routine to find out more about the object.  The
	   subtype-specific functions also call getPgpPacketInfo() (because 
	   they're shared with other code) so the queryInfo is re-populated on
	   each call */
	switch( queryInfo->type )
		{
		case CRYPT_OBJECT_ENCRYPTED_KEY:
			{
			const READKEK_FUNCTION readKekFunction = \
									getReadKekFunction( KEYEX_PGP );

			if( readKekFunction == NULL )
				{
				zeroise( queryInfo, sizeof( QUERY_INFO ) );
				return( CRYPT_ERROR_NOTAVAIL );
				}
			status = readKekFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_PKCENCRYPTED_KEY:
			{
			const READKEYTRANS_FUNCTION readKeytransFunction = \
									getReadKeytransFunction( KEYEX_PGP );

			if( readKeytransFunction == NULL )
				{
				zeroise( queryInfo, sizeof( QUERY_INFO ) );
				return( CRYPT_ERROR_NOTAVAIL );
				}
			status = readKeytransFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_SIGNATURE:
			{
			const READSIG_FUNCTION readSigFunction = \
									getReadSigFunction( SIGNATURE_PGP );

			if( readSigFunction == NULL )
				{
				zeroise( queryInfo, sizeof( QUERY_INFO ) );
				return( CRYPT_ERROR_NOTAVAIL );
				}
			status = readSigFunction( stream, queryInfo );
			break;
			}

		case CRYPT_OBJECT_LAST:
			/* First half of a one-pass signature */
			status = readPgpOnepassSigPacket( stream, queryInfo );
			break;

		case CRYPT_OBJECT_NONE:
			/* New, unrecognised object type.  getPgpPacketInfo() has 
			   already guaranteed that the size is an integer range, we
			   restrict it even further here because we shouldn't be
			   seeing gigantic keyex/signature objects */
			if( !isShortIntegerRangeNZ( queryInfo->size ) )
				status = CRYPT_ERROR_OVERFLOW;
			else
				{
				status = sSkip( stream, queryInfo->size, 
								MAX_INTLENGTH_SHORT );
				}
			break;

		default:
			retIntError();
		}
	if( cryptStatusOK( status ) && \
		( checkOverflowAdd( startPos, queryInfo->size ) || \
		  startPos + queryInfo->size != stell( stream ) ) )
		{
		/* Make sure that the given size of the object matches what we've 
		   actually processed */
		status = CRYPT_ERROR_BADDATA;
		}
	if( cryptStatusError( status ) )
		{
		zeroise( queryInfo, sizeof( QUERY_INFO ) );
		return( status );
		}
	sseek( stream, startPos );

	/* Unlike the ASN.1 object read code, we've read the object information 
	   directly into the queryInfo so there's no need to copy additional 
	   information from the basicQueryInfo across */

	return( CRYPT_OK );
	}
#endif /* USE_PGP || USE_PGPKEYS */

/****************************************************************************
*																			*
*						External Object Query Interface						*
*																			*
****************************************************************************/

#if defined( USE_INT_CMS ) || defined( USE_PGP )

/* Query an object.  This is just a wrapper that provides an external
   interface for the lower-level object-query routines */

C_CHECK_RETVAL C_NONNULL_ARG( ( 1, 3 ) ) \
C_RET cryptQueryObject( C_IN void C_PTR objectData,
						C_IN int objectDataLength,
						C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo )
	{
	QUERY_INFO queryInfo DUMMY_INIT_STRUCT;	/* If USE_PGP undef'd */
	STREAM stream;
	int value, length = objectDataLength, status;

	/* Perform basic error checking and clear the return value */
	if( objectDataLength < MIN_CRYPT_OBJECTSIZE || \
		objectDataLength >= MAX_BUFFER_SIZE )
		return( CRYPT_ERROR_PARAM2 );
	if( !isReadPtrDynamic( objectData, objectDataLength ) )
		return( CRYPT_ERROR_PARAM1 );
	if( !isWritePtr( cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) ) )
		return( CRYPT_ERROR_PARAM3 );
	memset( cryptObjectInfo, 0, sizeof( CRYPT_OBJECT_INFO ) );

	/* Query the object.  This is just a wrapper for the lower-level object-
	   query functions.  Note that we use sPeek() rather than peekTag() 
	   because we want to continue processing (or at least checking for) PGP 
	   data if it's not ASN.1 */
	sMemConnect( &stream, ( void * ) objectData, length );
	status = value = sPeek( &stream );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( value == BER_SEQUENCE || value == MAKE_CTAG( CTAG_RI_PASSWORD ) )
		{
		status = queryAsn1Object( &stream, &queryInfo, 
								  QUERYOBJECT_UNKNOWN );
		}
	else
		{
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
		status = queryPgpObject( &stream, &queryInfo, 
								 QUERYOBJECT_UNKNOWN );
#else
		status = CRYPT_ERROR_BADDATA;
#endif /* USE_PGP || USE_PGPKEYS */
		}
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Copy the externally-visible fields across */
	cryptObjectInfo->objectType = queryInfo.type;
	cryptObjectInfo->cryptAlgo = queryInfo.cryptAlgo;
	cryptObjectInfo->cryptMode = queryInfo.cryptMode;
	if( queryInfo.type == CRYPT_OBJECT_SIGNATURE )
		cryptObjectInfo->hashAlgo = queryInfo.hashAlgo;
	if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY && \
		queryInfo.saltLength > 0 )
		{
		REQUIRES( rangeCheck( queryInfo.saltLength, 1, 
							  CRYPT_MAX_HASHSIZE ) );
		memcpy( cryptObjectInfo->salt, queryInfo.salt, 
				queryInfo.saltLength );
		cryptObjectInfo->saltSize = queryInfo.saltLength;
		cryptObjectInfo->iterations = queryInfo.keySetupIterations;
		if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE )
			cryptObjectInfo->hashAlgo = queryInfo.keySetupAlgo;
		}

	return( CRYPT_OK );
	}

#else

/****************************************************************************
*																			*
*						Stub Functions for non-CMS/PGP Use					*
*																			*
****************************************************************************/

C_RET cryptQueryObject( C_IN void C_PTR objectData,
						C_IN int objectDataLength,
						C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo )
	{
	UNUSED_ARG( objectData );
	UNUSED_ARG( cryptObjectInfo );

	return( CRYPT_ERROR_NOTAVAIL );
	}
#endif /* USE_INT_CMS || USE_PGP */
