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

📄 fortezza.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:
	{ MESSAGE_NONE, MECHANISM_NONE, NULL }
	};

/* Close a previously-opened session with the device.  We have to have this
   before the init function since it may be called by it if the init process
   fails */

static void shutdownFunction( DEVICE_INFO *deviceInfo )
	{
	FORTEZZA_INFO *fortezzaInfo = deviceInfo->deviceFortezza;

	/* Clear the personality list if it exists */
	if( fortezzaInfo->personalities != NULL )
		{
		zeroise( fortezzaInfo->personalities, 
				 fortezzaInfo->personalityCount * sizeof( CI_PERSON ) );
		clFree( "shutdownFunction", fortezzaInfo->personalities );
		fortezzaInfo->personalities = NULL;
		fortezzaInfo->personalityCount = 0;
		}
	if( fortezzaInfo->certHashes != NULL )
		{
		zeroise( fortezzaInfo->certHashes, 
				 fortezzaInfo->personalityCount * sizeof( CI_HASHVALUE ) );
		clFree( "shutdownFunction", fortezzaInfo->certHashes );
		fortezzaInfo->certHashes = NULL;
		fortezzaInfo->certHashesInitialised = FALSE;
		}

	/* Unlock the socket and close the session with the device */
	if( deviceInfo->flags & DEVICE_LOGGEDIN )
		{
		pCI_Unlock();
		deviceInfo->flags &= ~DEVICE_LOGGEDIN;
		}
	pCI_Close( CI_NULL_FLAG, fortezzaInfo->socketIndex );
	}

/* Open a session with the device */

static int initFunction( DEVICE_INFO *deviceInfo, const char *name,
						 const int nameLength )
	{
	FORTEZZA_INFO *fortezzaInfo = deviceInfo->deviceFortezza;
	CI_CONFIG deviceConfiguration;
	CI_TIME cardTime;
	int socket, i, fortezzaStatus, status = CRYPT_ERROR_OPEN;

	UNUSED( name );

	/* The Fortezza open is in theory a bit problematic since with older
	   drivers the open will succeed even if there's no device in the socket, 
	   so after we perform the open we reset the card and check its state to 
	   make sure that we're not just rhapsodising into the void.  This also 
	   catches a bug in the Spyrus driver (see the comment further on) in 
	   which it tries to open a nonexistent device in the USB (pseudo)-slot 
	   before it opens the real Fortezza card in the PCMCIA slot.
	   
	   For some drivers such as the 1996-vintage (non-PnP) NT Fortezza 
	   driver which uses a custom kernel driver to handle PCMCIA cards this 
	   isn't a problem because the driver won't load unless there's a card 
	   inserted, but newer PnP drivers, multi-slot readers with the card 
	   inserted in a slot other than the first one, and the Unix driver 
	   (which has a dedicated daemon to handle the card) may not exhibit 
	   this behaviour so we check for things working in the manner specified 
	   in the docs.

	   The choice of socket for the card can be a bit confusing.  According 
	   to some docs the socket can start from 0 (in violation of the spec),
	   whereas others say they should start from 1, since some drivers do
	   start at slot 0 we go from there (typically we just get a 
	   CI_INV_SOCKET_INDEX for slot 0 if the driver happens to start at 1).  
	   
	   Once we've done that, we reset the card to get it into a known state 
	   (although judging by the equivalent time delay of CI_Open() and 
	   CI_Reset(), the open does this anyway) and check that a card is 
	   actually present (see the comments above - the NSA must be using 
	   their own drivers recovered from crashed UFOs if their ones really do 
	   behave as documented) */
	for( socket = 0; socket <= noSockets; socket++ )
		{
		CI_STATE deviceState;

		/* Try and open the card in the current socket */
		fortezzaStatus = pCI_Open( CI_NULL_FLAG, socket );
		if( fortezzaStatus != CI_OK )
			continue;
		fortezzaInfo->socketIndex = socket;

		/* We've opened the card, reset it to get it into a known state
		   and make sure that the state is valid.  Unfortunately the exact 
		   definition of a valid state is a bit tricky, for example we 
		   shouldn't allow the initialised or SSO initialised states here 
		   since there doesn't appear to be any way to get from them to 
		   CAW initialised at this point (that is, you need to go 
		   uninitialised -> initialised -> SSO initialised -> CAW 
		   initialised in a straight sequence), however we need to get
		   past this point in order to perform the only valid operation on
		   the card (zeroise) so we have to let these pass even though
		   there's not much we can do in them */
		fortezzaStatus = pCI_Reset();
		if( fortezzaStatus == CI_NO_CARD )
			/* Some versions of the Syprus driver return CI_NO_CARD at this
			   point if the Spyrus USB (pseudo-)slot is enabled, since they
			   allow an open of the USB pseudo-slot (even though no device
			   is present) and then fail to communicate with the nonexistant
			   device.  If we get this error, we continue, since the 
			   Fortezza should be present in a later slot */
			continue;
		if( fortezzaStatus == CI_OK )
			fortezzaStatus = pCI_GetState( &deviceState );
		if( fortezzaStatus != CI_OK || \
			( deviceState == CI_POWER_UP || \
			  deviceState == CI_INTERNAL_FAILURE ) )
			{
			pCI_Close( CI_NULL_FLAG, socket );
			if( fortezzaStatus == CI_OK )
				fortezzaStatus = CI_INV_STATE;
			continue;
			}
		deviceInfo->flags = DEVICE_ACTIVE | DEVICE_NEEDSLOGIN;
		status = CRYPT_OK;
		break;
		}
	if( cryptStatusError( status ) )
		{
		fortezzaInfo->errorCode = fortezzaStatus;
		return( status );
		}

	/* Since the onboard clock could be arbitrarily inaccurate (and even 
	   nonfunctional by now on older cards, since the design life was only
	   7 years), we compare it with the system 
	   time and only rely on it if it's within +/- 1 day of the system 
	   time */
	status = pCI_GetTime( cardTime );
	if( status == CI_OK )
		{
		const time_t theTime = getTokenTime( cardTime );
		const time_t currentTime = getTime();

		if( theTime >= currentTime - 86400 && \
			theTime <= currentTime + 86400 )
			deviceInfo->flags |= DEVICE_TIME;
		}

	/* Set up device-specific information.  We can't read the personality 
	   list until the user logs on, so all we can do at this point is 
	   allocate memory for it.  Note that personality 0 can never be selected
	   and so it isn't returned when the personality info is read, this leads 
	   to confusing fencepost errors so when we allocate/read the personality
	   info we leave space for a zero-th personality which is never used */
	pCI_GetConfiguration( &deviceConfiguration );
	fortezzaInfo->largestBlockSize = deviceConfiguration.LargestBlockSize;
	fortezzaInfo->minPinSize = 4;
	fortezzaInfo->maxPinSize = CI_PIN_SIZE;
	fortezzaInfo->keyRegisterCount = deviceConfiguration.KeyRegisterCount;
	fortezzaInfo->keyRegisterFlags = 1;	/* Register 0 is reserved */
	fortezzaInfo->personalityCount = deviceConfiguration.CertificateCount + 1;
	fortezzaInfo->personalities = \
					clAlloc( "initFunction", fortezzaInfo->personalityCount * \
											 sizeof( CI_PERSON ) );
	fortezzaInfo->certHashes = \
					clAlloc( "initFunction", fortezzaInfo->personalityCount * \
											 sizeof( CI_HASHVALUE ) );
	if( fortezzaInfo->personalities == NULL || fortezzaInfo->certHashes == NULL )
		{
		shutdownFunction( deviceInfo );
		return( CRYPT_ERROR_MEMORY );
		}
	memset( fortezzaInfo->personalities, 0, 
			fortezzaInfo->personalityCount * sizeof( CI_PERSON ) );
	fortezzaInfo->currentPersonality = CRYPT_ERROR;
	memset( fortezzaInfo->certHashes, 0, 
			fortezzaInfo->personalityCount * sizeof( CI_HASHVALUE ) );
	fortezzaInfo->certHashesInitialised = FALSE;
	memcpy( fortezzaInfo->labelBuffer, deviceConfiguration.ProductName, 
			CI_NAME_SIZE );
	for( i = CI_NAME_SIZE;
		 i && ( fortezzaInfo->labelBuffer[ i - 1 ] == ' ' || \
				!fortezzaInfo->labelBuffer[ i - 1 ] ); i-- );
	fortezzaInfo->labelBuffer[ i ] = '\0';
	deviceInfo->label = fortezzaInfo->labelBuffer;

	return( CRYPT_OK );
	}

/* Handle device control functions */

static int controlFunction( DEVICE_INFO *deviceInfo,
							const CRYPT_ATTRIBUTE_TYPE type,
							const void *data, const int dataLength )
	{
	FORTEZZA_INFO *fortezzaInfo = deviceInfo->deviceFortezza;
	int status;

	/* Handle user authorisation */
	if( type == CRYPT_DEVINFO_AUTHENT_USER || \
		type == CRYPT_DEVINFO_AUTHENT_SUPERVISOR )
		{
		CI_PERSON *personalityList = fortezzaInfo->personalities;
		CI_PIN pin;
		BYTE ivBuffer[ 64 ];	/* For LEAF handling */
		int certIndex;

		initPIN( pin, data, dataLength );
		status = pCI_CheckPIN( ( type == CRYPT_DEVINFO_AUTHENT_USER ) ? \
							   CI_USER_PIN : CI_SSO_PIN, pin );
		if( status != CI_OK )
			return( ( status == CI_FAIL ) ? CRYPT_ERROR_WRONGKEY : \
					mapError( status, CRYPT_ERROR_WRONGKEY ) );

		/* Get the list of device personalities (skipping the zero-th 
		   personality, which can't be selected) and lock the device for our 
		   exclusive use.  We should really do this as soon as we open the 
		   device to make sure that the user isn't presented with any nasty 
		   surprises due to state changes caused by other active sessions 
		   with the device, but the driver won't let us do it until we've 
		   authenticated ourselves to the device */
		status = pCI_GetPersonalityList( fortezzaInfo->personalityCount - 1, 
										 &personalityList[ 1 ] );
		if( status == CI_OK )
			{
			int index;

			/* Set a label for the zero-th personality (which can't be 
			   explicitly accessed but whose cert can be read) to make sure 
			   that it isn't treated as an empty personality slot */
			strcpy( personalityList[ 0 ].CertLabel, 
					"PAA1FFFFPersonality 0 dummy label" );

			/* Perform a sanity check for certificate indices.  The 
			   documentation implies that the certificate index always 
			   matches the personality index (skipping the zero-th 
			   personality), but doesn't seem to mandate this anywhere so 
			   we make sure that things really are set up this way */
			for( index = 0; index < fortezzaInfo->personalityCount; index++ )
				{
				CI_PERSON *personality = getPersonality( fortezzaInfo, index );

				if( personality->CertificateIndex != 0 && \
					personality->CertificateIndex != index )
					{
					status = CI_BAD_TUPLES;
					break;
					}
				}
			}
		if( status == CI_OK )
			status = pCI_Lock( CI_NULL_FLAG );
		if( status != CI_OK )
			{
			pCI_Reset();	/* Log off */
			fortezzaInfo->errorCode = status;
			return( CRYPT_ERROR_FAILED );
			}

		/* Look for the most likely required personality (other than 
		   personality 0, which is a non-personality used for the CA
		   root cert) and set it as the currently active one.  If this 
		   fails we stay with the default personality for lack of any 
		   better way to handle it */
		certIndex = findCertificateFromLabel( fortezzaInfo, NULL, 0 );
		if( !cryptStatusError( certIndex ) && certIndex )
			{
			pCI_SetPersonality( certIndex );
			fortezzaInfo->currentPersonality = certIndex;
			}

		/* Handle LEAF suppression.  On LEAF-suppressed cards the LEAF bytes
		   are replaced by 'THIS IS NOT LEAF', in case there are cards that
		   use a different string we remember it with the device info so we 
		   can load LEAF-less IV's */
		status = pCI_DeleteKey( 1 );
		if( status == CI_OK )
			status = pCI_GenerateMEK( 1, 0 );
		if( status == CI_OK )
			status = pCI_SetKey( 1 );
		if( status == CI_OK )
			status = pCI_GenerateIV( ivBuffer );
		memcpy( fortezzaInfo->leafString, ( status == CI_OK ) ? \
				ivBuffer : "THIS IS NOT LEAF", 16 );
		pCI_DeleteKey( 1 );

		/* The device is now ready for use */
		deviceInfo->flags |= DEVICE_LOGGEDIN;		
		krnlSendMessage( deviceInfo->objectHandle, IMESSAGE_SETATTRIBUTE, 
						 MESSAGE_VALUE_UNUSED, CRYPT_IATTRIBUTE_INITIALISED );
		return( CRYPT_OK );
		}

	/* Handle authorisation value change.  Fortezza uses a multi-stage
	   bootstrap FSM and requires that all of the various intialisation 
	   functions be used one after the other, with no intervening operations 
	   apart from setting the PAA (CA root) cert in the SSO initialised 
	   state.  Interrupting the process (for example by logging off/closing 
	   the device) requires that it be restarted from scratch:

			   uninitialised
					v
				CI_Zeroize			(enter zeroise PIN)
					v
				 zeroised
					v
				CI_CheckPIN			(enter init PIN)
					v
				initialised
					v
				CI_ChangePIN		(set SSO PIN)
					v
			  SSO initialised
					v
				CI_ChangePIN		(set user PIN)
					v
			  user initialised

	   The single-sequence requirement means that the initialised -> SSO
	   initialised step re-uses the PIN set at initialisation, and the SSO
	   initialised -> user initialised uses the same PIN as the old and new
	   PIN, since there's no user PIN set at that point.  Once we've set the
	   initial user PIN, the card is automagically moved into the user
	   initialised state */
	if( type == CRYPT_DEVINFO_SET_AUTHENT_SUPERVISOR )
		{
		CI_PIN oldPIN, newPIN;

		/* Make sure that there's an SSO PIN present from a previous device
		   initialisation */
		if( strlen( fortezzaInfo->initPIN ) <= 0 )
			{
			setErrorInfo( deviceInfo, CRYPT_DEVINFO_INITIALISE, 
						  CRYPT_ERRTYPE_ATTR_ABSENT );
			return( CRYPT_ERROR_NOTINITED );
			}

		/* This assumes that we're in the initialised state and are setting 
		   the initial SSO PIN to move us into the SSO initialised state, 
		   for which oldPIN == initialisation PIN.  Once we've done this we
		   clear the initialisation PIN, since it's no longer valid in the 
		   new state */

⌨️ 快捷键说明

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