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

📄 base64.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*							cryptlib Base64 Routines						*
*						Copyright Peter Gutmann 1992-2007					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "stream.h"
  #include "asn1.h"
#else
  #include "crypt.h"
  #include "io/stream.h"
  #include "misc/asn1.h"
#endif /* Compiler-specific includes */

/* Base64 encode/decode tables from RFC 1113 */

#define BPAD		'='		/* Padding for odd-sized output */
#define BERR		0xFF	/* Illegal character marker */
#define BEOF		0x7F	/* EOF marker (padding character or EOL) */

static const char FAR_BSS binToAscii[ 64 ] = { 
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 
	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
	'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
	'w', 'x', 'y', 'z', '0', '1', '2', '3', 
	'4', '5', '6', '7', '8', '9', '+', '/' 
	};

static const BYTE FAR_BSS asciiToBin[ 256 ] = { 
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* 00 */
	BERR, BERR, BEOF, BERR, BERR, BEOF, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* 10 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* 20 */
	BERR, BERR, BERR, 0x3E, BERR, BERR, BERR, 0x3F,
	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,	/* 30 */
	0x3C, 0x3D, BERR, BERR, BERR, BEOF, BERR, BERR,
	BERR, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,	/* 40 */
	0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
	0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,	/* 50 */
	0x17, 0x18, 0x19, BERR, BERR, BERR, BERR, BERR,
	BERR, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,	/* 60 */
	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
	0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,	/* 70 */
	0x31, 0x32, 0x33, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* 80 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* 90 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* A0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* B0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* C0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* D0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* E0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,	/* F0 */
	BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR
	};

/* The size of lines for base64-encoded data.  This is only used for 
   encoding, for decoding we adjust to whatever size the sender has used */

#define BASE64_LINESIZE	64

/* Basic single-character en/decode functions.  We mask the value to 6 or 8 
   bits both as a range check and to avoid generating negative array offsets 
   if the sign bit is set, since the strings are passed as 'char *'s */

#define encode( data )	binToAscii[ ( data ) & 0x3F ]
#define decode( data )	asciiToBin[ ( data ) & 0xFF ]

/* The headers and trailers used for base64-encoded certificate objects */

typedef struct {
	const CRYPT_CERTTYPE_TYPE type;
	BUFFER_FIXED( headerLen ) \
	const char FAR_BSS *header;
	const int headerLen;
	BUFFER_FIXED( trailerLen ) \
	const char FAR_BSS *trailer;
	const int trailerLen;
	} HEADER_INFO;
static const HEADER_INFO FAR_BSS headerInfo[] = {
	{ CRYPT_CERTTYPE_CERTIFICATE,
	  "-----BEGIN CERTIFICATE-----" EOL, 27 + EOL_LEN,
	  "-----END CERTIFICATE-----" EOL, 25 + EOL_LEN },
	{ CRYPT_CERTTYPE_ATTRIBUTE_CERT,
	  "-----BEGIN ATTRIBUTE CERTIFICATE-----" EOL, 37 + EOL_LEN,
	  "-----END ATTRIBUTE CERTIFICATE-----" EOL, 35 + EOL_LEN },
	{ CRYPT_CERTTYPE_CERTCHAIN,
	  "-----BEGIN CERTIFICATE CHAIN-----" EOL, 33 + EOL_LEN,
	  "-----END CERTIFICATE CHAIN-----" EOL, 31 + EOL_LEN },
	{ CRYPT_CERTTYPE_CERTREQUEST,
	  "-----BEGIN NEW CERTIFICATE REQUEST-----" EOL, 39 + EOL_LEN,
	  "-----END NEW CERTIFICATE REQUEST-----" EOL, 37 + EOL_LEN },
	{ CRYPT_CERTTYPE_REQUEST_CERT,
	  "-----BEGIN NEW CERTIFICATE REQUEST-----" EOL, 39 + EOL_LEN,
	  "-----END NEW CERTIFICATE REQUEST-----" EOL, 37 + EOL_LEN },
	{ CRYPT_CERTTYPE_CRL,
	  "-----BEGIN CERTIFICATE REVOCATION LIST-----"  EOL, 43 + EOL_LEN,
	  "-----END CERTIFICATE REVOCATION LIST-----" EOL, 41 + EOL_LEN },
	{ CRYPT_CERTTYPE_NONE,			/* Universal catch-all */
	  "-----BEGIN CERTIFICATE OBJECT-----"  EOL, 34 + EOL_LEN,
	  "-----END CERTIFICATE OBJECT-----" EOL, 32 + EOL_LEN },
	{ CRYPT_CERTTYPE_NONE,			/* Universal catch-all */
	  "-----BEGIN CERTIFICATE OBJECT-----"  EOL, 34 + EOL_LEN,
	  "-----END CERTIFICATE OBJECT-----" EOL, 32 + EOL_LEN }
	};

/****************************************************************************
*																			*
*						Decode Format-checking Functions					*
*																			*
****************************************************************************/

/* Check for raw base64 data.  There isn't a 100% reliable check that we can 
   apply for this but if the first 60 characters (the minimum base64 line 
   length) are all valid base64 data and the first characters match the 
   encoded form of data handled by cryptlib then it's reasonably certain 
   that it's base64 data */

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 );

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN checkBase64( INOUT STREAM *stream )
	{
	STREAM nullStream;
	BYTE buffer[ 60 + 8 ];
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	/* Make sure that there's enough data present to perform a reliable
	   check */
	status = sread( stream, buffer, 60 );
	if( cryptStatusError( status ) )
		return( FALSE );

	/* Make sure that the content is some form of encoded key or certificate 
	   data. For certificate data that begins with 30 8x the corresponding 
	   base64 values are MI...; for an SSH public key that begins 00 00 it's 
	   AA...; for a PGP public key that begins 99 0x it's mQ... 

	   Unfortunately in the case of MIME-encoded data with a MIME header, 
	   the header is likely to begin with "MIME-Version", which happens to
	   match the base64-encoded form of 30 8x = "MI", so this quick-reject
	   filter won't catch this one particular case */
	if( strCompare( buffer, "MI", 2 ) && \
		strCompare( buffer, "AA", 2 ) && \
		strCompare( buffer, "mQ", 2 ) )
		return( FALSE );

	/* Check that we have at least one minimal-length line of base64-encoded
	   data */
	sMemNullOpen( &nullStream );
	status = fixedBase64decode( &nullStream, buffer, 60 );
	sMemDisconnect( &nullStream );
	if( cryptStatusError( status ) )
		return( FALSE );

	return( TRUE );
	}

/* Check for PEM-encapsulated data.  All that we need to look for is the
   '-----..' header, which is fairly simple although we also need to handle
   the SSH '---- ...' variant (4 dashes and a space) */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int checkPEMHeader( INOUT STREAM *stream, 
						   OUT_LENGTH_Z int *headerLength )
	{
	BOOLEAN isSSH = FALSE, isPGP = FALSE;
	char buffer[ 1024 + 8 ], *bufPtr = buffer;
	int length, position = DUMMY_INIT, lineCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( headerLength, sizeof( int ) ) );

	/* Clear return value */
	*headerLength = 0;

	/* Check for the initial 5 dashes and 'BEGIN ' (unless we're SSH, in
	   which case we use 4 dashes, a space, and 'BEGIN ') */
	status = readTextLine( ( READCHARFUNCTION ) sgetc, stream, 
						   buffer, 1024, &length, NULL );
	if( cryptStatusError( status ) )
		return( status );
	if( length < 5 + 6 + 7 + 5 )
		{
		/* We need room for at least '-----' (5) + 'BEGIN ' (6) + 
		   'ABC XYZ' (7) + '-----' (5) */
		return( CRYPT_ERROR_BADDATA );
		}
	if( strCompare( bufPtr, "-----BEGIN ", 11 ) &&	/* PEM/PGP form */
		strCompare( bufPtr, "---- BEGIN ", 11 ) )	/* SSH form */
		return( CRYPT_ERROR_BADDATA );
	bufPtr += 11;
	length -= 11;

	/* Skip the object name */
	if( !strCompare( bufPtr, "SSH2 ", 5 ) )
		isSSH = TRUE;
	else
		{
		if( !strCompare( bufPtr, "PGP ", 4 ) )
			isPGP = TRUE;
		}
	while( length >= 4 )
		{
		if( *bufPtr == '-' )
			break;
		bufPtr++;
		length--;
		}
	if( length != 5 && length != 4 )
		return( CRYPT_ERROR_BADDATA );

	/* Check the the trailing 5 (4 for SSH) dashes */
	if( strCompare( bufPtr, "-----", length ) )
		return( CRYPT_ERROR_BADDATA );

	/* If it's not SSH or PGP data, we're done */
	if( !isSSH && !isPGP )
		{
		*headerLength = stell( stream );

		return( CRYPT_OK );
		}

	/* At this point SSH and PGP can continue with an arbitrary number of
	   type : value pairs that we have to strip before we get to the
	   payload.  SSH runs the header straight into the body so the only way 
	   to tell whether we've hit the body is to check for the absence of the 
	   ':' separator, while PGP uses a conventional header format with a 
	   blank line as the delimiter so all that we have to do is look for a 
	   zero-length line */
	for( lineCount = 0; lineCount < FAILSAFE_ITERATIONS_MED; lineCount++ )
		{
		position = stell( stream );
		status = readTextLine( ( READCHARFUNCTION ) sgetc, stream, 
							   buffer, 1024, &length, NULL );
		if( cryptStatusError( status ) )
			return( status );
		if( isSSH && strFindCh( buffer, length, ':' ) < 0 )
			break;
		if( isPGP && length <= 0 )
			break;
		}
	if( lineCount >= FAILSAFE_ITERATIONS_MED )
		return( CRYPT_ERROR_BADDATA );
	if( isSSH )
		{
		/* Return to the point before the line without the ':' */
		sseek( stream, position );
		}
	*headerLength = stell( stream );

	return( CRYPT_OK );
	}

/* Look for the EOL marker at the end of a line of text.  There's one
   problematic special case here in which, 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 either 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, return a 0-count EOL indicator */

CHECK_RETVAL_SPECIAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int checkEOL( IN_BUFFER( srcLen ) const char *src, 
					 IN_LENGTH const int srcLen,
					 OUT_LENGTH_Z int *eolSize,
					 IN_ENUM( CRYPT_CERTFORMAT ) \
						const CRYPT_CERTFORMAT_TYPE format )
	{
	int srcIndex = 0;

	assert( isReadPtr( src, srcLen ) );
	assert( isWritePtr( eolSize, sizeof( int ) ) );

	REQUIRES( srcLen > 0 && srcLen < MAX_INTLENGTH );
	REQUIRES( format > CRYPT_CERTFORMAT_NONE && \
			  format < CRYPT_CERTFORMAT_LAST );

	/* Clear return value */
	*eolSize = 0;

	/* Check for a '\0' at the end of the data */
	if( format == CRYPT_ICERTFORMAT_SMIME_CERTIFICATE && src[ 0 ] == '\0' )
		return( OK_SPECIAL );	/* We're at EOF, not just EOL */

	/* Check for EOL */
	if( *src == '\n' )
		srcIndex++;
	else
		{
		if( *src == '\r' )
			{
			srcIndex++;

			/* Some broken implementations emit two CRs before the LF.
			   Stripping these extra CRs clashes with other broken
			   implementations that emit only CRs, which means that we'll
			   be stripping the EOT blank line in MIME encapsulation,
			   however the two-CR bug (usually from older versions of
			   Netscape) appears to be more prevalent than the CR-only
			   bug (old Mac software) */
			if( ( srcIndex < srcLen ) && src[ srcIndex ] == '\r' )
				srcIndex++;
			if( ( srcIndex < srcLen ) && src[ srcIndex ] == '\n' )
				srcIndex++;
			}
		}
	if( srcIndex >= srcLen )
		return( OK_SPECIAL );	/* We're at EOF, not just EOL */

	/* Check for '\0' or EOL (S/MIME) or '----END...' (PEM) after EOL */
	if( format == CRYPT_ICERTFORMAT_SMIME_CERTIFICATE )
		{
		if( src[ srcIndex ] == '\0' || src[ srcIndex ] == '\n' || \
			src[ srcIndex ] == '\r' )
			return( OK_SPECIAL );	/* We're at EOF, not just EOL */
		}
	if( format == CRYPT_CERTFORMAT_TEXT_CERTIFICATE )
		{
		if( srcIndex + 9 <= srcLen && \
			!strCompare( src + srcIndex, "-----END ", 9 ) )
			return( OK_SPECIAL );	/* We're at EOF, not just EOL */
		}

	/* If we were expecting an EOL but didn't find one there's a problem 
	   with the data */
	if( srcIndex <= 0 )
		return( CRYPT_ERROR_BADDATA );

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -