📄 base64.c
字号:
/****************************************************************************
* *
* cryptlib Base64 Routines *
* Copyright Peter Gutmann 1992-2004 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "stream.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../io/stream.h"
#else
#include "crypt.h"
#include "io/stream.h"
#endif /* Compiler-specific includes */
/* Base64 encode/decode tables from RFC 1113. We convert from ASCII <->
EBCDIC on entry and exit, so there's no need for special-case EBCDIC
handling elsewhere */
#define BPAD '=' /* Padding for odd-sized output */
#define BERR 0xFF /* Illegal char marker */
#define BEOF 0x7F /* EOF marker (padding char or EOL) */
static const FAR_BSS char binToAscii[] = \
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const FAR_BSS BYTE 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
};
#if 0
/* EBCDIC character mappings:
A-I C1-C9
J-R D1-D9
S-Z E2-E9
a-i 81-89
j-r 91-99
s-z A2-A9
0-9 F0-F9
+ 4E
/ 61
= 7E Uses BEOF in table */
static const FAR_BSS BYTE asciiToBin[ 256 ] =
{ BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* 00 */
BERR, BERR, BEOF, BERR, BERR, BEOF, BERR, BERR, /* CR, LF */
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, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* 30 */
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* 40 */
BERR, BERR, BERR, BERR, BERR, BERR, 0x3E, BERR, /* + */
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* 50 */
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, 0x3F, BERR, BERR, BERR, BERR, BERR, BERR, /* 60 / */
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* 70 */
BERR, BERR, BERR, BERR, BERR, BERR, BEOF, BERR, /* = */
BERR, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, /* 80 a-i */
0x21, 0x22, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* 90 j-r */
0x2A, 0x2B, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, /* A0 s-z */
0x32, 0x33, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR, /* B0 */
BERR, BERR, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* C0 A-I */
0x07, 0x08, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* D0 J-R */
0x10, 0x11, BERR, BERR, BERR, BERR, BERR, BERR,
BERR, BERR, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* E0 S-Z */
0x18, 0x19, BERR, BERR, BERR, BERR, BERR, BERR,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, /* F0 0-9 */
0x3C, 0x3D, BERR, BERR, BERR, BERR, BERR, BERR
};
#endif /* 0 */
/* The size of lines for PEM-type formatting. This is only used for encoding,
for decoding we adjust to whatever size the sender has used */
#define TEXT_LINESIZE 64
#define BINARY_LINESIZE 48
/* Basic single-char en/decode functions. We cast the value to an unsigned
char to avoid generating negative array offsets if the sign bit is set,
since the strings are passed as char *'s */
#define encode(data) binToAscii[ ( BYTE ) data ]
#define decode(data) asciiToBin[ ( BYTE ) data ]
/* The headers and trailers used for base64-encoded certificate objects */
static const FAR_BSS struct {
const CRYPT_CERTTYPE_TYPE type;
const char *header, *trailer;
} headerInfo[] = {
{ CRYPT_CERTTYPE_CERTIFICATE,
"-----BEGIN CERTIFICATE-----" EOL,
"-----END CERTIFICATE-----" EOL },
{ CRYPT_CERTTYPE_ATTRIBUTE_CERT,
"-----BEGIN ATTRIBUTE CERTIFICATE-----" EOL,
"-----END ATTRIBUTE CERTIFICATE-----" EOL },
{ CRYPT_CERTTYPE_CERTCHAIN,
"-----BEGIN CERTIFICATE CHAIN-----" EOL,
"-----END CERTIFICATE CHAIN-----" EOL },
{ CRYPT_CERTTYPE_CERTREQUEST,
"-----BEGIN NEW CERTIFICATE REQUEST-----" EOL,
"-----END NEW CERTIFICATE REQUEST-----" EOL },
{ CRYPT_CERTTYPE_REQUEST_CERT,
"-----BEGIN NEW CERTIFICATE REQUEST-----" EOL,
"-----END NEW CERTIFICATE REQUEST-----" EOL },
{ CRYPT_CERTTYPE_CRL,
"-----BEGIN CERTIFICATE REVOCATION LIST-----" EOL,
"-----END CERTIFICATE REVOCATION LIST-----" EOL },
{ CRYPT_CERTTYPE_NONE, /* Universal catch-all */
"-----BEGIN CERTIFICATE OBJECT-----" EOL,
"-----END CERTIFICATE OBJECT-----" EOL }
};
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Read a line of text data ending in an EOL */
static int readLine( STREAM *stream, char *buffer, const int maxSize )
{
MIME_STATE state;
int status;
initMIMEstate( &state, maxSize );
do
{
const int ch = sgetc( stream );
status = ( cryptStatusError( ch ) ) ? ch : \
addMIMEchar( &state, buffer, ch );
}
while( cryptStatusOK( status ) );
if( cryptStatusError( status ) && status != OK_SPECIAL )
return( status );
return( endMIMEstate( &state ) );
}
/* Check for raw base64 data. There isn't a 100% reliable check for this,
but if the first 60 chars (the minimum base64 line length) are all valid
base64 chars and the first chars match the required values then it's
reasonably certain that it's base64 data */
static BOOLEAN checkBase64( STREAM *stream )
{
char buffer[ 8 ], headerBuffer[ 4 ];
BOOLEAN gotHeader = FALSE;
int i, status;
/* Make sure that there's enough data present to perform a reliable
check */
if( sMemDataLeft( stream ) < 15 * 4 )
return( FALSE );
/* Check that we have at least 15 lots (60 chars) of base64-encoded
data */
for( i = 0; i < 15; i++ )
{
BYTE c0, c1, c2, c3, cx;
status = sread( stream, buffer, 4 );
if( cryptStatusError( status ) )
return( FALSE );
if( !gotHeader )
{
memcpy( headerBuffer, buffer, 2 );
gotHeader = TRUE;
}
c0 = decode( buffer[ 0 ] );
c1 = decode( buffer[ 1 ] );
c2 = decode( buffer[ 2 ] );
c3 = decode( buffer[ 3 ] );
cx = c0 | c1 | c2 | c3;
if( cx == BEOF || cx == BERR )
return( FALSE );
}
/* Make sure that the content is some form of encoded cert. For cert
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... */
if( strCompare( headerBuffer, "MI", 2 ) && \
strCompare( headerBuffer, "AA", 2 ) && \
strCompare( headerBuffer, "mQ", 2 ) )
return( FALSE );
return( TRUE );
}
/* Check for PEM-encapsulated data. All 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) */
static int checkPEMHeader( STREAM *stream, int *startPos )
{
BOOLEAN isSSH = FALSE, isPGP = FALSE;
char buffer[ 1024 ], *bufPtr = buffer;
int i, position, length;
/* Check for the initial 5 dashes and 'BEGIN ' (unless we're SSH, in
which case we use 4 dashes, a space, and 'BEGIN ') */
length = readLine( stream, buffer, 1024 );
if( cryptStatusError( length ) )
return( length );
if( strCompare( bufPtr, "-----BEGIN ", 11 ) && \
strCompare( bufPtr, "---- BEGIN ", 11 ) )
return( CRYPT_CERTFORMAT_NONE );
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;
if( length != 4 && length != 3 )
return( CRYPT_CERTFORMAT_NONE );
/* Check the the trailing 5 (4 for SSH) dashes */
if( strCompare( bufPtr, "----", length ) )
return( CRYPT_CERTFORMAT_NONE );
/* 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 */
if( isSSH )
{
/* 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 */
do
{
position = stell( stream );
length = readLine( stream, buffer, 1024 );
if( cryptStatusError( length ) )
return( CRYPT_CERTFORMAT_NONE );
for( i = 0; i < length && buffer[ i ] != ':'; i++ );
}
while( i < length );
sseek( stream, position );
}
if( isPGP )
{
/* PGP uses a conventional header format with a blank line as the
delimiter so all we have to do is look for a zero-length line */
do
{
length = readLine( stream, buffer, 1024 );
if( cryptStatusError( length ) )
return( CRYPT_CERTFORMAT_NONE );
}
while( length > 0 );
}
/* Return the start position of the payload */
*startPos = stell( stream );
return( CRYPT_CERTFORMAT_TEXT_CERTIFICATE );
}
/****************************************************************************
* *
* Base64 En/Decoding Functions *
* *
****************************************************************************/
/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -