?? cryptmis.c
字號:
#define BINARY_LINESIZE 48
/* Basic single-char en/decode functions */
#define encode(data) binToAscii[ data ]
#define decode(data) asciiToBin[ data ]
/* The headers and trailers used for base64-encoded certificate objects.
Since the zero-th value is a non-value, we include a dummy entry at the
start */
static const char *headerTbl[] = {
NULL,
"-----BEGIN CERTIFICATE-----" EOL,
"-----BEGIN ATTRIBUTE CERTIFICATE-----" EOL,
"-----BEGIN CERTIFICATE CHAIN-----" EOL,
"-----BEGIN NEW CERTIFICATE REQUEST-----" EOL,
"-----BEGIN CRL-----" EOL
};
static const char *trailerTbl[] = {
NULL,
"-----END CERTIFICATE-----" EOL,
"-----END ATTRIBUTE CERTIFICATE-----" EOL,
"-----END CERTIFICATE CHAIN-----" EOL,
"-----END NEW CERTIFICATE REQUEST-----" EOL,
"-----END CRL-----" EOL
};
/* Check whether a data item has a header which identifies it as some form of
PEM-style encoded certificate object and return the start position of the
encoded data. Since there are so many variants possible, we don't perform
a very strict check because there'll always be some new variants which
isn't handled. The exact object type can be determined by the lower-level
routines */
int base64checkHeader( const char *data, const int dataLength )
{
STREAM stream;
char buffer[ 64 ];
int ch, i;
sMemConnect( &stream, data, dataLength );
/* Sometimes the object can be preceded by a few blank lines - we're
fairly lenient with this */
do
ch = sgetc( &stream );
while( ch == '\r' || ch == '\n' );
buffer[ 0 ] = ch;
/* We always have to start with 5 dashes and 'BEGIN '. After this there
can be all sorts of stuff, but it has to end with another five dashes
and a newline */
if( cryptStatusError( sread( &stream, buffer + 1, 10 ) ) || \
memcmp( buffer, "-----BEGIN ", 11 ) )
{
sMemDisconnect( &stream );
return( 0 );
}
for( i = 0; i < 40; i++ )
if( sgetc( &stream ) == '-' )
break;
if( i == 40 )
{
sMemDisconnect( &stream );
return( 0 );
}
if( cryptStatusError( sread( &stream, buffer, 4 ) ) || \
memcmp( buffer, "----", 4 ) )
{
sMemDisconnect( &stream );
return( 0 );
}
ch = sgetc( &stream );
if( ch != '\n' )
{
if( ch == '\r' )
{
if( sgetc( &stream ) != '\n' )
sungetc( &stream );
}
else
{
sMemDisconnect( &stream );
return( 0 );
}
}
/* Return the start position of the payload */
i = stell( &stream );
sMemDisconnect( &stream );
return( cryptStatusError( i ) ? 0 : i );
}
/* Check whether a data item has a header which identifies it as some form of
S/MIME certificate data. This gets 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. Older agents would put
the filename in the Content-Type "name" parameter while newer ones put it
in the optional Content-Disposition "filename" parameter (actually for
backwards-compatibility most newer ones tend to include both). The
Content-Description is optional. The general header format (all
whitespace is optional) is:
Content-Type: application/{x-} \
pkcs7-mime{; name=|; smime-type= \
enveloped-data|signed-data|certs-only}
pkcs7-signature{; name=}
pkcs10{; name=}
Content-Disposition: attachment{; filename=}
Content-Transfer-Encoding: base64
Content-Description: S/MIME {Cryptographic Signature|???}
The result is that we have to create a somewhat nontrivial parser to
handle all the variations.
In addition Netscape have their own MIME data types for certificates:
Content-Type: application/x-x509-{user-cert|ca-cert|email-cert}
(probably with other bits as well, details unknown) which we also handle,
although the exact cert type is ignored since it's up to the cert handling
routines to sort this out via authenticated attributes rather than using
the unauthenticated MIME content type */
/* Various nonspecific parsing routines */
static void skipWhitespace( STREAM *stream )
{
int ch;
do
ch = sgetc( stream );
while( ch == ' ' || ch == '\t' );
sungetc( stream );
}
static void skipEOL( STREAM *stream )
{
int ch;
/* Skip a LF or CR with optional LF */
ch = sgetc( stream );
if( ch == '\n' )
return;
if( ch == '\r' && \
sgetc( stream ) == '\n' )
return;
sungetc( stream );
}
static BOOLEAN skipToNextToken( STREAM *stream )
{
int ch = sgetc( stream );
/* If this is the end of the line, don't to anything */
if( ch == '\r' || ch == '\n' )
{
sungetc( stream );
return( TRUE );
}
/* Skip to the start of the next token after the current one. This
parses ";{ }{EOL }}" */
if( ch != ';' )
return( FALSE );
skipWhitespace( stream );
ch = sgetc( stream );
if( ch != '\r' && ch != '\n' )
{
sungetc( stream );
return( TRUE );
}
skipEOL( stream ); /* Line is continued, skip EOL and whitespace */
ch = sgetc( stream );
if( ch != ' ' && ch != '\t' )
return( FALSE ); /* Should have whitespace after continuation */
skipWhitespace( stream );
return( TRUE );
}
static BOOLEAN skipCurrentToken( STREAM *stream )
{
BOOLEAN isQuoted = FALSE;
int ch = sgetc( stream );
while( ch != '\n' && ch != '\r' )
{
if( !isQuoted && ch == ';' )
{
/* If we reach an unquoted semicolon, we're at the end of the
token */
sungetc( stream );
return( skipToNextToken( stream ) );
}
if( ch == '"' )
isQuoted = !isQuoted;
ch = sgetc( stream );
}
sungetc( stream );
return( isQuoted ? FALSE : skipToNextToken( stream ) );
}
static BOOLEAN skipLine( STREAM *stream )
{
BOOLEAN continuation;
int ch = sgetc( stream );
/* MIME headers can be continued over multiple lines. If there's a
semicolon at the end of the current line, we continue to the next
one */
do
{
continuation = FALSE;
while( ch != '\r' && ch != '\n' )
{
if( ( ch & 0x7F ) < ' ' )
continuation = ( ch == ';' );
ch = sgetc( stream );
}
if( continuation )
{
sungetc( stream );
if( !skipToNextToken( stream ) )
return( FALSE );
ch = sgetc( stream );
}
}
while( continuation );
sungetc( stream );
/* Check for a single EOL */
skipEOL( stream );
return( TRUE );
}
/* Parse the various MIME header lines */
static BOOLEAN parseContentType( STREAM *stream )
{
char buffer[ 64 ];
int ch;
/* Look for "application/{x-}pkcs{7-mime|7-signature|10}" */
skipWhitespace( stream );
if( cryptStatusError( sread( stream, buffer, 12 ) ) || \
memcmp( buffer, "application/", 12 ) )
return( FALSE );
if( sgetc( stream ) == 'x' )
{
/* Skip old-style "x-" header */
if( sgetc( stream ) != '-' )
return( FALSE );
/* Check for one of the Netscape cert types */
if( sgetc( stream ) == 'x' )
{
/* "x509-"*/
if( cryptStatusError( sread( stream, buffer, 4 ) ) || \
memcmp( buffer, "509-", 4 ) )
return( FALSE );
return( skipLine( stream ) );
}
sungetc( stream );
}
else
sungetc( stream );
if( cryptStatusError( sread( stream, buffer, 4 ) ) || \
memcmp( buffer, "pkcs", 4 ) )
return( FALSE );
ch = sgetc( stream );
if( ch == '7' )
{
if( sgetc( stream ) != '-' )
return( FALSE );
ch = sgetc( stream );
if( ch != 'm' && ch != 's' )
return( FALSE );
if( ch == 'm' )
{
/* "pkcs7-mime" */
if( cryptStatusError( sread( stream, buffer, 4 ) ) || \
memcmp( buffer, "mime", 4 ) )
return( FALSE );
}
else
/* "pkcs7-signature" */
if( cryptStatusError( sread( stream, buffer, 8 ) ) || \
memcmp( buffer, "ignature", 8 ) )
return( FALSE );
}
else
if( ch == '1' )
{
/* "pkcs10" */
if( sgetc( stream ) != '0' )
return( FALSE );
}
else
return( FALSE );
if( !skipToNextToken( stream ) )
return( FALSE );
ch = sgetc( stream );
if( ch == '\n' || ch == '\r' )
{
/* If that's all there is, return */
sungetc( stream );
skipEOL( stream );
return( TRUE );
}
/* Check for an optional old-style name attribute */
if( ch == 'n' )
{
/* name= */
if( cryptStatusError( sread( stream, buffer, 4 ) ) || \
memcmp( buffer, "ame=", 4 ) )
return( FALSE );
skipWhitespace( stream );
if( !skipCurrentToken( stream ) )
return( FALSE );
ch = sgetc( stream );
sungetc( stream );
if( ch == '\n' || ch == '\r' )
{
/* If that's all there is, return */
skipEOL( stream );
return( TRUE );
}
}
/* Check for an SMIME type */
if( cryptStatusError( sread( stream, buffer, 11 ) ) || \
memcmp( buffer, "smime-type=", 11 ) )
return( FALSE );
skipWhitespace( stream );
ch = sgetc( stream );
if( ch == 's' )
{
/* signed-data */
if( cryptStatusError( sread( stream, buffer, 10 ) ) || \
memcmp( buffer, "igned-data", 10 ) )
return( FALSE );
}
else
{
/* certs-only */
if( ch != 'c' )
return( FALSE );
if( cryptStatusError( sread( stream, buffer, 9 ) ) || \
memcmp( buffer, "erts-only", 9 ) )
return( FALSE );
}
return( skipLine( stream ) );
}
static BOOLEAN parseContentDisposition( STREAM *stream )
{
char buffer[ 64 ];
int ch;
/* Look for "attachment" */
skipWhitespace( stream );
ch = sgetc( stream );
if( ch == 'a' )
{
/* attachment */
if( cryptStatusError( sread( stream, buffer, 9 ) ) || \
memcmp( buffer, "ttachment", 9 ) )
return( FALSE );
}
else
{
/* inline */
if( ch != 'i' )
return( FALSE );
if( cryptStatusError( sread( stream, buffer, 5 ) ) || \
memcmp( buffer, "nline", 5 ) )
return( FALSE );
}
return( TRUE );
}
static BOOLEAN parseContentTransferEncoding( STREAM *stream )
{
char buffer[ 64 ];
/* Look for "base64" */
skipWhitespace( stream );
if( cryptStatusError( sread( stream, buffer, 6 ) ) || \
memcmp( buffer, "base64", 6 ) )
return( FALSE );
return( skipLine( stream ) );
}
/* Check an S/MIME header. Returns the length of the header */
int smimeCheckHeader( const char *data, const int dataLength )
{
STREAM stream;
BOOLEAN seenType = FALSE, seenDisposition = FALSE;
BOOLEAN seenDescription = FALSE, seenTransferEncoding = FALSE;
BOOLEAN dataOK = TRUE;
char buffer[ 64 ];
int ch;
sMemConnect( &stream, data, dataLength );
/* Sometimes the object can be preceded by a few blank lines - we're
fairly lenient with this */
do
ch = sgetc( &stream );
while( ch == '\r' || ch == '\n' );
/* Make sure there's a MIME content-type header there */
if( ch != 'C' )
{
sMemDisconnect( &stream );
return( 0 );
}
while( ch != '\r' && ch != '\n' )
{
/* Check for the different types of content header which are
allowed */
if( cryptStatusError( sread( &stream, buffer, 7 ) ) || \
memcmp( buffer, "ontent-", 7 ) )
dataOK = FALSE;
ch = sgetc( &stream );
if( ch != 'D' && ch != 'T' )
dataOK = FALSE;
if( ch == 'D' )
{
ch = sgetc( &stream );
if( ch == 'i' )
{
/* "Disposition:" */
if( cryptStatusError( sread( &stream, buffer, 10 ) ) || \
memcmp( buffer, "sposition:", 10 ) || seenDisposition )
dataOK = FALSE;
seenDisposition = TRUE;
if( dataOK )
dataOK = parseContentDisposition( &stream );
}
else
{
/* "Description:" */
if( ch != 'e' )
dataOK = FALSE;
if( cryptStatusError( sread( &stream, buffer, 10 ) ) || \
memcmp( buffer, "scription:", 10 ) || seenDescription )
dataOK = FALSE;
seenDescription = TRUE;
if( dataOK )
dataOK = skipLine( &stream );
}
}
else
{
ch = sgetc( &stream );
if( ch == 'y' )
{
/* "Type:" */
if( cryptStatusError( sread( &stream, buffer, 4 ) ) || \
memcmp( buffer, "ype:", 4 ) || seenType )
dataOK = FALSE;
seenType = TRUE;
if( dataOK )
dataOK = parseContentType( &stream );
}
else
{
/* "Transfer-Encoding:" */
if( ch != 'r' )
dataOK = FALSE;
if( cryptStatusError( sread( &stream, buffer, 16 ) ) || \
memcmp( buffer, "ansfer-Encoding:", 16 ) || \
seenTransferEncoding )
dataOK = FALSE;
seenTransferEncoding = TRUE;
if( dataOK )
dataOK = parseContentTransferEncoding( &stream );
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -