Example Source Code

This page contains example code for performing certain operations

AES encryption and key maskingProtected RSA private keysUsing the Global PINAdditional Static |

ECC key generation, signing and verificationTransactions

 


AES Encryption & Symmetric Key Masking

This code shows how an AES key stored in the application’s static memory can be protected (using masking) by the operating system such that the clear value of the key cannot be accessed by the application code. The masking is done at the end of the application loading process. In the example the key is generated randomly, otherwise up to that point the key’s value generated offline would have been protected using the MULTOS confidential ALU mechanism. The mask is random and different for each application.

The key checksum is an integrity measure and used to guard against attacks that aim to modify the key value.

The example also shows one use of the Process Events functionality running an application for a reason other than to process an APDU command. More information can be found on the Quick Reference page.

#pragma attribute("aid", "33")
#pragma attribute("access_list", "0407")  // Process Events + Contactless + Contact + Strong Crypto
#pragma attribute("name", "Mask on create")

#include <multos.h>
#include <ISO7816.h>

#define MAX_DATA 1024

#pragma melpublic
// P21 has 1240 bytes of public RAM
BYTE abPublic[MAX_DATA + 16];

#pragma melstatic
BYTE abAes128Key[16];
WORD wKeyChecksum;

void
main(void)
{
	BYTE bEventId;
	BYTE abIcv[16];
	WORD wDataLen;
	QWORD qRand;

	// Find out which event caused the application to be run
	bEventId = multosGetProcessEvent();

	// If the application has just been created
	if (bEventId == EVENT_CREATE)
	{
		// Generate a random key
		qRand = multosGetRandomNumber();
		multosCopy((BYTE*)&qRand,abAes128Key,8);
		qRand = multosGetRandomNumber();
		multosCopy((BYTE*)&qRand,abAes128Key + 8,8);

		// Mask the key value immediately
		wKeyChecksum = multosMaskData(abAes128Key,abAes128Key,sizeof(abAes128Key));

		// Turn off process event handling so that the app is only ever called for an APDU in the future
		// This parameter is a bitmap. See C-APIv2.pdf for a definition.
		multosUpdateProcessEvents(0x0001);

		// Exit
		multosExit();
	}

	// *** The application is processing an APDU command ***

	// Check command validity
	if (!multosCheckCase(4))
		multosExitSW(ISO7816_SW_DATA_INVALID);
	if (CLA != 0x00 || INS > 1)
		multosExitSW(ISO7816_SW_COMMAND_NOT_ALLOWED);
	if (Lc > MAX_DATA)
		multosExitSW(ISO7816_SW_WRONG_LENGTH);

	// Tell the O/S to internally prepare to use the masked key
	if (multosLoadMaskedKey(abAes128Key,MASK_AES_16KEY) != wKeyChecksum)
	{
		// The masked key value is not as expected
		multosExitSW(0x9A00);
	}

	// If encrypting
	wDataLen = Lc;
	if (INS == 0)
	{
		// Pad with 0x80 then zero or more 0x00 bytes up to the AES block size
		wDataLen = multosPad(1,abPublic,Lc, 16);

		// AES CBC encrypt the data in public telling the O/S to use the internally prepared AES key
		multosEncipherCBC(ALGORITHM_AES, abPublic, abPublic, sizeof(abAes128Key),MASK_NULL_PTR, wDataLen, abIcv, sizeof(abIcv));

		// Set the amount of data being returned to the padded length
		La = wDataLen;
	}
	else
	{
		// Check the length is a multiple of the AES block length
		if (wDataLen % 16)
			multosExitSW(ISO7816_SW_WRONG_LENGTH);

		// AES CBC decrypt the data in public telling the O/S to use the internally prepared AES key
		multosDecipherCBC(ALGORITHM_AES, abPublic, abPublic, sizeof(abAes128Key), MASK_NULL_PTR, wDataLen, abIcv, sizeof(abIcv));

		// Set the amount of data being returned minus the padding.
		// This assumes the message was padded before encryption.
		La = multosUnPad(1, abPublic, wDataLen);
	}

	// Unload the masked key and verify it is still good
	if (multosVerifyMaskedKey(MASK_VERIFY_KEY_AND_TERMINATE) != wKeyChecksum)
	{
		// The masked key value is not as expected
		multosExitSW(0x9A01);
	}
}

The following debugging file can be used to run the application in Eclipse.

c:\git\smartdeck\delivery\bin\hsim.exe
33
; Set memory sizes in line with P21
-ds 5000
-ps 1240
; Dummy Create MEL Application command to trigger the create event processing
-apdu BE1600000100

; Select the application ready for use
-apdu 00A404000133

; Send encrypt command (case 4)
-apdu 000000001E0102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E20

; From prompt, send decrypt command starting 0001000020 followed by result of encrypt command

Protected RSA Private Keys

The private part of RSA keys may be protected by the operating system such that the clear value is not available to the application. If required, the protection should be applied at the earliest possible moment, either during the processing of the Create Event or on receipt via an APDU.

The following code snippet shows how to apply protection to an RSA key that is in the application data.

 

typedef struct
{
	struct {
		BYTE dp[MODULUS_LENGTH/2];
		BYTE dq[MODULUS_LENGTH/2];
		BYTE p[MODULUS_LENGTH/2];
		BYTE q[MODULUS_LENGTH/2];
		BYTE u[MODULUS_LENGTH/2];
	} privateKey;
	BYTE publicKey[MODULUS_LENGTH];
	BYTE exponent[EXPONENT_LENGTH];
} rsa_t;

#pragma melstatic
rsa_t rsaKeyPairGood;

void
main(void)
{
	BYTE bEventId;

	bEventId = multosGetProcessEvent();

	if (bEventId == EVENT_CREATE)
	{
		// Apply protection to the private key
		multosModularExponentiationCRTProtected(
				keyPairGood.privateKey.p,
				keyPairGood.privateKey.dp,
				keyPairGood.privateKey.p,
				keyPairGood.privateKey.dp,
				MODULUS_LENGTH);
		
		multosExit();
	}

	// ... to use the private key, the "protected" ModExp function must be used
	multosModularExponentiationCRTProtected(
			decryptedData,
			encryptedData,
			keyPairGood.privateKey.p,
			keyPairGood.privateKey.dp,
			MODULUS_LENGTH);
}

Using the Global PIN

A global PIN (otherwise known as a shared PIN) is a PIN stored internally by the operating system that applications (with the correct access_list permissions) are able to share. The global PIN only has to be verified by one application, with that verification then being valid for all applications using the global PIN, until the card is reset. As well as storing the global PIN, the operating system stores and provides access to PIN Try Counter (PTC) and PIN Try Limit (PTL) values; how those values are managed and used is application specific.

The example includes use of assertions to improve resilience to fault attacks aimed at forcing code to be skipped to circumvent the PIN checks. The example is by no means guaranteed to be perfect but shows the sort of additional checks that can be added when necessary.

#pragma attribute("name","global pin")
#pragma attribute("aid","55")
#pragma attribute("access_list","0507") // Process Events + Global PIN Basic + Contact + Contactless + Strong Crypto

#include <multos.h>
#include <ISO7816.h>

#define PIN_VERIFIED 0xA5
#define PIN_UNVERIFIED 0x5A

// INS values
#define VERIFY_PIN 0x01

// Macro that will cause and abend on an assertion failure
#define assert(c) if (!(c))  __code(__PRIM, __PRIM_CALL_EXTENSION_PRIMITIVE, 0xFF, 0xFF, 0xFF)

// PIN data structure
typedef struct {
	BYTE abPIN[8];
	BYTE bLen;
	BYTE bPTC;
	BYTE bPTL;
	DWORD dwMultosChecksum; // calculated over abPIN to bPTL inclusive.
}sPinInit_t;

#pragma melpublic
BYTE abPublic[8];

#pragma melstatic
sPinInit_t sPinData = {{0x31,0x32,0x33,0x34,0,0,0,0},4,3,3,0x2e1270c5};

void
main (void)
{
	BYTE bEventId;
	BYTE bPTC;
	WORD wVerificationStatus1,wVerificationStatus2;

	bEventId = multosGetProcessEvent();
	if (bEventId == EVENT_CREATE)
	{
		// If the global PIN has not already been initialised
		if (multosGetPINStatus() == 0)
		{
			// Initialise the global PIN
			multosInitialisePIN((BYTE*) &sPinData);

			// Wipe the clear PIN data
			multosClear(sizeof(sPinData),&sPinData);
		}

		// Stop the app from processing any more events other than APDUs
		multosUpdateProcessEvents(0x0001);

		multosExit();
	}

	// .... APDU processing code starts here ...._

	if (INS == VERIFY_PIN)
	{
		// Assert that the global PIN is initialised, as logically it has to be,
		// unless there has been an attack.
		assert (multosGetPINStatus() != 0);

		// Ensure that the command data is available
		if (!(multosCheckCase(3) && Lc > 0 && Lc <= 8))
			multosExitSW(ISO7816_SW_DATA_INVALID);

		// Get the PIN Try Counter
		bPTC = multosGetPINTryCounter();

		// If PTC is not exhausted
		if (bPTC > 0)
		{
			// Decrement the PTC
			multosSetPINTryCounter(bPTC - 1);

			// Assert that the PTC has really been decremented
			assert(multosGetPINTryCounter() == (bPTC - 1));

			// Attempt to verify the PIN using the APDU data
			wVerificationStatus1 = multosVerifyPIN(abPublic,(BYTE) Lc);

			// Repeat
			wVerificationStatus2 = multosVerifyPIN(abPublic,(BYTE) Lc);

			// Results should ALWAYS match unless there has been an attack
			assert(wVerificationStatus1 == wVerificationStatus2);

			// Has the PIN been verified?
			if (wVerificationStatus1 == 0x5AA5)
			{
				// Confirm the second result is also verified, which is should ALWAYS be.
				assert(wVerificationStatus2 == 0x5AA5);

				// Flag the PIN as being verified
				multosSetPINVerificationStatus(PIN_VERIFIED);

				// Restore PTC to PTL
				multosSetPINTryCounter(multosGetPINTryLimit());

				multosExitSW(ISO7816_SW_NO_ERROR);
			}
			else
			{
				// PIN has not been verified
				multosExitSW(ISO7816_SW_SECURITY_STATUS_NOT_SATISFIED);
			}
		}
		else
		{
			// Number of tries exhausted
			multosExitSW(ISO7816_SW_COMMAND_NOT_ALLOWED);
		}
	}

	// .... continue processing other commands...
	if (multosGetPINVerificationStatus() == PIN_VERIFIED)
	{
		// Confirm the PIN is really verified
		assert (multosGetPINVerificationStatus() != PIN_UNVERIFIED);
	}
	else
	{
		// PIN has not been verified
		multosExitSW(ISO7816_SW_SECURITY_STATUS_NOT_SATISFIED);
	}
}

This debugging file can be used to step through the application in Eclipse.

c:\Program Files (x86)\SmartDeck\bin\hsim.exe
55  	
	  	
; Simulate OnCreate event in hsim
-apdu BE1600000100

; Select application
-apdu 00A404000155

; Verify PIN
-apdu 000100000431323334

; Verify incorrectly 3 times
-apdu 000100000431323335
-apdu 00010000053132333435
-apdu 0001000003313233

; Verify PIN should fail as PTC is exhausted
-apdu 000100000431323334

Additional Static Memory

Assuming sufficient EEPROM / Flash is available, the amount of static memory available to an application is limited by the maximum value of ST for an implementation, STmax (usually 32K bytes). In some applications it may be necessary to have more static memory than that. There are two ways of achieving this:-

  • Specify more data in the ALC than is the ALU or
  • Use a single large static data blob.

For more details see the MULTOS Developers’ Guide in the Technical Library. The following example code makes use of the first option. It shows storing and accessing records held in the area above the normal static variables of the application.

 

/*
 * To show use of Additional Static.
 *
 * A practical application would also need to implement authentication (say a PIN) and encryption of the stored
 * data (where the key is masked).
 *
 */
#pragma attribute("aid", "44")
#pragma attribute("name", "Data Store")
#pragma attribute("access_list", "0007")	// Contactless i/f + Contact i/f + Strong Crypto

#include <multos.h>
#include <ISO7816.h>

// Instructions that the app will handle
#define INS_SET_ENTRY 0x01
#define INS_GET_ENTRY 0x02
#define INS_CLEAR_ENTRY 0x03
#define INS_COPY_ENTRY 0x04

#define RECORD_SIZE 8

#pragma melpublic
BYTE abPublic[RECORD_SIZE];

#pragma melstatic
WORD wNumRecords = 0;

// The base of the additional static space is immediately after the normal static, including anything
// added by the compiler. Use hls -t on the .hzx file to check the size of .SB
#define AS_BASE 2

void main(void)
{
	DWORD dwStaticSize;
	WORD wTarget;

	// If the number of records in the store has not yet been calculated
	if (wNumRecords == 0)
	{
		// Get the total amount of static memory actually allocated to the application (part of the Open MEL Application command)
		multosGetStaticSize(&dwStaticSize);

		wNumRecords = (dwStaticSize - AS_BASE) / RECORD_SIZE;
	}

	if (CLA != 0x80)
		multosExitSW(ISO7816_SW_CLA_NOT_SUPPORTED);

	// Records in additional static are indexed from zero
	switch(INS)
	{
		case INS_SET_ENTRY:
			// Check command structure
			if (!multosCheckCase(3))
				multosExitSW(ISO7816_SW_DATA_INVALID);
			if (Lc != RECORD_SIZE)
				multosExitSW(ISO7816_SW_WRONG_LENGTH);
			if (P1P2 >= wNumRecords)
				multosExitSW(ISO7816_SW_WRONG_P1P2);

			// Copy from public to the additional static at the required offset
			multosCopyToAdditionalStatic(abPublic, AS_BASE + (RECORD_SIZE * P1P2), RECORD_SIZE);
			break;

		case INS_GET_ENTRY:
			// Check command structure
			if (!multosCheckCase(2))
				multosExitSW(ISO7816_SW_DATA_INVALID);
			if (Le != RECORD_SIZE)
				multosExitSW(ISO7816_SW_WRONG_LENGTH);
			if (P1P2 >= wNumRecords)
				multosExitSW(ISO7816_SW_WRONG_P1P2);

			// Copy from additional static to public
			multosCopyFromAdditionalStatic(AS_BASE + (RECORD_SIZE * P1P2), abPublic, RECORD_SIZE);
			La = RECORD_SIZE;
			break;

		case INS_COPY_ENTRY: // Source is in P1P2, target is in first two bytes of Public
			// Check command structure
			if (!multosCheckCase(3))
				multosExitSW(ISO7816_SW_DATA_INVALID);
			if (Lc != 2)
				multosExitSW(ISO7816_SW_WRONG_LENGTH);
			if (P1P2 >= wNumRecords)
				multosExitSW(ISO7816_SW_WRONG_P1P2);

			// Check target
			wTarget = abPublic[0] * 256 + abPublic[1];
			if (wTarget >= wNumRecords)
				multosExitSW(ISO7816_SW_WRONG_DATA);

			// Copy from additional to additional
			multosCopyWithinAdditionalStatic(AS_BASE + (RECORD_SIZE * P1P2), AS_BASE + (RECORD_SIZE * wTarget), RECORD_SIZE);
			break;

		case INS_CLEAR_ENTRY:
			// Check command structure
			if (!multosCheckCase(1))
				multosExitSW(ISO7816_SW_DATA_INVALID);
			if (P1P2 >= wNumRecords)
				multosExitSW(ISO7816_SW_WRONG_P1P2);

			// Set record to all zeros
			multosFillAdditionalStatic(AS_BASE + (RECORD_SIZE * P1P2), RECORD_SIZE, 0x00);
			break;

		default:
			multosExitSW(ISO7816_SW_INS_NOT_SUPPORTED);
	}
}

The following debugging file can be used to run the application in Eclipse.

c:\Program Files (x86)\SmartDeck\bin\hsim.exe
44	  	
; Allocate 200 x 255 byte blocks of additional static
-addStatic 200
-selectaid 44
-ds 5000

; Set record 16
-apdu 80010010080102030405060708

; Copy to record 32
-apdu 80040010020020

; Get record 32
-apdu 8002002008

; Clear record 32
-apdu 80030020

; Get record 32
-apdu 8002002008


ECC Signatures

The following “bare bones” example shows how to call the ECC primitives to create and verify signatures using the ECDSA algorithm.

 

#pragma attribute("aid", "11")
#pragma attribute("name", "ECC")

#include <multos.h>
#include <ISO7816.h>

#define multosEccVerifyPoint(domainAddr, pointAddr, option, success) \
do \
{ \
	__push(__typechk(BYTE *, domainAddr)); \
	__push(__typechk(BYTE *, pointAddr)); \
	__code (__PRIM, 0xD1, option); \
	__code (__PRIM, __PRIM_LOAD_CCR); \
	__code (__PRIM, __PRIM_BIT_MANIPULATE_BYTE, (0x80), 0x01); \
	__code (__STORE, __typechk(BOOL*, success), 1); \
} while (0)

// Key sizes
#define HASH_LEN 32
#define PRIME_LEN 32
#define BUF_LEN (PRIME_LEN*3)

// Supported instructions
#define INS_INIT 0x00
#define INS_SIGN 0x01
#define INS_VERIFY 0x02

// Structures for keys
typedef struct
{
	BYTE x[PRIME_LEN];
	BYTE y[PRIME_LEN];
} ecc_public_s;

typedef struct
{
	ecc_public_s publicKey;
	BYTE privateKey[PRIME_LEN];
} ecc_s;

typedef struct
{
	BYTE r[PRIME_LEN];
	BYTE s[PRIME_LEN];
} ecc_sig_s;

typedef struct
{
	BYTE representation;
	BYTE x[PRIME_LEN];
	BYTE y[PRIME_LEN];
	BYTE z[PRIME_LEN];
}ecc_point_s;

#pragma melpublic
union {
	BYTE data[255];
	ecc_public_s externalPublicKey;
	ecc_sig_s signature;
} public;

#pragma melstatic
// CURVE25519
BYTE domainParams[] = {
 0x00 , // Format
 0x20 , // Prime length
 //P =  7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED
 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xED, //P
 //A =  2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144
 0x2A,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0x98,0x49,0x14,0xA1,0x44, //A
 //B =  7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864
 0x7B,0x42,0x5E,0xD0,0x97,0xB4,0x25,0xED,0x09,0x7B,0x42,0x5E,0xD0,0x97,0xB4,0x25,0xED,0x09,0x7B,0x42,0x5E,0xD0,0x97,0xB4,0x26,0x0B,0x5E,0x9C,0x77,0x10,0xC8,0x64, //B
 //Gx = 2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A
 0x2A,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAD,0x24,0x5A, // Gx
 //Gy = 20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9
 0x20,0xAE,0x19,0xA1,0xB8,0xA0,0x86,0xB4,0xE0,0x1E,0xDD,0x2C,0x77,0x48,0xD1,0x4C,0x92,0x3D,0x4D,0x7E,0x6D,0x7C,0x61,0xB2,0x29,0xE9,0xC5,0xA2,0x7E,0xCE,0xD3,0xD9, // Gy
 //N =  1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED
 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0xDE,0xF9,0xDE,0xA2,0xF7,0x9C,0xD6,0x58,0x12,0x63,0x1A,0x5C,0xF5,0xD3,0xED, // N
 0x08  // H
};

ecc_s eccKeyPair;
WORD keyBitLen; // Bit length of N

#pragma melsession
ecc_public_s externalPublicKey;
BYTE hashToVerify[HASH_LEN];

// ******************* Local functions ********************************
static WORD bitLength(BYTE *buff, BYTE numBytes)
{
	BYTE i,j;
	BYTE testPattern;
	BYTE toCheck;
	BOOL oneFound;
	WORD bitLen = numBytes * 8;
	
	for(i = 0; i < numBytes && !oneFound; i++)
	{
		testPattern = 0x80;
		toCheck = buff[i];
		for(j = 0;j < 8 && !oneFound; j++)
		{
			if(!(toCheck & testPattern))
			{
				bitLen--;
				testPattern = testPattern >> 1;
			}
			else
				oneFound = TRUE;
		}
	}
	return(bitLen);
}

// ************************ MAIN ********************************

void
main(void)
{
	BYTE hash[HASH_LEN];
	WORD hashBitLen;
	ecc_sig_s signature;
	BOOL ok,supported;
	ecc_point_s point;

	if(INS == INS_INIT)
	{
		// Data OUT
		if (!multosCheckCase(2))
			multosExitSW(ISO7816_SW_DATA_INVALID);

		// Calculate the number of bits in N parameter
		keyBitLen = bitLength(domainParams+(5*PRIME_LEN)+2,PRIME_LEN);

		// Private key is protected - clear value not accessible by app
		if (multosEccGenerateKeyPairProtected((BYTE*)&eccKeyPair,domainParams))
		{
			// Output the public key
			multosCopy((BYTE*)&(eccKeyPair.publicKey), (BYTE*)&(public.externalPublicKey),sizeof(ecc_public_s));
			La = sizeof(ecc_public_s);
			multosExitSW(ISO7816_SW_NO_ERROR);
		}
		else
			multosExitSW(ISO7816_SW_UNKNOWN);
	}

	if (keyBitLen == 0)
		multosExitSW(ISO7816_SW_CONDITIONS_NOT_SATISFIED);

	if(INS == INS_SIGN)
	{
		// Data IN / OUT
		if (!multosCheckCase(4))
			multosExitSW(ISO7816_SW_DATA_INVALID);

		// Hash the data
		multosSecureHash(public.data,hash,sizeof(hash),Lc);

		// Apply FIPS180 truncation to the hash
		hashBitLen = bitLength(hash,HASH_LEN);
		if(hashBitLen > keyBitLen)
			multosShiftRight(public.data,Lc,hashBitLen - keyBitLen);

		// Sign with private key (maintaining protection)
		if(multosECDSAProtected((BYTE*)&signature,hash+(HASH_LEN-PRIME_LEN),eccKeyPair.privateKey,domainParams))
		{
			// Copy signature to the output
			multosCopyFixedLength(sizeof(signature),(BYTE*)&signature,(BYTE*)(&public.signature));
			La = sizeof(signature);
		}
		else
			multosExitSW(ISO7816_SW_UNKNOWN);
	}
	else if (INS == INS_VERIFY)
	{
		// Data IN
		if (!multosCheckCase(3))
			multosExitSW(ISO7816_SW_DATA_INVALID);

		if (P1 == 0) // Public key to use
		{
			// Verify that the external public key is a valid point on the curve (if check supported)
			multosQueryPrimitive(1,__PRIM_ECC_VERIFY, &supported);
			if (supported)
			{
				point.representation = 0x04; //Affine
				multosCopy(public.externalPublicKey.x,point.x,PRIME_LEN);
				multosCopy(public.externalPublicKey.y,point.y,PRIME_LEN);
				multosEccVerifyPoint(domainParams,(BYTE*)&point,0,&ok);
				if (!ok)
					multosExitSW(ISO7816_SW_UNKNOWN);
			}
			multosCopy((BYTE*)&(public.externalPublicKey),(BYTE*)&externalPublicKey,Lc);
		}
		else if (P1 == 1) // Hash to verify
		{
			multosCopy(public.data, hashToVerify, HASH_LEN);
		}
		else if (P1 == 2) // Signature to verify
		{
			// Verify
			if(multosECDSAVerify(hashToVerify+(HASH_LEN-PRIME_LEN),(BYTE*)&(public.signature),(BYTE*)&externalPublicKey,domainParams))
				multosExitSW(ISO7816_SW_NO_ERROR);
			else
				multosExitSW(ISO7816_SW_WRONG_DATA);
		}
	}
	else
		multosExitSW(ISO7816_SW_INS_NOT_SUPPORTED);
}

Transactions

The Transaction mechanism allows for writes to static to be buffered in RAM such that either all of the writes happen or none of them do. This ensures atomicity in the processing of a command when required. The pending changes are not visible to the running application with the exception of the MULTOS Checksum primitive, which will take in to account pending static writes when calculating the checksum.

Another important use for transactions is to increase performance. Writing to static involves a whole processor page being erased and rewritten regardless of how many bytes are changed within that page, so it makes sense to update all the bytes that need changing in a single page write. The maximum size of a transaction is limited by the amount of available RAM.

The following code demonstrates how pending changes are hidden by the app but visible to the checksum primitive.

#pragma attribute("aid", "11")
#pragma attribute("name", "transactions")

#include <multos.h>

#pragma melpublic
struct  {
	DWORD before;
	DWORD during;
	DWORD after;
	BYTE data5During;
	BYTE data5After;
} pub;

#pragma melstatic
BYTE data[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

void main(void)
{
	multosCheckCase(2);

	// Reset data to default values. Each update requires a page write.
	data[3] = 4;
	data[5] = 6;
	data[9] = 10;

	// Get the checksum of the data before it is changed
	pub.before = multosChecksum(data,sizeof(data));

	// Switch on transaction buffering
	multosSetTransactionProtection(MULTOS_TP_ON_AND_DISCARD);

	// Change some of the data and recalculate the checksum. The checksum
	// calculation will take the pending changes in to account but the application
	// code will still see the original, unchanged values as the changes haven't been committed
	// from RAM to static yet.
	data[3] = 42;
	data[5] = 99;
	data[9] = 32;
	pub.during = multosChecksum(data,sizeof(data));
	pub.data5During = data[5];

	// Commit the transaction; changes buffered in RAM get written to static.
	multosSetTransactionProtection(MULTOS_TP_OFF_AND_COMMIT);

	// The checksum will be the same as that calculated whilst the transaction was in progress
	pub.after = multosChecksum(data,sizeof(data));

	// The static data will have updated
	pub.data5After = data[5];

	La = sizeof(pub);
}