📄 mech_pkwrap.c
字号:
/****************************************************************************
* *
* cryptlib PKC Key Wrap Mechanism Routines *
* Copyright Peter Gutmann 1992-2008 *
* *
****************************************************************************/
#ifdef INC_ALL
#include "crypt.h"
#include "mech_int.h"
#include "asn1.h"
#include "misc_rw.h"
#include "pgp.h"
#else
#include "crypt.h"
#include "mechs/mech_int.h"
#include "misc/asn1.h"
#include "misc/misc_rw.h"
#include "misc/pgp.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
/* PGP checksums the PKCS #1 wrapped data even though this doesn't really
serve any purpose since any decryption error will corrupt the PKCS #1
padding, the following routine calculates this checksum and either
appends it to the data or checks it against the stored value */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int pgpGenerateChecksum( INOUT_BUFFER_FIXED( dataLength ) void *data,
IN_RANGE( 1, CRYPT_MAX_PKCSIZE + UINT16_SIZE ) \
const int dataLength,
IN_LENGTH_PKC const int keyDataLength )
{
STREAM stream;
BYTE *dataPtr = data;
int checksum = 0, i, status;
assert( isWritePtr( data, dataLength ) );
REQUIRES( dataLength > 0 && \
dataLength <= CRYPT_MAX_PKCSIZE + UINT16_SIZE );
REQUIRES( keyDataLength == dataLength - UINT16_SIZE );
/* Calculate the checksum for the MPI */
for( i = 0; i < keyDataLength; i++ )
checksum += dataPtr[ i ];
/* Append the checksum to the MPI data */
sMemOpen( &stream, dataPtr + keyDataLength, dataLength - keyDataLength );
status = writeUint16( &stream, checksum );
sMemDisconnect( &stream );
return( status );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN pgpVerifyChecksum( IN_BUFFER( dataLength ) const void *data,
IN_RANGE( 1, CRYPT_MAX_PKCSIZE + UINT16_SIZE ) \
const int dataLength )
{
STREAM stream;
int checksum = 0, storedChecksum, i;
assert( isReadPtr( data, dataLength ) );
REQUIRES_B( dataLength > UINT16_SIZE && \
dataLength <= CRYPT_MAX_PKCSIZE + UINT16_SIZE );
/* Calculate the checksum for the MPI and compare it to the appended
checksum. We don't have to check the return status of sgetc()
because an error status returned will cause the checksum comparison
to fail */
sMemConnect( &stream, data, dataLength );
for( i = 0; i < dataLength - UINT16_SIZE; i++ )
{
const int ch = sgetc( &stream );
if( cryptStatusError( ch ) )
{
sMemDisconnect( &stream );
return( FALSE );
}
checksum += ch;
}
storedChecksum = readUint16( &stream );
sMemDisconnect( &stream );
return( storedChecksum == checksum );
}
/* PGP includes the session key information alongside the encrypted key so
it's not really possible to import the key into a context in the
conventional sense. Instead, the import code has to create the context
as part of the import process and return it to the caller. This is ugly,
but less ugly than doing a raw import and handling the key directly in
the calling code */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int pgpExtractKey( OUT_HANDLE_OPT CRYPT_CONTEXT *iCryptContext,
IN_BUFFER( dataLength ) const BYTE *data,
IN_LENGTH_SHORT const int dataLength )
{
CRYPT_ALGO_TYPE cryptAlgo = CRYPT_ALGO_NONE;
MESSAGE_CREATEOBJECT_INFO createInfo;
static const CRYPT_MODE_TYPE mode = CRYPT_MODE_CFB;
int status;
assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) );
assert( isReadPtr( data, dataLength ) );
REQUIRES( dataLength >= MIN_KEYSIZE && dataLength <= CRYPT_MAX_PKCSIZE );
/* Clear return value */
*iCryptContext = CRYPT_ERROR;
/* Get the session key algorithm. We delay checking the algorithm ID
until after the checksum calculation to reduce the chance of being
used as an oracle */
status = pgpToCryptlibAlgo( data[ 0 ], PGP_ALGOCLASS_CRYPT, &cryptAlgo );
/* Checksum the session key, skipping the algorithm ID at the start and
the checksum at the end. This is actually superfluous since any
decryption error will be caught by corrupted PKCS #1 padding with
vastly higher probability than this simple checksum but we do it
anyway because other PGP implementations do it too */
if( !pgpVerifyChecksum( data + 1, dataLength - 1 ) )
return( CRYPT_ERROR_BADDATA );
/* Make sure that the algorithm ID is valid. We only perform the check
at this point because this returns a different error code than the
usual bad-data, we want to be absolutely sure that the problem really
is an unknown algorithm and not the result of scrambled decrypted
data */
if( cryptStatusError( status ) )
return( CRYPT_ERROR_NOTAVAIL );
/* Create the context ready to have the key loaded into it */
setMessageCreateObjectInfo( &createInfo, cryptAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
( void * ) &mode, CRYPT_CTXINFO_MODE );
if( cryptStatusError( status ) )
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
else
*iCryptContext = createInfo.cryptHandle;
return( CRYPT_OK );
}
#endif /* USE_PGP || USE_PGPKEYS */
/****************************************************************************
* *
* Low-level Data Wrap/Unwrap Routines *
* *
****************************************************************************/
/* Wrap/unwrap data using a public/private-key context */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int pkcWrapData( INOUT MECHANISM_WRAP_INFO *mechanismInfo,
INOUT_BUFFER_FIXED( wrappedDataLength ) BYTE *wrappedData,
IN_LENGTH_PKC const int wrappedDataLength,
const BOOLEAN usePgpWrap, const BOOLEAN isDlpAlgo )
{
BYTE dataSample[ 16 + 8 ];
const void *samplePtr = wrappedData + ( wrappedDataLength / 2 );
int status;
assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_WRAP_INFO ) ) );
assert( isWritePtr( wrappedData, wrappedDataLength ) );
REQUIRES( wrappedDataLength >= MIN_PKCSIZE && \
wrappedDataLength <= CRYPT_MAX_PKCSIZE );
REQUIRES( wrappedData[ 0 ] == 0x00 && \
( wrappedData[ 1 ] == 0x01 || wrappedData[ 1 ] == 0x02 ) );
/* Take a sample of the input for comparison with the output */
memcpy( dataSample, samplePtr, 16 );
if( isDlpAlgo )
{
DLP_PARAMS dlpParams;
/* For DLP-based PKC's the output length isn't the same as the key
size so we adjust the return length as required */
setDLPParams( &dlpParams, wrappedData, wrappedDataLength,
wrappedData, mechanismInfo->wrappedDataLength );
if( usePgpWrap )
dlpParams.formatType = CRYPT_FORMAT_PGP;
status = krnlSendMessage( mechanismInfo->wrapContext,
IMESSAGE_CTX_ENCRYPT, &dlpParams,
sizeof( DLP_PARAMS ) );
if( cryptStatusOK( status ) )
mechanismInfo->wrappedDataLength = dlpParams.outLen;
}
else
{
status = krnlSendMessage( mechanismInfo->wrapContext,
IMESSAGE_CTX_ENCRYPT, wrappedData,
wrappedDataLength );
if( cryptStatusOK( status ) )
mechanismInfo->wrappedDataLength = wrappedDataLength;
}
if( cryptStatusOK( status ) && !memcmp( dataSample, samplePtr, 16 ) )
{
/* The data to wrap is unchanged, there's been a catastrophic
failure of the encryption. We don't do a retIntError() at this
point because we want to at least continue and zeroise the data
first */
assert( FALSE );
status = CRYPT_ERROR_FAILED;
}
zeroise( dataSample, 16 );
if( cryptStatusError( status ) )
{
/* There was a problem with the wrapping, clear the output value */
zeroise( wrappedData, wrappedDataLength );
}
return( status );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int pkcUnwrapData( MECHANISM_WRAP_INFO *mechanismInfo,
INOUT_BUFFER( dataMaxLength, *dataOutLength ) \
BYTE *data,
IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \
const int dataMaxLength,
OUT_LENGTH_PKC_Z int *dataOutLength,
IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \
const int dataInLength,
const BOOLEAN usePgpWrap,
const BOOLEAN isDlpAlgo )
{
int status;
assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_WRAP_INFO ) ) );
assert( isWritePtr( data, dataMaxLength ) );
assert( isWritePtr( dataOutLength, sizeof( int ) ) );
REQUIRES( dataMaxLength >= MIN_PKCSIZE && \
dataMaxLength <= MAX_INTLENGTH_SHORT );
REQUIRES( dataInLength >= MIN_PKCSIZE && \
dataInLength <= dataMaxLength && \
dataInLength <= MAX_INTLENGTH_SHORT );
/* Clear return values */
memset( data, 0, min( 16, dataMaxLength ) );
*dataOutLength = 0;
if( isDlpAlgo )
{
DLP_PARAMS dlpParams;
setDLPParams( &dlpParams, mechanismInfo->wrappedData,
mechanismInfo->wrappedDataLength, data,
dataMaxLength );
if( usePgpWrap )
dlpParams.formatType = CRYPT_FORMAT_PGP;
status = krnlSendMessage( mechanismInfo->wrapContext,
IMESSAGE_CTX_DECRYPT, &dlpParams,
sizeof( DLP_PARAMS ) );
if( cryptStatusOK( status ) )
{
*dataOutLength = dlpParams.outLen;
return( CRYPT_OK );
}
}
else
{
status = adjustPKCS1Data( data, dataMaxLength,
mechanismInfo->wrappedData,
mechanismInfo->wrappedDataLength,
dataInLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( mechanismInfo->wrapContext,
IMESSAGE_CTX_DECRYPT, data,
dataInLength );
if( cryptStatusOK( status ) )
{
*dataOutLength = dataInLength;
return( CRYPT_OK );
}
}
/* There was a problem with the wrapping, clear the output value */
zeroise( data, dataMaxLength );
return( status );
}
/****************************************************************************
* *
* PKCS #1 Wrap/Unwrap Mechanisms *
* *
****************************************************************************/
/* Generate/recover a PKCS #1 data block */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int generatePkcs1DataBlock( INOUT_BUFFER( dataMaxLen, *dataLength ) \
BYTE *data,
IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \
const int dataMaxLen,
OUT_LENGTH_SHORT_Z int *dataLength,
IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \
const int messageLen )
{
MESSAGE_DATA msgData;
const int padSize = dataMaxLen - ( messageLen + 3 );
int status;
assert( isWritePtr( data, dataMaxLen ) );
assert( isWritePtr( dataLength, sizeof( int ) ) );
REQUIRES( dataMaxLen >= MIN_PKCSIZE && dataMaxLen < MAX_INTLENGTH_SHORT );
REQUIRES( messageLen >= MIN_KEYSIZE && messageLen < dataMaxLen && \
messageLen < MAX_INTLENGTH_SHORT );
/* Clear return values */
memset( data, 0, min( 16, dataMaxLen ) );
*dataLength = 0;
/* Determine PKCS #1 padding parameters and make sure that the key is
long enough to encrypt the payload. PKCS #1 requires that the
maximum payload size be 11 bytes less than the length to give a
minimum of 8 bytes of random padding */
if( messageLen > dataMaxLen - 11 )
return( CRYPT_ERROR_OVERFLOW );
ENSURES( padSize >= 8 && messageLen + padSize + 3 <= dataMaxLen );
/* Encode the payload using the PKCS #1 format:
[ 0 ][ 2 ][ nonzero random padding ][ 0 ][ payload ]
Note that the random padding is a nice place for a subliminal channel,
especially with the larger public key sizes where you can communicate
more information in the padding than you can in the payload */
data[ 0 ] = 0;
data[ 1 ] = 2;
setMessageData( &msgData, data + 2, padSize );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM_NZ );
if( cryptStatusError( status ) )
{
zeroise( data, dataMaxLen );
return( status );
}
data[ 2 + padSize ] = 0;
*dataLength = 2 + padSize + 1;
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int recoverPkcs1DataBlock( INOUT_BUFFER_FIXED( dataLength ) \
BYTE *data,
IN_LENGTH_SHORT_MIN( MIN_PKCSIZE ) \
const int dataLength,
OUT_LENGTH_SHORT_Z int *padSize )
{
int ch, length;
assert( isWritePtr( data, dataLength ) );
assert( isWritePtr( padSize, sizeof( int ) ) );
REQUIRES( dataLength >= MIN_PKCSIZE && dataLength < MAX_INTLENGTH );
/* Clear return value */
*padSize = 0;
/* Undo the PKCS #1 padding:
[ 0 ][ 2 ][ random nonzero padding ][ 0 ][ payload ]
with a minimum of 8 bytes padding. Note that some implementations
may have bignum code that zero-truncates the result which will
produce a CRYPT_ERROR_BADDATA error when we undo the padding, it's
the responsibility of the lower-level crypto layer to reformat the
data to return a correctly-formatted result if necessary.
In order to avoid being used as a decription timing oracle we bundle
all of the formatting checks into a single location and make the code
as simple and quick as possible. At best an attacker will get only a
few clock cycles of timing information, which should be lost in the
general noise */
if( dataLength < 11 + MIN_KEYSIZE )
{
/* PKCS #1 padding requires at least 11 (2 + 8 + 1) bytes of
padding data, if there isn't this much present then what we've
got can't be a valid payload */
return( CRYPT_ERROR_BADDATA );
}
if( data[ 0 ] != 0x00 || data[ 1 ] != 0x02 )
return( CRYPT_ERROR_BADDATA );
for( length = 2, ch = 0xFF;
length < dataLength - MIN_KEYSIZE && \
( ch = data[ length ] ) != 0x00;
length++ );
if( ch != 0x00 || length < 11 )
return( CRYPT_ERROR_BADDATA );
length++; /* Skip the final 0x00 */
/* Sanity check to make sure that the padding data looks OK. We only do
this in debug mode since it's a probabalistic test and we don't want
to bail out due to a false positive in production code */
assert( checkEntropy( data + 2, length - 1 ) );
/* Make sure that there's enough room left after the remaining PKCS #1
padding to hold at least a minimum-length key */
if( dataLength - length < MIN_KEYSIZE )
return( CRYPT_ERROR_BADDATA );
*padSize = length;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -