diff --git a/README.md b/README.md index 5c5fb209..da64bdfb 100644 --- a/README.md +++ b/README.md @@ -160,9 +160,7 @@ A number of examples are provided with this repo: | Technology | Example | |--------------|----------| | Cellular | The [sockets](/example/sockets "socket example") example brings up a TCP/UDP socket by using the [device](/common/device "device API"), [network](/common/network "network API") and [sock](/common/sock "sock API") APIs. | -| Cellular | The [end-to-end security](/example/security/e2e "E2E example") example using the [security](/common/security "security API") API. | | Cellular | The [PSK generation](/example/security/psk "PSK example") example using the [security](/common/security "security API") API. | -| Cellular | The [chip-to-chip security](/example/security/c2c "C2C example") example using the [security](/common/security "security API") API. | | Cellular | A [TLS-secured version](/example/sockets "TLS sockets example") of the sockets example. | | Cellular | An [MQTT/MQTT-SN client](/example/mqtt_client "MQTT/MQTT-SN example") using the [MQTT/MQTT-SN client](/common/mqtt_client "MQTT/MQTT-SN client API") API.| | Cellular | An [HTTP client](/example/http_client "HTTP example") using the [HTTP client](/common/http_client "HTTP client API") API.| diff --git a/cell/api/u_cell.h b/cell/api/u_cell.h index 7d2ebf9c..7f541553 100644 --- a/cell/api/u_cell.h +++ b/cell/api/u_cell.h @@ -45,30 +45,16 @@ extern "C" { /** The recommended UART buffer length for the cellular driver, * large enough to run AT sockets using the IP stack on the * cellular module (where the maximum packet length is 1024 bytes) - * without flow control. See #U_CELL_AT_BUFFER_LENGTH_BYTES for - * where the overhead numbers come from (for the C2C case). + * without flow control. */ -# define U_CELL_UART_BUFFER_LENGTH_BYTES (1024 + 16 + 16 + 16 + 16 + 16 + 6) +# define U_CELL_UART_BUFFER_LENGTH_BYTES 1024 #endif #ifndef U_CELL_AT_BUFFER_LENGTH_BYTES /** The AT client buffer length required in the AT client by the * cellular driver. - * - * When chip to chip security is employed the size of each - * secure frame adds a considerable overhead. Maximum - * chunk size is 1024 + 16 bytes (for an AT command, - * see u_cell_sec_c2c.h) plus the maximum padding length of 16, - * bytes plus the length of a truncated MAC (16 bytes) plus the - * length of the initial value (16 bytes) plus the length of - * the HMAC SHA tag for the V2 scheme (16 bytes) plus - * start/length/CRC/stop field totalling 6 bytes. Then it is - * possible for there to be part of one of these in the buffer - * being processed by the AT client when another is meant to - * turn up so allow for at least two. */ -# define U_CELL_AT_BUFFER_LENGTH_BYTES (U_AT_CLIENT_BUFFER_OVERHEAD_BYTES + \ - ((1024 + 16 + 16 + 16 + 16 + 16 + 6) * 2)) +# define U_CELL_AT_BUFFER_LENGTH_BYTES (U_AT_CLIENT_BUFFER_LENGTH_BYTES + U_CELL_UART_BUFFER_LENGTH_BYTES) #endif #ifndef U_CELL_UART_BAUD_RATE diff --git a/cell/api/u_cell_mux.h b/cell/api/u_cell_mux.h index 97d0a8ee..0f617806 100644 --- a/cell/api/u_cell_mux.h +++ b/cell/api/u_cell_mux.h @@ -87,10 +87,6 @@ void uCellMuxPrivateLink(void); * uCellAtClientHandleGet(); uCellAtClientHandleGet() will always * return the AT handle currently in use. * - * Multiplexer mode cannot be enabled while chip-to-chip security - * is active, however chip-to-chip security can be enabled _after_ - * multiplexer mode has been enabled. - * * Whether multiplexer mode is supported or not depends on the cellular * module and the interface in use: for instance a USB interface to * a module does not support multiplexer mode. diff --git a/cell/api/u_cell_sec.h b/cell/api/u_cell_sec.h index ae91b8d6..a3525767 100644 --- a/cell/api/u_cell_sec.h +++ b/cell/api/u_cell_sec.h @@ -128,96 +128,6 @@ int32_t uCellSecGetSerialNumber(uDeviceHandle_t cellHandle, int32_t uCellSecGetRootOfTrustUid(uDeviceHandle_t cellHandle, char *pRootOfTrustUid); -/* ---------------------------------------------------------------- - * FUNCTIONS: CHIP TO CHIP SECURITY - * -------------------------------------------------------------- */ - -/** Pair a cellular module's AT interface with this MCU for chip to - * chip security. This feature is available by arrangement with - * u-blox. The pairing process is expected to be carried out in a - * secure production environment *before* the device is boostrapped, - * i.e. before the module is allowed to contact the u-blox security - * services over the network. Only if a special feature, - * "LocalC2CKeyPairing", is enabled in the u-blox security service - * can pairing be carried out after a device has been sealed, since - * this represents a potential attack vector. - * - * Once this function returns successfully the values of the locally - * generated pTESecret and the pKey and pHMac parameters returned must - * be stored securely on this MCU by the caller. Later, after the - * module has bootstrapped and been sealed the parameters may be used - * in a call to uSecurityC2cOpen() to encrypt communication over the - * AT interface between this MCU and the module. - * - * @param cellHandle the handle of the instance to be used. - * @param[in] pTESecret a pointer to the fixed-length 16 byte - * secret generated by this MCU (the - * "Terminal Equipment") to be used in the - * pairing process; cannot be NULL. - * @param[out] pKey a place to store the fixed-length 16 byte - * encryption key that must be used when a - * secure AT session is opened. It is up to - * the caller to store this securely in - * non-volatile memory for future use. - * Cannot be NULL. - * @param[out] pHMacKey a place to store the fixed-length - * 16 byte HMAC key that must be used when a - * secure AT session is opened. It is up - * to the caller to store this securely in - * non-volatile memory for future use. - * Cannot be NULL. - * @return zero on success else negative error code. - */ -int32_t uCellSecC2cPair(uDeviceHandle_t cellHandle, - const char *pTESecret, - char *pKey, char *pHMacKey); - -/** Open a secure AT session. Once this has returned - * successfully the AT client will encrypt the outgoing data - * stream to the cellular module and decrypt data received back from - * the cellular module using the keys provided. pTeSecret, pKey, - * and pHMax are provided from non-volatile storage on the - * MCU, the latter two resulting from the C2C pairing process - * carried out earlier. Once this function returns successfully - * all AT communications will be encrypted by the AT client until - * uSecurityC2cClose() is called or the cellular module is powered - * off or rebooted. If a chip to chip security session is - * already open when this is called it will do nothing and - * return success. - * - * Should chip to chip security have somehow failed the cellular - * module will appear as though it is unresponsive. If this - * happen use hard power off, uCellPwrOffHard() (but no need - * for "trulyHard"), which uses electrical rather than AT-command - * means to power the module down, and then restart it to try again. - * - * @param cellHandle the handle of the instance to be used. - * @param[in] pTESecret a pointer to the fixed-length 16 byte - * secret key that was used during pairing; - * cannot be NULL. - * @param[in] pKey a pointer to the fixed-length 16 byte - * encryption key that was returned during - * pairing; cannot be NULL. - * @param[in] pHMacKey a pointer to the fixed-length 16 byte - * HMAC key that was returned during pairing; - * cannot be NULL. - * @return zero on success else negative error code. - */ -int32_t uCellSecC2cOpen(uDeviceHandle_t cellHandle, - const char *pTESecret, - const char *pKey, - const char *pHMacKey); - -/** Close a secure AT session. Once this has returned - * successfully the AT exchange with the cellular module will - * once more be unencrypted. If there is no open C2C session - * this function will do nothing and return success. - * - * @param cellHandle the handle of the instance to be used. - * @return zero on success else negative error code. - */ -int32_t uCellSecC2cClose(uDeviceHandle_t cellHandle); - /* ---------------------------------------------------------------- * FUNCTIONS: SEAL * -------------------------------------------------------------- */ @@ -232,14 +142,9 @@ int32_t uCellSecC2cClose(uDeviceHandle_t cellHandle); * * @param cellHandle the handle of the cellular instance. * @param[in] pDeviceProfileUid the null-terminated device profile - * UID string provided by u-blox. - * Note: if you have activated your module - * via the Thingstream portal - * (https://portal.thingstream.io) then the - * device profile UID string is visible - * once you have created a device profile - * for your module; it will look something - * like "AgbCtixjwqLjwV3VWpfPyz". + * UID string provided by u-blox; it will + * look something like + * "AgbCtixjwqLjwV3VWpfPyz". * @param[in] pDeviceSerialNumberStr the null-terminated device serial * number string; you may chose what this * is, noting that there may be an upper @@ -287,7 +192,7 @@ bool uCellSecIsSealed(uDeviceHandle_t cellHandle); * during the sealing process. If the certificate does not [yet] * exist an error will be returned. This feature is only * supported if the Zero Touch Provisioning feature is enabled - * in your Thingstream portal for the module. + * for your module. * * If pData is set to NULL then the number of bytes required to * store the certificate, including a null terminator, will still @@ -300,11 +205,6 @@ bool uCellSecIsSealed(uDeviceHandle_t cellHandle); * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param cellHandle the handle of the cellular instance. * @param[out] pData a pointer to somewhere to store * the certificate; use NULL to @@ -325,8 +225,7 @@ int32_t uCellSecZtpGetDeviceCertificate(uDeviceHandle_t cellHandle, /** Read the device private key that was generated during the * sealing process. If the key does not [yet] exist an error * will be returned. This feature is only supported if the Zero - * Touch Provisioning feature is enabled in your Thingstream - * portal for the module. + * Touch Provisioning feature is enabled for your module. * * If pData is set to NULL then the number of bytes required to * store the key, including a null terminator, will still be @@ -339,11 +238,6 @@ int32_t uCellSecZtpGetDeviceCertificate(uDeviceHandle_t cellHandle, * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param cellHandle the handle of the cellular instance. * @param[out] pData a pointer to somewhere to store * the key; use NULL to just get the @@ -365,7 +259,7 @@ int32_t uCellSecZtpGetPrivateKey(uDeviceHandle_t cellHandle, * the sealing process. If the certificate(s) do not [yet] * exist an error will be returned. This feature is only * supported if the Zero Touch Provisioning feature is enabled - * in your Thingstream portal for the module. + * for your module. * * If pData is set to NULL then the number of bytes required to * store the certificates, including a null terminator, will still @@ -378,11 +272,6 @@ int32_t uCellSecZtpGetPrivateKey(uDeviceHandle_t cellHandle, * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param cellHandle the handle of the cellular instance. * @param[out] pData a pointer to somewhere to store * the certificate authorities; use @@ -400,66 +289,6 @@ int32_t uCellSecZtpGetCertificateAuthorities(uDeviceHandle_t cellHandle, char *pData, size_t dataSizeBytes); -/* ---------------------------------------------------------------- - * FUNCTIONS: END TO END ENCRYPTION - * -------------------------------------------------------------- */ - -/** Set the E2E encryption version to be used. Not all cellular - * module types support all versions: refer to the AT manual for your - * cellular module to determine what's what. If a cellular module - * only supports a single E2E encryption type then it probably won't - * support setting the E2E encryption version. - * - * @param cellHandle the handle of the instance to be used. - * @param version the version to use; use 1 for version 1, - * etc. (so there is no version 0). - * @return zero on success else negative error code. - */ -int32_t uCellSecE2eSetVersion(uDeviceHandle_t cellHandle, int32_t version); - -/** Get the E2E encryption version. If a cellular module only supports - * a single E2E encryption type then it may not support getting the - * E2E encryption version. Note that while the AT+USECOPCMD="e2e_enc" - * command returns 0 for version 1 etc., this function will return - * 1 for version 1, i.e. there is no version 0. - * - * @param cellHandle the handle of the instance to be used. - * @return on success the E2E encryption version, - * else negative error code. - */ -int32_t uCellSecE2eGetVersion(uDeviceHandle_t cellHandle); - -/** Ask a cellular module to encrypt a block of data. For this to - * work the module must have previously been security sealed but no - * current connection is required. Data encrypted in this way - * can be decrypted on arrival at its destination by requesting - * the relevant security keys from u-blox via the security services - * web API. - * - * @param cellHandle the handle of the instance to be used. - * @param[in] pDataIn a pointer to dataSizeBytes of data to be - * encrypted, may be NULL, in which case this - * function does nothing. - * @param[out] pDataOut a pointer to a location to store the - * encrypted data that MUST BE at least of - * size dataSizeBytes + - * #U_SECURITY_E2E_V1_HEADER_LENGTH_BYTES for - * E2E encryption version 1 or dataSizeBytes + - * #U_SECURITY_E2E_V2_HEADER_LENGTH_BYTES for - * E2E encryption version 2 (or you can - * just use #U_SECURITY_E2E_HEADER_LENGTH_MAX_BYTES - * for both cases); can only be NULL if pDataIn - * is NULL. - * @param dataSizeBytes the number of bytes of data to encrypt; - * must be zero if pDataIn is NULL. - * @return on success the number of bytes in the - * encrypted data block else negative error - * code. - */ -int32_t uCellSecE2eEncrypt(uDeviceHandle_t cellHandle, - const void *pDataIn, - void *pDataOut, size_t dataSizeBytes); - /* ---------------------------------------------------------------- * FUNCTIONS: PRE-SHARED KEY GENERATION * -------------------------------------------------------------- */ @@ -492,11 +321,11 @@ int32_t uCellSecPskGenerate(uDeviceHandle_t cellHandle, /** Trigger a security heartbeat: this is useful if modifications * have been made to the security profile of the device in the - * u-blox security services REST API (or through the Thingstream - * service) and the device needs to be updated with these changes. - * HOWEVER, note that rate limiting is applied to these adhoc security - * hearbeats and hence if requested too frequently (e.g. more than - * once every 24 hours) the trigger request may return an error. + * u-blox security services REST API and the device needs to be + * updated with these changes. HOWEVER, note that rate limiting is + * applied to these adhoc security hearbeats and hence if requested + * too frequently (e.g. more than once every 24 hours) the trigger + * request may return an error. * * @param cellHandle the handle of the instance to be used, for * example obtained through uDeviceOpen(). diff --git a/cell/src/u_cell.c b/cell/src/u_cell.c index e3660a22..d4340dc9 100644 --- a/cell/src/u_cell.c +++ b/cell/src/u_cell.c @@ -130,8 +130,6 @@ static void removeCellInstance(uCellPrivateInstance_t *pInstance) uAtClientSetWakeUpHandler(pInstance->atHandle, NULL, NULL, 0); // Free any scan results uCellPrivateScanFree(&(pInstance->pScanResults)); - // Free any chip to chip security context - uCellPrivateC2cRemoveContext(pInstance); // Free any location context and associated URC uCellPrivateLocRemoveContext(pInstance); // Free any sleep context diff --git a/cell/src/u_cell_mux.c b/cell/src/u_cell_mux.c index b2ca0431..f7e69d7e 100644 --- a/cell/src/u_cell_mux.c +++ b/cell/src/u_cell_mux.c @@ -1561,159 +1561,156 @@ int32_t uCellMuxEnable(uDeviceHandle_t cellHandle) if (pInstance != NULL) { errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; if (U_CELL_PRIVATE_HAS(pInstance->pModule, U_CELL_PRIVATE_FEATURE_CMUX)) { - errorCode = (int32_t) U_CELL_ERROR_TEMPORARY_FAILURE; - if (pInstance->pSecurityC2cContext == NULL) { - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - if (pInstance->pMuxContext == NULL) { - errorCode = (int32_t) U_ERROR_COMMON_NO_MEMORY; - // Allocate memory for our CMUX context; this will be - // deallocated only when the cellular instance is removed - pInstance->pMuxContext = pUPortMalloc(sizeof(uCellMuxPrivateContext_t)); - if (pInstance->pMuxContext != NULL) { - pContext = (uCellMuxPrivateContext_t *) pInstance->pMuxContext; - memset(pContext, 0, sizeof(*pContext)); - // To save memory, we use a single event queue for all callbacks - // from the CMUX channels, re-using the AT client sizes - pContext->eventQueueHandle = uPortEventQueueOpen(eventHandler, - "cmuxCallbacks", - sizeof(uCellMuxEvenTrampoline_t), - U_CELL_MUX_CALLBACK_TASK_STACK_SIZE_BYTES, - U_CELL_MUX_CALLBACK_TASK_PRIORITY, - U_CELL_MUX_CALLBACK_QUEUE_LENGTH); - if (pContext->eventQueueHandle >= 0) { - if (uRingBufferCreateWithReadHandle(&(pContext->ringBuffer), - pContext->linearBuffer, - sizeof(pContext->linearBuffer), 1) == 0) { - uRingBufferSetReadRequiresHandle(&(pContext->ringBuffer), true); - pContext->readHandle = uRingBufferTakeReadHandle(&(pContext->ringBuffer)); - } else { - // Clean up on error - uPortEventQueueClose(pContext->eventQueueHandle); - uPortFree(pInstance->pMuxContext); - pInstance->pMuxContext = NULL; - } + errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; + if (pInstance->pMuxContext == NULL) { + errorCode = (int32_t) U_ERROR_COMMON_NO_MEMORY; + // Allocate memory for our CMUX context; this will be + // deallocated only when the cellular instance is removed + pInstance->pMuxContext = pUPortMalloc(sizeof(uCellMuxPrivateContext_t)); + if (pInstance->pMuxContext != NULL) { + pContext = (uCellMuxPrivateContext_t *) pInstance->pMuxContext; + memset(pContext, 0, sizeof(*pContext)); + // To save memory, we use a single event queue for all callbacks + // from the CMUX channels, re-using the AT client sizes + pContext->eventQueueHandle = uPortEventQueueOpen(eventHandler, + "cmuxCallbacks", + sizeof(uCellMuxEvenTrampoline_t), + U_CELL_MUX_CALLBACK_TASK_STACK_SIZE_BYTES, + U_CELL_MUX_CALLBACK_TASK_PRIORITY, + U_CELL_MUX_CALLBACK_QUEUE_LENGTH); + if (pContext->eventQueueHandle >= 0) { + if (uRingBufferCreateWithReadHandle(&(pContext->ringBuffer), + pContext->linearBuffer, + sizeof(pContext->linearBuffer), 1) == 0) { + uRingBufferSetReadRequiresHandle(&(pContext->ringBuffer), true); + pContext->readHandle = uRingBufferTakeReadHandle(&(pContext->ringBuffer)); } else { // Clean up on error + uPortEventQueueClose(pContext->eventQueueHandle); uPortFree(pInstance->pMuxContext); pInstance->pMuxContext = NULL; } + } else { + // Clean up on error + uPortFree(pInstance->pMuxContext); + pInstance->pMuxContext = NULL; } } - if (pInstance->pMuxContext != NULL) { - pContext = (uCellMuxPrivateContext_t *) pInstance->pMuxContext; - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - if (pContext->savedAtHandle == NULL) { - // Initialise the other parts of [an existing] context - pContext->pInstance = pInstance; - pContext->channelGnss = getChannelGnss(pInstance); - pContext->holdingBufferIndex = 0; - // Initiate CMUX - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - streamHandle = uAtClientStreamGet(atHandle, &streamType); - uRingBufferFlushHandle(&(pContext->ringBuffer), pContext->readHandle); - pContext->underlyingStreamHandle = streamHandle; - uAtClientCommandStart(atHandle, "AT+CMUX="); - // Only basic mode and only UIH frames are supported by any - // of the cellular modules we support - uAtClientWriteInt(atHandle, 0); - uAtClientWriteInt(atHandle, 0); - // As advised in the u-blox multiplexer document, port - // speed is left empty for max compatibility - uAtClientWriteString(atHandle, "", false); - // Set the information field length - uAtClientWriteInt(atHandle, - U_CELL_MUX_PRIVATE_INFORMATION_LENGTH_MAX_BYTES); - // Everything else is left at defaults for max compatibility - uAtClientCommandStopReadResponse(atHandle); - // Not unlocking here, just check for errors - errorCode = uAtClientErrorGet(atHandle); + } + if (pInstance->pMuxContext != NULL) { + pContext = (uCellMuxPrivateContext_t *) pInstance->pMuxContext; + errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; + if (pContext->savedAtHandle == NULL) { + // Initialise the other parts of [an existing] context + pContext->pInstance = pInstance; + pContext->channelGnss = getChannelGnss(pInstance); + pContext->holdingBufferIndex = 0; + // Initiate CMUX + atHandle = pInstance->atHandle; + uAtClientLock(atHandle); + streamHandle = uAtClientStreamGet(atHandle, &streamType); + uRingBufferFlushHandle(&(pContext->ringBuffer), pContext->readHandle); + pContext->underlyingStreamHandle = streamHandle; + uAtClientCommandStart(atHandle, "AT+CMUX="); + // Only basic mode and only UIH frames are supported by any + // of the cellular modules we support + uAtClientWriteInt(atHandle, 0); + uAtClientWriteInt(atHandle, 0); + // As advised in the u-blox multiplexer document, port + // speed is left empty for max compatibility + uAtClientWriteString(atHandle, "", false); + // Set the information field length + uAtClientWriteInt(atHandle, + U_CELL_MUX_PRIVATE_INFORMATION_LENGTH_MAX_BYTES); + // Everything else is left at defaults for max compatibility + uAtClientCommandStopReadResponse(atHandle); + // Not unlocking here, just check for errors + errorCode = uAtClientErrorGet(atHandle); + if (errorCode == 0) { + // Leave the AT client locked to stop it reacting to stuff coming + // back over the UART, which will shortly become the MUX + // control channel and not an AT interface at all. + // Replace the URC handler of the existing AT client + // with our own so that we get the received data + // and can decode it + uAtClientUrcHandlerHijack(atHandle, cmuxReceiveCallback, pContext); + // Give the module a moment for the MUX switcheroo + uPortTaskBlock(U_CELL_MUX_PRIVATE_ENABLE_DISABLE_DELAY_MS); + // Open the control channel, channel 0; for this we need no + // data buffer, since it does not carry user data + pContext->savedAtHandle = atHandle; + errorCode = openChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_CONTROL, 0); if (errorCode == 0) { - // Leave the AT client locked to stop it reacting to stuff coming - // back over the UART, which will shortly become the MUX - // control channel and not an AT interface at all. - // Replace the URC handler of the existing AT client - // with our own so that we get the received data - // and can decode it - uAtClientUrcHandlerHijack(atHandle, cmuxReceiveCallback, pContext); - // Give the module a moment for the MUX switcheroo - uPortTaskBlock(U_CELL_MUX_PRIVATE_ENABLE_DISABLE_DELAY_MS); - // Open the control channel, channel 0; for this we need no - // data buffer, since it does not carry user data - pContext->savedAtHandle = atHandle; - errorCode = openChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_CONTROL, 0); - if (errorCode == 0) { #ifdef U_CELL_MUX_ENABLE_DEBUG - uPortLog("U_CELL_CMUX_0: control channel open.\n"); + uPortLog("U_CELL_CMUX_0: control channel open.\n"); #endif - // Channel 0 is up, now we need channel 1, on which - // we will need a data buffer for the information field carrying the - // user data (i.e. AT commands) - errorCode = openChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT, - U_CELL_MUX_PRIVATE_VIRTUAL_SERIAL_BUFFER_LENGTH_BYTES); - if (errorCode == 0) { + // Channel 0 is up, now we need channel 1, on which + // we will need a data buffer for the information field carrying the + // user data (i.e. AT commands) + errorCode = openChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT, + U_CELL_MUX_PRIVATE_VIRTUAL_SERIAL_BUFFER_LENGTH_BYTES); + if (errorCode == 0) { #ifdef U_CELL_MUX_ENABLE_DEBUG - uPortLog("U_CELL_CMUX_1: AT channel open, flushing stored URCs...\n"); + uPortLog("U_CELL_CMUX_1: AT channel open, flushing stored URCs...\n"); #endif - pDeviceSerial = pUCellMuxPrivateGetDeviceSerial(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT); - // Some modules (e.g. SARA-R422) can have stored up loads of URCs - // which they like to emit over the new mux channel; flush these - // out here - uPortTaskBlock(500); - do { - uPortTaskBlock(10); - } while (pDeviceSerial->read(pDeviceSerial, tempBuffer, sizeof(tempBuffer)) > 0); - // Create a copy of the current AT client on this serial port - atHandle = uAtClientAdd((int32_t) pDeviceSerial, - U_AT_CLIENT_STREAM_TYPE_VIRTUAL_SERIAL, - NULL, U_CELL_AT_BUFFER_LENGTH_BYTES); - if (atHandle != NULL) { + pDeviceSerial = pUCellMuxPrivateGetDeviceSerial(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT); + // Some modules (e.g. SARA-R422) can have stored up loads of URCs + // which they like to emit over the new mux channel; flush these + // out here + uPortTaskBlock(500); + do { + uPortTaskBlock(10); + } while (pDeviceSerial->read(pDeviceSerial, tempBuffer, sizeof(tempBuffer)) > 0); + // Create a copy of the current AT client on this serial port + atHandle = uAtClientAdd((int32_t) pDeviceSerial, + U_AT_CLIENT_STREAM_TYPE_VIRTUAL_SERIAL, + NULL, U_CELL_AT_BUFFER_LENGTH_BYTES); + if (atHandle != NULL) { #ifdef U_CELL_MUX_ENABLE_DEBUG - uPortLog("U_CELL_CMUX: AT client added.\n"); + uPortLog("U_CELL_CMUX: AT client added.\n"); #endif - errorCode = uCellMuxPrivateCopyAtClient(pContext->savedAtHandle, - atHandle); - if (errorCode == 0) { + errorCode = uCellMuxPrivateCopyAtClient(pContext->savedAtHandle, + atHandle); + if (errorCode == 0) { #ifdef U_CELL_MUX_ENABLE_DEBUG - uPortLog("U_CELL_CMUX: existing AT client copied, CMUX is running.\n"); + uPortLog("U_CELL_CMUX: existing AT client copied, CMUX is running.\n"); #endif - // Now that we have everything, we set the AT handle - // of our instance to the new AT handle, leaving the - // old AT handle locked - pInstance->atHandle = atHandle; - // The setting of echo-off and AT+CMEE is port-specific, - // so we need to set those here for the new port + // Now that we have everything, we set the AT handle + // of our instance to the new AT handle, leaving the + // old AT handle locked + pInstance->atHandle = atHandle; + // The setting of echo-off and AT+CMEE is port-specific, + // so we need to set those here for the new port #ifdef U_CFG_CELL_ENABLE_NUMERIC_ERROR - cmeeMode = 1; + cmeeMode = 1; #endif - uAtClientLock(atHandle); - uAtClientCommandStart(atHandle, "ATE0"); - uAtClientCommandStopReadResponse(atHandle); - uAtClientCommandStart(atHandle, "AT+CMEE="); - uAtClientWriteInt(atHandle, cmeeMode); - uAtClientCommandStopReadResponse(atHandle); - errorCode = uAtClientUnlock(atHandle); - } else { - // Recover on error - uAtClientRemove(atHandle); - atHandle = pContext->savedAtHandle; - } + uAtClientLock(atHandle); + uAtClientCommandStart(atHandle, "ATE0"); + uAtClientCommandStopReadResponse(atHandle); + uAtClientCommandStart(atHandle, "AT+CMEE="); + uAtClientWriteInt(atHandle, cmeeMode); + uAtClientCommandStopReadResponse(atHandle); + errorCode = uAtClientUnlock(atHandle); } else { // Recover on error + uAtClientRemove(atHandle); atHandle = pContext->savedAtHandle; } + } else { + // Recover on error + atHandle = pContext->savedAtHandle; } } } - if (errorCode < 0) { - // Clean up and unlock the AT client on error - uCellMuxPrivateCloseChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT); - // Closing the control channel will take us out of CMUX mode - uCellMuxPrivateCloseChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_CONTROL); - uAtClientUrcHandlerHijack(atHandle, NULL, NULL); - pContext->savedAtHandle = NULL; - uAtClientUnlock(atHandle); - } + } + if (errorCode < 0) { + // Clean up and unlock the AT client on error + uCellMuxPrivateCloseChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_AT); + // Closing the control channel will take us out of CMUX mode + uCellMuxPrivateCloseChannel(pContext, U_CELL_MUX_PRIVATE_CHANNEL_ID_CONTROL); + uAtClientUrcHandlerHijack(atHandle, NULL, NULL); + pContext->savedAtHandle = NULL; + uAtClientUnlock(atHandle); } } } diff --git a/cell/src/u_cell_private.c b/cell/src/u_cell_private.c index 29d8ba0c..ec7bc7c2 100644 --- a/cell/src/u_cell_private.c +++ b/cell/src/u_cell_private.c @@ -57,7 +57,6 @@ #include "u_cell_net.h" // important here #include "u_cell_private.h" // don't change it #include "u_cell_pwr.h" -#include "u_cell_sec_c2c.h" #include "u_cell_cfg.h" #include "u_cell_http.h" #include "u_cell_http_private.h" @@ -206,7 +205,6 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = { ((1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_MNO_PROFILE) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_CSCON) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_ROOT_OF_TRUST) | - (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SECURITY_C2C) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_DATA_COUNTERS) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SECURITY_TLS_IANA_NUMBERING) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SECURITY_TLS_CIPHER_LIST) | @@ -895,33 +893,6 @@ const uCellPrivateModule_t *pUCellPrivateGetModule(uDeviceHandle_t cellHandle) return pModule; } -// Remove a chip-to-chip security context. -void uCellPrivateC2cRemoveContext(uCellPrivateInstance_t *pInstance) -{ - uCellSecC2cContext_t *pContext = (uCellSecC2cContext_t *) pInstance->pSecurityC2cContext; - - if (pContext != NULL) { - if (pContext->pTx != NULL) { - uAtClientStreamInterceptTx(pInstance->atHandle, - NULL, NULL); - // For safety - memset(pContext->pTx, 0, sizeof(*(pContext->pTx))); - uPortFree(pContext->pTx); - } - if (pContext->pRx != NULL) { - uAtClientStreamInterceptRx(pInstance->atHandle, - NULL, NULL); - // For safety - memset(pContext->pRx, 0, sizeof(*(pContext->pRx))); - uPortFree(pContext->pRx); - } - // For safety - memset(pContext, 0, sizeof(*pContext)); - uPortFree(pContext); - pInstance->pSecurityC2cContext = NULL; - } -} - // Remove a location context. void uCellPrivateLocRemoveContext(uCellPrivateInstance_t *pInstance) { diff --git a/cell/src/u_cell_private.h b/cell/src/u_cell_private.h index bcacba7c..469956ab 100644 --- a/cell/src/u_cell_private.h +++ b/cell/src/u_cell_private.h @@ -200,7 +200,6 @@ typedef enum { U_CELL_PRIVATE_FEATURE_CSCON, U_CELL_PRIVATE_FEATURE_ROOT_OF_TRUST, U_CELL_PRIVATE_FEATURE_ASYNC_SOCK_CLOSE, - U_CELL_PRIVATE_FEATURE_SECURITY_C2C, U_CELL_PRIVATE_FEATURE_DATA_COUNTERS, U_CELL_PRIVATE_FEATURE_SECURITY_TLS_IANA_NUMBERING, U_CELL_PRIVATE_FEATURE_SECURITY_TLS_SERVER_NAME_INDICATION, @@ -435,7 +434,6 @@ typedef struct uCellPrivateInstance_t { void *pGreetingCallbackParameter; uCellPrivateNet_t *pScanResults; /**< Anchor for list of network scan results. */ int32_t sockNextLocalPort; - void *pSecurityC2cContext; /**< Hook for a chip to chip security context. */ volatile void *pMqttContext; /**< Hook for MQTT context, volatile as it can be populared by a URC in a different thread. */ uCellPrivateLocContext_t *pLocContext; /**< Hook for a location context. **/ @@ -625,14 +623,6 @@ void uCellPrivateScanFree(uCellPrivateNet_t **ppScanResults); //lint -esym(765, pUCellPrivateGetModule) may be compiled-out in various ways const uCellPrivateModule_t *pUCellPrivateGetModule(uDeviceHandle_t cellHandle); -/** Remove the chip to chip security context for the given instance. - * - * Note: gUCellPrivateMutex should be locked before this is called. - * - * @param pInstance a pointer to the cellular instance. - */ -void uCellPrivateC2cRemoveContext(uCellPrivateInstance_t *pInstance); - /** Remove the location context for the given instance. * * Note: gUCellPrivateMutex should be locked before this is called. diff --git a/cell/src/u_cell_pwr.c b/cell/src/u_cell_pwr.c index 2a181127..75a6c31f 100644 --- a/cell/src/u_cell_pwr.c +++ b/cell/src/u_cell_pwr.c @@ -1226,10 +1226,6 @@ static int32_t powerOff(uCellPrivateInstance_t *pInstance, (int32_t) !U_CELL_PRIVATE_PWR_ON_PIN_TOGGLE_TO_STATE(pInstance->pinStates)); } - // Remove any security context as these disappear - // at power off - uCellPrivateC2cRemoveContext(pInstance); - return errorCode; } @@ -1257,9 +1253,6 @@ static void quickPowerOff(uCellPrivateInstance_t *pInstance, uPortGpioSet(pInstance->pinEnablePower, (int32_t) !U_CELL_PRIVATE_ENABLE_POWER_PIN_ON_STATE(pInstance->pinStates)); } - // Remove any security context as these disappear - // at power off - uCellPrivateC2cRemoveContext(pInstance); } } @@ -1834,9 +1827,6 @@ int32_t uCellPwrOffHard(uDeviceHandle_t cellHandle, bool trulyHard, (int32_t) !U_CELL_PRIVATE_ENABLE_POWER_PIN_ON_STATE(pInstance->pinStates)); // Need to disable mux mode uCellMuxPrivateDisable(pInstance); - // Remove any security context as these disappear - // at power off - uCellPrivateC2cRemoveContext(pInstance); errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; } else { if (pInstance->pinPwrOn >= 0) { @@ -1870,9 +1860,6 @@ int32_t uCellPwrOffHard(uDeviceHandle_t cellHandle, bool trulyHard, uPortGpioSet(pInstance->pinEnablePower, (int32_t) !U_CELL_PRIVATE_ENABLE_POWER_PIN_ON_STATE(pInstance->pinStates)); } - // Remove any security context as these disappear - // at power off - uCellPrivateC2cRemoveContext(pInstance); errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; } } @@ -1949,8 +1936,6 @@ int32_t uCellPwrReboot(uDeviceHandle_t cellHandle, uAtClientCommandStopReadResponse(atHandle); errorCode = uAtClientUnlock(atHandle); if (errorCode == 0) { - // Remove any security context as these disappear at reboot - uCellPrivateC2cRemoveContext(pInstance); // We have rebooted pInstance->rebootIsRequired = false; // Wait for the module to boot @@ -2089,8 +2074,6 @@ int32_t uCellPwrResetHard(uDeviceHandle_t cellHandle, int32_t pinReset) gpioConfig.direction = U_PORT_GPIO_DIRECTION_OUTPUT; platformError = uPortGpioConfig(&gpioConfig); if (platformError == 0) { - // Remove any security context as these disappear at reboot - uCellPrivateC2cRemoveContext(pInstance); // We have rebooted pInstance->rebootIsRequired = false; startTime = uPortGetTickTimeMs(); diff --git a/cell/src/u_cell_sec.c b/cell/src/u_cell_sec.c index 885266e2..d2c7afa7 100644 --- a/cell/src/u_cell_sec.c +++ b/cell/src/u_cell_sec.c @@ -53,7 +53,6 @@ #include "u_cell_info.h" // For the IMEI #include "u_cell_sec.h" -#include "u_cell_sec_c2c.h" /* ---------------------------------------------------------------- * COMPILE-TIME MACROS @@ -76,44 +75,12 @@ # define U_CELL_SEC_USECDEVINFO_DELAY_SECONDS 5 #endif -/** The length of the encrypted C2C confirmation tag, - * used in V2 C2C key pairing. - */ -#define U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES (U_CELL_SEC_C2C_IV_LENGTH_BYTES + \ - U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES + \ - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES + \ - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES) - // Check that U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES is big enough // to hold the IMEI as a string #if U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES < (U_CELL_INFO_IMEI_SIZE + 1) # error U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES must be at least as big as U_CELL_INFO_IMEI_SIZE plus room for a null terminator. #endif -// Check that U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES is big enough to hold -// the hex version of array of U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES. -#if U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES * 2 != U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES -# error U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES not the same size as the ASCII hex version of U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES. -#endif - -// Check that U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES is big enough to hold -// the hex version of array of U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES. -#if U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES * 2 != U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES -# error U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES not the same size as the ASCII hex version of U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES. -#endif - -// Check that U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES is big enough to hold -// the hex version of array of U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES. -#if U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES * 2 != U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES -# error U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES not the same size as the ASCII hex version of U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES. -#endif - -// Check that U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES is big enough to hold -// the hex version of array of U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES. -#if U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2 != U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES -# error U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES not the same size as the ASCII hex version of U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES. -#endif - // Check that U_SECURITY_PSK_MAX_LENGTH_BYTES is at least as big as U_SECURITY_PSK_ID_MAX_LENGTH_BYTES #if U_SECURITY_PSK_MAX_LENGTH_BYTES < U_SECURITY_PSK_ID_MAX_LENGTH_BYTES # error U_SECURITY_PSK_MAX_LENGTH_BYTES is smaller than U_SECURITY_PSK_ID_MAX_LENGTH_BYTES. @@ -219,102 +186,6 @@ static int32_t ztpGet(uDeviceHandle_t cellHandle, int32_t type, return errorCodeOrSize; } -// Enrypt a C2C confirmation tag, consisting -// of U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES -// but hex encode (so twice that long). -// pC2cConfirmationTagHex must point to the hex-coded -// C2C confirmation tag, length -// U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2. -// pTeSecret is the fixed length TE secret, length -// U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES. -// pKey is the fixed length encryption key, length -// U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES. -// pHMacKey is the fixed length HMAC key, length -// U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES. -// pOutputBuffer must point to storage of length -// U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES. -// Note that this is actually just the "body" part -// of the V2 C2C frame encoding, see encode() over in -// u_cell_sec_cec.c. -static size_t encryptC2cConfirmationTag(const char *pC2cConfirmationTagHex, - const char *pTeSecret, - const char *pKey, - const char *pHMacKey, - char *pOutputBuffer) -{ - size_t length = 0; - char ivOrMac[U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES]; - // *INDENT-OFF* (otherwise AStyle makes a mess of this) - char c2cConfirmationTagPadded[U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES + - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES]; - // *INDENT-ON* - - // Get an IV into a local variable - memcpy(ivOrMac, pUCellSecC2cGetIv(), U_CELL_SEC_C2C_IV_LENGTH_BYTES); - - // We want to end up with this: - // - // ---------------------------------------------------------------- - // | IV | Encrypted padded C2C confirmation | truncated MAC | - // | 16 bytes | tag (binary) | 16 bytes | - // ---------------------------------------------------------------- - // - // Write IV into its position in the output. - // Then the encryption function can be pointed at the - // local copy and will overwrite it - memcpy(pOutputBuffer, ivOrMac, U_CELL_SEC_C2C_IV_LENGTH_BYTES); - length += U_CELL_SEC_C2C_IV_LENGTH_BYTES; - - // Copy the hex into the padding buffer as binary - uHexToBin(pC2cConfirmationTagHex, U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2, - c2cConfirmationTagPadded); - - // Need to deal with padding. Counter-intuitively, though - // the binary confirmation tag will be 16 bytes long, - // that is actually the worst case for padding with the - // RFC 5652 algorithm: it gains a whole 16 bytes of padding. - for (size_t x = 0; x < U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES; x++) { - c2cConfirmationTagPadded[U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES + x] = - (char) U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES; - } - - // Encrypt the padded binary C2C confirmation tag into the - // output buffer after the IV using the encryption key and the IV - if (uPortCryptoAes128CbcEncrypt(pKey, - U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES, - ivOrMac, c2cConfirmationTagPadded, - sizeof(c2cConfirmationTagPadded), - pOutputBuffer + U_CELL_SEC_C2C_IV_LENGTH_BYTES) == 0) { - length += sizeof(c2cConfirmationTagPadded); - // Next we need to create a HMAC tag across the - // IV, the encrypted text and the TE Secret. - // The simplest way to do this is to copy - // the TE Secret into the output buffer, perform - // the calculation (putting the result into the - // local variable ivOrMac) and then we overwrite - // where it is in the buffer with the truncated MAC - // (which is at least as big, as checked with - // a #error above) - memcpy(pOutputBuffer + length, pTeSecret, U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES); - // NOLINTNEXTLINE(readability-suspicious-call-argument) - if (uPortCryptoHmacSha256(pHMacKey, - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES, - pOutputBuffer, - length + U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES, - ivOrMac) == 0) { - // Now copy the first 16 bytes of the - // generated HMAC tag into the output, - // overwriting the TE Secret - memcpy(pOutputBuffer + length, ivOrMac, - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES); - // Account for its length - length += U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES; - } - } - - return length; -} - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: WORKAROUND FOR LINKER ISSUE * -------------------------------------------------------------- */ @@ -479,304 +350,6 @@ int32_t uCellSecGetRootOfTrustUid(uDeviceHandle_t cellHandle, return errorCodeOrSize; } -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: CHIP TO CHIP SECURITY - * -------------------------------------------------------------- */ - -// Pair a cellular module's AT interface for chip to chip security. -int32_t uCellSecC2cPair(uDeviceHandle_t cellHandle, - const char *pTESecret, - char *pKey, char *pHMac) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - int32_t x = -1; - int32_t y = -1; - int32_t z = -1; - char buffer[U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES + 1]; // +1 for terminator - char *pEncryptedC2cConfirmationTag; - char *pEncryptedC2cConfirmationTagHex; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - pInstance = pUCellPrivateGetInstance(cellHandle); - errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - if ((pInstance != NULL) && (pTESecret != NULL) && - (pKey != NULL) && (pHMac != NULL)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_SECURITY_C2C)) { - errorCode = (int32_t) U_ERROR_COMMON_DEVICE_ERROR; - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECC2C="); - uAtClientWriteInt(atHandle, 0); - uBinToHex(pTESecret, sizeof(buffer) / 2, buffer); - // Add terminator since the AT write needs a string - *(buffer + sizeof(buffer) - 1) = 0; - uAtClientWriteString(atHandle, buffer, true); - uAtClientCommandStop(atHandle); - uAtClientResponseStart(atHandle, "+USECC2C:"); - // Must get back a zero and then another zero indicating - // success - // NOLINTBEGIN(misc-redundant-expression) - if ((uAtClientReadInt(atHandle) == 0) && - (uAtClientReadInt(atHandle) == 0)) { - // NOLINTEND(misc-redundant-expression) - // Success: read the key - x = uAtClientReadString(atHandle, buffer, - sizeof(buffer), false); - if (x == sizeof(buffer) - 1) { - x = (int32_t) uHexToBin(buffer, - sizeof(buffer) - 1, - pKey); - } - // Try to read the HMAC key, which will - // only be present if the module implements - // the V2 chip to chip scheme - y = uAtClientReadString(atHandle, buffer, - sizeof(buffer), false); - if (y == sizeof(buffer) - 1) { - y = (int32_t) uHexToBin(buffer, - sizeof(buffer) - 1, - pHMac); - // If the HMAC key is present, there must - // also be a chip to chip confirmation tag - z = uAtClientReadString(atHandle, buffer, - sizeof(buffer), false); - // We don't need to convert this to binary, - // just need the hex - } else { - // Zero the HMAC key field so that we know it is - // empty, then we know to use the V1 scheme. - memset(pHMac, 0, - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES); - uAtClientClearError(atHandle); - } - } - uAtClientResponseStop(atHandle); - // Key has to be the right length and, if present, - // so do both the HMAC key and the C2C confirmation tag - if ((uAtClientUnlock(atHandle) == 0) && - (x == U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES) && - ((z < 0) || - ((y == U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES) && - // * 2 since we're only using the hex here - (z == U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2)))) { - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - - if ((errorCode == 0) && (y > 0) && (z > 0)) { - errorCode = (int32_t) U_ERROR_COMMON_NO_MEMORY; - // For V2 encryption there is another step: the C2C - // confirmation tag has to be encrypted in exactly - // the same way as we would encrypt a C2C frame, using - // the secrets, but without the surrounding framing - // and then sent back to the module, hex coded, to - // confirm that we have received all of the above. - - // Get memory to put it in - // *INDENT-OFF* (otherwise AStyle makes a mess of this) - pEncryptedC2cConfirmationTag = (char *) pUPortMalloc(U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES); - if (pEncryptedC2cConfirmationTag != NULL) { - // ...and memory to put the hex-coded version in, +1 for terminator - pEncryptedC2cConfirmationTagHex = (char *) pUPortMalloc((U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2) + 1); - // *INDENT-ON* - if (pEncryptedC2cConfirmationTagHex != NULL) { - errorCode = (int32_t) U_ERROR_COMMON_AUTHENTICATION_FAILURE; - // Encrypt the buffer, which should contain the - // hex-coded C2C confirmation tag, with all the - // other bits and pieces - // NOLINTNEXTLINE(readability-suspicious-call-argument) - x = (int32_t) encryptC2cConfirmationTag(buffer, pTESecret, - pKey, pHMac, - pEncryptedC2cConfirmationTag); - if (x == U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES) { - // Now send the TE secret and this to the module - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECC2C="); - uAtClientWriteInt(atHandle, 4); - uBinToHex(pTESecret, U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES, buffer); - // Add a terminator since the AT write needs a string - buffer[sizeof(buffer) - 1] = 0; - uAtClientWriteString(atHandle, buffer, true); - uBinToHex(pEncryptedC2cConfirmationTag, - U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES, - pEncryptedC2cConfirmationTagHex); - // Add a terminator since the AT write needs a string - *(pEncryptedC2cConfirmationTagHex + - (U_CELL_SEC_ENCRYPTED_C2C_CONFIRMATION_TAG_LENGTH_BYTES * 2)) = 0; - uAtClientWriteString(atHandle, pEncryptedC2cConfirmationTagHex, true); - uAtClientCommandStopReadResponse(atHandle); - // Should get OK back - if (uAtClientUnlock(atHandle) == 0) { - // NOW we're good - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - } - // Free the hex buffer - uPortFree(pEncryptedC2cConfirmationTagHex); - } - // Free the binary buffer - uPortFree(pEncryptedC2cConfirmationTag); - } - } - - // For safety, don't want keys sitting around in RAM - uAtClientFlush(atHandle); - memset(buffer, 0, sizeof(buffer)); - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCode; -} - -// Open a secure AT session. -int32_t uCellSecC2cOpen(uDeviceHandle_t cellHandle, - const char *pTESecret, - const char *pKey, - const char *pHMacKey) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - char buffer[U_CELL_SEC_HEX_BUFFER_LENGTH_BYTES + 1]; // +1 for terminator - uCellSecC2cContext_t *pContext; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - pInstance = pUCellPrivateGetInstance(cellHandle); - errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - if ((pInstance != NULL) && (pTESecret != NULL) && - (pKey != NULL) && (pHMacKey != NULL)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_SECURITY_C2C)) { - if (pInstance->pSecurityC2cContext == NULL) { - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECC2C="); - uAtClientWriteInt(atHandle, 1); - uBinToHex(pTESecret, sizeof(buffer) / 2, buffer); - // Add terminator since the AT write needs a string - *(buffer + sizeof(buffer) - 1) = 0; - uAtClientWriteString(atHandle, buffer, true); - uAtClientCommandStopReadResponse(atHandle); - errorCode = uAtClientUnlock(atHandle); - if (errorCode == 0) { - errorCode = (int32_t) U_ERROR_COMMON_NO_MEMORY; - // If that was successful, set up - // the chip to chip security context - pInstance->pSecurityC2cContext = pUPortMalloc(sizeof(uCellSecC2cContext_t)); - if (pInstance->pSecurityC2cContext != NULL) { - pContext = (uCellSecC2cContext_t *) pInstance->pSecurityC2cContext; - memset(pContext, 0, sizeof(uCellSecC2cContext_t)); - pContext->pTx = (uCellSecC2cContextTx_t *) pUPortMalloc(sizeof(uCellSecC2cContextTx_t)); - if (pContext->pTx != NULL) { - memset(pContext->pTx, 0, sizeof(uCellSecC2cContextTx_t)); - pContext->pRx = (uCellSecC2cContextRx_t *) pUPortMalloc(sizeof(uCellSecC2cContextRx_t)); - if (pContext->pRx != NULL) { - memset(pContext->pRx, 0, sizeof(uCellSecC2cContextRx_t)); - // Copy the values we've been given into - // the context - memcpy(pContext->teSecret, pTESecret, - sizeof(pContext->teSecret)); - memcpy(pContext->key, pKey, - sizeof(pContext->key)); - memcpy(pContext->hmacKey, pHMacKey, - sizeof(pContext->hmacKey)); - pContext->pTx->txInLimit = U_CELL_SEC_C2C_USER_MAX_TX_LENGTH_BYTES; - // If the pHmacTag has anything other than zero - // in it this must be a V2 implementation - for (size_t x = sizeof(pContext->hmacKey); - (x > 0) && !pContext->isV2; x--) { - pContext->isV2 = (pContext->hmacKey[x] != 0); - } - // Hook the intercept functions into the AT handler - uAtClientStreamInterceptTx(atHandle, pUCellSecC2cInterceptTx, - (void *) pContext); - uAtClientStreamInterceptRx(atHandle, pUCellSecC2cInterceptRx, - (void *) pContext); - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - } - } - } - // For safety, don't want keys sitting around in RAM - uAtClientFlush(atHandle); - memset(buffer, 0, sizeof(buffer)); - } else { - // Nothing to do - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCode; -} - -// Close a secure AT session. -int32_t uCellSecC2cClose(uDeviceHandle_t cellHandle) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - pInstance = pUCellPrivateGetInstance(cellHandle); - errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - if (pInstance != NULL) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_SECURITY_C2C)) { - if (pInstance->pSecurityC2cContext != NULL) { - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECC2C="); - uAtClientWriteInt(atHandle, 2); - uAtClientCommandStopReadResponse(atHandle); - errorCode = uAtClientUnlock(atHandle); - if (errorCode == 0) { - // If that was successful, remove - // the security context - uCellPrivateC2cRemoveContext(pInstance); - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - } else { - // Nothing to do - errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - } - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCode; -} - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: SEAL * -------------------------------------------------------------- */ @@ -884,165 +457,6 @@ int32_t uCellSecZtpGetCertificateAuthorities(uDeviceHandle_t cellHandle, return ztpGet(cellHandle, 2, pData, dataSizeBytes); } -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: END TO END ENCRYPTION - * -------------------------------------------------------------- */ - -// Set the E2E encryption version to be used. -int32_t uCellSecE2eSetVersion(uDeviceHandle_t cellHandle, int32_t version) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - pInstance = pUCellPrivateGetInstance(cellHandle); - if ((pInstance != NULL) && (version > 0)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_ROOT_OF_TRUST)) { - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECOPCMD="); - uAtClientWriteString(atHandle, "e2e_enc", true); - uAtClientWriteInt(atHandle, version - 1); - uAtClientCommandStopReadResponse(atHandle); - errorCode = uAtClientUnlock(atHandle); - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCode; -} - -// Get the E2E encryption version. -int32_t uCellSecE2eGetVersion(uDeviceHandle_t cellHandle) -{ - int32_t errorCodeOrVersion = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - int32_t version; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - errorCodeOrVersion = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - pInstance = pUCellPrivateGetInstance(cellHandle); - if (pInstance != NULL) { - errorCodeOrVersion = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_ROOT_OF_TRUST)) { - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECOPCMD="); - uAtClientWriteString(atHandle, "e2e_enc", true); - uAtClientCommandStop(atHandle); - uAtClientResponseStart(atHandle, "+USECOPCMD:"); - // Skip the first parameter, which is just "e2e_enc" - // being sent back to us - uAtClientSkipParameters(atHandle, 1); - version = uAtClientReadInt(atHandle); - uAtClientResponseStop(atHandle); - errorCodeOrVersion = uAtClientUnlock(atHandle); - if (errorCodeOrVersion == 0) { - errorCodeOrVersion = version + 1; - } - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCodeOrVersion; -} - -// Ask a cellular module to encrypt a block of data. -int32_t uCellSecE2eEncrypt(uDeviceHandle_t cellHandle, - const void *pDataIn, - void *pDataOut, - size_t dataSizeBytes) -{ - int32_t errorCodeOrSize = (int32_t) U_ERROR_COMMON_NOT_INITIALISED; - int32_t sizeOutBytes; - uCellPrivateInstance_t *pInstance; - uAtClientHandle_t atHandle; - - if (gUCellPrivateMutex != NULL) { - - U_PORT_MUTEX_LOCK(gUCellPrivateMutex); - - errorCodeOrSize = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - if (pDataIn != NULL) { - pInstance = pUCellPrivateGetInstance(cellHandle); - if (pInstance != NULL) { - errorCodeOrSize = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; - if (U_CELL_PRIVATE_HAS(pInstance->pModule, - U_CELL_PRIVATE_FEATURE_ROOT_OF_TRUST)) { - if ((pDataOut == NULL) && (dataSizeBytes == 0)) { - // Nothing to do - errorCodeOrSize = 0; - } else { - atHandle = pInstance->atHandle; - uAtClientLock(atHandle); - uAtClientTimeoutSet(atHandle, - U_CELL_SEC_TRANSACTION_TIMEOUT_SECONDS * 1000); - uAtClientCommandStart(atHandle, "AT+USECE2EDATAENC="); - uAtClientWriteInt(atHandle, (int32_t) dataSizeBytes); - uAtClientCommandStop(atHandle); - // Wait for the prompt - if (uAtClientWaitCharacter(atHandle, '>') == 0) { - // Wait for it... - uPortTaskBlock(50); - // Go! - uAtClientWriteBytes(atHandle, (const char *) pDataIn, - dataSizeBytes, true); - // Grab the response - uAtClientResponseStart(atHandle, "+USECE2EDATAENC:"); - // Read the length of the response - sizeOutBytes = uAtClientReadInt(atHandle); - if (sizeOutBytes > 0) { - // Don't stop for anything! - uAtClientIgnoreStopTag(atHandle); - // Get the leading quote mark out of the way - uAtClientReadBytes(atHandle, NULL, 1, true); - // Now read out all the actual data - uAtClientReadBytes(atHandle, (char *) pDataOut, - sizeOutBytes, true); - } - // Make sure to wait for the top tag before - // we finish - uAtClientRestoreStopTag(atHandle); - uAtClientResponseStop(atHandle); - errorCodeOrSize = uAtClientUnlock(atHandle); - if (errorCodeOrSize == 0) { - // All good - errorCodeOrSize = sizeOutBytes; - } - } else { - errorCodeOrSize = uAtClientUnlock(atHandle); - } - } - } - } - } - - U_PORT_MUTEX_UNLOCK(gUCellPrivateMutex); - } - - return errorCodeOrSize; -} - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: PRE-SHARED KEY GENERATION * -------------------------------------------------------------- */ diff --git a/cell/src/u_cell_sec_c2c.c b/cell/src/u_cell_sec_c2c.c deleted file mode 100644 index 5f04942c..00000000 --- a/cell/src/u_cell_sec_c2c.c +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright 2019-2023 u-blox - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Only #includes of u_* and the C standard library are allowed here, - * no platform stuff and no OS stuff. Anything required from - * the platform/OS must be brought in through u_port* to maintain - * portability. - */ - -/** @file - * @brief Implementation of the u-blox security chip-to-chip - * feature for cellular. This functions are called by the - * u_cell_sec.h API functions, they are not intended for use - * externally. - */ - -#ifdef U_CFG_OVERRIDE -# include "u_cfg_override.h" // For a customer's configuration override -#endif - -#include "stddef.h" // NULL, size_t etc. -#include "stdint.h" // int32_t etc. -#include "stdbool.h" -#include "string.h" // memcpy(), memcmp() -#include "stdlib.h" // rand() - -#include "u_cfg_sw.h" -#include "u_compiler.h" // for U_WEAK - -#include "u_port_clib_platform_specific.h" // rand() -#include "u_port_debug.h" -#include "u_port_crypto.h" - -#include "u_at_client.h" - -#include "u_security.h" - -#include "u_cell_sec_c2c.h" - -// Note: the compilation flag U_CELL_SEC_C2C_DETAILED_DEBUG -// was used during early stage development against real -// modems. It is FAR too heavyweight to be used normally, -// and of course shouldn't be necessary, but it is retained -// here in anticipation of that corner case appearing... -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG -#if U_CFG_ENABLE_LOGGING -#include "ctype.h" -#include "u_port_os.h" -#endif -#endif - -/* ---------------------------------------------------------------- - * COMPILE-TIME MACROS - * -------------------------------------------------------------- */ - -/** The chip to chip frame boundary marker. - */ -#define U_CELL_SEC_C2C_FRAME_MARKER 0xf9 - -// Check that an array of size U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES -// is big enough to hold an IV also -#if U_CELL_SEC_C2C_IV_LENGTH_BYTES > U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES -# error U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES must be at least as big as U_CELL_SEC_C2C_IV_LENGTH_BYTES since we size a local array below on U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES and it is used for both. -#endif - -// Check that U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES is at least as -// big as U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES -#if U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES > U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES -# error U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES must be at least as big as U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES since a TE secret is temporarily written to the space a truncated MAC would occupy during V2 encoding. -#endif - -/* ---------------------------------------------------------------- - * TYPES - * -------------------------------------------------------------- */ - -/* ---------------------------------------------------------------- - * VARIABLES - * -------------------------------------------------------------- */ - -/** Storage for the random IV value. - */ -static char gIv[U_CELL_SEC_C2C_IV_LENGTH_BYTES]; - -/** Table for FCS generation according to RFC 1662. - */ -static const uint16_t gFcsTable[] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; - -/* ---------------------------------------------------------------- - * STATIC FUNCTIONS - * -------------------------------------------------------------- */ - -// Calculate FCS according to RFC 1662. -static uint16_t fcsGenerate(const char *pBuffer, size_t bufferLength) -{ - uint16_t fcs = 0xffff; - - while (bufferLength > 0) { - fcs = (fcs >> 8) ^ gFcsTable[(fcs ^ *pBuffer) & 0xff]; - pBuffer++; - bufferLength--; - } - - // The FCS is then complemented before it is used - fcs ^= 0xFFFF; - - return fcs; -} - -// Return the length of a buffer after packing by the -// given modulo according to RFC 5652 section 6.3. -static size_t paddedLength(size_t length, size_t padModulo) -{ - return length + ((char) (padModulo - (length % padModulo))); -} - -// Pad a buffer to the given modulo according to RFC 5652 -// section 6.3. -static size_t pad(char *pBuffer, size_t length, - size_t bufferLength, size_t padModulo) -{ - char fill = (char) (padModulo - (length % padModulo)); - - do { - *(pBuffer + length) = fill; - length++; - } while ((length % padModulo != 0) && (length < bufferLength)); - - return length; -} - -// Unpad a buffer that was padded according to RFC 5652 -// section 6.3. -static size_t unpad(const char *pBuffer, size_t bufferLength) -{ - size_t fill; - - if (bufferLength > 0) { - fill = *(pBuffer + bufferLength - 1); - if (bufferLength >= fill) { - bufferLength -= fill; - } - } - - return bufferLength; -} - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG -#if U_CFG_ENABLE_LOGGING -// Print out text. -static void print(const char *pStr, size_t length) -{ - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - if (!isprint((int32_t) c)) { - // Print the hex - uPortLog("[%02x]", c); - } else { - // Print the ASCII character - uPortLog("%c", c); - } - } -} - -// Print out binary. -static void printHex(const char *pStr, size_t length) -{ - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - uPortLog("[%02x]", c); - } - (void) pStr; - (void) length; -} -#endif - -// On some platforms printing is line -// buffered so long strings will get lost unless -// they are chunked up: this function -// prints reasonable block sizes -//lint -esym(522, printBlock) Suppress "lacks side effects", which -// will be true if logging is compiled out -void printBlock(const char *pStr, size_t length, - bool isBinary) -{ -#if U_CFG_ENABLE_LOGGING - int32_t x = (int32_t) length; - int32_t y; - - while (x > 0) { - uPortLog("\""); - y = x; - if (y > 32) { - y = 32; - } - if (isBinary) { - printHex(pStr, y); - } else { - print(pStr, y); - } - uPortLog("\"\n"); - // Don't overwhelm the poor debug output, - // there there - uPortTaskBlock(100); - x -= y; - pStr += y; - } -#else - (void) pStr; - (void) length; - (void) isBinary; -#endif -} -#endif - -// Run chip to chip encode. -static size_t encode(const uCellSecC2cContext_t *pContext) -{ - size_t length = 0; - uCellSecC2cContextTx_t *pTx = pContext->pTx; - size_t x; - uint16_t y; - char ivOrMac[U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES]; - bool success = false; - - // Get the IV into a local variable - memcpy(ivOrMac, pUCellSecC2cGetIv(), U_CELL_SEC_C2C_IV_LENGTH_BYTES); - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: IV:\n"); - printBlock(ivOrMac, U_CELL_SEC_C2C_IV_LENGTH_BYTES, true); - - uPortLog("U_CELL_SEC_C2C_ENCODE: key:\n"); - printBlock(pContext->key, sizeof(pContext->key), true); - - uPortLog("U_CELL_SEC_C2C_ENCODE: HMAC key:\n"); - printBlock(pContext->hmacKey, sizeof(pContext->hmacKey), true); - - uPortLog("U_CELL_SEC_C2C_ENCODE: TE secret:\n"); - printBlock(pContext->teSecret, sizeof(pContext->teSecret), true); - - uPortLog("U_CELL_SEC_C2C_ENCODE: input text is (%d byte(s)):\n", - pTx->txInLength); - printBlock(pTx->txIn, pTx->txInLength, false); -#endif - - // Pad the input data as required - pTx->txInLength = pad(pTx->txIn, pTx->txInLength, pTx->txInLimit, - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES); - - // The frame looks like this: - // --------------------------------------------- - // | F9 | | | | ... | | | | F9 | - // | length | body | CRC | - // --------------------------------------------- - // F9 is the frame marker, the two-byte length and - // CRC fields are little-endian. Length is of the - // body only. - - // Add the opening frame marker - pTx->txOut[0] = (char) U_CELL_SEC_C2C_FRAME_MARKER; - - // Encrypt the data - if (pContext->isV2) { - // In V2 the body is as follows: - // - // ----------------------------------------------- - // | IV | encrypted padded | truncated MAC | - // | 16 bytes | user data | 16 bytes | - // ----------------------------------------------- - // - // Note that the V2 body is also encoded in a similar - // function over in u_cell_sec.c, used when - // creating the C2C confirmation tag: I would have - // separated this part out and had a single version but - // the C2C confirmation tag was a late-breaking - // change and I didn't want to pull this code to bits. - - // Length is the padded input length plus the IV length - // plus a truncated MAC length. - // Little endian, like the CRC -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: version 2.\n"); - uPortLog("U_CELL_SEC_C2C_ENCODE: padded input length is %d byte(s).\n", pTx->txInLength); -#endif - x = pTx->txInLength + U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES; - pTx->txOut[1] = (char) x; - pTx->txOut[2] = (char) (x >> 8); -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: chunk length will be %d byte(s).\n", x); -#endif - - // Write IV into the output. - // Then the encryption function can be pointed at the - // local copy so that we can cheerfully overwrite it - memcpy(pTx->txOut + 3, ivOrMac, - U_CELL_SEC_C2C_IV_LENGTH_BYTES); - x = U_CELL_SEC_C2C_IV_LENGTH_BYTES; - // Encrypt the padded plain text into the - // output buffer using the encryption key and the IV - if (uPortCryptoAes128CbcEncrypt(pContext->key, - sizeof(pContext->key), - ivOrMac, pTx->txIn, - pTx->txInLength, - pTx->txOut + 3 + x) == 0) { - x += pTx->txInLength; - // Next we need to create a HMAC tag across the - // encrypted text, the IV and the TE Secret. - // The simplest way to do this is to copy - // the TE Secret into the output buffer, perform - // the calculation (putting the result into the - // local variable ivOrMac) and then we overwrite - // where it is in the buffer with the truncated MAC - // (which is at least as big, as checked with - // a #error above) - memcpy(pTx->txOut + 3 + x, pContext->teSecret, - sizeof(pContext->teSecret)); - if (uPortCryptoHmacSha256(pContext->hmacKey, - sizeof(pContext->hmacKey), - pTx->txOut + 3, - x + sizeof(pContext->teSecret), - ivOrMac) == 0) { - // Now copy the first 16 bytes of the - // generated HMAC tag into the output, - // overwriting the TE Secret - memcpy(pTx->txOut + 3 + x, - ivOrMac, U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES); - // Account for its length - x += U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES; - success = true; - } - } - } else { - // In V1 the body is as follows: - // - // --------------------------------------------- - // | encrypted padded | MAC | IV | - // | user data | 32 bytes | 16 bytes | - // --------------------------------------------- - // - // Length is the padded input length plus the MAC length - // plus the IV length. - // Little endian, like the CRC -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: version 1.\n"); - uPortLog("U_CELL_SEC_C2C_ENCODE: input length will be %d byte(s).\n", pTx->txInLength); -#endif - x = pTx->txInLength + - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES + - U_CELL_SEC_C2C_IV_LENGTH_BYTES; - pTx->txOut[1] = (char) x; - pTx->txOut[2] = (char) (x >> 8); -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: chunk length will be %d byte(s).\n", x); -#endif - // Create the MAC and put it on the end of - // the padded plain text in the input buffer - x = pTx->txInLength; - if (uPortCryptoSha256(pTx->txIn, x, pTx->txIn + x) == 0) { - x += U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES; - // Write IV into its position in the output - // then the encryption function is pointed at the - // local copy so that if can cheerfully overwrite it - memcpy(pTx->txOut + 3 + x, ivOrMac, - U_CELL_SEC_C2C_IV_LENGTH_BYTES); - // Encrypt the padded plain text plus MAC into the - // output buffer using the encryption key and the IV - if (uPortCryptoAes128CbcEncrypt(pContext->key, - sizeof(pContext->key), - ivOrMac, pTx->txIn, x, - pTx->txOut + 3) == 0) { - // Now account for the length of the initial vector - x += U_CELL_SEC_C2C_IV_LENGTH_BYTES; - success = true; - } - } - } - - if (success) { - // Calculate the checksum over the length - // and everything else up to here - x += 2; - y = fcsGenerate(pTx->txOut + 1, x); - // Account for the opening marker - x++; - // Write in the checksum, little-endianly it says - // in RFC 1662 - pTx->txOut[x] = (char) y; - pTx->txOut[x + 1] = (char) (y >> 8); - - // Account for the checksum - x += 2; - // Finally add the closing marker - pTx->txOut[x] = (char) U_CELL_SEC_C2C_FRAME_MARKER; - x++; - length = x; - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_ENCODE: output is (%d byte(s)):\n", length); - printBlock(pTx->txOut, length, true); -#endif - } - - return length; -} - -// Run chip to chip decode. -static size_t decode(const uCellSecC2cContext_t *pContext) -{ - size_t length = 0; - uCellSecC2cContextRx_t *pRx = pContext->pRx; - size_t x = 0; - size_t chunkLength; - size_t chunkLengthLimit; - uint16_t y; - char *pData = pRx->pRxIn; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - size_t z = 0; -#endif - - // Look for an opening frame marker - // The frame looks like this: - // --------------------------------------------- - // | F9 | | | | ... | | | | F9 | - // | length | body | CRC | - // --------------------------------------------- - // F9 is the frame marker, the two-byte length and - // CRC fields are little-endian. Length is of the - // body only. - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: buffer is %d byte(s) long.\n", - pRx->rxInLength); -#endif - - // We need to avoid acting on corrupt lengths (due to - // frame boundaries being mis-detected on loss of data) - // so work out what the maximum length is. - if (pContext->isV2) { - chunkLengthLimit = U_CELL_SEC_C2C_USER_MAX_RX_LENGTH_BYTES + - U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES + - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES; - } else { - chunkLengthLimit = U_CELL_SEC_C2C_USER_MAX_RX_LENGTH_BYTES + - U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES + - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES; - } - - while ((x < pRx->rxInLength) && - (*pData != (char) U_CELL_SEC_C2C_FRAME_MARKER)) { - pData++; - x++; - } - - if ((*pData == (char) U_CELL_SEC_C2C_FRAME_MARKER) && - ((pRx->rxInLength - x) > U_CELL_SEC_C2C_OVERHEAD_BYTES)) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - if (x > 0) { - uPortLog("U_CELL_SEC_C2C_DECODE: frame marker found after %d" - " byte(s) were discarded:\n", x); - printBlock(pRx->pRxIn, x, true); - } - uPortLog("U_CELL_SEC_C2C_DECODE: found a frame marker and" - " enough bytes following (%d) to potentially hold" - " a frame.\n", pRx->rxInLength - x); -#endif - // Have a frame marker and at least a non-zero length frame - // Grab the length, little endian - pData++; - // Cast in two stages to keep Lint happy - chunkLength = ((size_t) (int32_t) * pData); - pData++; - chunkLength += ((size_t) (int32_t) * pData) << 8; - pData++; - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - z = chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES; - if (z > pRx->rxInLength - x) { - z = pRx->rxInLength - x; - } - uPortLog("U_CELL_SEC_C2C_DECODE: chunk is %d byte(s) (including" - " %d bytes of overhead) of which we have %d byte(s):\n", - chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES, - U_CELL_SEC_C2C_OVERHEAD_BYTES, z); - printBlock(pData - 3, z, true); - z = 0; - if ((pRx->rxInLength - x) > chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES) { - z = (pRx->rxInLength - x) - (chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES); - uPortLog("U_CELL_SEC_C2C_DECODE: first 16 bytes of %d byte(s)" - " after chunk ends:\n", z); - if (z > 16) { - z = 16; - } - printBlock((pData - 3) + chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES, z, true); - } -#endif - // pData now points to the start of the - // encrypted data, x is the number of bytes discarded - // before we reach the frame marker - if ((chunkLength >= U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES) && - (pRx->rxInLength - x >= chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES) && - (chunkLength <= chunkLengthLimit)) { - // Length is sane, now calculate the - // CRC, which is over the chunk length - // plus the length value itself - y = fcsGenerate(pData - 2, chunkLength + 2); - // CRC is little-endian according to RFC 1662 - if ((*(pData + chunkLength) == (char) y) && - (*(pData + chunkLength + 1) == (char) (y >> 8))) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: FCS is good.\n"); -#endif - if (pContext->isV2) { - // In V2 the body is as follows: - // - // ----------------------------------------------- - // | IV | encrypted padded | truncated MAC | - // | 16 bytes | user data | 16 bytes | - // ----------------------------------------------- - // - // The CRC matches. Now we want - // to compute the HMAC tag across the - // encrypted text (i.e. minus the - // HMAC tag that forms part of - // the payload) plus the TE Secret. - // To do this concatenate the two - // into rxOut (there is enough room - // to do so). -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: version 2.\n"); - - uPortLog("U_CELL_SEC_C2C_DECODE: key:\n"); - printBlock(pContext->key, sizeof(pContext->key), true); - - uPortLog("U_CELL_SEC_C2C_DECODE: HMAC key:\n"); - printBlock(pContext->hmacKey, sizeof(pContext->hmacKey), true); - - uPortLog("U_CELL_SEC_C2C_DECODE: TE secret:\n"); - printBlock(pContext->teSecret, sizeof(pContext->teSecret), true); -#endif - x = chunkLength - - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES; - memcpy(pRx->rxOut, pData, x); - memcpy(pRx->rxOut + x, pContext->teSecret, - sizeof(pContext->teSecret)); - // Compute the HMAC SHA256 of this block - // using the HMAC tag as the key and put it - // on the end of rxOut as temporary storage. - if (uPortCryptoHmacSha256(pContext->hmacKey, - sizeof(pContext->hmacKey), - pRx->rxOut, - x + sizeof(pContext->teSecret), - pRx->rxOut + - x + sizeof(pContext->teSecret)) == 0) { - // Compare the first 16 bytes of - // it with the truncated MAC we received. - if (memcmp(pData + x, pRx->rxOut + x + - sizeof(pContext->teSecret), - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES) == 0) { - // The MAC's match, decrypt the contents - // into rxOut using the key and the IV from the - // incoming message. This will cause - // the IV in the incoming message to - // be overwritten with a new value - // but we don't care about that. - x = chunkLength - (U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES); -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: MACs match.\n"); - uPortLog("U_CELL_SEC_C2C_DECODE: IV:\n"); - printBlock(pData, U_CELL_SEC_C2C_IV_LENGTH_BYTES, true); -#endif - if (uPortCryptoAes128CbcDecrypt(pContext->key, - sizeof(pContext->key), - pData, /* IV */ - pData + U_CELL_SEC_C2C_IV_LENGTH_BYTES, - x, - pRx->rxOut) == 0) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: padded decrypted data:\n"); - printBlock(pRx->rxOut, x, false); -#endif - // Unpad the now plain text - length = unpad(pRx->rxOut, x); - // Copy it back into the receive buffer - // and set the output pointer - memcpy(pRx->pRxIn, pRx->rxOut, length); - pRx->pRxOut = pRx->pRxIn; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: decrypted data:\n"); - printBlock(pRx->rxOut, length, false); -#endif - } -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: truncated MAC mismatch.\n"); -#endif - } - } - } else { - // In V1 the body is as follows: - // - // --------------------------------------------- - // | encrypted padded | MAC | IV | - // | user data | 32 bytes | 16 bytes | - // --------------------------------------------- - // - // The CRC matches, decrypt the contents - // using the key and the IV from the - // incoming message. This will cause - // the IV in the incoming message to - // be overwritten with a new value - // but we don't care about that. - x = chunkLength - U_CELL_SEC_C2C_IV_LENGTH_BYTES; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: version 1.\n"); - - uPortLog("U_CELL_SEC_C2C_DECODE: key:\n"); - printBlock(pContext->key, sizeof(pContext->key), true); - - uPortLog("U_CELL_SEC_C2C_DECODE: IV:\n"); - printBlock(pData + x, - U_CELL_SEC_C2C_IV_LENGTH_BYTES, true); -#endif - if (uPortCryptoAes128CbcDecrypt(pContext->key, - sizeof(pContext->key), - pData + x, /* IV */ - pData, x, - pRx->rxOut) == 0) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - if (x >= U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES) { - uPortLog("U_CELL_SEC_C2C_DECODE: padded decrypted data:\n"); - printBlock(pRx->rxOut, - x - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES, false); - uPortLog("U_CELL_SEC_C2C_DECODE: decrypted MAC:\n"); - printBlock(pRx->rxOut + (x - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES), - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES, true); - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: chunk is too short" - " (%d byte(s)):\n", chunkLength); - } -#endif - // The decrypted data consists of the padded - // plain-text data plus the MAC on the end. - // Compute the SHA256 of the plain-text data - // (room is left to put it on the end of rxOut, - // after the end of the MAC since we no longer - // need the [over-written] IV) and then compare - // it with the MAC we received. - if (uPortCryptoSha256(pRx->rxOut, - x - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES, - pRx->rxOut + x) == 0) { - if (memcmp(pRx->rxOut + x - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES, - pRx->rxOut + x, - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES) == 0) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: MACs match.\n"); -#endif - // The MAC's match, get the unpadded length - // of the plain-text data - length = unpad(pRx->rxOut, - x - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES); - // Copy it back into the receive buffer - // and set the output pointer - memcpy(pRx->pRxIn, pRx->rxOut, length); - pRx->pRxOut = pRx->pRxIn; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: %d byte(s) decrypted" - " data:\n", length); - printBlock(pRx->rxOut, length, false); -#endif - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: MAC mismatch.\n"); - } - } - } - } - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: corrupt frame, FCS mismatch.\n"); - } - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - z = 0; -#endif - // Look for the closing frame marker - pData += chunkLength + 2; // +2 for the FCS bytes - x = pRx->rxInLength - (pData - pRx->pRxIn); - while ((x < pRx->rxInLength) && - (*pData != (char) U_CELL_SEC_C2C_FRAME_MARKER)) { - pData++; - x++; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - z++; -#endif - } -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: discarded %d byte(s)" - " looking for a closing frame marker.\n", z); -#endif - if ((x < pRx->rxInLength) && - (*pData == (char) U_CELL_SEC_C2C_FRAME_MARKER)) { - pData++; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: didn't find one though.\n"); -#endif - } - // Set the input length that is left - pRx->rxInLength -= pData - pRx->pRxIn; - } else { - if (chunkLength > chunkLengthLimit) { - // Error recovery: the chunk length is bigger - // than it can be, potentially a mis-detected - // frame-start flag due to corrupt input data. - // Search forward for a potential new frame - // start flag and dump up to that. - uPortLog("U_CELL_SEC_C2C_DECODE: corrupt frame," - " chunk length %d is larger than the" - " maximum %d byte(s).\n", - chunkLength, chunkLengthLimit); -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - z = 0; -#endif - x = pRx->rxInLength - (pData - pRx->pRxIn); - while ((x < pRx->rxInLength) && - (*pData != (char) U_CELL_SEC_C2C_FRAME_MARKER)) { - pData++; - x++; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - z++; -#endif - } -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: dumped %d byte(s)" - " looking for a frame marker to move on to.\n", z); -#endif - if ((x < pRx->rxInLength) && - (*pData == (char) U_CELL_SEC_C2C_FRAME_MARKER)) { - // This could be a starting or an ending frame marker: - // if there's nothing beyond it or the next byte is another - // frame marker then it is very likely an ending one so - // discard it -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: found a frame marker.\n"); -#endif - if ((x == pRx->rxInLength - 1) || - ((x < pRx->rxInLength - 1) && - (*(pData + 1) == (char) U_CELL_SEC_C2C_FRAME_MARKER))) { -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: it was likely a closing" - " frame marker, moving beyond it..\n"); -#endif - pData++; - } -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: didn't find one though.\n"); -#endif - } - } else { - // Don't have enough data to constitute a frame, - // set pData back to where it was - pData = pRx->pRxIn; -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: only have" - " %d byte(s) in the buffer, not enough" - " for all of our %d byte chunk (including" - " overheads), another %d byte(s) still" - " needed.\n", - pRx->rxInLength - x, - chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES, - chunkLength + U_CELL_SEC_C2C_OVERHEAD_BYTES - (pRx->rxInLength - x)); -#endif - } - } -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - } else { - uPortLog("U_CELL_SEC_C2C_DECODE: either no frame marker or" - " not enough bytes to form a frame yet.\n"); -#endif - } - -#ifdef U_CELL_SEC_C2C_DETAILED_DEBUG - uPortLog("U_CELL_SEC_C2C_DECODE: %d byte(s) consumed, %d byte(s) left.\n", - pData - pRx->pRxIn, pRx->rxInLength); -#endif - // Set pRxIn to wherever we've ended up - pRx->pRxIn = pData; - - return length; -} - -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS - * -------------------------------------------------------------- */ - -// Transmit intercept function. -const char *pUCellSecC2cInterceptTx(uAtClientHandle_t atHandle, - const char **ppData, - size_t *pLength, - void *pParameter) -{ - const char *pData = NULL; - size_t length = 0; - size_t lengthLeftOver = 0; - uCellSecC2cContext_t *pContext = (uCellSecC2cContext_t *) pParameter; - uCellSecC2cContextTx_t *pTx; - - (void) atHandle; - - if ((pContext != NULL) && (pLength != NULL)) { - pTx = pContext->pTx; - length = *pLength; - if ((ppData != NULL) && (length > 0)) { - // There is data to collect, add it to our transmit - // input buffer, taking into account how big our buffer - // would become when padding is added. - if (paddedLength(pTx->txInLength + length, - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES) > pTx->txInLimit) { - // If the padding would take us over, the length - // we can fit in is the limit minus one byte, - // since padding always adds at least one byte to - // the input - length = pTx->txInLimit - (pTx->txInLength + 1); - lengthLeftOver = *pLength - length; - } - memcpy(pTx->txIn + pTx->txInLength, *ppData, length); - pTx->txInLength += length; - // Move the data pointer on so that the caller - // can see how far we've got - *ppData += length; - } - // Assume that there is nothing to transmit onwards - *pLength = 0; - if (((ppData == NULL) && (pTx->txInLength > 0)) || - (lengthLeftOver > 0)) { - // Either we're out of room or we're being flushed - // so perform an encode - *pLength = encode(pContext); - pData = pTx->txOut; - pTx->txInLength = 0; - } - } - - return pData; -} - -// Obtain a random string to use as initial value. -// This is a default implementation only, intended to be -// overridden by the application. -U_WEAK -const char *pUCellSecC2cGetIv() -{ - for (size_t x = 0; x < sizeof(gIv); x++) { - gIv[x] = (char) rand(); - } - - return gIv; -} - -// Receive intercept function. -char *pUCellSecC2cInterceptRx(uAtClientHandle_t atHandle, - char **ppData, - size_t *pLength, - void *pParameter) -{ - char *pData = NULL; - uCellSecC2cContext_t *pContext = (uCellSecC2cContext_t *) pParameter; - uCellSecC2cContextRx_t *pRx; - - (void) atHandle; - - if ((pContext != NULL) && (pLength != NULL)) { - pRx = pContext->pRx; - pRx->rxInLength = *pLength; - if (pRx->rxInLength > 0) { - // Set the input and output pointers - pRx->pRxIn = *ppData; - pRx->pRxOut = NULL; - // Try to decode a frame - *pLength = decode(pContext); - // Set the return value - pData = pRx->pRxOut; - // Set the pointer to the - // amount consumed - *ppData = pRx->pRxIn; - } - } - - return pData; -} - -// End of file diff --git a/cell/src/u_cell_sec_c2c.h b/cell/src/u_cell_sec_c2c.h deleted file mode 100644 index 33fdc43c..00000000 --- a/cell/src/u_cell_sec_c2c.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2019-2023 u-blox - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _U_CELL_SEC_C2C_H_ -#define _U_CELL_SEC_C2C_H_ - -/* Only header files representing a direct and unavoidable - * dependency between the API of this module and the API - * of another module should be included here; otherwise - * please keep #includes to your .c files. */ - -/** @file - * @brief This header file defines functions for the u-blox security - * chip-to-chip feature. These functions are called from within the - * u_cell_sec.h API, they are not intended for external use. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* ---------------------------------------------------------------- - * COMPILE-TIME MACROS - * -------------------------------------------------------------- */ - -/** The maximum transmit (to the module) size for the user - * data in a chip to chip security chunk: this is a hard limit of - * the C2C protocol. Should be a multiple of 16 bytes for maximal - * efficiency. It is worth noting that the amount of user data that - * can be fitted into a chunk is always one less than this because - * the RFC 5652 padding scheme always adds at least one byte to the - * input data. - */ -#define U_CELL_SEC_C2C_USER_MAX_TX_LENGTH_BYTES 256 - -/** The maximum received (from the module) size for the - * user data in a chip to chip security chunk. This is dictated by - * the largest portion of TCP/UDP data we ever ask for from the module - * when running sockets, i.e. U_CELL_SOCK_MAX_SEGMENT_SIZE_BYTES - * (see u_cell_sock.h), plus the overhead for the "+USORD:" or - * "+USORF:" that precedes it, the surrounding quote marks and the - * line-ending. Should be a multiple of 16 bytes for maximal - * efficiency. If this is increased it will also be necessary to - * increase the size of U_CELL_AT_BUFFER_LENGTH_BYTES in cell.h - * since a whole chunk must be read-in before it can be decoded. - */ -#define U_CELL_SEC_C2C_USER_MAX_RX_LENGTH_BYTES (1024 + 16) // +16 for the AT-string overheads - -/** The chunk overhead for chip to chip security: start and - * stop flags, 2-byte length and 2-byte CRC. - */ -#define U_CELL_SEC_C2C_OVERHEAD_BYTES 6 - -/** The length of the initial vector for chip to chip security. - */ -#define U_CELL_SEC_C2C_IV_LENGTH_BYTES 16 - -/** The maximum length of padding that may be added to the - * plain-text input for the encryption algorithm to work. - */ -#define U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES 16 - -/* ---------------------------------------------------------------- - * TYPES - * -------------------------------------------------------------- */ - -/** Structure to hold context data for the chip to chip - * security operations in the MCU to module (transmit/encode) - * direction. - */ -typedef struct { - // Leave room for a generated MAC on the end of the input text - char txIn[U_CELL_SEC_C2C_USER_MAX_TX_LENGTH_BYTES + - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES]; - size_t txInLength; - size_t txInLimit; - char txOut[U_CELL_SEC_C2C_USER_MAX_TX_LENGTH_BYTES + - U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES + - U_CELL_SEC_C2C_IV_LENGTH_BYTES + - U_CELL_SEC_C2C_OVERHEAD_BYTES]; -} uCellSecC2cContextTx_t; - -/** Structure to hold context data for the chip to chip - * security operations in the module to MCU (receive/decode) - * direction. - */ -typedef struct { - char *pRxIn; - size_t rxInLength; - // Times two to leave room for a generated MAC, - // used during checking, on the end of the input - // text - char rxOut[U_CELL_SEC_C2C_USER_MAX_RX_LENGTH_BYTES + - U_CELL_SEC_C2C_MAX_PAD_LENGTH_BYTES + - (U_PORT_CRYPTO_SHA256_OUTPUT_LENGTH_BYTES * 2)]; - char *pRxOut; -} uCellSecC2cContextRx_t; - -/** Context data. - */ -typedef struct { - bool isV2; - char teSecret[U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES]; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmacKey[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - uCellSecC2cContextTx_t *pTx; - uCellSecC2cContextRx_t *pRx; -} uCellSecC2cContext_t; - -/* ---------------------------------------------------------------- - * FUNCTIONS - * -------------------------------------------------------------- */ - -/** Transmit intercept function, suitable for hooking - * into the AT stream with uAtClientStreamInterceptTx(). - * - * @param atHandle the handle of the AT client calling this - * function (not used). - * @param ppData a pointer to the data pointer into the - * buffer that is ready for transmission. - * This function will move the pointer - * forward to indicate how much of the - * buffer it has processed. - * @param pLength a pointer to the length of the data. - * When this function is called the value at - * pLength should be the amount of data in - * the buffer. When this function returns - * the value at pLength will be the amount - * of data that can now be transmitted. - * @param pContext a pointer to uCellSecC2cContext_t that - * has been populated appropriately. - * @return a pointer to the data that is now - * ready for transmission, or NULL on error. - * The amount of data at this pointer - * is the value at pLength. - */ -const char *pUCellSecC2cInterceptTx(uAtClientHandle_t atHandle, - const char **ppData, - size_t *pLength, - void *pContext); - -/** Obtain a random string to use as the initial value - * at each encryption in pUCellSecC2cIntercepTx(). - * This function is implemented with weak linkage, - * it should be overridden by the application defining - * a function of the same name which can provide better - * randomness. - * - * @return a pointer to a buffer of length - * U_CELL_SEC_C2C_IV_LENGTH_BYTES containing - * random values which must: - * - * a) remain present for the life of the chip to - * chip session, - * b) be updated with new random values every - * time this function is called. - */ -const char *pUCellSecC2cGetIv(void); - -/** Receive intercept function, suitable for hooking - * into the AT stream with uAtClientStreamInterceptRx(). - * This should be called repeatedly until it returns NULL, - * a which point it has run out of frames to process and - * ppData represents how far it has got into the buffer - * it was passed. - * - * @param atHandle the handle of the AT client calling this - * function (not used). - * @param ppData a pointer to the data pointer for the - * buffer of received data. - * This function will move the pointer - * forward to indicate how much of the - * buffer it has processed. - * @param pLength a pointer to the length of the data. - * When this function is called the value at - * pLength should be the amount of data in - * the buffer. When this function returns - * the value at pLength will be the amount - * of data that can now be processed by - * the AT client. - * @param pContext a pointer to uCellSecC2cContext_t that - * has been populated appropriately. - * @return a pointer to the data that is now - * ready to be processed by the AT client, - * or NULL if there is none. - * The amount of data at this pointer - * is the value at pLength. - */ -char *pUCellSecC2cInterceptRx(uAtClientHandle_t atHandle, - char **ppData, - size_t *pLength, - void *pContext); - -#ifdef __cplusplus -} -#endif - -#endif // _U_CELL_SEC_C2C_H_ - -// End of file diff --git a/cell/test/u_cell_mux_test.c b/cell/test/u_cell_mux_test.c index 2c7a65fb..c4caafd7 100644 --- a/cell/test/u_cell_mux_test.c +++ b/cell/test/u_cell_mux_test.c @@ -958,92 +958,6 @@ U_PORT_TEST_FUNCTION("[cellMux]", "cellMuxHttp") U_PORT_TEST_ASSERT(heapUsed <= 0); } -# ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - -/** Test C2C security over CMUX. - */ -U_PORT_TEST_FUNCTION("[cellMux]", "cellMuxC2c") -{ - uDeviceHandle_t cellHandle; - const uCellPrivateModule_t *pModule; - int32_t heapUsed; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - int32_t y; - - // In case a previous test failed - uCellTestPrivateCleanup(&gHandles); - - // Obtain the initial heap size - heapUsed = uPortGetHeapFree(); - - gTestPassed = false; - - // Do the standard preamble - U_PORT_TEST_ASSERT(uCellTestPrivatePreamble(U_CFG_TEST_CELL_MODULE_TYPE, - &gHandles, true) == 0); - cellHandle = gHandles.cellHandle; - - // Get the private module data so that we can check for CMUX support - pModule = pUCellPrivateGetModule(cellHandle); - U_PORT_TEST_ASSERT(pModule != NULL); - //lint -esym(613, pModule) Suppress possible use of NULL pointer - // for pModule from now on - - if (U_CELL_PRIVATE_HAS(pModule, U_CELL_PRIVATE_FEATURE_CMUX)) { - - U_TEST_PRINT_LINE("enabling CMUX...\n"); - U_PORT_TEST_ASSERT(uCellMuxEnable(cellHandle) == 0); - - if (uCellSecIsSupported(cellHandle)) { - U_TEST_PRINT_LINE("C2C pairing..."); - y = -1; - // Try this a few times as sometimes +CME ERROR: SEC busy - // can be returned if we've just recently powered on - for (size_t x = 0; (y < 0) && (x < 3); x++) { - y = uCellSecC2cPair(cellHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac); - if (y < 0) { - uPortTaskBlock(5000); - } - } - U_PORT_TEST_ASSERT(y == 0); - - U_TEST_PRINT_LINE("opening a secure session..."); - U_PORT_TEST_ASSERT(uCellSecC2cOpen(cellHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - U_TEST_PRINT_LINE("closing the session again..."); - U_PORT_TEST_ASSERT(uCellSecC2cClose(cellHandle) == 0); - } else { - U_TEST_PRINT_LINE("C2C security is not supported, skipping test."); - } - - U_TEST_PRINT_LINE("disabling CMUX...\n"); - U_PORT_TEST_ASSERT(uCellMuxDisable(cellHandle) == 0); - - } else { - U_TEST_PRINT_LINE("CMUX is not supported, not running tests."); - U_PORT_TEST_ASSERT(uCellMuxEnable(cellHandle) < 0); - } - - gTestPassed = true; - - // Do the standard postamble, leaving the module on for the next - // test to speed things up - uCellTestPrivatePostamble(&gHandles, false); - - // Check for memory leaks - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("we have leaked %d byte(s).", heapUsed); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= 0); -} - -# endif // U_CFG_TEST_SECURITY_C2C_TE_SECRET - /** Clean-up to be run at the end of this round of tests, just * in case there were test failures which would have resulted * in the deinitialisation being skipped. diff --git a/cell/test/u_cell_sec_c2c_test.c b/cell/test/u_cell_sec_c2c_test.c deleted file mode 100644 index d00f1b74..00000000 --- a/cell/test/u_cell_sec_c2c_test.c +++ /dev/null @@ -1,1717 +0,0 @@ -/* - * Copyright 2019-2023 u-blox - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Only #includes of u_* and the C standard library are allowed here, - * no platform stuff and no OS stuff. Anything required from - * the platform/OS must be brought in through u_port* to maintain - * portability. - */ - -/** @file - * @brief Tests for the internal cellular chip to chip security API. - * These should pass on all platforms. No cellular module is - * required to run this set of tests, all testing is back to back. - * IMPORTANT: see notes in u_cfg_test_platform_specific.h for the - * naming rules that must be followed when using the U_PORT_TEST_FUNCTION() - * macro. - */ - -#ifdef U_CFG_OVERRIDE -# include "u_cfg_override.h" // For a customer's configuration override -#endif - -#include "stddef.h" // NULL, size_t etc. -#include "stdint.h" // int32_t etc. -#include "stdbool.h" -#include "string.h" // memcpy(), memcmp(), strncpy(), strncat() -#include "ctype.h" // isdigit() - -#include "u_cfg_sw.h" -#include "u_cfg_os_platform_specific.h" // For #define U_CFG_OS_CLIB_LEAKS -#include "u_cfg_app_platform_specific.h" -#include "u_cfg_test_platform_specific.h" - -#include "u_error_common.h" - -#include "u_port.h" -#include "u_port_debug.h" -#include "u_port_os.h" -#include "u_port_uart.h" -#include "u_port_crypto.h" - -#include "u_at_client.h" - -#include "u_security.h" - -#include "u_cell_module_type.h" -#include "u_cell_file.h" -#include "u_cell.h" // Order is -#include "u_cell_net.h" // important here -#include "u_cell_private.h" // don't change it - -#include "u_cell_sec_c2c.h" - -/* ---------------------------------------------------------------- - * COMPILE-TIME MACROS - * -------------------------------------------------------------- */ - -/** The base string to put at the start of all prints from this test. - */ -#define U_TEST_PREFIX_BASE "U_CELL_C2C_TEST" - -/** The string to put at the start of all prints from this test - * that do not require an iteration on the end. - */ -#define U_TEST_PREFIX U_TEST_PREFIX_BASE ": " - -/** Print a whole line, with terminator, prefixed for this test - * file, no iteration version. - */ -#define U_TEST_PRINT_LINE(format, ...) uPortLog(U_TEST_PREFIX format "\n", ##__VA_ARGS__) - -/** The string to put at the start of all prints from this test - * where an interation is required on the end. - */ -#define U_TEST_PREFIX_X U_TEST_PREFIX_BASE "_%d: " - -/** Print a whole line, with terminator and iteration on the end, - * prefixed for this test file. - */ -#define U_TEST_PRINT_LINE_X(format, ...) uPortLog(U_TEST_PREFIX_X format "\n", ##__VA_ARGS__) - -/** The 16 byte TE secret to use during testing. - */ -#define U_CELL_SEC_C2C_TEST_TE_SECRET "\x00\x01\x02\x03\x04\x05\x06\x07" \ - "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - -/** The 16 byte key to use during testing. - */ -#define U_CELL_SEC_C2C_TEST_KEY "\x10\x11\x12\x13\x14\x15\x16\x17" \ - "\xe8\xe9\xea\xeb\xec\xed\xee\xef" - -/** The 16 byte truncated HMAC (or tag) to use - * during testing, needed for V2 only. - */ -#define U_CELL_SEC_C2C_TEST_HMAC_TAG "\x20\x21\x22\x23\x24\x25\x26\x27" \ - "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - -/** We only send back what we receive so the max length - * is the max TX length. - */ -#define U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES U_CELL_SEC_C2C_USER_MAX_TX_LENGTH_BYTES - -/** Guard contents. - */ -#define U_CELL_SEC_C2C_GUARD "deadarea" - -/** Length of guard contents. - */ -#define U_CELL_SEC_C2C_GUARD_LENGTH_BYTES 8 - -/** Check a buffer for underrun. - */ -#define U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(x) \ - U_PORT_TEST_ASSERT(memcmp(x, U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES) == 0) - -/** Check a buffer for overrun. - */ -#define U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(x) \ - U_PORT_TEST_ASSERT(memcmp(x + sizeof(x) - U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, \ - U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES) == 0) - -#ifndef U_CELL_SEC_C2C_TEST_TASK_STACK_SIZE_BYTES -/** The stack size for the test task. This is chosen to - * work for all platforms, the governing factor being ESP32, - * which seems to require around twice the stack of NRF52 - * or STM32F4 and more again in the version pre-built for - * Arduino. - */ -# define U_CELL_SEC_C2C_TEST_TASK_STACK_SIZE_BYTES 2304 -#endif - -#ifndef U_CELL_SEC_C2C_TEST_TASK_PRIORITY -/** The priority for the C2C test task, re-using the - * URC task priority for convenience. - */ -# define U_CELL_SEC_C2C_TEST_TASK_PRIORITY U_AT_CLIENT_URC_TASK_PRIORITY -#endif - -/* ---------------------------------------------------------------- - * TYPES - * -------------------------------------------------------------- */ - -/** Definition of clear text and encrypted version for back to back - * testing of the intercept functions. - */ -typedef struct { - bool isV2; - const char *pTeSecret; - const char *pKey; - const char *pHmacTag; /** Needed for V2 only. */ - const char *pClear; - size_t chunkLengthMax; - size_t numChunks; - // Allow up to five chunks for test purposes - size_t clearLength[5]; - // Allow up to five chunks for test purposes - size_t encryptedLength[5]; -} uCellSecC2cTest_t; - -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) - -/** Definition of an outgoing AT command, what the response - * should be plus an optional URC, for testing of the intercept - * functions inside the AT client. - * ORDER IS IMPORTANT: this is statically initialised. - */ -typedef struct { - bool isV2; - size_t chunkLengthMax; - const char *pTeSecret; - const char *pKey; - const char *pHmacTag; /** Needed for V2 only. */ - const char *pCommandPrefix; - bool isBinary; /** Command and response are either - a string or binary bytes. */ - const char *pCommandBody; - size_t commandBodyLength; - int32_t commandWaitTimeSeconds; /** How long the server should wait to receive the command. */ - const char *pUrcPrefix; /** Set to NULL if there is no URC. */ - const char *pUrcBody; /** Can only be a string. */ - const char *pResponsePrefix; - const char *pResponseBody; - size_t responseBodyLength; - int32_t responseWaitTimeSeconds; /** How long the client should wait to receive the response. */ -} uCellSecC2cTestAt_t; - -#endif - -/* ---------------------------------------------------------------- - * VARIABLES - * -------------------------------------------------------------- */ - -/** Storage for the common part of the security context. - */ -static uCellSecC2cContext_t gContext; - -/** Storage for the transmit/encode direction of the security context. - */ -static uCellSecC2cContextTx_t gContextTx; - -/** Storage for the receive/decode direction of the security context. - */ -static uCellSecC2cContextRx_t gContextRx; - -/** Test data. - */ -//lint -e{785} Suppress too few initialisers -//lint -e{786} Suppress string concatenation within initialiser -static uCellSecC2cTest_t gTestData[] = { - {/* 1: Basic V1 */ - false, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "Hello world!", U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, 1, {12}, - {1 + 2 + 12 + 4 /* pad to 16 */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1} - }, - {/* 2: Basic V2 */ - true, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "Hello world!", U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, 1, {12}, - {1 + 2 + 12 + 4 /* pad to 16 */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1} - }, - {/* 3: V1, clear text exactly 16 bytes (padding length) long */ - false, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "0123456789abcdef", U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, 1, {16}, - {1 + 2 + 32 /* padding causes this */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1} - }, - {/* 4: V2, clear text exactly 16 bytes (padding length) long */ - true, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "0123456789abcdef", U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, 1, {16}, - {1 + 2 + 32 /* padding causes this */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1} - }, - {/* 5: V1, clear text of exactly chunk length when padded */ - false, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "47 bytes, one less than the chunk length of 48.", 48, 1, {47}, - {1 + 2 + 48 /* max chunk length when padded */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1} - }, - {/* 6: V2, clear text of exactly chunk length when padded */ - true, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "47 bytes, one less than the chunk length of 48.", 48, 1, {47}, - {1 + 2 + 48 /* max chunk length when padded */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1} - }, - {/* 7: V1, clear text of greater than the chunk length */ - false, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "With a chunk length of 48 this is just a bit longer at 58.", 48, 2, {47, 11}, - { - 1 + 2 + 48 /* max chunk length */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1, - 1 + 2 + 16 /* remainder, padded to 16 */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1 - } - }, - {/* 8: V2, clear text of greater than the chunk length */ - true, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "With a chunk length of 48 this is just a bit longer at 58.", 48, 2, {47, 11}, - { - 1 + 2 + 48 /* max chunk length */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1, - 1 + 2 + 16 /* remainder, padded to 16 */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1 - } - }, - {/* 9: V1, a biggee*/ - false, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789", 48, 5, {47, 47, 47, 47, 12}, - { - 1 + 2 + 48 /* max chunk length */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1, - 1 + 2 + 16 /* remainder, padded to 16 */ + 32 /* SHA256 */ + 16 /* IV */ + 2 + 1 - } - }, - {/* 10: V2, a biggee*/ - true, U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789", 48, 5, {47, 47, 47, 47, 12}, - { - 1 + 2 + 48 /* max chunk length */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1, - 1 + 2 + 48 /* max chunk length */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1, - 1 + 2 + 16 /* remainder, padded to 16 */ + 16 /* IV */ + 16 /* HMAC TAG */ + 2 + 1 - } - } -}; - -/** A buffer for transmitted data. - */ -static char gBufferA[(U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES * 5) + - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)]; - -/** A buffer for received data. - */ -static char gBufferB[(U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES * 5) + - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)]; - -/** Handle for the AT client UART stream. - */ -static int32_t gUartAHandle = -1; - -/** Handle for the AT server UART stream (the reverse direction). - */ -static int32_t gUartBHandle = -1; - -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) - -/** A buffer for received URC data. - */ -static char gBufferC[(U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES * 5) + - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)]; - -/** For tracking heap lost to memory lost by the C library. - */ -static size_t gSystemHeapLost = 0; - -/** Count our way through the AT client-based tests. - */ -static size_t gAtTestCount = 0; - -/** For the server to track how much it has received - * and not yet decrypted. - */ -static size_t gAtServerLengthBuffered = 0; - -/** For the server to track how much it has decrypted. - */ -static size_t gAtServerLengthDecrypted = 0; - -/** For the server to track how long it has been waiting - * for stuff to arrive. - */ -static int32_t gAtServerWaitTimeMs = 0; - -/** Flag an error on the server side of the AT interface. - */ -static int32_t gAtServerErrorOrSize = 0; - -/** Flag an error in a URC. - */ -static int32_t gUrcErrorOrSize = 0; - -/** Count the number of URCs received. - */ -static size_t gUrcCount = 0; - -/** A chip-to-chip security context for the - * AT server side. - */ -static uCellSecC2cContext_t gAtServerContext; - -/** A receive chip-to-chip security context for the - * AT server-side to use to decrypt packets. - */ -static uCellSecC2cContextRx_t gAtServerContextRx; - -/** A transmit chip-to-chip security context for the - * AT server-side to use to encrypt packets. - */ -static uCellSecC2cContextTx_t gAtServerContextTx; - -/** Test data for the AT client based testing. - */ -//lint -e{786} Suppress string concatenation within initialiser -static const uCellSecC2cTestAt_t gTestAt[] = { - {/* 1: command with string parameter and OK response, no URC */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLAH0=", false, "thing-thing", 11, 1, /* Command with string parameter */ - NULL, NULL, /* No URC */ - NULL, NULL, 0, 1 /* No prefix or response body */ - }, - {/* 2: command with string parameter and information response, no URC */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLAH1=", false, "thing thang", 11, 1, /* Command with string parameter */ - NULL, NULL, /* No URC */ - "+BLAH1:", "thong", 5, 2 /* Information response prefix and body */ - }, - {/* 3: command with string parameter, URC inserted then OK response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLAH2=", false, "whotsit", 7, 1, /* Command with string parameter */ - "+UBOO:", "bang", /* URC inserted */ - NULL, NULL, 0, 1 /* No prefix or response body */ - }, - {/* 4: command with string parameter, URC inserted then information response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLAH3=", false, "questionable", 12, 1, /* Command with string parameter */ - "+UPAF:", "boomer", /* URC inserted */ - "+BLAH3:", "not at all", 10, 2 /* Information response prefix and body */ - }, - {/* 5: as (1) but with binary parameter and response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLING0=", true, "\x00\x01\x02\x04\xff\xfe\xfd\xfc", 8, 1, /* Command with binary parameter */ - NULL, NULL, /* No URC */ - NULL, NULL, 0, 1 /* No prefix or response body */ - }, - {/* 6: as (2) but with binary parameter and response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLING1=", true, "\xff\xfe\xfd\xfc\x03\x02\x01\x00", 8, 1, /* Command with binary parameter */ - NULL, NULL, /* No URC */ - "+BLAH1:", "\x00", 1, 2 /* Information response prefix and body */ - }, - {/* 7: as (3) but with binary parameter and response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLING2=", true, "\xaa\x55", 2, 1, /* Command with binary parameter */ - "+UBLIM:", "blam", /* URC inserted */ - NULL, NULL, 0, 1 /* No prefix or response body */ - }, - {/* 8: as (4) but with binary parameter and response */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+BLING3=", true, "\x55\xaa", 2, 1, /* Command with binary parameter */ - "+UPIF:", "blammer 1", /* URC inserted */ - "+BLING3:", "\x00\xff\x00\xff", 4, 2 /* Information response prefix and body */ - }, - {/* 9: as (8) but with V2 scheme */ - true /* V2 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "AT+BLING3=", true, "\x55\xaa", 2, 1, /* Command with binary parameter */ - "+UPIF:", "blammer 2", /* URC inserted */ - "+BLING3:", "\x00\xff\x00\xff", 4, 2 /* Information response prefix and body */ - }, - { /* 10: as (8) but with command and response of the maximum amount */ - /* of user data that can be fitted into a chunk (which is one less */ - /* than U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES because of the way */ - /* RFC 5652 padding works) */ - /* [This comment done as separate lines and with this exact indentation */ - /* as otherwise AStyle wants to move it another four spaces to the right */ - /* every time it processes it :-)] */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+VERYLONG_V1=", false, /* Command prefix 15 bytes */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:01234567890123456789012345678", 239, 5, - /* (total becomes 255 with \r command delimiter) */ - "+UPUF:", "little URC 1", /* URC inserted */ - "+VERYLONG_V1:", /* Information response prefix 13 bytes */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:012345678901234567890123456789", 240, 5 - /* (total becomes 255 with \r\n response delimiter) */ - }, - {/* 11: as (10) but with V2 scheme */ - true /* V2 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "AT+VERYLONG_V2=", false, /* Command prefix 15 bytes */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:01234567890123456789012345678", 239, 5, - /* (total becomes 255 with \r command delimiter) */ - "+UPUF:", "little URC 2", /* URC inserted */ - "+VERYLONG_V2:", /* Information response prefix 13 bytes */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:012345678901234567890123456789", 240, 5 - /* (total becomes 255 with \r\n response delimiter) */ - }, - {/* 12: a real biggee */ - false /* V1 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, NULL, - "AT+REALLYLONGONE=", false, /* Command prefix */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:0123456789012345678901234567890123456789" - "_____0005:0123456789012345678901234567890123456789" - "_____0006:0123456789012345678901234567890123456789" - "_____0007:0123456789012345678901234567890123456789" - "_____0008:0123456789012345678901234567890123456789" - "_____0009:0123456789012345678901234567890123456789", - 500, 15, - "+UPUF:", "little URC 3", /* URC inserted */ - "+ALSOAREALLYLONGONE:", /* Information response prefix */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:0123456789012345678901234567890123456789" - "_____0005:0123456789012345678901234567890123456789" - "_____0006:0123456789012345678901234567890123456789" - "_____0007:0123456789012345678901234567890123456789" - "_____0008:0123456789012345678901234567890123456789" - "_____0009:0123456789012345678901234567890123456789", - 500, 15 - }, - {/* 13: as (12) but with V2 scheme */ - true /* V2 */, U_CELL_SEC_C2C_CHUNK_MAX_LENGTH_BYTES, - U_CELL_SEC_C2C_TEST_TE_SECRET, U_CELL_SEC_C2C_TEST_KEY, U_CELL_SEC_C2C_TEST_HMAC_TAG, - "AT+ANOTHERREALLYLONGONE=", false, /* Command prefix */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:0123456789012345678901234567890123456789" - "_____0005:0123456789012345678901234567890123456789" - "_____0006:0123456789012345678901234567890123456789" - "_____0007:0123456789012345678901234567890123456789" - "_____0008:0123456789012345678901234567890123456789" - "_____0009:0123456789012345678901234567890123456789", - 500, 15, - "+UPUF:", "little URC 4", /* URC inserted */ - "+ALSOANOTHERREALLYLONGONE:", /* Information response prefix */ - "_____0000:0123456789012345678901234567890123456789" - "_____0001:0123456789012345678901234567890123456789" - "_____0002:0123456789012345678901234567890123456789" - "_____0003:0123456789012345678901234567890123456789" - "_____0004:0123456789012345678901234567890123456789" - "_____0005:0123456789012345678901234567890123456789" - "_____0006:0123456789012345678901234567890123456789" - "_____0007:0123456789012345678901234567890123456789" - "_____0008:0123456789012345678901234567890123456789" - "_____0009:0123456789012345678901234567890123456789", - 500, 15 - } -}; - -#endif - -/* ---------------------------------------------------------------- - * STATIC FUNCTIONS - * -------------------------------------------------------------- */ - -// Initialise the guard areas on the buffers. -static void initGuards() -{ - // The buffers here doesn't need null termination so we suppress the - // clang-tidy warning - // NOLINTBEGIN(bugprone-not-null-terminated-result) - memcpy(gBufferA, U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - memcpy(gBufferA + sizeof(gBufferA) - U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - memcpy(gBufferB, U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - memcpy(gBufferB + sizeof(gBufferB) - U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) - memcpy(gBufferC, U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - memcpy(gBufferC + sizeof(gBufferC) - U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - U_CELL_SEC_C2C_GUARD, U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); -#endif - // NOLINTEND(bugprone-not-null-terminated-result) -} - -// Print out text. -static void print(const char *pStr, size_t length) -{ - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - if (!isprint((int32_t) c)) { - // Print the hex - uPortLog("[%02x]", (unsigned char) c); - } else { - // Print the ASCII character - uPortLog("%c", c); - } - } -} - -// Print out binary. -//lint -esym(522, printHex) Suppress "lacks side effects", which -// will be true if logging is compiled out -static void printHex(const char *pStr, size_t length) -{ -#if U_CFG_ENABLE_LOGGING - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - uPortLog("[%02x]", (unsigned char) c); - } -#else - (void) pStr; - (void) length; -#endif -} - -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) -// On some platforms printing is line -// buffered so long strings will get lost unless -// they are chunked up: this function -// prints reasonable block sizes -//lint -esym(522, printBlock) Suppress "lacks side effects", which -// will be true if logging is compiled out -static void printBlock(const char *pStr, size_t length, - bool isBinary, size_t index) -{ -#if U_CFG_ENABLE_LOGGING - int32_t x = (int32_t) length; - int32_t y; - - while (x > 0) { - uPortLog(U_TEST_PREFIX_X, index); - y = x; - if (y > 32) { - y = 32; - } - if (isBinary) { - printHex(pStr, y); - } else { - print(pStr, y); - } - uPortLog("\n"); - // Don't overwhelm the poor debug output, - // there there - uPortTaskBlock(100); - x -= y; - pStr += y; - } -#else - (void) pStr; - (void) length; - (void) index; - (void) isBinary; -#endif -} -#endif - -// Check the result of an encryption. -static void checkEncrypted(size_t testIndex, - size_t chunkIndex, - const char *pEncrypted, - size_t encryptedLength, - const uCellSecC2cTest_t *pTestData) -{ - char *pData; - char *pDecrypted; - size_t length; - size_t previousLength = 0; - const char *pTmp = pEncrypted; - size_t x; - - // Make sure that testIndex is used to keep - // compilers happy if logging is compiled out - (void) testIndex; - - U_TEST_PRINT_LINE_X("encrypted chunk %d, %d byte(s):", - testIndex + 1, chunkIndex + 1, encryptedLength); - if (pTmp != NULL) { - length = encryptedLength; - while (length > 0) { - uPortLog(U_TEST_PREFIX_X, testIndex + 1); - x = length; - if (x > 16) { - x = 16; - } - printHex(pTmp, x); - uPortLog("\n"); - // Don't overwhelm the poor debug output, - // there there - uPortTaskBlock(100); - length -= x; - pTmp += x; - } - } else { - uPortLog("[NULL]"); - } - U_PORT_TEST_ASSERT(encryptedLength == pTestData->encryptedLength[chunkIndex]); - - for (x = 0; x < chunkIndex; x++) { - previousLength += pTestData->clearLength[x]; - } - - if (pEncrypted != NULL) { - // Decrypt the data block to check if the contents were correct - memcpy(gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + previousLength, - pEncrypted, encryptedLength); - pData = gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + previousLength; - length = encryptedLength; - pDecrypted = pUCellSecC2cInterceptRx(0, &pData, &length, - &gContext); - - uPortLog(U_TEST_PREFIX_X "decrypted becomes %d byte(s) \"", - testIndex + 1, length); - if (pDecrypted != NULL) { - print(pDecrypted, length); - } else { - uPortLog("[NULL]"); - } - uPortLog("\".\n"); - - U_PORT_TEST_ASSERT(pData == gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + - previousLength + encryptedLength); - U_PORT_TEST_ASSERT(length == pTestData->clearLength[chunkIndex]); - if (pDecrypted != NULL) { - U_PORT_TEST_ASSERT(memcmp(pDecrypted, pTestData->pClear + previousLength, - pTestData->clearLength[chunkIndex]) == 0); - } - } -} - -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) - -/** Send a thing over a UART. - */ -static int32_t atServerSendThing(int32_t uartHandle, - const char *pThing, - size_t length) -{ - int32_t sizeOrError = 0; - - U_TEST_PRINT_LINE_X("AT server sending %d byte(s):", - gAtTestCount + 1, length); - printBlock(pThing, length, true, gAtTestCount + 1); - - while ((length > 0) && (sizeOrError >= 0)) { - sizeOrError = uPortUartWrite(uartHandle, - pThing, length); - if (sizeOrError > 0) { - pThing += sizeOrError; - length -= sizeOrError; - } - } - - return sizeOrError; -} - -/** Encrypt and send a buffer of stuff. - */ -static int32_t atServerEncryptAndSendThing(int32_t uartHandle, - const char *pThing, - size_t length, - size_t chunkLengthMax) -{ - int32_t sizeOrError = 0; - int32_t x; - const char *pStart = pThing; - const char *pOut; - size_t outLength = length; - - // The AT server-side security context will - // have already been set up, just need to - // reset a few parameters - gAtServerContext.pTx->txInLength = 0; - gAtServerContext.pTx->txInLimit = chunkLengthMax; - - while ((pThing < pStart + length) && (sizeOrError >= 0)) { - pOut = pUCellSecC2cInterceptTx(0, &pThing, &outLength, - &gAtServerContext); - if (outLength > 0) { - // More than a chunk's worth must have accumulated, - // send it - x = atServerSendThing(uartHandle, pOut, outLength); - if (x >= 0) { - sizeOrError += x; - } else { - sizeOrError = x; - } - } - outLength = length - (pThing - pStart); - } - - if (sizeOrError >= 0) { - // Flush the remainder out of the encryption function - // by calling it again with NULL - outLength = 0; - pOut = pUCellSecC2cInterceptTx(0, NULL, &outLength, - &gAtServerContext); - if (outLength > 0) { - x = atServerSendThing(uartHandle, pOut, outLength); - if (x >= 0) { - sizeOrError += x; - } else { - sizeOrError = x; - } - } - } - - return sizeOrError; -} - -// Callback which receives commands, decrypts them, checks them -// and then sends back potentially a URC and a response. -//lint -e{818} suppress "could be declared as pointing to const", callback -// has to follow function signature -static void atServerCallback(int32_t uartHandle, uint32_t eventBitmask, - void *pParameters) -{ - size_t x; -//lint -esym(438, y) Suppress last value not used, which will -// be the case if logging is off - size_t y; - size_t z; - int32_t sizeOrError = 0; - const uCellSecC2cTestAt_t *pTestAt = *((uCellSecC2cTestAt_t **) pParameters); - size_t interceptLength = 0; - char *pData; - char *pTmp; - char *pDecrypted; - bool allReceived = false; -#if U_CFG_OS_CLIB_LEAKS - int32_t heapUsed; -#endif - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - if ((pTestAt != NULL) && - (eventBitmask & U_PORT_UART_EVENT_BITMASK_DATA_RECEIVED)) { - // Loop until there are no characters left to receive, - // filling up gBufferA - while ((sizeOrError >= 0) && - (gAtServerWaitTimeMs < pTestAt->commandWaitTimeSeconds * 1000) && - (uPortUartGetReceiveSize(uartHandle) > 0)) { - sizeOrError = uPortUartRead(uartHandle, - gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + - gAtServerLengthDecrypted + gAtServerLengthBuffered, - (sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)) - - (gAtServerLengthDecrypted + gAtServerLengthBuffered)); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - - if (sizeOrError > 0) { - gAtServerLengthBuffered += sizeOrError; - if (gAtServerLengthDecrypted + gAtServerLengthBuffered > - sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)) { - U_TEST_PRINT_LINE_X("AT server receive overflow.", - gAtTestCount + 1); - sizeOrError = -1; - } - } - - // Rest a while - uPortTaskBlock(100); - gAtServerWaitTimeMs += 100; - } - - if ((sizeOrError > 0) && (gAtServerLengthBuffered > 0)) { -#if U_CFG_OS_CLIB_LEAKS - // Calling printf() from a new task causes newlib - // to allocate additional memory which, depending - // on the OS/system, may not be recovered; - // take account of that here. - heapUsed = uPortGetHeapFree(); -#endif - U_TEST_PRINT_LINE_X("AT server has %d byte(s) to decrypt:", - gAtTestCount + 1, sizeOrError); - printBlock(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + - gAtServerLengthDecrypted, gAtServerLengthBuffered, - true, gAtTestCount + 1); - -#if U_CFG_OS_CLIB_LEAKS - // Take account of any heap lost through the first - // printf() - gSystemHeapLost += (size_t) (unsigned) (heapUsed - uPortGetHeapFree()); -#endif - - // Try to decrypt the received chunk or chunks in place - // by calling pUCellSecC2cInterceptRx with - // the server context. - pData = gBufferA + gAtServerLengthDecrypted + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES; - pTmp = pData; - x = gAtServerLengthBuffered; - interceptLength = gAtServerLengthBuffered; - sizeOrError = 0; - while ((x > 0) && (sizeOrError >= 0)) { - pDecrypted = pUCellSecC2cInterceptRx(0, &pData, - &interceptLength, - &gAtServerContext); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - - if (pDecrypted != NULL) { - U_TEST_PRINT_LINE_X("AT server decrypted %d byte(s):", - gAtTestCount + 1, interceptLength); - printBlock(pDecrypted, interceptLength, false, gAtTestCount + 1); - // Out intercept function returns a pointer to the - // start of the decrypted data in the buffer, i.e. - // to the value of pData when it was called, so just need - // to shuffle everything down so that the next pData - // we provide to the intercept function will be - // contiguous with the already decrypted data. - // The buffer is as below where "sizeOrError" - // is the decrypted data from a previous loop, - // "interceptLength" the decrypted data from this loop - // and "pData" is where we've got to in the buffer. - // - // |-------------------- X ------------------| - // +------------------+-----------------+-----------------------+ - // | sizeOrError | interceptLength | | - // +------------------+-----------------+-------+---------------+ - // pTmp pDecrypted pData - // = |------ Y ------| - // gBufferA + |-- Z --| - // U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + - // gAtServerLengthDecrypted - // - // y is the amount of data to move - y = pTmp + sizeOrError + x - pData; - // Grow size - sizeOrError += (int32_t) interceptLength; - // Do the move - memmove(pTmp + sizeOrError, pData, y); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - - // z is the distance it was moved - z = pData - (pTmp + sizeOrError); - // Shift pData down to match - pData -= z; - // Reduce the amount of data left to process - x -= z + interceptLength; - // The length passed to the intercept function - // becomes what we moved - interceptLength = y; - } - } - gAtServerLengthBuffered = x; - gAtServerLengthDecrypted += sizeOrError; - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - - x = strlen(pTestAt->pCommandPrefix); - if ((sizeOrError >= 0) && - (gAtServerLengthDecrypted == x + pTestAt->commandBodyLength + - U_AT_CLIENT_COMMAND_DELIMITER_LENGTH_BYTES)) { - // We've got the lot, check it - if (memcmp(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestAt->pCommandPrefix, x) == 0) { - if (memcmp(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x, pTestAt->pCommandBody, - pTestAt->commandBodyLength) == 0) { - // Should be the correct command delimiter on the end - if (memcmp(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x + - pTestAt->commandBodyLength, - U_AT_CLIENT_COMMAND_DELIMITER, - U_AT_CLIENT_COMMAND_DELIMITER_LENGTH_BYTES) == 0) { - // All good - U_TEST_PRINT_LINE_X("command received is as expected.", - gAtTestCount + 1); - allReceived = true; - } else { - uPortLog(U_TEST_PREFIX_X "expected command" - " delimiter \""); - printHex(U_AT_CLIENT_COMMAND_DELIMITER, - U_AT_CLIENT_COMMAND_DELIMITER_LENGTH_BYTES); - uPortLog("\" but received \""); - printHex(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + - x + pTestAt->commandBodyLength, - U_AT_CLIENT_COMMAND_DELIMITER_LENGTH_BYTES); - uPortLog("\".\n"); - sizeOrError = -400; - } - } else { - uPortLog(U_TEST_PREFIX_X "expected command body \"", - gAtTestCount + 1); - if (pTestAt->isBinary) { - printHex(pTestAt->pCommandBody, pTestAt->commandBodyLength); - } else { - print(pTestAt->pCommandBody, pTestAt->commandBodyLength); - } - uPortLog("\"\n but received \""); - if (pTestAt->isBinary) { - printHex(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x, - sizeOrError - x); - } else { - print(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x, - sizeOrError - x); - } - uPortLog("\".\n"); - sizeOrError = -300; - } - } else { - uPortLog(U_TEST_PREFIX_X "expected command prefix \"", - gAtTestCount + 1); - print(pTestAt->pCommandPrefix, x); - uPortLog("\"\n but received \""); - print(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, x); - uPortLog("\".\n"); - sizeOrError = -200; - } - } else { - if (sizeOrError >= 0) { - U_TEST_PRINT_LINE_X("decrypted %d byte(s) so far, expecting" - " command length %d byte(s) (including" - " terminator).", - gAtTestCount + 1, gAtServerLengthDecrypted, - x + pTestAt->commandBodyLength + - U_AT_CLIENT_COMMAND_DELIMITER_LENGTH_BYTES); - } - } - - if (allReceived) { - // If there is one, assemble and encrypt a URC - sizeOrError = 0; - if (pTestAt->pUrcPrefix != NULL) { - U_TEST_PRINT_LINE_X("AT server inserting URC \"%s %s\".", - gAtTestCount + 1, - pTestAt->pUrcPrefix, pTestAt->pUrcBody); - strncpy(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestAt->pUrcPrefix, - sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)); - strncat(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestAt->pUrcBody, - sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)); - strncat(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, "\r\n", - sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)); - y = uPortGetTickTimeMs(); - sizeOrError = atServerEncryptAndSendThing(uartHandle, - gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - strlen(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES), - pTestAt->chunkLengthMax); - U_TEST_PRINT_LINE_X("...took %d ms.", gAtTestCount + 1, - uPortGetTickTimeMs() - y); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - } - - if (sizeOrError >= 0) { - // Assemble and encrypt the response - U_TEST_PRINT_LINE_X("AT server sending response:", gAtTestCount + 1); - if ((pTestAt->pResponsePrefix != NULL) || (pTestAt->pResponseBody != NULL)) { - if (pTestAt->pResponsePrefix != NULL) { - U_TEST_PRINT_LINE_X("\"%s\" ...and then:", - gAtTestCount + 1, - pTestAt->pResponsePrefix); - } - if (pTestAt->pResponseBody != NULL) { - printBlock(pTestAt->pResponseBody, - pTestAt->responseBodyLength, - false, gAtTestCount + 1); - } else { - U_TEST_PRINT_LINE_X("[nothing]", gAtTestCount + 1); - } - } else { - U_TEST_PRINT_LINE_X("[nothing]", gAtTestCount + 1); - } - U_TEST_PRINT_LINE_X("...and then \"OK\".", gAtTestCount + 1); - *(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES) = 0; - if (pTestAt->pResponsePrefix != NULL) { - strncpy(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestAt->pResponsePrefix, - sizeof(gBufferA) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2)); - } - x = strlen(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - if (pTestAt->pResponseBody != NULL) { - memcpy(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x, - pTestAt->pResponseBody, pTestAt->responseBodyLength); - x += pTestAt->responseBodyLength; - } - memcpy(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + x, - "\r\nOK\r\n", 6); - x += 6; - y = uPortGetTickTimeMs(); - sizeOrError = atServerEncryptAndSendThing(uartHandle, - gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - x, pTestAt->chunkLengthMax); - U_TEST_PRINT_LINE_X("...took %d ms.", gAtTestCount + 1, - uPortGetTickTimeMs() - y); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - } - } else { - // Check for timeout - if (gAtServerWaitTimeMs > pTestAt->commandWaitTimeSeconds * 1000) { - U_TEST_PRINT_LINE_X("AT server timed-out after %d second(s)" - " with %d byte(s) decrypted.", - gAtTestCount + 1, gAtServerWaitTimeMs / 1000, - gAtServerLengthDecrypted); - if (gAtServerLengthBuffered > 0) { - U_TEST_PRINT_LINE_X("AT server buffer undecrypted buffer" - " contained %d byte(s):", gAtTestCount + 1, - gAtServerLengthBuffered); - printBlock(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + gAtServerLengthDecrypted, - gAtServerLengthBuffered, true, gAtTestCount + 1); - } else { - U_TEST_PRINT_LINE_X("AT server buffer had no undecrypted data.", - gAtTestCount + 1); - } - sizeOrError = -100; - } - } - } - } - - if ((sizeOrError < 0) || allReceived) { - // If there was an error or we've finished, reset - // these so that we can start again - gAtServerLengthBuffered = 0; - gAtServerLengthDecrypted = 0; - gAtServerWaitTimeMs = 0; - } - - gAtServerErrorOrSize = sizeOrError; -} - -// The URC handler for these tests. -//lint -e{818} suppress "could be declared as pointing to const", callback -// has to follow function signature -static void urcHandler(uAtClientHandle_t atClientHandle, void *pParameters) -{ - size_t x = 0; - int32_t sizeOrError; - const uCellSecC2cTestAt_t *pTestAt = (uCellSecC2cTestAt_t *) pParameters; -#if U_CFG_OS_CLIB_LEAKS - int32_t heapUsed; -#endif - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - // Read the single string parameter - sizeOrError = uAtClientReadString(atClientHandle, - gBufferC + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - sizeof(gBufferC) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2), - false); - if (pTestAt != NULL) { - if (pTestAt->pUrcBody != NULL) { - x = strlen(pTestAt->pUrcBody); - } -#if U_CFG_OS_CLIB_LEAKS - // Calling printf() from a new task causes newlib - // to allocate additional memory which, depending - // on the OS/system, may not be recovered; - // take account of that here. - heapUsed = uPortGetHeapFree(); -#endif - uPortLog(U_TEST_PREFIX_X "AT client received URC \"%s ", - gAtTestCount + 1, pTestAt->pUrcPrefix); - print(gBufferC + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, x); - uPortLog("\".\n"); -#if U_CFG_OS_CLIB_LEAKS - // Take account of any heap lost through the first - // printf() - gSystemHeapLost += (size_t) (unsigned) (heapUsed - uPortGetHeapFree()); -#endif - if (sizeOrError == x) { - if (memcmp(gBufferC + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, pTestAt->pUrcBody, x) != 0) { - uPortLog(U_TEST_PREFIX_X "AT client expected URC body \"", - gAtTestCount + 1); - print(pTestAt->pUrcBody, x); - uPortLog("\".\n"); - sizeOrError = -800; - } - } else { - U_TEST_PRINT_LINE_X("AT client expected URC body to be of length %d" - " but was %d.", gAtTestCount + 1, - x, sizeOrError); - sizeOrError = -700; - } - } else { -#if U_CFG_OS_CLIB_LEAKS - // Calling printf() from a new task causes newlib - // to allocate additional memory which, depending - // on the OS/system, may not be recovered; - // take account of that here. - heapUsed = uPortGetHeapFree(); -#endif - uPortLog(U_TEST_PREFIX_X "AT client received URC fragment \"", - gAtTestCount + 1); - print(gBufferC + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, sizeOrError); - uPortLog("\" when there wasn't meant to be one.\n"); -#if U_CFG_OS_CLIB_LEAKS - // Take account of any heap lost through the first - // printf() - gSystemHeapLost += (size_t) (unsigned) (heapUsed - uPortGetHeapFree()); -#endif - sizeOrError = -600; - } - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - gUrcCount++; - gUrcErrorOrSize = sizeOrError; -} - -#endif - -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS - * -------------------------------------------------------------- */ - -/** Test the transmit and receive intercept functions standalone. - * - * IMPORTANT: see notes in u_cfg_test_platform_specific.h for the - * naming rules that must be followed when using the - * U_PORT_TEST_FUNCTION() macro. - */ -U_PORT_TEST_FUNCTION("[cellSecC2c]", "cellSecC2cIntercept") -{ - uCellSecC2cTest_t *pTestData; - const char *pData; - const char *pDataStart; - const char *pOut; - size_t totalLength; - size_t outLength; - size_t numChunks; - int32_t heapUsed; - - // Initialise the guard areas at either end of the buffers - initGuards(); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - - // Whatever called us likely initialised the - // port so deinitialise it here to obtain the - // correct initial heap size - uPortDeinit(); - - // On some platforms (e.g. ESP32) the crypto libraries, - // which the underlying chip-to-chip encryption functions - // call, allocate a semaphore when they are first called - // which is never deleted. To avoid that getting in their - // way of our heap loss calculation, make a call to one - // of the crypto functions here. - uPortCryptoSha256(NULL, 0, gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - - heapUsed = uPortGetHeapFree(); - U_PORT_TEST_ASSERT(uPortInit() == 0); - - U_TEST_PRINT_LINE("testing chip-to-chip encryption and decryption" - " intercept functions standalone."); - - gContext.pTx = &gContextTx; - gContext.pRx = &gContextRx; - - for (size_t x = 0; x < sizeof(gTestData) / sizeof(gTestData[0]); x++) { - pTestData = &gTestData[x]; - totalLength = 0; - for (size_t y = 0; y < (sizeof(pTestData->clearLength) / - sizeof(pTestData->clearLength[0])); y++) { - totalLength += pTestData->clearLength[y]; - } - uPortLog(U_TEST_PREFIX_X "clear text %d byte(s) \"", x + 1, totalLength); - print(pTestData->pClear, totalLength); - uPortLog("\".\n"); - - // Populate context - gContext.isV2 = pTestData->isV2; - memcpy(gContext.teSecret, pTestData->pTeSecret, - sizeof(gContext.teSecret)); - memcpy(gContext.key, pTestData->pKey, - sizeof(gContext.key)); - if (pTestData->pHmacTag != NULL) { - memcpy(gContext.hmacKey, pTestData->pHmacTag, - sizeof(gContext.hmacKey)); - } - gContext.pTx->txInLength = 0; - gContext.pTx->txInLimit = pTestData->chunkLengthMax; - - memcpy(gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestData->pClear, totalLength); - pData = gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES; - numChunks = 0; - pDataStart = pData; - - // Do the encryption by calling the transmit intercept - do { - U_PORT_TEST_ASSERT(numChunks < pTestData->numChunks); - outLength = totalLength - (pData - pDataStart); - pOut = pUCellSecC2cInterceptTx(0, &pData, &outLength, - &gContext); - if (outLength > 0) { - // There will only be a result here if the input reached - // the chunk length limit - checkEncrypted(x, numChunks, pOut, outLength, pTestData); - numChunks++; - } - } while (pData < gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES + totalLength); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - - // Flush the transmit intercept by calling it again with NULL - outLength = 0; - pOut = pUCellSecC2cInterceptTx(0, NULL, &outLength, &gContext); - if (outLength > 0) { - checkEncrypted(x, numChunks, pOut, outLength, pTestData); - numChunks++; - } - - U_PORT_TEST_ASSERT(numChunks == pTestData->numChunks); - // When done, the RX buffer should contain the complete - // clear message - U_PORT_TEST_ASSERT(memcmp(gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestData->pClear, totalLength) == 0); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - } - - uPortDeinit(); - -#ifndef __XTENSA__ - // Check for memory leaks - // TODO: this if'ed out for ESP32 (xtensa compiler) at - // the moment as there is an issue with ESP32 hanging - // on to memory in the UART drivers that can't easily be - // accounted for. - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("we have leaked %d byte(s).", heapUsed); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= 0); -#else - (void) heapUsed; -#endif -} - -#if (U_CFG_TEST_UART_A >= 0) && (U_CFG_TEST_UART_B >= 0) - -/** Test use of the intercept functions inside the AT client - * with a dummy AT server to loop stuff back to us. - * NOTE: this test is a bit of a balancing act; need to - * print lots of debug so that we can see what's going on - * in case there's a problem but at the same time it has two - * independent tasks running between two actual serial ports - * without flow control (out of pins) and with deliberate - * timing constraints in the AT client. So, it works, but - * I suggest you don't fiddle with any of the timings, it's - * quite carefully tuned to work on all platforms. - */ -U_PORT_TEST_FUNCTION("[cellSecC2c]", "cellSecC2cAtClient") -{ - uAtClientHandle_t atClientHandle; - int32_t sizeOrError; - const uCellSecC2cTestAt_t *pTestAt = NULL; - const char *pLastAtPrefix = NULL; - size_t urcCount = 0; - int32_t stackMinFreeBytes; - int32_t heapUsed; - int32_t heapClibLossOffset = (int32_t) gSystemHeapLost; - int32_t y; - - // Initialise the guard areas at either end of the buffers - initGuards(); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - gContext.pTx = &gContextTx; - gContext.pRx = &gContextRx; - - // Whatever called us likely initialised the - // port so deinitialise it here to obtain the - // correct initial heap size - uPortDeinit(); - - // On some platforms (e.g. ESP32) the crypto libraries, - // which the underlying chip-to-chip encryption functions - // call, allocate a semaphore when they are first called - // which is never deleted. To avoid that getting in their - // way of our heap loss calculation, make a call to one - // of the crypto functions here. - uPortCryptoSha256(NULL, 0, gBufferA + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES); - - heapUsed = uPortGetHeapFree(); - - U_TEST_PRINT_LINE("testing chip-to-chip encryption and decryption" - " intercept functions inside an AT client."); - - U_PORT_TEST_ASSERT(uPortInit() == 0); - - gUartAHandle = uPortUartOpen(U_CFG_TEST_UART_A, - U_CFG_TEST_BAUD_RATE, - NULL, - U_CFG_TEST_UART_BUFFER_LENGTH_BYTES, - U_CFG_TEST_PIN_UART_A_TXD, - U_CFG_TEST_PIN_UART_A_RXD, - U_CFG_TEST_PIN_UART_A_CTS, - U_CFG_TEST_PIN_UART_A_RTS); - U_PORT_TEST_ASSERT(gUartAHandle >= 0); - - U_TEST_PRINT_LINE("AT client will be on UART %d, TXD pin %d (0x%02x)" - " and RXD pin %d (0x%02x).", - U_CFG_TEST_UART_A, U_CFG_TEST_PIN_UART_A_TXD, - U_CFG_TEST_PIN_UART_A_TXD, U_CFG_TEST_PIN_UART_A_RXD, - U_CFG_TEST_PIN_UART_A_RXD); - - gUartBHandle = uPortUartOpen(U_CFG_TEST_UART_B, - U_CFG_TEST_BAUD_RATE, - NULL, - U_CFG_TEST_UART_BUFFER_LENGTH_BYTES, - U_CFG_TEST_PIN_UART_B_TXD, - U_CFG_TEST_PIN_UART_B_RXD, - U_CFG_TEST_PIN_UART_B_CTS, - U_CFG_TEST_PIN_UART_B_RTS); - U_PORT_TEST_ASSERT(gUartBHandle >= 0); - - U_TEST_PRINT_LINE("AT server will be on UART %d, TXD pin %d (0x%02x)" - " and RXD pin %d (0x%02x).", - U_CFG_TEST_UART_B, U_CFG_TEST_PIN_UART_B_TXD, - U_CFG_TEST_PIN_UART_B_TXD, U_CFG_TEST_PIN_UART_B_RXD, - U_CFG_TEST_PIN_UART_B_RXD); - - U_TEST_PRINT_LINE("make sure these pins are cross-connected."); - - // Set up an AT server event handler on UART B - // This event handler receives our encrypted chunks, decrypts - // them and sends back an encrypted response for us to decrypt. - U_PORT_TEST_ASSERT(uPortUartEventCallbackSet(gUartBHandle, - U_PORT_UART_EVENT_BITMASK_DATA_RECEIVED, - atServerCallback, (void *) &pTestAt, - U_CELL_SEC_C2C_TEST_TASK_STACK_SIZE_BYTES, - U_CELL_SEC_C2C_TEST_TASK_PRIORITY) == 0); - - U_PORT_TEST_ASSERT(uAtClientInit() == 0); - - U_TEST_PRINT_LINE("adding an AT client on UART %d...", U_CFG_TEST_UART_A); - atClientHandle = uAtClientAdd(gUartAHandle, U_AT_CLIENT_STREAM_TYPE_UART, - NULL, U_CELL_AT_BUFFER_LENGTH_BYTES); - U_PORT_TEST_ASSERT(atClientHandle != NULL); - - // Add transmit and receive intercepts - uAtClientStreamInterceptTx(atClientHandle, pUCellSecC2cInterceptTx, - (void *) &gContext); - uAtClientStreamInterceptRx(atClientHandle, pUCellSecC2cInterceptRx, - (void *) &gContext); - - U_TEST_PRINT_LINE("%d chunks(s) to execute.", sizeof(gTestAt) / sizeof(gTestAt[0])); - for (size_t x = 0; x < sizeof(gTestAt) / sizeof(gTestAt[0]); x++) { - pTestAt = &(gTestAt[x]); - - // Populate the AT client-side chip to chip - // security context - gContext.isV2 = pTestAt->isV2; - memcpy(gContext.teSecret, pTestAt->pTeSecret, - sizeof(gContext.teSecret)); - memcpy(gContext.key, pTestAt->pKey, - sizeof(gContext.key)); - if (pTestAt->pHmacTag != NULL) { - memcpy(gContext.hmacKey, pTestAt->pHmacTag, - sizeof(gContext.hmacKey)); - } - gContext.pTx->txInLimit = pTestAt->chunkLengthMax; - - // Copy this into the AT server-side chip to chip - // security context - memcpy(&gAtServerContext, &gContext, - sizeof(gAtServerContext)); - gAtServerContext.pRx = &gAtServerContextRx; - gAtServerContext.pTx = &gAtServerContextTx; - - if (pTestAt->pUrcPrefix != NULL) { - urcCount++; - } - // Add a URC handler if there is one, removing the old one - if (pTestAt->pUrcPrefix != NULL) { - if (pLastAtPrefix != NULL) { - uAtClientRemoveUrcHandler(atClientHandle, pLastAtPrefix); - } - // GCC can complain here that - // we're passing a const pointer - // (pTestAt) as a parameter - // that is not const. - // Since this is an anonymous parameter - // passed to a callback we have no - // choice, the callback itself - // has to know how to behave, we - // can't dictate that. - U_PORT_TEST_ASSERT(uAtClientSetUrcHandler(atClientHandle, - pTestAt->pUrcPrefix, - urcHandler, - //lint -e(1773) Suppress "attempt to cast away const": - // pTestAt definitely points to a const but the URC handler - // parameter is a generic part of a callback which can't know - // that. - (void *) pTestAt) == 0); - pLastAtPrefix = pTestAt->pUrcPrefix; - } - - // Send the AT string: we only test sending strings or - // binary here, the other uAtClientWritexxx - // operations are assumed to work in the same way - U_TEST_PRINT_LINE_X("AT client sending: \"%s\" and then...", - x + 1, pTestAt->pCommandPrefix); - printBlock(pTestAt->pCommandBody, - pTestAt->commandBodyLength, - pTestAt->isBinary, x + 1); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - uAtClientLock(atClientHandle); - - // We do a LOT of debug prints in the AT server task which responds - // to this and we have to take our time with them so as not to - // overload the debug stream on some platforms so give this plenty - // time: enough time for the command to get there and be printed - // out, and the response to be printed out and then received and - // printed out - y = 20000 + (pTestAt->commandWaitTimeSeconds * 1000 * 2) + - (pTestAt->responseWaitTimeSeconds * 1000 * 3); - U_TEST_PRINT_LINE_X("AT timeout set to %d ms.", x + 1, y); - uAtClientTimeoutSet(atClientHandle, y); - y = (int32_t) uPortGetTickTimeMs(); - uAtClientCommandStart(atClientHandle, pTestAt->pCommandPrefix); - if (pTestAt->isBinary) { - // Binary bytes - U_PORT_TEST_ASSERT(uAtClientWriteBytes(atClientHandle, pTestAt->pCommandBody, - pTestAt->commandBodyLength, false) == - pTestAt->commandBodyLength); - } else { - // String without quotes - uAtClientWriteString(atClientHandle, pTestAt->pCommandBody, false); - } - uAtClientCommandStop(atClientHandle); - - uPortLog(U_TEST_PREFIX_X "AT client send took %d ms, waiting for response", - x + 1, ((int32_t) uPortGetTickTimeMs()) - y); - if (pTestAt->pResponsePrefix != NULL) { - uPortLog(" \"%s\"", pTestAt->pResponsePrefix); - } - uPortLog("...\n"); - y = (int32_t) uPortGetTickTimeMs(); - - uAtClientResponseStart(atClientHandle, pTestAt->pResponsePrefix); - if (pTestAt->isBinary) { - // Standalone bytes - sizeOrError = uAtClientReadBytes(atClientHandle, - gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - sizeof(gBufferB) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2), - true); - } else { - // Quoted string - sizeOrError = uAtClientReadString(atClientHandle, gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - sizeof(gBufferB) - (U_CELL_SEC_C2C_GUARD_LENGTH_BYTES * 2), - false); - } - uAtClientResponseStop(atClientHandle); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - // Wait a moment before printing so that any URCs get to - // be printed without us trampling over them - uPortTaskBlock(1000); - U_TEST_PRINT_LINE_X("AT client read result (after %d ms wait) is %d.", - x + 1, ((int32_t) uPortGetTickTimeMs()) - y, sizeOrError); - U_PORT_TEST_ASSERT(sizeOrError >= 0); - U_TEST_PRINT_LINE_X("AT client received response:", x + 1); - if (sizeOrError > 0) { - if (pTestAt->pResponsePrefix != NULL) { - U_TEST_PRINT_LINE_X("\"%s\" and then...", x + 1, pTestAt->pResponsePrefix); - } - printBlock(gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, sizeOrError, - pTestAt->isBinary, x + 1); - } else { - U_TEST_PRINT_LINE_X("[nothing]", x + 1); - } - - U_PORT_TEST_ASSERT(uAtClientUnlock(atClientHandle) == 0); - - U_PORT_TEST_ASSERT(sizeOrError == pTestAt->responseBodyLength); - if (sizeOrError > 0) { - U_PORT_TEST_ASSERT(memcmp(gBufferB + U_CELL_SEC_C2C_GUARD_LENGTH_BYTES, - pTestAt->pResponseBody, - pTestAt->responseBodyLength) == 0); - } - - U_PORT_TEST_ASSERT(gAtServerErrorOrSize >= 0); - U_PORT_TEST_ASSERT(gUrcErrorOrSize >= 0); - U_PORT_TEST_ASSERT(urcCount == gUrcCount); - U_TEST_PRINT_LINE_X("...and then \"OK\"", x + 1); - - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferA); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferB); - U_CELL_SEC_C2C_CHECK_GUARD_UNDERRUN(gBufferC); - U_CELL_SEC_C2C_CHECK_GUARD_OVERRUN(gBufferC); - - gAtTestCount++; - // Wait between iterations to avoid the debug - // streams overunning - uPortTaskBlock(1000); - } - U_PORT_TEST_ASSERT(gAtTestCount == sizeof(gTestAt) / sizeof(gTestAt[0])); - - stackMinFreeBytes = uAtClientUrcHandlerStackMinFree(atClientHandle); - if (stackMinFreeBytes != (int32_t) U_ERROR_COMMON_NOT_SUPPORTED) { - U_TEST_PRINT_LINE("AT client URC task had min %d byte(s)" - " stack free out of %d.", stackMinFreeBytes, - U_CELL_SEC_C2C_TEST_TASK_STACK_SIZE_BYTES); - U_PORT_TEST_ASSERT(stackMinFreeBytes > 0); - } - - stackMinFreeBytes = uAtClientCallbackStackMinFree(); - if (stackMinFreeBytes != (int32_t) U_ERROR_COMMON_NOT_SUPPORTED) { - U_TEST_PRINT_LINE("AT client callback task had min %d byte(s)" - " stack free out of %d.", stackMinFreeBytes, - U_AT_CLIENT_CALLBACK_TASK_STACK_SIZE_BYTES); - U_PORT_TEST_ASSERT(stackMinFreeBytes > 0); - } - - // Check the stack extent for the task on the end of the - // event queue - stackMinFreeBytes = uPortUartEventStackMinFree(gUartBHandle); - if (stackMinFreeBytes != (int32_t) U_ERROR_COMMON_NOT_SUPPORTED) { - U_TEST_PRINT_LINE("the AT server event queue task had %d byte(s)" - " free out of %d.", stackMinFreeBytes, - U_CELL_SEC_C2C_TEST_TASK_STACK_SIZE_BYTES); - U_PORT_TEST_ASSERT(stackMinFreeBytes > 0); - } - - U_TEST_PRINT_LINE("removing AT client..."); - uAtClientRemove(atClientHandle); - uAtClientDeinit(); - - uPortUartClose(gUartBHandle); - gUartBHandle = -1; - uPortUartClose(gUartAHandle); - gUartAHandle = -1; - uPortDeinit(); - - // Check for memory leaks - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("%d byte(s) of heap were lost to the C library" - " during this test and we have leaked %d byte(s).", - gSystemHeapLost - heapClibLossOffset, - heapUsed - (gSystemHeapLost - heapClibLossOffset)); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT((heapUsed < 0) || - (heapUsed <= ((int32_t) gSystemHeapLost) - heapClibLossOffset)); -} - -#endif - -/** Clean-up to be run at the end of this round of tests, just - * in case there were test failures which would have resulted - * in the deinitialisation being skipped. - */ -U_PORT_TEST_FUNCTION("[cellSecC2c]", "cellSecC2cCleanUp") -{ - int32_t minFreeStackBytes; - - uAtClientDeinit(); - if (gUartAHandle >= 0) { - uPortUartClose(gUartAHandle); - } - if (gUartBHandle >= 0) { - uPortUartClose(gUartBHandle); - } - - minFreeStackBytes = uPortTaskStackMinFree(NULL); - if (minFreeStackBytes != (int32_t) U_ERROR_COMMON_NOT_SUPPORTED) { - U_TEST_PRINT_LINE("main task stack had a minimum of %d byte(s)" - " free at the end of these tests.", minFreeStackBytes); - U_PORT_TEST_ASSERT(minFreeStackBytes >= - U_CFG_TEST_OS_MAIN_TASK_MIN_FREE_STACK_BYTES); - } - - uPortDeinit(); -} - -// End of file diff --git a/common/at_client/src/u_at_client.c b/common/at_client/src/u_at_client.c index 4fb17774..1bb651e4 100644 --- a/common/at_client/src/u_at_client.c +++ b/common/at_client/src/u_at_client.c @@ -1327,14 +1327,14 @@ static bool bufferFill(uAtClientInstance_t *pClient, // received from the UART, readIndex is how far into that has // been read off by the AT parsing code. Normally "length" and // "lengthBuffered" are the same, they only differ if there is - // an active intercept function, e.g. for C2C security; stuff - // between "length" and lengthBuffered has not yet been - // processed by the intercept function (e.g. it's just new or - // there's not enough of it to form some sort of frame structure - // that the intercept function needs). The intercept function - // reads the stuff between "length" and lengthBuffered at which - // point it may make it available as normal stuff which this - // function then copies down into the unread part of "length". + // an active intercept function; stuff between "length" and + // lengthBuffered has not yet been processed by the intercept + // function (e.g. it's just new or there's not enough of it to + // form some sort of frame structure that the intercept function + // needs). The intercept function reads the stuff between + // "length" and lengthBuffered at which point it may make it + // available as normal stuff which this function then copies + // down into the unread part of "length". LOG_BUFFER_FILL(1); diff --git a/common/security/api/u_security.h b/common/security/api/u_security.h index 41b4b2c4..fd900125 100644 --- a/common/security/api/u_security.h +++ b/common/security/api/u_security.h @@ -50,40 +50,6 @@ extern "C" { */ #define U_SECURITY_ROOT_OF_TRUST_UID_LENGTH_BYTES 8 -/** The amount of additional space required in a message - * buffer to accommodate the header for end to end encryption; - * this is the value for E2E encryption version 1 and is - * included for backwards-compatibility: please instead - * use the V1 or V2 values depending on what the function - * uSecurityE2eGetVersion() returns, or use - * #U_SECURITY_E2E_HEADER_LENGTH_MAX_BYTES. - */ -#define U_SECURITY_E2E_HEADER_LENGTH_BYTES 32 - -/** The amount of additional space required in a message - * buffer to accommodate the header for end to end - * encryption version 1. - */ -#define U_SECURITY_E2E_V1_HEADER_LENGTH_BYTES 32 - -/** The amount of additional space required in a message - * buffer to accommodate the header for end to end - * encryption version 2. - */ -#define U_SECURITY_E2E_V2_HEADER_LENGTH_BYTES 28 - -/** The maximum amount of additional space required in a message - * buffer to accommodate the header for any version of end to end - * encryption. - */ -#define U_SECURITY_E2E_HEADER_LENGTH_MAX_BYTES U_SECURITY_E2E_V1_HEADER_LENGTH_BYTES - -/** The minimum amount of additional space required in a message - * buffer to accommodate the header for any version of end to end - * encryption. - */ -#define U_SECURITY_E2E_HEADER_LENGTH_MIN_BYTES U_SECURITY_E2E_V2_HEADER_LENGTH_BYTES - /** The maximum amount of storage required for a generated * pre-shared key. */ @@ -94,28 +60,6 @@ extern "C" { */ #define U_SECURITY_PSK_ID_MAX_LENGTH_BYTES 32 -/** The length of the secret required to be generated by - * this MCU in order to complete pairing with the module - * for AT interface encruption. - */ -#define U_SECURITY_C2C_TE_SECRET_LENGTH_BYTES 16 - -/** The length of the encryption key returned by a module - * during pairing for chip to chip encryption. - */ -#define U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES 16 - -/** The length of the HMAC returned by a module during - * pairing for chip to chip encryption. - */ -#define U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES 16 - -/** The length of the chip to chip confirmation tag - * returned by a module during pairing for chip to chip - * encryption. - */ -#define U_SECURITY_C2C_CONFIRMATION_TAG_LENGTH_BYTES 16 - /* ---------------------------------------------------------------- * TYPES * -------------------------------------------------------------- */ @@ -186,103 +130,6 @@ int32_t uSecurityGetSerialNumber(uDeviceHandle_t devHandle, int32_t uSecurityGetRootOfTrustUid(uDeviceHandle_t devHandle, char *pRootOfTrustUid); -/* ---------------------------------------------------------------- - * FUNCTIONS: CHIP TO CHIP SECURITY - * -------------------------------------------------------------- */ - -/** Pair a module's AT interface with this MCU for chip to chip - * security. This feature is available by arrangement with - * u-blox. The pairing process is expected to be carried out in a - * secure production environment *before* the device is boostrapped, - * i.e. before the module is allowed to contact the u-blox security - * services over the network. Only if a special feature, - * "LocalC2CKeyPairing", is enabled in the u-blox security service - * can pairing be carried out after a device has been sealed, since - * this represents a potential attack vector. - * - * Once this function returns successfully the values of the locally - * generated pTESecret and the pKey and pHMac parameters returned must - * be stored securely on this MCU by the caller. Later, after the - * module has bootstrapped and been sealed the parameters may be used - * in a call to uSecurityC2cOpen() to encrypt communication over the - * AT interface between this MCU and the module. - * - * Note: if the module has very recently powered-on it may return - * "+CME ERROR: SEC busy" when asked to perform security pairing, - * hence it may be worth re-trying C2C pairing on failure. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @param pTESecret a pointer to the fixed-length 16 byte - * secret generated by this MCU (the - * "Terminal Equipment") to be used in the - * pairing process; cannot be NULL. - * @param pKey a place to store the fixed-length 16 byte - * encryption key that must be used when a - * secure AT session is opened. It is up to - * the caller to store this securely in - * non-volatile memory for future use. - * Cannot be NULL. - * @param pHMac a place to store the fixed-length - * 16 byte HMAC that must be used when a - * secure AT session is opened. It is up - * to the caller to store this securely in - * non-volatile memory for future use. - * Cannot be NULL. - * @return zero on success else negative error code. - */ - -int32_t uSecurityC2cPair(uDeviceHandle_t devHandle, - const char *pTESecret, - char *pKey, char *pHMac); - -/** Open a secure AT session. Once this has returned - * successfully the AT client will encrypt the outgoing data - * stream to the module and decrypt data received back from - * the module using the keys provided. pTeSecret, pKey, - * and pHMax are provided from non-volatile storage on the - * MCU, the latter two resulting from the C2C pairing process - * carried out earlier. Once this function returns successfully - * all AT communications will be encrypted by the AT client until - * uSecurityC2cClose() is called or the module is powered - * off or rebooted. If a chip to chip security session is - * already open when this is called it will do nothing and - * return success. - * - * Note: when using C2C over serial lines it is very important - * to ensure no data loss, otherwise whole blocks of encrypted - * data will be lost; always make sure HW flow control is enabled - * on your UART port. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @param pTESecret a pointer to the fixed-length 16 byte - * secret key that was used during pairing; - * cannot be NULL. - * @param pKey a pointer to the fixed-length 16 byte - * encryption key that was returned during - * pairing; cannot be NULL. - * @param pHMac a pointer to the fixed-length 16 byte - * HMAC that was returned during pairing; - * cannot be NULL. - * @return zero on success else negative error code. - */ -int32_t uSecurityC2cOpen(uDeviceHandle_t devHandle, - const char *pTESecret, - const char *pKey, - const char *pHMac); - -/** Close a secure AT session. Once this has returned - * successfully the AT exchange with the module will once - * more be unencrypted. If there is no open C2C session - * this function will do nothing and return success. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @return zero on success else negative error code. - */ -int32_t uSecurityC2cClose(uDeviceHandle_t devHandle); - /* ---------------------------------------------------------------- * FUNCTIONS: SEAL * -------------------------------------------------------------- */ @@ -300,14 +147,8 @@ int32_t uSecurityC2cClose(uDeviceHandle_t devHandle); * uDeviceOpen(). * @param pDeviceProfileUid the null-terminated device * profile UID string provided by - * u-blox. - * Note: if you have activated your module - * via the Thingstream portal - * (https://portal.thingstream.io) then the - * device profile UID string is visible - * once you have created a device profile - * for your module; it will look something - * like "AgbCtixjwqLjwV3VWpfPyz". + * u-blox, something like + * "AgbCtixjwqLjwV3VWpfPyz". * @param pDeviceSerialNumberStr the null-terminated device serial * number string; you may chose what this * is, noting that there may be an upper @@ -356,11 +197,10 @@ bool uSecurityIsSealed(uDeviceHandle_t devHandle); /** Read the device X.509 public certificate that was generated * during the sealing process. If the certificate does not [yet] * exist an error will be returned. This feature is only supported - * if the Zero Touch Provisioning feature is enabled in your - * Thingstream portal for the module. For certificates stored in - * the module by the application, or for certificates pre-stored - * in the module, see instead the uSecurityCredential*() functions - * in u_security_credential.h. + * if the Zero Touch Provisioning feature for your module. For + * certificates stored in the module by the application, or for + * certificates pre-stored in the module, see instead the + * uSecurityCredential*() functions in u_security_credential.h. * * If pData is set to NULL then the number of bytes required to * store the certificate, including a null terminator, will still @@ -373,11 +213,6 @@ bool uSecurityIsSealed(uDeviceHandle_t devHandle); * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param devHandle the handle of the instance to be used, * for example obtained using * uDeviceOpen(). @@ -400,10 +235,9 @@ int32_t uSecurityZtpGetDeviceCertificate(uDeviceHandle_t devHandle, /** Read the device private key that was generated during * the sealing process. If the key does not [yet] exist an * error will be returned. This feature is only supported - * if the Zero Touch Provisioning feature is enabled in your - * Thingstream portal for the module. For keys stored in the - * module by the application see instead the - * uSecurityCredential*() functions in u_security_credential.h. + * if the Zero Touch Provisioning feature is for your module. + * For keys stored in the module by the application see instead + * the uSecurityCredential*() functions in u_security_credential.h. * * If pData is set to NULL then the number of bytes required to * store the key, including a null terminator, will still be @@ -416,11 +250,6 @@ int32_t uSecurityZtpGetDeviceCertificate(uDeviceHandle_t devHandle, * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param devHandle the handle of the instance to be used, * for example obtained using uDeviceOpen(). * @param pData a pointer to somewhere to store @@ -442,11 +271,11 @@ int32_t uSecurityZtpGetPrivateKey(uDeviceHandle_t devHandle, /** Read the X.509 certificate authorities that were used during * the sealing process. If the certificate(s) do not [yet] exist * an error will be returned. This feature is only supported if - * the Zero Touch Provisioning feature is enabled in your - * Thingstream portal for the module. For certificate authorities - * stored in the module by the application, or for certificate - * authorities pre-stored in the module, see instead the - * uSecurityCredential*() functions in u_security_credential.h. + * the Zero Touch Provisioning feature is for your module. For + * certificate authorities stored in the module by the application, + * or for certificate authorities pre-stored in the module, see + * instead the uSecurityCredential*() functions in + * u_security_credential.h. * * If pData is set to NULL then the number of bytes required to * store the certificates, including a null terminator, will still @@ -459,11 +288,6 @@ int32_t uSecurityZtpGetPrivateKey(uDeviceHandle_t devHandle, * flow control lines are connected on the interface to the * module. * - * Note that if the chip-to-chip security feature is enabled - * in the Thingstream portal for a module then a chip-to-chip - * security session must have been opened before this function is - * called, otherwise it will return an error. - * * @param devHandle the handle of the instance to be used, * for example obtained using * uDeviceOpen(). @@ -483,68 +307,6 @@ int32_t uSecurityZtpGetCertificateAuthorities(uDeviceHandle_t devHandle, char *pData, size_t dataSizeBytes); -/* ---------------------------------------------------------------- - * FUNCTIONS: END TO END ENCRYPTION - * -------------------------------------------------------------- */ - -/** Set the E2E encryption version to be used. Not all module - * types support all versions: refer to the AT manual for your - * module to determine what's what. If a module only supports - * a single E2E encryption type then it probably won't support - * setting the E2E encryption version. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @param version the version to use; 1 for version 1, etc. - * @return zero on success else negative error code. - */ -int32_t uSecurityE2eSetVersion(uDeviceHandle_t devHandle, int32_t version); - -/** Get the E2E encryption version. If a module only supports - * a single E2E encryption type then it may not support getting the - * E2E encryption version. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @return on success the E2E encryption version - * (1 for version 1, etc.), else negative - * error code. - */ -int32_t uSecurityE2eGetVersion(uDeviceHandle_t devHandle); - -/** Ask a module to encrypt a block of data. For this to work - * the module must have previously been security sealed but no - * current connection is required. Data encrypted in this way - * can be decrypted on arrival at its destination by requesting - * the relevant security keys from u-blox via the security services - * REST API. - * - * @param devHandle the handle of the instance to be used, - * for example obtained using uDeviceOpen(). - * @param pDataIn a pointer to dataSizeBytes of data to be - * encrypted, may be NULL, in which case this - * function does nothing. - * @param pDataOut a pointer to a location to store the - * encrypted data that MUST BE at least of - * size dataSizeBytes + - * #U_SECURITY_E2E_V1_HEADER_LENGTH_BYTES for - * E2E encryption version 1 or dataSizeBytes + - * #U_SECURITY_E2E_V2_HEADER_LENGTH_BYTES for - * E2E encryption version 2 (or you can - * just use #U_SECURITY_E2E_HEADER_LENGTH_MAX_BYTES - * for both cases); can only be NULL if pDataIn - * is NULL. - * @param dataSizeBytes the number of bytes of data to encrypt; - * must be zero if pDataIn is NULL. - * @return on success the number of bytes in the - * encrypted data block else negative error - * code. - */ -int32_t uSecurityE2eEncrypt(uDeviceHandle_t devHandle, - const void *pDataIn, - void *pDataOut, - size_t dataSizeBytes); - /* ---------------------------------------------------------------- * FUNCTIONS: PRE-SHARED KEY GENERATION * -------------------------------------------------------------- */ @@ -583,11 +345,11 @@ int32_t uSecurityPskGenerate(uDeviceHandle_t devHandle, /** Trigger a security heartbeat: this is useful if modifications * have been made to the security profile of the device in the - * u-blox security services REST API (or through the Thingstream - * service) and the device needs to be updated with these changes. - * HOWEVER, note that rate limiting is applied to these adhoc security - * hearbeats and hence if requested too frequently (e.g. more than - * once every 24 hours) the trigger request may return an error. + * u-blox security services REST API and the device needs to be updated + * with these changes. HOWEVER, note that rate limiting is applied to + * these adhoc security hearbeats and hence if requested too frequently + * (e.g. more than once every 24 hours) the trigger request may return + * an error. * * @param devHandle the handle of the instance to be used, * for example obtained using uDeviceOpen(). diff --git a/common/security/src/u_security.c b/common/security/src/u_security.c index 3ce8cb69..450e995a 100644 --- a/common/security/src/u_security.c +++ b/common/security/src/u_security.c @@ -56,24 +56,6 @@ * int32_t uXxxSecGetRootOfTrustUid(int32_t handle, * char *pRootOfTrustUid); * - * Pair with a module for chip to chip security (optional): - * - * int32_t uXxxSecC2cPair(int32_t handle, const char * pTESecret, - * char * pKey, char * pHMac); - * - * Open a chip to chip secure session (mandatory if - * uXxxSecC2cPair() is implemented): - * - * int32_t uXxxSecC2cOpen(int32_t handle, - * const char *pTESecret, - * const char *pKey, - * const char *pHMac); - * - * Close a chip to chip secure session (mandatory if - * uXxxSecC2cPair() is implemented): - * - * int32_t uXxxSecC2cClose(int32_t handle); - * * Security seal a module (mandatory): * * int32_t uXxxSecSealSet(int32_t handle, @@ -105,21 +87,6 @@ * * Perform end to end encryption on a block of data (optional): * - * int32_t uXxxSecE2eEncrypt(int32_t handle, - * const void *pDataIn, - * void *pDataOut, - * size_t dataSizeBytes); - * - * Set the end to end encryption version in use (optional): - * - * int32_t uXxxSecE2eSetVersion(int32_t handle, - * int32_t version); - * - * Get the end to end encryption version in use (mandatory - * if uXxxSecE2eSetVersion() is supported): - * - * int32_t uXxxSecE2eGetVersion(int32_t handle); - * * Trigger a security heartbeat (optional): * * int32_t uXxxSecHeartbeatTrigger(int32_t handle); @@ -230,59 +197,6 @@ int32_t uSecurityGetRootOfTrustUid(uDeviceHandle_t devHandle, return errorCodeOrSize; } -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: CHIP TO CHIP SECURITY - * -------------------------------------------------------------- */ - -// Pair a module's AT interface for chip to chip security. -int32_t uSecurityC2cPair(uDeviceHandle_t devHandle, - const char *pTESecret, - char *pKey, char *pHMac) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - - if ((pTESecret != NULL) && (pKey != NULL) && (pHMac != NULL)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCode = uCellSecC2cPair(devHandle, - pTESecret, pKey, pHMac); - } - } - - return errorCode; -} - -// Open a secure AT session. -int32_t uSecurityC2cOpen(uDeviceHandle_t devHandle, - const char *pTESecret, - const char *pKey, - const char *pHMac) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - - if ((pTESecret != NULL) && (pKey != NULL) && (pHMac != NULL)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCode = uCellSecC2cOpen(devHandle, - pTESecret, pKey, pHMac); - } - } - - return errorCode; -} - -// Close a secure AT session. -int32_t uSecurityC2cClose(uDeviceHandle_t devHandle) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCode = uCellSecC2cClose(devHandle); - } - - return errorCode; -} - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: SEAL * -------------------------------------------------------------- */ @@ -373,57 +287,6 @@ int32_t uSecurityZtpGetCertificateAuthorities(uDeviceHandle_t devHandle, return errorCodeOrSize; } -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: END TO END ENCRYPTION - * -------------------------------------------------------------- */ - -// Set the E2E encryption version to be used. -int32_t uSecurityE2eSetVersion(uDeviceHandle_t devHandle, int32_t version) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCode = uCellSecE2eSetVersion(devHandle, - version); - } - - return errorCode; -} - -// Get the E2E encryption version. -int32_t uSecurityE2eGetVersion(uDeviceHandle_t devHandle) -{ - int32_t errorCodeOrVersion = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCodeOrVersion = uCellSecE2eGetVersion(devHandle); - } - - return errorCodeOrVersion; -} - -// Ask a module to encrypt a block of data. -int32_t uSecurityE2eEncrypt(uDeviceHandle_t devHandle, - const void *pDataIn, - void *pDataOut, size_t dataSizeBytes) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_SUCCESS; - - if (pDataIn != NULL) { - errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - if ((pDataOut != NULL) && (dataSizeBytes > 0)) { - errorCode = (int32_t) U_ERROR_COMMON_NOT_IMPLEMENTED; - if (U_DEVICE_IS_TYPE(devHandle, U_DEVICE_TYPE_CELL)) { - errorCode = uCellSecE2eEncrypt(devHandle, - pDataIn, pDataOut, - dataSizeBytes); - } - } - } - - return errorCode; -} - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: PRE-SHARED KEY GENERATION * -------------------------------------------------------------- */ diff --git a/common/security/src/u_security_stub_cell.c b/common/security/src/u_security_stub_cell.c index 6e56eee6..901d25f3 100644 --- a/common/security/src/u_security_stub_cell.c +++ b/common/security/src/u_security_stub_cell.c @@ -73,35 +73,6 @@ U_WEAK int32_t uCellSecGetRootOfTrustUid(uDeviceHandle_t cellHandle, return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; } -U_WEAK int32_t uCellSecC2cPair(uDeviceHandle_t cellHandle, - const char *pTESecret, - char *pKey, char *pHMac) -{ - (void) cellHandle; - (void) pTESecret; - (void) pKey; - (void) pHMac; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - -U_WEAK int32_t uCellSecC2cOpen(uDeviceHandle_t cellHandle, - const char *pTESecret, - const char *pKey, - const char *pHMacKey) -{ - (void) cellHandle; - (void) pTESecret; - (void) pKey; - (void) pHMacKey; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - -U_WEAK int32_t uCellSecC2cClose(uDeviceHandle_t cellHandle) -{ - (void) cellHandle; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - U_WEAK int32_t uCellSecSealSet(uDeviceHandle_t cellHandle, const char *pDeviceProfileUid, const char *pDeviceSerialNumberStr, @@ -150,31 +121,6 @@ U_WEAK int32_t uCellSecZtpGetCertificateAuthorities(uDeviceHandle_t cellHandle, return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; } -U_WEAK int32_t uCellSecE2eSetVersion(uDeviceHandle_t cellHandle, int32_t version) -{ - (void) cellHandle; - (void) version; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - -U_WEAK int32_t uCellSecE2eGetVersion(uDeviceHandle_t cellHandle) -{ - (void) cellHandle; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - -U_WEAK int32_t uCellSecE2eEncrypt(uDeviceHandle_t cellHandle, - const void *pDataIn, - void *pDataOut, - size_t dataSizeBytes) -{ - (void) cellHandle; - (void) pDataIn; - (void) pDataOut; - (void) dataSizeBytes; - return (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; -} - U_WEAK int32_t uCellSecPskGenerate(uDeviceHandle_t cellHandle, size_t pskSizeBytes, char *pPsk, char *pPskId) diff --git a/common/security/test/u_security_test.c b/common/security/test/u_security_test.c index 511bb91f..26553b44 100644 --- a/common/security/test/u_security_test.c +++ b/common/security/test/u_security_test.c @@ -29,6 +29,8 @@ * macro. */ +#ifndef U_CFG_TEST_SECURITY_DISABLE + #ifdef U_CFG_OVERRIDE # include "u_cfg_override.h" // For a customer's configuration override #endif @@ -59,17 +61,6 @@ #include "u_network.h" #include "u_network_test_shared_cfg.h" -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET -#include "u_port_event_queue.h" -#include "u_sock.h" -#include "u_sock_test_shared_cfg.h" -# ifdef U_CFG_AT_CLIENT_DETAILED_DEBUG -extern void uAtClientDetailedDebugOn(); -extern void uAtClientDetailedDebugOff(); -extern void uAtClientDetailedDebugPrint(); -# endif -#endif - #include "u_security.h" /* ---------------------------------------------------------------- @@ -90,61 +81,6 @@ extern void uAtClientDetailedDebugPrint(); # define U_SECURITY_TEST_SEAL_TIMEOUT_SECONDS (60 * 4) #endif -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - -#ifndef U_SECURITY_TEST_TASK_STACK_SIZE_BYTES -/** The stack size to use for the test task created during - * async sockets testing with C2C. - */ -# define U_SECURITY_TEST_TASK_STACK_SIZE_BYTES 2048 -#endif - -#ifndef U_SECURITY_TEST_TASK_PRIORITY -/** The priority to use for the test task created during - * async sockets testing with C2C. If an AT client is running - * make sure that this is lower priority than its URC handler. - */ -# define U_SECURITY_TEST_TASK_PRIORITY (U_CFG_OS_PRIORITY_MIN + 5) -#endif - -#ifndef U_SECURITY_TEST_RECEIVE_QUEUE_LENGTH -/** The queue length, used for asynchronous tests. - */ -# define U_SECURITY_TEST_RECEIVE_QUEUE_LENGTH 10 -#endif - -# ifndef U_SECURITY_TEST_C2C_MAX_TCP_READ_WRITE_SIZE -/** The maximum TCP read/write size to use during C2C testing. - */ -# define U_SECURITY_TEST_C2C_MAX_TCP_READ_WRITE_SIZE 1024 -# endif - -# ifndef U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE -/** The small packet size to send when what we're actually - * trying to test is the URC behaviour of C2C. - */ -# define U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE 50 -# endif - -# ifndef U_SECURITY_TEST_C2C_SMALL_CHUNK_TOTAL_SIZE -/** The total amount of data to send during the small - * chunks test. - */ -# define U_SECURITY_TEST_C2C_SMALL_CHUNK_TOTAL_SIZE 250 -# endif - -# ifdef U_CFG_AT_CLIENT_DETAILED_DEBUG -# define LOG_ON uAtClientDetailedDebugOn() -# define LOG_OFF uAtClientDetailedDebugOff() -# define LOG_PRINT uAtClientDetailedDebugPrint() -# else -# define LOG_ON -# define LOG_OFF -# define LOG_PRINT -# endif - -#endif - /* ---------------------------------------------------------------- * TYPES * -------------------------------------------------------------- */ @@ -159,52 +95,6 @@ extern void uAtClientDetailedDebugPrint(); static int64_t gStopTimeMs; #endif -// A string of all possible characters, used -// when testing end to end encryption -static const char gAllChars[] = "the quick brown fox jumps over the lazy dog " - "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 0123456789 " - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" - "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c" - "\x1d\x1e!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\x7f"; - -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET -/** Data to exchange in a sockets test. - */ -static const char gSendData[] = "_____0000:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0100:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0200:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0300:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0400:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0500:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0600:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0700:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0800:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789" - "_____0900:0123456789012345678901234567890123456789" - "01234567890123456789012345678901234567890123456789"; - -/** Descriptor for asynchronous data reception. - */ -uSockDescriptor_t gDescriptor; - -/** Handle for the event queue used during asynchronous data testing. - */ -int32_t gEventQueueHandle = -1; - -/** Pointer to buffer for asynchronous data reception. - */ -char *gpBuffer = NULL; - -#endif - /* ---------------------------------------------------------------- * STATIC FUNCTIONS * -------------------------------------------------------------- */ @@ -261,650 +151,10 @@ static uNetworkTestList_t *pStdPreamble() return pList; } -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET -// Send an entire TCP data buffer until done -static size_t sendTcp(uSockDescriptor_t descriptor, - const char *pData, size_t sizeBytes) -{ - int32_t x; - size_t sentSizeBytes = 0; - int32_t startTimeMs; - - U_TEST_PRINT_LINE("sending %d byte(s) of TCP data...", sizeBytes); - startTimeMs = uPortGetTickTimeMs(); - while ((sentSizeBytes < sizeBytes) && - ((uPortGetTickTimeMs() - startTimeMs) < 10000)) { - x = uSockWrite(descriptor, (const void *) pData, - sizeBytes - sentSizeBytes); - if (x > 0) { - sentSizeBytes += x; - U_TEST_PRINT_LINE("sent %d byte(s) of TCP data @%d ms.", - sentSizeBytes, (int32_t) uPortGetTickTimeMs()); - } else { - U_TEST_PRINT_LINE("send returned %d.", x); - } - } - - return sentSizeBytes; -} - -// Make sure that size is greater than 0 and no more than limit, -// useful since, when moduloing a very large number number, -// compilers sometimes screw up and produce a small *negative* -// number. -static size_t fix(size_t size, size_t limit) -{ - if (size == 0) { - size = limit / 2; // better than 1 - } else if (size > limit) { - size = limit; - } - - return size; -} - -// Event task triggered by the arrival of data. -static void rxAsyncEventTask(void *pParameter, size_t parameterLength) -{ - int32_t thisSizeReceived; - int32_t totalSizeReceived = 0; - // The parameter that arrives here is a pointer to the - // payload which is itself a pointer to sizeBytesReceive, - // hence the need to double dereference here. - int32_t *pSizeBytes = *((int32_t **) pParameter); - - (void) parameterLength; - - if (gpBuffer != NULL) { - // Read from the socket until there's nothing left to read - //lint -e{776} Suppress possible truncation of addition - do { - thisSizeReceived = uSockRead(gDescriptor, gpBuffer + totalSizeReceived, - U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE - totalSizeReceived); - if (thisSizeReceived > 0) { - totalSizeReceived += thisSizeReceived; - } - } while ((thisSizeReceived > 0) && (totalSizeReceived < U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE)); - *pSizeBytes += totalSizeReceived; - } -} - -// Callback to send to event queue triggered by -// data arriving. -//lint -e{818} Suppress could be const, need to follow -// function signature -static void sendToEventQueue(void *pParameter) -{ - U_PORT_TEST_ASSERT(gEventQueueHandle >= 0); - - // Forward the pointer to rxAsyncEventTask(). - // Note: uPortEventQueueSend() expects to - // receive a pointer to a payload, so here - // we give it the address of pParameter, - // so that it will send on a copy - // of the pointer that is pParameter. - uPortEventQueueSend(gEventQueueHandle, - &pParameter, sizeof(size_t *)); -} - -#endif // U_CFG_TEST_SECURITY_C2C_TE_SECRET - /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS: TESTS * -------------------------------------------------------------- */ -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - -/** Test chip to chip security, basic test. - * - * IMPORTANT: see notes in u_cfg_test_platform_specific.h for the - * naming rules that must be followed when using the - * U_PORT_TEST_FUNCTION() macro. - */ -U_PORT_TEST_FUNCTION("[security]", "securityC2cBasic") -{ - uNetworkTestList_t *pList; - uDeviceHandle_t devHandle; - int32_t heapUsed; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - int32_t z; - - // The first time rand() is called the C library may - // allocate memory, not something we can do anything - // about, so call it once here to move that number - // out of our sums. - rand(); - - // Do the standard preamble to make sure there is - // a network underneath us - pList = pStdPreamble(); - // Repeat for all bearers - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - devHandle = *pTmp->pDevHandle; - // Get the initial-ish heap - heapUsed = uPortGetHeapFree(); - - U_TEST_PRINT_LINE("checking if u-blox security is supported by handle" - " 0x%08x...", devHandle); - if (uSecurityIsSupported(devHandle)) { - U_TEST_PRINT_LINE("security is supported."); - // Note: don't check sealed status here, C2C key pairing - // is intended to be performed by a customer only BEFORE - // bootstrapping or sealing is completed, in a sanitized - // environment where the returned values can be stored - // in the MCU. - // On the u-blox test farm we enable the feature - // LocalC2CKeyPairing via the u-blox security services REST - // API for all our modules so that we can complete the - // pairing process even after sealing. - - // Test that closing a session that is not open is fine - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); - U_TEST_PRINT_LINE("pairing..."); - LOG_ON; - z = -1; - // Try this a few times as sometimes +CME ERROR: SEC busy - // can be returned if we've just recently powered on - for (size_t y = 0; (z < 0) && (y < 3); y++) { - z = uSecurityC2cPair(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac); - if (z < 0) { - uPortTaskBlock(5000); - } - } - U_PORT_TEST_ASSERT(z == 0); - // Make sure it's still fine - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); - U_TEST_PRINT_LINE("opening a secure session..."); - U_PORT_TEST_ASSERT(uSecurityC2cOpen(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - U_TEST_PRINT_LINE("closing the session again..."); - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); - LOG_OFF; - LOG_PRINT; - } - - // Check for memory leaks - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("we have leaked %d byte(s).", heapUsed); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= 0); - } - - // Close the devices once more and free the list - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - if (*pTmp->pDevHandle != NULL) { - U_TEST_PRINT_LINE("closing device %s...", - gpUNetworkTestDeviceTypeName[pTmp->pDeviceCfg->deviceType]); - U_PORT_TEST_ASSERT(uDeviceClose(*pTmp->pDevHandle, false) == 0); - *pTmp->pDevHandle = NULL; - } - } - uNetworkTestListFree(); -} - -/** Test chip to chip security but this time there's a sock in it. - */ -U_PORT_TEST_FUNCTION("[security]", "securityC2cSock") -{ - uNetworkTestList_t *pList; - int32_t errorCode; - uDeviceHandle_t devHandle; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - uSockAddress_t remoteAddress; - uSockDescriptor_t descriptor; - size_t sizeBytes; - size_t offset; - int32_t y; - char *pDataReceived; - int32_t startTimeMs; - int32_t heapUsed; - int32_t heapSockInitLoss = 0; - int32_t heapXxxSockInitLoss = 0; - - // Do the standard preamble to make sure there is - // a network underneath us - pList = pStdPreamble(); - // Repeat for all bearers - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - devHandle = *pTmp->pDevHandle; - // Get the initial-ish heap - heapUsed = uPortGetHeapFree(); - - U_TEST_PRINT_LINE("checking if u-blox security is supported by" - " handle 0x%08x...", devHandle); - if (uSecurityIsSupported(devHandle)) { - U_TEST_PRINT_LINE("security is supported."); - // Note: don't check sealed status here, C2C key pairing - // is intended to be performed by a customer only BEFORE - // bootstrapping or sealing is completed, in a sanitized - // environment where the returned values can be stored - // in the MCU. - // On the u-blox test farm we enable the feature - // LocalC2CKeyPairing via the u-blox security services REST - // API for all our modules so that we can complete the - // pairing process even after sealing. - - U_TEST_PRINT_LINE("pairing..."); - U_PORT_TEST_ASSERT(uSecurityC2cPair(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - - // Open a new secure session and perform a sockets operation - U_TEST_PRINT_LINE("opening a secure session..."); - LOG_ON; - U_PORT_TEST_ASSERT(uSecurityC2cOpen(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - - U_TEST_PRINT_LINE("looking up echo server \"%s\"...", - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME); - - // Look up the address of the server we use for TCP echo - // The first call to a sockets API needs to - // initialise the underlying sockets layer; take - // account of that initialisation heap cost here. - heapSockInitLoss = uPortGetHeapFree(); - U_PORT_TEST_ASSERT(uSockGetHostByName(devHandle, - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME, - &(remoteAddress.ipAddress)) == 0); - heapSockInitLoss -= uPortGetHeapFree(); - - // Add the port number we will use - remoteAddress.port = U_SOCK_TEST_ECHO_TCP_SERVER_PORT; - - // Create a TCP socket - // Creating a socket may use heap in the underlying - // network layer which will be reclaimed when the - // network layer is closed but we don't do that here - // to save time so need to allow for it in the heap loss - // calculation - heapXxxSockInitLoss += uPortGetHeapFree(); - descriptor = uSockCreate(devHandle, U_SOCK_TYPE_STREAM, - U_SOCK_PROTOCOL_TCP); - heapXxxSockInitLoss -= uPortGetHeapFree(); - U_PORT_TEST_ASSERT(descriptor >= 0); - - // Connect the socket - U_TEST_PRINT_LINE("connect socket to \"%s:%d\"...", - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME, - U_SOCK_TEST_ECHO_TCP_SERVER_PORT); - // Connections can fail so allow this a few goes - errorCode = -1; - for (y = 2; (y > 0) && (errorCode < 0); y--) { - errorCode = uSockConnect(descriptor, &remoteAddress); - } - U_PORT_TEST_ASSERT(errorCode == 0); - - U_TEST_PRINT_LINE("sending/receiving %d bytes of data over a" - " TCP socket with data reception into the" - " same task...", sizeof(gSendData) - 1); - - // Throw random sized TCP segments up... - offset = 0; - y = 0; - startTimeMs = uPortGetTickTimeMs(); - while ((offset < sizeof(gSendData) - 1) && - (uPortGetTickTimeMs() - startTimeMs < 20000)) { - sizeBytes = (rand() % U_SECURITY_TEST_C2C_MAX_TCP_READ_WRITE_SIZE) + 1; - sizeBytes = fix(sizeBytes, - U_SECURITY_TEST_C2C_MAX_TCP_READ_WRITE_SIZE); - if (offset + sizeBytes > sizeof(gSendData) - 1) { - sizeBytes = (sizeof(gSendData) - 1) - offset; - } - if (sendTcp(descriptor, gSendData + offset, - sizeBytes) == sizeBytes) { - offset += sizeBytes; - } - y++; - } - sizeBytes = offset; - U_TEST_PRINT_LINE("%d byte(s) sent via TCP @%d ms, now receiving...", - sizeBytes, (int32_t) uPortGetTickTimeMs()); - U_PORT_TEST_ASSERT(sizeBytes >= sizeof(gSendData) - 1); - - // ...and capture them all again afterwards - pDataReceived = (char *) pUPortMalloc(sizeof(gSendData) - 1); - U_PORT_TEST_ASSERT(pDataReceived != NULL); - startTimeMs = uPortGetTickTimeMs(); - offset = 0; - //lint -e{441} Suppress loop variable not found in - // condition: we're using time instead - for (y = 0; (offset < sizeof(gSendData) - 1) && - (uPortGetTickTimeMs() - startTimeMs < 20000); y++) { - //lint -e{613} Suppress possible use of NULL pointer - // for pDataReceived - sizeBytes = uSockRead(descriptor, - pDataReceived + offset, - (sizeof(gSendData) - 1) - offset); - if (sizeBytes > 0) { - offset += sizeBytes; - U_TEST_PRINT_LINE("received %d byte(s) out of %d on TCP socket.", - offset, sizeof(gSendData) - 1); - } - } - sizeBytes = offset; - if (sizeBytes < sizeof(gSendData) - 1) { - U_TEST_PRINT_LINE("only %d byte(s) received after %d ms.", sizeBytes, - (int32_t) (uPortGetTickTimeMs() - startTimeMs)); - //lint -e(506, 774) Suppress constant Boolean always evaluates to false - U_PORT_TEST_ASSERT(false); - } else { - U_TEST_PRINT_LINE("all %d byte(s) received back after %d ms," - " checking if they were as expected...", sizeBytes, - (int32_t) (uPortGetTickTimeMs() - startTimeMs)); - // Check the characters are the same - //lint -e(668) Suppress possible use of NULL pointer - // for pDataReceived - U_PORT_TEST_ASSERT(memcmp(pDataReceived, gSendData, sizeBytes) == 0); - } - - // Close the socket - U_PORT_TEST_ASSERT(uSockClose(descriptor) == 0); - uSockCleanUp(); - - uPortFree(pDataReceived); - - U_TEST_PRINT_LINE("closing the session again..."); - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); - LOG_OFF; - LOG_PRINT; - } - -#ifndef __XTENSA__ - // Check for memory leaks - // This if'ed out for ESP32 (xtensa compiler) as - // the way it's heap work means that if blocks are - // freed in a different order to they were allocated and - // any one of those blocks remains allocated (which sockets - // will do here as we allocate two mutexes when they are first - // used) then the amount of heap remaining is not possible - // to calculate with any degree of confidence (a four byte - // variant due to block length tracking in their - // implementation). - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("during this part of the test %d byte(s) were" - " lost to sockets initialisation; we have" - " leaked %d byte(s).", - heapSockInitLoss + heapXxxSockInitLoss, - heapUsed - (heapSockInitLoss + heapXxxSockInitLoss)); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= heapSockInitLoss + heapXxxSockInitLoss); -#else - (void) heapUsed; - (void) heapSockInitLoss; - (void) heapXxxSockInitLoss; -#endif - } - - // Close the devices once more and free the list - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - if (*pTmp->pDevHandle != NULL) { - U_TEST_PRINT_LINE("closing device %s...", - gpUNetworkTestDeviceTypeName[pTmp->pDeviceCfg->deviceType]); - U_PORT_TEST_ASSERT(uDeviceClose(*pTmp->pDevHandle, false) == 0); - *pTmp->pDevHandle = NULL; - } - } - uNetworkTestListFree(); -} - -/** Test chip to chip security but this time with asynchronous - * data reception in order to test URCs are properly handled. - */ -U_PORT_TEST_FUNCTION("[security]", "securityC2cSockAsync") -{ - uNetworkTestList_t *pList; - int32_t errorCode; - uDeviceHandle_t devHandle; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - uSockAddress_t remoteAddress; - size_t sizeBytesSend; - size_t sizeBytesReceive = 0; - size_t offset; - int32_t y; - int32_t startTimeMs; - int32_t heapUsed; - int32_t heapSockInitLoss = 0; - int32_t heapXxxSockInitLoss = 0; - - // Do the standard preamble to make sure there is - // a network underneath us - pList = pStdPreamble(); - // Repeat for all bearers - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - devHandle = *pTmp->pDevHandle; - // Get the initial-ish heap - heapUsed = uPortGetHeapFree(); - - U_TEST_PRINT_LINE("checking if u-blox security is supported by handle 0x%08x...", - devHandle); - if (uSecurityIsSupported(devHandle)) { - U_TEST_PRINT_LINE("security is supported."); - // Note: don't check sealed status here, C2C key pairing - // is intended to be performed by a customer only BEFORE - // bootstrapping or sealing is completed, in a sanitized - // environment where the returned values can be stored - // in the MCU. - // On the u-blox test farm we enable the feature - // LocalC2CKeyPairing via the u-blox security services REST - // API for all our modules so that we can complete the - // pairing process even after sealing. - - U_TEST_PRINT_LINE("pairing..."); - U_PORT_TEST_ASSERT(uSecurityC2cPair(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - - // Open a new secure session and perform a sockets operation - U_TEST_PRINT_LINE("opening a secure session..."); - LOG_ON; - U_PORT_TEST_ASSERT(uSecurityC2cOpen(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - - U_TEST_PRINT_LINE("looking up echo server \"%s\"...", - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME); - - // Look up the address of the server we use for TCP echo - // The first call to a sockets API needs to - // initialise the underlying sockets layer; take - // account of that initialisation heap cost here. - heapSockInitLoss = uPortGetHeapFree(); - U_PORT_TEST_ASSERT(uSockGetHostByName(devHandle, - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME, - &(remoteAddress.ipAddress)) == 0); - heapSockInitLoss -= uPortGetHeapFree(); - - // Add the port number we will use - remoteAddress.port = U_SOCK_TEST_ECHO_TCP_SERVER_PORT; - - // Create a TCP socket - // Creating a socket may use heap in the underlying - // network layer which will be reclaimed when the - // network layer is closed but we don't do that here - // to save time so need to allow for it in the heap loss - // calculation - heapXxxSockInitLoss += uPortGetHeapFree(); - gDescriptor = uSockCreate(devHandle, U_SOCK_TYPE_STREAM, - U_SOCK_PROTOCOL_TCP); - heapXxxSockInitLoss -= uPortGetHeapFree(); - U_PORT_TEST_ASSERT(gDescriptor >= 0); - - // Connect the socket - U_TEST_PRINT_LINE("connect socket to \"%s:%d\"...", - U_SOCK_TEST_ECHO_TCP_SERVER_DOMAIN_NAME, - U_SOCK_TEST_ECHO_TCP_SERVER_PORT); - // Connections can fail so allow this a few goes - errorCode = -1; - for (y = 2; (y > 0) && (errorCode < 0); y--) { - errorCode = uSockConnect(gDescriptor, &remoteAddress); - } - U_PORT_TEST_ASSERT(errorCode == 0); - - // Create the event queue with, at the end of it, - // a task that will handle the received TCP packets. - // The thing it gets sent on the event queue is a pointer - // to sizeBytesReceive - gEventQueueHandle = uPortEventQueueOpen(rxAsyncEventTask, - "testTaskRxData", - //lint -e(866) Suppress unusual - // use of & in sizeof() - sizeof(&sizeBytesReceive), - U_SECURITY_TEST_TASK_STACK_SIZE_BYTES, - U_SECURITY_TEST_TASK_PRIORITY, - U_SECURITY_TEST_RECEIVE_QUEUE_LENGTH); - U_PORT_TEST_ASSERT(gEventQueueHandle >= 0); - - // Ask the sockets API for a pointer to sizeBytesReceive - // to be sent to our trampoline function, - // sendToEventQueue(), whenever UDP data arrives. - // sendToEventQueue() will then forward the - // pointer to the event queue and hence to - // rxAsyncEventTask() - uSockRegisterCallbackData(gDescriptor, - sendToEventQueue, - &sizeBytesReceive); - - // Set the port to be non-blocking; we will pick up - // the TCP data that we have been called-back to - // say has arrived and then if we ask again we want - // to know that there is nothing more to receive - // without hanging about so that we can leave the - // event handler quickly. - uSockBlockingSet(gDescriptor, false); - - U_TEST_PRINT_LINE("sending/receiving data over a TCP socket" - " with data reception into another task..."); - - // Throw small TCP segments up and wait - // for them to come back... - gpBuffer = (char *) pUPortMalloc(U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE); - U_PORT_TEST_ASSERT(gpBuffer != NULL); - offset = 0; - y = 0; - startTimeMs = uPortGetTickTimeMs(); - while ((offset < U_SECURITY_TEST_C2C_SMALL_CHUNK_TOTAL_SIZE) && - (uPortGetTickTimeMs() - startTimeMs < 120000)) { - sizeBytesSend = U_SECURITY_TEST_C2C_SMALL_CHUNK_SIZE; - if (offset + sizeBytesSend > sizeof(gSendData) - 1) { - sizeBytesSend = (sizeof(gSendData) - 1) - offset; - } - sizeBytesReceive = 0; - if (sendTcp(gDescriptor, gSendData + offset, - sizeBytesSend) == sizeBytesSend) { - U_TEST_PRINT_LINE("%d byte(s) sent via TCP @%d ms, now receiving...", - sizeBytesSend, (int32_t) uPortGetTickTimeMs()); - // Give the data time to come back - for (size_t z = 20; (z > 0) && - (sizeBytesReceive < sizeBytesSend); z--) { - uPortTaskBlock(1000); - } - if (sizeBytesReceive < sizeBytesSend) { - U_TEST_PRINT_LINE("after sending a total of %d byte(s)," - " receiving failed.", sizeBytesSend + offset); - //lint -e(506, 774) Suppress constant Boolean always - // evaluates to false - U_PORT_TEST_ASSERT(false); - } - // Check it - //lint -e(668) Suppress possible use of NULL pointer - // for gpBuffer - if (memcmp(gpBuffer, gSendData + offset, sizeBytesReceive) != 0) { - U_TEST_PRINT_LINE("expected received data contents not what" - " was expected."); - U_TEST_PRINT_LINE("expected \"%*s\", received \"%*s\".", - sizeBytesSend, gSendData + offset, - sizeBytesReceive, gpBuffer); - //lint -e(506, 774) Suppress constant Boolean always - // evaluates to false - U_PORT_TEST_ASSERT(false); - } - offset += sizeBytesSend; - } - y++; - } - - sizeBytesSend = offset; - if (sizeBytesSend < U_SECURITY_TEST_C2C_SMALL_CHUNK_TOTAL_SIZE) { - U_TEST_PRINT_LINE("only %d byte(s) sent after %d ms.", sizeBytesSend, - (int32_t) (uPortGetTickTimeMs() - startTimeMs)); - //lint -e(506, 774) Suppress constant Boolean always evaluates to false - U_PORT_TEST_ASSERT(false); - } - - // As a sanity check, make sure that - // U_SECURITY_TEST_TASK_STACK_SIZE_BYTES - // was big enough - y = uPortEventQueueStackMinFree(gEventQueueHandle); - U_TEST_PRINT_LINE("event queue task had %d byte(s) free at a minimum.", y); - U_PORT_TEST_ASSERT((y > 0) || - (y == (int32_t) U_ERROR_COMMON_NOT_SUPPORTED)); - - // Close the socket - U_PORT_TEST_ASSERT(uSockClose(gDescriptor) == 0); - uSockCleanUp(); - - // Close the event queue - U_PORT_TEST_ASSERT(uPortEventQueueClose(gEventQueueHandle) == 0); - gEventQueueHandle = -1; - - uPortFree(gpBuffer); - uPortEventQueueCleanUp(); - - U_TEST_PRINT_LINE("closing the session again..."); - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); - LOG_OFF; - LOG_PRINT; - } - -#if !defined(__XTENSA__) && !U_CFG_OS_CLIB_LEAKS - // Check for memory leaks, if the platform isn't leaky - // This if'ed out for ESP32 (xtensa compiler) as - // the way it's heap work means that if blocks are - // freed in a different order to they were allocated and - // any one of those blocks remains allocated (which sockets - // will do here as we allocate two mutexes when they are first - // used) then the amount of heap remaining is not possible - // to calculate with any degree of confidence (a four byte - // variant due to block length tracking in their - // implementation). - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("during this part of the test %d byte(s) were lost" - " to sockets initialisation; we have leaked %d byte(s).", - heapSockInitLoss + heapXxxSockInitLoss, - heapUsed - (heapSockInitLoss + heapXxxSockInitLoss)); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= heapSockInitLoss + heapXxxSockInitLoss); -#else - (void) heapUsed; - (void) heapSockInitLoss; - (void) heapXxxSockInitLoss; -#endif - } - - // Close the devices once more and free the list - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - if (*pTmp->pDevHandle != NULL) { - U_TEST_PRINT_LINE("closing device %s...", - gpUNetworkTestDeviceTypeName[pTmp->pDeviceCfg->deviceType]); - U_PORT_TEST_ASSERT(uDeviceClose(*pTmp->pDevHandle, false) == 0); - *pTmp->pDevHandle = NULL; - } - } - uNetworkTestListFree(); -} - -#endif // U_CFG_TEST_SECURITY_C2C_TE_SECRET - /** Test security sealing, requires a network connection. * Note: this test will *only* attempt a seal if * U_CFG_SECURITY_DEVICE_PROFILE_UID is defined to contain @@ -1019,121 +269,6 @@ U_PORT_TEST_FUNCTION("[security]", "securitySeal") uNetworkTestListFree(); } -/** Test end to end encryption. - */ -U_PORT_TEST_FUNCTION("[security]", "securityE2eEncryption") -{ - uNetworkTestList_t *pList; - uDeviceHandle_t devHandle; - //lint -esym(838, y) Suppress not used, which will be true - // if logging is compiled out - int32_t y; - int32_t heapUsed; - void *pData; - int32_t version; - int32_t headerLengthBytes = U_SECURITY_E2E_V1_HEADER_LENGTH_BYTES; - - // Do the standard preamble to make sure there is - // a network underneath us - pList = pStdPreamble(); - // Repeat for all bearers - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - devHandle = *pTmp->pDevHandle; - // Get the initial-ish heap - heapUsed = uPortGetHeapFree(); - - U_TEST_PRINT_LINE("checking if u-blox security is supported by handle" - " 0x%08x...", devHandle); - if (uSecurityIsSupported(devHandle)) { - U_TEST_PRINT_LINE("security is supported."); - U_TEST_PRINT_LINE("waiting for seal status..."); - if (uSecurityIsSealed(devHandle)) { - U_TEST_PRINT_LINE("device is sealed."); - - // Ask for a security heartbeat to be triggered: - // this very likely won't be permitted since - // it is quite severely rate limited (e.g. just once - // in 24 hours) so we're really only checking that it - // doesn't crash here - // TODO: temporarily remove the security heartbeat - // call here. One of the test instances is misbehaving - // in this function (taking too long to return), will - // disable while the problem is investigated. - //y = uSecurityHeartbeatTrigger(devHandle); - //U_TEST_PRINT_LINE("uSecurityHeartbeatTrigger() returned %d.", y); - U_TEST_PRINT_LINE("testing end to end encryption..."); - - // First get the current E2E encryption version - version = uSecurityE2eGetVersion(devHandle); - if (version > 0) { - U_PORT_TEST_ASSERT((version == 1) || (version == 2)); - U_TEST_PRINT_LINE("end to end encryption is v%d.", version); - if (version == 2) { - // On all current modules where V2 is supported and - // selected V1 is also supported; this may change - // in future of course - version = 1; - U_TEST_PRINT_LINE("setting end to end encryption v%d.", version); - U_PORT_TEST_ASSERT(uSecurityE2eSetVersion(devHandle, version) == 0); - U_PORT_TEST_ASSERT(uSecurityE2eGetVersion(devHandle) == version); - version = 2; - U_TEST_PRINT_LINE("setting end to end encryption v%d again.", version); - U_PORT_TEST_ASSERT(uSecurityE2eSetVersion(devHandle, version) == 0); - U_PORT_TEST_ASSERT(uSecurityE2eGetVersion(devHandle) == version); - headerLengthBytes = U_SECURITY_E2E_V2_HEADER_LENGTH_BYTES; - } - U_TEST_PRINT_LINE("end to end encryption is v%d.", version); - - } else { - U_TEST_PRINT_LINE("end to end encryption version check not supported," - " assuming v1."); - version = 1; - (void)version; // Not used at the moment - } - - // Allocate memory to receive into - pData = pUPortMalloc(sizeof(gAllChars) + headerLengthBytes); - U_PORT_TEST_ASSERT(pData != NULL); - // Copy the output data into the input buffer, just to have - // something in there we can compare against - //lint -e(668) Suppress possible NULL pointer, it is checked above - memcpy(pData, gAllChars, sizeof(gAllChars)); - U_TEST_PRINT_LINE("requesting end to end encryption of %d byte(s) of data...", - sizeof(gAllChars)); - y = uSecurityE2eEncrypt(devHandle, gAllChars, - pData, sizeof(gAllChars)); - U_PORT_TEST_ASSERT(y == sizeof(gAllChars) + headerLengthBytes); - U_TEST_PRINT_LINE("%d byte(s) of data returned.", y); - //lint -e(668) Suppress possible NULL pointer, it is checked above - U_PORT_TEST_ASSERT(memcmp(pData, gAllChars, sizeof(gAllChars)) != 0); - uPortFree(pData); - } else { - U_TEST_PRINT_LINE("this device supports u-blox security but has not" - " been security sealed, no testing of end to end" - " encryption will be carried out."); - } - } - - // Check for memory leaks - heapUsed -= uPortGetHeapFree(); - U_TEST_PRINT_LINE("we have leaked %d byte(s).", heapUsed); - // heapUsed < 0 for the Zephyr case where the heap can look - // like it increases (negative leak) - U_PORT_TEST_ASSERT(heapUsed <= 0); - } - - // Close the devices once more and free the list - for (uNetworkTestList_t *pTmp = pList; pTmp != NULL; pTmp = pTmp->pNext) { - if (*pTmp->pDevHandle != NULL) { - U_TEST_PRINT_LINE("closing device %s...", - gpUNetworkTestDeviceTypeName[pTmp->pDeviceCfg->deviceType]); - U_PORT_TEST_ASSERT(uDeviceClose(*pTmp->pDevHandle, false) == 0); - *pTmp->pDevHandle = NULL; - } - } - uNetworkTestListFree(); -} - /** Test PSK generation. */ U_PORT_TEST_FUNCTION("[security]", "securityPskGeneration") @@ -1273,10 +408,6 @@ U_PORT_TEST_FUNCTION("[security]", "securityZtp") int32_t z; int32_t heapUsed; char *pData; -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; -#endif // Do the standard preamble to make sure there is // a network underneath us @@ -1295,22 +426,6 @@ U_PORT_TEST_FUNCTION("[security]", "securityZtp") if (uSecurityIsSealed(devHandle)) { U_TEST_PRINT_LINE("device is sealed."); -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - // If C2C security is in place for a module then the - // certificates can only be read if a C2C session is - // open - U_TEST_PRINT_LINE("pairing for C2C..."); - LOG_ON; - U_PORT_TEST_ASSERT(uSecurityC2cPair(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - U_TEST_PRINT_LINE("opening a C2C session..."); - U_PORT_TEST_ASSERT(uSecurityC2cOpen(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0); - LOG_OFF; - LOG_PRINT; -#endif // First get the size of the device public certificate y = uSecurityZtpGetDeviceCertificate(devHandle, NULL, 0); U_TEST_PRINT_LINE("device public X.509 certificate is %d bytes.", y); @@ -1373,12 +488,6 @@ U_PORT_TEST_FUNCTION("[security]", "securityZtp") } else { U_TEST_PRINT_LINE("module does not support reading certificate authorities."); } - -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - U_TEST_PRINT_LINE("closing C2C session again..."); - U_PORT_TEST_ASSERT(uSecurityC2cClose(devHandle) == 0); -#endif - } else { U_TEST_PRINT_LINE("this device supports u-blox security but has" " not been security sealed, no testing of reading" @@ -1414,13 +523,6 @@ U_PORT_TEST_FUNCTION("[security]", "securityCleanUp") { int32_t y; -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - if (gEventQueueHandle >= 0) { - uPortEventQueueClose(gEventQueueHandle); - gEventQueueHandle = -1; - } -#endif - // The network test configuration is shared between // the network, sockets, security and location tests // so must reset the handles here in case the @@ -1445,4 +547,6 @@ U_PORT_TEST_FUNCTION("[security]", "securityCleanUp") } } +#endif // #ifndef U_CFG_TEST_SECURITY_DISABLE + // End of file diff --git a/example/security/README.md b/example/security/README.md index 28b453f5..659ea90f 100644 --- a/example/security/README.md +++ b/example/security/README.md @@ -1,113 +1,12 @@ -# Contents -* [u-blox IoT Security-as-a-Service Introduction](#u-blox-iot-security-as-a-service-introduction) -* [Evaluation And Starter Kits](#evaluation-and-starter-kits) -* [Device-side](#device-side) - * [Building](#building) - * [Using A Cellular Module](#using-a-cellular-module) -* [Broker-side](#broker-side) - * [Sign-up](#sign-up) - * [Generate The Access Keys](#generate-the-access-keys) - * [Create A Device Profile](#create-a-device-profile) - * [Using Features And Services](#using-features-and-services) +# Introduction +This example demonstrate how to use the pre-shared key generation u-blox security feature. The module in use must have been security sealed for this example to work: see the comments in the device-side example code that describe how to do this. -# u-blox IoT Security-as-a-Service Introduction -IoT Security-as-a-Service is a managed services solution that makes it extremely simple to protect your data at rest and from silicon to cloud, ensuring that you can focus more on your business and enjoy faster time-to-market. +# Usage +Follow the instructions in the directory above this to build and download the target code, setting the \#define `U_CFG_APP_FILTER` to `exampleSecPsk` (noting that NO quotation marks should be included) if you wish to run *just* this example, as opposed to all the examples and unit tests. -We implement a true end-to-end concept where data are protected from the device to the end user and are not visible at the intermediate nodes/platforms nor by the service provider. Our approach ensures minimal code development and investment and provides the highest standards of security, leveraging the root of trust in u-blox SARA-R4, SARA-R5 and LARA-R6 module platforms to bring a unique and immutable identity for univocal identification and on-boarding in leading IoT cloud platforms. +When the target code has run and completed successfully it will print the example generated PSK and associated PSK ID in the debug stream, something like: -The innovative symmetric Key Management System delivers an unprecedented level of security, giving the possibility to generate an infinite number of crypto keys on-the-fly, to be used for (D)TLS or for any other purpose. - -All u-blox security solutions are designed for LPWA constrained devices, reducing the data usage and the number of handshakes, thus minimizing the power consumption that is a critical metric for most IoT devices. - -You can find more information about IoT Security-as-a-Service [here](https://www.u-blox.com/en/iot-security-service). - -# Evaluation And Starter Kits -IoT Security-as-a-Service is available on SARA-R4, SARA-R5 and LARA-R6 series modules, though note that `ubxlib` currently only supports security on SARA-R5. Several evaluation kits are available: - -* EVK-R500S - evaluation kit for SARA-R500S -* EVK-R510S - evaluation kit for SARA-R510S -* EVK-R510M8S - evaluation kit for SARA-R510M8S -* EVK-R422M8S - evaluation kit for SARA-R422M8S -* EVK-R410-8-00 - evaluation kit including LTE module for multi-regional use; Cat M1, NB1 bands: 3, 5, 8, 20, 28 -* EVK-R410-7-00 - evaluation kit including LTE module for Korea; Cat M1 deployed bands 3, 5, 26 -* EVK-R410-6-00 - evaluation kit including LTE module for Japan; Cat M1 deployed bands 1, 8, 19 -* C030 application board that lets you easily start testing u-blox services -* EVK-R6 - evaluation kit for LARA-R6 - -Please [contact us](mailto:thingstream-support@u-blox.com) to discuss your needs and to request a kit. - -# Device-side -The examples here demonstrate how to use the u-blox security features for: - -* design security: - * local chip-to-chip (C2C) security, - * local data protection [coming soon]. -* E2E data protection: - * E2E symmetric KMS, - * E2E data protection, - * E2E data integrity, -* access control: - * zero touch provisioning (ZTP) [coming soon]. - -Under each example folder you will find an `xxx_main.c` file and one or more Python scripts: the `xxx_main.c` file is the example code for the embedded target while the Python script(s) can be used to access the REST APIs of the u-blox security service. - -Note that some of these examples assume that the module in use has already been security sealed, a once-only process of claiming ownership which you would normally carry out as part of your production process: where this is the case the comments in `xxx_main.c` describe how to do it. - -## Building -To build and run these examples on a supported platform you need to travel down into the [port/platform](/port/platform)`///` directory of your choice and find the `runner` build. The instructions there will tell you how to set/override \#defines. The following \#defines are relevant: - -`U_CFG_APP_FILTER`: set this to the name of the example you want to run (noting that NO quotation marks should be included) to run *just* that example (as opposed to all the examples and unit tests): use `exampleSecE2e` for the end to end data protection example. - -For the remainder of the \#defines you may either override their values in the same way or, if you are only running this example, you may edit the values directly in `xxx_main.c` before compiling. - -## Using A Cellular Module -u-blox security is currently only supported on cellular, on the SARA-R5 cellular module. - -`U_CFG_TEST_CELL_MODULE_TYPE`: consult the [u_cell_module_type.h](/cell/api/u_cell_module_type.h) file to determine the type name for the cellular module you intend to use. Since only SARA-R5 is supported you must set `U_CFG_TEST_CELL_MODULE_TYPE` to `U_CELL_MODULE_TYPE_SARA_R5`. - -`U_CFG_APP_PIN_CELL_xxx`: the default values for the MCU pins connecting your cellular module to your MCU are \#defined in the file [port/platform](/port/platform)`///cfg/cfg_app_platform_specific.h`. You should check if these are correct for your board and, if not, override the values of the \#defines (where -1 means "not connected"). - -`U_CFG_APP_CELL_UART`: this sets the internal HW UART block that your chosen MCU will use to talk to the cellular module. The default is usually acceptable but if you wish to change it then consult the file [port/platform](/port/platform)`///cfg/cfg_hw_platform_specific.h` for other options. - -Obviously you will need a SIM in your board, an antenna connected and you may need to know the APN associated with the SIM (though accepting the network default often works). - -# Broker-side -u-blox Thingstream provides a management console that you can use to manage the entire suite of u-blox services and the Security Thing, which is the logical representation of your module in the Thingstream platform. - -## Sign-up -Sign-up is free, quick and easy. Just go to the [management console](https://portal.thingstream.io/) and register with your company information. If you already have a Thingstream domain for [Communication-as-a-Service](https://www.u-blox.com/en/iot-communication-service) (MQTT Anywhere, MQTT Here or MQTT Now), you do not need to register again, security services are already available. - -The management console lets you create the credentials (access key and secret pair) required to manage and use IoT security services through REST APIs. The API documentation and swagger (YAML) specification download are available [here](https://api.services.u-blox.com/). - -## Generate The Access Keys -In order to start using IoT Security-as-a-Service, you'll need to generate an access key and secret. You can do this by going to the Access Keys page under Security Services and clicking on the "Generate Keys" button. - -![keys](keys.jpg) - -Related information: -* [Security Services API documentation](https://api.services.u-blox.com/) -* [Tools and Software](https://developer.thingstream.io/guides/security-services/security-as-a-service-tools) - -If you need more help or have any questions, please send an email to [thingstream-support@u-blox.com](thingstream-support@u-blox.com). - -Once you have generated your key and secret, make sure you save them somewhere safe as the secret cannot be recovered after you leave the page. You can generate up to 5 access keys. - -## Create A Device Profile -You now need to create a device profile to identify a group of devices that will share the same [price plan](https://portal.thingstream.io/pricing/saas) and feature set. - -To create the device profile, and get the `DeviceProfileUID` required for device provisioning, go to the [management console](https://portal.thingstream.io/), and then select "Device Profile" under the "Security Services" panel on the left side. - -A wizard will guide you through the steps to select the features and services linked to the profile. You can always change these at a later stage. You will also need to select a price plan to be used by devices that are provisioned using the profile. To get started, you can use the free Developer plan which allows you to manage up to 10 active devices. Find out more about the available price plans here. - -Once you have created the device profile you need to seal the `DeviceProfileUID` in the device using the `ubxlib` API `uSecuritySealSet()` (see [u_security.h](/common/security/api/u_security.h), also explained in the "Claim Ownership" section of the [IoT Security-as-a-Service Application Note](https://www.u-blox.com/en/docs/UBX-20013561)). - -Once you have completed these steps, the device will automatically appear in your account in the "Things" section of the management console with the selected service and features enabled. You can use the same device profile for all the devices that need the same set of features and services and you can make changes for individual devices via the management console. - -## Using Features And Services -To test the APIs visit the [Tools and Software page](https://developer.thingstream.io/guides/security-services/security-as-a-service-tools). - -Refer to the [IoT Security-as-a-Service Application Note](https://www.u-blox.com/en/docs/UBX-20013561) to learn more about how to use the services and features. The application note provides step-by-step instructions for all of the APIs required. - -You can also refer to the [u-blox GitHub repository](https://github.com/u-blox) which is constantly updated with sample code to simplify the service implementation and reduce your time-to-market. - -If you need more help or have any questions, please send an email to [thingstream-support@u-blox.com](thingstream-support@u-blox.com). +``` +32 bytes of PSK returned: 70665f1ba1753d36e5a412a56233507da1dbd0d8476f418423892de0895c7e9f +14 byte(s) of PSK ID returned: 11010008003f9720f38c30c88428 +``` \ No newline at end of file diff --git a/example/security/c2c/README.md b/example/security/c2c/README.md deleted file mode 100644 index e1bb746d..00000000 --- a/example/security/c2c/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Introduction -This example demonstrate how to use the chip to chip (C2C) protection u-blox security feature. This feature allows the communication across the AT interface between the MCU and the module to be authenticated and encrypted, preventing snooping of hardware lines. Since the process of pairing a module with an MCU for chip-to-chip security is a one-time-only irreversible process (except by special arrangement with u-blox) this example will only run if `U_CFG_TEST_SECURITY_C2C_TE_SECRET` (a 16 byte binary value that forms part of the pairing process) is defined. - -IMPORTANT: it is intended that the pairing process that enables chip to chip security is carried out in a secure environment, e.g. in your factory. To ensure that is the case the module will ONLY allow chip to chip security pairing to be performed BEFORE the module has been security boot-strapped, something the module will do THE MOMENT it contacts the network for the first time. In other words, the sequence must be: - -1. Complete the C2C pairing process between your MCU and the module; your MCU must store the pairing keys that are used to switch C2C security on and off later as desired. -2. Allow the module to contact the network for the first time: it will bootstrap with the u-blox security servers. -3. Complete the security sealing process. - -Steps 1 to 3 must be performed in the order given and should be performed in a secure environment. With that done C2C security can be employed by your MCU at any time it wishes. - -Note: in order to test this example code, we have enabled a special permission on our test devices, LocalC2CKeyPairing, which DOES permit C2C pairing to be performed on a security bootstrapped/sealed module. - -# Usage -Follow the instructions in the directory above this to build and download the target code. Before commencing your build: - -- set the \#define `U_CFG_APP_FILTER` to `exampleSecC2c` (noting that NO quotation marks should be included) if you wish to run *just* this example, as opposed to all the examples and unit tests, -- as stated above, since the process of C2C pairing a module with an MCU is normally an irreversible one (except by arrangement with u-blox) this example will do nothing unless you define a value for `U_CFG_TEST_SECURITY_C2C_TE_SECRET`, again with no quotation marks around the value; for instance, we use `U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8` for our internal testing. - -You will, of course, notice no difference in operation to that with C2C security disabled, since the scrambled AT comms are encrypted/decypted and integrity checked by this code: to see the effect of C2C protection you should monitor the serial lines between the MCU and the module with something like a Salaea logic probe to see that the AT communications are, in fact, scrambled when a C2C session is open. \ No newline at end of file diff --git a/example/security/c2c/c2c_main.c b/example/security/c2c/c2c_main.c deleted file mode 100644 index d4fecaac..00000000 --- a/example/security/c2c/c2c_main.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2019-2023 u-blox - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** @brief This example demonstrates how to use u-blox chip to chip - * security. This example will only run if a value is defined for - * U_CFG_TEST_SECURITY_C2C_TE_SECRET (with no quotation marks around the - * value) and, once it has run, the module it was run against CANNOT - * be C2C-paired again except by arrangement with u-blox (see below - * for an explanation). In other words, this is a once-only and - * irreversible process unless you arrange otherwise by contacting - * u-blox. - * - * The choice of module and the choice of platform on which this - * code runs is made at build time, see the README.md for - * instructions. - * - * IMPORTANT: it is intended that the pairing process that enables - * chip to chip security is carried out in a secure environment, - * e.g. in your factory. To ensure that is the case the module will - * ONLY allow chip to chip security pairing to be performed BEFORE - * the module has been security boot-strapped, something the module - * will do THE MOMENT it contacts the cellular network for the first - * time. In other words, the sequence must be: - * - * 1. Complete the C2C pairing process between your MCU and the module; - * your MCU must store the pairing keys that are used to switch C2C - * security on and off later as desired. - * 2. Allow the module to contact the network for the first time: it - * will perform security-bootstrapping with the u-blox security - * servers. - * 3. Complete the security sealing process. - * - * Steps 1 to 3 must be performed in the order given and should be - * performed in a secure environment. With that done C2C security can - * be started and stopped by your MCU at any time. - * - * Note: in order to test this example code, we have enabled a - * special permission, LocalC2CKeyPairing, on our test devices which - * DOES permit C2C pairing to be performed on a security - * bootstrapped/sealed module. - */ - -#ifdef U_CFG_TEST_SECURITY_C2C_TE_SECRET - -// Bring in all of the ubxlib public header files -#include "ubxlib.h" - -// Bring in the application settings -#include "u_cfg_app_platform_specific.h" - -#ifndef U_CFG_DISABLE_TEST_AUTOMATION -// This purely for internal u-blox testing -# include "u_cfg_test_platform_specific.h" -#endif - -#include "string.h" // For memcmp() - -/* ---------------------------------------------------------------- - * COMPILE-TIME MACROS - * -------------------------------------------------------------- */ - -// For u-blox internal testing only -#ifdef U_PORT_TEST_ASSERT -# define EXAMPLE_FINAL_STATE(x) U_PORT_TEST_ASSERT(x); -#else -# define EXAMPLE_FINAL_STATE(x) -#endif - -#ifndef U_PORT_TEST_FUNCTION -# error if you are not using the unit test framework to run this code you must ensure that the platform clocks/RTOS are set up and either define U_PORT_TEST_FUNCTION yourself or replace it as necessary. -#endif - -/* ---------------------------------------------------------------- - * TYPES - * -------------------------------------------------------------- */ - -/* ---------------------------------------------------------------- - * VARIABLES - * -------------------------------------------------------------- */ - -// Cellular configuration. -// Set U_CFG_TEST_CELL_MODULE_TYPE to your module type, -// chosen from the values in cell/api/u_cell_module_type.h -// -// Note that the pin numbers are those of the MCU: if you -// are using an MCU inside a u-blox module the IO pin numbering -// for the module is likely different that from the MCU: check -// the data sheet for the module to determine the mapping. - -#ifdef U_CFG_TEST_CELL_MODULE_TYPE -// DEVICE i.e. module/chip configuration: in this case a cellular -// module connected via UART -static const uDeviceCfg_t gDeviceCfg = { - .deviceType = U_DEVICE_TYPE_CELL, - .deviceCfg = { - .cfgCell = { - .moduleType = U_CFG_TEST_CELL_MODULE_TYPE, - .pSimPinCode = NULL, /* SIM pin */ - .pinEnablePower = U_CFG_APP_PIN_CELL_ENABLE_POWER, - .pinPwrOn = U_CFG_APP_PIN_CELL_PWR_ON, - .pinVInt = U_CFG_APP_PIN_CELL_VINT, - .pinDtrPowerSaving = U_CFG_APP_PIN_CELL_DTR - }, - }, - .transportType = U_DEVICE_TRANSPORT_TYPE_UART, - .transportCfg = { - .cfgUart = { - .uart = U_CFG_APP_CELL_UART, - .baudRate = U_CELL_UART_BAUD_RATE, - .pinTxd = U_CFG_APP_PIN_CELL_TXD, - .pinRxd = U_CFG_APP_PIN_CELL_RXD, - .pinCts = U_CFG_APP_PIN_CELL_CTS, - .pinRts = U_CFG_APP_PIN_CELL_RTS - }, - }, -}; -#else -static const uDeviceCfg_t gDeviceCfg = {.deviceType = U_DEVICE_TYPE_NONE}; -#endif - -/* ---------------------------------------------------------------- - * STATIC FUNCTIONS - * -------------------------------------------------------------- */ - -// Print out binary. -static void printHex(const char *pStr, size_t length) -{ - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - uPortLog("%02x", c); - } -} - -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: THE EXAMPLE - * -------------------------------------------------------------- */ - -// The entry point, main(): before this is called the system -// clocks must have been started and the RTOS must be running; -// we are in task space. -U_PORT_TEST_FUNCTION("[example]", "exampleSecC2c") -{ - uDeviceHandle_t devHandle = NULL; - char rotUid[U_SECURITY_ROOT_OF_TRUST_UID_LENGTH_BYTES]; - char key[U_SECURITY_C2C_ENCRYPTION_KEY_LENGTH_BYTES]; - char hmac[U_SECURITY_C2C_HMAC_TAG_LENGTH_BYTES]; - char serialNumber1[U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES]; - char serialNumber2[U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES]; - int32_t x; - int32_t y; - bool same = false; - - // Initialise the APIs we will need - uPortInit(); - uDeviceInit(); - - // Open the device - x = uDeviceOpen(&gDeviceCfg, &devHandle); - uPortLog("Opened device with return code %d.\n", x); - - if (x == 0) { - // Remember: at this point the module must NEVER have - // been able to contact the u-blox security servers, - // must never have been connected to cellular, hence - // no "uNetworkInterfaceUp()" here. - - if (uSecurityIsSupported(devHandle)) { - - // This simply a mechanism to ensure - // that the module has had time to - // wake-up the u-blox security features - // completely, since there's no point in - // wasting time checking for device status - uSecurityGetRootOfTrustUid(devHandle, rotUid); - - // Your MCU or factory test system would have - // generated the 16-byte U_CFG_TEST_SECURITY_C2C_TE_SECRET - uPortLog("Performing C2c pairing...\n"); - if (uSecurityC2cPair(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0) { - uPortLog("Pairing completed, the values:"); - uPortLog("\nC2C TE secret: "); - printHex(U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - sizeof(U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET)) - 1); - uPortLog("\nC2C key: "); - printHex(key, sizeof(key)); - uPortLog("\nC2C HMAC: "); - printHex(hmac, sizeof(hmac)); - uPortLog("\n...should be stored securely by your MCU" - " as they are required to switch on C2C" - " protection when you need it.\n"); - uPortLog("Note: HMAC will be zero for v1 C2C but" - " must still be provided to uSecurityC2cOpen().\n"); - - // The pairing process above is now NEVER EVER - // run again: C2C sessions are simply opened - // and closed using the stored keys. - - uPortLog("A C2C session is not yet open, the following" - " AT transaction will be in plain text.\n"); - x = uSecurityGetSerialNumber(devHandle, serialNumber1); - uPortLog("Module returned serial number %.*s.\n", - x, serialNumber1); - - uPortLog("Opening a secure session using the stored" - " keys...\n"); - if (uSecurityC2cOpen(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_TEST_SECURITY_C2C_TE_SECRET), - key, hmac) == 0) { - uPortLog("With a C2C session open AT comms are" - " now scrambled; please connect a logic" - " probe to the serial lines between" - " the MCU and the module to see the" - " effect.\n"); - y = uSecurityGetSerialNumber(devHandle, serialNumber2); - uPortLog("Module returned serial number %.*s.\n", - y, serialNumber2); - same = memcmp(serialNumber1, serialNumber2, x) == 0; - if (!same) { - uPortLog("There's a problem- those should have" - " been the same!\n"); - } - - // Perform any other operations you wish with - // C2C enabled. - - uPortLog("Closing the C2C session...\n"); - if (uSecurityC2cClose(devHandle) == 0) { - uPortLog("With the C2C session closed AT" - " communications are in plain text" - " once more.\n"); - y = uSecurityGetSerialNumber(devHandle, serialNumber2); - uPortLog("Module returned serial number %.*s.\n", - y, serialNumber2); - } else { - uPortLog("Unable to close the C2C security session!\n"); - } - } else { - uPortLog("Unable to open a C2C security session!\n"); - } - } else { - uPortLog("Unable to perform C2C pairing!\n"); - } - } else { - uPortLog("This device does not support u-blox security.\n"); - } - - // For u-blox internal testing only - EXAMPLE_FINAL_STATE((same) || !uSecurityIsSupported(devHandle)); - - // Close the device - // Note: we don't power the device down here in order - // to speed up testing; you may prefer to power it off - // by setting the second parameter to true. - uDeviceClose(devHandle, false); - - } else { - uPortLog("Unable to bring up the device!\n"); - } - - // Tidy up - uDeviceDeinit(); - uPortDeinit(); - - uPortLog("Done.\n"); -} - -#endif // U_CFG_TEST_SECURITY_C2C_TE_SECRET - -// End of file diff --git a/example/security/e2e/README.md b/example/security/e2e/README.md deleted file mode 100644 index ec3abdb5..00000000 --- a/example/security/e2e/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Introduction -This example demonstrate how to use the end to end data protection u-blox security feature. The module in use must have been security sealed for this example to work: see the comments in the device-side example code that describe how to do this. - -# Usage -Follow the instructions in the directory above this to build and download the target code, setting the \#define `U_CFG_APP_FILTER` to `exampleSecE2e` (noting that NO quotation marks should be included) if you wish to run *just* this example, as opposed to all the examples and unit tests. - -When the target code has run and completed successfully it will print the encrypted message in the debug stream, something like: - -``` -76 byte(s) of data returned. -11010008003f97203b08c787d1b200006423b8017f96548d65fcaf1036f21d10bb18bd86b57178685ffbaf471162e5bf5a7445ff568290fbb8cce57d75d5ae4830aad00da6cfd589c6795691 -``` - -To obtain the plain text once more, edit the lines at the top of `e2edecrypt.py` to fill in your `API_KEY` and `API_SECRET` and set `ENC_DATA` to be the encrypted data. Run the Python script and it should return to you the un-encrypted data once more. diff --git a/example/security/e2e/e2e_main.c b/example/security/e2e/e2e_main.c deleted file mode 100644 index 5f6975e7..00000000 --- a/example/security/e2e/e2e_main.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2019-2023 u-blox - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** @brief This example demonstrates how to use u-blox end to end data - * protection. - * - * The choice of module and the choice of platform on which this - * code runs is made at build time, see the README.md for - * instructions. - * - * IMPORTANT: the module in use must have been security sealed before - * this example can be used. Since this is a once-only irreversible - * procedure this example does not perform a security seal automatically. - * Please read the code in the \#if 0 section below to see how it would - * be done. - */ - -// Bring in all of the ubxlib public header files -#include "ubxlib.h" - -// Bring in the application settings -#include "u_cfg_app_platform_specific.h" - -#ifndef U_CFG_DISABLE_TEST_AUTOMATION -// This purely for internal u-blox testing -# include "u_cfg_test_platform_specific.h" -#endif - -/* ---------------------------------------------------------------- - * COMPILE-TIME MACROS - * -------------------------------------------------------------- */ - -// The message to encrypt: edit this as you wish, ensuring that -// MY_MESSAGE_LENGTH reflects the length of the message when done. -#define MY_MESSAGE "The quick brown fox jumps over the lazy dog." - -// The length of MY_MESSAGE in bytes. -#define MY_MESSAGE_LENGTH 44 - -// For u-blox internal testing only -#ifdef U_PORT_TEST_ASSERT -# define EXAMPLE_FINAL_STATE(x) U_PORT_TEST_ASSERT(x); -#else -# define EXAMPLE_FINAL_STATE(x) -#endif - -#ifndef U_PORT_TEST_FUNCTION -# error if you are not using the unit test framework to run this code you must ensure that the platform clocks/RTOS are set up and either define U_PORT_TEST_FUNCTION yourself or replace it as necessary. -#endif - -/* ---------------------------------------------------------------- - * TYPES - * -------------------------------------------------------------- */ - -/* ---------------------------------------------------------------- - * VARIABLES - * -------------------------------------------------------------- */ - -// Cellular configuration. -// Set U_CFG_TEST_CELL_MODULE_TYPE to your module type, -// chosen from the values in cell/api/u_cell_module_type.h -// -// Note that the pin numbers are those of the MCU: if you -// are using an MCU inside a u-blox module the IO pin numbering -// for the module is likely different that from the MCU: check -// the data sheet for the module to determine the mapping. - -#ifdef U_CFG_TEST_CELL_MODULE_TYPE -// DEVICE i.e. module/chip configuration: in this case a cellular -// module connected via UART -static const uDeviceCfg_t gDeviceCfg = { - .deviceType = U_DEVICE_TYPE_CELL, - .deviceCfg = { - .cfgCell = { - .moduleType = U_CFG_TEST_CELL_MODULE_TYPE, - .pSimPinCode = NULL, /* SIM pin */ - .pinEnablePower = U_CFG_APP_PIN_CELL_ENABLE_POWER, - .pinPwrOn = U_CFG_APP_PIN_CELL_PWR_ON, - .pinVInt = U_CFG_APP_PIN_CELL_VINT, - .pinDtrPowerSaving = U_CFG_APP_PIN_CELL_DTR - }, - }, - .transportType = U_DEVICE_TRANSPORT_TYPE_UART, - .transportCfg = { - .cfgUart = { - .uart = U_CFG_APP_CELL_UART, - .baudRate = U_CELL_UART_BAUD_RATE, - .pinTxd = U_CFG_APP_PIN_CELL_TXD, - .pinRxd = U_CFG_APP_PIN_CELL_RXD, - .pinCts = U_CFG_APP_PIN_CELL_CTS, - .pinRts = U_CFG_APP_PIN_CELL_RTS - }, - }, -}; -// NETWORK configuration for cellular -static const uNetworkCfgCell_t gNetworkCfg = { - .type = U_NETWORK_TYPE_CELL, - .pApn = NULL, /* APN: NULL to accept default. If using a Thingstream SIM enter "tsiot" here */ - .timeoutSeconds = 240 /* Connection timeout in seconds */ - // There is an additional field here "pKeepGoingCallback", - // which we do NOT set, we allow the compiler to set it to 0 - // and all will be fine. You may set the field to a function - // of the form "bool keepGoingCallback(uDeviceHandle_t devHandle)", - // e.g.: - // .pKeepGoingCallback = keepGoingCallback - // ...and your function will be called periodically during an - // abortable network operation such as connect/disconnect; - // if it returns true the operation will continue else it - // will be aborted, allowing you immediate control. If this - // field is set, timeoutSeconds will be ignored. -}; -#else -static const uDeviceCfg_t gDeviceCfg = {.deviceType = U_DEVICE_TYPE_NONE}; -static const uNetworkCfgCell_t gNetworkCfg = {.type = U_NETWORK_TYPE_NONE}; -#endif - -/* ---------------------------------------------------------------- - * STATIC FUNCTIONS - * -------------------------------------------------------------- */ - -// Print out binary. -static void printHex(const char *pStr, size_t length) -{ - char c; - - for (size_t x = 0; x < length; x++) { - c = *pStr++; - uPortLog("%02x", c); - } -} - -/* ---------------------------------------------------------------- - * PUBLIC FUNCTIONS: THE EXAMPLE - * -------------------------------------------------------------- */ - -// The entry point, main(): before this is called the system -// clocks must have been started and the RTOS must be running; -// we are in task space. -U_PORT_TEST_FUNCTION("[example]", "exampleSecE2e") -{ - uDeviceHandle_t devHandle = NULL; - int32_t rxSize = 0; - int32_t returnCode; - char buffer[MY_MESSAGE_LENGTH + U_SECURITY_E2E_HEADER_LENGTH_MAX_BYTES]; - - // Initialise the APIs we will need - uPortInit(); - uDeviceInit(); - - // Open the device - returnCode = uDeviceOpen(&gDeviceCfg, &devHandle); - uPortLog("Opened device with return code %d.\n", returnCode); - - if (returnCode == 0) { - // Bring up the network interface - uPortLog("Bringing up the network...\n"); - if (uNetworkInterfaceUp(devHandle, U_NETWORK_TYPE_CELL, - &gNetworkCfg) == 0) { - - // The module must have previously been security - // sealed for this example to work - if (uSecurityIsSealed(devHandle)) { - uPortLog("Device is security sealed.\n"); - - uPortLog("Requesting end to end encryption of %d" - " byte(s) of data \"%s\"...\n", - MY_MESSAGE_LENGTH, MY_MESSAGE); - rxSize = uSecurityE2eEncrypt(devHandle, MY_MESSAGE, - buffer, MY_MESSAGE_LENGTH); - uPortLog("%d byte(s) of data returned.\n", rxSize); - printHex(buffer, rxSize); - uPortLog("\n"); - uPortLog("This completes the embedded-side of the example;" - " see the README.md for what to do next.\n"); - } else { - uPortLog("This device is not security sealed, the end to" - " end encryption example will not run; see comments" - " in the example source code for how to do sealing.\n"); - // The code below would effect a security seal. -#if 0 - // Since sealing is a once-only irreversible process this code - // is #if 0'ed out. Should you want to perform security - // sealing you may compile this code in, maybe move it up to - // always occur before the end-to-end encryption code runs - // (if the device is detected to not be already sealed) but if - // you do so make VERY SURE that the compilation flag discussed - // below is set correctly each time. - - // There are two inputs to the sealing process: a device profile - // UID (see the README.md in the directory above for how this - // is obtained from u-blox) and a serial number of your choosing. - - // To run sealing with this example code, set the value of - // U_CFG_SECURITY_DEVICE_PROFILE_UID to the device profile UID - // *without* quotation marks, i.e. something like: - // - // U_CFG_SECURITY_DEVICE_PROFILE_UID=AgbCtixjwqLjwV3VWpfPyz - -# ifdef U_CFG_SECURITY_DEVICE_PROFILE_UID - int32_t x; - char serialNumber[U_SECURITY_SERIAL_NUMBER_MAX_LENGTH_BYTES]; - - uPortLog("Waiting for bootstrap status...\n"); - // Before security sealing can be performed the device must - // have contacted u-blox security services and "bootstrapped" - // itself (a once-only process): check that this has happened - for (x = 10; (x > 0) && !uSecurityIsBootstrapped(devHandle); x--) { - uPortTaskBlock(5000); - } - - if (uSecurityIsBootstrapped(devHandle)) { - uPortLog("Device is bootstrapped.\n"); - - // In this example we obtain the serial number of the - // device and use that in the sealing process. You - // may chose your own serial number instead if you wish. - x = uSecurityGetSerialNumber(devHandle, serialNumber); - if ((x > 0) && x < (int32_t) sizeof(serialNumber)) { - uPortLog("Performing security seal with device profile UID" - " string \"%s\" and serial number \"%s\"...\n", - U_PORT_STRINGIFY_QUOTED(U_CFG_SECURITY_DEVICE_PROFILE_UID), - serialNumber); - if (uSecuritySealSet(devHandle, - U_PORT_STRINGIFY_QUOTED(U_CFG_SECURITY_DEVICE_PROFILE_UID), - serialNumber, NULL) == 0) { - uPortLog("Device is security sealed with device profile UID string \"%s\"" - " and serial number \"%s\".\n", - U_PORT_STRINGIFY_QUOTED(U_CFG_SECURITY_DEVICE_PROFILE_UID), - serialNumber); - } else { - uPortLog("Unable to security seal device!\n"); - } - } else { - uPortLog("Unable to obtain a serial number from the device!\n"); - } - } else { - uPortLog("This device has not bootstrapped itself!\n"); - } -# else -# error U_CFG_SECURITY_DEVICE_PROFILE_UID must be set to your device profile UID (without quotation marks) to use this code. -# endif -#endif - } - - // When finished with the network layer - uPortLog("Taking down network...\n"); - uNetworkInterfaceDown(devHandle, U_NETWORK_TYPE_CELL); - } else { - uPortLog("Unable to bring up the network!\n"); - } - -#ifndef U_CFG_SECURITY_DEVICE_PROFILE_UID - // For u-blox internal testing only - EXAMPLE_FINAL_STATE((rxSize >= MY_MESSAGE_LENGTH + U_SECURITY_E2E_HEADER_LENGTH_MIN_BYTES) || \ - !uSecurityIsSupported(devHandle)); -#endif - - // Close the device - // Note: we don't power the device down here in order - // to speed up testing; you may prefer to power it off - // by setting the second parameter to true. - uDeviceClose(devHandle, false); - - } else { - uPortLog("Unable to bring up the device!\n"); - } - - // Tidy up - uDeviceDeinit(); - uPortDeinit(); - - uPortLog("Done.\n"); -} - -// End of file diff --git a/example/security/e2e/e2edecrypt.py b/example/security/e2e/e2edecrypt.py deleted file mode 100644 index 3ec79bae..00000000 --- a/example/security/e2e/e2edecrypt.py +++ /dev/null @@ -1,126 +0,0 @@ -from collections import namedtuple -from base64 import b64decode -import binascii -import json -import sys -import requests -from Cryptodome.Cipher import AES - - -#################################################################################################### -# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv EDIT HERE vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv # -# API key and API secret as Base64 strings -API_KEY = "" -API_SECRET = "" -# Encrypted data returned by AT+USECE2EDATAENC as HEX string -ENC_DATA = "" -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EDIT HERE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # -#################################################################################################### - - -SERVER = "https://ssapi.services.u-blox.com" -DecParams = namedtuple('DecParams', 'Nonce, MACLength, CipherSuite, Key, ROTPublicUID') -CONTENT_TYPE = 'application/json' - - -def get_key_identity(): - if ENC_DATA[0] == '1': - key_id = ENC_DATA[:32] - elif ENC_DATA[0] == '2': - key_id = ENC_DATA[:22] - else: - print("Encrypted data format unexpected!") - sys.exit(1) - print(f"Using key identity: {key_id}") - return key_id - - -def authenticate(): - endpoint = "/v1/authenticate" - payload = { - "APIKey": API_KEY, - "APISecret": API_SECRET, - } - headers = { - 'Content-Type': CONTENT_TYPE - } - print(f"Sending request to API {endpoint}") - response = requests.request("POST", SERVER+endpoint, headers=headers, data=json.dumps(payload)) - - if response.ok: - try: - jresp = response.json() - print("The request succeeded!") - return (jresp['AuthToken'], jresp['RefreshToken']) - except ValueError: - print(f"Error parsing the response: {response.text.encode('utf8')}") - else: - print("The request failed!") - print(response.text.encode('utf8')) - - -def get_e2e_decrypt_params(auth_token): - endpoint = "/v1/e2e/uplink/protectionparameters/get" - payload = { - "EncryptedHeader": get_key_identity() - } - headers = { - 'Authorization': auth_token, - 'Content-Type': CONTENT_TYPE - } - print(f"Sending request to API {endpoint}") - response = requests.request("POST", SERVER+endpoint, headers=headers, data=json.dumps(payload)) - - if response.ok: - try: - jresp = response.json() - print("The request succeeded!") - print(jresp) - return DecParams(**jresp) - except ValueError: - print(f"Error parsing the response: {response.text.encode('utf8')}") - else: - print("The request failed!") - print(response.text.encode('utf8')) - - -def aesccm_dec(dec_params): - # AES-128-CCM - # decryption key and nonce are given by the API response as base64 strings - # they need to be converted to bytes (binary data) - key = b64decode(dec_params.Key) - nonce = b64decode(dec_params.Nonce) - # ciphertext and MAC tag are extracted from the encrypted data as hex strings - # get key identity and MAC tag lengths - mac_len = dec_params.MACLength // 8 # in bytes - key_id_len = 16 if ENC_DATA[0] == '1' else 11 # in bytes - # they need to be converted to bytes (binary data) - ciphertext = binascii.unhexlify(ENC_DATA[key_id_len*2:-mac_len*2]) - tag = binascii.unhexlify(ENC_DATA[-mac_len*2:]) - - try: - cipher = AES.new(key, AES.MODE_CCM, nonce=nonce) - plaintext = cipher.decrypt_and_verify(ciphertext, tag) - print("The message was: " + plaintext.decode('utf-8')) - except (ValueError, KeyError) as ex: - print("Incorrect decryption") - print(ex) - - -if __name__ == '__main__': - auth_token, refresh_token = authenticate() - if auth_token is None: - print("Can't authenticate to security server!") - sys.exit(0) - - dec_params = get_e2e_decrypt_params(auth_token) - if dec_params is None: - print("Can't get decryption parameters!") - sys.exit(0) - - if dec_params.CipherSuite == "AES_128_CCM": - print("V1: using AES-128-CCM decryption") - aesccm_dec(dec_params) - else: - print("V2 not supported") - sys.exit(0) diff --git a/example/security/keys.jpg b/example/security/keys.jpg deleted file mode 100644 index 58e662b0..00000000 Binary files a/example/security/keys.jpg and /dev/null differ diff --git a/example/security/psk/README.md b/example/security/psk/README.md deleted file mode 100644 index 659ea90f..00000000 --- a/example/security/psk/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Introduction -This example demonstrate how to use the pre-shared key generation u-blox security feature. The module in use must have been security sealed for this example to work: see the comments in the device-side example code that describe how to do this. - -# Usage -Follow the instructions in the directory above this to build and download the target code, setting the \#define `U_CFG_APP_FILTER` to `exampleSecPsk` (noting that NO quotation marks should be included) if you wish to run *just* this example, as opposed to all the examples and unit tests. - -When the target code has run and completed successfully it will print the example generated PSK and associated PSK ID in the debug stream, something like: - -``` -32 bytes of PSK returned: 70665f1ba1753d36e5a412a56233507da1dbd0d8476f418423892de0895c7e9f -14 byte(s) of PSK ID returned: 11010008003f9720f38c30c88428 -``` \ No newline at end of file diff --git a/example/security/psk/psk_main.c b/example/security/psk_main.c similarity index 100% rename from example/security/psk/psk_main.c rename to example/security/psk_main.c diff --git a/port/platform/arduino/source.txt b/port/platform/arduino/source.txt index 97b220c2..83829685 100644 --- a/port/platform/arduino/source.txt +++ b/port/platform/arduino/source.txt @@ -19,7 +19,6 @@ cell/src/u_cell_info.c cell/src/u_cell_net.c cell/src/u_cell_sock.c cell/src/u_cell_sec.c -cell/src/u_cell_sec_c2c.c cell/src/u_cell_sec_tls.c cell/src/u_cell_mqtt.c cell/src/u_cell_http.c diff --git a/port/platform/arduino/source_test.txt b/port/platform/arduino/source_test.txt index fb2a4eb1..eeed9b62 100644 --- a/port/platform/arduino/source_test.txt +++ b/port/platform/arduino/source_test.txt @@ -6,8 +6,7 @@ example/sockets/main.c example/sockets/main_tls.c example/sockets/credentials_tls.c -example/security/e2e/e2e_main.c -example/security/psk/psk_main.c +example/security/psk_main.c example/mqtt_client/mqtt_main.c example/http_client/http_main.c example/location/main_loc_gnss.c @@ -29,7 +28,6 @@ cell/test/u_cell_cfg_test.c cell/test/u_cell_info_test.c cell/test/u_cell_net_test.c cell/test/u_cell_sock_test.c -cell/test/u_cell_sec_c2c_test.c cell/test/u_cell_sec_tls_test.c cell/test/u_cell_mqtt_test.c cell/test/u_cell_http_test.c diff --git a/port/platform/cell_ucpu/r5/runner/CMakeLists.txt b/port/platform/cell_ucpu/r5/runner/CMakeLists.txt index 2df9b352..655c465b 100644 --- a/port/platform/cell_ucpu/r5/runner/CMakeLists.txt +++ b/port/platform/cell_ucpu/r5/runner/CMakeLists.txt @@ -170,9 +170,7 @@ elseif (U_CFG_TEST_FILTER STREQUAL exampleCell) elseif (U_CFG_TEST_FILTER STREQUAL exampleMqtt) add_ubxlib_tests(${UBXLIB_BASE}/example/mqtt_client) elseif (U_CFG_TEST_FILTER STREQUAL exampleSec) - add_ubxlib_tests(${UBXLIB_BASE}/example/security/c2c) - add_ubxlib_tests(${UBXLIB_BASE}/example/security/e2e) - add_ubxlib_tests(${UBXLIB_BASE}/example/security/psk) + add_ubxlib_tests(${UBXLIB_BASE}/example/security) elseif (U_CFG_TEST_FILTER STREQUAL exampleSockets) add_ubxlib_tests(${UBXLIB_BASE}/example/sockets) else () diff --git a/port/platform/common/automation/DATABASE.md b/port/platform/common/automation/DATABASE.md index a8e8068c..4d0451c4 100644 --- a/port/platform/common/automation/DATABASE.md +++ b/port/platform/common/automation/DATABASE.md @@ -29,7 +29,7 @@ The table below defines the instances of test hardware available on the `ubxlib` | 5.3 | Check public headers not in ubxlib.h | | | | | | | | | | 5.4 | Check malloc()/free() being called | | | | | | | | | | 6.1 | CodeChecker (Zephyr) | | NRF5340 | nrf5340dk_nrf5340_cpuapp | CodeChecker:Zephyr | | SARA_R5 M8 NINA_W15 | | U_CFG_BLE_MODULE_INTERNAL U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_BLE_TEST_CFG_REMOTE_SPS_PERIPHERAL=2462ABB6EAC6p U_CFG_APP_SHORT_RANGE_ROLE=1 U_CFG_TEST_NET_STATUS_CELL U_CFG_TEST_NET_STATUS_SHORT_RANGE U_DEBUG_UTILS_DUMP_THREADS | -| 6.2 | CodeChecker (STM32Cube) | | STM32F4 | | CodeChecker:STM32Cube || SARA_R5 NINA_W15 M8 | | U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=0x42 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_CFG_TEST_NET_STATUS_CELL U_CFG_TEST_NET_STATUS_SHORT_RANGE U_DEBUG_UTILS_DUMP_THREADS | +| 6.2 | CodeChecker (STM32Cube) | | STM32F4 | | CodeChecker:STM32Cube || SARA_R5 NINA_W15 M8 | | U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=0x42 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_NET_STATUS_CELL U_CFG_TEST_NET_STATUS_SHORT_RANGE U_DEBUG_UTILS_DUMP_THREADS | | 7 | Build PIO examples | | | | | | | | | | 8 | Build as a Zephyr module | | NRF5340 | nrf5340dk_nrf5340_cpuapp | | | | | | 10.0 | ESP32-DevKitC | 10 | ESP32 | | ESP-IDF | | M10 | port ubx_protocol gnss spartn | U_CFG_APP_GNSS_I2C=0 U_CFG_TEST_PIN_GNSS_RESET_N=23 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_CFG_MUTEX_DEBUG U_DEBUG_UTILS_DUMP_THREADS | @@ -38,11 +38,11 @@ The table below defines the instances of test hardware available on the `ubxlib` | 11.0 | ESP32-DevKitC | 5 | ESP32 | | ESP-IDF | | M9 | port at_client ubx_protocol gnss spartn | U_CFG_APP_GNSS_SPI=2 U_CFG_TEST_PIN_GNSS_RESET_N=25 U_CFG_MUTEX_DEBUG U_CFG_TEST_UART_B=1 U_CFG_TEST_PIN_UART_A_CTS=-1 U_CFG_TEST_PIN_UART_A_RTS=-1 U_CFG_TEST_PIN_UART_A_RXD=26 U_CFG_TEST_PIN_UART_B_TXD=27 U_CFG_TEST_PIN_UART_B_RXD=14 U_DEBUG_UTILS_DUMP_THREADS | | 11.1 | ESP32-DevKitC | 5 | ESP32 | esp32:esp32:esp32doit-devkit-v1 | Arduino | ESP-IDF | M9 | port at_client ubx_protocol gnss spartn | U_CFG_APP_GNSS_SPI=2 U_CFG_TEST_PIN_GNSS_RESET_N=25 U_CFG_TEST_UART_B=1 U_CFG_TEST_PIN_UART_A_CTS=-1 U_CFG_TEST_PIN_UART_A_RTS=-1 U_CFG_TEST_PIN_UART_A_RXD=26 U_CFG_TEST_PIN_UART_B_TXD=27 U_CFG_TEST_PIN_UART_B_RXD=14 | | 11.2 | ESP32-DevKitC | 5 | ESP32 | esp-wrover-kit | platformio | arduino | M9 | port device network | U_CFG_APP_FILTER=port.example U_CFG_APP_GNSS_SPI=2 U_CFG_TEST_PIN_GNSS_RESET_N=25 | -| 12.0 | ESP32-DevKitC + EVK, Cat M1 | 25 | ESP32 | | ESP-IDF | | SARA_R5 M8 NINA_W15 | port device network sock ble wifi cell short_range security mqtt_client http_client gnss location | U_CELL_TEST_MUX_ALWAYS U_CFG_TEST_CELL_PWR_DISABLE U_CELL_CFG_SARA_R5_00B U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_DEBUG_UTILS_DUMP_THREADS | -| 12.2.0| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | SARA_R5 M8 NINA_W15 | port device network | U_CFG_APP_FILTER=port.example.gnssInfo U_CELL_CFG_SARA_R5_00B U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 | -| 12.2.1| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | SARA_R5 | port device network | UBXLIB_FEATURES=cell U_CFG_APP_FILTER=cellInfo U_CELL_CFG_SARA_R5_00B U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 | -| 12.2.2| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | NINA_W15 | port device network | UBXLIB_FEATURES=short_range U_CFG_APP_FILTER=wifiStation U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 | -| 13.0.0| Nordic DK board (NRF52840) + EVK, Cat M1 | 25 | NRF52840 | | nRF5SDK | GCC | SARA_R5 M10 | port at_client cell sock network mqtt_client ubx_protocol gnss spartn | U_CFG_TEST_CELL_PWR_DISABLE U_CFG_TEST_MQTT_CLIENT_SN_DISABLE_CONNECTIVITY_TEST U_CELL_CFG_SARA_R5_00B U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_APP_GNSS_I2C=1 U_CFG_TEST_PIN_GNSS_RESET_N=29 U_CFG_TEST_UART_B=0 U_CFG_TEST_PIN_UART_A_CTS=-1 U_CFG_TEST_PIN_UART_A_RTS=-1 U_CFG_TEST_PIN_UART_B_TXD=44 U_CFG_TEST_PIN_UART_B_RXD=43 U_CFG_TEST_PIN_UART_A_RXD=45 U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_DEBUG_UTILS_DUMP_THREADS | +| 12.0 | ESP32-DevKitC + EVK, Cat M1 | 25 | ESP32 | | ESP-IDF | | SARA_R5 M8 NINA_W15 | port device network sock ble wifi cell short_range security mqtt_client http_client gnss location | U_CELL_TEST_MUX_ALWAYS U_CFG_TEST_SECURITY_DISABLE U_CFG_TEST_CELL_PWR_DISABLE U_CELL_CFG_SARA_R5_00B U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_DEBUG_UTILS_DUMP_THREADS | +| 12.2.0| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | SARA_R5 M8 NINA_W15 | port device network | U_CFG_APP_FILTER=port.example.gnssInfo U_CELL_CFG_SARA_R5_00B U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p | +| 12.2.1| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | SARA_R5 | port device network | UBXLIB_FEATURES=cell U_CFG_APP_FILTER=cellInfo U_CFG_TEST_SECURITY_DISABLE U_CELL_CFG_SARA_R5_00B U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_APP_PIN_CELL_TXD=21 U_CFG_APP_PIN_CELL_RXD=19 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 | +| 12.2.2| ESP32-DevKitC + EVK, Cat M1 | 5 | ESP32 | esp-wrover-kit | platformio | espidf | NINA_W15 | port device network | UBXLIB_FEATURES=short_range U_CFG_APP_FILTER=wifiStation U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=2 U_CFG_APP_PIN_SHORT_RANGE_CTS=22 U_CFG_APP_PIN_SHORT_RANGE_RTS=23 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p | +| 13.0.0| Nordic DK board (NRF52840) + EVK, Cat M1 | 25 | NRF52840 | | nRF5SDK | GCC | SARA_R5 M10 | port at_client cell sock network mqtt_client ubx_protocol gnss spartn | U_CFG_TEST_CELL_PWR_DISABLE U_CFG_TEST_MQTT_CLIENT_SN_DISABLE_CONNECTIVITY_TEST U_CELL_CFG_SARA_R5_00B U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_APP_GNSS_I2C=1 U_CFG_TEST_PIN_GNSS_RESET_N=29 U_CFG_TEST_UART_B=0 U_CFG_TEST_PIN_UART_A_CTS=-1 U_CFG_TEST_PIN_UART_A_RTS=-1 U_CFG_TEST_PIN_UART_B_TXD=44 U_CFG_TEST_PIN_UART_B_RXD=43 U_CFG_TEST_PIN_UART_A_RXD=45 U_DEBUG_UTILS_DUMP_THREADS | | 13.1 | Nordic DK board (NRF52840) + EVK | 10 | NRF52840 | nrf52840dk_nrf52840 | Zephyr | | | port at_client ubx_protocol spartn | U_CFG_TEST_UART_B=0 U_DEBUG_UTILS_DUMP_THREADS | | 14 | STM32F4 Discovery (STM32F407) + EVK, Cat M1| 25 | STM32F4 | | STM32Cube | | SARA_R422 M9 | port device network sock security cell mqtt_client http_client gnss location | CMSIS_V2 U_CFG_TEST_CELL_PWR_DISABLE U_LOCATION_TEST_DISABLE U_CFG_1V8_SIM_WORKAROUND HSE_VALUE=8000000U U_CFG_APP_GNSS_SPI=2 U_CFG_APP_PIN_GNSS_SPI_MOSI=0x1F U_CFG_APP_PIN_GNSS_SPI_MISO=0x1E U_CFG_APP_PIN_GNSS_SPI_CLK=0x1D U_CFG_APP_PIN_GNSS_SPI_SELECT=0x1C U_CFG_TEST_PIN_GNSS_RESET_N=0x40 U_CFG_TEST_PIN_C=0x3F U_CFG_APP_GNSS_UART=-1 U_CFG_APP_PIN_GNSS_ENABLE_POWER=-1 U_CFG_TEST_UART_A=-1 U_CFG_APP_PIN_C030_ENABLE_3V3=-1 U_CFG_APP_PIN_CELL_RESET=-1 U_CFG_APP_CELL_UART=3 U_CFG_APP_PIN_CELL_TXD=0x38 U_CFG_APP_PIN_CELL_RXD=0x39 U_CFG_APP_PIN_CELL_RTS=-1 U_CFG_APP_PIN_CELL_CTS=-1 U_DEBUG_UTILS_DUMP_THREADS | | 15.0.0| Nordic DK board (NRF52840) + EVK | 10 | NRF52840 | | nRF5SDK | GCC | M9 | port gnss device | U_CFG_APP_GNSS_SPI=3 U_CFG_TEST_PIN_GNSS_RESET_N=37 U_CFG_MUTEX_DEBUG U_DEBUG_UTILS_DUMP_THREADS | @@ -50,12 +50,12 @@ The table below defines the instances of test hardware available on the `ubxlib` | 16 | STM32F4 Discovery (STM32F407) | 10 | STM32F4 | | STM32Cube | | M10 | port ubx_protocol gnss spartn | HSE_VALUE=8000000U U_PORT_TEST_DISABLE_I2C U_CFG_APP_GNSS_I2C=1 U_CFG_TEST_PIN_GNSS_RESET_N=0x40 U_CFG_APP_GNSS_UART=-1 U_CFG_APP_PIN_GNSS_ENABLE_POWER=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_CFG_APP_PIN_C030_ENABLE_3V3=-1 U_CFG_APP_PIN_CELL_RESET=-1 U_DEBUG_UTILS_DUMP_THREADS | | 17.1.0| Nordic NRF5340 DK board | 10 | NRF5340 | nrf5340dk_nrf5340_cpuapp | Zephyr | | M9 | port device network ble short_range lib_common ubx_protocol gnss spartn | U_CFG_APP_GNSS_SPI=2 U_CFG_APP_PIN_GNSS_SPI_SELECT=46 U_CFG_TEST_PIN_GNSS_RESET_N=37 U_CFG_BLE_MODULE_INTERNAL U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_BLE_TEST_CFG_REMOTE_SPS_PERIPHERAL=2462ABB6EAC6p U_CFG_APP_SHORT_RANGE_ROLE=1 U_DEBUG_UTILS_DUMP_THREADS | | 17.1.1| Nordic NRF5340 DK board | 5 | NRF5340 | nrf5340dk_nrf5340_cpuapp | Zephyr | | M9 | port | U_CFG_APP_FILTER=port U_CFG_APP_GNSS_SPI=2 U_CFG_TEST_GNSS_SPI_SELECT_INDEX=1 U_CFG_APP_PIN_GNSS_SPI_SELECT=46 U_CFG_TEST_PIN_GNSS_RESET_N=37 U_DEBUG_UTILS_DUMP_THREADS | -| 18 | NORA-B1 NRF5340 DK board + EVK, Cat M1 | 20 | NRF5340 | nrf5340dk_nrf5340_cpuapp | Zephyr | | SARA_R5 M8 | port device network sock security cell lib_common mqtt_client http_client gnss location | U_CELL_TEST_MUX_ALWAYS U_CFG_TEST_CELL_PWR_DISABLE U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_TEST_CLOUD_LOCATE U_CFG_TEST_CELL_LOCATE U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_PIN_CELL_DTR=37 U_CFG_APP_PIN_CELL_PWR_ON=36 U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_DEBUG_UTILS_DUMP_THREADS | -| 19 | C030-R5 board (STM32F437), Cat M1 | 25 | STM32F4 | | STM32Cube | | SARA_R5 NINA_W15 M8 | port device network sock ble wifi cell short_range security mqtt_client http_client ubx_protocol spartn gnss location | U_CFG_TEST_CELL_PWR_DISABLE U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=0x42 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_DEBUG_UTILS_DUMP_THREADS | +| 18 | NORA-B1 NRF5340 DK board + EVK, Cat M1 | 20 | NRF5340 | nrf5340dk_nrf5340_cpuapp | Zephyr | | SARA_R5 M8 | port device network sock security cell lib_common mqtt_client http_client gnss location | U_CELL_TEST_MUX_ALWAYS U_CFG_TEST_CELL_PWR_DISABLE U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_TEST_CLOUD_LOCATE U_CFG_TEST_CELL_LOCATE U_CFG_SARA_R5_M8_WORKAROUND U_CFG_APP_PIN_CELL_DTR=37 U_CFG_APP_PIN_CELL_PWR_ON=36 U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | +| 19 | C030-R5 board (STM32F437), Cat M1 | 25 | STM32F4 | | STM32Cube | | SARA_R5 NINA_W15 M8 | port device network sock ble wifi cell short_range security mqtt_client http_client ubx_protocol spartn gnss location | U_CFG_TEST_CELL_PWR_DISABLE U_CFG_APP_PIN_SHORT_RANGE_RESET_TO_DEFAULTS=0x42 U_CFG_SARA_R5_M8_WORKAROUND U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_DEBUG_UTILS_DUMP_THREADS | | 20 | WHRE board (NINA-W1), Cat M1 | 5 | ESP32 | | ESP-IDF | | SARA_R412M_02B | cell mqtt_client | U_CFG_APP_FILTER=cellMqtt.mqttClient.exampleMqtt.cellMuxMqtt.cellCfgGreeting.cellInfo U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | | 21 | WHRE board (NINA-W1), Cat M1 | 5 | ESP32 | | ESP-IDF | | SARA_R410M_02B | cell mqtt_client | U_CFG_APP_FILTER=cellMqtt.mqttClient.exampleMqtt.cellMuxMqtt.cellCfgGreeting.cellInfo U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | | 22 | NINA-W1 + EVK, Cat M1 | 20 | ESP32 | esp32:esp32:nina_w10 | Arduino | ESP-IDF | SARA_R422 M8 | port device network sock security cell mqtt_client http_client gnss location | U_CFG_TEST_CELL_PWR_DISABLE U_GNSS_TEST_DISABLE_ACTIVE_ANTENNA_DISABLE U_CFG_MONITOR_DTR_RTS_OFF U_CFG_1V8_SIM_WORKAROUND U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_PWR_ON=5 U_CFG_APP_PIN_CELL_TXD=14 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | -| 23 | Windows + EVK, Cat M1 | 30 | WIN32 | | WINDOWS | MSVC | SARA_R5 M8 NINA_W15 | port device network sock ble wifi cell short_range security mqtt_client http_client ubx_protocol gnss spartn location | U_CFG_TEST_DISABLE_GREETING_CALLBACK U_CFG_MUTEX_DEBUG U_NETWORK_GNSS_CFG_CELL_USE_AT_ONLY U_GNSS_MSG_TEST_MESSAGE_RECEIVE_NON_BLOCKING_PRINT U_CFG_TEST_NET_STATUS_CELL=RF_SWITCH_A U_CFG_TEST_NET_STATUS_SHORT_RANGE=PWR_SWITCH_A U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=6009C390E4DAp U_CFG_TEST_UART_A=100 U_CFG_APP_SHORT_RANGE_UART=101 U_CFG_APP_CELL_UART=102 U_CFG_TEST_SECURITY_C2C_TE_SECRET=\x00\x01\x02\x03\x04\x05\x06\x07\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8 U_CFG_QUEUE_DEBUG | +| 23 | Windows + EVK, Cat M1 | 30 | WIN32 | | WINDOWS | MSVC | SARA_R5 M8 NINA_W15 | port device network sock ble wifi cell short_range security mqtt_client http_client ubx_protocol gnss spartn location | U_CFG_TEST_DISABLE_GREETING_CALLBACK U_CFG_MUTEX_DEBUG U_NETWORK_GNSS_CFG_CELL_USE_AT_ONLY U_GNSS_MSG_TEST_MESSAGE_RECEIVE_NON_BLOCKING_PRINT U_CFG_TEST_NET_STATUS_CELL=RF_SWITCH_A U_CFG_TEST_NET_STATUS_SHORT_RANGE=PWR_SWITCH_A U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=6009C390E4DAp U_CFG_TEST_UART_A=100 U_CFG_APP_SHORT_RANGE_UART=101 U_CFG_APP_CELL_UART=102 U_CFG_QUEUE_DEBUG | | 24 | Linux/Posix under Zephyr | 5 | LINUX32 | native_posix | Zephyr | | | port | U_CFG_MUTEX_DEBUG U_CFG_TEST_UART_A=0 U_CFG_TEST_UART_B=1 | | 25 | HPG Solution board (NINA-W1), live network | 25 | ESP32 | | ESP-IDF | | LARA_R6 M9 | port device network sock cell security mqtt_client gnss location | U_CFG_TEST_DISABLE_MUX U_NETWORK_GNSS_CFG_CELL_USE_AT_ONLY U_HTTP_CLIENT_DISABLE_TEST U_CFG_MONITOR_DTR_RTS_OFF U_CELL_TEST_NO_INVALID_APN U_CELL_TEST_CFG_BANDMASK1=0x0000000000080084ULL U_CELL_NET_TEST_RAT=U_CELL_NET_RAT_LTE U_CELL_TEST_CFG_MNO_PROFILE=90 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_CFG_APP_PIN_CELL_PWR_ON=0x800c U_CFG_APP_PIN_CELL_RESET=13 U_CELL_RESET_PIN_DRIVE_MODE=U_PORT_GPIO_DRIVE_MODE_NORMAL U_CFG_APP_PIN_CELL_VINT=0x8025 U_CFG_APP_PIN_CELL_DTR=15 U_CFG_APP_PIN_CELL_TXD=25 U_CFG_APP_PIN_CELL_RXD=26 U_CFG_APP_PIN_CELL_RTS=27 U_CFG_APP_PIN_CELL_CTS=36 U_CFG_APP_GNSS_I2C=0 U_GNSS_TEST_I2C_ADDRESS_EXTRA=0x43 U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | | 26 | NINA-B4 | 10 | NRF52833 | ubx_evkninab4_nrf52833 | Zephyr | | M10 | port ubx_protocol gnss spartn | U_CFG_APP_GNSS_I2C=0 U_CFG_TEST_PIN_GNSS_RESET_N=30 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 | diff --git a/port/platform/common/automation/README.md b/port/platform/common/automation/README.md index c3bdc6d8..876d2a51 100644 --- a/port/platform/common/automation/README.md +++ b/port/platform/common/automation/README.md @@ -229,7 +229,7 @@ If the commit text does *not* contain a line starting with `test:` (the usual ca [scripts/u_run_windows.py](./scripts/u_run_windows.py): build and run on Windows; called by `automation.test` PyInvoke task. -[scripts/u_run_linux.py](./scripts/u_run_linux.py): build and run on Linux; called by `automation.test` PyInvoke task. In particular, if you install the Linux `socat` utility this script will automatically handle mapping of the `/dev/pts/x` UARTs of the `ubxlib` Linux application to real Linux devices. For instance, to make `UART_0` the `U_CFG_TEST_UART_A` loop-back UART, used by the porting tests, then simply pass the \#define `U_CFG_TEST_UART_A=0` into the build. Similarly, to make `UART_1` the `U_CFG_TEST_UART_B` loop-back UART (used for the scenario in the AT command and chip-to-chip security tests where `U_CFG_TEST_UART_A` is looped back to `U_CFG_TEST_UART_B`) then you would also pass \#define `U_CFG_TEST_UART_B=1` into the build. And finally, if you have a real module connected to a real device on Linux, let's say a cellular module on `/dev/tty/5`, and you want to connect it to `ubxlib` as `UART_1`, then as well as passing the \#define `U_CFG_APP_CELL_UART=1` into the build you would also pass `U_CFG_APP_CELL_UART_DEV=/dev/tty/5`. The \#defines `U_CFG_APP_GNSS_UART` and `U_CFG_APP_SHORT_RANGE_UART` can be used similarly. +[scripts/u_run_linux.py](./scripts/u_run_linux.py): build and run on Linux; called by `automation.test` PyInvoke task. In particular, if you install the Linux `socat` utility this script will automatically handle mapping of the `/dev/pts/x` UARTs of the `ubxlib` Linux application to real Linux devices. For instance, to make `UART_0` the `U_CFG_TEST_UART_A` loop-back UART, used by the porting tests, then simply pass the \#define `U_CFG_TEST_UART_A=0` into the build. Similarly, to make `UART_1` the `U_CFG_TEST_UART_B` loop-back UART (used for the scenario in the AT commandtests where `U_CFG_TEST_UART_A` is looped back to `U_CFG_TEST_UART_B`) then you would also pass \#define `U_CFG_TEST_UART_B=1` into the build. And finally, if you have a real module connected to a real device on Linux, let's say a cellular module on `/dev/tty/5`, and you want to connect it to `ubxlib` as `UART_1`, then as well as passing the \#define `U_CFG_APP_CELL_UART=1` into the build you would also pass `U_CFG_APP_CELL_UART_DEV=/dev/tty/5`. The \#defines `U_CFG_APP_GNSS_UART` and `U_CFG_APP_SHORT_RANGE_UART` can be used similarly. [scripts/u_select.py](./scripts/u_select.py): see above. diff --git a/port/platform/common/automation/SETUP.md b/port/platform/common/automation/SETUP.md index df225924..d341f50d 100644 --- a/port/platform/common/automation/SETUP.md +++ b/port/platform/common/automation/SETUP.md @@ -623,7 +623,7 @@ Similarly, some of the short-range test instances require access to a Wi-Fi Acce - Instance 23, Windows: - Requires you to configure the [Virtual Serial Port](https://www.virtual-serial-port.org/) application to create a `loopback` serial port. [DATABASE.md](DATABASE.md) will tell you which is the loopback serial port (`U_CFG_TEST_UART_A`, usually 100). - When a cellular, short-range, whatever, board is plugged into the Windows machine you will need to go into the `Device Manager` -> `Port Settings` -> `Advanced` and set the COM port number to match the one given in [DATABASE.md](DATABASE.md); e.g. `U_CFG_APP_SHORT_RANGE_UART=101` means you set the COM port for the short-range board to `COM101`. - - For a cellular EVK, which uses an FTDI chip, you will need to download and install the [FTDI Windows drivers](https://ftdichip.com/drivers/vcp-drivers/); if you use just the Windows 10 drivers you will end up with character loss, particularly noticable with the chip-to-chip tests. + - For a cellular EVK, which uses an FTDI chip, you will need to download and install the [FTDI Windows drivers](https://ftdichip.com/drivers/vcp-drivers/); if you use just the Windows 10 drivers you will end up with character loss. - This instance also runs the special test `networkOutage` which controls an external MiniCircuits Ethernet-based RF switch and KMTronic Ethernet-based relay box; you will see in [DATABASE.md](DATABASE.md) the macros `U_CFG_TEST_NET_STATUS_CELL` and `U_CFG_TEST_NET_STATUS_SHORT_RANGE` which equate to strings such as `RF_SWITCH_A` and `PWR_SWITCH_A`. The entries under `SWITCH_LIST` in the file `%homedrive%%homepath%\.ubx_automation\settings_v2_agent_specific.json` on the Windows agent map `RF_SWITCH_A`/`PWR_SWITCH_A` to actual IP addresses and an action for `0` or `1`, e.g. `:SETA=1` to switch on the RF switch, `FF0101` to switch on port 1 of the KMTronic switch, all of which are put together by the test scripts to form a URL string. You need to set up the Ethernet addresses for these correctly in the `%homedrive%%homepath%\.ubx_automation\settings_v2_agent_specific.json` file of the Windows agent. For the short-range part of this test local test peers are required for both WiFi and BLE: this is done with an appropriately [configured](https://wiki.u-blox.com/bin/view/ShortRange/NewPlatforms) NINA-W1 board, powered from a relay on `PWR_SWITCH_A` (e.g. the first relay on the KMTronic relay box) so that it can be switched off by the test script and configured to advertise a given BLE MAC address (e.g. remote central `6009C390E4DAp`) and include a Wi-Fi AP of a known SSID ( e.g. `disconnect_test_peer`, though not broadcast). - Note: if you have problems getting a COM port to appear, which does happen randomly, unplug the USB cable, go to the FTDI website and download their "CDM uninstaller" tool, get it to uninstall drivers for the relevant HW ID (e.g. `4030 6011` for the FTDI chip on the NINA-W1 EVK), then just plug the USB in again and a COM port should appear. - Instance 24 is pure Linux and needs a 32-bit compiler, which means it cannot be run on a Raspberry Pi (since GCC for ARM64 is 64-bit only); you will need to add the label `instance_24` to one of the non-Pi Linux nodes for this. diff --git a/port/ubxlib.cmake b/port/ubxlib.cmake index f02a04d1..5fba7aed 100644 --- a/port/ubxlib.cmake +++ b/port/ubxlib.cmake @@ -195,9 +195,7 @@ u_add_test_source_dir(base ${UBXLIB_BASE}/common/device/test) u_add_test_source_dir(base ${UBXLIB_BASE}/common/network/test) # Examples are compiled as tests u_add_test_source_dir(base ${UBXLIB_BASE}/example/sockets) -u_add_test_source_dir(base ${UBXLIB_BASE}/example/security/e2e) -u_add_test_source_dir(base ${UBXLIB_BASE}/example/security/psk) -u_add_test_source_dir(base ${UBXLIB_BASE}/example/security/c2c) +u_add_test_source_dir(base ${UBXLIB_BASE}/example/security) u_add_test_source_dir(base ${UBXLIB_BASE}/example/mqtt_client) u_add_test_source_dir(base ${UBXLIB_BASE}/example/http_client) u_add_test_source_dir(base ${UBXLIB_BASE}/example/location) diff --git a/port/ubxlib.mk b/port/ubxlib.mk index c6ece0a1..a685a6b5 100644 --- a/port/ubxlib.mk +++ b/port/ubxlib.mk @@ -147,9 +147,7 @@ UBXLIB_TEST_DIRS += \ # Examples are compiled as tests UBXLIB_TEST_DIRS += \ ${UBXLIB_BASE}/example/sockets \ - ${UBXLIB_BASE}/example/security/e2e \ - ${UBXLIB_BASE}/example/security/psk \ - ${UBXLIB_BASE}/example/security/c2c \ + ${UBXLIB_BASE}/example/security \ ${UBXLIB_BASE}/example/mqtt_client \ ${UBXLIB_BASE}/example/http_client \ ${UBXLIB_BASE}/example/location \