📄 keyex_rw.c
字号:
/****************************************************************************
* *
* Key Exchange Read/Write Routines *
* Copyright Peter Gutmann 1992-2003 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "pgp.h"
#include "asn1_rw.h"
#include "asn1_rw.h"
#include "misc_rw.h"
#include "objinfo.h"
#elif defined( INC_CHILD )
#include "../envelope/pgp.h"
#include "asn1_rw.h"
#include "asn1s_rw.h"
#include "misc_rw.h"
#include "objinfo.h"
#else
#include "envelope/pgp.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_rw.h"
#include "misc/misc_rw.h"
#include "misc/objinfo.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 };
/* Context-specific tags for the RecipientInfo record. KeyTrans has no tag
(actually it has an implied 0 tag because of CMS misdesign, so the other
tags start at 1). To allow for addition of new RI types we permit (but
ignore) objects tagged up to CTAG_RI_MAX */
enum { CTAG_RI_KEYAGREE = 1, CTAG_RI_KEKRI, CTAG_RI_PWRI, CTAG_RI_MAX = 9 };
/* CMS version numbers for various objects. They're monotonically increasing
because it was thought that this was enough to distinguish the record
types (see the note about CMS misdesign above). This was eventually fixed
but the odd version numbers remain, except for PWRI which was done right */
enum { KEYTRANS_VERSION, SIGNATURE_VERSION, KEYTRANS_EX_VERSION,
SIGNATURE_EX_VERSION, KEK_VERSION, PWRI_VERSION = 0 };
/* Prototypes for functions in sign_rw.c */
int readOnepassSigPacket( STREAM *stream, QUERY_INFO *queryInfo );
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Get information on an ASN.1 object */
static int getObjectInfo( STREAM *stream, QUERY_INFO *queryInfo )
{
const long startPos = stell( stream );
long value;
int tag, status;
/* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do
anything */
if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE )
return( CRYPT_ERROR_UNDERFLOW );
/* Get the type, length, and version information */
value = getStreamObjectLength( stream );
if( cryptStatusError( value ) )
return( value );
queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB;
queryInfo->size = value;
tag = peekTag( stream );
readGenericHole( stream, NULL, tag );
status = readShortInteger( stream, &value );
if( cryptStatusError( status ) )
return( status );
queryInfo->version = value;
switch( tag )
{
case BER_SEQUENCE:
/* This could be a signature or a PKC-encrypted key, see what
follows */
switch( value )
{
case KEYTRANS_VERSION:
case KEYTRANS_EX_VERSION:
queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
break;
case SIGNATURE_VERSION:
case SIGNATURE_EX_VERSION:
queryInfo->type = CRYPT_OBJECT_SIGNATURE;
break;
default:
return( CRYPT_ERROR_BADDATA );
}
if( value == KEYTRANS_VERSION || value == SIGNATURE_VERSION )
queryInfo->formatType = CRYPT_FORMAT_CMS;
break;
case MAKE_CTAG( CTAG_RI_KEYAGREE ):
queryInfo->type = CRYPT_OBJECT_KEYAGREEMENT;
break;
case MAKE_CTAG( CTAG_RI_PWRI ):
queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
break;
default:
queryInfo->type = CRYPT_OBJECT_NONE;
if( tag > MAKE_CTAG( CTAG_RI_PWRI ) && \
tag <= MAKE_CTAG( CTAG_RI_MAX ) )
/* This is probably a new RecipientInfo type, skip it */
break;
return( CRYPT_ERROR_BADDATA );
}
/* Reset the stream and make sure that all the data is present */
sseek( stream, startPos );
return( sMemDataLeft( stream ) < queryInfo->size ? \
CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
}
#ifdef USE_PGP
/* Get information on a PGP data object. This doesn't reset the stream like
the ASN.1 equivalent because the PGP header is complex enough that it
can't be read inline like the ASN.1 header */
int getPacketInfo( STREAM *stream, QUERY_INFO *queryInfo )
{
const long startPos = stell( stream );
long length;
int ctb, packetType, status;
/* Read the packet header and extract information from the CTB. Note
that the assignment of version numbers is speculative only, since
it's possible to use PGP 2.x packet headers to wrap up OpenPGP
packets */
status = pgpReadPacketHeader( stream, &ctb, &length );
if( cryptStatusError( status ) )
return( status );
queryInfo->formatType = CRYPT_FORMAT_PGP;
if( ( ctb & PGP_CTB_OPENPGP ) == PGP_CTB_OPENPGP )
{
queryInfo->version = PGP_VERSION_OPENPGP;
packetType = ctb & 0x3F;
}
else
{
queryInfo->version = PGP_VERSION_2;
packetType = ( ctb >> 2 ) & 0x0F;
}
queryInfo->size = length + ( stell( stream ) - startPos );
switch( packetType )
{
case PGP_PACKET_SKE:
queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY;
break;
case PGP_PACKET_PKE:
queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY;
break;
case PGP_PACKET_SIGNATURE:
queryInfo->type = CRYPT_OBJECT_SIGNATURE;
break;
case PGP_PACKET_SIGNATURE_ONEPASS:
/* First half of a one-pass signature, this is given a special
type of 'none' since it's not a normal packet */
queryInfo->type = CRYPT_OBJECT_NONE;
break;
default:
assert( NOTREACHED );
return( CRYPT_ERROR_BADDATA );
}
/* Make sure that all the data is present without resetting the stream */
return( sMemDataLeft( stream ) < ( queryInfo->size - stell( stream ) ) ? \
CRYPT_ERROR_UNDERFLOW : CRYPT_OK );
}
#endif /* USE_PGP */
/****************************************************************************
* *
* 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 = readOID( 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 ) )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -