/****************************************************************************
*																			*
*						  Signature Read/Write Routines						*
*						Copyright Peter Gutmann 1992-2007					*
*																			*
****************************************************************************/

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

/* Context-specific tags for the SignerInfo record */

enum { CTAG_SI_SKI };

/****************************************************************************
*																			*
*							X.509 Signature Routines						*
*																			*
****************************************************************************/

/* Read/write raw signatures */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readRawSignature( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	int status;

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

	/* Read the start of the signature */
	status = readBitStringHole( stream, &queryInfo->dataLength, 18 + 18,
								DEFAULT_TAG );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->dataStart = stell( stream ) - startPos;

	/* Make sure that the remaining signature data is present */
	return( sSkip( stream, queryInfo->dataLength ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeRawSignature( INOUT STREAM *stream, 
							  STDC_UNUSED const CRYPT_CONTEXT iSignContext,
							  STDC_UNUSED const CRYPT_ALGO_TYPE hashAlgo,
							  STDC_UNUSED const CRYPT_ALGO_TYPE signAlgo,
							  IN_BUFFER( signatureLength ) const BYTE *signature,
							  IN_LENGTH_SHORT_MIN( 40 ) const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( signatureLength >= 40 && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	/* Write the BIT STRING wrapper and signature */
	writeBitStringHole( stream, signatureLength, DEFAULT_TAG );
	return( writeRawObject( stream, signature, signatureLength ) );
	}

/* Read/write X.509 signatures */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readX509Signature( INOUT STREAM *stream, 
							  INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	int status;

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

	/* Read the signature/hash algorithm information followed by the start
	   of the signature */
	status = readAlgoIDext( stream, &queryInfo->cryptAlgo,
							&queryInfo->hashAlgo );
	if( cryptStatusOK( status ) )
		{
		status = readBitStringHole( stream, &queryInfo->dataLength, 18 + 18,
									DEFAULT_TAG );
		}
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->dataStart = stell( stream ) - startPos;

	/* Make sure that the remaining signature data is present */
	return( sSkip( stream, queryInfo->dataLength ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeX509Signature( INOUT STREAM *stream,
							   IN_HANDLE const CRYPT_CONTEXT iSignContext,
							   IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
							   STDC_UNUSED const CRYPT_ALGO_TYPE signAlgo,
							   IN_BUFFER( signatureLength ) const BYTE *signature,
							   IN_LENGTH_SHORT_MIN( 40 ) const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( isHandleRangeValid( iSignContext ) );
	REQUIRES( hashAlgo >= CRYPT_ALGO_FIRST_HASH && \
			  hashAlgo <= CRYPT_ALGO_LAST_HASH );
	REQUIRES( signatureLength >= 40 && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	/* Write the hash+signature algorithm identifier followed by the BIT
	   STRING wrapper and signature */
	writeContextAlgoID( stream, iSignContext, hashAlgo );
	writeBitStringHole( stream, signatureLength, DEFAULT_TAG );
	return( writeRawObject( stream, signature, signatureLength ) );
	}

/****************************************************************************
*																			*
*							CMS Signature Routines							*
*																			*
****************************************************************************/

/* Read/write PKCS #7/CMS (issuerAndSerialNumber) signatures */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readCmsSignature( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	long value, endPos;
	int length, status;

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

	status = getStreamObjectLength( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	endPos = startPos + length;

	/* Read the header */
	readSequence( stream, NULL );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) )
		return( status );
	if( value != SIGNATURE_VERSION )
		return( CRYPT_ERROR_BADDATA );

	/* Read the issuer and serial number and hash algorithm ID.  Since we're 
	   recording the position of the issuerAndSerialNumber as a blob we have 
	   to use getStreamObjectLength() to get the overall blob data size */
	status = getStreamObjectLength( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->iAndSStart = stell( stream ) - startPos;
	queryInfo->iAndSLength = length;
	sSkip( stream, length );
	status = readAlgoID( stream, &queryInfo->hashAlgo );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the authenticated attributes if there are any present */
	if( peekTag( stream ) == MAKE_CTAG( 0 ) )
		{
		status = getStreamObjectLength( stream, &length );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->attributeStart = stell( stream ) - startPos;
		queryInfo->attributeLength = length;
		status = sSkip( stream, length );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Read the CMS/cryptlib signature algorithm and the start of the 
	   signature */
	status = readAlgoID( stream, &queryInfo->cryptAlgo );
	if( cryptStatusOK( status ) )
		{
		status = readOctetStringHole( stream, &queryInfo->dataLength, 
									  18 + 18, DEFAULT_TAG );
		}
	if( cryptStatusOK( status ) )
		{
		queryInfo->dataStart = stell( stream ) - startPos;
		status = sSkip( stream, queryInfo->dataLength );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Read the unauthenticated attributes if there are any present */
	if( stell( stream ) < endPos && peekTag( stream ) == MAKE_CTAG( 1 ) )
		{
		status = getStreamObjectLength( stream, &length );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->unauthAttributeStart = stell( stream ) - startPos;
		queryInfo->unauthAttributeLength = length;
		status = sSkip( stream, length );
		if( cryptStatusError( status ) )
			return( status );
		}

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeCmsSignature( INOUT STREAM *stream,
							  IN_HANDLE const CRYPT_CONTEXT iSignContext,
							  STDC_UNUSED const CRYPT_ALGO_TYPE hashAlgo,
							  STDC_UNUSED const CRYPT_ALGO_TYPE signAlgo,
							  IN_BUFFER( signatureLength ) const BYTE *signature,
							  IN_LENGTH_SHORT_MIN( 40 ) const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( isHandleRangeValid( iSignContext ) );
	REQUIRES( signatureLength >= 40 && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	/* Write the signature algorithm identifier and signature data.  The
	   handling of CMS signatures is non-orthogonal to readCmsSignature()
	   because creating a CMS signature involves adding assorted additional
	   data like iAndS and signed attributes that present too much
	   information to pass into a basic writeSignature() call */
	writeContextAlgoID( stream, iSignContext, CRYPT_ALGO_NONE );
	return( writeOctetString( stream, signature, signatureLength, DEFAULT_TAG ) );
	}

/* Read/write cryptlib/CMS (keyID) signatures */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readCryptlibSignature( INOUT STREAM *stream, 
								  INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	long value;
	int status;

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

	/* Read the header */
	readSequence( stream, NULL );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) )
		return( status );
	if( value != SIGNATURE_EX_VERSION )
		return( CRYPT_ERROR_BADDATA );

	/* Read the key ID and hash algorithm identifier */
	readOctetStringTag( stream, queryInfo->keyID, &queryInfo->keyIDlength,
						8, CRYPT_MAX_HASHSIZE, CTAG_SI_SKI );
	status = readAlgoID( stream, &queryInfo->hashAlgo );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the CMS/cryptlib signature algorithm and the start of the 
	   signature */
	status = readAlgoID( stream, &queryInfo->cryptAlgo );
	if( cryptStatusOK( status ) )
		{
		status = readOctetStringHole( stream, &queryInfo->dataLength, 
									  18 + 18, DEFAULT_TAG );
		}
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->dataStart = stell( stream ) - startPos;

	/* Make sure that the remaining signature data is present */
	return( sSkip( stream, queryInfo->dataLength ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeCryptlibSignature( INOUT STREAM *stream,
								   IN_HANDLE const CRYPT_CONTEXT iSignContext,
								   IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
								   STDC_UNUSED const CRYPT_ALGO_TYPE signAlgo,
								   IN_BUFFER( signatureLength ) \
									const BYTE *signature,
								   IN_LENGTH_SHORT_MIN( 40 ) \
									const int signatureLength )
	{
	MESSAGE_DATA msgData;
	BYTE keyID[ CRYPT_MAX_HASHSIZE + 8 ];
	const int signAlgoIdSize = \
				sizeofContextAlgoID( iSignContext, CRYPT_ALGO_NONE );
	const int hashAlgoIdSize = sizeofAlgoID( hashAlgo );
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( isHandleRangeValid( iSignContext ) );
	REQUIRES( hashAlgo >= CRYPT_ALGO_FIRST_HASH && \
			  hashAlgo <= CRYPT_ALGO_LAST_HASH );
	REQUIRES( signatureLength >= 40 && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	if( cryptStatusError( signAlgoIdSize ) )
		return( signAlgoIdSize );
	if( cryptStatusError( hashAlgoIdSize ) )
		return( hashAlgoIdSize );

	/* Get the key ID */
	setMessageData( &msgData, keyID, CRYPT_MAX_HASHSIZE );
	status = krnlSendMessage( iSignContext, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_IATTRIBUTE_KEYID );
	if( cryptStatusError( status ) )
		return( status );

	/* Write the header */
	writeSequence( stream, ( int ) sizeofShortInteger( SIGNATURE_EX_VERSION ) + \
				   sizeofObject( msgData.length ) + \
				   signAlgoIdSize + hashAlgoIdSize + \
				   sizeofObject( signatureLength ) );

	/* Write the version, key ID and algorithm identifier */
	writeShortInteger( stream, SIGNATURE_EX_VERSION, DEFAULT_TAG );
	writeOctetString( stream, msgData.data, msgData.length, CTAG_SI_SKI );
	writeAlgoID( stream, hashAlgo );
	writeContextAlgoID( stream, iSignContext, CRYPT_ALGO_NONE );
	return( writeOctetString( stream, signature, signatureLength, DEFAULT_TAG ) );
	}

/****************************************************************************
*																			*
*							PGP Signature Routines							*
*																			*
****************************************************************************/

#ifdef USE_PGP

/* Read a PGP type-and-value packet and check whether it's one of ours */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readTypeAndValue( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo,
							 IN_LENGTH_Z const int startPos )
	{
	BYTE nameBuffer[ 32 + 8 ];
	static const char FAR_BSS *nameString = "issuerAndSerialNumber";
	int nameLength, valueLength, status;	/* length = 21 */

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

	REQUIRES( startPos >= 0 && startPos < MAX_INTLENGTH );
	REQUIRES( startPos < stell( stream ) );

	/* Skip the flags */
	status = sSkip( stream, UINT32_SIZE );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the attribute length information and make sure that it looks 
	   valid */
	nameLength = readUint16( stream );
	valueLength = readUint16( stream );
	if( cryptStatusError( valueLength ) )
		return( valueLength );
	if( nameLength != 21 || valueLength < 16 || valueLength > 2048 )
		return( sSkip( stream, nameLength + valueLength ) );

	/* Read the name and check whether it's one that we recognise */
	status = sread( stream, nameBuffer, nameLength );
	if( cryptStatusError( status ) )
		return( status );
	if( !memcmp( nameBuffer, nameString, nameLength ) )
		{
		/* It's an issuerAndSerialNumber, remember it for later */
		queryInfo->iAndSStart = stell( stream ) - startPos;
		queryInfo->iAndSLength = valueLength;
		}
	return( sSkip( stream, valueLength ) );
	}

/* Read signature subpackets.  In theory we could do something with the 
   isAuthenticated flag but at the moment we don't rely on any attributes 
   that require authentication.  The most that an attacker can do by 
   changing the keyID/iAndS field is cause the signature check to fail, 
   which they can do just as easily by flipping a bit */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readSignatureSubpackets( INOUT STREAM *stream, 
									INOUT QUERY_INFO *queryInfo,
									IN_LENGTH_SHORT const int length, 
									IN_LENGTH const int startPos,
									const BOOLEAN isAuthenticated )
	{
	const int endPos = stell( stream ) + length;
	int iterationCount = 0;

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

	REQUIRES( length > 0 && length < MAX_INTLENGTH_SHORT );
	REQUIRES( startPos >= 0 && startPos < MAX_INTLENGTH );
	REQUIRES( startPos < stell( stream ) );

	while( stell( stream ) < endPos && \
		   iterationCount++ < FAILSAFE_ITERATIONS_MED )
		{
		int subpacketLength, type = DUMMY_INIT, status;

		/* Read the subpacket length and type */
		status = pgpReadShortLength( stream, &subpacketLength, 
									 PGP_CTB_OPENPGP );
		if( cryptStatusOK( status ) )
			status = type = sgetc( stream );
		if( cryptStatusError( status ) )
			return( status );

		/* If it's an unrecognised subpacket with the critical flag set,
		   reject the signature.  The range check isn't complete since there
		   are a few holes in the range, but since the holes presumably exist
		   because of deprecated subpacket types any new packets will be 
		   added at the end so it's safe to use */
		if( ( type & 0x80 ) && ( ( type & 0x7F ) > PGP_SUBPACKET_LAST ) )
			return( CRYPT_ERROR_NOTAVAIL );

		switch( type )
			{
			case PGP_SUBPACKET_KEYID:
				/* Make sure that the length is valid */
				if( subpacketLength != PGP_KEYID_SIZE + 1 )
					return( CRYPT_ERROR_BADDATA );

				/* If it's a key ID and we haven't already set this from a 
				   preceding one-pass signature packet (which can happen 
				   with detached sigs), set it now */
				if( queryInfo->keyIDlength <= 0 )
					{
					status = sread( stream, queryInfo->keyID, PGP_KEYID_SIZE );
					queryInfo->keyIDlength = PGP_KEYID_SIZE;
					}
				else
					{
					/* We've already got the ID, skip it and continue.  The 
					   -1 is for the packet type, which we've already read */
					status = sSkip( stream, subpacketLength - 1 );
					}
				break;

			case PGP_SUBPACKET_TYPEANDVALUE:
				/* It's a type-and-value packet, check whether it's one of 
				   ours */
				status = readTypeAndValue( stream, queryInfo, startPos );
				break;

			default:
				/* It's something else, skip it and continue.  The -1 is for 
				   the packet type, which we've already read */
				status = sSkip( stream, subpacketLength - 1 );
			}

		if( cryptStatusError( status ) )
			return( status );
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );

	return( CRYPT_OK );
	}

/* Signature info:

	byte	ctb = PGP_PACKET_SIGNATURE_ONEPASS
	byte[]	length
	byte	version = 3 (= OpenPGP, not the expected PGP3)
	byte	sigType
	byte	hashAlgo
	byte	sigAlgo
	byte[8]	keyID
	byte	1 */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readPgpOnepassSigPacket( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	int status;

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

	/* Make sure that the packet header is in order and check the packet
	   version.  This is an OpenPGP-only packet */
	status = getPgpPacketInfo( stream, queryInfo );
	if( cryptStatusError( status ) )
		return( status );
	if( sgetc( stream ) != 3 )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->version = PGP_VERSION_OPENPGP;

	/* Skip the sig.type and get the hash algorithm and signature 
	   algorithms */
	status = sgetc( stream );	/* Skip signature type */
	if( cryptStatusOK( status ) )
		status = readPgpAlgo( stream, &queryInfo->hashAlgo, 
							  PGP_ALGOCLASS_HASH );
	if( cryptStatusOK( status ) )
		status = readPgpAlgo( stream, &queryInfo->cryptAlgo, 
							  PGP_ALGOCLASS_SIGN );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->type = CRYPT_OBJECT_SIGNATURE;

	/* Get the PGP key ID and make sure that this isn't a nested signature */
	status = sread( stream, queryInfo->keyID, PGP_KEYID_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->keyIDlength = PGP_KEYID_SIZE;
	return( ( sgetc( stream ) != 1 ) ? CRYPT_ERROR_BADDATA : CRYPT_OK );
	}

/* Read/write PGP signatures.

		byte	ctb = PGP_PACKET_SIGNATURE
		byte[]	length
	v3:	byte	version = PGP_2,3	v4: byte	version = PGP_VERSION_OPENPGP
		byte	infoLen = 5				byte	sigType
			byte	sigType				byte	sigAlgo
			byte[4]	sig.time			byte	hashAlgo
		byte[8]	keyID					uint16	length of auth.attributes
		byte	sigAlgo					byte[]	authenticated attributes
		byte	hashAlgo				uint16	length of unauth.attributes
		byte[2]	hash check				byte[]	unauthenticated attributes
		mpi(s)	signature							-- Contains keyID
										byte[2]	hash check
										mpi(s)	signature */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readPgp2SigInfo( INOUT STREAM *stream, 
							INOUT QUERY_INFO *queryInfo,
							IN_LENGTH_Z const int startPos )
	{
	int status;

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

	REQUIRES( startPos >= 0 && startPos < MAX_INTLENGTH );
	REQUIRES( startPos < stell( stream ) );

	/* Read PGP 2.x additional signature information */
	if( sgetc( stream ) != 5 )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->attributeStart = stell( stream ) - startPos;
	queryInfo->attributeLength = 5;
	status = sSkip( stream, 5 );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the signer keyID and signature and hash algorithms */
	status = sread( stream, queryInfo->keyID, PGP_KEYID_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	queryInfo->keyIDlength = PGP_KEYID_SIZE;
	status = readPgpAlgo( stream, &queryInfo->cryptAlgo, 
						  PGP_ALGOCLASS_SIGN );
	if( cryptStatusOK( status ) )
		status = readPgpAlgo( stream, &queryInfo->hashAlgo, 
							  PGP_ALGOCLASS_HASH );
	return( status );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readOpenPgpSigInfo( INOUT STREAM *stream, 
							   INOUT QUERY_INFO *queryInfo,
							   IN_LENGTH_Z const int startPos )
	{
	int length, status;

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

	REQUIRES( startPos >= 0 && startPos < MAX_INTLENGTH );
	REQUIRES( startPos < stell( stream ) );

	/* Remember the extra data to be hashed and read the signature and hash 
	   algorithms.  Since the extra data starts at the version byte that 
	   we've already read, we add a -1 offset to the start position, as well
	   as including it in the overall length calculation */
	queryInfo->attributeStart = ( stell( stream ) - 1 ) - startPos;
	queryInfo->attributeLength = PGP_VERSION_SIZE + 1 + \
								 PGP_ALGOID_SIZE + PGP_ALGOID_SIZE;
	status = sgetc( stream );	/* Skip signature type */
	if( cryptStatusOK( status ) )
		status = readPgpAlgo( stream, &queryInfo->cryptAlgo, 
							  PGP_ALGOCLASS_SIGN );
	if( cryptStatusOK( status ) )
		status = readPgpAlgo( stream, &queryInfo->hashAlgo, 
							  PGP_ALGOCLASS_HASH );
	if( cryptStatusError( status ) )
		return( status );

	/* Process the authenticated attributes */
	length = readUint16( stream );
	if( cryptStatusError( length ) )
		return( length );
	if( length < 0 || length > 2048 )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->attributeLength += UINT16_SIZE + length;
	if( length > 0 )
		{
		status = readSignatureSubpackets( stream, queryInfo, length,
										  startPos, TRUE );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Process the unauthenticated attributes */
	queryInfo->unauthAttributeStart = stell( stream ) - startPos;
	length = readUint16( stream );
	if( cryptStatusError( length ) )
		return( length );
	if( length < 0 || length > 2048 )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->unauthAttributeLength = UINT16_SIZE + length;
	if( length > 0 )
		{
		status = readSignatureSubpackets( stream, queryInfo, length, 
										  startPos, FALSE );
		if( cryptStatusError( status ) )
			return( status );
		}

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readPgpSignature( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	int value, status;

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

	/* Make sure that the packet header is in order and check the packet
	   version.  For this packet type a version number of 3 denotes PGP 2.x
	   whereas for key transport it denotes OpenPGP */
	status = getPgpPacketInfo( stream, queryInfo );
	if( cryptStatusError( status ) )
		return( status );
	value = sgetc( stream );
	if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
		value != PGP_VERSION_OPENPGP )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->type = CRYPT_OBJECT_SIGNATURE;
	queryInfo->version = ( value == PGP_VERSION_OPENPGP ) ? \
						 PGP_VERSION_OPENPGP : PGP_VERSION_2;

	/* Read the signing attributes and skip the hash check */
	if( value != PGP_VERSION_OPENPGP )
		status = readPgp2SigInfo( stream, queryInfo, startPos );
	else
		status = readOpenPgpSigInfo( stream, queryInfo, startPos );
	if( cryptStatusOK( status ) )
		status = sSkip( stream, 2 );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the signature, recording the position and length of the raw RSA 
	   signature data.  We have to be careful how we handle this because 
	   readInteger16Ubits() returns the canonicalised form of the values 
	   (with leading zeroes truncated) so an stell() before the read doesn't 
	   necessarily represent the start of the payload:

		startPos	dataStart		 stell()
			|			|				|
			v			v <-- length -->v
		+---+-----------+---------------+
		|	|			|///////////////| Stream
		+---+-----------+---------------+ */
	if( queryInfo->cryptAlgo == CRYPT_ALGO_RSA )
		{
		status = readInteger16Ubits( stream, NULL, &queryInfo->dataLength,
									 MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->dataStart = stell( stream ) - queryInfo->dataLength;
		}
	else
		{
		const int dataStartPos = stell( stream );
		int dummy;

		REQUIRES( queryInfo->cryptAlgo == CRYPT_ALGO_DSA );

		/* Read the DSA signature, recording the position and combined 
		   lengths of the MPI pair.  Again, we can't use the length returned 
		   by readInteger16Ubits() to determine the overall size but have to 
		   calculate it from the position in the stream */
		status = readInteger16Ubits( stream, NULL, &dummy, 16, 20 );
		if( cryptStatusOK( status ) )
			status = readInteger16Ubits( stream, NULL, &dummy, 16, 20 );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->dataStart = dataStartPos - startPos;
		queryInfo->dataLength = stell( stream ) - dataStartPos;
		}

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writePgpSignature( INOUT STREAM *stream,
							  STDC_UNUSED const CRYPT_CONTEXT iSignContext,
							  STDC_UNUSED const CRYPT_ALGO_TYPE hashAlgo,
							  IN_ALGO const CRYPT_ALGO_TYPE signAlgo,
							  IN_BUFFER( signatureLength ) const BYTE *signature,
							  IN_LENGTH_SHORT_MIN( 18 + 18 + 1 ) \
								const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( signAlgo >= CRYPT_ALGO_FIRST_PKC && \
			  signAlgo <= CRYPT_ALGO_LAST_PKC );
	REQUIRES( signatureLength > ( 18 + 18 ) && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	/* If it's a DLP algorithm we've already specified the DLP output format 
	   as PGP so there's no need for further processing.  The handling of 
	   PGP signatures is non-orthogonal to readPgpSignature() because 
	   creating a PGP signature involves adding assorted additional data 
	   like key IDs and authenticated attributes, which present too much 
	   information to pass into a basic writeSignature() call */
	if( isDlpAlgo( signAlgo ) || isEccAlgo( signAlgo ) )
		return( swrite( stream, signature, signatureLength ) );

	/* Write the signature as a PGP MPI */
	return( writeInteger16Ubits( stream, signature, signatureLength ) );
	}
#endif /* USE_PGP */

/****************************************************************************
*																			*
*						Miscellaneous Signature Routines					*
*																			*
****************************************************************************/

#ifdef USE_SSH

/* Read/write SSH signatures.  SSH signature data is treated as a blob
   encoded as an SSH string rather than properly-formatted data so we don't
   encode/decode it as SSH MPIs */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readSshSignature( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	BYTE buffer[ CRYPT_MAX_TEXTSIZE + 8 ];
	int length, status;

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

	/* Read the signature record size and algorithm information */
	readUint32( stream );
	status = readString32( stream, buffer, CRYPT_MAX_TEXTSIZE, &length );
	if( cryptStatusError( status ) )
		return( status );
	if( length != 7 )
		return( CRYPT_ERROR_BADDATA );
	if( !memcmp( buffer, "ssh-rsa", 7 ) )
		queryInfo->cryptAlgo = CRYPT_ALGO_RSA;
	else
		{
		if( !memcmp( buffer, "ssh-dss", 7 ) )
			queryInfo->cryptAlgo = CRYPT_ALGO_DSA;
		else
			return( CRYPT_ERROR_BADDATA );
		}

	/* Read the start of the signature */
	length = readUint32( stream );
	if( cryptStatusError( length ) )
		return( length );
	if( queryInfo->cryptAlgo == CRYPT_ALGO_DSA )
		{
		if( length != ( 20 + 20 ) )
			return( CRYPT_ERROR_BADDATA );
		}
	else
		{
		if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE )
			return( CRYPT_ERROR_BADDATA );
		}
	queryInfo->dataStart = stell( stream ) - startPos;
	queryInfo->dataLength = length;

	/* Make sure that the remaining signature data is present */
	return( sSkip( stream, length ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeSshSignature( INOUT STREAM *stream,
							  STDC_UNUSED const CRYPT_CONTEXT iSignContext,
							  STDC_UNUSED const CRYPT_ALGO_TYPE hashAlgo,
							  IN_ALGO const CRYPT_ALGO_TYPE signAlgo,
							  IN_BUFFER( signatureLength ) const BYTE *signature,
							  IN_LENGTH_SHORT_MIN( 40 ) const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( signAlgo == CRYPT_ALGO_RSA || signAlgo == CRYPT_ALGO_DSA );
	REQUIRES( signatureLength >= ( 20 + 20 ) && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	writeUint32( stream, sizeofString32( "ssh-Xsa", 7 ) + \
						 sizeofString32( "", signatureLength ) );
	writeString32( stream, ( signAlgo == CRYPT_ALGO_RSA ) ? \
						   "ssh-rsa" : "ssh-dss", 7 );
	return( writeString32( stream, signature, signatureLength ) );
	}
#endif /* USE_SSH */

#ifdef USE_SSL

/* Read/write SSL signatures.  This is just a raw signature without any
   encapsulation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readSslSignature( INOUT STREAM *stream, 
							 INOUT QUERY_INFO *queryInfo )
	{
	const int startPos = stell( stream );
	int length;

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

	/* Read the start of the signature */
	length = readUint16( stream );
	if( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->dataStart = stell( stream ) - startPos;
	queryInfo->dataLength = length;

	/* Make sure that the remaining signature data is present */
	return( sSkip( stream, length ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 5 ) ) \
static int writeSslSignature( INOUT STREAM *stream,
							  STDC_UNUSED const CRYPT_CONTEXT iSignContext,
							  STDC_UNUSED const CRYPT_ALGO_TYPE hashAlgo,
							  STDC_UNUSED const CRYPT_ALGO_TYPE signAlgo,
							  IN_BUFFER( signatureLength ) const BYTE *signature,
							  IN_LENGTH_SHORT_MIN( 18 + 18 + 1 ) \
								const int signatureLength )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( signature, signatureLength ) );
			/* Other parameters aren't used for this format */

	REQUIRES( signatureLength > ( 18 + 18 ) && \
			  signatureLength < MAX_INTLENGTH_SHORT );

	writeUint16( stream, signatureLength );
	return( swrite( stream, signature, signatureLength ) );
	}
#endif /* USE_SSL */

/****************************************************************************
*																			*
*					Signature Read/Write Access Functions					*
*																			*
****************************************************************************/

typedef struct {
	const SIGNATURE_TYPE type;
	const READSIG_FUNCTION function;
	} SIG_READ_INFO;
static const SIG_READ_INFO sigReadTable[] = {
	{ SIGNATURE_RAW, readRawSignature },
	{ SIGNATURE_X509, readX509Signature },
	{ SIGNATURE_CMS, readCmsSignature },
	{ SIGNATURE_CRYPTLIB, readCryptlibSignature },
#ifdef USE_PGP
	{ SIGNATURE_PGP, readPgpSignature },
#endif /* USE_PGP */
#ifdef USE_SSH
	{ SIGNATURE_SSH, readSshSignature },
#endif /* USE_SSH */
#ifdef USE_SSL
	{ SIGNATURE_SSL, readSslSignature },
#endif /* USE_SSL */
	{ SIGNATURE_NONE, NULL }, { SIGNATURE_NONE, NULL }
	};

typedef struct {
	const SIGNATURE_TYPE type;
	const WRITESIG_FUNCTION function;
	} SIG_WRITE_INFO;
static const SIG_WRITE_INFO sigWriteTable[] = {
	{ SIGNATURE_RAW, writeRawSignature },
	{ SIGNATURE_X509, writeX509Signature },
	{ SIGNATURE_CMS, writeCmsSignature },
	{ SIGNATURE_CRYPTLIB, writeCryptlibSignature },
#ifdef USE_PGP
	{ SIGNATURE_PGP, writePgpSignature },
#endif /* USE_PGP */
#ifdef USE_SSH
	{ SIGNATURE_SSH, writeSshSignature },
#endif /* USE_SSH */
#ifdef USE_SSL
	{ SIGNATURE_SSL, writeSslSignature },
#endif /* USE_SSH */
	{ SIGNATURE_NONE, NULL }, { SIGNATURE_NONE, NULL }
	};

CHECK_RETVAL_PTR \
READSIG_FUNCTION getReadSigFunction( IN_ENUM( SIGNATURE ) \
										const SIGNATURE_TYPE sigType )
	{
	int i;

	REQUIRES_N( sigType > SIGNATURE_NONE && sigType < SIGNATURE_LAST );

	for( i = 0; 
		 sigReadTable[ i ].type != SIGNATURE_NONE && \
			i < FAILSAFE_ARRAYSIZE( sigReadTable, SIG_READ_INFO ); 
		 i++ )
		{
		if( sigReadTable[ i ].type == sigType )
			return( sigReadTable[ i ].function );
		}
	ENSURES_N( i < FAILSAFE_ARRAYSIZE( sigReadTable, SIG_READ_INFO ) );

	return( NULL );
	}
CHECK_RETVAL_PTR \
WRITESIG_FUNCTION getWriteSigFunction( IN_ENUM( SIGNATURE ) \
										const SIGNATURE_TYPE sigType )
	{
	int i;

	REQUIRES_N( sigType > SIGNATURE_NONE && sigType < SIGNATURE_LAST );

	for( i = 0; 
		 sigWriteTable[ i ].type != SIGNATURE_NONE && \
			i < FAILSAFE_ARRAYSIZE( sigWriteTable, SIG_WRITE_INFO ); 
		 i++ )
		{
		if( sigWriteTable[ i ].type == sigType )
			return( sigWriteTable[ i ].function );
		}
	ENSURES_N( i < FAILSAFE_ARRAYSIZE( sigWriteTable, SIG_WRITE_INFO ) );

	return( NULL );
	}
