📄 sign.c
字号:
return( status );
}
/****************************************************************************
* *
* X.509-style Signature Functions *
* *
****************************************************************************/
/* Create/check an X.509-style signature. These work with objects of the
form:
signedObject ::= SEQUENCE {
object ANY,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING
}
This is complicated by a variety of b0rken PKI protocols that couldn't
quite manage a cut & paste of two lines of text, adding all sorts of
unnecessary extra tagging and wrappers to the signature. To handle the
tagging and presence of extra data, we allow two extra parameters, a
tag/wrapper formatting info specifier and an extra data length value (with
the data being appended by the caller). If the tag/wrapper is a small
integer value, it's treated as [n] { ... }; if it has the 7th bit set
(0x80), it's treated as [n] { SEQUENCE { ... }} */
int createX509signature( void *signedObject, int *signedObjectLength,
const int sigMaxLength,
const void *object, const int objectLength,
const CRYPT_CONTEXT signContext,
const CRYPT_ALGO_TYPE hashAlgo,
const int formatInfo, const int extraDataLength )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
STREAM stream;
#if INT_MAX > 32767
int startOffset = ( objectLength > 64500 ) ? 5 : 4;
#else
int startOffset = 4;
#endif /* 32-bit ints */
BYTE *payloadStart = ( BYTE * ) signedObject + startOffset;
int sigWrapperSize = ( formatInfo == CRYPT_UNUSED ) ? 0 : 16;
int signatureLength, totalSigLength, delta, status;
/* Hash the data to be signed */
setMessageCreateObjectInfo( &createInfo, hashAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
( void * ) object, objectLength );
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
( void * ) object, 0 );
/* Create the wrapped-up signed object. This gets somewhat ugly because
the only way we can find out how long the signature will be is by
actually creating it, since the ASN.1 encoding constraints mean that
the size can vary by a few bytes depending on what values the integers
that make up the signature take. Because of this, we first generate
the signature a reasonable distance back from the start of the buffer,
write the header and data to sign at the start, and finally move the
signature down to the end of the header if required. startOffset is
the initial estimate of the length of the encapsulating SEQUENCE and
covers a payload length of 256-64K bytes, delta is the difference
between the estimate and the actual size that we later need to
correct for. Since the combination of data to sign and signature are
virtually always in the range 256-64K bytes, the data move is almost
never performed:
startOfs objLength sigLength
v v v
+---+-------------------+-------+-------+
| | object |wrapper| sig |
+---+-------------------+-------+-------+
| ^
payloadStart sigWrapperSize */
status = createSignature( payloadStart + objectLength + sigWrapperSize,
&signatureLength,
sigMaxLength - ( startOffset + objectLength + \
sigWrapperSize ),
signContext, createInfo.cryptHandle,
SIGNATURE_X509 );
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
if( cryptStatusError( status ) )
return( status );
if( formatInfo == CRYPT_UNUSED )
totalSigLength = signatureLength + extraDataLength;
else
if( !( formatInfo & 0x80 ) )
totalSigLength = ( int ) \
sizeofObject( signatureLength + extraDataLength );
else
totalSigLength = ( int ) \
sizeofObject( sizeofObject( signatureLength + extraDataLength ) );
sMemOpen( &stream, signedObject, startOffset );
writeSequence( &stream, objectLength + totalSigLength );
delta = startOffset - stell( &stream );
sMemDisconnect( &stream );
if( delta > 0 )
{
payloadStart -= delta;
startOffset -= delta;
}
memcpy( payloadStart, object, objectLength );
if( sigWrapperSize > 0 )
{
const int oldSigWrapperSize = sigWrapperSize;
sMemOpen( &stream, payloadStart + objectLength, sigWrapperSize );
if( !( formatInfo & 0x80 ) )
writeConstructed( &stream, signatureLength + extraDataLength,
formatInfo );
else
{
writeConstructed( &stream,
sizeofObject( signatureLength + extraDataLength ),
formatInfo & 0x7F );
writeSequence( &stream, signatureLength + extraDataLength );
}
sigWrapperSize = stell( &stream );
sMemDisconnect( &stream );
memmove( payloadStart + objectLength + sigWrapperSize,
payloadStart + objectLength + oldSigWrapperSize,
signatureLength );
}
if( delta > 0 )
memmove( payloadStart + objectLength,
payloadStart + delta + objectLength,
sigWrapperSize + signatureLength );
*signedObjectLength = startOffset + objectLength + sigWrapperSize + \
signatureLength;
return( status );
}
int checkX509signature( const void *signedObject, const int signedObjectLength,
void **object, int *objectLength,
const CRYPT_CONTEXT sigCheckContext,
const int formatInfo )
{
CRYPT_ALGO_TYPE signAlgo, sigCheckAlgo, hashAlgo;
MESSAGE_CREATEOBJECT_INFO createInfo;
STREAM stream;
void *objectPtr, *sigPtr;
int status, length, signatureLength;
/* Check the start of the object and record the start and size of the
encapsulated signed object, with special handling for unsually long
data in mega-CRLs. The use of the length from readSequence() is safe
here because the data has to be DER if it's signed */
sMemConnect( &stream, signedObject, signedObjectLength );
#if INT_MAX > 32767
readLongSequence( &stream, NULL );
#else
readSequence( &stream, NULL );
#endif /* Non-16-bit systems */
objectPtr = sMemBufPtr( &stream );
#if INT_MAX > 32767
if( signedObjectLength >= 32767 )
{
long longLength;
status = readLongSequence( &stream, &longLength );
if( cryptStatusOK( status ) )
{
/* If it's an (invalid) indefinite-length encoding we can't do
anything with it */
if( longLength == CRYPT_UNUSED )
status = CRYPT_ERROR_BADDATA;
else
length = ( int ) longLength;
}
}
else
#endif /* Non-16-bit systems */
status = readSequence( &stream, &length );
if( cryptStatusOK( status ) )
{
status = sSkip( &stream, length ); /* Move past the object */
length = ( int ) sizeofObject( length );/* Include header size */
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( objectLength != NULL )
*objectLength = length;
if( object != NULL )
*object = objectPtr;
/* Make sure that the signing parameters are in order and create a hash
context from the algorithm identifier of the signature */
status = krnlSendMessage( sigCheckContext, IMESSAGE_GETATTRIBUTE,
&sigCheckAlgo, CRYPT_CTXINFO_ALGO );
if( cryptStatusOK( status ) )
{
/* If it's a broken signature, process the extra encapsulation */
if( formatInfo != CRYPT_UNUSED )
{
if( !( formatInfo & 0x80 ) )
readConstructed( &stream, NULL, formatInfo );
else
{
readConstructed( &stream, NULL, formatInfo & 0x7F );
readSequence( &stream, NULL );
}
}
sigPtr = sMemBufPtr( &stream );
status = readAlgoIDex( &stream, &signAlgo, &hashAlgo, NULL );
}
signatureLength = sMemDataLeft( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
if( sigCheckAlgo != signAlgo )
/* The signature algorithm isn't what we expected, the best we can do
is report a signature error */
return( CRYPT_ERROR_SIGNATURE );
setMessageCreateObjectInfo( &createInfo, hashAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT, &createInfo,
OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
/* Hash the signed data and check the signature on the object */
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
objectPtr, length );
krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
objectPtr, 0 );
status = checkSignature( sigPtr, signatureLength, sigCheckContext,
createInfo.cryptHandle, SIGNATURE_X509 );
/* Clean up */
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
return( status );
}
/****************************************************************************
* *
* PKI Protocol Signature Functions *
* *
****************************************************************************/
/* The various cert management protocols are built using the twin design
guidelines that nothing should use a standard style of signature and no
two protocols should use the same nonstandard format, the only way to
handle these (without creating dozens of new signature types, each with
their own special-case handling) is to process most of the signature
information at the protocol level and just check the raw signature here */
int createRawSignature( void *signature, int *signatureLength,
const int sigMaxLength,
const CRYPT_CONTEXT iSignContext,
const CRYPT_CONTEXT iHashContext )
{
return( createSignature( signature, signatureLength, sigMaxLength,
iSignContext, iHashContext, SIGNATURE_RAW ) );
}
int checkRawSignature( const void *signature, const int signatureLength,
const CRYPT_CONTEXT iSigCheckContext,
const CRYPT_CONTEXT iHashContext )
{
return( checkSignature( signature, signatureLength, iSigCheckContext,
iHashContext, SIGNATURE_RAW ) );
}
/****************************************************************************
* *
* Create/Check a CMS Signature *
* *
****************************************************************************/
/* CMS version */
#define CMS_VERSION 1
/* The maximum size for the encoded CMS signed attributes */
#define ENCODED_ATTRIBUTE_SIZE 512
/* Write CMS signer information:
SignerInfo ::= SEQUENCE {
version INTEGER (1),
issuerAndSerialNumber IssuerAndSerialNumber,
digestAlgorithm AlgorithmIdentifier,
signedAttrs [ 0 ] IMPLICIT SET OF Attribute OPTIONAL,
signatureAlgorithm AlgorithmIdentifier,
signature OCTET STRING,
unsignedAttrs [ 1 ] IMPLICIT SET OF Attribute OPTIONAL
} */
static int writeCmsSignerInfo( STREAM *stream,
const CRYPT_CERTIFICATE certificate,
const CRYPT_ALGO_TYPE hashAlgorithm,
const void *attributes, const int attributeSize,
const void *signature, const int signatureSize,
const CRYPT_HANDLE unsignedAttrObject )
{
RESOURCE_DATA msgData;
DYNBUF iAndSDB;
int timeStampSize, unsignedAttributeSize = 0;
int status;
/* Get the signerInfo information */
status = dynCreate( &iAndSDB, certificate,
CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
if( cryptStatusError( status ) )
return( status );
if( unsignedAttrObject != CRYPT_UNUSED )
{
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( unsignedAttrObject, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_ENC_TIMESTAMP );
timeStampSize = msgData.length;
if( cryptStatusOK( status ) )
unsignedAttributeSize = ( int ) \
sizeofObject( sizeofOID( OID_TSP_TSTOKEN ) + \
sizeofObject( timeStampSize ) );
}
if( cryptStatusError( status ) )
{
dynDestroy( &iAndSDB );
return( status );
}
/* Write the outer SEQUENCE wrapper and version number */
writeSequence( stream, sizeofShortInteger( CMS_VERSION ) + \
dynLength( iAndSDB ) + \
sizeofAlgoID( hashAlgorithm ) + \
attributeSize + signatureSize + \
( ( unsignedAttributeSize ) ? \
( int ) sizeofObject( unsignedAttributeSize ) : 0 ) );
writeShortInteger( stream, CMS_VERSION, DEFAULT_TAG );
/* Write the issuerAndSerialNumber, digest algorithm identifier,
attributes (if there are any) and signature */
swrite( stream, dynData( iAndSDB ), dynLength( iAndSDB ) );
writeAlgoID( stream, hashAlgorithm );
if( attributeSize )
swrite( stream, attributes, attributeSize );
status = swrite( stream, signature, signatureSize );
dynDestroy( &iAndSDB );
if( cryptStatusError( status ) || unsignedAttributeSize <= 0 )
return( status );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -