During the normal operation of an application running on an Axis system, the Heber Security Device (HSD) needs to be regularly authenticated against the Smart Card. This mechanism can be done in different ways. The mechanism presented here is a method that would be started from the application initialisation and then left to operate inside a Linux pthread.
Using the method detailed here, a game developer would set up an autonomous thread to poll the HSD security and Smart Card for authentication. This method is not completely robust to an attack and it may be possible for an attacker to allow just this loop to execute without the rest of the application. The ease of use needs to be considered against possible attacks.
One possible defence against this kind of attack is to make the pthread use a counter. Every time it performs a security verification it decrements a counter. If this counter ever reaches zero then the pthread does not perform any more security verifications. Inside the application main loop a call is made to reset this counter to a non-zero value. Using this mechanism it would be very hard for an attacker to stop the main application without causing the pthread to eventually stop performing security verifications.
During the system initialisation, a call to start the hardware security thread would be enabled. From this point on every 75 seconds a Smart Card - HSD authentication would occur. The code to enable something like this would look as below. If the period for Smart Card HSD authentications is about every 4 minutes then performing this authentication every 75 seconds would give a good margin for a busy system.
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include "zccri.h"
#include "zcbci.h"
#include "axis.h"
#include "sec-game.h"
#include "key-game.h"
#define TRUE 1
#define FALSE 0
#define SLEEP_TIME 75
#define APP_REFRESH_COUNT (5L)
static volatile unsigned char qSecurityFailure;
static volatile unsigned long lSessionID;
static volatile unsigned long lAppRefreshCntr;
static unsigned char qKeepRunning;
static pthread_t tPthread;
static char acAxisCryptoPort[] = "/dev/axis_0/crypto";
/* Note This key is the encrypted version of what is stored inside the Smart Card */
static unsigned char abSecretKey[ 3 ][ 8 ] =
{
{ 0x56, 0xeb, 0x5c, 0x4d, 0x76, 0xbc, 0x60, 0x4d },
{ 0x9d, 0xe4, 0x06, 0x72, 0x68, 0xd9, 0x0c, 0xa0 },
{ 0x25, 0xbe, 0x68, 0xf1, 0x51, 0x43, 0x1f, 0x76 }
};
void *auth_SmartFpga( void *);
static int auth_PerformFpgaAuthStep1( unsigned char *pabBuf, int zlBufSize );
static int auth_PerformFpgaAuthStep2( unsigned char *pabBuf, int zlBufSize );
/*
* When calling this initialisation function the lSmtCrdSessionId must be a big number
* and must be the same for all calls to the smart card.
*/
int auth_PthreadInit( unsigned long lSmtCrdSessionId )
{
pthread_attr_t tPthreadAttr;
qKeepRunning = TRUE;
qSecurityFailure = FALSE;
lSessionID = lSmtCrdSessionId;
lAppRefreshCntr = APP_REFRESH_COUNT;
pthread_attr_init( &tPthreadAttr );
pthread_attr_setdetachstate( &tPthreadAttr, PTHREAD_CREATE_DETACHED );
pthread_create( &tPthread, &tPthreadAttr, auth_SmartFpga,
NULL );
return( 0 );
}
void auth_AppSecurityRefresh( void )
{
lAppRefreshCntr = APP_REFRESH_COUNT;
}
/* returns 0 if security OK and non-zero if security failed */
unsigned char auth_HwSecurityStatus( void )
{
return( qSecurityFailure );
}
void auth_TimeToDie( void )
{
qKeepRunning = FALSE;
}
void *auth_SmartFpga( void *donotuse)
{
tsSMARTCARD_INFO sSmartCardInfo;
unsigned char abNonce[ 8 ];
while( qKeepRunning != 0 )
{
if(( 0 != lAppRefreshCntr ) &&
( 0 == sec_OpenSmartCard( &sSmartCardInfo, 1, abKeyGame, sizeof( abKeyGame ))))
{
lAppRefreshCntr--;
if( 0 == sec_SetSessionId( &sSmartCardInfo, lSessionID ))
{
if( 0 == auth_PerformFpgaAuthStep1( abNonce, sizeof( abNonce )))
{
if( 0 == sec_AuthFpga( &sSmartCardInfo, abNonce, sizeof( abNonce )))
{
auth_PerformFpgaAuthStep2( abNonce, sizeof( abNonce ));
}
else
{
goto security_error;
}
}
else
{
goto security_error;
}
}
else
{
goto security_error;
}
}
else
{
security_error:
qSecurityFailure = TRUE;
}
sec_CloseSmartCard( &sSmartCardInfo );
/* Wait a bit for another one to take place */
sleep( SLEEP_TIME );
}
return( 0 );
}
static int auth_PerformFpgaAuthStep1( unsigned char *pabBuf, int zlBufSize )
{
int fdCrypt;
KEY_OP sKey;
CRYPTO_OP sCrypto;
int zlRtnv = 0;
memcpy( sKey.key1, abSecretKey[ 0 ], 8 );
memcpy( sKey.key2, abSecretKey[ 1 ], 8 );
memcpy( sKey.key3, abSecretKey[ 2 ], 8 );
sKey.flags = 0;
fdCrypt = open( acAxisCryptoPort, O_RDWR );
if( -1 != fdCrypt )
{
ioctl( fdCrypt, AXIS_KEY_OP, &sKey );
sCrypto.op_type = DO_GET_SECURE_SEED;
sCrypto.op_type |= DO_ENCRYPT;
sCrypto.size = zlBufSize;
sCrypto.data = pabBuf;
ioctl( fdCrypt, AXIS_SECURE_OP, &sCrypto );
close( fdCrypt );
}
else
{
zlRtnv = -1;
}
return( zlRtnv );
}
static int auth_PerformFpgaAuthStep2( unsigned char *pabBuf, int zlBufSize )
{
int fdCrypt;
KEY_OP sKey;
CRYPTO_OP sCrypto;
int zlRtnv = 0;
memcpy( sKey.key1, abSecretKey[ 0 ], 8 );
memcpy( sKey.key2, abSecretKey[ 1 ], 8 );
memcpy( sKey.key3, abSecretKey[ 2 ], 8 );
sKey.flags = 0;
fdCrypt = open( acAxisCryptoPort, O_RDWR );
if( -1 != fdCrypt )
{
ioctl( fdCrypt, AXIS_KEY_OP, &sKey );
sCrypto.op_type = DO_WRITE_SECURE_SEED;
sCrypto.size = zlBufSize;
sCrypto.data = pabBuf;
ioctl( fdCrypt, AXIS_SECURE_OP, &sCrypto );
close( fdCrypt );
}
else
{
zlRtnv = -1;
}
return( zlRtnv );
}