📄 keyex_rw.c
字号:
/****************************************************************************
* *
* Key Exchange Read/Write Routines *
* Copyright Peter Gutmann 1992-2004 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "pgp.h"
#include "mechanism.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "misc_rw.h"
#elif defined( INC_CHILD )
#include "../envelope/pgp.h"
#include "mechanism.h"
#include "../misc/asn1.h"
#include "../misc/asn1_ext.h"
#include "../misc/misc_rw.h"
#else
#include "envelope/pgp.h"
#include "mechs/mechanism.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#include "misc/misc_rw.h"
#endif /* Compiler-specific includes */
/* Context-specific tags for the KEK record */
enum { CTAG_KK_DA };
/* Context-specific tags for the KeyTrans record */
enum { CTAG_KT_SKI };
/* Context-specific tags for the KeyAgree/Fortezza record */
enum { CTAG_KA_ORIG, CTAG_KA_UKM };
/****************************************************************************
* *
* Conventionally-Encrypted Key Routines *
* *
****************************************************************************/
/* The OID for the PKCS #5 v2.0 key derivation function and the parameterised
PWRI key wrap algorithm */
#define OID_PBKDF2 MKOID( "\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x05\x0C" )
#define OID_PWRIKEK MKOID( "\x06\x0B\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x09" )
/* Write a PBKDF2 key derivation record */
static int writeKeyDerivationInfo( STREAM *stream,
const CRYPT_CONTEXT iCryptContext )
{
RESOURCE_DATA msgData;
BYTE salt[ CRYPT_MAX_HASHSIZE ];
int keySetupIterations, derivationInfoSize, status;
/* Get the key derivation information */
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&keySetupIterations,
CRYPT_CTXINFO_KEYING_ITERATIONS );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, salt, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_KEYING_SALT );
}
if( cryptStatusError( status ) )
return( status );
derivationInfoSize = ( int ) sizeofObject( msgData.length ) + \
sizeofShortInteger( ( long ) keySetupIterations );
/* Write the PBKDF2 information */
writeConstructed( stream, sizeofOID( OID_PBKDF2 ) +
( int ) sizeofObject( derivationInfoSize ), CTAG_KK_DA );
writeOID( stream, OID_PBKDF2 );
writeSequence( stream, derivationInfoSize );
writeOctetString( stream, msgData.data, msgData.length, DEFAULT_TAG );
status = writeShortInteger( stream, keySetupIterations, DEFAULT_TAG );
zeroise( salt, CRYPT_MAX_HASHSIZE );
return( status );
}
/* Read a PBKDF2 key derivation record */
static int readKeyDerivationInfo( STREAM *stream, QUERY_INFO *queryInfo )
{
long endPos, value;
int length, status;
/* Read the outer wrapper and key derivation algorithm OID */
readConstructed( stream, NULL, CTAG_KK_DA );
status = readFixedOID( stream, OID_PBKDF2 );
if( cryptStatusError( status ) )
return( status );
/* Read the PBKDF2 parameters, limiting the salt and iteration count to
sane values */
readSequence( stream, &length );
endPos = stell( stream ) + length;
readOctetString( stream, queryInfo->salt, &queryInfo->saltLength,
CRYPT_MAX_HASHSIZE );
status = readShortInteger( stream, &value );
if( cryptStatusError( status ) )
return( status );
if( value > MAX_KEYSETUP_ITERATIONS )
return( CRYPT_ERROR_BADDATA );
queryInfo->keySetupIterations = ( int ) value;
queryInfo->keySetupAlgo = CRYPT_ALGO_HMAC_SHA;
if( stell( stream ) < endPos )
return( sseek( stream, endPos ) );
return( CRYPT_OK );
}
/* Read/write CMS KEK data */
static int readCmsKek( STREAM *stream, QUERY_INFO *queryInfo )
{
long value;
int status;
/* Read the header */
readConstructed( stream, NULL, CTAG_RI_KEKRI );
status = readShortInteger( stream, &value );
if( cryptStatusError( status ) )
return( status );
if( value != KEK_VERSION )
return( CRYPT_ERROR_BADDATA );
return( CRYPT_ERROR_NOTAVAIL );
}
static int writeCmsKek( STREAM *stream, const CRYPT_CONTEXT iCryptContext,
const BYTE *encryptedKey, const int encryptedKeyLength )
{
STREAM localStream;
RESOURCE_DATA msgData;
BYTE kekInfo[ 128 ], label[ CRYPT_MAX_TEXTSIZE ];
int kekInfoSize, labelSize, status;
/* Get the label */
setMessageData( &msgData, label, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_LABEL );
if( cryptStatusError( status ) )
return( status );
labelSize = msgData.length;
/* Determine the size of the KEK info. To save evaluating it twice in a
row and because it's short, we just write it to local buffers */
sMemOpen( &localStream, kekInfo, 128 );
writeSequence( &localStream,
sizeofOID( OID_PWRIKEK ) + \
sizeofContextAlgoID( iCryptContext, CRYPT_ALGO_NONE,
ALGOID_FLAG_NONE ) );
writeOID( &localStream, OID_PWRIKEK );
status = writeContextAlgoID( &localStream, iCryptContext, CRYPT_ALGO_NONE,
ALGOID_FLAG_NONE );
kekInfoSize = stell( &localStream );
sMemDisconnect( &localStream );
if( cryptStatusError( status ) )
return( status );
/* Write the algorithm identifiers and encrypted key */
writeConstructed( stream, ( int ) sizeofShortInteger( KEK_VERSION ) + \
sizeofObject( sizeofObject( labelSize ) ) + \
kekInfoSize + sizeofObject( encryptedKeyLength ),
CTAG_RI_KEKRI );
writeShortInteger( stream, KEK_VERSION, DEFAULT_TAG );
writeSequence( stream, sizeofObject( labelSize ) );
writeOctetString( stream, label, labelSize, DEFAULT_TAG );
swrite( stream, kekInfo, kekInfoSize );
return( writeOctetString( stream, encryptedKey, encryptedKeyLength,
DEFAULT_TAG ) );
}
/* Read/write cryptlib KEK data */
static int readCryptlibKek( STREAM *stream, QUERY_INFO *queryInfo )
{
long value;
int status;
/* If it's CMS KEK, read it as such */
if( peekTag( stream ) == CTAG_RI_KEKRI )
return( readCmsKek( stream, queryInfo ) );
/* Read the header */
readConstructed( stream, NULL, CTAG_RI_PWRI );
status = readShortInteger( stream, &value );
if( cryptStatusError( status ) )
return( status );
if( value != PWRI_VERSION )
return( CRYPT_ERROR_BADDATA );
/* Read the optional KEK derivation info and KEK algorithm info */
if( peekTag( stream ) == MAKE_CTAG( CTAG_KK_DA ) )
status = readKeyDerivationInfo( stream, queryInfo );
if( cryptStatusOK( status ) )
{
const long position = stell( stream );
/* Because of the last-minute change in the PWRI format before the
RFC was published, older versions of cryptlib generate a slightly
different KEK algorithm info format. To handle this, we read
part of the AlgorithmIdentifier and, if it's the newer format,
skip the extra level of wrapping */
readSequence( stream, NULL );
status = readFixedOID( stream, OID_PWRIKEK );
if( cryptStatusError( status ) )
{
/* It's the original format, clear the stream error state caused
by the failed PWRI KEK OID read and try again */
sClearError( stream );
sseek( stream, position );
}
status = readContextAlgoID( stream, NULL, queryInfo, DEFAULT_TAG );
}
if( cryptStatusError( status ) )
return( status );
/* Finally, read the start of the encrypted key. We never read the data
itself since it's passed directly to the decrypt function */
status = readOctetStringHole( stream, &queryInfo->dataLength,
DEFAULT_TAG );
if( cryptStatusOK( status ) )
{
queryInfo->dataStart = sMemBufPtr( stream );
if( queryInfo->dataLength < bitsToBytes( MIN_KEYSIZE_BITS ) )
/* We shouldn't be using a key this short, we can't actually
load it anyway but a CRYPT_ERROR_BADDATA at this point
provides more meaningful information to the caller */
status = CRYPT_ERROR_BADDATA;
}
return( status );
}
static int writeCryptlibKek( STREAM *stream, const CRYPT_CONTEXT iCryptContext,
const BYTE *encryptedKey, const int encryptedKeyLength )
{
STREAM localStream;
BYTE derivationInfo[ CRYPT_MAX_HASHSIZE + 32 ], kekInfo[ 128 ];
BOOLEAN hasKeyDerivationInfo = TRUE;
int derivationInfoSize = 0, kekInfoSize, value, status;
/* If it's a non-password-derived key and there's a label attached,
write it as a KEKRI with a PWRI algorithm identifier as the key
encryption algorithm */
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&value, CRYPT_CTXINFO_KEYING_ITERATIONS );
if( status == CRYPT_ERROR_NOTINITED )
{
RESOURCE_DATA msgData;
/* There's no password-derivation information present, see if there's
a label present */
hasKeyDerivationInfo = FALSE;
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_LABEL );
if( cryptStatusOK( status ) )
/* There's a label present, write it as a PWRI within a KEKRI */
return( writeCmsKek( stream, iCryptContext, encryptedKey,
encryptedKeyLength ) );
}
/* Determine the size of the derivation info and KEK info. To save
evaluating it twice in a row and because it's short, we just write
it to local buffers */
if( hasKeyDerivationInfo )
{
sMemOpen( &localStream, derivationInfo, CRYPT_MAX_HASHSIZE + 32 );
status = writeKeyDerivationInfo( &localStream, iCryptContext );
derivationInfoSize = stell( &localStream );
sMemDisconnect( &localStream );
if( cryptStatusError( status ) )
return( status );
}
sMemOpen( &localStream, kekInfo, 128 );
writeSequence( &localStream,
sizeofOID( OID_PWRIKEK ) + \
sizeofContextAlgoID( iCryptContext, CRYPT_ALGO_NONE,
ALGOID_FLAG_NONE ) );
writeOID( &localStream, OID_PWRIKEK );
status = writeContextAlgoID( &localStream, iCryptContext, CRYPT_ALGO_NONE,
ALGOID_FLAG_NONE );
kekInfoSize = stell( &localStream );
sMemDisconnect( &localStream );
if( cryptStatusError( status ) )
return( status );
/* Write the algorithm identifiers and encrypted key */
writeConstructed( stream, sizeofShortInteger( PWRI_VERSION ) +
derivationInfoSize + kekInfoSize +
( int ) sizeofObject( encryptedKeyLength ),
CTAG_RI_PWRI );
writeShortInteger( stream, PWRI_VERSION, DEFAULT_TAG );
if( derivationInfoSize )
swrite( stream, derivationInfo, derivationInfoSize );
swrite( stream, kekInfo, kekInfoSize );
return( writeOctetString( stream, encryptedKey, encryptedKeyLength,
DEFAULT_TAG ) );
}
#ifdef USE_PGP
/* Read/write PGP KEK data.
SKE:
byte ctb = PGP_PACKET_SKE
byte[] length
byte version = 4
byte cryptAlgo
byte stringToKey specifier:
byte[] stringToKey data
0x00: byte hashAlgo
0x01: byte[8] salt
0x02: byte iterations */
static int readPgpKek( STREAM *stream, QUERY_INFO *queryInfo )
{
int value, status;
/* Make sure that the packet header is in order and check the packet
version. This is an OpenPGP-only packet */
status = getPacketInfo( stream, queryInfo );
if( cryptStatusError( status ) )
return( status );
if( sgetc( stream ) != PGP_VERSION_OPENPGP )
return( CRYPT_ERROR_BADDATA );
queryInfo->version = PGP_VERSION_OPENPGP;
/* Get the password hash algorithm */
if( ( queryInfo->cryptAlgo = \
pgpToCryptlibAlgo( sgetc( stream ),
PGP_ALGOCLASS_PWCRYPT ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
/* Read the S2K specifier */
value = sgetc( stream );
if( value != 0 && value != 1 && value != 3 )
return( cryptStatusError( value ) ? value : CRYPT_ERROR_BADDATA );
if( ( queryInfo->keySetupAlgo = \
pgpToCryptlibAlgo( sgetc( stream ),
PGP_ALGOCLASS_HASH ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
if( value == 0 )
/* It's a straight hash, we're done */
return( CRYPT_OK );
status = sread( stream, queryInfo->salt, PGP_SALTSIZE );
if( cryptStatusError( status ) )
return( status );
queryInfo->saltLength = PGP_SALTSIZE;
if( value == 3 )
{
/* Salted iterated hash, get the iteration count, limited to a sane
value. The "iteration count" is actually a count of how many
bytes are hashed, this is because the "iterated hashing" treats
the salt + password as an infinitely-repeated sequence of values
and hashes the resulting string for PGP-iteration-count bytes
worth. The value we calculate here (to prevent overflow on 16-bit
machines) is the count without the base * 64 scaling, this also
puts the range within the value of the standard sanity check */
value = sgetc( stream );
if( cryptStatusError( value ) )
return( value );
queryInfo->keySetupIterations = \
( 16 + ( ( long ) value & 0x0F ) ) << ( value >> 4 );
if( queryInfo->keySetupIterations <= 0 || \
queryInfo->keySetupIterations > MAX_KEYSETUP_ITERATIONS )
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
static int writePgpKek( STREAM *stream, const CRYPT_CONTEXT iCryptContext,
const BYTE *encryptedKey, const int encryptedKeyLength )
{
CRYPT_ALGO_TYPE hashAlgo, cryptAlgo;
BYTE salt[ CRYPT_MAX_HASHSIZE ];
int keySetupIterations, count = 0, status;
assert( encryptedKey == NULL );
assert( encryptedKeyLength == 0 );
/* Get the key derivation information */
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&keySetupIterations, CRYPT_CTXINFO_KEYING_ITERATIONS );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&hashAlgo, CRYPT_CTXINFO_KEYING_ALGO );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&cryptAlgo, CRYPT_CTXINFO_ALGO );
if( cryptStatusOK( status ) )
{
RESOURCE_DATA msgData;
setMessageData( &msgData, salt, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_KEYING_SALT );
}
if( cryptStatusError( status ) )
return( status );
/* Calculate the PGP "iteration count" from the value used to derive
the key. The "iteration count" is actually a count of how many bytes
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -