⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 base64.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:

	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 + -