📄 mech_privk.c
字号:
/****************************************************************************
* *
* cryptlib Private-Key Wrap Mechanism Routines *
* Copyright Peter Gutmann 1992-2008 *
* *
****************************************************************************/
#ifdef INC_ALL
#include "crypt.h"
#include "stream.h"
#include "mech_int.h"
#include "asn1.h"
#include "misc_rw.h"
#include "pgp.h"
#else
#include "crypt.h"
#include "io/stream.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 )
/* Decrypt a PGP MPI */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int pgpReadDecryptMPI( INOUT STREAM *stream,
IN_HANDLE const CRYPT_CONTEXT iCryptContext,
IN_LENGTH_PKC const int minLength,
IN_LENGTH_PKC const int maxLength )
{
void *mpiDataPtr = DUMMY_INIT_PTR;
const long mpiDataStartPos = stell( stream ) + UINT16_SIZE;
int mpiLength, dummy, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( isHandleRangeValid( iCryptContext ) );
REQUIRES( minLength >= bitsToBytes( 155 ) && \
minLength <= maxLength && \
maxLength <= CRYPT_MAX_PKCSIZE );
/* Get the MPI length and decrypt the payload data. We have to be
careful how we handle this because readInteger16Ubits() returns the
canonicalised form of the values (with leading zeroes truncated) so
the returned length value doesn't necessarily represent the amount
of data that we need to decrypt:
startPos dataStart stell()
| | |
v v <-- length -->v
+---+-----------+---------------+
| | |///////////////| Stream
+---+-----------+---------------+ */
status = readInteger16Ubits( stream, NULL, &dummy, minLength,
maxLength );
if( cryptStatusError( status ) )
return( status );
mpiLength = stell( stream ) - mpiDataStartPos;
status = sMemGetDataBlockAbs( stream, mpiDataStartPos, &mpiDataPtr,
mpiLength );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCryptContext, IMESSAGE_CTX_DECRYPT,
mpiDataPtr, mpiLength );
return( status );
}
/* The PGP 2.x key wrap encrypts only the MPI payload data rather than the
entire private key record so we have to read and then decrypt each
component separately. This is a horrible way to handle things because we
have to repeatedly process the MPI data, first in the PGP keyring code to
find out how much key data is present, then again during decryption to
find the MPI payload that needs to be decrypted, and finally again after
decryption to find the MPI payload that needs to be checksummed (although
we can shortcut the latter, see the comment in checkPgp2KeyIntegrity().
Unfortunately we can't use the xxxPARAM_MIN_x / xxxPARAM_MAX_x values to
perform range checking since they're only visible to the context-
manipulation code, so we have to use approximations here (the actual
values will be checked by the keyload code anyway, it just means that we
can't perform very precise pre-filtering here) */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int pgp2DecryptKey( IN_BUFFER( dataLength ) const void *data,
IN_LENGTH_SHORT_MIN( 16 ) const int dataLength,
OUT_LENGTH_SHORT_Z int *bytesToChecksum,
IN_HANDLE const CRYPT_CONTEXT iCryptContext,
const BOOLEAN isDlpAlgo )
{
STREAM stream;
int status;
assert( isReadPtr( data, dataLength ) );
assert( isWritePtr( bytesToChecksum, sizeof( int ) ) );
REQUIRES( dataLength >= 16 && \
dataLength < MAX_INTLENGTH_SHORT );
REQUIRES( isHandleRangeValid( iCryptContext ) );
/* Clear return value */
*bytesToChecksum = 0;
sMemConnect( &stream, data, dataLength );
status = pgpReadDecryptMPI( &stream, iCryptContext, /* d or x */
bitsToBytes( 155 ), CRYPT_MAX_PKCSIZE );
if( isDlpAlgo )
{
if( cryptStatusOK( status ) )
*bytesToChecksum = stell( &stream );
sMemDisconnect( &stream );
return( status );
}
if( cryptStatusOK( status ) )
status = pgpReadDecryptMPI( &stream, iCryptContext, /* p */
MIN_PKCSIZE / 2, CRYPT_MAX_PKCSIZE );
if( cryptStatusOK( status ) )
status = pgpReadDecryptMPI( &stream, iCryptContext, /* q */
MIN_PKCSIZE / 2, CRYPT_MAX_PKCSIZE );
if( cryptStatusOK( status ) )
status = pgpReadDecryptMPI( &stream, iCryptContext, /* u */
MIN_PKCSIZE / 2, CRYPT_MAX_PKCSIZE );
if( cryptStatusOK( status ) )
*bytesToChecksum = stell( &stream );
sMemDisconnect( &stream );
return( status );
}
#endif /* USE_PGP || USE_PGPKEYS */
/* Check that the unwrapped data hasn't been corrupted */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkKeyIntegrity( IN_BUFFER( dataLength ) const void *data,
IN_LENGTH_SHORT_MIN( MIN_PRIVATE_KEYSIZE ) \
const int dataLength,
IN_RANGE( 8, CRYPT_MAX_IVSIZE ) \
const int blockSize )
{
const BYTE *padPtr;
int length, padSize, i, status;
assert( isReadPtr( data, dataLength ) );
REQUIRES( dataLength >= MIN_PRIVATE_KEYSIZE && \
dataLength < MAX_INTLENGTH_SHORT );
REQUIRES( blockSize >= 8 && blockSize <= CRYPT_MAX_IVSIZE );
/* Get the length of the encapsulated ASN.1 object */
status = getObjectLength( data, dataLength, &length );
if( cryptStatusError( status ) )
{
return( ( status == CRYPT_ERROR_BADDATA ) ? \
CRYPT_ERROR_WRONGKEY : status );
}
/* Check that the PKCS #5 padding is as expected. Performing the check
this way is the reverse of the way that it's usually done because we
already know the payload size from the ASN.1 and can use this to
determine the expected padding value and thus check that the end of
the encrypted data hasn't been subject to a bit-flipping attack. For
example for RSA private keys the end of the data is:
[ INTEGER u ][ INTEGER keySize ][ padding ]
where the keySize is encoded as a 4-byte value and the padding is 1-8
bytes. If the low bits of u are flipped there's a 5/8 chance that
either the keySize value (checked in the RSA read code) or padding
will be messed up, both of which will be detected (in addition the
RSA key load checks try and verify u when the key is loaded). For
DLP keys the end of the data is:
[ INTEGER x ][ padding ]
for which bit flipping is rather harder to detect since 7/8 of the
time the following block won't be affected, however the DLP key load
checks also verify x when the key is loaded. The padding checking is
effectively free and helps make Klima-Rosa type attacks harder */
padPtr = ( const BYTE * ) data + length;
padSize = blockSize - ( length & ( blockSize - 1 ) );
if( padSize < 1 || padSize > CRYPT_MAX_IVSIZE || \
length + padSize > dataLength )
return( CRYPT_ERROR_BADDATA );
for( i = 0; i < padSize; i++ )
{
if( padPtr[ i ] != padSize )
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkPgp2KeyIntegrity( IN_BUFFER( dataLength ) const void *data,
IN_LENGTH_SHORT_MIN( 16 ) const int dataLength,
IN_LENGTH_SHORT_MIN( 16 ) const int keyDataLength,
const BOOLEAN isDlpAlgo )
{
STREAM stream;
const BYTE *keyData = data;
int checksum = 0, storedChecksum, i, status;
assert( isReadPtr( data, dataLength ) );
REQUIRES( dataLength >= 16 && dataLength < MAX_INTLENGTH_SHORT );
REQUIRES( keyDataLength >= 16 && \
keyDataLength + UINT16_SIZE <= dataLength && \
keyDataLength < MAX_INTLENGTH_SHORT );
/* Calculate the checksum for the MPIs. In theory we'd have to process
them all over again but the checksumming procedure is inconsistent
with the encryption in that only the MPI data is encrypted but the
overall length and data are checksummed. Since these data blocks are
stored consecutively in memory we can checksum all MPI data as one
continuous block */
for( i = 0; i < keyDataLength; i++ )
checksum += keyData[ i ];
/* Recover the stored checksum that follows the MPI data and compare it
to the calculated checksum */
sMemConnect( &stream, keyData + keyDataLength, dataLength - keyDataLength );
status = storedChecksum = readUint16( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) || checksum != storedChecksum )
return( CRYPT_ERROR_WRONGKEY );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkOpenPgpKeyIntegrity( IN_BUFFER( dataLength ) const void *data,
IN_LENGTH_SHORT_MIN( 16 ) const int dataLength )
{
HASHFUNCTION_ATOMIC hashFunctionAtomic;
BYTE hashValue[ CRYPT_MAX_HASHSIZE + 8 ];
const BYTE *hashValuePtr;
int hashSize;
assert( isReadPtr( data, dataLength ) );
REQUIRES( dataLength >= 16 && dataLength < MAX_INTLENGTH_SHORT );
/* Get the hash algorithm info and make sure that there's room for
minimal-length data and the checksum */
getHashAtomicParameters( CRYPT_ALGO_SHA1, &hashFunctionAtomic, &hashSize );
if( dataLength < bitsToBytes( 155 ) + hashSize )
return( CRYPT_ERROR_BADDATA );
hashValuePtr = ( const BYTE * ) data + dataLength - hashSize;
/* Hash the data and make sure that it matches the stored MDC */
hashFunctionAtomic( hashValue, CRYPT_MAX_HASHSIZE, data,
dataLength - hashSize );
if( memcmp( hashValue, hashValuePtr, hashSize ) )
return( CRYPT_ERROR_WRONGKEY );
return( CRYPT_OK );
}
#endif /* USE_PGP || USE_PGPKEYS */
/****************************************************************************
* *
* PKCS #15 Private Key Wrap/Unwrap Mechanisms *
* *
****************************************************************************/
/* Perform private key wrapping/unwrapping. There are several variations of
this that are handled through common private key wrap mechanism
functions */
typedef enum { PRIVATEKEY_WRAP_NONE, PRIVATEKEY_WRAP_NORMAL,
PRIVATEKEY_WRAP_OLD, PRIVATEKEY_WRAP_LAST } PRIVATEKEY_WRAP_TYPE;
CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
static int privateKeyWrap( STDC_UNUSED void *dummy,
INOUT MECHANISM_WRAP_INFO *mechanismInfo,
IN_ENUM( PRIVATEKEY_WRAP ) \
const PRIVATEKEY_WRAP_TYPE type )
{
const KEYFORMAT_TYPE formatType = ( type == PRIVATEKEY_WRAP_NORMAL ) ? \
KEYFORMAT_PRIVATE : KEYFORMAT_PRIVATE_OLD;
STREAM stream;
int payloadSize = DUMMY_INIT, blockSize, padSize, status;
UNUSED_ARG( dummy );
assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_WRAP_INFO ) ) );
REQUIRES( type > PRIVATEKEY_WRAP_NONE && type < PRIVATEKEY_WRAP_LAST );
/* Clear return value */
if( mechanismInfo->wrappedData != NULL )
{
memset( mechanismInfo->wrappedData, 0,
mechanismInfo->wrappedDataLength );
}
/* Get the payload details */
sMemNullOpen( &stream );
status = exportPrivateKeyData( &stream, mechanismInfo->keyContext,
formatType, "private_key", 11 );
if( cryptStatusOK( status ) )
payloadSize = stell( &stream );
sMemClose( &stream );
if( cryptStatusError( status ) )
return( status );
status = krnlSendMessage( mechanismInfo->wrapContext,
IMESSAGE_GETATTRIBUTE, &blockSize,
CRYPT_CTXINFO_IVSIZE );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -