📄 base64.c
字号:
ENSURES( srcIndex > 0 && srcIndex < srcLen );
*eolSize = srcIndex;
return( CRYPT_OK );
}
/* Check whether a data item has a header that identifies it as some form of
encoded object and return the start position of the encoded data. For
S/MIME certificate data this can in theory get quite complex because
there are many possible variations in the headers. Some early S/MIME
agents used a content type of "application/x-pkcs7-mime",
"application/x-pkcs7-signature", and "application/x-pkcs10", while newer
ones use the same without the "x-" at the start. In addition Netscape
have their own MIME data types for certificates, "application/x-x509-"
"{user-cert|ca-cert|email-cert}, and this tradition is perpetuated by the
mass of further types in the neverending stream of RFCs that PKIX churns
out. There are a whole pile of other possible headers as well, none of
them terribly relevant for our purposes, so all that we check for is the
base64 indicator */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int base64checkHeader( IN_BUFFER( dataLength ) const char *data,
IN_LENGTH const int dataLength,
OUT_ENUM_OPT( CRYPT_CERTFORMAT ) \
CRYPT_CERTFORMAT_TYPE *format,
OUT_LENGTH_Z int *startPos )
{
STREAM stream;
BOOLEAN seenTransferEncoding = FALSE, isBinaryEncoding = FALSE;
BOOLEAN seenDash = FALSE, isBase64;
int position = DUMMY_INIT, lineCount, length, status;
assert( isReadPtr( data, dataLength ) );
assert( isWritePtr( format, sizeof( CRYPT_CERTFORMAT_TYPE ) ) );
assert( isWritePtr( startPos, sizeof( int ) ) );
REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH );
/* Clear return values */
*format = CRYPT_CERTFORMAT_NONE;
*startPos = 0;
/* If the item is too small to contain any useful data we don't even try
and examine it. We don't treat this as a data or underflow error
since it may be a short but valid data object like an empty CRL */
if( dataLength < 64 )
return( CRYPT_OK );
sMemConnect( &stream, data, dataLength );
/* Perform a quick check to weed out unencoded certificate data, which
is usually the case. Certificates and related objects are always an
ASN.1 SEQUENCE so if we find data that begins with this value then we
perform the check for a certificate object. For very large objects
(which can only be CRLs) we can get an overflow error trying to read
a short length so if the length is suspiciously long we allow a long
length. We don't do this unconditionally in order to reduce
potential false positives */
if( sPeek( &stream ) == BER_SEQUENCE )
{
if( dataLength < 32000L )
status = readSequenceI( &stream, NULL );
else
status = readLongSequence( &stream, NULL );
if( cryptStatusOK( status ) )
{
sMemDisconnect( &stream );
return( CRYPT_OK );
}
sClearError( &stream );
sseek( &stream, 0 );
}
/* Sometimes the object can be preceded by a few blank lines, which we
ignore */
for( length = 0, lineCount = 0;
length <= 0 && lineCount < FAILSAFE_ITERATIONS_MED;
lineCount++ )
{
char buffer[ 1024 + 8 ];
position = stell( &stream );
status = readTextLine( ( READCHARFUNCTION ) sgetc, &stream,
buffer, 1024, &length, NULL );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( buffer[ 0 ] == '-' )
seenDash = TRUE;
}
if( lineCount >= FAILSAFE_ITERATIONS_MED )
{
sMemDisconnect( &stream );
return( CRYPT_ERROR_BADDATA );
}
sseek( &stream, position );
/* If the data starts with a dash check for PEM header encapsulation
followed by a base64-encoded body */
if( seenDash )
{
status = checkPEMHeader( &stream, &position );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( !checkBase64( &stream ) )
{
sMemDisconnect( &stream );
return( CRYPT_ERROR_BADDATA );
}
sMemDisconnect( &stream );
*format = CRYPT_CERTFORMAT_TEXT_CERTIFICATE;
*startPos = position;
return( CRYPT_OK );
}
/* Check for non-encapsulated base64 data */
if( checkBase64( &stream ) )
{
sMemDisconnect( &stream );
*format = CRYPT_CERTFORMAT_TEXT_CERTIFICATE;
*startPos = position;
return( CRYPT_OK );
}
sseek( &stream, position );
/* It doesn't look like base64 encoded data, check for an S/MIME header */
for( length = 1, lineCount = 0;
length > 0 && lineCount < FAILSAFE_ITERATIONS_MED;
lineCount++ )
{
char buffer[ 1024 + 8 ];
status = readTextLine( ( READCHARFUNCTION ) sgetc, &stream,
buffer, 1024, &length, NULL );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( !seenTransferEncoding && length >= 33 && \
!strCompare( buffer, "Content-Transfer-Encoding:", 26 ) )
{
int index = strSkipWhitespace( buffer + 26, length - 26 );
/* Check for a valid content encoding type */
if( index < 0 || index > 1024 - 26 )
continue;
index += 26; /* Skip "Content-Transfer-Encoding:" */
if( length - index < 6 )
{
/* It's too short to be a valid encoding type, skip it */
continue;
}
if( !strCompare( buffer + index, "base64", 6 ) )
seenTransferEncoding = TRUE;
else
{
if( !strCompare( buffer + index, "binary", 6 ) )
seenTransferEncoding = isBinaryEncoding = TRUE;
}
}
}
if( lineCount >= FAILSAFE_ITERATIONS_MED || !seenTransferEncoding )
{
sMemDisconnect( &stream );
return( CRYPT_ERROR_BADDATA );
}
position = stell( &stream );
/* Make sure that the content is some form of encoded certificate using
the same check as the one that we used earlier */
if( isBinaryEncoding )
{
if( dataLength < 32000L )
status = readSequenceI( &stream, NULL );
else
status = readLongSequence( &stream, NULL );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_BADDATA );
*startPos = position;
*format = CRYPT_CERTFORMAT_CERTIFICATE;
return( CRYPT_OK );
}
isBase64 = checkBase64( &stream );
sMemDisconnect( &stream );
if( !isBase64 )
return( CRYPT_ERROR_BADDATA );
*startPos = position;
*format = CRYPT_ICERTFORMAT_SMIME_CERTIFICATE;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Base64 Decoding Functions *
* *
****************************************************************************/
/* Decode a chunk of four base64 characters into three binary characters */
CHECK_RETVAL_SPECIAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int decodeBase64chunk( INOUT STREAM *stream,
IN_BUFFER( srcLeft ) const char *src,
IN_LENGTH const int srcLeft,
const BOOLEAN fixedLenData )
{
BYTE c0, c1, c2 = 0, c3 = 0, cx;
int srcIndex = 0, outByteCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( src, srcLeft ) );
REQUIRES( srcLeft > 0 && srcLeft < MAX_INTLENGTH );
/* Make sure that there's sufficient input left to decode. We need at
least two more characters to produce one byte of output */
if( srcLeft < 2 )
return( CRYPT_ERROR_UNDERFLOW );
/* Decode a block of data from the input buffer */
c0 = decode( src[ srcIndex++ ] );
c1 = decode( src[ srcIndex++ ] );
if( srcLeft > 2 )
{
c2 = decode( src[ srcIndex++ ] );
if( srcLeft > 3 )
c3 = decode( src[ srcIndex++ ] );
}
cx = c0 | c1 | c2 | c3;
if( cx == BERR || cx == BEOF )
{
/* If we're decoding fixed-length data and the decoding produces
an invalid character or an EOF, there's a problem with the
input */
if( fixedLenData )
return( CRYPT_ERROR_BADDATA );
/* We're decoding indefinite-length data for which EOFs are valid
characters. We have to be a bit careful with the order of
checking since hitting an EOF at an earlier character may cause
later input data to be decoded as BERR */
if( c0 == BEOF )
{
/* No more input, we're done */
return( 0 );
}
if( c0 == BERR || c1 == BEOF || c1 == BERR )
{
/* We can't produce output with only one character of input
data, there's a problem with the input */
return( CRYPT_ERROR_BADDATA );
}
if( c2 == BEOF )
{
/* Two characters of input, then EOF, resulting in one character
of output */
outByteCount = 1;
}
else
{
if( c2 == BERR || c3 == BERR )
return( CRYPT_ERROR_BADDATA );
ENSURES( c3 == BEOF );
outByteCount = 2;
}
}
else
{
/* All decoded characters are valid */
outByteCount = ( srcLeft > 4 ) ? 3 : srcLeft - 1;
}
ENSURES( outByteCount > 0 && outByteCount < 4 );
/* Write the decoded data to the output buffer */
status = sputc( stream, ( ( c0 << 2 ) | ( c1 >> 4 ) ) & 0xFF );
if( outByteCount > 1 )
{
status = sputc( stream, ( ( c1 << 4 ) | ( c2 >> 2 ) ) & 0xFF );
if( outByteCount > 2 )
status = sputc( stream, ( ( c2 << 6 ) | c3 ) & 0xFF );
}
if( cryptStatusError( status ) )
return( status );
/* If we've reached the end of the input, let the caller know */
return( ( outByteCount < 3 ) ? OK_SPECIAL : CRYPT_OK );
}
/* Decode a block of binary data from the base64 format, returning the total
number of decoded bytes */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int fixedBase64decode( STREAM *stream,
IN_BUFFER( srcLen ) const char *src,
IN_LENGTH_MIN( 10 ) const int srcLen )
{
int srcIndex;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( src, srcLen ) );
REQUIRES( srcLen >= 10 && srcLen < MAX_INTLENGTH );
/* Decode the base64 string as a fixed-length continuous string without
padding or newlines. Since we're processing arbitrary-sized input we
can't use the usual FAILSAFE_ITERATIONS_MAX to bound the loop because
the input could be larger than this so we use MAX_INTLENGTH instead */
for( srcIndex = 0; srcIndex < srcLen && \
srcIndex < MAX_INTLENGTH; srcIndex += 4 )
{
int status;
status = decodeBase64chunk( stream, src + srcIndex,
srcLen - srcIndex, TRUE );
if( cryptStatusError( status ) )
{
/* If we've reached the end of the input, we're done */
if( status == OK_SPECIAL )
break;
return( status );
}
}
ENSURES( srcIndex < MAX_INTLENGTH );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int base64decode( OUT_BUFFER( destMaxLen, *destLen ) void *dest,
IN_LENGTH_MIN( 10 ) const int destMaxLen,
OUT_LENGTH_Z int *destLen,
IN_BUFFER( srcLen ) const char *src,
IN_LENGTH_MIN( 10 ) const int srcLen,
IN_ENUM_OPT( CRYPT_CERTFORMAT ) \
const CRYPT_CERTFORMAT_TYPE format )
{
STREAM stream;
int srcIndex, lineByteCount, lineSize = 0, status = DUMMY_INIT;
assert( destMaxLen > 10 && isWritePtr( dest, destMaxLen ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -