/** * @file axCliUtil.c * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2017 NXP * SPDX-License-Identifier: Apache-2.0 * * @par Description * Implementation of command line utility functions */ #include #include #include #include // project specific include files #include "sm_types.h" // #include "sm_apdu.h" #include "axCliUtil.h" #include "tst_sm_util.h" #define FLOW_VERBOSE_PROBE_A70 #ifdef FLOW_VERBOSE_PROBE_A70 #define FPRINTF(...) printf (__VA_ARGS__) #else #define FPRINTF(...) #endif // #define DBG_AX_CLI_UTIL #ifdef DBG_AX_CLI_UTIL #define DBGPRINTF(...) printf (__VA_ARGS__) #else #define DBGPRINTF(...) #endif #define AX_CLI_GET_MODE_OPTIONAL 0 #define AX_CLI_GET_MODE_STRICT 1 #define CMD_HANDLER_MAX 20 #define CMD_NAME_MAX 64 typedef int (*a7xCmdHandler_t) (int argc, char ** argv); typedef struct { char szName[CMD_NAME_MAX]; a7xCmdHandler_t cmdHandler; } a7xCmd_t; int a7xAddCmd(char *szName, a7xCmdHandler_t cmdHandler); int a7xRemoveCmd(char *szName); static int nCmd = 0; static a7xCmd_t a7xCmd[CMD_HANDLER_MAX]; static int axCliGetString_Internal(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent, int mode); int a7xAddCmd(char *szName, a7xCmdHandler_t cmdHandler) { if (nCmd == CMD_HANDLER_MAX) { return -1; } if (strlen(szName) >= (CMD_NAME_MAX-1) ) { return -1; } strcpy(a7xCmd[nCmd].szName, szName); a7xCmd[nCmd].cmdHandler = cmdHandler; nCmd++; return 0; } char *axGetErrorString(int errorCode) { switch(errorCode) { case AX_CLI_EXEC_OK: return "exec ok"; case AX_CLI_EXEC_FAILED: return "exec failed"; case AX_CLI_ERR_IP_ADR_MISSING: return "err ip adr missing"; case AX_CLI_ERR_CANNOT_CONNECT: return "err cannot connect"; case AX_CLI_ERR_SELECT_FAILS: return "err select fails"; case AX_CLI_NOT_IMPLEMENTED: return "not implemented"; case AX_CLI_CHECK_USAGE: return "check usage"; case AX_CLI_PEM_CONVERT_FAILED: return "pem covert failed"; case AX_CLI_FILE_OPEN_FAILED: return "file open failed"; case AX_CLI_FILE_PEM_READ_FAILED: return "pemfile read failed"; case AX_CLI_FILE_PEM_WRITE_FAILED: return "pemfile write failed"; case AX_CLI_BIT_CURVE_ERROR: return "bit curve error"; case AX_CLI_DYN_ALLOC_ERROR: return "dyn alloc error"; case AX_CLI_EXEC_HALTED: return "exec halted"; case AX_CLI_FILE_FORMAT_ERROR: return "file format error"; case AX_CLI_ARG_RANGE_ERROR: return "arg range error"; case AX_CLI_ARG_NAME_ERROR: return "arg name error"; case AX_CLI_ARG_OPTION_ERROR: return "arg option error"; case AX_CLI_ARG_COUNT_MISTAKE: return "arg count mistake"; case AX_CLI_ARG_VALUE_ERROR: return "arg value error"; case AX_CLI_BUFFER_SIZE_ERROR: return "buffer size error"; case AX_CLI_API_ERROR: return "api error"; case AX_CLI_WRAP_ERROR: return "wrap error"; case AX_CLI_NO_OBJECTS: return "enumeration no object found error"; case AX_CLI_OBJECT_NOT_FOUND: return "object at index not found error"; case AX_CLI_WRITE_CER_FILE_ERROR: return "write certificate file error"; case AX_CLI_ERASE_CER_FILE_ERROR: return "erase certificate file error"; case AX_CLI_UPDATE_CER_FILE_ERROR: return "update certificate file error"; default: return "unknown"; } } /** * Extract an integer value from argument array in case of matching option * \pre argCurrent points to correct position (argument option to be handled) * \post argCurrent points to next argument option to be handled. * * \note szLongOption not yet supported * * @param[in] szShortOption Name of option (excluding '-' character) * @param[in] szLongOption * @param[in,out] value Integer value to be retrieved (must be a base 10 number) * @param[in] minVal Minimum value (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value) * @param[in] maxVal Maximum value (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value) * @param[in] argc maximum amount of arguments passed as argument * @param[in] argv argument array * @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array) * * @retval ::AX_CLI_EXEC_OK Upon successful execution */ int axCliGetInteger(char *szShortOption, char *szLongOption, int *value, int minVal, int maxVal, int argc, char ** argv, int *argCurrent) { long longTmp = 0L; char szDummy[] = "szDummy"; char *pastConverted = szDummy; // Catch number conversion issues char szShort[64]; // Do not go beyond the last argument when parsing if ((*argCurrent + 1) >= argc) { return AX_CLI_ARG_COUNT_MISTAKE; } // Long option not yet supported if (strcmp(szLongOption, "") != 0) { return AX_CLI_NOT_IMPLEMENTED; } szShort[0] = '-'; strcpy(&szShort[1], szShortOption); if (strcmp(argv[*argCurrent], szShort) == 0) { longTmp = strtol(argv[*argCurrent+1], &pastConverted, 10); if (pastConverted == argv[*argCurrent+1]) { // Conversion failed printf("%s %s: %s is not an integer\n", szShort, argv[*argCurrent+1], argv[*argCurrent+1]); return AX_CLI_ARG_VALUE_ERROR; } else { if (longTmp < minVal) { return AX_CLI_ARG_RANGE_ERROR; } if (longTmp > maxVal) { return AX_CLI_ARG_RANGE_ERROR; } *value = (int)longTmp; DBGPRINTF("axCliGetInteger: %s option with %d\n", szShort, *value); } } else { printf("Argument %s not expected\n", argv[*argCurrent]); return AX_CLI_ARG_OPTION_ERROR; } // Upon success. (*argCurrent) += 2; return AX_CLI_EXEC_OK; } /** * Extract a hexadecimal (ASCII) string from argument array in case of matching option, * store it as a binary array * \pre argCurrent points to correct position (argument option to be handled) * \post argCurrent points to next argument option to be handled. * * \note szLongOption not yet supported * * @param[in] szShortOption Name of option (excluding '-' character) * @param[in] szLongOption Pointer to the provided hash (or any other bytestring). * @param[in,out] hex IN: Buffer to contain hex value; OUT: hex value retrieved * @param[in,out] hexLen IN: Buffersize; OUT: Actual amount of byte retrieved * @param[in] minLen Minimum length in byte (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value) * @param[in] maxLen Maximum length in byte (values outside range cause a ::AX_CLI_ARG_RANGE_ERROR return value) * @param[in] argc maximum amount of arguments passed as argument * @param[in] argv argument array * @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array) * * @retval ::AX_CLI_EXEC_OK Upon successful execution */ int axCliGetHexString(char *szShortOption, char *szLongOption, U8 *hex, U16 *hexLen, int minLen, int maxLen, int argc, char ** argv, int *argCurrent) { char szShort[64]; // Do not go beyond the last argument when parsing if ((*argCurrent + 1) >= argc) { return AX_CLI_ARG_COUNT_MISTAKE; } // Long option not yet supported if (strcmp(szLongOption, "") != 0) { return AX_CLI_NOT_IMPLEMENTED; } szShort[0] = '-'; strcpy(&szShort[1], szShortOption); if (strcmp(argv[*argCurrent], szShort) == 0) { // Check Hex String Argument size. The count of ASCII characters is double the byte count int nLen = (int)strlen(argv[*argCurrent+1]); if ( (nLen >= (minLen << 1)) && (nLen <= (maxLen << 1)) && ((nLen % 2) == 0 ) ) { int nRet = 0; nRet = axConvertHexString2ByteArray(hex, argv[*argCurrent+1], 0, (int)(nLen >> 1)); *hexLen = (U16)(nLen >> 1); if (nRet == AX_UTIL_OK) { #ifdef DBG_AX_CLI_UTIL axPrintByteArray("hex", hex, *hexLen, AX_COMPACT_32); #endif } else { printf("axCliGetHexString: Cannot convert %s to HEX.\n", argv[*argCurrent+1]); return AX_CLI_ARG_VALUE_ERROR; } } else { printf("axCliGetHexString: argument has wrong length: %s\n\tlen=%d char\'s\n", argv[*argCurrent+1], (int)nLen); printf("\tValid range: [%d:%d]\n", minLen << 1, maxLen << 1); // DBGPRINTF("nLen = %d\n", nLen); // DBGPRINTF("minLen << 1 = %d\n", ); // DBGPRINTF("maxLen << 1 = %d\n",); return AX_CLI_ARG_VALUE_ERROR; } } else { DBGPRINTF("Argument %s not expected\n", argv[*argCurrent]); return AX_CLI_ARG_OPTION_ERROR; } // Upon success. (*argCurrent) += 2; return AX_CLI_EXEC_OK; } /** * Extract a string from argument array in case of matching option. * Don't echo a warning to stdout when the option is missing * \pre argCurrent points to correct position (argument option to be handled) * \post argCurrent points to next argument option to be handled. * * \note szLongOption not yet supported * * @param[in] szShortOption Name of option (excluding '-' character) * @param[in] szLongOption Pointer to the provided hash (or any other bytestring). * @param[in,out] szString IN: Buffer to contain string; OUT: string retrieved * @param[in,out] bufLen IN: Buffersize of szString * @param[in] argc maximum amount of arguments passed as argument * @param[in] argv argument array * @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array) * * @retval ::AX_CLI_EXEC_OK Upon successful execution */ int axCliGetOptionalString(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent) { int mode = AX_CLI_GET_MODE_OPTIONAL; return axCliGetString_Internal(szShortOption, szLongOption, szString, bufLen, argc, argv, argCurrent, mode); } /** * Extract a string from argument array in case of matching option * \pre argCurrent points to correct position (argument option to be handled) * \post argCurrent points to next argument option to be handled. * * \note szLongOption not yet supported * * @param[in] szShortOption Name of option (excluding '-' character) * @param[in] szLongOption Pointer to the provided hash (or any other bytestring). * @param[in,out] szString IN: Buffer to contain string; OUT: string retrieved * @param[in,out] bufLen IN: Buffersize of szString * @param[in] argc maximum amount of arguments passed as argument * @param[in] argv argument array * @param[in,out] argCurrent IN: Current argument; OUT: Next argument position (or end of array) * * @retval ::AX_CLI_EXEC_OK Upon successful execution */ int axCliGetString(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent) { int mode = AX_CLI_GET_MODE_STRICT; return axCliGetString_Internal(szShortOption, szLongOption, szString, bufLen, argc, argv, argCurrent, mode); } static int axCliGetString_Internal(char *szShortOption, char *szLongOption, char *szString, int bufLen, int argc, char ** argv, int *argCurrent, int mode) { char szShort[64]; // Do not go beyond the last argument when parsing if ((*argCurrent + 1) >= argc) { return AX_CLI_ARG_COUNT_MISTAKE; } // Long option not yet supported if (strcmp(szLongOption, "") != 0) { return AX_CLI_NOT_IMPLEMENTED; } szShort[0] = '-'; strcpy(&szShort[1], szShortOption); if (strcmp(argv[*argCurrent], szShort) == 0) { // Check Hex String Argument size. The count of ASCII characters is double the byte count int nLen = (int)strlen(argv[*argCurrent+1]); if ( nLen < bufLen) { strcpy(szString, argv[*argCurrent+1]); } else { printf("axCliGetString: illegal argument (too long): %s\n", argv[*argCurrent+1]); return AX_CLI_ARG_VALUE_ERROR; } } else { if (mode != AX_CLI_GET_MODE_OPTIONAL) { printf("Argument %s not expected\n", argv[*argCurrent]); } return AX_CLI_ARG_OPTION_ERROR; } // Upon success. (*argCurrent) += 2; return AX_CLI_EXEC_OK; } /** * Convert a string into an ASCII key (of variable length) and a byte array of * fixed length. One needs to know the length of the expected byte array up front * Symbolic Representation of input string * 00..FF * * @param[in,out] key IN: buffer to contain key; OUT: key retrieved * @param[in] keyBufSize Size of buffer key * @param[in,out] hex IN: buffer to contain hex array of at least size hexLen; OUT: hex array retrieved * @param[in] hexLen IN: expected size of hex array * @param[in] szLine IN: line of text (string) that will be parsed */ int axCliGetKeyFixedLenHexValueFromLine(char *key, int keyBufSize, U8 *hex, U16 hexLen, const char *szLine) { int nTokens = 0; char **myargv; int nRet = AX_CLI_EXEC_FAILED; int nLen = 0; if ((nRet = axMakeArgv(szLine, " \r\n", &myargv, &nTokens)) != AX_CLI_EXEC_OK) { DBGPRINTF("Could not make argument array for %s\n", szLine); return nRet; } if (nTokens == 2) { if ( (int)strlen(myargv[0]) < keyBufSize) { strcpy(key, myargv[0]); if ( (nLen = (int)strlen(myargv[1])) == (hexLen << 1)) { if ( axConvertHexString2ByteArray(hex, myargv[1], 0, hexLen) == AX_UTIL_OK ) { nRet = AX_CLI_EXEC_OK; } else { DBGPRINTF("ASCII-Hex-String %s cannot be converted to hex array.\n", myargv[1]); nRet = AX_CLI_ARG_VALUE_ERROR; } } else { DBGPRINTF("ASCII-Hex-String %s wrong size.\n", myargv[1]); nRet = AX_CLI_ARG_VALUE_ERROR; } } } else { DBGPRINTF("Cannot handle input line: wrong number of tokens: %d\n", nTokens); DBGPRINTF("token[0]: %s\n", myargv[0]); DBGPRINTF("token[1]: %s\n", myargv[1]); nRet = AX_CLI_ARG_COUNT_MISTAKE; } axFreeArgv(myargv); return nRet; } int axMakeArgv(const char *s, const char *delimiters, char ***argvp, int *argc) { int i; int nToken; const char *sStart; char *szCopy; *argc = 0; if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) { return AX_CLI_ARG_VALUE_ERROR; } *argvp = NULL; sStart = s + strspn(s, delimiters); if ((szCopy = (char *)malloc(strlen(sStart) + 1)) == NULL) { return AX_CLI_DYN_ALLOC_ERROR; } strcpy(szCopy, sStart); nToken = 0; // Count the number of tokens in a first run through if (strtok(szCopy, delimiters) != NULL) { for (nToken = 1; strtok(NULL, delimiters) != NULL; nToken++) ; } // Create argument array for ptrs to the tokens if ((*argvp = (char **)malloc((nToken + 1)*sizeof(char *))) == NULL) { free(szCopy); return AX_CLI_DYN_ALLOC_ERROR; } // Insert pointers to tokens into the argument array, they point to segments of the // allocated string (szCopy) if (nToken == 0) { free(szCopy); } else { strcpy(szCopy, sStart); **argvp = strtok(szCopy, delimiters); for (i = 1; i < nToken; i++) { *((*argvp) + i) = strtok(NULL, delimiters); } } // Close pointer array with a final NULL pointer *((*argvp) + nToken) = NULL; *argc = nToken; return AX_CLI_EXEC_OK; } void axFreeArgv(char **argv) { if (argv == NULL) { return; } if (*argv != NULL) { free(*argv); } free(argv); }