/****************************************************************************
*																			*
*					cryptlib Internal Mechanism Routines					*
*					  Copyright Peter Gutmann 1992-2006						*
*																			*
****************************************************************************/

#ifdef INC_ALL
  #include "crypt.h"
  #include "mech_int.h"
#else
  #include "crypt.h"
  #include "mechs/mech_int.h"
#endif /* Compiler-specific includes */

/****************************************************************************
*																			*
*									PKC Routines							*
*																			*
****************************************************************************/

/* The length of the input data for PKCS #1 transformations is usually
   determined by the key size, however sometimes we can be passed data that
   has been zero-padded (for example data coming from an ASN.1 INTEGER in
   which the high bit is a sign bit) making it longer than the key size, or
   that has leading zero byte(s), making it shorter than the key size.  The
   best place to handle this is somewhat uncertain, it's an encoding issue
   so it probably shouldn't be visible to the raw crypto routines, but
   putting it at the mechanism layer removes the algorithm-independence of
   that layer, and putting it at the mid-level sign/key-exchange routine
   layer both removes the algorithm-independence and requires duplication of
   the code for signatures and encryption.  The best place to put it seems to
   be at the mechanism layer, since an encoding issue really shouldn't be
   visible at the crypto layer, and because it would require duplicating the
   handling every time a new PKC implementation is plugged in.

   The intent of the size adjustment is to make the data size match the key
   length.  If it's longer, we try to strip leading zero bytes.  If it's
   shorter, we pad it with zero bytes to match the key size.  The result is
   either the data adjusted to match the key size, or CRYPT_ERROR_BADDATA if
   this isn't possible */

int adjustPKCS1Data( BYTE *outData, const int outDataMaxLen, 
					 const BYTE *inData, const int inLen, const int keySize )
	{
	int length, i;

	assert( isWritePtr( outData, outDataMaxLen ) );
	assert( isReadPtr( inData, inLen ) );
	assert( keySize >= MIN_PKCSIZE && keySize <= CRYPT_MAX_PKCSIZE );
	assert( outData != inData );

	/* Make sure that the result will fit in the output buffer.  This has 
	   already been checked by the kernel mechanism ACL, but we make the 
	   check explicit here */
	if( keySize > outDataMaxLen )
		return( CRYPT_ERROR_OVERFLOW );

	/* Find the start of the data payload.  If it's suspiciously short, 
	   don't try and process it */
	for( i = 0; i < inLen && inData[ i ] == 0; i++ );
	length = inLen - i;
	if( length < MIN_PKCSIZE )
		return( CRYPT_ERROR_BADDATA );

	/* If it's of the correct size, exit */
	if( inLen == keySize )
		{
		memcpy( outData, inData, keySize );
		return( CRYPT_OK );
		}

	/* If it's too long, try and strip leading zero bytes.  If it's still too
	   long, complain */
	while( length > keySize && *inData == 0 )
		{
		length--;
		inData++;
		}
	if( length > keySize )
		return( CRYPT_ERROR_BADDATA );

	/* If it's suspiciously short, don't try and process it */
	if( length < MIN_PKCSIZE - 8 )
		return( CRYPT_ERROR_BADDATA );

	/* We've adjusted the size to account for zero-padding during encoding,
	   now we have to move the data into a fixed-length format to match the
	   key size.  To do this we copy the payload into the output buffer with
	   enough leading-zero bytes to bring the total size up to the key size */
	memset( outData, 0, keySize );
	memcpy( outData + ( keySize - length ), inData, length );

	return( CRYPT_OK );
	}

/* Get PKC algorithm parameters */

int getPkcAlgoParams( const CRYPT_CONTEXT pkcContext,
					  CRYPT_ALGO_TYPE *pkcAlgo, int *pkcKeySize )
	{
	int status;

	assert( isHandleRangeValid( pkcContext ) );
	assert( ( pkcAlgo == NULL ) || \
			isWritePtr( pkcAlgo, sizeof( CRYPT_ALGO_TYPE ) ) );
	assert( isWritePtr( pkcKeySize, sizeof( int ) ) );

	/* Clear return values */
	if( pkcAlgo != NULL )
		*pkcAlgo = CRYPT_ALGO_NONE;
	*pkcKeySize = 0;

	/* Get various PKC algorithm parameters */
	if( pkcAlgo != NULL )
		{
		status = krnlSendMessage( pkcContext, IMESSAGE_GETATTRIBUTE, 
								  pkcAlgo, CRYPT_CTXINFO_ALGO );
		if( cryptStatusError( status ) )
			return( status );
		}
	return( krnlSendMessage( pkcContext, IMESSAGE_GETATTRIBUTE, 
							 pkcKeySize, CRYPT_CTXINFO_KEYSIZE ) );
	}

/****************************************************************************
*																			*
*									Hash Routines							*
*																			*
****************************************************************************/

/* Get hash algorithm parameters */

int getHashAlgoParams( const CRYPT_CONTEXT hashContext,
					   CRYPT_ALGO_TYPE *hashAlgo, int *hashSize )
	{
	int status;

	assert( isHandleRangeValid( hashContext ) );
	assert( isWritePtr( hashAlgo, sizeof( CRYPT_ALGO_TYPE ) ) );
	assert( ( hashSize == NULL ) || \
			isWritePtr( hashSize, sizeof( int ) ) );

	/* Clear return values */
	if( hashSize != NULL )
		*hashSize = 0;
	*hashAlgo = CRYPT_ALGO_NONE;

	/* Get various PKC algorithm parameters */
	if( hashSize != NULL )
		{
		status = krnlSendMessage( hashContext, IMESSAGE_GETATTRIBUTE, 
								  hashSize, CRYPT_CTXINFO_KEYSIZE );
		if( cryptStatusError( status ) )
			return( status );
		}
	return( krnlSendMessage( hashContext, IMESSAGE_GETATTRIBUTE, 
							 hashAlgo, CRYPT_CTXINFO_ALGO ) );
	}

