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

📄 dbxdbx.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 4 页
字号:
/****************************************************************************
*																			*
*							cryptlib DBMS Interface							*
*						Copyright Peter Gutmann 1996-2003					*
*																			*
****************************************************************************/

#include <stdarg.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "keyset.h"
  #include "dbxdbx.h"
  #include "asn1_rw.h"
  #include "rpc.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "../keyset/keyset.h"
  #include "../keyset/dbxdbx.h"
  #include "../misc/asn1_rw.h"
  #include "../misc/rpc.h"
#else
  #include "crypt.h"
  #include "keyset/keyset.h"
  #include "keyset/dbxdbx.h"
  #include "misc/asn1_rw.h"
  #include "misc/rpc.h"
#endif /* Compiler-specific includes */

#ifdef USE_DBMS

/* The table structure for the various DBMS tables is (* = unique, + = cert
   store only):

	CertReq: type, C, SP, L, O, OU, CN, email, certID, certData
	Cert: C, SP, L, O, OU, CN, email, validTo, nameID, issuerID*, keyID*, certID*, certData
	CRL: expiryDate+, nameID+, issuerID*, certID+, certData
	PKIUsers: C, SP, L, O, OU, CN, nameID*, keyID*, certID, certData
	CertLog: action, date, certID*, reqCertID, subjCertID, certData

   Note that in the CRL table the certID is the ID of the cert being
   revoked, not of the per-entry CRL data, and in the  PKIUsers table the
   keyID isn't for a public key but a nonce used to identify the PKI user
   and the nameID is used purely to ensure uniqueness of users.

   The cert store contains a table for logging cert management operations (e.g.
   when issued, when revoked, etc etc).  The operations are tied together by
   the certID of each object, associated with this in the log are optional
   certIDs of the request that caused the action to be taken and the subject
   that was affected by the request.  This allows a complete history of each
   item to be built via the log.  The certLog has a UNIQUE INDEX on the
   certID that detects attempts to add duplicates, although this
   unfortunately requires the addition of dummy nonce certIDs to handle
   certain types of actions that don't produce objects with certIDs.

   The handling for each type of CA management operation is:

	CERTACTION_REQUEST_CERT/CERTACTION_REQUEST_RENEWAL/
	CERTACTION_REQUEST_REVOCATION: Stores the incoming requests and generates
	a log entry.  Duplicate issue requests are detected by the certLog.certID
	uniqueness constraint.  Available: request with certID:

	  INSERT INTO certRequests VALUES ( <type>, <DN components>, <certID>, <request> );
	  INSERT INTO certLog VALUES
		(ACTION_REQUEST_CERT/RENEWAL/REVOCATION, $date, <certID>, NULL, NULL,
		  <request>);

	CERTACTION_ISSUE_CERT/CERTACTION_CERT_CREATION: Add the cert and remove
	the issue request.  Duplicate cert issuance is detected by the
	certLog.certID uniqueness constraint.  Available: request with
	req.certID, certificate with certID

	  INSERT INTO certificates VALUES (<DN components>, <IDs>, <cert>);
	  INSERT INTO certLog VALUES
		(ACTION_ISSUE_CERT/CERT_CREATION, $date, <certID>, <req.certID>, NULL,
		  <cert>);
	  DELETE FROM certRequests WHERE certID = <req.certID>;

	CERTACTION_ISSUE_CRL: Read each CRL entry with caCert.nameID and assemble
	the full CRL.  Requires an ongoing query:

	  SELECT FROM CRLs WHERE nameID = <caCert.nameID>

	CERTACTION_REVOKE_CERT: Add the CRL entry that causes the revocation,
	delete the cert and the request that caused the action.  Available:
	request with req.certID, certificate with cert.certID, CRL entry with
	certID

	  INSERT INTO CRLs VALUES (<IDs>, <crlData>);
	  INSERT INTO certLog VALUES
		(ACTION_REVOKE_CERT, $date, <nonce>, <req.certID>, <cert.certID>, <crlData>);
	  DELETE FROM certRequests WHERE certID = <req.certID>;
	  DELETE FROM certificates WHERE certID = <cert.certID>;

	CERTACTION_EXPIRE_CERT/CERTACTION_RESTART_CLEANUP: Delete each expired
	entry or clean up leftover cert requests after a restart.  The logging
	for these is a bit tricky, ideally we'd want to "INSERT INTO certLog
	VALUES (ACTION_CERT_EXPIRE, $date, SELECT certID FROM certificates WHERE
	validTo <= $date)" or the cleanup equivalent, however this isn't
	possible both because it's not possible to mix static values and a
	select result in an INSERT and because the certID is already present
	from when the cert/request was originally added.  We can fix the former
	by making the static values part of the select result, i.e."INSERT INTO
	certLog VALUES SELECT ACTION_CERT_EXPIRE, $date, certID FROM
	certificates WHERE validTo <= $date" but this still doesn't fix the
	problem with the duplicate IDs.  In fact there isn't really a certID
	present since it's an implicit action, but we can't make the certID
	column null since it's not possible to index nullable columns.  As a
	result the only way we can do it is to repetitively perform 'SELECT
	certID FROM certificates WHERE validTo <= $date' (or the equivalent
	cleanup select) and for each time it succeeds follow it with:

	  INSERT INTO certLog VALUES
		(ACTION_EXPIRE_CERT, $date, <nonce>, NULL, <certID>);
	  DELETE FROM certificates WHERE certID = <certID>

	or

	  INSERT INTO certLog VALUES
		(ACTION_RESTART_CLEANUP, $date, <nonce>, NULL, <certID>);
	  DELETE FROM certRequests WHERE certID = <certID>

	This has the unfortunate side-effect that the update isn't atomic, we
	could enforce this with "LOCK TABLE <name> IN EXCLUSIVE MODE", however
	the MS databases don't support this and either require the use of
	baroque mechanisms such as a "(TABLOCKX HOLDLOCK)" as a locking hint
	after the table name in the first statement after the transaction is
	begun or don't support this type of locking at all.  Because of this it
	isn't really possible to make the update atomic, in particular for the
	cleanup operation we rely on the caller to perform it at startup before
	anyone else accesses the cert store.  The fact that the update isn't
	quite atomic isn't really a major problem, at worst it'll result in
	either an expired cert being visible or a leftover request blocking a
	new request for a split second longer than they should */

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* Set up key ID information for a query.  There are two variations of
   this, makeKeyID() encodes an existing keyID value and getKeyID() reads an
   attribute from an object and encodes it */

static void makeKeyID( char *keyIDbuffer, const CRYPT_KEYID_TYPE keyIDtype,
					   const void *keyID, const int keyIDlength )
	{
	BYTE hashBuffer[ CRYPT_MAX_HASHSIZE ];
	int idLength = keyIDlength;

	assert( ( keyIDtype == CRYPT_KEYID_NAME || \
			  keyIDtype == CRYPT_KEYID_EMAIL ) || \
			( keyIDtype == CRYPT_IKEYID_KEYID || \
			  keyIDtype == CRYPT_IKEYID_ISSUERID || \
			  keyIDtype == CRYPT_IKEYID_CERTID ) );

	/* Name and email address are used as is */
	if( keyIDtype == CRYPT_KEYID_NAME || \
		keyIDtype == CRYPT_KEYID_EMAIL )
		{
		idLength = min( idLength, ( CRYPT_MAX_TEXTSIZE * 2 ) - 1 );
		memcpy( keyIDbuffer, keyID, idLength );
		keyIDbuffer[ idLength ] = '\0';
		return;
		}

	/* A keyID is just a subjectKeyIdentifier, which is supposed to be an
	   SHA-1 hash anyway but which in practice can be almost anything so we
	   always hash it to a fixed-length value */
	if( keyIDtype == CRYPT_IKEYID_KEYID )
		{
		HASHFUNCTION hashFunction;

		/* Get the hash algorithm information and hash the keyID to get
		   the fixed-length keyID */
		getHashParameters( CRYPT_ALGO_SHA, &hashFunction, NULL );
		hashFunction( NULL, hashBuffer, ( void * ) keyID, keyIDlength,
					  HASH_ALL );
		keyID = hashBuffer;
		idLength = DBXKEYID_SIZE;
		}

	assert( idLength >= DBXKEYID_SIZE );

	/* base64-encode the key ID so that we can use it with database queries.
	   Since we only store 128 bits of a (usually 160 bit) ID to save space
	   (particularly where it's used in indices) and speed lookups, this
	   encoding step has the side-effect of truncating the ID down to the
	   correct size */
	base64encode( keyIDbuffer, keyID, DBXKEYID_SIZE, CRYPT_CERTTYPE_NONE );
	keyIDbuffer[ MAX_ENCODED_DBXKEYID_SIZE ] = '\0';
	}

int getKeyID( char *keyIDbuffer, const CRYPT_HANDLE cryptHandle,
			  const CRYPT_ATTRIBUTE_TYPE keyIDtype )
	{
	BYTE hashBuffer[ CRYPT_MAX_HASHSIZE ];
	int status;

	assert( ( keyIDtype == CRYPT_CERTINFO_FINGERPRINT_SHA || \
			  keyIDtype == CRYPT_IATTRIBUTE_AUTHCERTID ) || \
			( keyIDtype == CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER || \
			  keyIDtype == CRYPT_IATTRIBUTE_ISSUER || \
			  keyIDtype == CRYPT_IATTRIBUTE_SUBJECT || \
			  keyIDtype == CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER || \
			  keyIDtype == CRYPT_IATTRIBUTE_SPKI ) );

	/* Get the attribute from the cert and hash it, unless it's already a
	   hash */
	if( keyIDtype == CRYPT_CERTINFO_FINGERPRINT_SHA || \
		keyIDtype == CRYPT_IATTRIBUTE_AUTHCERTID )
		{
		RESOURCE_DATA msgData;

		setMessageData( &msgData, hashBuffer, CRYPT_MAX_HASHSIZE );
		status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, keyIDtype );
		if( cryptStatusError( status ) )
			return( status );
		assert( msgData.length == KEYID_SIZE );
		}
	else
		{
		DYNBUF idDB;
		HASHFUNCTION hashFunction;
		int hashSize;

		/* Get the attribute data and hash it to get the ID */
		status = dynCreate( &idDB, cryptHandle, keyIDtype );
		if( cryptStatusError( status ) )
			return( status );
		getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
		hashFunction( NULL, hashBuffer, dynData( idDB ), dynLength( idDB ), 
					  HASH_ALL );
		assert( hashSize == KEYID_SIZE );
		dynDestroy( &idDB );
		}

	makeKeyID( keyIDbuffer, CRYPT_IKEYID_CERTID, hashBuffer, KEYID_SIZE );
	return( CRYPT_OK );
	}

/* Get a keyID for a certificate */

int getCertKeyID( char *keyID, const CRYPT_CERTIFICATE iCryptCert )
	{
	int status;

	/* Certificate keyID handling isn't quite as simple as just reading an
	   attribute from the certificate since the subjectKeyIdentifier (if
	   present) may not be the same as the keyID if the cert has come from
	   a CA that does strange things with the sKID.  To resolve this we try
	   and build the key ID from the sKID, if this isn't present we use the
	   keyID (the sKID may have a nonstandard length since it's possible to
	   stuff anything in there, getKeyID() will hash it to the standard size
	   if the length is wrong) */
	status = getKeyID( keyID, iCryptCert,
					   CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER );
	if( cryptStatusOK( status ) )
		return( CRYPT_OK );

	/* There's no subjectKeyIdentifier, use the keyID.  Note that we can't
	   just read the CRYPT_IATTRIBUTE_KEYID attribute directly since this
	   may be a data-only cert (either a standalone cert or one from the
	   middle of a chain), so we have to generate it indirectly by hashing
	   the SubjectPublicKeyInfo, which is equivalent to the keyID and is
	   always present in a cert */
	return( getKeyID( keyID, iCryptCert, CRYPT_IATTRIBUTE_SPKI ) );
	}

/* Get names for various items */

char *getKeyName( const CRYPT_KEYID_TYPE keyIDtype )
	{
	switch( keyIDtype )
		{
		case CRYPT_KEYID_NAME:
			return( "CN" );

		case CRYPT_KEYID_EMAIL:
			return( "email" );

		case CRYPT_IKEYID_KEYID:
			return( "keyID" );

		case CRYPT_IKEYID_ISSUERID:
			return( "issuerID" );

		case CRYPT_IKEYID_CERTID:
			return( "certID" );
		}

	assert( NOTREACHED );
	return( "XXXX" );		/* Get rid of compiler warning */
	}

static char *getTableName( const KEYMGMT_ITEM_TYPE itemType )
	{
	switch( itemType )
		{
		case KEYMGMT_ITEM_REQUEST:
			return( "certRequests" );

		case KEYMGMT_ITEM_PKIUSER:
			return( "pkiUsers" );

		case KEYMGMT_ITEM_PUBLICKEY:
			return( "certificates" );

		case KEYMGMT_ITEM_REVOCATIONINFO:
			return( "CRLs" );
		}

	assert( NOTREACHED );
	return( "XXXX" );		/* Get rid of compiler warning */
	}

/* Check an encoded cert for a matching key usage.  The semantics of key
   usage flags are vague in the sense that the query "Is this key valid for
   X" is easily resolved, but the query "Which key is appropriate for X" is
   NP-hard due to the potential existence of unbounded numbers of
   certificates with usage semantics expressed in an arbitrary number of
   ways.  For now we distinguish between signing and encryption keys (this,
   at least, is feasible) by doing a quick check for keyUsage if we get
   multiple certs with the same DN and choosing the one with the appropriate
   key usage.

   Rather than performing a relatively expensive cert import for each cert,
   we find the keyUsage by doing an optimised search through the cert data
   for its encoded form.  The pattern that we look for is:

	OID				06 03 55 1D 0F
	BOOLEAN			(optional)
	OCTET STRING {	04 (4 or 5)
		BIT STRING	03 (2 or 3) nn (value) */

static BOOLEAN checkCertUsage( const BYTE *certificate, const int length,
							   const int requestedUsage )
	{
	int i;

	assert( requestedUsage & KEYMGMT_MASK_USAGEOPTIONS );

	/* Scan the payload portion of the cert for the keyUsage extension */
	for( i = 256; i < length - 64; i++ )
		{
		int keyUsage;

		/* Look for the OID.  This potentially skips two bytes at a
		   time, but this is safe since the preceding bytes can never
		   contain either of these two values (they're 0x30 + 11...15) */
		if( certificate[ i++ ] != BER_OBJECT_IDENTIFIER || \
			certificate[ i++ ] != 3 )
			continue;
		if( memcmp( certificate + i, "\x55\x1D\x0F", 3 ) )
			continue;
		i += 3;

		/* We've found the OID (with 1.1e-12 error probability), skip
		   the critical flag if necessary */
		if( certificate[ i ] == BER_BOOLEAN )
			i += 3;

		/* Check for the OCTET STRING wrapper and BIT STRING */
		if( certificate[ i++ ] != BER_OCTETSTRING || \
			( certificate[ i ] != 4 && certificate[ i ] != 5 ) || \
			certificate[ ++i ] != BER_BITSTRING )
			continue;
		keyUsage = certificate[ i + 3 ];

		/* We've got to the BIT STRING payload, check whether the requested
		   usage is allowed.  This is somewhat ugly since it hardcodes in
		   the bit values, but it's difficult to handle otherwise without
		   resorting to interpresting the encoded ASN.1 */
		if( requestedUsage & KEYMGMT_FLAG_USAGE_CRYPT )
			{

⌨️ 快捷键说明

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