📄 base64.c
字号:
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 there are further types in the
endless 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 we check for is the base64 indicator */
CRYPT_CERTFORMAT_TYPE base64checkHeader( const char *data,
const int dataLength, int *startPos )
{
STREAM stream;
BOOLEAN seenTransferEncoding = FALSE, isBinaryEncoding = FALSE;
int position, ch, status;
assert( isReadPtr( data, dataLength ) );
assert( isWritePtr( startPos, sizeof( int ) ) );
/* Clear return value */
*startPos = 0;
/* If the item is too small to contain any useful data, we don't even try
and examine it */
if( dataLength < 64 )
return( CRYPT_CERTFORMAT_NONE );
sMemConnect( &stream, data, dataLength );
/* Sometimes the object can be preceded by a few blank lines. We're
fairly lenient with this. Note that we can't use readLine() at this
point because we don't know yet whether we're getting binary or ASCII
data */
do
ch = sgetc( &stream );
while( ch == '\r' || ch == '\n' );
position = stell( &stream ) - 1;
/* Perform a quick check to weed out non-encoded cert data, which is
usually the case */
if( ( ch == 0x30 ) && ( !isAlpha( sgetc( &stream ) ) || \
!isAlpha( sgetc( &stream ) ) || \
!isAlpha( sgetc( &stream ) ) ) )
{
sMemDisconnect( &stream );
return( CRYPT_CERTFORMAT_NONE );
}
sseek( &stream, position );
/* If it starts with a dash, check for PEM header encapsulation */
if( ch == '-' )
{
status = checkPEMHeader( &stream, startPos );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( checkBase64( &stream ) )
{
sMemDisconnect( &stream );
return( CRYPT_CERTFORMAT_TEXT_CERTIFICATE );
}
}
/* Check for raw base64 data */
if( checkBase64( &stream ) )
{
sMemDisconnect( &stream );
*startPos = position;
return( CRYPT_CERTFORMAT_TEXT_CERTIFICATE );
}
sseek( &stream, position );
/* It doesn't look like raw base64, check for an S/MIME header */
do
{
char buffer[ 1024 ];
status = readLine( &stream, buffer, 1024 );
if( !cryptStatusError( status ) && status >= 33 && \
!strCompare( buffer, "Content-Transfer-Encoding:", 26 ) )
{
int index;
/* Check for a valid content encoding type */
for( index = 26; index < status && buffer[ index ] == ' ';
index++ );
if( status - 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;
}
}
while( status > 0 );
if( cryptStatusError( status ) || !seenTransferEncoding )
{
sMemDisconnect( &stream );
return( CRYPT_CERTFORMAT_NONE );
}
/* Skip trailing blank lines */
do
ch = sgetc( &stream );
while( ch == '\r' || ch == '\n' );
position = stell( &stream ) - 1;
sseek( &stream, position );
/* Make sure that the content is some form of encoded cert */
*startPos = position;
status = isBinaryEncoding ? CRYPT_CERTFORMAT_CERTIFICATE : \
checkBase64( &stream ) ? CRYPT_ICERTFORMAT_SMIME_CERTIFICATE : \
CRYPT_CERTFORMAT_NONE;
sMemDisconnect( &stream );
return( status );
}
/* Encode a block of binary data into the base64 format, returning the total
number of output bytes */
int base64encode( char *dest, const int destMaxLen, const void *src,
const int srcLen, const CRYPT_CERTTYPE_TYPE certType )
{
BYTE *srcPtr = ( BYTE * ) src;
int srcIndex = 0, destIndex = 0, lineCount = 0, remainder = srcLen % 3;
int headerInfoIndex;
assert( destMaxLen > 10 && isWritePtr( dest, destMaxLen ) );
assert( srcLen > 10 && isReadPtr( src, srcLen ) );
/* If it's a certificate object, add the header */
if( certType != CRYPT_CERTTYPE_NONE )
{
for( headerInfoIndex = 0;
headerInfo[ headerInfoIndex ].type != certType && \
headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE;
headerInfoIndex++ );
assert( headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE );
destIndex = strlen( headerInfo[ headerInfoIndex ].header );
if( destIndex > destMaxLen )
return( CRYPT_ERROR_OVERFLOW );
memcpy( dest, headerInfo[ headerInfoIndex ].header, destIndex );
}
/* Encode the data */
while( srcIndex < srcLen )
{
/* If we've reached the end of a line of binary data and it's a
certificate, add the EOL marker */
if( certType != CRYPT_CERTTYPE_NONE && lineCount >= BINARY_LINESIZE )
{
strcpy( dest + destIndex, EOL );
destIndex += EOL_LEN;
lineCount = 0;
}
lineCount += 3;
/* Encode a block of data from the input buffer */
dest[ destIndex++ ] = encode( srcPtr[ srcIndex ] >> 2 );
dest[ destIndex++ ] = encode( ( ( srcPtr[ srcIndex ] << 4 ) & 0x30 ) |
( ( srcPtr[ srcIndex + 1 ] >> 4 ) & 0x0F ) );
srcIndex++;
dest[ destIndex++ ] = encode( ( ( srcPtr[ srcIndex ] << 2 ) & 0x3C ) |
( ( srcPtr[ srcIndex + 1 ] >> 6 ) & 0x03 ) );
srcIndex++;
dest[ destIndex++ ] = encode( srcPtr[ srcIndex++ ] & 0x3F );
if( destIndex > destMaxLen )
return( CRYPT_ERROR_OVERFLOW );
}
/* Go back and add padding and correctly encode the last char if we've
encoded too many characters */
if( remainder == 2 )
{
/* There were only 2 bytes in the last group */
dest[ destIndex - 1 ] = BPAD;
dest[ destIndex - 2 ] = \
encode( ( srcPtr[ srcIndex - 2 ] << 2 ) & 0x3C );
}
else
if( remainder == 1 )
{
/* There was only 1 byte in the last group */
dest[ destIndex - 2 ] = dest[ destIndex - 1 ] = BPAD;
dest[ destIndex - 3 ] = \
encode( ( srcPtr[ srcIndex - 3 ] << 4 ) & 0x30 );
}
/* If it's a certificate object, add the trailer */
if( certType != CRYPT_CERTTYPE_NONE )
{
const int length = strlen( headerInfo[ headerInfoIndex ].trailer );
if( destIndex + EOL_LEN + length > destMaxLen )
return( CRYPT_ERROR_OVERFLOW );
memcpy( dest + destIndex, EOL, EOL_LEN );
memcpy( dest + destIndex + EOL_LEN,
headerInfo[ headerInfoIndex ].trailer, length );
destIndex += EOL_LEN + length;
}
else
/* It's not a certificate, truncate the unnecessary padding */
destIndex -= ( 3 - remainder ) % 3;
/* Return a count of encoded bytes */
#ifdef EBCDIC_CHARS
asciiToEbcdic( dest, dest, length );
#endif /* EBCDIC_CHARS */
return( destIndex );
}
/* Decode a block of binary data from the base64 format, returning the total
number of decoded bytes */
static int fixedBase64decode( void *dest, const int destMaxLen,
const char *src, const int srcLen )
{
int srcIndex = 0, destIndex = 0;
BYTE *destPtr = dest;
/* Decode the base64 string as a fixed-length continuous string without
padding or newlines */
while( srcIndex < srcLen )
{
BYTE c0, c1, c2 = 0, c3 = 0;
const int delta = srcLen - srcIndex;
/* Decode a block of data from the input buffer */
c0 = decode( src[ srcIndex++ ] );
c1 = decode( src[ srcIndex++ ] );
if( delta > 2 )
{
c2 = decode( src[ srcIndex++ ] );
if( delta > 3 )
c3 = decode( src[ srcIndex++ ] );
}
if( ( c0 | c1 | c2 | c3 ) == BERR )
return( CRYPT_ERROR_BADDATA );
/* Copy the decoded data to the output buffer */
destPtr[ destIndex++ ] = ( c0 << 2 ) | ( c1 >> 4 );
if( delta > 2 )
{
destPtr[ destIndex++ ] = ( c1 << 4 ) | ( c2 >> 2);
if( delta > 3 )
destPtr[ destIndex++ ] = ( c2 << 6 ) | ( c3 );
}
if( destIndex > destMaxLen )
return( CRYPT_ERROR_OVERFLOW );
}
/* Return count of decoded bytes */
return( destIndex );
}
int base64decode( void *dest, const int destMaxLen, const char *src,
const int srcLen, const CRYPT_CERTFORMAT_TYPE format )
{
int srcIndex = 0, destIndex = 0, lineCount = 0, lineSize = 0;
BYTE c0, c1, c2, c3, *destPtr = dest;
assert( destMaxLen > 10 && isWritePtr( dest, destMaxLen ) );
assert( srcLen > 10 && isReadPtr( src, srcLen ) );
/* If it's not a certificate, it's a straight base64 string and we can
use the simplified decoding routines */
if( format == CRYPT_CERTFORMAT_NONE )
return( fixedBase64decode( dest, destMaxLen, src, srcLen ) );
/* Decode the encoded object */
while( srcIndex < srcLen )
{
BYTE cx;
/* Depending on implementations, the length of the base64-encoded
line can vary from 60 to 72 chars, we adjust for this by checking
for an EOL and setting the line length to this size */
if( !lineSize && \
( src[ srcIndex ] == '\r' || src[ srcIndex ] == '\n' ) )
lineSize = lineCount;
/* If we've reached the end of a line of text, look for the EOL
marker. There's one problematic special case here where, if the
encoding has produced bricktext, the end of the data will
coincide with the EOL. For CRYPT_CERTFORMAT_TEXT_CERTIFICATE
this will give us '-----END...' on the next line which is easy to
check for, but for CRYPT_ICERTFORMAT_SMIME_CERTIFICATE what we
end up with depends on the calling code, it could truncate
immediately at the end of the data (which it isn't supposed to)
so we get '\0', it could truncate after the EOL (so we get EOL +
'\0'), it could continue with a futher content type after a blank
line (so we get EOL + EOL), or it could truncate without the '\0'
so we get garbage, which is the caller's problem. Because of
this we look for all of these situations and, if any are found,
set c0 to BEOF and advance srcIndex by 4 to take into account the
adjustment for overshoot that occurs when we break out of the
loop */
if( lineCount >= lineSize )
{
/* Check for '\0' at the end of the data */
if( format == CRYPT_ICERTFORMAT_SMIME_CERTIFICATE && \
!src[ srcIndex ] )
{
c0 = c1 = c2 = BEOF;
srcIndex += 4;
break;
}
/* Check for EOL */
if( src[ srcIndex ] == '\n' )
srcIndex++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -