📄 fortezza.c
字号:
{ 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 + -