/** * @file ex_gpstorage.c * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2016 NXP * SPDX-License-Identifier: Apache-2.0 * * @par Description * Example invocations of general purpose storage related functionality of the A71CH */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> // #include "a70cm_configuration.h" #include "a71ch_ex.h" #include "ax_util.h" #include "a71_debug.h" #include "sm_types.h" #include "sm_apdu.h" #include "tst_sm_util.h" #include "tst_a71ch_util.h" #include "nxLog_hostLib.h" #define EX_RND_DATA 0x00 //!< Bit position 0 decides on random or incrementing data payload #define EX_INC_DATA 0x01 //!< Bit position 0 decides on random or incrementing data payload #define EX_ALL_PACKET_SIZES 0x02 //!< Bit position 1 decides on all or a selection of packetsizes /****************************************************************************** * test *****************************************************************************/ static U8 exGpStoragePacketSize(U8 initMode, U8 tstMode); static U8 exGpStorageFreeze(U8 initMode, U8 tstMode); static U8 exMonotonicCounter(U8 initMode); /** * Demonstrate general purpose storage functionality * - ::exGpStoragePacketSize * - ::exGpStorageFreeze * * Demonstrate monotonic counter usage * - ::exMonotonicCounter */ U8 exGPStorage(U8 tstMode) { U8 result = 1; LOG_I( "-----------Start exGPStorage(%s)------------", ((tstMode & EXTENDED_TEST) == EXTENDED_TEST) ? "Extended Test" : "Fast Test"); // No channel encryption DEV_ClearChannelState(); result &= exMonotonicCounter(INIT_MODE_RESET); result &= exGpStoragePacketSize(INIT_MODE_RESET, EX_RND_DATA); result &= exGpStoragePacketSize(INIT_MODE_NO_RESET, EX_INC_DATA); if ((tstMode & EXTENDED_TEST) == EXTENDED_TEST) { result &= exGpStoragePacketSize(INIT_MODE_NO_RESET, EX_RND_DATA|EX_ALL_PACKET_SIZES); } result &= exGpStorageFreeze(INIT_MODE_RESET, EX_INC_DATA); // Use channel encryption result &= exMonotonicCounter(INIT_MODE_RESET_DO_SCP03); result &= exGpStoragePacketSize(INIT_MODE_NO_RESET, EX_RND_DATA); LOG_I( "-----------End exGPStorage(%s), result = %s------------", ((tstMode & EXTENDED_TEST) == EXTENDED_TEST) ? "Extended Test" : "Fast Test", ((result == 1)? "OK": "FAILED")); return result; } /** * Demonstrate monotonic counter usage. * * @param[in] initMode Visit the documentation of ::a71chInitModule for more information on this parameter */ static U8 exMonotonicCounter(U8 initMode) { U8 result = 1; U8 index = 0; U16 err; U32 tgtValue[] = {0x00000004, 0x00000014, 0x00000024, 0x00000034}; U32 readValue = 0; LOG_I("-----------Start exMonotonicCounter(%s)------------", getInitModeAsString(initMode)); // Initialize the A71CH (Debug mode restrictions may apply) result &= a71chInitModule(initMode); assert(result); // Check all counters default to value 0 for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_GetCounter(index=0x%02x)", index); err = A71_GetCounter(index, &readValue); result &= AX_CHECK_SW(err, SW_OK, "Failed to retrieve counter value"); if (err == SW_OK) { if (readValue != 0) { LOG_E("Failed to retrieve expected counter value (index=0x%02x): %ld != 0", index, readValue); result &= 0; } } } // Set all counters to a counter specific target value for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_SetCounter(index=0x%02x, %ld)", index, tgtValue[index]); err = A71_SetCounter(index, tgtValue[index]); result &= AX_CHECK_SW(err, SW_OK, "Failed to set counter value"); } // Verify all counters were set to target value for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_GetCounter(index=0x%02x)", index); err = A71_GetCounter(index, &readValue); result &= AX_CHECK_SW(err, SW_OK, "Failed to retrieve counter value"); if (err == SW_OK) { if (readValue != tgtValue[index]) { LOG_E("Failed to retrieve expected counter value (index=0x%02x): %ld != %ld", index, readValue, tgtValue[index]); result &= 0; } } } // Increment all counters with one for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_IncrementCounter(index=0x%02x)", index); err = A71_IncrementCounter(index); result &= AX_CHECK_SW(err, SW_OK, "Failed to increment counter value"); } // Verify all counters have target value for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_GetCounter(index=0x%02x)", index); err = A71_GetCounter(index, &readValue); result &= AX_CHECK_SW(err, SW_OK, "Failed to retrieve counter value"); if (err == SW_OK) { if (readValue != (tgtValue[index]+1)) { LOG_E("Failed to retrieve expected counter value (index=0x%02x): %ld != %ld", index, readValue, (tgtValue[index]+1)); result &= 0; } } } // Now erase all counters and check whether they are back to default value 0 for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_DbgEraseCounter(index=0x%02x)", index); err = A71_DbgEraseCounter(index); result &= AX_CHECK_SW(err, SW_OK, "Failed to erase counter value"); } for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_GetCounter(index=0x%02x)", index); err = A71_GetCounter(index, &readValue); result &= AX_CHECK_SW(err, SW_OK, "Failed to retrieve counter value"); if (err == SW_OK) { if (readValue != 0) { LOG_E("Failed to retrieve expected counter value (index=0x%02x): %ld != 0", index, readValue); result &= 0; } } } // Set all counters to a counter specific target value & increment the value & freeze the counter for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_SetCounter(index=0x%02x, %ld)", index, tgtValue[index]); err = A71_SetCounter(index, tgtValue[index]); result &= AX_CHECK_SW(err, SW_OK, "Failed to set counter value"); LOG_I("A71_IncrementCounter(index=0x%02x)", index); err = A71_IncrementCounter(index); result &= AX_CHECK_SW(err, SW_OK, "Failed to increment counter value"); } // Check value just set for (index=0; index<A71CH_COUNTER_MAX; index++) { LOG_I("A71_GetCounter(index=0x%02x)", index); err = A71_GetCounter(index, &readValue); result &= AX_CHECK_SW(err, SW_OK, "Failed to retrieve counter value"); if (err == SW_OK) { if (readValue != (tgtValue[index]+1)) { LOG_E("Failed to retrieve expected counter value (index=0x%02x): %ld != %ld", index, readValue, (tgtValue[index]+1)); result &= 0; } } // LOG_I("A71_IncrementCounter(index=0x%02x) - negative test", index); // err = A71_IncrementCounter(index); // result &= AX_CHECK_SW(err, SW_COMMAND_NOT_ALLOWED, "Increment frozen counter must fail"); } LOG_I("-----------End exMonotonicCounter(%s), result = %s------------", getInitModeAsString(initMode), ((result == 1)? "OK": "FAILED")); return result; } /** * Demonstrate reading and writing to GP Storage. * * @param[in] initMode Visit the documentation of ::a71chInitModule for more information on this parameter * @param[in] tstMode Influence test execution through this parameter */ static U8 exGpStoragePacketSize(U8 initMode, U8 tstMode) { U16 testSizes[] = {60, 200, 300, 400, 500, 600, 700, 800, 900, 1000, A71CH_GP_STORAGE_SIZE}; U8 gpStorageRef[A71CH_GP_STORAGE_SIZE]; U8 gpStorage[A71CH_GP_STORAGE_SIZE]; int i = 0; U16 packetSize = 0; int maxIter = sizeof(testSizes)/sizeof(U16); U8 result = 1; U16 err; LOG_I( "-----------Start exGpStoragePacketSize(%s)------------", getInitModeAsString(initMode)); if ((tstMode & EX_INC_DATA) == EX_INC_DATA) { for (i=0; i<A71CH_GP_STORAGE_SIZE; i++) { gpStorageRef[i] = (U8)i; } } else { // Create reference array containing random values srand(0); for (i=0; i<A71CH_GP_STORAGE_SIZE; i++) { gpStorageRef[i] = (U8)rand(); } } // Initialize the A71CH (Debug mode restrictions may apply) result &= a71chInitModule(initMode); assert(result); if ((tstMode & EX_ALL_PACKET_SIZES) == EX_ALL_PACKET_SIZES) { // Test all packetsizes in read and write mode for (packetSize = 1; packetSize <= A71CH_GP_STORAGE_SIZE; packetSize++) { if ((tstMode & EX_INC_DATA) == EX_RND_DATA) { // In case of random data, recreate the reference array srand(0); for (i=0; i<packetSize; i++) { gpStorageRef[i] = (U8)rand(); } } LOG_I( "A71_SetGpData(0, %d, ...)", packetSize); err = A71_SetGpData(0, gpStorageRef, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); LOG_I( "A71_GetGpData(0, %d, ...)", packetSize); err = A71_GetGpData(0, gpStorage, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY( "gpStorageRef", gpStorageRef, packetSize, "gpStorage", gpStorage, packetSize, AX_COLON_32); } } else { // Always start from offset 0 for (i = 0; i < maxIter; i++) { LOG_I( "A71_SetGpData(0, %d, ...)", testSizes[i]); err = A71_SetGpData(0, gpStorageRef, testSizes[i]); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); LOG_I( "A71_GetGpData(0, %d, ...)", testSizes[i]); err = A71_GetGpData(0, gpStorage, testSizes[i]); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY("gpStorageRef", gpStorageRef, testSizes[i], "gpStorage", gpStorage, testSizes[i], AX_COLON_32); } } LOG_I( "-----------End exGpStoragePacketSize(%s), result = %s------------", getInitModeAsString(initMode), ((result == 1)? "OK": "FAILED")); return result; } /** * Demonstrate freezing of GP Storage data chunks. * GP Storage data can be frozen with a granularity of ::A71CH_GP_STORAGE_GRANULARITY byte * * @param[in] initMode Visit the documentation of ::a71chInitModule for more information on this parameter * @param[in] tstMode Influence test execution through this parameter */ static U8 exGpStorageFreeze(U8 initMode, U8 tstMode) { #if (A71CH_GP_STORAGE_SIZE == A71CH_GP_STORAGE_SIZE_A) U16 testSizes[] = {60, 200, 300, 400, 500, 600, 700, 800, 900, 1000, A71CH_GP_STORAGE_SIZE}; #else U16 testSizes[] = {60, 200, 300, 400, 500, 600, 1000, 1500, 2000, 3000, A71CH_GP_STORAGE_SIZE}; #endif U8 gpStorageRef[A71CH_GP_STORAGE_SIZE]; U8 gpStorageNew[A71CH_GP_STORAGE_SIZE]; U8 gpStorage[A71CH_GP_STORAGE_SIZE]; U8 gpStorageExpected[A71CH_GP_STORAGE_SIZE]; int i = 0; U16 packetSize = 0; U16 offset = 0; int maxIter = sizeof(testSizes)/sizeof(U16); U8 result = 1; U16 err; LOG_I( "-----------Start exGpStorageFreeze(%s, A71CH_GP_STORAGE_SIZE=%d)------------", getInitModeAsString(initMode), A71CH_GP_STORAGE_SIZE); if ((tstMode & EX_INC_DATA) == EX_INC_DATA) { for (i=0; i<A71CH_GP_STORAGE_SIZE; i++) { gpStorageRef[i] = (U8)i; } } else { // Create reference array containing random values srand(0); for (i=0; i<A71CH_GP_STORAGE_SIZE; i++) { gpStorageRef[i] = (U8)rand(); } } for (i=0; i<A71CH_GP_STORAGE_SIZE; i++) { gpStorageNew[i] = 0xAA; } // Initialize the A71CH (Debug mode restrictions may apply) result &= a71chInitModule(initMode); assert(result); if ((tstMode & EX_ALL_PACKET_SIZES) == EX_ALL_PACKET_SIZES) { // Test all packetsizes in read and write mode for (packetSize = 1; packetSize <= A71CH_GP_STORAGE_SIZE; packetSize++) { if ((tstMode & EX_INC_DATA) == EX_RND_DATA) { // In case of random data, recreate the reference array srand(0); for (i=0; i<packetSize; i++) { gpStorageRef[i] = (U8)rand(); } } LOG_I( "A71_SetGpData(0, %d, ...)", packetSize); err = A71_SetGpData(0, gpStorageRef, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); LOG_I( "A71_GetGpData(0, %d, ...)", packetSize); err = A71_GetGpData(0, gpStorage, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY("gpStorageRef", gpStorageRef, packetSize, "gpStorage", gpStorage, packetSize, AX_COLON_32); } } else { // Always start from offset 0 for (i = 0; i < maxIter; i++) { LOG_I( "A71_SetGpData(0, %d, ...)", testSizes[i]); err = A71_SetGpData(0, gpStorageRef, testSizes[i]); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); LOG_I( "A71_GetGpData(0, %d, ...)", testSizes[i]); err = A71_GetGpData(0, gpStorage, testSizes[i]); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY("gpStorageRef", gpStorageRef, testSizes[i], "gpStorage", gpStorage, testSizes[i], AX_COLON_32); } } // Just fill up the full GpStorage with reference data packetSize = A71CH_GP_STORAGE_SIZE; LOG_I( "A71_SetGpData(0, %d, ...)", packetSize); err = A71_SetGpData(0, gpStorageRef, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); // Check whether data was written successfully LOG_I( "A71_GetGpData(0, %d, ...)", packetSize); err = A71_GetGpData(0, gpStorage, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY("gpStorageRef", gpStorageRef, packetSize, "gpStorage", gpStorage, packetSize, AX_COLON_32); // Lock the first half of GP storage LOG_I( "A71_FreezeGpData(offset=%d, dataLen=%d, ...)", 0, A71CH_GP_STORAGE_SIZE>>1); err = A71_FreezeGpData(0, A71CH_GP_STORAGE_SIZE>>1); result &= AX_CHECK_SW(err, SW_OK, "A71_FreezeGpData fails"); // Attempt to write in the frozen area packetSize = 16; offset = 0; LOG_I( "A71_SetGpData(%d, %d, ...)", offset, packetSize); err = A71_SetGpData(offset, gpStorageNew, packetSize); result &= AX_CHECK_SW(err, SW_COMMAND_NOT_ALLOWED, "A71_SetGpData was expected to fail (because area has just been locked)"); // Write to open area offset = A71CH_GP_STORAGE_SIZE>>1; LOG_I( "A71_SetGpData(%d, %d, ...)", offset, packetSize); err = A71_SetGpData(offset, gpStorageNew, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_SetGpData fails"); // Create expected data pattern memcpy(gpStorageExpected, gpStorageRef, A71CH_GP_STORAGE_SIZE); memcpy(gpStorageExpected+(A71CH_GP_STORAGE_SIZE>>1), gpStorageNew, 16); // Retrieve data and compare with expected data pattern packetSize = A71CH_GP_STORAGE_SIZE; LOG_I( "A71_GetGpData(0, %d, ...)", packetSize); err = A71_GetGpData(0, gpStorage, packetSize); result &= AX_CHECK_SW(err, SW_OK, "A71_GetGpData fails"); result &= AX_COMPARE_BYTE_ARRAY("gpStorageExpected", gpStorageExpected, A71CH_GP_STORAGE_SIZE, "gpStorage", gpStorage, A71CH_GP_STORAGE_SIZE, AX_COLON_32); LOG_I( "-----------End exGpStorageFreeze(%s), result = %s------------", getInitModeAsString(initMode), ((result == 1)? "OK": "FAILED")); return result; }