📄 pgp_rw.c
字号:
/****************************************************************************
* *
* Miscellaneous (Non-ASN.1) Read/Write Routines *
* Copyright Peter Gutmann 1992-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "pgp_rw.h"
#else
#include "crypt.h"
#include "misc/pgp_rw.h"
#endif /* Compiler-specific includes */
#ifdef USE_PGP
/****************************************************************************
* *
* PGP Read/Write Routines *
* *
****************************************************************************/
/* Read a length in OpenPGP or PGP2 format */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int readOpenPGPLength( INOUT STREAM *stream,
OUT_LENGTH_Z long *length,
OUT_BOOL BOOLEAN *indefiniteLength,
const BOOLEAN indefOK )
{
long localLength;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( length, sizeof( long ) ) );
assert( isWritePtr( indefiniteLength, sizeof( BOOLEAN ) ) );
/* Clear return values */
*length = 0;
*indefiniteLength = FALSE;
/* Get the initial length byte to allow us to decode what else is there.
The error status is also checked later but we make it explicit here */
localLength = sgetc( stream );
if( cryptStatusError( localLength ) )
return( localLength );
/* 0...191 is a literal value */
if( localLength <= 191 )
{
*length = localLength;
return( CRYPT_OK );
}
/* 192...223 is a 13-bit value with offset 192, giving a length in the
range 192...8383 */
if( localLength <= 223 )
{
const int value = sgetc( stream );
if( cryptStatusError( value ) )
return( value );
localLength = ( ( localLength - 192 ) << 8 ) + value + 192;
if( localLength < 192 || localLength > 8383 )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
*length = localLength;
return( CRYPT_OK );
}
/* 224...254 is PGP's annoying interpretation of indefinite-length
encoding. This is an incredible pain to handle but fortunately
except for McAfee's PGP implementation it doesn't seem to be used by
anything. The only data type that would normally need indefinite
lengths, compressed data, uses the 2.x CTB 0xA3 instead */
if( localLength < 255 )
{
if( !indefOK )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
/* Unlike ASN.1, which encodes an outer indefinite-length marker and
then encodes each sub-segment as a data unit within it, PGP
encodes a partial length as a sequence of power-of-two exponent
values with a standard length encoding for the last sub-segment.
So once we're in indefinite-length mode we have to record the
current *type* of the length (as well as its value) to determine
whether more length packets follow */
*indefiniteLength = TRUE;
localLength = 1 << ( localLength & 0x1F );
if( localLength < 1 || localLength >= MAX_INTLENGTH )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
*length = localLength;
return( CRYPT_OK );
}
ENSURES( localLength == 255 );
/* 255 is a marker that a standard 32-bit length follows */
localLength = readUint32( stream );
if( cryptStatusError( localLength ) )
return( localLength );
*length = localLength;
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readPGP2Length( INOUT STREAM *stream,
OUT_LENGTH_Z long *length,
IN_BYTE const int ctb )
{
long localLength;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( length, sizeof( long ) ) );
/* Clear return value */
*length = 0;
/* It's a PGP 2.x CTB, decode the length as a byte, word, or long */
switch( ctb & 3 )
{
case 0:
localLength = sgetc( stream );
break;
case 1:
localLength = readUint16( stream );
break;
case 2:
localLength = readUint32( stream );
break;
default:
/* A length value of 3 indicates that the data length is
determined externally, this is a deprecated PGP 2.x value
that we don't handle */
localLength = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( localLength ) )
return( localLength );
if( localLength < 0 || localLength >= MAX_INTLENGTH )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
*length = localLength;
return( CRYPT_OK );
}
/* Read PGP variable-length length values and packet headers (CTB + length).
We also have a short-length version which is used to read small packets
such as keyrings and sigs and which ensures that the length is in the
range 1...16K */
CHECK_RETVAL_SPECIAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int pgpReadLength( INOUT STREAM *stream,
OUT_LENGTH long *length,
IN_BYTE const int ctb,
IN_LENGTH_SHORT_Z const int minLength,
IN_LENGTH const int maxLength,
const BOOLEAN indefOK )
{
BOOLEAN indefiniteLength = FALSE;
long localLength;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( length, sizeof( long ) ) );
REQUIRES_S( minLength >= 0 && minLength < MAX_INTLENGTH_SHORT && \
minLength < maxLength && maxLength <= MAX_INTLENGTH );
/* Clear return value */
*length = 0;
/* If it doesn't look like PGP data, don't go any further */
if( !pgpIsCTB( ctb ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
/* If it's an OpenPGP CTB, undo the hand-Huffman-coding */
if( pgpGetPacketVersion( ctb ) == PGP_VERSION_OPENPGP )
{
status = readOpenPGPLength( stream, &localLength,
&indefiniteLength, indefOK );
}
else
status = readPGP2Length( stream, &localLength, ctb );
if( cryptStatusError( status ) )
return( status );
if( localLength < minLength || localLength > maxLength )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
*length = localLength;
return( indefiniteLength ? OK_SPECIAL : CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readPacketHeader( INOUT STREAM *stream,
OUT_OPT_BYTE int *ctb,
OUT_OPT_LENGTH_Z long *length,
IN_LENGTH_SHORT_Z const int minLength,
IN_LENGTH const int maxLength,
const BOOLEAN indefOK )
{
long localLength;
int localCTB, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( ctb == NULL || isWritePtr( ctb, sizeof( int ) ) );
assert( length == NULL || isWritePtr( length, sizeof( long ) ) );
REQUIRES_S( minLength >= 0 && minLength < MAX_INTLENGTH_SHORT && \
minLength < maxLength && maxLength <= MAX_INTLENGTH );
/* Clear return values */
if( ctb != NULL )
*ctb = 0;
if( length != NULL )
*length = 0;
/* Examine the CTB and figure out whether we need to perform any
special-case handling */
localCTB = sgetc( stream );
if( cryptStatusError( localCTB ) )
return( localCTB );
if( !pgpIsCTB( localCTB ) )
{
/* If it doesn't look like PGP data, don't go any further */
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
}
if( localCTB == PGP_CTB_COMPRESSED )
{
/* If it's a compressed data packet, there's no length present.
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 but can still be processed because the user has to
explicitly flush the data at some point and we assume that this
is EOF. For this reason we don't return OK_SPECIAL to indicate
an indefinite-length encoding because this isn't a standard
segmented encoding but a virtual definite-length that ends when
the user says it ends. This is far uglier than the PKCS #7/CMS/
SMIME equivalent where we've got an explicit end-of-data
indication, but it's the best that we can do */
if( ctb != NULL )
*ctb = localCTB;
if( length != NULL )
*length = CRYPT_UNUSED;
return( CRYPT_OK ); /* Not-really-indef. return status */
}
/* Now that we know the format, get the length information */
status = pgpReadLength( stream, &localLength, localCTB,
minLength, maxLength, indefOK );
if( cryptStatusError( status ) )
{
int type;
if( status != OK_SPECIAL )
return( status );
/* It's an indefinite-length encoding, this is only valid for
payload data packets so we make sure that we've got one of these
packet types present */
ENSURES( indefOK );
type = pgpGetPacketType( localCTB );
if( type != PGP_PACKET_DATA && type != PGP_PACKET_COPR && \
type != PGP_PACKET_ENCR && type != PGP_PACKET_ENCR_MDC )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
}
if( ctb != NULL )
*ctb = localCTB;
if( length != NULL )
*length = localLength;
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int pgpReadShortLength( INOUT STREAM *stream,
OUT_LENGTH int *length,
IN_BYTE const int ctb )
{
long localLength;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
status = pgpReadLength( stream, &localLength, ctb, 0,
MAX_INTLENGTH_SHORT, FALSE );
if( cryptStatusError( status ) )
return( status );
*length = ( int ) localLength;
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int pgpReadPacketHeader( INOUT STREAM *stream, OUT_OPT_BYTE int *ctb,
OUT_OPT_LENGTH_Z long *length,
IN_LENGTH_SHORT const int minLength )
{
return( readPacketHeader( stream, ctb, length, minLength, MAX_INTLENGTH,
FALSE ) );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int pgpReadPacketHeaderI( INOUT STREAM *stream, OUT_OPT_BYTE int *ctb,
OUT_OPT_LENGTH_Z long *length,
IN_LENGTH_SHORT const int minLength )
{
return( readPacketHeader( stream, ctb, length, minLength, MAX_INTLENGTH,
TRUE ) );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int pgpReadPartialLength( INOUT STREAM *stream,
OUT_OPT_LENGTH_Z long *length )
{
/* This is a raw length value so we have to feed in a pseudo-CTB */
return( pgpReadLength( stream, length, PGP_CTB_OPENPGP,
0, MAX_INTLENGTH, TRUE ) );
}
/* Write PGP variable-length length values and packet headers (CTB +
length) */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int pgpWriteLength( INOUT STREAM *stream, IN_LENGTH const long length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
if( length <= 191 )
return( sputc( stream, length ) );
if( length <= 8383 )
{
const long adjustedLength = length - 192;
sputc( stream, ( ( adjustedLength >> 8 ) & 0xFF ) + 192 );
return( sputc( stream, ( adjustedLength & 0xFF ) ) );
}
sputc( stream, 0xFF );
sputc( stream, ( length >> 24 ) & 0xFF );
sputc( stream, ( length >> 16 ) & 0xFF );
sputc( stream, ( length >> 8 ) & 0xFF );
return( sputc( stream, ( length & 0xFF ) ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int pgpWritePacketHeader( INOUT STREAM *stream,
IN_ENUM( PGP_PACKET ) const PGP_PACKET_TYPE packetType,
IN_LENGTH const long length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES_S( packetType > PGP_PACKET_NONE && \
packetType < PGP_PACKET_LAST );
REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
sputc( stream, PGP_CTB_OPENPGP | packetType );
return( pgpWriteLength( stream, length ) );
}
#endif /* USE_PGP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -