📄 pgp_deen.c
字号:
/****************************************************************************
* *
* cryptlib PGP De-enveloping Routines *
* Copyright Peter Gutmann 1996-2002 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "envelope.h"
#include "pgp.h"
#elif defined( INC_CHILD )
#include "../envelope/envelope.h"
#include "../envelope/pgp.h"
#else
#include "envelope/envelope.h"
#include "envelope/pgp.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Get information on a PGP data packet, checking that there's enough
data left to get the packet information */
static int getPacketInfo( STREAM *stream, ENVELOPE_INFO *envelopeInfoPtr,
long *length )
{
long localLength;
BYTE ctb;
/* We always need at least two more bytes to do anything */
if( sMemDataLeft( stream ) < 2 )
return( CRYPT_ERROR_UNDERFLOW );
/* Peek at the CTB and figure out whether we've got enough data left to
read the header */
ctb = sgetc( stream );
if( ( ctb & PGP_CTB_OPENPGP ) == PGP_CTB_OPENPGP )
{
/* OpenPGP has an awkward variable-length encoding which requires
that we burrow further down into the data to get the actual
length, to avoid problems with having to undo this we assume a
worst-case length of 5 bytes. This is safe because the shortest
possible packet type, a conventionally-encrypted data packet with
a 1-byte payload, contains a minimum of 11 bytes of data (8-byte
IV, 2 bytes of repeated IV data, and 1 byte of payload) */
if( sMemDataLeft( stream ) < 5 )
{
sungetc( stream );
return( CRYPT_ERROR_UNDERFLOW );
}
envelopeInfoPtr->version = PGP_VERSION_OPENPGP;
}
else
if( ctb == PGP_CTB_COMPRESSED )
{
/* Normally we reject any indefinite-length packets since these
can't be processed sensibly (PGP 2.x, which used intermediate
files for everything, just read to EOF, OpenPGP deprecates
them because this doesn't exactly lead to portable
implementations). However, compressed-data packets can only
be stored in this manner and can be processed because the user
has to explicitly flush the data at some point and we assume
that this is EOF. This isn't anywhere near as clean as the
PKCS #7/CMS/SMIME equivalent where we've got an explicit end-
of-data indication, but it does the trick */
if( length != NULL )
*length = CRYPT_UNUSED;
return( PGP_PACKET_COPR );
}
else
{
static const int lengthOfLength[ 4 ] = { 1, 2, 4, 0 };
if( sMemDataLeft( stream ) < lengthOfLength[ ctb & 3 ] )
{
sungetc( stream );
return( CRYPT_ERROR_UNDERFLOW );
}
envelopeInfoPtr->version = PGP_VERSION_2;
}
/* Now that the header is present as plaintext, parse it */
localLength = pgpGetLength( stream, ctb );
if( cryptStatusError( localLength ) )
return( ( int ) localLength );
if( length != NULL )
*length = localLength;
/* Extract and return the packet type */
return( ( ( ctb & PGP_CTB_OPENPGP ) == PGP_CTB_OPENPGP ) ? \
( ctb & 0x3F ) : ( ( ctb >> 2 ) & 0x0F ) );
}
/* Read various PGP algorithm IDs and convert them to a cryptlib algorithm
type */
static int readHashAlgorithmID( STREAM *stream )
{
switch( sgetc( stream ) )
{
case PGP_ALGO_MD2:
return( CRYPT_ALGO_MD2 );
case PGP_ALGO_MD5:
return( CRYPT_ALGO_MD5 );
case PGP_ALGO_SHA:
return( CRYPT_ALGO_SHA );
case PGP_ALGO_RIPEMD160:
return( CRYPT_ALGO_RIPEMD160 );
}
return( CRYPT_ALGO_NONE );
}
static int readSignAlgorithmID( STREAM *stream )
{
switch( sgetc( stream ) )
{
case PGP_ALGO_RSA:
case PGP_ALGO_RSA_SIGN:
return( CRYPT_ALGO_RSA );
case PGP_ALGO_DSA:
return( CRYPT_ALGO_DSA );
}
return( CRYPT_ERROR_NOTAVAIL );
}
/****************************************************************************
* *
* Read Key Exchange/Signature Packets *
* *
****************************************************************************/
/* Process signature subpackets */
static int processSignatureSubpackets( STREAM *stream,
CONTENT_LIST *contentListItem,
const int length,
const int startOffset,
const BOOLEAN isAuthenticated )
{
const int endPos = ( int ) stell( stream ) + length;
while( stell( stream ) < endPos )
{
const int subpacketLength = ( int ) pgpGetLength( stream,
PGP_CTB_OPENPGP );
const int type = sgetc( stream );
if( cryptStatusError( subpacketLength ) )
return( subpacketLength );
/* If it's an unrecognised subpacket with the critical flag set,
reject the signature. The range check isn't complete since there
are a few holes in the range, but since the holes presumably exist
because of deprecated subpacket types, any new packets will be
added at the end so it's safe to use */
if( ( type & 0x80 ) && ( ( type & 0x7F ) > PGP_SUBPACKET_LAST ) )
return( CRYPT_ERROR_NOTAVAIL );
/* If it's a key ID and we haven't already set this from a preceding
one-pass signature packet (which can happen with detached sigs),
set it now */
if( type == PGP_SUBPACKET_KEYID && !contentListItem->keyIDsize )
{
sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
contentListItem->keyIDsize = PGP_KEYID_SIZE;
continue;
}
/* If it's a type-and-value packet, see whether it's one of
ours */
if( type == PGP_SUBPACKET_TYPEANDVALUE )
{
BYTE nameBuffer[ 32 ];
static const char *nameString = "issuerAndSerialNumber";
int nameLength, valueLength;
sSkip( stream, 4 ); /* Flags */
nameLength = ( sgetc( stream ) << 8 ) | sgetc( stream );
valueLength = ( sgetc( stream ) << 8 ) | sgetc( stream );
if( nameLength != strlen( nameString ) || \
valueLength < 16 || valueLength > 2048 )
{
sSkip( stream, nameLength + valueLength );
continue;
}
sread( stream, nameBuffer, nameLength );
if( memcmp( nameBuffer, nameString, nameLength ) )
{
sSkip( stream, valueLength );
continue;
}
/* It's an issuerAndSerialNumber, remember it for later */
contentListItem->issuerAndSerialNumber = \
( BYTE * ) contentListItem->object + \
( ( int ) stell( stream ) - startOffset );
contentListItem->issuerAndSerialNumberSize = valueLength;
sSkip( stream, valueLength );
continue;
}
/* It's something else, skip it and continue */
sSkip( stream, subpacketLength - 1 );
}
return( sGetStatus( stream ) );
}
/* Read various PGP packets:
SKE: byte version = 4
byte cryptAlgo
byte stringToKey specifier
byte[] stringToKey data */
static int readSKEPacket( STREAM *stream, CONTENT_LIST *contentListItem )
{
int value, status;
/* Check the packet version and get the password hash algorithm */
if( sgetc( stream ) != 4 )
return( CRYPT_ERROR_BADDATA );
switch( sgetc( stream ) )
{
case PGP_ALGO_IDEA:
contentListItem->cryptAlgo = CRYPT_ALGO_IDEA;
break;
case PGP_ALGO_3DES:
contentListItem->cryptAlgo = CRYPT_ALGO_3DES;
break;
case PGP_ALGO_CAST5:
contentListItem->cryptAlgo = CRYPT_ALGO_CAST;
break;
case PGP_ALGO_BLOWFISH:
contentListItem->cryptAlgo = CRYPT_ALGO_BLOWFISH;
break;
case PGP_ALGO_AES_128:
contentListItem->cryptAlgo = CRYPT_ALGO_AES;
break;
default:
return( CRYPT_ERROR_NOTAVAIL );
}
contentListItem->cryptMode = CRYPT_MODE_CFB;
contentListItem->envInfo = CRYPT_ENVINFO_PASSWORD;
/* Read the S2K specifier */
value = sgetc( stream );
if( value != 0 && value != 1 && value != 3 )
return( CRYPT_ERROR_BADDATA );
if( ( contentListItem->hashAlgo = \
readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
if( value == 0 )
/* It's a straight hash, we're done */
return( CRYPT_OK );
status = sread( stream, contentListItem->saltOrIV, PGP_SALTSIZE );
if( cryptStatusError( status ) )
return( status );
contentListItem->saltOrIVsize = 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( status ) )
return( status );
contentListItem->keySetupIterations = \
( 16 + ( ( long ) value & 0x0F ) ) << ( value >> 4 );
if( contentListItem->keySetupIterations > MAX_KEYSETUP_ITERATIONS )
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
/* PKE: byte version = 2 or 3
byte[8] keyID
byte PKC algo
mpi(s) encrypted session key */
static int readPKEPacket( STREAM *stream, CONTENT_LIST *contentListItem )
{
int offset = ( int ) stell( stream );
int value, status;
/* Check the packet version and get the PGP key ID and algorithm */
value = sgetc( stream );
if( value != PGP_VERSION_2 && value != PGP_VERSION_3 )
return( CRYPT_ERROR_BADDATA );
status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
if( cryptStatusError( status ) )
return( status );
contentListItem->keyIDsize = PGP_KEYID_SIZE;
switch( sgetc( stream ) )
{
case PGP_ALGO_RSA:
case PGP_ALGO_RSA_ENCRYPT:
contentListItem->cryptAlgo = CRYPT_ALGO_RSA;
break;
case PGP_ALGO_ELGAMAL:
contentListItem->cryptAlgo = CRYPT_ALGO_ELGAMAL;
break;
default:
return( CRYPT_ERROR_NOTAVAIL );
}
contentListItem->envInfo = CRYPT_ENVINFO_PRIVATEKEY;
/* Read the encrypted key */
offset = ( int ) stell( stream ) - offset;
status = pgpReadMPI( stream, NULL );
if( cryptStatusError( status ) )
return( status );
contentListItem->payload = ( BYTE * ) contentListItem->object + offset;
contentListItem->payloadSize = bitsToBytes( status ) + 2;
if( contentListItem->payloadSize < 56 || \
contentListItem->payloadSize > CRYPT_MAX_PKCSIZE )
return( CRYPT_ERROR_BADDATA );
if( contentListItem->cryptAlgo == CRYPT_ALGO_RSA )
return( CRYPT_OK );
status = pgpReadMPI( stream, NULL );
if( cryptStatusError( status ) )
return( status );
contentListItem->payloadSize += bitsToBytes( status ) + 2;
if( contentListItem->payloadSize < ( 56 * 2 ) || \
contentListItem->payloadSize > ( CRYPT_MAX_PKCSIZE * 2 ) )
return( CRYPT_ERROR_BADDATA );
return( CRYPT_OK );
}
/* Signature info:
byte version = 3
byte sigType
byte hashAlgo
byte sigAlgo
byte[8] keyID
byte 1 */
static int readOnepassSigPacket( STREAM *stream, CONTENT_LIST *contentListItem )
{
int status;
/* Check the packet version, skip the sig.type, get the hash algorithm
and check the signature algorithm */
if( sgetc( stream ) != PGP_VERSION_3 )
return( CRYPT_ERROR_BADDATA );
sgetc( stream );
if( ( contentListItem->hashAlgo = \
readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
if( ( contentListItem->cryptAlgo = \
readSignAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;
/* Get the PGP key ID and make sure this isn't a nested signature */
status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
if( cryptStatusError( status ) )
return( status );
contentListItem->keyIDsize = PGP_KEYID_SIZE;
return( ( sgetc( stream ) != 1 ) ? CRYPT_ERROR_BADDATA : CRYPT_OK );
}
/* Signature:
v3: byte version = 3 v4: byte version = 4
byte infoLen = 5 byte sigType
byte sigType byte sigAlgo
byte[4] sig.time byte hashAlgo
byte[8] keyID byte[2] length of auth.attributes
byte sigAlgo byte[] authenticated attributes
byte hashAlgo byte[2] length of unauth.attributes
byte[2] hash check byte[] unauthenticated attributes
mpi(s) signature byte[2] hash check
mpi(s) signature */
static int readSigPacket( STREAM *stream, CONTENT_LIST *contentListItem )
{
int offset = ( int ) stell( stream );
int value, status;
/* Check the packet version */
value = sgetc( stream );
if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
value != PGP_VERSION_OPENPGP )
return( CRYPT_ERROR_BADDATA );
contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;
/* If it's not an OpenPGP packet, read it as a PGP 2.x-format
signature */
if( value != PGP_VERSION_OPENPGP )
{
/* Read the additional signature information */
if( sgetc( stream ) != 5 )
return( CRYPT_ERROR_BADDATA );
contentListItem->extraData = ( BYTE * ) contentListItem->object + \
( ( int ) stell( stream ) - offset );
contentListItem->extraDataLength = 5;
sSkip( stream, 5 );
/* Read the signer keyID, signature and hash algorithm */
status = sread( stream, contentListItem->keyID, PGP_KEYID_SIZE );
if( cryptStatusError( status ) )
return( status );
contentListItem->keyIDsize = PGP_KEYID_SIZE;
if( ( contentListItem->cryptAlgo = \
readSignAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
if( ( contentListItem->hashAlgo = \
readHashAlgorithmID( stream ) ) == CRYPT_ALGO_NONE )
return( CRYPT_ERROR_NOTAVAIL );
/* Skip the hash check and read the signature, recording the start
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -