/****************************************************************************
*																			*
*						cryptlib PKCS #12 Read Routines						*
*						Copyright Peter Gutmann 1997-2020					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "asn1.h"
  #include "asn1_ext.h"
  #include "keyset.h"
  #include "pkcs12.h"
#else
  #include "crypt.h"
  #include "enc_dec/asn1.h"
  #include "enc_dec/asn1_ext.h"
  #include "keyset/keyset.h"
  #include "keyset/pkcs12.h"
#endif /* Compiler-specific includes */

#ifdef USE_PKCS12

/* OID information used to read a PKCS #12 keyset */

static const CMS_CONTENT_INFO oidInfoEncryptedData = { 0, 2 };

static const OID_INFO keyDataOIDinfo[] = {
	{ OID_CMS_ENCRYPTEDDATA, TRUE, &oidInfoEncryptedData },
	{ OID_CMS_DATA, FALSE },
	{ NULL, 0 }, { NULL, 0 }
	};

/* OID information used to read decrypted PKCS #12 objects */

static const OID_INFO certBagOIDinfo[] = {
	{ OID_PKCS12_CERTBAG, 0 },
	{ NULL, 0 }, { NULL, 0 }
	};
static const OID_INFO certOIDinfo[] = {
	{ OID_PKCS9_X509CERTIFICATE, 0 },
	{ NULL, 0 }, { NULL, 0 }
	};

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Copy PKCS #12 object information.  If there's already content present in 
   the destination information then we copy in additional information, for
   example to augment existing private-key data with an associated 
   certificate or vice-versa */

STDC_NONNULL_ARG( ( 1, 2 ) ) \
static void copyObjectInfo( INOUT_PTR PKCS12_INFO *destPkcs12Info, 
							IN_PTR const PKCS12_INFO *srcPkcs12Info,
							IN_BOOL const BOOLEAN isCertificate )
	{
	assert( isWritePtr( destPkcs12Info, sizeof( PKCS12_INFO ) ) );
	assert( isReadPtr( srcPkcs12Info, sizeof( PKCS12_INFO ) ) );

	REQUIRES_V( isBooleanValue( isCertificate ) );

	destPkcs12Info->flags |= srcPkcs12Info->flags;
	if( isCertificate )
		{
		memcpy( &destPkcs12Info->certInfo, &srcPkcs12Info->certInfo, 
				sizeof( PKCS12_OBJECT_INFO ) );
		}
	else
		{
		memcpy( &destPkcs12Info->keyInfo, &srcPkcs12Info->keyInfo, 
				sizeof( PKCS12_OBJECT_INFO ) );
		}
	if( destPkcs12Info->labelLength <= 0 && \
		srcPkcs12Info->labelLength > 0 )
		{
		REQUIRES_V( rangeCheck( srcPkcs12Info->labelLength, 1, 
								CRYPT_MAX_TEXTSIZE ) );
		memcpy( destPkcs12Info->label, srcPkcs12Info->label,
				srcPkcs12Info->labelLength );
		destPkcs12Info->labelLength = srcPkcs12Info->labelLength;
		}
	if( destPkcs12Info->idLength <= 0 && \
		srcPkcs12Info->idLength > 0 )
		{
		REQUIRES_V( rangeCheck( srcPkcs12Info->idLength, 1, 
								CRYPT_MAX_HASHSIZE ) );
		memcpy( destPkcs12Info->id, srcPkcs12Info->id,
				srcPkcs12Info->idLength );
		destPkcs12Info->idLength = srcPkcs12Info->idLength;
		}
	}

/* PKCS #12's lack of useful indexing information makes it extremely 
   difficult to reliably track and match up object types like private keys
   and certificates.  The PKCS #15 code simply does a findEntry() to find 
   the entry that matches a newly-read object and attaches it to whatever's 
   already present for an existing entry if required (so for example it'd 
   attach a certificate to a previously-read private key), however with PKCS 
   #12 there's no reliable way to do this since entries may or may not have 
   ID information attached and even if something like a friendlyName is 
   present the friendlyName for the private key may not match the 
   friendlyName for the matching certificate (!!!), which makes it 
   impossible to reliably attach keys to certificates.

   To deal with this as best we can, we apply the following strategy:

	1. For the first item read, we save it as is in the zero-th position
	   (the item may or may not have an ID attached, typically if it's an
	   encrypted certificate then it won't, if it's a private key then it
	   will).

	2. For the next item read, if there's no ID information present then 
	   we assume that it's attached to the previously-read first entry 
	   provided that it's compatible with it, for example an ID-less 
	   encrypted certificate attached to an encrypted key.  The "no ID
	   information present" can mean either that the currently-read item has 
	   no ID information or that the first item has no ID information.

	3. Alternatively, if there's ID information attached, we handle it as 
	   we would for a PKCS #15 item.
	
	4. For subsequent items read, if they're certificates then just record
	   them.  Because of the lack of useful ID information there's not much 
	   else that we can do with them */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int findObjectEntryLocation( OUT_PTR_PTR_COND \
										PKCS12_INFO **pkcs12infoPtrPtr, 
									IN_ARRAY( maxNoPkcs12objects ) \
										const PKCS12_INFO *pkcs12info, 
									IN_LENGTH_SHORT const int maxNoPkcs12objects, 
									IN_BUFFER_OPT( idLength ) const void *id, 
									IN_LENGTH_KEYID_Z const int idLength,
									IN_FLAGS( PKCS12 ) const int flags )

	{
	const PKCS12_INFO *pkcs12firstItem = &pkcs12info[ 0 ];
	const PKCS12_INFO *pkcs12infoPtr;
	int index;

	assert( isReadPtr( pkcs12infoPtrPtr, sizeof( PKCS12_INFO * ) ) );
	assert( isReadPtrDynamic( pkcs12info, sizeof( PKCS12_INFO ) * \
										  maxNoPkcs12objects ) );
	assert( ( id == NULL && idLength == 0 ) || \
			isReadPtrDynamic( id, idLength ) );

	REQUIRES( isShortIntegerRangeNZ( maxNoPkcs12objects ) );
	REQUIRES( ( id == NULL && idLength == 0 ) || \
			  ( id != NULL && \
				idLength > 0 && idLength < MAX_ATTRIBUTE_SIZE ) );

	/* Clear return value */
	*pkcs12infoPtrPtr = NULL;

	/* If this is the first entry being added, just add it as is */
	if( pkcs12FindFreeEntry( pkcs12info, maxNoPkcs12objects, \
							 &index ) != NULL && index == 0 )
		{
		*pkcs12infoPtrPtr = ( PKCS12_INFO * ) pkcs12firstItem;

		return( CRYPT_OK );
		}

	/* If we've already got a private key and its associated certificate 
	   present and this is a standalone certificate, add it to the next 
	   slot.  Because of PKCS #12's complete absence of identifier 
	   information other than the friendlyName which doesn't identify the 
	   certificate, there's no way to tie this into the existing key/
	   certificate pair, but we can at least read it and continue */
	if( ( pkcs12firstItem->flags & PKCS12_FLAG_PRIVKEY ) && \
		( pkcs12firstItem->flags & ( PKCS12_FLAG_CERT | \
									 PKCS12_FLAG_ENCCERT ) ) && \
		( flags == PKCS12_FLAG_CERT ) )
		{
		pkcs12infoPtr = pkcs12FindFreeEntry( pkcs12info, maxNoPkcs12objects, 
											 NULL );
		if( pkcs12infoPtr == NULL )
			return( CRYPT_ERROR_OVERFLOW );
		*pkcs12infoPtrPtr = ( PKCS12_INFO * ) pkcs12infoPtr;

		return( CRYPT_OK );
		}

	/* If there's no ID information present, try and find an appropriate 
	   existing item to attach it to.  This can occur in two variations,
	   either the existing entry is EncryptedData containing a certificate 
	   with no ID information and the newly-read item is a private key, or 
	   the existing item is a private key with ID information and the newly-
	   read item is EncryptedData containing a certificate with no ID 
	   information */
	if( ( pkcs12firstItem->labelLength == 0 && \
		  pkcs12firstItem->idLength == 0 ) || id == NULL )
		{
		LOOP_INDEX i;

		/* If the combination of items isn't an encrypted certificate (which 
		   has no ID data associated with it since it's EncryptedData) rather 
		   than a non-encrypted certificate or a private key (which is Data 
		   and should have ID data associated with it) then we can't process 
		   it */
		if( !( ( pkcs12firstItem->flags == PKCS12_FLAG_ENCCERT && \
				 flags == PKCS12_FLAG_PRIVKEY ) || \
			   ( pkcs12firstItem->flags == PKCS12_FLAG_PRIVKEY && \
				 flags == PKCS12_FLAG_ENCCERT ) ) )
			return( CRYPT_ERROR_BADDATA );

		pkcs12infoPtr = NULL;
		LOOP_MED( i = 0, i < maxNoPkcs12objects, i++ )
			{
			ENSURES( LOOP_INVARIANT_MED( i, 0, maxNoPkcs12objects - 1 ) );

			/* If this entry isn't in use, continue */
			if( pkcs12info[ i ].flags == PKCS12_FLAG_NONE )
				continue;

			/* If we've already found a matching entry then finding a second 
			   one is ambiguous since we don't know know which one to 
			   associate the newly-read no-ID entry with */
			if( pkcs12infoPtr != NULL )
				return( CRYPT_ERROR_DUPLICATE );

			/* We've found a potential location to add the ID-less entry */
			pkcs12infoPtr = &pkcs12info[ i ];
			}
		ENSURES( LOOP_BOUND_OK );
		ENSURES( pkcs12infoPtr != NULL )
				 /* There's always a zero-th entry present to act as the 
					known elephant in Cairo */

		*pkcs12infoPtrPtr = ( PKCS12_INFO * ) pkcs12infoPtr;

		return( CRYPT_OK );
		}

	/* There's an ID present, try and find a matching existing entry for 
	   it */
	pkcs12infoPtr = pkcs12FindEntry( pkcs12info, maxNoPkcs12objects, 
									 CRYPT_IKEYID_KEYID, id, idLength,
									 FALSE );
	if( pkcs12infoPtr != NULL )
		{
		*pkcs12infoPtrPtr = ( PKCS12_INFO * ) pkcs12infoPtr;

		return( CRYPT_OK );
		}

	/* This personality isn't present yet, find out where we can add the 
	   object data */
	pkcs12infoPtr = pkcs12FindFreeEntry( pkcs12info, maxNoPkcs12objects, 
										 NULL );
	if( pkcs12infoPtr == NULL )
		return( CRYPT_ERROR_OVERFLOW );
	*pkcs12infoPtrPtr = ( PKCS12_INFO * ) pkcs12infoPtr;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Import Keys/Certificates						*
*																			*
****************************************************************************/

/* Unwrap and import an encrypted certificate */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 5, 7, 8 ) ) \
static int importCertificate( const PKCS12_OBJECT_INFO *certObjectInfo,
							  IN_HANDLE const CRYPT_USER cryptOwner,
							  IN_BUFFER( passwordLen ) const void *password,
							  IN_LENGTH_TEXT const int passwordLen,
							  INOUT_BUFFER_FIXED( certObjectDataLen ) \
									void *certObjectData,
							  IN_LENGTH_SHORT const int certObjectDataLen,
							  OUT_HANDLE_OPT CRYPT_CERTIFICATE *iDataCert,
							  INOUT_PTR ERROR_INFO *errorInfo )
	{
	CRYPT_CONTEXT iWrapContext;
	STREAM stream;
	long length DUMMY_INIT;
	int certDataSize, status;

	assert( isReadPtr( certObjectInfo, sizeof( PKCS12_OBJECT_INFO ) ) );
	assert( isReadPtrDynamic( password, passwordLen ) );
	assert( isWritePtrDynamic( certObjectData, certObjectDataLen ) );
	assert( isWritePtr( iDataCert, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );

	REQUIRES( cryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
			  isHandleRangeValid( cryptOwner ) );
	REQUIRES( passwordLen >= MIN_NAME_LENGTH && \
			  passwordLen <= CRYPT_MAX_TEXTSIZE );
	REQUIRES( isShortIntegerRangeMin( certObjectDataLen, MIN_OBJECT_SIZE ) );

	/* Clear return value */
	*iDataCert = CRYPT_ERROR;

	/* Create the wrap context used to decrypt the public certificate data */
	status = createPkcs12KeyWrapContext( ( PKCS12_OBJECT_INFO * ) certObjectInfo, 
										 cryptOwner, password, passwordLen, 
										 &iWrapContext, FALSE );
	if( cryptStatusError( status ) )
		return( status );

	/* Decrypt the certificate */
	status = krnlSendMessage( iWrapContext, IMESSAGE_CTX_DECRYPT, 
							  certObjectData, certObjectDataLen );
	krnlSendNotifier( iWrapContext, IMESSAGE_DECREFCOUNT );
	if( cryptStatusError( status ) )
		return( status );

	/* Make sure that the decryption succeeded.  We have to be a bit careful
	   here because there are so many garbled certificates used with the
	   equally-garbled PKCS #12 format that an invalid object doesn't
	   necessarily imply that an incorrect decryption key was used.  To 
	   avoid false positives, if we get an invalid encoding we try and read 
	   the outer layer of wrapping around the certificate object, and if 
	   that succeeds then it's a valid decrypt of a garbled certificate 
	   rather than an invalid key leading to an invalid certificate.
	   
	   In addition to this we can occasionally get a failed decrypt that
	   looks like a valid ASN.1 object, typically a single primitive-encoded
	   ASN.1 item like a text string.  To deal with this we make sure that 
	   it's pretty close to the length of the decrypted object, "close" 
	   meaning that the difference is less than any block cipher padding 
	   that may be removed by the decryption */
	status = checkCertObjectEncodingLength( certObjectData, 
											certObjectDataLen, 
											&certDataSize );
	if( cryptStatusError( status ) || \
		certDataSize < certObjectDataLen - CRYPT_MAX_IVSIZE )
		{
		int sequenceLength;

		sMemConnect( &stream, certObjectData, certObjectDataLen );
		status = readSequence( &stream, &sequenceLength );
		if( cryptStatusOK( status ) && \
			( sequenceLength < MIN_OBJECT_SIZE || \
			  sequenceLength > certObjectDataLen ) )
			status = CRYPT_ERROR_BADDATA;
		if( cryptStatusOK( status ) )
			status = readSequence( &stream, &sequenceLength );
		if( cryptStatusOK( status ) && \
			( sequenceLength < MIN_OBJECT_SIZE || \
			  sequenceLength > certObjectDataLen ) )
			status = CRYPT_ERROR_BADDATA;
		sMemDisconnect( &stream );
		if( cryptStatusError( status ) )
			{
			/* The data is garbled so that it doesn't look anything like a
			   CMS-wrapped certificate, report it as a decryption problem */
			retExt( CRYPT_ERROR_WRONGKEY, 
					( CRYPT_ERROR_WRONGKEY, errorInfo, 
					  "Error reading decrypted certificate data, probably "
					  "due to incorrect decryption key" ) );
			}
		
		/* The data at least looks like a CMS-wrapped certificate, since we 
		   couldn't get the length via the encoding check, we have to use 
		   the overall object size as an approximation of the length.  This
		   won't be the exact length because of cipher block padding, but
		   the cert-import code won't read past the end of the certificate
		   data */
		certDataSize = certObjectDataLen;
		}

	/* Import the certificate as a data-only certificate.  At this point we 
	   have two redundant CMS headers, one within the other, with the nested 
	   inner header of the outer CMS header being the start of the inner CMS 
	   header.  To handle this we read the outer CMS header with the 
	   READCMS_FLAG_WRAPPERONLY flag set to avoid reading the start of the
	   inner header, which is then read by the second readCMSheader() 
	   call */
	sMemConnect( &stream, certObjectData, certDataSize );
	readSequence( &stream, NULL );
	status = readCMSheader( &stream, certBagOIDinfo, 
							FAILSAFE_ARRAYSIZE( certBagOIDinfo, OID_INFO ), 
							NULL, NULL, READCMS_FLAG_WRAPPERONLY );
	if( cryptStatusOK( status ) )
		{
		status = readCMSheader( &stream, certOIDinfo, 
								FAILSAFE_ARRAYSIZE( certOIDinfo, OID_INFO ), 
								NULL, &length, READCMS_FLAG_INNERHEADER | \
											   READCMS_FLAG_DEFINITELENGTH );
		}
	if( cryptStatusOK( status ) && \
		( length < MIN_OBJECT_SIZE || length > certObjectDataLen ) )
		status = CRYPT_ERROR_BADDATA;
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		retExt( status, 
				( status, errorInfo, 
				  "Invalid CMS header on decrypted certificate" ) );
		}
	ENSURES( isIntegerRange( length ) );
	status = importCertFromStream( &stream, iDataCert, cryptOwner,
								   CRYPT_CERTTYPE_CERTIFICATE, 
								   ( int ) length, 
								   KEYMGMT_FLAG_DATAONLY_CERT, 
								   errorInfo );
	sMemDisconnect( &stream );

	return( status );
	}

/* Import an encrypted private key */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4, 6 ) ) \
static int importPrivateKey( const PKCS12_OBJECT_INFO *keyObjectInfo,
							 IN_HANDLE const CRYPT_USER cryptOwner,
							 IN_HANDLE CRYPT_CONTEXT iPrivKeyContext,
							 IN_BUFFER( passwordLen ) const void *password,
							 IN_LENGTH_TEXT const int passwordLen,
							 IN_BUFFER( keyObjectDataLen ) const void *keyObjectData,
							 IN_LENGTH_SHORT const int keyObjectDataLen,
							 IN_BUFFER_OPT( labelLength ) const void *label,
							 IN_LENGTH_SHORT_Z const int labelLength )
	{
	CRYPT_CONTEXT iWrapContext;
	MECHANISM_WRAP_INFO mechanismInfo;
	MESSAGE_DATA msgData;
	int status;

	assert( isReadPtr( keyObjectInfo, sizeof( PKCS12_OBJECT_INFO ) ) );
	assert( isReadPtrDynamic( password, passwordLen ) );
	assert( isReadPtrDynamic( keyObjectData, keyObjectDataLen) );
	assert( ( label == NULL && labelLength == 0 ) || \
			isReadPtrDynamic( label, labelLength ) );

	REQUIRES( cryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
			  isHandleRangeValid( cryptOwner ) );
	REQUIRES( isHandleRangeValid( iPrivKeyContext ) );
	REQUIRES( passwordLen >= MIN_NAME_LENGTH && \
			  passwordLen <= CRYPT_MAX_TEXTSIZE );
	REQUIRES( isShortIntegerRangeMin( keyObjectDataLen, MIN_OBJECT_SIZE ) );
	REQUIRES( ( label == NULL && labelLength == 0 ) || \
			  ( label != NULL && \
				isShortIntegerRangeNZ( labelLength ) ) );

	/* Create the wrap context used to unwrap the private key */
	status = createPkcs12KeyWrapContext( ( PKCS12_OBJECT_INFO * ) keyObjectInfo, 
										 cryptOwner, password, passwordLen, 
										 &iWrapContext, FALSE );
	if( cryptStatusError( status ) )
		return( status );

	/* Set the key label.  We have to set the label before we load the key 
	   or the key load will be blocked by the kernel */
	if( label != NULL )
		{ 
		setMessageData( &msgData, ( MESSAGE_CAST ) label, \
						min( labelLength, CRYPT_MAX_TEXTSIZE ) ); 
		}
	else
		{ 
		setMessageData( &msgData, ( MESSAGE_CAST ) "Dummy label", 11 ); 
		}
	status = krnlSendMessage( iPrivKeyContext, IMESSAGE_SETATTRIBUTE_S, 
							  &msgData, CRYPT_CTXINFO_LABEL );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iWrapContext, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Import the encrypted private key into the PKC context */
	setMechanismWrapInfo( &mechanismInfo, ( MESSAGE_CAST * ) keyObjectData, 
						  keyObjectDataLen, NULL, 0, iPrivKeyContext, 
						  iWrapContext );
	status = krnlSendMessage( MECHANISM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT, 
							  &mechanismInfo, 
							  MECHANISM_PRIVATEKEYWRAP_PKCS8 );
	clearMechanismInfo( &mechanismInfo );
	krnlSendNotifier( iWrapContext, IMESSAGE_DECREFCOUNT );

	return( status );
	}

/****************************************************************************
*																			*
*							Read PKCS #12 Keys								*
*																			*
****************************************************************************/

/* Read a set of objects in a keyset */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 6 ) ) \
static int readObjects( INOUT_PTR STREAM *stream, 
						INOUT_ARRAY( maxNoPkcs12objects ) PKCS12_INFO *pkcs12info, 
						IN_LENGTH_SHORT const int maxNoPkcs12objects, 
						IN_LENGTH_MIN( 32 ) const long endPos,
						IN_BOOL const BOOLEAN isEncryptedCert,
						INOUT_PTR ERROR_INFO *errorInfo )
	{
	int status, LOOP_ITERATOR;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( pkcs12info, sizeof( PKCS12_INFO ) * \
									maxNoPkcs12objects ) );

	REQUIRES( isShortIntegerRangeNZ( maxNoPkcs12objects ) );
	REQUIRES( isBufsizeRangeMin( endPos, 32 ) );
	REQUIRES( isBooleanValue( isEncryptedCert ) );
	REQUIRES( errorInfo != NULL );

	/* Read each object in the current collection */
	LOOP_MED_WHILE( stell( stream ) < endPos )
		{
		PKCS12_INFO localPkcs12Info, *pkcs12infoPtr;
		PKCS12_OBJECT_INFO *pkcs12ObjectInfoPtr;
		BOOLEAN isCertificate = FALSE;

		ENSURES( LOOP_INVARIANT_MED_GENERIC() );

		/* Read one object */
		status = pkcs12ReadObject( stream, &localPkcs12Info, 
								   isEncryptedCert, errorInfo );
		if( cryptStatusError( status ) )
			return( status );
		if( localPkcs12Info.flags & ( PKCS12_FLAG_CERT | \
									  PKCS12_FLAG_ENCCERT ) )
			isCertificate = TRUE;

		/* Find the location where we'll copy over the new object data */
		status = findObjectEntryLocation( &pkcs12infoPtr, pkcs12info, 
										  maxNoPkcs12objects, 
										  localPkcs12Info.idLength <= 0 ? \
												NULL : localPkcs12Info.id,
										  localPkcs12Info.idLength,
										  localPkcs12Info.flags );
		if( cryptStatusError( status ) )
			{
			pkcs12freeObjectEntry( isCertificate ? \
					&localPkcs12Info.certInfo : &localPkcs12Info.keyInfo );
			if( status == CRYPT_ERROR_OVERFLOW )
				{
				retExt( CRYPT_ERROR_OVERFLOW, 
						( CRYPT_ERROR_OVERFLOW, errorInfo, 
						  "No more room in keyset data to add further "
						  "PKCS #12 items" ) );
				}
			retExt( status, 
					( status, errorInfo, 
					  "Couldn't reconcile ID-less object with existing "
					  "PKCS #12 objects" ) );
			}

		/* Copy the newly-read PKCS #12 object information into the PKCS #12
		   keyset info */
		pkcs12ObjectInfoPtr = isCertificate ? &pkcs12infoPtr->certInfo : \
											  &pkcs12infoPtr->keyInfo;
		if( pkcs12ObjectInfoPtr->data != NULL )
			{
			pkcs12freeObjectEntry( isCertificate ? \
					&localPkcs12Info.certInfo : &localPkcs12Info.keyInfo );
			retExt( CRYPT_ERROR_BADDATA, 
					( CRYPT_ERROR_BADDATA, errorInfo, 
					  "Multiple conflicting %s found in keyset",
					  isCertificate ? "certificates" : "keys" ) );
			}
		copyObjectInfo( pkcs12infoPtr, &localPkcs12Info, isCertificate );
		}
	ENSURES( LOOP_BOUND_OK );

	return( CRYPT_OK );
	}

/* Read an entire keyset.  There is one keyset format that we can't read 
   which is created by Primekey and mixes fragmented OCTET STRINGs with data
   encapsulated inside the fragmented strings.  Dumping the outer level of
   this encoding gives:

	   0 NDEF: SEQUENCE {
	   2    1:   INTEGER 3
	   5 NDEF:   SEQUENCE {
	   7    9:     OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
	  18 NDEF:     [0] {
	  20 NDEF:       OCTET STRING {
	  22 1000:         OCTET STRING
	         :           30 80 30 80 06 09 2A 86 48 86 F7 0D 01 07 01 A0
	         :           80 24 80 04 82 01 41 30 82 01 3D 30 82 01 39 06
	         :           0B 2A 86 48 86 F7 0D 01 0C 0A 01 02 A0 81 C9 30
	         :           81 C6 30 29 06 0A 2A 86 48 86 F7 0D 01 0C 01 03
	[...]
	1026 1000:         OCTET STRING
	         :           E0 7B 81 49 22 7D C2 81 9D CE A3 CA 0E 95 8F D1
	         :           F1 88 10 93 93 A2 4B AE 08 1B FB 84 84 EB 62 3D
	[...]

   Trying to decode the inner content gives:

	   0 NDEF: SEQUENCE {
	   2    1:   INTEGER 3
	   5 NDEF:   SEQUENCE {
	   7    9:     OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
	  18 NDEF:     [0] {
	  20 NDEF:       OCTET STRING {
	  22 1000:         OCTET STRING, encapsulates {
	  26 NDEF:           SEQUENCE {
	  28 NDEF:             SEQUENCE {
	  30    9:               OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
	  41 NDEF:               [0] {
	  43 NDEF:                 OCTET STRING {
	  45  321:                   OCTET STRING, encapsulates {
	  49  317:                     SEQUENCE {
	  53  313:                       SEQUENCE {
	  57   11:                         OBJECT IDENTIFIER
	         :                           pkcs-12-pkcs-8ShroudedKeyBag (1 2 840 [...]
	  70  201:                         [0] {
	[...]
	         :                           }
	 274   94:                         SET {
	[...]
	         :               } } } } } } }
	 376 NDEF:             SEQUENCE {
	 378    9:               OBJECT IDENTIFIER encryptedData (1 2 840 113549 1 7 6)
	 389 NDEF:               [0] {
	 391 NDEF:                 SEQUENCE {
	 393    1:                   INTEGER 0
	 396 NDEF:                   SEQUENCE {
	 398    9:                     OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
	 409   41:                     SEQUENCE {
	 411   10:                       OBJECT IDENTIFIER pbeWithSHAAnd40BitRC2-CBC ([...]
	 423   27:                       SEQUENCE {
	[...]
	         :                         }
	         :                       }
	 452 NDEF:                     [0] {
	 454 1000:                       OCTET STRING
	         :                         1B 68 1C AC 26 0C B1 9C 68 64 EB 25 D0 21 2A 85
	         :                         58 16 B6 1F C1 B7 23 A1 7B E9 4E 4C 71 7D 8A F1
	[...]
	1458   19:                       [APPLICATION 30] 2D 04 04 82 03 E8 9F 86 78 [...]

   By offset 1458 we've hit the OCTET STRING fragment somewhere which means 
   that any data beyond that point appears corrupted because there's OCTET 
   STRING fragmentation data overlaid on the content.
   
   The probable way to process this mess would be to unwrap successive 
   layers of the enveloping into new buffers and restart the stream on the 
   unwrapped content, however this then requires the creation of a number of 
   arbitrary-sized buffers to hold the unwrapped layers, made even more 
   complex by the fact that the fragmented constructed encoding has an 
   unknown length so we don't know in advance how big to make the required 
   buffer.
   
   This code doesn't try and verify the MAC, which would require several 
   more layers of buffering and processing because of the broken way the 
   PKCS #12 MAC is implemented, see the comments in pkcs12_wr.c for details.  
   This isn't an issue because cryptlib doesn't use PKCS #12 as a key 
   storage format but purely as an ephemeral import-only format in which 
   some other application exports the key and cryptlib then imports it
   immediately afterwards, which makes it highly unlikely that someone would
   slip in and tamper with it between those two steps.  For long-term 
   storage where it does matter, PKCS #15, cryptlib does verify the MAC */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
int pkcs12ReadKeyset( INOUT_PTR STREAM *stream, 
					  OUT_ARRAY( maxNoPkcs12objects ) PKCS12_INFO *pkcs12info, 
					  IN_LENGTH_SHORT const int maxNoPkcs12objects, 
					  IN_LENGTH const long endPos,
					  INOUT_PTR ERROR_INFO *errorInfo )
	{
	int status, LOOP_ITERATOR;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( pkcs12info, sizeof( PKCS12_INFO ) * \
									maxNoPkcs12objects ) );
	assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );

	REQUIRES( isShortIntegerRangeNZ( maxNoPkcs12objects ) );
	REQUIRES( isBufsizeRangeNZ( endPos ) && endPos > stell( stream ) );

	/* Clear return value */
	memset( pkcs12info, 0, sizeof( PKCS12_INFO ) * maxNoPkcs12objects );

	/* Scan all of the objects in the keyset.  This gets quite complicated
	   because there are multiple points at which we can have a SET/SEQUENCE
	   OF and depending on the implementation multiple objects may be stored
	   at any of these points, so we have to handle multiple nesting points
	   at which we can find more than one of an object */
	LOOP_MED_INITCHECK( status = CRYPT_OK, 
						cryptStatusOK( status ) && \
							stell( stream ) < endPos - MIN_OBJECT_SIZE )
		{
		long payloadLength;
		int tag, length, innerEndPos = CRYPT_ERROR, isEncrypted, noEOCs = 0;

		ENSURES( LOOP_INVARIANT_MED_GENERIC() );

		/* Read the CMS header encapsulation for the object.  At this point 
		   we get to more PKCS #12 stupidity, if we hit CMS EncryptedData 
		   (isEncrypted == TRUE) then it's actually a certificate (that 
		   doesn't need to be encrypted), and if we hit CMS Data 
		   (isEncrypted == FALSE) then it's usually a private key wrapped 
		   within a bizarre reinvention of CMS EncryptedData that's nested 
		   within the CMS Data, although in some rare cases it may be a 
		   SEQUENCE OF certificates and/or private key data that doesn't 
		   correspond to either of the above.  The combinations are:

			Data
				SEQUENCE OF
					ShroundedKeyBag | CertBag
			
			EncryptedData
				Data (= Certificate) 

		   So if we find EncryptedData then we know that it's definitely a 
		   certificate (so far nothing has tried to put private keys in
		   EncryptedData, although the spec allows it).  If we find Data 
		   then it could be anything, and we have to keep looking at a lower
		   level */
		status = readCMSheader( stream, keyDataOIDinfo, 
								FAILSAFE_ARRAYSIZE( keyDataOIDinfo, \
													OID_INFO ),
								&isEncrypted, &payloadLength, 
								READCMS_FLAG_NONE );
		if( cryptStatusOK( status ) )
			{
			if( !isShortIntegerRange( payloadLength ) && \
				payloadLength != CRYPT_UNUSED )
				status = CRYPT_ERROR_BADDATA;
			}
		if( cryptStatusError( status ) )
			{
			pkcs12Free( pkcs12info, maxNoPkcs12objects );
			retExt( CRYPT_ERROR_BADDATA, 
					( CRYPT_ERROR_BADDATA, errorInfo, 
					  "Invalid PKCS #12 object header" ) );
			}
		REQUIRES( isShortIntegerRange( payloadLength ) || \
				  payloadLength == CRYPT_UNUSED );
		length = ( int ) payloadLength;

		/* If we get to this point without finding a length then we need to 
		   have encountered three indefinite-length wrappers in 
		   readCMSheader(), which means that we need to skip three EOCs at 
		   the end */
		if( length == CRYPT_UNUSED )
			noEOCs = 3;

		/* Deal with constructed OCTET STRINGs, see the comment in 
		   readPkcs12header() for details */
		status = tag = peekTag( stream );
		if( !cryptStatusError( status ) && tag == BER_OCTETSTRING )
			status = readOctetStringHole( stream, &length, 4, DEFAULT_TAG );
		if( cryptStatusError( status ) )
			return( status );

		/* Find out where the collection of PKCS #12 objects in the current
		   set of objects ends.  There may be only one, or many, and they
		   may or may not be of the same type */
		if( length != CRYPT_UNUSED )
			{
			innerEndPos = stell( stream ) + length;
			ENSURES( isIntegerRangeMin( innerEndPos, length ) );
			}
		if( !isEncrypted )
			{
			int innerLength;

			/* Skip the SET OF PKCS12Bag encapsulation */
			status = readSequenceI( stream, &innerLength );
			if( cryptStatusError( status ) )
				{
				pkcs12Free( pkcs12info, maxNoPkcs12objects );
				return( status );
				}
			if( length == CRYPT_UNUSED && innerLength != CRYPT_UNUSED )
				{
				innerEndPos = stell( stream ) + innerLength;
				ENSURES( isIntegerRangeMin( innerEndPos, innerLength ) );
				}
			}
		if( innerEndPos == CRYPT_ERROR )
			{
			int innerLength;

			/* We still haven't got any length information, at this point 
			   the best that we can do is assume that there's a single 
			   encapsulated object present (which, in the rare situations
			   where this case arises, always seems to be the case) and
			   explicitly find its length before we can continue */
			status = getStreamObjectLength( stream, &innerLength,
											MIN_CRYPT_OBJECTSIZE );
			if( cryptStatusError( status ) )
				{
				pkcs12Free( pkcs12info, maxNoPkcs12objects );
				return( status );
				}
			innerEndPos = stell( stream ) + innerLength;
			REQUIRES( isIntegerRangeMin( innerEndPos, innerLength ) );

			/* In practice it's not quite this simple.  Firstly, this
			   approach is somewhat risky because we're calling 
			   getStreamObjectLength() on a stream that could in theory be 
			   non-seekable (although in practice file streams are always 
			   seekable so this is more a theoretical than an actual 
			   problem).  In addition PKCS #12 files usually contain a
			   single object that fits within the stream buffer, so even if 
			   the stream was non-seekable the call would usually work,
			   however for very large keys it's possible that the object
			   won't fit within the stream buffer leading to a reload of
			   the buffer by getStreamObjectLength() which does require a
			   seekable stream.

			   Secondly, and more critically, readObjects() calls
			   pkcs12ReadObject() which calls readRawObjectAlloc(), which
			   needs a definite length, again because it's dealing with a 
			   non-seekable stream (and in this case it really may be a non-
			   seekable stream).  Since we're dealing with an indefinite
			   length, readRawObjectAlloc() can't continue.
			   
			   A kludge workaround for this would be to pass the innerLength 
			   value in to readObjects() as a length hint and to tell it to 
			   bypass the call to readRawObjectAlloc() in favour of a direct
			   malloc() and fixed-length read, duplicating part of 
			   readRawObjectAlloc().  For now we'll leave this until there's 
			   an actual demand for it */
			pkcs12Free( pkcs12info, maxNoPkcs12objects );
			retExt( CRYPT_ERROR_BADDATA, 
					( CRYPT_ERROR_BADDATA, errorInfo, 
					  "Couldn't process indefinite-length PKCS #12 object" ) );
			}
		ENSURES( isIntegerRangeNZ( innerEndPos ) );

		/* Read the set of objects */
		status = readObjects( stream, pkcs12info, maxNoPkcs12objects,
							  innerEndPos, isEncrypted, errorInfo );
		if( cryptStatusError( status ) )
			{
			pkcs12Free( pkcs12info, maxNoPkcs12objects );
			return( status );
			}

		/* Skip any EOCs that may be present.  In the simplest case where
		   the data was encoded using all indefinte-length encoding we know
		   how many EOCs are present and skip them all */
		if( noEOCs > 0 )
			{
			LOOP_INDEX_ALT i;

			LOOP_SMALL_ALT( i = 0, i < noEOCs, i++ )
				{
				int value;

				ENSURES( LOOP_INVARIANT_SMALL_ALT( i, 0, noEOCs - 1 ) );

				value = checkEOC( stream );
				if( cryptStatusError( value ) || value == FALSE )
					{
					pkcs12Free( pkcs12info, maxNoPkcs12objects );
					return( cryptStatusError( value ) ? \
							value : CRYPT_ERROR_BADDATA );
					}
				}
			ENSURES( LOOP_BOUND_OK );
			}
		else
			{
			/* If the data was encoded using a mixture of definite and 
			   indefinite encoding there may be EOC's present even though 
			   the length is known so we skip them if necessary.  We have to 
			   make the reads speculative since the indefinite-length values 
			   were processed inside readCMSheader(), so we don't know how 
			   many there were */
			if( ( status = checkEOC( stream ) ) == TRUE )
				status = checkEOC( stream );
			if( cryptStatusError( status ) )
				{
				pkcs12Free( pkcs12info, maxNoPkcs12objects );
				return( status );
				}
			status = CRYPT_OK;	/* checkEOC() returns TRUE/FALSE */
			}
		}
	ENSURES( LOOP_BOUND_OK );
	
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*									Get a Key								*
*																			*
****************************************************************************/

#if 0	/* Crack an RC2-40 key.  Call as 'keyCrack( certData, certDataSize )'
		   before the 'importCertificate()' call.  A more efficient version
		   of this is in ctx_rc2.c, the following code is really only 
		   present for testing the process with known-key data */

#pragma message( "#############################" )
#pragma message( "Building PKCS #12 key-cracker" )
#pragma message( "#############################" )

static int keyCrack( const void *encData, const int length )
	{
	CRYPT_CONTEXT cryptContext;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	MESSAGE_DATA msgData;
	BYTE data[ 32 ], key[ 32 ], *keyPtr = key + 14;
	int i, status;

	/* Test file: IV =  17 17 F1 B0 94 E8 EE F8		encData + 0
				  PT =	06 0B 2A 86 48 86 F7 0D
						-----------------------
				  XOR:	11 1C DB 36 DC 6E 19 F5 

	   So encr. above = CT block 2.
					  =	9C 4E 66 8A C7 6B 97 F5		encData + 8

	   Actual:    IV =  6F A0 7E A5 65 00 65 6C		encData + 0
				  PT =	06 0B 2A 86 48 86 F7 0D
						-----------------------
				  XOR:	69 AB 54 23 2D 86 92 61 

	   So encr. above = CT block 2.
					  =	34 AA F1 83 BD 9C C0 15		encData + 8 */
	
//	memcpy( data, "\x17\x17\xF1\xB0\x94\xE8\xEE\xF8", 8 );
	memcpy( data, "\x6F\xA0\x7E\xA5\x65\x00\x65\x6C", 8 );
	for( i = 0; i < 8; i++ )
		data[ i ] ^= i[ "\x06\x0B\x2A\x86\x48\x86\xF7\x0D" ];

	memcpy( key, "PKCS#12PKCS#12", 14 );
	memset( key + 14, 0, 5 );

//	memcpy( keyPtr, "\x13\x25\x0c\x1a\x60", 5 );	// Test PKCS #12 file, file #1.
//	memcpy( keyPtr, "\x2C\x28\x14\xC4\x01", 5 );	// "Tellus" PKCS #12 file, file #2

	for( i = 0; i < 256; i++ )
		{
		int keyIndex;

		printf( "Trying keys %02X xx.\n", i );
		fflush( stdout );
		while( keyPtr[ 0 ] == i )
			{
			setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RC2 );
			status = krnlSendMessage( CRYPTO_OBJECT_HANDLE, 
									  IMESSAGE_DEV_CREATEOBJECT, &createInfo, 
									  OBJECT_TYPE_CONTEXT );
			if( cryptStatusError( status ) )
				return( status );
			cryptContext = createInfo.cryptHandle;
			setMessageData( &msgData, key, 19 );
			status = krnlSendMessage( cryptContext, IMESSAGE_SETATTRIBUTE_S, 
									  &msgData, CRYPT_CTXINFO_KEY );
			if( cryptStatusOK( status ) )
				{
				setMessageData( &msgData, ( MESSAGE_CAST ) encData, 8 );
				status = krnlSendMessage( cryptContext, IMESSAGE_SETATTRIBUTE_S, 
										  &msgData, CRYPT_CTXINFO_IV );
				}
			if( cryptStatusOK( status ) )
				{
#if 0	/* For key-crack */
				memcpy( data, ( const BYTE * ) encData + 8, 8 );
				status = krnlSendMessage( cryptContext, IMESSAGE_CTX_DECRYPT, 
										  data, 8 );
#else	/* For full decrypt */
				status = krnlSendMessage( cryptContext, IMESSAGE_CTX_DECRYPT, 
										  ( MESSAGE_CAST ) \
											( ( const BYTE * ) encData + 8 ), 
										  length - 8 );
				DEBUG_DUMP( "crack_result", ( const BYTE * ) encData + 8,
							length - 8 );
#endif /* 0 */
				}
			krnlSendNotifier( cryptContext, IMESSAGE_DECREFCOUNT );
			if( cryptStatusError( status ) )
				return( status );
			if( data[ 0 ] == 0x06 && \
				!memcmp( data, "\x06\x0B\x2A\x86\x48\x86\xF7\x0D", 8 ) )
				{
				printf( "Found at %02X %02X %02X %02X %02X.\n",
						keyPtr[ 0 ], keyPtr[ 1 ], keyPtr[ 2 ],
						keyPtr[ 3 ], keyPtr[ 4 ] );
				fflush( stdout );
				return( CRYPT_OK );
				}
			for( keyIndex = 4; keyIndex >= 0; keyIndex++ )
				{
				keyPtr[ keyIndex ]++;
				if( keyPtr[ keyIndex ] > 0 )
					break;
				}
			if( keyIndex == 1 )
				{
				printf( "Trying keys %02X %02X.\n", 
						keyPtr[ 0 ], keyPtr[ 1 ] );
				fflush( stdout );
				}
			}
		}

	return( CRYPT_OK );
	}
#endif /* 0 */

/* Import a certificate as a data-only certificate object to be attached to 
   the private key */

#ifdef USE_CERTIFICATES

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4, 5, 7 ) ) \
static int importDataOnlyCertificate( const PKCS12_INFO *pkcs12infoPtr,	
									  IN_HANDLE const CRYPT_USER iCryptUser,
									  OUT_HANDLE_OPT CRYPT_CONTEXT *iCryptContext,
									  OUT_HANDLE_OPT CRYPT_CERTIFICATE *iDataCert,
									  IN_BUFFER( certIDlength ) const void *certID, 
									  IN_LENGTH_SHORT const int certIDlength,
									  INOUT_PTR ERROR_INFO *errorInfo )
	{
	CRYPT_CERTIFICATE iCertificate;
	const PKCS12_OBJECT_INFO *certObjectInfo = &pkcs12infoPtr->certInfo;
	const int certDataSize = certObjectInfo->payloadSize;
	DYNBUF pubKeyDB;
	STREAM stream;
	ERROR_INFO localErrorInfo;
	int status;

	assert( isReadPtr( pkcs12infoPtr, sizeof( PKCS12_INFO ) ) );
	assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) );
	assert( isWritePtr( iDataCert, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isReadPtrDynamic( certID, certIDlength ) );

	REQUIRES( iCryptUser == DEFAULTUSER_OBJECT_HANDLE || \
			  isHandleRangeValid( iCryptUser ) );
	REQUIRES( isShortIntegerRangeNZ( certIDlength ) );

	/* Clear return values */
	*iCryptContext = *iDataCert = CRYPT_ERROR;

	/* If it's an unencrypted certificate (they almost never are) then we 
	   can import it directly */
	clearErrorInfo( &localErrorInfo );
	if( pkcs12infoPtr->flags & PKCS12_FLAG_CERT )
		{
		sMemConnect( &stream, ( BYTE * ) certObjectInfo->data + \
										 certObjectInfo->payloadOffset, 
					 certDataSize );
		status = importCertFromStream( &stream, &iCertificate, iCryptUser,
									   CRYPT_CERTTYPE_CERTIFICATE, 
									   certDataSize, 
									   KEYMGMT_FLAG_DATAONLY_CERT,
									   &localErrorInfo );
		sMemDisconnect( &stream );
		}
	else
		{
		BYTE certDataBuffer[ 2048 + 8 ], *certData = certDataBuffer;

		REQUIRES( pkcs12infoPtr->flags & PKCS12_FLAG_ENCCERT );

		/* It's an encrypted certificate, we need to decrypt it before we 
		   can import it.  First we set up a buffer to decrypt the 
		   certificate data */
		if( certDataSize > 2048 )
			{
			if( certDataSize >= MAX_INTLENGTH_SHORT )
				return( CRYPT_ERROR_OVERFLOW );
			REQUIRES( isShortIntegerRangeNZ( certDataSize ) );
			if( ( certData = clAlloc( "importDataOnlyCertificate", \
									  certDataSize ) ) == NULL )
				return( CRYPT_ERROR_MEMORY );
			}
		REQUIRES( boundsCheck( certObjectInfo->payloadOffset, certDataSize,
							   certObjectInfo->dataSize ) );
		memcpy( certData, 
				( BYTE * ) certObjectInfo->data + \
						   certObjectInfo->payloadOffset, certDataSize );

		/* Decrypt and import the certificate */
		status = importCertificate( certObjectInfo, iCryptUser, 
									certID, certIDlength, certData, 
									certDataSize, &iCertificate,
									&localErrorInfo );
		REQUIRES( isShortIntegerRangeNZ( certDataSize ) ); 
		zeroise( certData, certDataSize );
		if( certData != certDataBuffer )
			clFree( "importDataOnlyCertificate", certData );
		}
	if( cryptStatusError( status ) )
		{
		retExtErr( status, 
				   ( status, errorInfo, &localErrorInfo,
					 "Couldn't recreate certificate from stored "
					 "certificate data" ) );
		}
		
	/* We've got the certificate, now create the public part of the context 
	   from the certificate's encoded public-key components */
	status = dynCreate( &pubKeyDB, iCertificate, CRYPT_IATTRIBUTE_SPKI );
	if( cryptStatusError( status ) )
		return( status );
	sMemConnect( &stream, dynData( pubKeyDB ), dynLength( pubKeyDB ) );
	status = iCryptReadSubjectPublicKey( &stream, iCryptContext,
										 CRYPTO_OBJECT_HANDLE, TRUE );
	sMemDisconnect( &stream );
	dynDestroy( &pubKeyDB );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCertificate, IMESSAGE_DECREFCOUNT );
		retExt( status, 
				( status, errorInfo, 
				  "Couldn't recreate public key from certificate" ) );
		}

	*iDataCert = iCertificate;
	return( CRYPT_OK );
	}
#endif /* USE_CERTIFICATES */

/* Get a key from a PKCS #12 keyset.  This gets pretty ugly both because 
   PKCS #12 keysets contain no effective indexing information (making it 
   impossible to look up objects within them) and because in most cases all 
   data, including public keys and certificates, is encrypted.  To handle 
   this we only allow private-key reads, and treat whatever's in the keyset 
   as being a match for any request, since without indexing information 
   there's no way to tell whether it really is a match or not */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
static int getItemFunction( INOUT_PTR KEYSET_INFO *keysetInfoPtr,
							OUT_HANDLE_OPT CRYPT_HANDLE *iCryptHandle,
							IN_ENUM( KEYMGMT_ITEM ) \
								const KEYMGMT_ITEM_TYPE itemType,
							IN_KEYID const CRYPT_KEYID_TYPE keyIDtype,
							IN_BUFFER( keyIDlength ) const void *keyID, 
							IN_LENGTH_KEYID const int keyIDlength,
							IN_PTR_OPT void *auxInfo, 
							INOUT_INT_Z int *auxInfoLength,
							IN_FLAGS_Z( KEYMGMT ) const int flags )
	{
#ifdef USE_CERTIFICATES
	CRYPT_CERTIFICATE iDataCert = CRYPT_ERROR;
#endif /* USE_CERTIFICATES */
	CRYPT_CONTEXT iCryptContext;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	const PKCS12_INFO *pkcs12info = DATAPTR_GET( keysetInfoPtr->keyData );
	const PKCS12_INFO *pkcs12infoPtr;
	const int auxInfoMaxLength = *auxInfoLength;
	int status;

	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
	assert( isWritePtr( iCryptHandle, sizeof( CRYPT_HANDLE ) ) );
	assert( isReadPtrDynamic( keyID, keyIDlength ) );
	assert( ( auxInfo == NULL && auxInfoMaxLength == 0 ) || \
			isReadPtrDynamic( auxInfo, auxInfoMaxLength ) );
	assert( isReadPtr( pkcs12info, sizeof( PKCS12_INFO ) ) );

	REQUIRES( sanityCheckKeyset( keysetInfoPtr ) );
	REQUIRES( keysetInfoPtr->type == KEYSET_FILE && \
			  keysetInfoPtr->subType == KEYSET_SUBTYPE_PKCS12 );
	REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \
			  itemType == KEYMGMT_ITEM_PRIVATEKEY );
	REQUIRES( keyIDtype == CRYPT_KEYID_NAME || \
			  keyIDtype == CRYPT_KEYID_URI || \
			  keyIDtype == CRYPT_IKEYID_KEYID || \
			  keyIDtype == CRYPT_IKEYID_PGPKEYID || \
			  keyIDtype == CRYPT_IKEYID_ISSUERID );
	REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
			  keyIDlength < MAX_ATTRIBUTE_SIZE );
	REQUIRES( ( auxInfo == NULL && *auxInfoLength == 0 ) || \
			  ( auxInfo != NULL && \
				isShortIntegerRangeNZ( *auxInfoLength ) ) );
	REQUIRES( isFlagRangeZ( flags, KEYMGMT ) );
	REQUIRES( pkcs12info != NULL );

	/* Clear return values */
	*iCryptHandle = CRYPT_ERROR;

	/* PKCS #12 doesn't provide any useful indexing information that we
	   can use to look up a key apart from an optional label, so if we get 
	   a key ID type that we can't do anything with or we're matching on
	   the special-case key ID "[none]" we perform a fetch of the first 
	   matching private key on the basis that most PKCS #12 keysets only 
	   contain a single key of interest */
	if( keyIDtype == CRYPT_IKEYID_KEYID || \
		keyIDtype == CRYPT_IKEYID_PGPKEYID || \
		keyIDtype == CRYPT_IKEYID_ISSUERID || \
		( keyIDlength == 6 && !strCompare( keyID, "[none]", 6 ) ) )
		{
		pkcs12infoPtr = pkcs12FindEntry( pkcs12info,
										 keysetInfoPtr->keyDataNoObjects,
										 CRYPT_KEYID_NAME, NULL, 0, 
										 TRUE );
		}
	else
		{
		/* Find a private-key entry */
		pkcs12infoPtr = pkcs12FindEntry( pkcs12info,
										 keysetInfoPtr->keyDataNoObjects,
										 keyIDtype, keyID, keyIDlength, 
										 FALSE );
		}
	if( pkcs12infoPtr == NULL )
		{
		retExt( CRYPT_ERROR_NOTFOUND, 
				( CRYPT_ERROR_NOTFOUND, KEYSET_ERRINFO, 
				  "No information present for" ) );
				  /* Rest added by caller */
		}
	if( pkcs12infoPtr->keyInfo.data == NULL )
		{
		/* There's not enough information present to get a private key */
		retExt( CRYPT_ERROR_NOTFOUND, 
				( CRYPT_ERROR_NOTFOUND, KEYSET_ERRINFO, 
				  "No private key data present for" ) );
				  /* Rest added by caller */
		}

	/* If we're just checking whether an object exists, return now.  If all
	   that we want is the key label, copy it back to the caller and exit */
	if( flags & KEYMGMT_FLAG_CHECK_ONLY )
		return( CRYPT_OK );
	if( flags & KEYMGMT_FLAG_LABEL_ONLY )
		{
		return( attributeCopyParams( auxInfo, auxInfoMaxLength, 
									 auxInfoLength, pkcs12infoPtr->label, 
									 pkcs12infoPtr->labelLength ) );
		}

	/* If we're reading the public key, read the certificate if it's 
	   present in unencrypted form */
	if( itemType == KEYMGMT_ITEM_PUBLICKEY )
		{
		const PKCS12_OBJECT_INFO *certObjectInfo = &pkcs12infoPtr->certInfo;
		const int certDataSize = certObjectInfo->payloadSize;
		STREAM stream;
		ERROR_INFO localErrorInfo;

		clearErrorInfo( &localErrorInfo );
		if( !( pkcs12infoPtr->flags & PKCS12_FLAG_CERT ) )
			{
			retExt( CRYPT_ERROR_NOTFOUND, 
					( CRYPT_ERROR_NOTFOUND, KEYSET_ERRINFO, 
					  "PKCS #12 keyset doesn't contain a readable "
					  "certificate" ) );
			}
		sMemConnect( &stream, ( BYTE * ) certObjectInfo->data + \
										 certObjectInfo->payloadOffset, 
					 certDataSize );
		status = importCertFromStream( &stream, iCryptHandle, 
									   keysetInfoPtr->ownerHandle,
									   CRYPT_CERTTYPE_CERTIFICATE, 
									   certDataSize, KEYMGMT_FLAG_NONE,
									   &localErrorInfo );
		sMemDisconnect( &stream );
		if( cryptStatusError( status ) )
			{ 
			retExtErr( status, 
					   ( status, KEYSET_ERRINFO, &localErrorInfo,
						 "Couldn't recreate certificate from stored "
						 "certificate data" ) );
			}

		return( CRYPT_OK );
		}

	/* Make sure that the user has supplied a password */
	if( auxInfo == NULL )
		return( CRYPT_ERROR_WRONGKEY );

	/* If there's a certificate present with the private key, import it as a 
	   data-only certificate object to be attached to the private key */
#ifdef USE_CERTIFICATES
	if( pkcs12infoPtr->certInfo.data != NULL )
		{
		status = importDataOnlyCertificate( pkcs12infoPtr, 
											keysetInfoPtr->ownerHandle, 
											&iCryptContext, &iDataCert,
											auxInfo, *auxInfoLength,
											KEYSET_ERRINFO );
		if( cryptStatusError( status ) )
			return( status );
		if( iDataCert != CRYPT_ERROR )
			{
			status = iCryptVerifyID( iDataCert, keyIDtype, keyID, 
									 keyIDlength );
			if( cryptStatusError( status ) )
				{
				krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
				krnlSendNotifier( iDataCert, IMESSAGE_DECREFCOUNT );
				retExt( status, 
						( status, KEYSET_ERRINFO, 
						  "Certificate fetched for given %s doesn't "
						  "actually correspond to that ID", 
						  getKeyIDName( keyIDtype ) ) );
				}
			}
		}
	else
#endif /* USE_CERTIFICATES */
		{
		/* Create the private-key object that we'll be importing the key 
		   data into.  In yet another piece of design brilliance, the PKC 
		   algorithm that's needed to create the public/private-key context 
		   is stored inside the encrypted key data, so we can't create a 
		   context to import the key data into until we've already imported 
		   the key data.  To get around this we default to CRYPT_ALGO_RSA, 
		   which is almost always the case */
		setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RSA );
		status = krnlSendMessage( CRYPTO_OBJECT_HANDLE, 
								  IMESSAGE_DEV_CREATEOBJECT, &createInfo, 
								  OBJECT_TYPE_CONTEXT );
		if( cryptStatusError( status ) )
			return( status );
		iCryptContext = createInfo.cryptHandle;
		}

	/* Import the wrapped private key */
	status = importPrivateKey( &pkcs12infoPtr->keyInfo, 
					keysetInfoPtr->ownerHandle, iCryptContext,
					auxInfo, *auxInfoLength, 
					( const BYTE * ) pkcs12infoPtr->keyInfo.data + \
									 pkcs12infoPtr->keyInfo.payloadOffset,
					pkcs12infoPtr->keyInfo.payloadSize, 
					( pkcs12infoPtr->labelLength > 0 ) ? \
						pkcs12infoPtr->label : NULL, 
					pkcs12infoPtr->labelLength );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
		if( iDataCert != CRYPT_ERROR )
			krnlSendNotifier( iDataCert, IMESSAGE_DECREFCOUNT );
		retExt( status, 
				( status, KEYSET_ERRINFO, 
				  "Couldn't unwrap and import private key" ) );
		}

#ifdef USE_CERTIFICATES
	/* Connect the data-only certificate object to the private-key context 
	   if necessary.  This is an internal object used only by the context so 
	   we tell the kernel to mark it as owned by the context only */
	if( iDataCert != CRYPT_ERROR )
		{
		status = krnlSendMessage( iCryptContext, IMESSAGE_SETDEPENDENT, 
								  &iDataCert, SETDEP_OPTION_NOINCREF );
		if( cryptStatusError( status ) )
			{
			krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
			krnlSendNotifier( iDataCert, IMESSAGE_DECREFCOUNT );
			retExt( status, 
					( status, KEYSET_ERRINFO, 
					  "Couldn't attach certificate to key" ) );
			}
		}
#endif /* USE_CERTIFICATES */
	*iCryptHandle = iCryptContext;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Keyset Access Routines							*
*																			*
****************************************************************************/

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int initPKCS12get( INOUT_PTR KEYSET_INFO *keysetInfoPtr )
	{
	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );

	REQUIRES( keysetInfoPtr->type == KEYSET_FILE && \
			  keysetInfoPtr->subType == KEYSET_SUBTYPE_PKCS12 );

	/* Set the access method pointers */
	FNPTR_SET( keysetInfoPtr->getItemFunction, getItemFunction );

	return( CRYPT_OK );
	}
#endif /* USE_PKCS12 */
