📄 mech_sig.c
字号:
/****************************************************************************
* *
* cryptlib Signature Mechanism Routines *
* Copyright Peter Gutmann 1992-2008 *
* *
****************************************************************************/
#ifdef INC_ALL
#include "crypt.h"
#include "mech_int.h"
#include "asn1.h"
#include "asn1_ext.h"
#else
#include "crypt.h"
#include "mechs/mech_int.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Unlike PKCS #1 encryption there isn't any minimum-weight requirement for
the PKCS #1 signature padding, however we require a set minimum number of
bytes of 0xFF padding because if they're not present then there's
something funny going on. For a given key size we require that all but
( 3 bytes PKCS #1 formatting + ( 2 + 15 + 2 ) bytes ASN.1 wrapper +
CRYPT_MAX_HASHSIZE bytes hash ) be 0xFF padding */
#define getMinPadBytes( length ) \
( ( length ) - ( 3 + 19 + CRYPT_MAX_HASHSIZE ) )
/* Decode PKCS #1 signature formatting */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int decodePKCS1( INOUT STREAM *stream,
IN_LENGTH_PKC const int length )
{
int ch, i;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( length >= MIN_PKCSIZE && length <= CRYPT_MAX_PKCSIZE );
/* Decode the payload using the PKCS #1 format:
[ 0 ][ 1 ][ 0xFF padding ][ 0 ][ payload ]
Note that some implementations may have bignum code that zero-
truncates the RSA data, which would remove the leading zero from the
PKCS #1 padding and produce a CRYPT_ERROR_BADDATA error. It's the
responsibility of the lower-level crypto layer to reformat the data
to return a correctly-formatted result if necessary */
if( sgetc( stream ) != 0 || sgetc( stream ) != 1 )
{
/* No [ 0 ][ 1 ] at start */
return( CRYPT_ERROR_BADDATA );
}
for( i = 2, ch = 0xFF; ( i < length - 16 ) && ( ch == 0xFF ); i++ )
{
ch = sgetc( stream );
if( cryptStatusError( ch ) )
return( CRYPT_ERROR_BADDATA );
}
if( ch != 0 || i < getMinPadBytes( length ) || i >= length - 16 )
{
/* No [ 0 ] at end or insufficient/excessive 0xFF padding */
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
/* Compare the ASN.1-encoded hash value in the signature with the hash
information that we've been given. We have to be very careful how we
handle this because we don't want to allow an attacker to inject random
data into gaps in the encoding, which would allow for signature forgery
if small exponents are used (although cryptlib disallows any exponents
that make this easy). The obvious approach of using
checkObjectEncoding() doesn't work because an attacker can still encode
the signature in a form that's syntactically valid ASN.1, just not the
correct ASN.1 for a MessageDigest record. To avoid having to have every
function that handles reading the hash value be anal-retentive about
every data element that it reads, we take the hash value that we've been
given and encode it correctly as a MessageDigest record and then do a
straight memcmp() of the encoded form rather than trying to validity-
check the externally-supplied value */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int compareHashInfo( INOUT STREAM *stream,
IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
IN_BUFFER( hashSize ) const void *hash,
IN_LENGTH_HASH const int hashSize )
{
STREAM mdStream;
BYTE encodedMD[ 32 + CRYPT_MAX_HASHSIZE + 8 ];
BYTE recreatedMD[ 32 + CRYPT_MAX_HASHSIZE + 8 ];
int encodedMdLength, recreatedMdLength = DUMMY_INIT;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( hash, hashSize ) );
REQUIRES( hashAlgo >= CRYPT_ALGO_FIRST_HASH && \
hashAlgo <= CRYPT_ALGO_LAST_HASH );
REQUIRES( hashSize >= 16 && hashSize <= CRYPT_MAX_HASHSIZE );
/* Read the encoded hash data as a blob */
status = readRawObject( stream, encodedMD, 32 + CRYPT_MAX_HASHSIZE,
&encodedMdLength, BER_SEQUENCE );
if( cryptStatusError( status ) )
return( status );
/* Write the supplied hash information into an encoded blob */
sMemOpen( &mdStream, recreatedMD, 32 + CRYPT_MAX_HASHSIZE );
status = writeMessageDigest( &mdStream, hashAlgo, hash, hashSize );
if( cryptStatusOK( status ) )
recreatedMdLength = stell( &mdStream );
sMemDisconnect( &mdStream );
if( cryptStatusError( status ) )
return( status );
/* Compare the two encoded blobs */
if( encodedMdLength != recreatedMdLength || \
memcmp( encodedMD, recreatedMD, encodedMdLength ) )
status = CRYPT_ERROR_SIGNATURE;
zeroise( encodedMD, 32 + CRYPT_MAX_HASHSIZE );
zeroise( recreatedMD, 32 + CRYPT_MAX_HASHSIZE );
return( status );
}
/* Make sure that the recovered signature data matches the data the we
originally signed. The rationale behind this operation is covered (in
great detail) in ctx_rsa.c */
static int checkRecoveredSignature( IN_HANDLE const CRYPT_CONTEXT iSignContext,
IN_BUFFER( sigDataLen ) const void *sigData,
IN_LENGTH_PKC const int sigDataLen,
IN_BUFFER( sigLen ) const void *signature,
IN_LENGTH_PKC const int sigLen )
{
BYTE recoveredSignature[ CRYPT_MAX_PKCSIZE + 8 ];
int status;
assert( isReadPtr( sigData, sigDataLen ) );
assert( isReadPtr( signature, sigLen ) );
REQUIRES( sigDataLen >= MIN_PKCSIZE && sigDataLen <= CRYPT_MAX_PKCSIZE );
REQUIRES( sigLen >= MIN_PKCSIZE && sigLen <= CRYPT_MAX_PKCSIZE );
/* Recover the original signature data, unless we're in the unlikely
situation that the key isn't valid for signature checking */
memcpy( recoveredSignature, signature, sigLen );
status = krnlSendMessage( iSignContext, IMESSAGE_CTX_SIGCHECK,
recoveredSignature, sigLen );
if( status == CRYPT_ERROR_PERMISSION || status == CRYPT_ERROR_NOTAVAIL )
{
/* The key can't be used for signature checking, there's not much
that we can do */
return( CRYPT_OK );
}
if( cryptStatusError( status ) )
return( CRYPT_ERROR_FAILED );
/* Make sure that recovered data matches the original data */
if( sigDataLen != sigLen || \
memcmp( sigData, recoveredSignature, sigLen ) )
{
assert( DEBUG_WARN );
status = CRYPT_ERROR_FAILED;
}
zeroise( recoveredSignature, CRYPT_MAX_PKCSIZE );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Signature Mechanisms *
* *
****************************************************************************/
/* Perform signing. There are several variations of this that are handled
through common signature mechanism functions */
typedef enum { SIGN_NONE, SIGN_PKCS1, SIGN_SSL, SIGN_LAST } SIGN_TYPE;
/* Perform PKCS #1 signing/sig.checking */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int sign( INOUT MECHANISM_SIGN_INFO *mechanismInfo,
IN_ENUM( SIGN ) const SIGN_TYPE type )
{
CRYPT_ALGO_TYPE hashAlgo = DUMMY_INIT;
MESSAGE_DATA msgData;
STREAM stream;
BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ], hash2[ CRYPT_MAX_HASHSIZE + 8 ];
BYTE preSigData[ CRYPT_MAX_PKCSIZE + 8 ];
int sideChannelProtectionLevel = DUMMY_INIT;
int hashSize, hashSize2 = DUMMY_INIT, length, i, status;
assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_SIGN_INFO ) ) );
REQUIRES( type > SIGN_NONE && type < SIGN_LAST );
/* Clear return value */
if( mechanismInfo->signature != NULL )
{
memset( mechanismInfo->signature, 0,
mechanismInfo->signatureLength );
}
/* Get various algorithm and config parameters */
status = getPkcAlgoParams( mechanismInfo->signContext, NULL,
&length );
if( cryptStatusOK( status ) )
status = getHashAlgoParams( mechanismInfo->hashContext,
&hashAlgo, NULL );
if( cryptStatusOK( status ) )
{
status = krnlSendMessage( mechanismInfo->signContext,
IMESSAGE_GETATTRIBUTE,
&sideChannelProtectionLevel,
CRYPT_OPTION_MISC_SIDECHANNELPROTECTION );
}
if( cryptStatusError( status ) )
return( status );
/* If this is just a length check, we're done */
if( mechanismInfo->signature == NULL )
{
mechanismInfo->signatureLength = length;
return( CRYPT_OK );
}
/* Get the hash data and determine the encoded payload size */
setMessageData( &msgData, hash, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( mechanismInfo->hashContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_HASHVALUE );
if( cryptStatusError( status ) )
return( status );
hashSize = msgData.length;
if( type == SIGN_SSL )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -