📄 base64.c
字号:
assert( isWritePtr( destLen, sizeof( int ) ) );
assert( srcLen > 10 && isReadPtr( src, srcLen ) );
REQUIRES( destMaxLen > 10 && destMaxLen < MAX_INTLENGTH );
REQUIRES( srcLen > 10 && srcLen < MAX_INTLENGTH );
REQUIRES( format >= CRYPT_CERTFORMAT_NONE && \
format < CRYPT_CERTFORMAT_LAST );
/* Clear return values */
memset( dest, 0, min( 16, destMaxLen ) );
*destLen = 0;
sMemOpen( &stream, dest, destMaxLen );
/* 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 )
{
status = fixedBase64decode( &stream, src, srcLen );
if( cryptStatusOK( status ) )
*destLen = stell( &stream );
sMemDisconnect( &stream );
return( status );
}
/* Decode the encoded object. 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, lineByteCount = 0;
srcIndex < srcLen && srcIndex < MAX_INTLENGTH;
srcIndex += 4, lineByteCount += 4 )
{
/* Depending on implementations, the length of the base64-encoded
line can vary from 60 to 72 characters. We adjust for this by
checking for the first EOL and setting the line length to the
size of the first line of base64 text */
if( lineSize <= 0 && \
( src[ srcIndex ] == '\r' || src[ srcIndex ] == '\n' ) )
{
if( lineByteCount < 56 || lineByteCount > 128 )
{
/* Suspiciously short or long text line */
sMemDisconnect( &stream );
return( CRYPT_ERROR_BADDATA );
}
lineSize = lineByteCount;
}
/* If we've reached the end of a line of text, look for the EOL
marker */
if( lineSize > 0 && lineByteCount >= lineSize )
{
int eolDataSize;
status = checkEOL( src + srcIndex, srcLen - srcIndex,
&eolDataSize, format );
if( cryptStatusError( status ) )
{
/* If we get an OK_SPECIAL status it means that we've
reached the EOF rather than just an EOL */
if( status == OK_SPECIAL )
{
status = CRYPT_OK;
break;
}
sMemDisconnect( &stream );
return( status );
}
srcIndex += eolDataSize;
lineByteCount = 0;
}
/* Decode a chunk of data from the input buffer */
status = decodeBase64chunk( &stream, src + srcIndex,
srcLen - srcIndex, FALSE );
if( cryptStatusError( status ) )
{
/* If we've reached the end of the input, we're done. Note that
we can't just wait for srcIndex to pass srcLen as for the
fixed-length decode because there could be extra trailer data
following the base64 data.
In theory we could call checkEOL() here to make sure that the
trailer is well-formed but if the data is truncated right on
the base64 end marker then this would produce an error so we
just stop decoding as soon as we find the end marker */
if( status == OK_SPECIAL )
{
status = CRYPT_OK;
break;
}
sMemDisconnect( &stream );
return( status );
}
}
ENSURES( srcIndex < MAX_INTLENGTH );
if( cryptStatusOK( status ) )
*destLen = stell( &stream );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
/* Calculate the size of a quantity of data once it's en/decoded */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
int base64decodeLen( IN_BUFFER( dataLength ) const char *data,
IN_LENGTH_MIN( 10 ) const int dataLength,
OUT_LENGTH_Z int *decodedLength )
{
STREAM stream;
int ch, length = DUMMY_INIT, iterationCount;
assert( isReadPtr( data, dataLength ) );
assert( isWritePtr( decodedLength, sizeof( int ) ) );
REQUIRES( dataLength >= 10 && dataLength < MAX_INTLENGTH );
/* Clear return value */
*decodedLength = 0;
/* Skip ahead until we find the end of the decodable data. This ignores
errors on the input stream since at this point all that we're
interested in is how much we can decode from it and not whether it's
valid or not. Handling this gets a bit complicated since once the
stream enters an error state stell() doesn't try and return a stream
position any more because the stream is in the error state, so we have
to check the position before every read.
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 */
sMemConnect( &stream, data, dataLength );
for( iterationCount = 0; iterationCount < MAX_INTLENGTH; iterationCount++ )
{
length = stell( &stream );
ch = sgetc( &stream );
if( cryptStatusError( ch ) || ch == BPAD )
break;
if( ch == '\r' || ch == '\n' )
{
/* Don't try and decode out-of-band data */
continue;
}
ch = decode( ch );
if( ch == BERR || ch == BEOF )
break;
}
ENSURES( iterationCount < MAX_INTLENGTH );
sMemDisconnect( &stream );
/* Return a rough estimate of how much room the decoded data will occupy.
This ignores the EOL size so it always overestimates, but a strict
value isn't necessary since it's only used for memory buffer
allocation */
*decodedLength = ( length * 3 ) / 4;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Base64 Encoding Functions *
* *
****************************************************************************/
/* Calculate the size of a quantity of data once it's encoded */
CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int base64encodeLen( IN_LENGTH_MIN( 10 ) const int dataLength,
OUT_LENGTH_Z int *encodedLength,
IN_ENUM_OPT( CRYPT_CERTTYPE ) \
const CRYPT_CERTTYPE_TYPE certType )
{
int length = roundUp( ( dataLength * 4 ) / 3, 4 ), headerInfoIndex;
assert( isWritePtr( encodedLength, sizeof( int ) ) );
REQUIRES( dataLength >= 10 && dataLength < MAX_INTLENGTH );
REQUIRES( certType >= CRYPT_CERTTYPE_NONE && \
certType < CRYPT_CERTTYPE_LAST );
ENSURES( length >= 10 && length < MAX_INTLENGTH );
/* Clear return value */
*encodedLength = 0;
/* Find the header/trailer info for this format */
for( headerInfoIndex = 0;
headerInfo[ headerInfoIndex ].type != certType && \
headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE && \
headerInfoIndex < FAILSAFE_ARRAYSIZE( headerInfo, HEADER_INFO );
headerInfoIndex++ );
ENSURES( headerInfoIndex < FAILSAFE_ARRAYSIZE( headerInfo, HEADER_INFO ) );
ENSURES( headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE );
/* Calculate the extra length due to EOLs and delimiters */
length += ( ( roundUp( length, BASE64_LINESIZE ) / BASE64_LINESIZE ) * EOL_LEN );
length = headerInfo[ headerInfoIndex ].headerLen + length + \
headerInfo[ headerInfoIndex ].trailerLen;
ENSURES( length > 10 && length < MAX_INTLENGTH );
*encodedLength = length;
return( CRYPT_OK );
}
/* Encode a block of binary data into the base64 format */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int base64encode( OUT_BUFFER( destMaxLen, *destLen ) char *dest,
IN_LENGTH_MIN( 10 ) const int destMaxLen,
OUT_LENGTH_Z int *destLen,
IN_BUFFER( srcLen ) const void *src,
IN_LENGTH_MIN( 10 ) const int srcLen,
IN_ENUM_OPT( CRYPT_CERTTYPE ) \
const CRYPT_CERTTYPE_TYPE certType )
{
STREAM stream;
const BYTE *srcPtr = src;
int srcIndex, lineByteCount, remainder = srcLen % 3;
int headerInfoIndex = DUMMY_INIT, status = DUMMY_INIT;
assert( destMaxLen > 10 && isWritePtr( dest, destMaxLen ) );
assert( isWritePtr( destLen, sizeof( int ) ) );
assert( srcLen >= 10 && isReadPtr( src, srcLen ) );
REQUIRES( destMaxLen >= 10 && destMaxLen > srcLen && \
destMaxLen < MAX_INTLENGTH );
REQUIRES( srcLen >= 10 && srcLen < MAX_INTLENGTH );
REQUIRES( certType >= CRYPT_CERTTYPE_NONE && \
certType < CRYPT_CERTTYPE_LAST );
/* Clear return values */
memset( dest, 0, min( 16, destMaxLen ) );
*destLen = 0;
sMemOpen( &stream, dest, destMaxLen );
/* If it's an encoded certificate object rather than raw base64 data,
add the header */
if( certType != CRYPT_CERTTYPE_NONE )
{
for( headerInfoIndex = 0;
headerInfo[ headerInfoIndex ].type != certType && \
headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE && \
headerInfoIndex < FAILSAFE_ARRAYSIZE( headerInfo, HEADER_INFO );
headerInfoIndex++ );
ENSURES( headerInfoIndex < FAILSAFE_ARRAYSIZE( headerInfo, HEADER_INFO ) );
ENSURES( headerInfo[ headerInfoIndex ].type != CRYPT_CERTTYPE_NONE );
status = swrite( &stream, headerInfo[ headerInfoIndex ].header,
headerInfo[ headerInfoIndex ].headerLen );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
}
/* Encode the data */
for( srcIndex = 0, lineByteCount = 0;
srcIndex < srcLen;
lineByteCount += 4 )
{
const int srcLeft = srcLen - srcIndex;
/* If we've reached the end of a line of binary data and it's a
certificate object rather than a raw binary blob, add the EOL
marker */
if( certType != CRYPT_CERTTYPE_NONE && \
lineByteCount >= BASE64_LINESIZE )
{
status = swrite( &stream, EOL, EOL_LEN );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
lineByteCount = 0;
}
/* Encode a block of data from the input buffer */
sputc( &stream, encode( ( srcPtr[ srcIndex ] >> 2 ) & 0x3F ) );
if( srcLeft < 2 )
{
REQUIRES( remainder == 1 );
status = sputc( &stream, encode( ( srcPtr[ srcIndex ] << 4 ) & 0x30 ) );
break;
}
sputc( &stream, encode( ( ( srcPtr[ srcIndex ] << 4 ) & 0x30 ) | \
( ( srcPtr[ srcIndex + 1 ] >> 4 ) & 0x0F ) ) );
srcIndex++;
if( srcLeft < 3 )
{
REQUIRES( remainder == 2 );
status = sputc( &stream, encode( ( srcPtr[ srcIndex ] << 2 ) & 0x3C ) );
break;
}
sputc( &stream, encode( ( ( srcPtr[ srcIndex ] << 2 ) & 0x3C ) | \
( ( srcPtr[ srcIndex + 1 ] >> 6 ) & 0x03 ) ) );
srcIndex++;
status = sputc( &stream, encode( srcPtr[ srcIndex++ ] & 0x3F ) );
if( cryptStatusError( status ) )
break;
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* If it's a certificate object, add any required padding and the
trailer */
if( certType != CRYPT_CERTTYPE_NONE )
{
/* Add any necessary padding. For 0 bytes of remainder there's no
padding (the data fits exactly), for 1 byte of remainder there's
2 bytes of padding ("X=="), and for 2 bytes of remainder there's
1 byte of padding ("XX=") */
if( remainder > 0 )
{
status = sputc( &stream, BPAD );
if( remainder == 1 )
status = sputc( &stream, BPAD );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
}
/* Add the trailer */
swrite( &stream, EOL, EOL_LEN );
status = swrite( &stream, headerInfo[ headerInfoIndex ].trailer,
headerInfo[ headerInfoIndex ].trailerLen );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
}
*destLen = stell( &stream );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -