Introduction

IoT devices are becoming more common in data sensitive applications such as medical monitoring. Such devices are exposed to the following types of attack vectors: internet, LAN, and physical access. The SimpleLink™ Wi-Fi® CC3x20, CC3x35 device family offers security features which mitigate vulnerability from these attack vectors. These security features include: secure file system, secure socket, secure http server, secure content delivery, and secure manufacturing.

Vulnerabilities of IoT Devices

IoT devices are typically connected to the internet and often provide access to personal/sensitive information. Some common attack vectors include:

  • Internet Network Connectivity Vector: The vulnerabilities in this vector could come from all communication channels (ex. sockets) and protocols in use. In general, these attacks target all the resources and information that pass through these communication channels.
  • Local Network Connectivity Vector: This attack vector is similar to the internet network connectivity vector but with a much narrower geographical scope. The attackers could attempt to breach security with attacks that are based on monitoring the traffic of the wireless network. Monitoring a wireless network is fairly easy, and even in secured wireless networks some of the frame headers are not encrypted.
  • Physical Access (Operation): The operation vector refers to attacks that enable the hacker to control the operation of the device by using the interfaces that the final product offers (ex. power line, buttons, etc.).
  • Physical Access (PCB Manipulation): The manipulation vector relates to the ability of the hacker to have access to the actual board allowing an individual to monitor the lines and interfaces or even manipulate wires.

In addition to the secure sockets, SimpleLink Wi-Fi includes the following security features:

  • Secure Networking: Ensures the authenticity, reliability, and confidentiality of data flow between peers over the network
  • Wi-Fi Security: The Wi-Fi layer of the device supports security to ensure the integrity and confidentiality of L2 transaction frames between AP and station, or between two peers in Wi-Fi Direct mode
  • Secure File System: File system security for confidentiality and integrity of data (including: authentication and integratity verification, content encryption, cloning protection, failsafe update mechanism, factory image recovery)
  • Secure Content Delivery: This capability enables the exchange of confidential content to the device and provides another level of security at the application-layer
  • Crypto Utilities: The SimpleLink device exposes a set of crypto primitives to aid in some common cryptographic related operations
  • Hardware Crypto Engine: The CC32xx microcontroller includes a set of hardware crypto engines such as CRC, AES, DES and SHA/MD5, known as DTHE (Data Transformation and Hash Engine), to enhance the performance of applications that require custom application level security

Prerequisites

Software

  • Code Composer Studio v8.3 or newer
    • SimpleLink Wi-Fi CC32xx Wireless MCUs support installed
    • Make sure that CCS is using the latest updates: HelpCheck for Updates
  • CC32xx SDK v2.40.00.05 or newer
  • UniFlash v4.6.0 or newer
  • Terminal emulator program such as TeraTerm or PuTTY

Hardware

File System Overview

The second generation of SimpleLink Wi-Fi devices uses an externally attached NVM (non-volatile memory) in the form of an SFLASH (serial flash device) to store a range of sensitive information such as passkeys, device configurations, and TI/customer intellectual property. The SimpleLink solution utilizes a file system to organize data on the SFLASH which is accessible using a simple host interface.

The file system provides security for the user and system files while also securing the data structures of the file system itself. The following security functionalities are related to the CC3120, CC3135, CC3220S, CC3235S, CC3220SF and CC3235SF devices:

File System Funcionality

Encryption

  • Files can be kept encrypted on the serial flash using the AES128-CTR encryption cryptographic standard. Decryption is done automatically when reading the file.
  • Keys are generated by the device using a true random number generator hardware engine.
  • Some of the system files are also encrypted including the files that contain sensitive information such as keys.

Cloning Protection

  • Secure files can be read only by the device that created them.
  • The device enters into a lock state if the content of the serial flash has been copied to another device, or the serial flash has been assembled into a different device from the one that programmed it.

Integrity Verification

  • As part of an integrity test, the device generates an encrypted string of the file content every time it is changed by the file system. The result is kept as the file signature. Once the file is opened for read, the file content is tested against this signature. If the device detects that the content of a file was changed by unauthorized user, it triggers a system alert.
  • System files and the file system structure are also protected and their integrity is tested by the device. The integrity validation of system files is done by using the HMAC-SHA2 (256) algorithm. If the device detects content change by an unauthorized user in a system file or in a file system structure, it enters a lock state.
  • The SimpleLink device ensures system integrity during programming and downloading updates by providing:
    • Image Programming: A procedure that uses a single image file to ensure the device is not partially configured
    • Files Bundle: Used during Over-The-Air programming to allow the user to modify files and approve new content as one operation or rollback to former content

Access Control

  • Each secure file has several access levels uses to restrict access to the file operation such as delete, read, or write. Tokens are used to control the access to the secure files.
  • When a secure file is created, four different tokens (32-bit numbers) are generated. Each token provides a different access level to the file.
Token Type Description
Master Token Enables full access to the file. A secure file can be deleted only with the master token. The master token is unchanged as long as the file exists
Read/Write Token Enables read and write access. This token is generated automatically when the file has been changed
Write Only Token Enable write access only. This access level is can be used to protect private keys. This token is generated automatically when the file has changed
Read Only Token Enables read access only. This token is generated automatically when the file has changed

Origin Authenticity

  • The authentication method used for this process is a well-known methodology—PKCS#1 SHA-1 with RSA encryption (the key size is 128 or 256); creating a file signature verifies both the file integrity and the authentication of the file
  • To create a digital signature for the authentication process, the vendor must have an RSA key-pair. The certificate that holds the public key must be signed by a certificate authority
  • The entire chain of trust for this certificate is verified by the device using the certificate store
  • Writing the service-pack and the certificate store files requires a signature; the signatures for those files are supplied by TI. For any other secure signed files, no certificate is required

Recovery Mechanism

  • The SimpleLink device has embedded an internal recovery mechanism that allows the file system to be rolled back to a pre-defined image programmed in the production line
  • The recovery image is a system file kept on the serial flash. The file is protected and secure. The recovery image file can be viewed by the file list but cannot be accessed from the host
  • The process of restore to factory is fail-safe, as it resumes even if there was a power failure during the operation

System Alerts

  • The SimpleLink device provides a data tampering procedure with a security alert counter
  • This procedure detects any integrity violations of the file system data, content of secure-authenticate files, and system files. It also detects unauthorized operations, such as trying to read a secure file with an invalid token
  • When the system reaches the security alerts threshold, the device is locked. In addition, the host receives an asynchronous event regarding the lock
  • The counter-alert lock mechanism protects against brute-force attacks

Levels of File Protection

The file system is the major building block for most of the device’s security and stores the device’s sensitive information (IP, system configuration, user-files, etc.). The file system maintains the integrity and authenticity of this data, and files on the external storage can be classified as follows:

  • Non-secure Files:
    • Unrestricted read and write access to this plain text file type
    • No validation is performed
  • Secure Files:
    • File content is stored as AES128 encrypted text and the encryption key is changed on every write
    • Access control where the file can only be accessed with a valid token
    • Cloning protection since a file cannot be copied to other devices
  • Secure-n-authenticated Files:
    • Contains all security properties of secure files
    • The authenticity of the writer of the secure-n-authenticated file is validated on every write cycle
    • The authenticity uses digital signature of the file content, the file is signed using an RSA private key, and supports X.509 certificate chain
    • On every open for read operation the file content is verified, by calculating the file SHA and comparing it to a signature which was created and kept during the write-cycle
    • The device maintains a root certificate on ROM, and the chain-of-trust must be rooted to the device’s ROM either:
      • By using a chain with TI’s ROM certificate
      • Or, by using a chain whose root certificate is part of the device’s "Certificate Store" file. The "Certificate Store" file is stored on the file system and is signed by the ROM certificate

Secure File System API

The 4 primary API calls for read, write, open, and close used in the secure file system demos are explored below:

  • sl_FsOpen:
    _i32 sl_FsOpen(const _u8 *pFileName,const _u32 AccessModeAndMaxSize,_u32 *pToken);

    • Function Parameters:
      • pFileName: File name ,pointer to a null-terminated string contains file name
      • pToken: Uses as input token in case of open-read and in case of open-write with vendor flag, otherwise it is output token
      • AccessModeAndMaxSize:
        • SL_FS_READ: Read a file
        • SL_FS_WRITE: Open for write for an existing file
        • SL_FS_OVERWRITE: In case the file do exists open it for write else create the file and open for write
        • SL_FS_CREATE: Create a new file and open for write
    • Return Value:
      • On success, file handle is returned. On error, negative error code is returned
  • sl_FsRead:
    _i32 sl_FsRead(const _i32 FileHdl,_u32 Offset ,_u8* pData,_u32 Len);

    • Function Parameters:
      • FileHdl: Handle to the file (assigned from sl_FsOpen())
      • Offset: Offset to specific location to be read/write
      • pData: Pointer the receive/transmit data to the storage device
      • Len: Length to read/write
    • Return Value:
      • On success, returns the number of read bytes. On error, negative number is returned
  • sl_FsWrite:
    _i32 sl_FsWrite(const _i32 FileHdl,_u32 Offset,_u8* pData,_u32 Len);

    • Function Parameters:
      • FileHdl: Handle to the file (assigned from sl_FsOpen())
      • Offset: Offset to specific location to be read/write
      • pData: Pointer the receive/transmit data to the storage device
      • Len: Length to read/write
    • Return Value:
      • On success, returns the number of written bytes. On error, negative error code is returned
  • sl_FsClose:
    _i32 sl_FsClose(const _i32 FileHdl,const _u8* pCeritificateFileName,const _u8* pSignature,const _u32 SignatureLen);

    • Function Parameters:
      • FileHdl: Handle to the file (assigned from sl_FsOpen)
      • pCeritificateFileName: Certificate file, or NULL if irrelevant
      • pSignature: The signature is SHA-1, the certificate chain may include SHA-256
      • SignatureLen: The actual signature length
    • Return Value:
      • Zero on success, or a negative value if an error occurred
    • Notes:
      • Close file in storage device (authentication is performed if needed)
      • In case the file was opened as not secure file or as secure-not signed, any certificate or signature provided are ignored, those fields should be set to NULL

Task 1: Preparing the Environment

The following section will discuss the steps needed to experience the file system on the CC32xx LaunchPad. This example is based on the Portable example found within the CC32xx SDK. We will enable Wi-Fi using the SimpleLink Wi-Fi APIs and the file system capabilities on top of the existing example.

Modifying the Portable Example

  1. In CCS, open the TI Resource Explorer (View → Resource Explorer)
  2. Expand the folders as shown to select the Portable example, then click the Import to IDE icon at the top-right

    • Be sure you select your desired project "flavor" (CC3235S-LAUNCHXL, CC3220SF-LAUNCHXL, TI-RTOS, Free-RTOS, CCS, GCC, etc.).
    • We will be using the TI-RTOS CCS example for this lab: portable_CC3220SF_LAUNCHXL_tirtos_ccs


  3. We will be using the Portable example as a base project, but we will remove the original temperature and console threads and replace them with a file-system implementation. We first have to edit the main_tirtos.c file. Select the code below and use to replace the entire existing main_tirtos.c code.

     /*
      * Copyright (c) 2016, Texas Instruments Incorporated
      * All rights reserved.
      *
      * Redistribution and use in source and binary forms, with or without
      * modification, are permitted provided that the following conditions
      * are met:
      *
      * *  Redistributions of source code must retain the above copyright
      *    notice, this list of conditions and the following disclaimer.
      *
      * *  Redistributions in binary form must reproduce the above copyright
      *    notice, this list of conditions and the following disclaimer in the
      *    documentation and/or other materials provided with the distribution.
      *
      * *  Neither the name of Texas Instruments Incorporated nor the names of
      *    its contributors may be used to endorse or promote products derived
      *    from this software without specific prior written permission.
      *
      * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
      * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
      * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    
      * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
      * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      */
    
     /*
      *  ======== main_tirtos.c ========
      */
     #include <stdint.h>
    
     /* POSIX Header files */
     #include <pthread.h>
    
     /* RTOS header files */
     #include <ti/sysbios/BIOS.h>
    
     /* Driver header files */
     #include <ti/drivers/GPIO.h>
    
     /* Example/Board Header files */
     #include "Board.h"
    
     extern void *secureFSExampleThread(void *arg0);
    
     /* Stack size in bytes */
     #define THREADSTACKSIZE    2048
    
     /*
      *  ======== main ========
      */
     int main(void)
     {
         pthread_t           thread;
         pthread_attr_t      pAttrs;
         struct sched_param  priParam;
         int                 retc;
         int                 detachState;
    
         /* Call driver init functions */
         Board_initGeneral();
    
         /* Set priority and stack size attributes */
         pthread_attr_init(&pAttrs);
         priParam.sched_priority = 1;
    
         detachState = PTHREAD_CREATE_DETACHED;
         retc = pthread_attr_setdetachstate(&pAttrs, detachState);
         if (retc != 0) {
             /* pthread_attr_setdetachstate() failed */
             while (1);
         }
    
         pthread_attr_setschedparam(&pAttrs, &priParam);
    
         retc |= pthread_attr_setstacksize(&pAttrs, THREADSTACKSIZE);
         if (retc != 0) {
             /* pthread_attr_setstacksize() failed */
             while (1);
         }
    
         retc = pthread_create(&thread, &pAttrs, secureFSExampleThread, NULL);
         if (retc != 0) {
             /* pthread_create() failed */
             while (1);
         }
    
         /* Start the TI-RTOS scheduler */
         BIOS_start();
    
         return (0);
     }
    

    main_tirtos.c

  4. You may exclude the temperature.c and console.c from the projet compilation.

  5. Since we are going to use the terminal in this example, we will add the common UART services. From another Wi-Fi application, copy uart_term.c and uart_term.h and add the files into your project. You can find these files in the Network Terminal example: simplelink_cc32xx_sdk_x_xx_xx_xx/examples/rtos/CC3220SF_LAUNCHXL/demos/network_terminal/. This will enable us to initialize the UART to a standard configuration (baudrate of 115200) and use a formatted output (Report()).

  6. Create a new file titled secure_fs.c in your project with the following code. This will implement the new file-system example thread.

     /*
      *   Copyright (C) 2016 Texas Instruments Incorporated
      *
      *   All rights reserved. Property of Texas Instruments Incorporated.
      *   Restricted rights to use, duplicate or disclose this code are
      *   granted through contract.
      *
      *   The program may not be used without the written permission of
      *   Texas Instruments Incorporated or against the terms and conditions
      *   stipulated in the agreement under which this program has been supplied,
      *   and under no circumstances can it be used with non-TI connectivity device.
      *
      */
    
     #include <stdint.h>
     #include <stddef.h>
     #include <unistd.h>
     #include <stdarg.h>
     #include <stdlib.h>
     #include <stdio.h>
     #include <string.h>
     #include <ti/drivers/net/wifi/simplelink.h>
     #include <pthread.h>
     #include "Board.h"
     #include "uart_term.h"
    
     /* Put callbacks and helper functions here*/
    
     /* This is the example thread */
     void *secureFSExampleThread(void *arg0)
     {
         /* Put wifi_init and sl_Start here */
    
         /* Put Task 3 code here */
    
         /* This is the empty while loop. This should always be at the end of the function. */
         while (1);
     }
    

    secure_fs.c

  7. The modified project can be built (although it will do nothing if run at this stage). The modified project content is shown below:

In order to use the flash file system, we first need to enable the network processor.

  1. Add the SimpleLink Wi-Fi host driver library simplelink.a to the project.

    • Right-click on the Portable project name and select Properties -> Build -> ARM Linker -> File Search Path
    • Click the document icon with the green plus next to "Include library file..."

    • Browse to your SimpleLink CC32xx SDK installation, then source/ti/drivers/net/wifi/ccs/rtos/ and select simplelink.a. Select OK.
  2. The library requires that all the SimpleLink driver callbacks must be implemented (although they are not all used by this example). Add the following code to secure_fs.c:

     void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
     {
         UART_PRINT("pWlanEvent->Id=%d\n\r", pWlanEvent->Id);
     }
    
     void SimpleLinkFatalErrorEventHandler(SlDeviceFatal_t *slFatalErrorEvent)
     {
         UART_PRINT("slFatalErrorEvent->Id=%d\n\r", slFatalErrorEvent->Id);
     }
    
     void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent)
     {
          UART_PRINT("pDevEvent->Id=%d\n\r", pDevEvent->Id);
     }
    
     void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent)
     {
         /* Unused in this application */
     }
    
     void SimpleLinkSockEventHandler(SlSockEvent_t *pSock)
     {
         /* Unused in this application */
     }
    
     void SimpleLinkHttpServerEventHandler(SlNetAppHttpServerEvent_t *pHttpEvent,
                                           SlNetAppHttpServerResponse_t *pHttpResponse)
     {
         /* Unused in this application */
     }
    
     void SimpleLinkNetAppRequestMemFreeEventHandler (uint8_t *buffer)
     {
         /* Unused in this application */
     }
    
     void SimpleLinkNetAppRequestEventHandler (SlNetAppRequest_t *pNetAppRequest, 
                                                 SlNetAppResponse_t *pNetAppResponse)
     {
         /* Unused in this application */
     }
    

    secure_fs.c

  3. The following code is used to enable the SPI interface and trigger the Wi-Fi driver thread. Copy and paste this code into secure_fs.c above the thread function:

     #define SPAWN_TASK_PRIORITY     (9)
     #define TASK_STACK_SIZE         (2048)
    
     int wifi_init()
     {
         int RetVal;
         pthread_attr_t      pAttrs_spawn;
         pthread_t g_spawn_thread = (pthread_t)NULL;
         struct sched_param  priParam;
    
         GPIO_init();
         SPI_init();
    
         //create the sl_Task
         pthread_attr_init(&pAttrs_spawn);
         priParam.sched_priority = SPAWN_TASK_PRIORITY;
         RetVal = pthread_attr_setschedparam(&pAttrs_spawn, &priParam);
         RetVal |= pthread_attr_setstacksize(&pAttrs_spawn, TASK_STACK_SIZE);
    
         if(RetVal == 0)
             RetVal = pthread_create(&g_spawn_thread, &pAttrs_spawn, sl_Task, NULL);
    
         return RetVal;
     }
    

    secure_fs.c

  4. Call thewifi_init() function from the application main thread secureFSExampleThread() that we added earlier to secure_fs.c. Then we will call sl_Start() to start the network processor. Copy and paste the following code inside secureFSExampleThread() before the while loop:

     int RetVal;
    
     /* Terminal Initialization */
     InitTerm();
    
     /* Wifi Enabling */
     RetVal = wifi_init();
     if(RetVal < 0)
     {
         UART_PRINT("wifi_init error:  %d\n\r", RetVal);
         return NULL;
     }
    
     /* Powerup the CC3x20 Network Processor */ 
     RetVal = sl_Start(0, 0, 0);
     if(RetVal < 0)
     {
         UART_PRINT("sl_Start error:  %d\n\r", RetVal);
         return NULL;
     }
     // Device is awake (the exact role is not relevant for this example)
     // and ready for processing FS commands
    

    secure_fs.c :: secureFSExampleThread()

Task 2: Retrieving file system information

  1. Enter the following code into secure_fs.c to present the storage information and list of files in a readable format.

     static _i32 st_ShowStorageInfo()
     {
         _i32        RetVal = 0;
         _i32        size;
         _i32        used;
         _i32        avail;
         SlFsControlGetStorageInfoResponse_t storageInfo;
    
         UART_PRINT("\n\rGet Storage Info:\n\r");
    
         RetVal = sl_FsCtl(( SlFsCtl_e)SL_FS_CTL_GET_STORAGE_INFO,
                        0,
                        NULL ,
                        NULL ,
                        0,
                        (_u8 *)&storageInfo,
                        sizeof(SlFsControlGetStorageInfoResponse_t),
                        NULL );
    
         if(RetVal < 0)
         {
             UART_PRINT("sl_FsCtl error: %d\n\r");
         }
    
         size = (storageInfo.DeviceUsage.DeviceBlocksCapacity *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
         UART_PRINT("Total space: %dK\n\r\n\r", size);
    
         UART_PRINT("Filestsyem      Size \tUsed \tAvail\t\n\r");
    
         size = ((storageInfo.DeviceUsage.NumOfAvailableBlocksForUserFiles +
                 storageInfo.DeviceUsage.NumOfAllocatedBlocks) *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
    
         used = (storageInfo.DeviceUsage.NumOfAllocatedBlocks *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
    
         avail = (storageInfo.DeviceUsage.NumOfAvailableBlocksForUserFiles *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
    
         UART_PRINT("%-15s %dK \t%dK \t%dK \t\n\r", "User", size, used, avail);
    
         size = (storageInfo.DeviceUsage.NumOfReservedBlocksForSystemfiles *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
    
         UART_PRINT("%-15s %dK \n\r", "System", size);
    
         size = (storageInfo.DeviceUsage.NumOfReservedBlocks *
                 storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
    
         UART_PRINT("%-15s %dK \n\r", "Reserved", size);
    
         UART_PRINT("\n\r");
         UART_PRINT("\n\r");
    
         UART_PRINT("%-32s: %d \n\r", "Max number of files",
                    storageInfo.FilesUsage.MaxFsFiles);
    
         UART_PRINT("%-32s: %d \n\r", "Max number of system files",
                    storageInfo.FilesUsage.MaxFsFilesReservedForSysFiles);
    
         UART_PRINT("%-32s: %d \n\r", "Number of user files",
                    storageInfo.FilesUsage.ActualNumOfUserFiles);
    
         UART_PRINT("%-32s: %d \n\r", "Number of system files",
                    storageInfo.FilesUsage.ActualNumOfSysFiles);
    
         UART_PRINT("%-32s: %d \n\r", "Number of alert",
                    storageInfo.FilesUsage.NumOfAlerts);
    
         UART_PRINT("%-32s: %d \n\r", "Number Alert threshold",
                    storageInfo.FilesUsage.NumOfAlertsThreshold);
    
         UART_PRINT("%-32s: %d \n\r", "FAT write counter",
                    storageInfo.FilesUsage.FATWriteCounter);
    
         UART_PRINT("%-32s: ", "Bundle state");
    
         if(storageInfo.FilesUsage.Bundlestate == SL_FS_BUNDLE_STATE_STOPPED)
         {
             UART_PRINT("%s \n\r", "Stopped");
         }
         else if(storageInfo.FilesUsage.Bundlestate == SL_FS_BUNDLE_STATE_STARTED)
         {
             UART_PRINT("%s \n\r", "Started");
         }
         else if(storageInfo.FilesUsage.Bundlestate ==
                 SL_FS_BUNDLE_STATE_PENDING_COMMIT)
         {
             UART_PRINT("%s \n\r", "Commit pending");
         }
    
         UART_PRINT("\n\r");
    
         return RetVal;
     }
    
     #define MAX_FILE_ENTRIES 4
    
     typedef struct
     {
         SlFileAttributes_t attribute;
         char fileName[SL_FS_MAX_FILE_NAME_LENGTH];
     } slGetfileList_t;
    
     static _i32 st_listFiles(_i32 numOfFiles, int bPrintDescription)
     {
         int retVal = SL_ERROR_BSD_ENOMEM;
         _i32            index = -1;
         _i32 fileCount = 0;
         slGetfileList_t *buffer = malloc(MAX_FILE_ENTRIES * sizeof(slGetfileList_t));
    
         UART_PRINT("\n\rRead files list:\n\r");
         if(buffer)
         {
             while( numOfFiles > 0 )
             {
                 _i32 i;
                 _i32 numOfEntries = (numOfFiles < MAX_FILE_ENTRIES) ? numOfFiles : MAX_FILE_ENTRIES;
    
                 // Get FS list
                 retVal = sl_FsGetFileList(&index,
                                           numOfEntries,
                                           sizeof(slGetfileList_t),
                                           (_u8*)buffer,
                                           SL_FS_GET_FILE_ATTRIBUTES);
                 if(retVal < 0)
                 {
                     UART_PRINT("sl_FsGetFileList error:  %d\n\r", retVal);
                     break;
                 }
                 if(retVal == 0)
                 {
                     break;
                 }
    
                 // Print single column format
                 for (i = 0; i < retVal; i++)
                 {
                     UART_PRINT("[%3d] ", ++fileCount);
                     UART_PRINT("%-40s\t", buffer[i].fileName);
                     UART_PRINT("%8d\t", buffer[i].attribute.FileMaxSize);
                     UART_PRINT("0x%03x\t", buffer[i].attribute.Properties);
                     UART_PRINT("\n\r");
                 }
                 numOfFiles -= retVal;
             }
             UART_PRINT("\n\r");
    
             if(bPrintDescription)
             {
                 UART_PRINT(" File properties flags description:\n\r");
                 UART_PRINT(" 0x001 - Open file commit\n\r");
                 UART_PRINT(" 0x002 - Open bundle commit\n\r");
                 UART_PRINT(" 0x004 - Pending file commit\n\r");
                 UART_PRINT(" 0x008 - Pending bundle commit\n\r");
                 UART_PRINT(" 0x010 - Secure file\n\r");
                 UART_PRINT(" 0x020 - No file safe\n\r");
                 UART_PRINT(" 0x040 - System file\n\r");
                 UART_PRINT(" 0x080 - System with user access\n\r");
                 UART_PRINT(" 0x100 - No valid copy\n\r");
                 UART_PRINT(" 0x200 - Public write\n\r");
                 UART_PRINT(" 0x400 - Public read\n\r");
                 UART_PRINT("\n\r");
             }
             free (buffer);
         }
    
         return retVal;
     }
    

    secure_fs.c

  2. Add the function calls to the example main thread (after wifi_init() and sl_Start()):

     st_ShowStorageInfo();
     st_listFiles(40, 1);
    

    secure_fs.c :: secureFSExampleThread()

  3. Open a terminal emulation program, select the UART serial port, and set the baud rate to 115200. Please see the Wi-Fi Fundamentals lab if you are not familiar with how to set up your terminal.

  4. Build, load, and run the example code. The following shows an example terminal output.

Task 3: Demonstrating the Fail-Safe (Recovery) mechanism

The next task creates an unsecure fail-safe file and then tries to override it. The example will first demonstrate a rollback (usually due to an error in the file validation) and then show a succesfull commit.

  1. First, we will add simple "write file" and "read file" helper functions. These methods will handle fragmentation during reading from (or writing to) the network processor. Copy the following code into secure_fs.c.

     static int st_writeFile( _i32 fileHandle, _u32 length, _const char *buffer)
     {
         int RetVal;
         _i32 offset = 0;
    
         while(offset < length)
         {
             // write data to open file
             RetVal = sl_FsWrite(fileHandle,
                              offset,
                              (_u8 *)buffer,
                              length);
             if (RetVal <= 0)
             {
                 UART_PRINT("sl_FsWrite error:  %d\n\r" ,RetVal);
                 return RetVal;
             }
             else
             {
                 offset += RetVal;
                 length -= RetVal;
             }
    
         }
         UART_PRINT("Wrote %d bytes...\n\r", offset);
         return offset;
     }
    
     static int st_readFile( _i32 fileHandle, _u32 length)
     {
         int offset = 0;
         int RetVal = 0;
         char buff[100];
    
         while(offset < length)
         {
             RetVal = sl_FsRead(fileHandle, offset, (_u8*)buff, length);
    
             if(RetVal == SL_ERROR_FS_OFFSET_OUT_OF_RANGE)
             {// EOF
                 break;
             }
             if(RetVal < 0)
             {// Error
                 UART_PRINT("sl_FsRead error: %d\n\r", RetVal);
                 return RetVal;
             }
    
             offset += RetVal;
             length -= RetVal;
         }
         UART_PRINT("Read \"%s\" (%d bytes)...\n\r", buff, offset);
         return offset;
     }
    

    secure_fs.c

  2. We will add content for the new file and the content that will overide it.

     #define MAX_FILE_SIZE        100
     #define TEST_FILENAME        "NewFile"
     #define TEST_CERTIFICATE    "dummy-trusted-cert"
    
     _const char originalContent[] = "This is the original content!";
     _const char secondaryContent[] = "Overridden by the secondary content...";
    

    secure_fs.c

    Code for Task 3

    All code in the rest of this task should be copied-and-pasted in the given order inside secureFSExampleThread() before the while(1) loop.

  3. Now, let's create an unsecured file with the fail-safe option in the secureFSExampleThread().

     // Creating unsecured, MAX_SIZE file (this is the maximum length allowed) 
     _i32 fd = sl_FsOpen(TEST_FILENAME,
                         (SL_FS_CREATE | SL_FS_CREATE_FAILSAFE | SL_FS_CREATE_MAX_SIZE(MAX_FILE_SIZE)),
                         0);
     if(fd < 0)
     {
         UART_PRINT("sl_FsOpen error: %d\n\r", fd);
     }
     else
     {
         st_writeFile(fd, strlen(originalContent), originalContent);
         RetVal = sl_FsClose(fd, 0, 0, 0);
         if(fd < 0)
         {
             UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
         }
     }
    

    secure_fs.c :: secureFSExampleThread()

  4. Then we can add some code to verify the successful file creation within the example thread.

         // Check that the new file exists (make sure the "0x020 - No File Safe" property is disabled for the new file) 
         // Note that the size is rounded to the nearest block size
         st_listFiles(40,0);
    
         // Read the file content
         UART_PRINT("Reading file after CREATION:\n\r");
         fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, 0);
         if(fd < 0)
         {
             UART_PRINT("sl_FsOpen error: %d\n\r", fd);
         }
         else
         {
             st_readFile(fd, MAX_FILE_SIZE);
             RetVal = sl_FsClose(fd, 0, 0, 0);
             if(fd < 0)
             {
                 UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
             }
         }
    

    secure_fs :: secureFSExampleThread()

    Terminal output for the creation of a new file

    Replacing the file content

    During OTA or in normal operation, a developer may want to replace the content of a file. The file-safe option (used in the previous task) provides a mechanism that allows you to revert to the original file in case the new content is damaged. The new content is kept as a candidate while the old copy is still in flash. After the new file has been tested, it can be committed (i.e. the old content will be deleted) or it can be rolled back so the old copy becomes the valid one. This feature can be enabled for a specific file or to a group of files (each should be created with the SL_FS_WRITE_BUNDLE_FILE). In this case, the commit or rollback will affect the entire bundle.

  5. Assuming the NewFile is already in flash, we will try to override it. Note that the SL_FS_WRITE_MUST_COMMIT should be added to the File Open flags.

     UART_PRINT("\n\r\n\r ** Replacing file content\n\r");
     fd = sl_FsOpen(TEST_FILENAME, SL_FS_WRITE | SL_FS_WRITE_MUST_COMMIT, 0);
     if(fd < 0)
     {
         UART_PRINT("sl_FsOpen (re-write) error: %d\n\r", fd);
     }
     else
     {
         st_writeFile(fd, strlen(secondaryContent), secondaryContent);
         RetVal = sl_FsClose(fd, 0, 0, 0);
         if(RetVal < 0)
         {
             UART_PRINT("sl_FsClose (re-write) error: %d\n\r", RetVal);
         }
     }
    

    secure_fs.c :: secureFSExampleThread()

  6. In order to verify that the file was re-written, we will read its content again by reusing the code from step 4.

    // Read the file content
    UART_PRINT("Reading File before ROLLBACK:\n\r");
    fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, 0);
    if(fd < 0)
    {
        UART_PRINT("sl_FsOpen error: %d\n\r", fd);
    }
    else
    {
        st_readFile(fd, MAX_FILE_SIZE);
        RetVal = sl_FsClose(fd, 0, 0, 0);
        if(fd < 0)
        {
            UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
        }
    }
    

    secure_fs.c :: secureFSExampleThread()

  7. After the file is re-written, we can run some sanity tests and act accordingly. We'll first follow a failure case which will trigger a rollback command. Note that in case the MCU image is replaced, it will rollback automatically upon the next MCU reset if COMMIT is not used.

     SlFsControl_t   FsControl;
     RetVal = sl_FsCtl(SL_FS_CTL_ROLLBACK, 0, TEST_FILENAME, (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 0);
    

    secure_fs.c :: secureFSExampleThread()

  8. If we check the file again (repeating step 4), the original content should appear.

    // Read the file content
    UART_PRINT("Reading File after ROLLBACK:\n\r");
    fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, 0);
    if(fd < 0)
    {
       UART_PRINT("sl_FsOpen error: %d\n\r", fd);
    }
    else
    {
       st_readFile(fd, MAX_FILE_SIZE);
       RetVal = sl_FsClose(fd, 0, 0, 0);
       if(fd < 0)
       {
           UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
       }
    }
    

    secure_fs.c :: secureFSExampleThread()

  9. To test a successful file replacement, repeat steps 5-7, but use SL_FS_CTL_COMMIT in step 7 (instead of the SL_FS_CTL_ROLLBACK).

     UART_PRINT("\n\r\n\r ** Replacing file content (again)\n\r");
     fd = sl_FsOpen(TEST_FILENAME, SL_FS_WRITE | SL_FS_WRITE_MUST_COMMIT, 0);
     if(fd < 0)
     {
         UART_PRINT("sl_FsOpen (re-write) error: %d\n\r", fd);
     }
     else
     {
         st_writeFile(fd, strlen(secondaryContent), secondaryContent);
         RetVal = sl_FsClose(fd, 0, 0, 0);
         if(RetVal < 0)
         {
             UART_PRINT("sl_FsClose (re-write) error: %d\n\r", RetVal);
         }
     }
    
     // Read the file content
     UART_PRINT("Reading File before COMMIT:\n\r");
     fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, 0);
     if(fd < 0)
     {
         UART_PRINT("sl_FsOpen error: %d\n\r", fd);
     }
     else
     {
         st_readFile(fd, MAX_FILE_SIZE);
         RetVal = sl_FsClose(fd, 0, 0, 0);
         if(fd < 0)
         {
             UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
         }
     }
    
     RetVal = sl_FsCtl(SL_FS_CTL_COMMIT, 0, TEST_FILENAME, (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 0);
    

    secure_fs.c :: secureFSExampleThread()

  10. If we check the file again (repeating step 4), now the secondary content should appear.

    // Read the file content
    UART_PRINT("Reading file after COMMIT:\n\r");
    fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, 0);
    if(fd < 0)
    {
        UART_PRINT("sl_FsOpen error: %d\n\r", fd);
    }
    else
    {
        st_readFile(fd, MAX_FILE_SIZE);
        RetVal = sl_FsClose(fd, 0, 0, 0);
        if(fd < 0)
        {
            UART_PRINT("sl_FsClose error: %d\n\r", RetVal);
        }
    }
    

    secure_fs.c :: secureFSExampleThread()

  11. To complete this task, we will delete the file from the file system and verify.

    UART_PRINT("\n\r\n\r ** Deleting the file\n\r");
    RetVal = sl_FsDel(TEST_FILENAME, 0);
    st_listFiles(40,0);
    UART_PRINT("Example complete\n\r");
    

    secure_fs.c :: secureFSExampleThread()

  12. Build and run the example. Verify the results using the debug terminal.

Note that the following code example uses some additional helper methods to simplify the implementation.

    /*
    *   Copyright (C) 2016 Texas Instruments Incorporated
    *
    *   All rights reserved. Property of Texas Instruments Incorporated.
    *   Restricted rights to use, duplicate or disclose this code are
    *   granted through contract.
    *
    *   The program may not be used without the written permission of
    *   Texas Instruments Incorporated or against the terms and conditions
    *   stipulated in the agreement under which this program has been supplied,
    *   and under no circumstances can it be used with non-TI connectivity device.
    *
    */

    #include <stdint.h>
    #include <stddef.h>
    #include <unistd.h>
    #include <stdarg.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <ti/drivers/net/wifi/simplelink.h>
    #include <pthread.h>

    #include "Board.h"
    #include "uart_term.h"

    #define SPAWN_TASK_PRIORITY     (9)
    #define TASK_STACK_SIZE         (2048)

    #define MAX_FILE_SIZE 100
    #define TEST_FILENAME "NewFile"
    #define TEST_CERTIFICATE "dummy-trusted-cert"
    #define MAX_FILE_ENTRIES 4

    typedef struct
    {
        SlFileAttributes_t attribute;
        char fileName[SL_FS_MAX_FILE_NAME_LENGTH];
    } slGetfileList_t;

    _const char originalContent[] = "This is the original content!";
    _const char secondaryContent[] = "Overridden by the secondary content...";

    /************************************************************************
    * Static Functions
    *************************************************************************/
    void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
    {
        UART_PRINT("[WLAN EVENT] pWlanEvent->Id=%d\n\r", pWlanEvent->Id);
    }

    void SimpleLinkFatalErrorEventHandler(SlDeviceFatal_t *slFatalErrorEvent)
    {
        UART_PRINT("[FATAL ERROR EVENT] slFatalErrorEvent->Id=%d\n\r", slFatalErrorEvent->Id);
    }

    void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent)
    {
        SlNetAppEventData_u *pNetAppEventData = NULL;

        if(NULL == pNetAppEvent) return;

        pNetAppEventData = &pNetAppEvent->Data;

        switch(pNetAppEvent->Id)
        {
            case SL_NETAPP_EVENT_IPV4_ACQUIRED:
            {
                UART_PRINT("[NETAPP EVENT] IPv4 acquired: IP = %d.%d.%d.%d\n\r",
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Ip,3),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Ip,2),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Ip,1),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Ip,0));
                UART_PRINT("               Gateway = %d.%d.%d.%d\n\r",
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Gateway,3),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Gateway,2),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Gateway,1),
                (uint8_t)SL_IPV4_BYTE(pNetAppEventData->IpAcquiredV4.Gateway,0));

            }
            break;

            case SL_NETAPP_EVENT_IPV4_LOST:
            case SL_NETAPP_EVENT_DHCP_IPV4_ACQUIRE_TIMEOUT:
            {
                UART_PRINT("[NETAPP EVENT] IPv4 lost Id or timeout, Id [0x%x]!!!\n\r", pNetAppEvent->Id);
                //SignalEvent(APP_EVENT_DISCONNECT); /* use existing disconnect event, no need for another event */
            }
            break;

            case SL_NETAPP_EVENT_IPV6_ACQUIRED:
            UART_PRINT("[NETAPP EVENT] IPv6 acquired by the device\n\r");
            break;

            case SL_NETAPP_EVENT_DHCPV4_LEASED:
            UART_PRINT("[NETAPP EVENT] IP Leased to Client\n\r");
            break;

            case SL_NETAPP_EVENT_DHCPV4_RELEASED:
            UART_PRINT("[NETAPP EVENT] IP Released to Client\n\r");
            break;

            default:
            {
                UART_PRINT("[NETAPP EVENT] Unexpected event with Id [0x%x] \n\r", pNetAppEvent->Id);
            }
            break;
        }
    }

    void SimpleLinkHttpServerEventHandler(SlNetAppHttpServerEvent_t *pHttpEvent,
    SlNetAppHttpServerResponse_t *pHttpResponse)
    {
        UART_PRINT("[HTTP SERVER EVENT] Unexpected HTTP server event \n\r");
    }

    void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent)
    {
        UART_PRINT("[GENERAL EVENT] pDevEvent->Id=%d\n\r", pDevEvent->Id);
    }

    void SimpleLinkSockEventHandler(SlSockEvent_t *pSock)
    {
        UART_PRINT("[SOCKET EVENT] pSock->Event=%d\n\r", pSock->Event);
    }

    void SimpleLinkNetAppRequestMemFreeEventHandler (uint8_t *buffer)
    {
        /* Unused in this application */
    }

    void SimpleLinkNetAppRequestEventHandler (SlNetAppRequest_t *pNetAppRequest, SlNetAppResponse_t *pNetAppResponse)
    {
        /* Unused in this application */
    }


    /* st_wifiInit -
    * enables the inetrface to the CC3X20 Network Processor
    */
    int st_wifiInit()
    {
        int RetVal;
        pthread_attr_t      pAttrs_spawn;
        pthread_t g_spawn_thread = (pthread_t)NULL;
        struct sched_param  priParam;

        GPIO_init();
        SPI_init();

        //create the sl_Task
        pthread_attr_init(&pAttrs_spawn);
        priParam.sched_priority = SPAWN_TASK_PRIORITY;
        RetVal = pthread_attr_setschedparam(&pAttrs_spawn, &priParam);
        RetVal |= pthread_attr_setstacksize(&pAttrs_spawn, TASK_STACK_SIZE);

        if(RetVal == 0)
        RetVal = pthread_create(&g_spawn_thread, &pAttrs_spawn, sl_Task, NULL);

        return RetVal;
    }

    /* st_showStorageInfo -
    * Retrieves and displays the storage information
    */
    static _i32 st_showStorageInfo()
    {
        _i32        RetVal = 0;
        _i32        size;
        _i32        used;
        _i32        avail;
        SlFsControlGetStorageInfoResponse_t storageInfo;

        UART_PRINT("\n\rGet Storage Info:\n\r");

        RetVal = sl_FsCtl(( SlFsCtl_e)SL_FS_CTL_GET_STORAGE_INFO,
        0,
        NULL ,
        NULL ,
        0,
        (_u8 *)&storageInfo,
        sizeof(SlFsControlGetStorageInfoResponse_t),
        NULL );

        if(RetVal < 0)
        {
            UART_PRINT("sl_FsCtl error: %d\n\r");
        }

        size = (storageInfo.DeviceUsage.DeviceBlocksCapacity *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;
        UART_PRINT("Total space: %dK\n\r\n\r", size);

        UART_PRINT("Filestsyem      Size \tUsed \tAvail\t\n\r");

        size = ((storageInfo.DeviceUsage.NumOfAvailableBlocksForUserFiles +
        storageInfo.DeviceUsage.NumOfAllocatedBlocks) *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;

        used = (storageInfo.DeviceUsage.NumOfAllocatedBlocks *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;

        avail = (storageInfo.DeviceUsage.NumOfAvailableBlocksForUserFiles *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;

        UART_PRINT("%-15s %dK \t%dK \t%dK \t\n\r", "User", size, used, avail);

        size = (storageInfo.DeviceUsage.NumOfReservedBlocksForSystemfiles *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;

        UART_PRINT("%-15s %dK \n\r", "System", size);

        size = (storageInfo.DeviceUsage.NumOfReservedBlocks *
        storageInfo.DeviceUsage.DeviceBlockSize) / 1024;

        UART_PRINT("%-15s %dK \n\r", "Reserved", size);

        UART_PRINT("\n\r");
        UART_PRINT("\n\r");

        UART_PRINT("%-32s: %d \n\r", "Max number of files",
        storageInfo.FilesUsage.MaxFsFiles);

        UART_PRINT("%-32s: %d \n\r", "Max number of system files",
        storageInfo.FilesUsage.MaxFsFilesReservedForSysFiles);

        UART_PRINT("%-32s: %d \n\r", "Number of user files",
        storageInfo.FilesUsage.ActualNumOfUserFiles);

        UART_PRINT("%-32s: %d \n\r", "Number of system files",
        storageInfo.FilesUsage.ActualNumOfSysFiles);

        UART_PRINT("%-32s: %d \n\r", "Number of alert",
        storageInfo.FilesUsage.NumOfAlerts);

        UART_PRINT("%-32s: %d \n\r", "Number Alert threshold",
        storageInfo.FilesUsage.NumOfAlertsThreshold);

        UART_PRINT("%-32s: %d \n\r", "FAT write counter",
        storageInfo.FilesUsage.FATWriteCounter);

        UART_PRINT("%-32s: ", "Bundle state");

        if(storageInfo.FilesUsage.Bundlestate == SL_FS_BUNDLE_STATE_STOPPED)
        {
            UART_PRINT("%s \n\r", "Stopped");
        }
        else if(storageInfo.FilesUsage.Bundlestate == SL_FS_BUNDLE_STATE_STARTED)
        {
            UART_PRINT("%s \n\r", "Started");
        }
        else if(storageInfo.FilesUsage.Bundlestate ==
        SL_FS_BUNDLE_STATE_PENDING_COMMIT)
        {
            UART_PRINT("%s \n\r", "Commit pending");
        }

        UART_PRINT("\n\r");

        return RetVal;
    }

    /* st_listFiles -
    * Retrieves and displays the list of files in the flash
    */
    static _i32 st_listFiles(int bShowDescription)
    {
        int retVal = SL_ERROR_BSD_ENOMEM;
        _i32            index = -1;
        _i32 fileCount = 0;
        _i32 numOfFiles = 255;
        slGetfileList_t *buffer = malloc(MAX_FILE_ENTRIES * sizeof(slGetfileList_t));

        UART_PRINT("\n\rRead files list:\n\r");
        if(buffer)
        {
            while( numOfFiles > 0 )
            {
                _i32 i;
                _i32 numOfEntries = (numOfFiles < MAX_FILE_ENTRIES) ? numOfFiles : MAX_FILE_ENTRIES;

                // Get FS list
                retVal = sl_FsGetFileList(&index,
                numOfEntries,
                sizeof(slGetfileList_t),
                (_u8*)buffer,
                SL_FS_GET_FILE_ATTRIBUTES);
                if(retVal < 0)
                {
                    UART_PRINT("sl_FsGetFileList error:  %d\n\r", retVal);
                    break;
                }
                if(retVal == 0)
                {
                    break;
                }

                // Print single column format
                for (i = 0; i < retVal; i++)
                {
                    UART_PRINT("[%3d] ", ++fileCount);
                    UART_PRINT("%-40s\t", buffer[i].fileName);
                    UART_PRINT("%8d\t", buffer[i].attribute.FileMaxSize);
                    UART_PRINT("0x%03x\t", buffer[i].attribute.Properties);
                    UART_PRINT("\n\r");
                }
                numOfFiles -= retVal;
            }
            UART_PRINT("\n\r");
            if(bShowDescription)
            {
                UART_PRINT(" File properties flags description:\n\r");
                UART_PRINT(" 0x001 - Open file commit\n\r");
                UART_PRINT(" 0x002 - Open bundle commit\n\r");
                UART_PRINT(" 0x004 - Pending file commit\n\r");
                UART_PRINT(" 0x008 - Pending bundle commit\n\r");
                UART_PRINT(" 0x010 - Secure file\n\r");
                UART_PRINT(" 0x020 - No file safe\n\r");
                UART_PRINT(" 0x040 - System file\n\r");
                UART_PRINT(" 0x080 - System with user access\n\r");
                UART_PRINT(" 0x100 - No valid copy\n\r");
                UART_PRINT(" 0x200 - Public write\n\r");
                UART_PRINT(" 0x400 - Public read\n\r");
                UART_PRINT("\n\r");
            }
            free (buffer);
        }

        return retVal;
    }

    /* st_writeFile -
    * Helper function for writing "length" bytes from a user buffer to a (an open) file
    */
    static int st_writeFile( _i32 fileHandle, _u32 length, _const char *buffer)
    {
        int RetVal = 0;
        _i32 offset = 0;

        while(offset < length)
        {
            // write data to open file
            RetVal = sl_FsWrite(fileHandle,
            offset,
            (_u8 *)buffer,
            length);
            if (RetVal <= 0)
            {
                UART_PRINT("sl_FsWrite error:  %d\n\r" ,RetVal);
                return RetVal;
            }
            else
            {
                offset += RetVal;
                length -= RetVal;
            }

        }
        UART_PRINT("   Wrote %d bytes...\n\r", offset);
        return offset;
    }

    /* st_writeFile -
    * Helper function for reading "length" bytes from an open file to the user buffer.
    * (also prints the read content as a string).
    */
    static int st_readFile( _i32 fileHandle, _u32 length, _i8 *buffer)
    {
        int offset = 0;
        int RetVal = 0;

        while(offset < length)
        {
            RetVal = sl_FsRead(fileHandle, offset, (_u8*)buffer, length);

            if(RetVal == SL_ERROR_FS_OFFSET_OUT_OF_RANGE)
            {// EOF
                break;
            }
            if(RetVal < 0)
            {// Error
                UART_PRINT("sl_FsRead error:  %d\n\r", RetVal);
                return RetVal;
            }

            offset += RetVal;
            length -= RetVal;
        }
        UART_PRINT("   Read \"%s\" (%d bytes)...\n\r", buffer, offset);
        return offset;
    }

    /* st_openForRead -
    * Open and read file content
    */
    int st_openForRead(const _u8 *filename)
    {
        int RetVal = 0;
        _i8 buffer[MAX_FILE_SIZE];
        _i32 fd;

        fd = sl_FsOpen(filename, SL_FS_READ, 0);
        if(fd < 0)
        {
            UART_PRINT("sl_FsOpen (read) error: %d\n\r", fd);
        }
        else
        {
            st_readFile(fd, MAX_FILE_SIZE, buffer);
            RetVal = sl_FsClose(fd, 0, 0, 0);
            if(RetVal < 0)
            {
                UART_PRINT("sl_FsClose (read) error: %d\n\r", RetVal);
            }
        }
        return RetVal;
    }

    /* st_openForWrite -
    * Open (an existing file) and re-write its content
    */
    int st_openForWrite(const _u8 *filename, const char *content)
    {
        int RetVal = 0;
        _i32 fd;

        // Trying to override the file (writing new content to it)
        fd = sl_FsOpen(filename, SL_FS_WRITE | SL_FS_WRITE_MUST_COMMIT, 0);
        if(fd < 0)
        {
            UART_PRINT("sl_FsOpen (re-write) error: %d\n\r", fd);
        }
        else
        {
            st_writeFile(fd, strlen(secondaryContent), content);
            RetVal = sl_FsClose(fd, 0, 0, 0);
            if(RetVal < 0)
            {
                UART_PRINT("sl_FsClose (re-write) error: %d\n\r", RetVal);
            }
        }
        return RetVal;
    }

    int st_task3_createFailsafeFile()
    {
        int RetVal = 0;
        _i32 fd;

        UART_PRINT("\n\r\n\r ** Creating the fail-safe file\n\r");

        /* Creating Unsecured FailSafe file */
        fd = sl_FsOpen(TEST_FILENAME,
        (SL_FS_CREATE | SL_FS_CREATE_FAILSAFE | SL_FS_CREATE_MAX_SIZE(MAX_FILE_SIZE)),
        0);
        if(fd < 0)
        {
            UART_PRINT("sl_FsOpen (create) error: %d\n\r", fd);
        }
        else
        {
            st_writeFile(fd, strlen(originalContent), originalContent);
            RetVal = sl_FsClose(fd, 0, 0, 0);
            if(RetVal < 0)
            {
                UART_PRINT("sl_FsClose (create) error: %d\n\r", RetVal);
            }
        }

        /* Verifying the successful creation */
        st_listFiles(0);

        UART_PRINT("Reading %s after CREATION:\n\r");
        st_openForRead(TEST_FILENAME);

        return RetVal;
    }

    /* st_task3_replaceFileContent -
    * demonstrates the replacing of file content and the Rollback / Commit operation
    */
    int st_task3_replaceFileContent()
    {
        int RetVal = 0;
        SlFsControl_t   FsControl;

        UART_PRINT("\n\r\n\r ** Replacing file content\n\r");

        // Write the secondary content to the test file
        st_openForWrite(TEST_FILENAME, secondaryContent);

        UART_PRINT("Reading File before ROLLBACK:\n\r");
        st_openForRead(TEST_FILENAME);
        RetVal = sl_FsCtl(SL_FS_CTL_ROLLBACK, 0, TEST_FILENAME, (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 0);
        UART_PRINT("Reading File after ROLLBACK:\n\r");
        st_openForRead(TEST_FILENAME);

        UART_PRINT("\n\r\n\r ** Replacing file content (again)\n\r");

        // Re-write the secondary content to the test file
        st_openForWrite(TEST_FILENAME, secondaryContent);

        UART_PRINT("Reading File before COMMIT:\n\r");
        st_openForRead(TEST_FILENAME);
        RetVal = sl_FsCtl(SL_FS_CTL_COMMIT, 0, TEST_FILENAME, (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 0);
        UART_PRINT("Reading File after COMMIT:\n\r");
        st_openForRead(TEST_FILENAME);

        return RetVal;
    }

    /* st_task3_deleteFile -
    * demonstrates file deletion
    */
    int st_task3_deleteFile()
    {
        UART_PRINT("\n\r\n\r ** Deleting the file\n\r");
        sl_FsDel(TEST_FILENAME, 0);
        st_listFiles(0);
        return 0;
    }

    void *secureFSExampleThread(void *arg0)
    {
        int RetVal;

        InitTerm();

        RetVal = st_wifiInit();
        if(RetVal < 0)
        {
            UART_PRINT("wifi_init error:  %d\n\r", RetVal);
            return NULL;
        }

        RetVal = sl_Start(0, 0, 0);
        if(RetVal < 0)
        {
            UART_PRINT("sl_Start error:  %d\n\r", RetVal);
            return NULL;
        }
        // Device is awake (the exact role is not relevant for this example)
        // and ready for processing FS commands

        /* Task 2: demonstrates retrieving file system information */
        UART_PRINT("\n\r\n\r*** TASK 2\n\r");
        st_showStorageInfo();
        st_listFiles(1);

        /* Task 3: file safe demonstration */
        UART_PRINT("\n\r\n\r*** TASK 3\n\r");
        RetVal = st_task3_createFailsafeFile();

        if (RetVal == 0)
        {
            RetVal = st_task3_replaceFileContent();
        }

        st_task3_deleteFile();

        while(1);
    }

secure_fs.c

Task 4: Using tokens to control file access

This task will build on the ones above. Tokens are used to control the access to secure (encrypted) files. We will update the example code to use tokens.

  1. Add global variables to store the tokens and the file access mode.

     /* The Tokens array defines the Master, Write-Read, Write-Only and Read-Only tokens
      * (see SlFsTokenId_e).
      */
     _u32 g_tokens[4] = {0x12345678, 0, 0, 0};
    
     /* In this example we set the master token to a vendor-specific and static value. 
      * To use the the tokens mechanism, the file should be created as SECURED.
      * The NOSIGNATURE flag was added, as this task doesn't require signatures (those are demonstrated in Task 5). 
      * If we remove the VENDOR flag, the token would be gerenated by the system and will be retrieved as output of FS API.
      * If we remove the STATIC flag, the value will be replaced every time we write new content to the file.
      */
     _u32 g_accessMode = SL_FS_CREATE_SECURE | SL_FS_CREATE_NOSIGNATURE |
                         SL_FS_CREATE_VENDOR_TOKEN | SL_FS_CREATE_STATIC_TOKEN;
    

    secure_fs.c

    Note

    The access control (authorization) requires that we define the file as secured.

    Auto-generated tokens

    When using UniFlash ImageCreator to create new file, there is an issue when using auto-generated token as currently there is no method to retrieve the token value. The possible soultions are using VENDOR token or setting the PUBLIC_WRITE flags, that allows to re-write the file (e.g. in OTA) without a token. A token will still be required for reading the file.

  2. Add the access mode and the master token (pointer) to the file creation (sl_FsOpen()) from Task 3.

     /* Creating FailSafe file */
     _i32 fd = sl_FsOpen(TEST_FILENAME,
                     (SL_FS_CREATE | SL_FS_CREATE_FAILSAFE | SL_FS_CREATE_MAX_SIZE(MAX_FILE_SIZE) | g_accessMode),
                     &g_tokens[SL_FS_TOKEN_MASTER]);
    

    secure_fs.c :: secureFSExampleThread() - Create file

  3. Add the master token (pointer) to the file delete (sl_FsDel()) from Task 3.

     RetVal = sl_FsDel(TEST_FILENAME, g_tokens[SL_FS_TOKEN_MASTER]);
    

    secure_fs.c :: secureFSExampleThread() - Delete file

  4. Build and execute the example. All the attempts to read from the file or write to it should fail with error -10365 (SL_ERROR_FS_INVALID_TOKEN_SECURITY_ALERT). The following is an example terminal output:

    Important

    SimpleLink Wi-Fi devices include a Software Tamper Detection mechanism which will lock the device if the number of "invalid token" errors exceeds a certain threshold (default is 15). If you run the example several times with this failure, it will lock and reprogramming (or factory reset) will be needed.

  5. To fix this error, you'll need to add the required token to each file opening call (for READ and WRITE). You may use the master token (SL_FS_TOKEN_MASTER) or the Read/Write specific tokens as retrieved by sl_FsGetInfo(). Paste this helper function into secure_fs.c.

      /* Call this function after the secure file is created */
      int st_getFileInfo()
      { 
         SlFsFileInfo_t fileInfo;
         int RetVal;
    
         RetVal = sl_FsGetInfo(TEST_FILENAME, g_tokens[SL_FS_TOKEN_MASTER], &fileInfo);
         if(0 == RetVal)
         {
             g_tokens[SL_FS_TOKEN_READ_ONLY] = fileInfo.Token[SL_FS_TOKEN_READ_ONLY];
             g_tokens[SL_FS_TOKEN_WRITE_ONLY] = fileInfo.Token[SL_FS_TOKEN_WRITE_ONLY];
             g_tokens[SL_FS_TOKEN_WRITE_READ] = fileInfo.Token[SL_FS_TOKEN_WRITE_READ];
         }
         return RetVal;
      }
    

    secure_fs.c

  6. Call st_getFileInfo() after the secured file is created and closed in the example thread.

     st_getFileInfo();
    

    secure_fs.c :: secureFSExampleThread()

    and edit all sl_FsOpen() calls to use the relevant tokens for write and read.

     fd = sl_FsOpen(TEST_FILENAME, SL_FS_READ, &g_tokens[SL_FS_TOKEN_READ_ONLY]);
    

    secure_fs.c :: secureFSExampleThread() - Reads

     fd = sl_FsOpen(TEST_FILENAME, SL_FS_WRITE | SL_FS_WRITE_MUST_COMMIT, &g_tokens[SL_FS_TOKEN_WRITE_READ]);
    

    secure_fs.c :: secureFSExampleThread() - Writes

    You can also use master token, or use WRITE_ONLY instead of WRITE_READ.

  7. Add the master token to your rollback or commit configuration commands from task 3. These are how each command would look:

     RetVal = sl_FsCtl(SL_FS_CTL_ROLLBACK, g_tokens[SL_FS_TOKEN_MASTER], TEST_FILENAME,
                       (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 
                       &g_tokens[SL_FS_TOKEN_MASTER]);
    

    secure_fs.c :: secureFSExampleThread() - ROLLBACK

     RetVal = sl_FsCtl(SL_FS_CTL_COMMIT, g_tokens[SL_FS_TOKEN_MASTER], TEST_FILENAME,
                       (_u8 *)&FsControl, sizeof(SlFsControl_t), NULL, 0, 
                       &g_tokens[SL_FS_TOKEN_MASTER]);
    

    secure_fs.c :: secureFSExampleThread() - COMMIT

  8. Build and execute the example. There should not be any more errors.

Task 5: Using certificates to verify authenticity and integrity

The authentication method used for this process is a well-known methodology. Creating a file signature verifies both the file integrity and the authentication of the file. To create a digital signature for the authentication process, the vendor must have an RSA key-pair. The certificate that holds the public key must be signed by a certificate authority. In this example, we will use the dummy "playground" certificate that is provided for development purposes only. The entire chain of trust for this certificate is verified by the device using the certificate store. A signature is required to write the service-pack and the certificate store files; the signatures for those files are supplied by TI. For any other secure signed files, no certificate is required. Integrity is checked when a file is opened for read, and authenticity is checked when a file closed after write.

  1. Load the entire "playground" dummy trusted chain onto your serial flash (dummy-trusted-cert, dummy-trusted-ca-cert, and dummy-root-ca-cert).

    Production releases

    In a production release, the secured files should be signed using a customer certificate that was signed by a known root CA (Certificate Authority). The full list of root CAs supported by TI's certificate store is available in the SDK: simplelink_cc32xx_sdk_x_xx_xx_xx/tools/cc32xx_tools/certificate-catalog/readme.html

    For more details on certificate handling, refer to the SimpleLink Wi-Fi Certificates Handling User's Guide.

  2. Next, we will create the signature using the dummy playground private-key (see dummy-trusted-cert-key in simplelink_cc32xx_sdk_x_xx_xx_xx/tools/cc32xx_tools/certificate-playground/) and openssl from a command line.

    Copy the contents of the originalContent array we created in task 3 (from secure_fs.c) to a text file (e.g: origin.txt)

     $ cat > origin.txt
     This is the original content! <Ctrl + D>
    

    and verify using the following command to read its contents:

     $ cat origin.txt
     This is the original content!
    
  3. Use the following command to create the signature in hex format via command line. dummy-trusted-cert-key (found in simplelink_cc32xx_sdk_x_xx_xx_xx/tools/cc32xx_tools/certificate-playground) must be in the same file directory you are working in.

     $ openssl dgst -hex -c -sha1 -keyform DER -sign dummy-trusted-cert-key -out origin.hex.sign  origin.txt
    

    The result should look like:

     $ cat origin.hex.sign
     RSA-SHA1(origin.txt)= 28:fa:72:ea:9e:fe:d2:8c:00:72:04:d4:02:fb:35:16:31:c9:6d:d8:6d:c5:26:8e:75:e4:36:b2:f3:df:ac:1d:5a:4f:80:46:f4:1e:65:da:c0:05:bf:29:ff:79:6d:7d:a4:29:79:54:d5:76:58:57:5f:f4:f9:53:7b:48:6b:1c:7c:d9:b1:8e:df:92:c0:4e:86:0e:0a:a9:1c:76:f0:53:f1:ef:d2:4a:b9:c6:ea:b1:d8:11:df:33:4d:74:1a:be:b8:68:ae:ee:2d:5a:b3:a5:12:56:96:44:53:ea:3d:e8:5b:b8:85:75:70:b9:34:50:b0:a5:af:6a:8d:de:4e:e0:15:37:2d:e0:39:d4:fa:13:73:3a:21:9d:16:aa:f3:b8:f0:9b:76:fc:c3:7d:ee:d4:5e:3c:9c:0e:e7:de:88:0d:2f:93:e4:b0:42:e9:94:10:69:3b:4b:b6:06:0d:87:d1:74:6e:6c:f4:85:6a:f5:a3:ac:76:e7:b1:86:c3:ed:69:7a:11:71:10:26:b9:bc:22:71:1e:25:b1:c6:ec:d4:e0:61:a4:7b:26:5a:bf:70:bf:62:5e:bf:6f:dc:34:df:89:6a:87:f3:6f:37:75:2e:a9:a4:67:f6:90:97:ee:36:fb:12:5f:23:e7:98:a1:f9:4a:e2:48:dc:a1:94:6d:81:59
    
  4. Convert the content of the origin.hex.sign to make it look like a C array. You may use the following commands or do it manualy:

    Replace all the ':' with ', 0x'

     $ sed 's/:/, 0x/g' origin.hex.sign > tmp1.c
    

    Handle the first byte (add the '0x' prefix)

     $ sed 's/\= /= {0x/' tmp1.c > tmp2.c
    

    Fix the array name

     $ sed 's/RSA-SHA1(origin.txt)/_const char originalSignature[] /' tmp2.c > origin.c
    

    Add trailing brackets '};'

     $ echo "};" >> origin.c
    

    The result should look like:

     $ cat origin.c
     originalSignature[] = {0x28, 0xfa, 0x72, 0xea, 0x9e, 0xfe, 0xd2, 0x8c, 0x00, 0x72, 0x04, 0xd4, 0x02, 0xfb, 0x35, 0x16, 0x31, 0xc9, 0x6d, 0xd8, 0x6d, 0xc5, 0x26, 0x8e, 0x75, 0xe4, 0x36, 0xb2, 0xf3, 0xdf, 0xac, 0x1d, 0x5a, 0x4f, 0x80, 0x46, 0xf4, 0x1e, 0x65, 0xda, 0xc0, 0x05, 0xbf, 0x29, 0xff, 0x79, 0x6d, 0x7d, 0xa4, 0x29, 0x79, 0x54, 0xd5, 0x76, 0x58, 0x57, 0x5f, 0xf4, 0xf9, 0x53, 0x7b, 0x48, 0x6b, 0x1c, 0x7c, 0xd9, 0xb1, 0x8e, 0xdf, 0x92, 0xc0, 0x4e, 0x86, 0x0e, 0x0a, 0xa9, 0x1c, 0x76, 0xf0, 0x53, 0xf1, 0xef, 0xd2, 0x4a, 0xb9, 0xc6, 0xea, 0xb1, 0xd8, 0x11, 0xdf, 0x33, 0x4d, 0x74, 0x1a, 0xbe, 0xb8, 0x68, 0xae, 0xee, 0x2d, 0x5a, 0xb3, 0xa5, 0x12, 0x56, 0x96, 0x44, 0x53, 0xea, 0x3d, 0xe8, 0x5b, 0xb8, 0x85, 0x75, 0x70, 0xb9, 0x34, 0x50, 0xb0, 0xa5, 0xaf, 0x6a, 0x8d, 0xde, 0x4e, 0xe0, 0x15, 0x37, 0x2d, 0xe0, 0x39, 0xd4, 0xfa, 0x13, 0x73, 0x3a, 0x21, 0x9d, 0x16, 0xaa, 0xf3, 0xb8, 0xf0, 0x9b, 0x76, 0xfc, 0xc3, 0x7d, 0xee, 0xd4, 0x5e, 0x3c, 0x9c, 0x0e, 0xe7, 0xde, 0x88, 0x0d, 0x2f, 0x93, 0xe4, 0xb0, 0x42, 0xe9, 0x94, 0x10, 0x69, 0x3b, 0x4b, 0xb6, 0x06, 0x0d, 0x87, 0xd1, 0x74, 0x6e, 0x6c, 0xf4, 0x85, 0x6a, 0xf5, 0xa3, 0xac, 0x76, 0xe7, 0xb1, 0x86, 0xc3, 0xed, 0x69, 0x7a, 0x11, 0x71, 0x10, 0x26, 0xb9, 0xbc, 0x22, 0x71, 0x1e, 0x25, 0xb1, 0xc6, 0xec, 0xd4, 0xe0, 0x61, 0xa4, 0x7b, 0x26, 0x5a, 0xbf, 0x70, 0xbf, 0x62, 0x5e, 0xbf, 0x6f, 0xdc, 0x34, 0xdf, 0x89, 0x6a, 0x87, 0xf3, 0x6f, 0x37, 0x75, 0x2e, 0xa9, 0xa4, 0x67, 0xf6, 0x90, 0x97, 0xee, 0x36, 0xfb, 0x12, 0x5f, 0x23, 0xe7, 0x98, 0xa1, 0xf9, 0x4a, 0xe2, 0x48, 0xdc, 0xa1, 0x94, 0x6d, 0x81, 0x59
     };
    
  5. Repeat steps 2-4 above for the secondaryContent array. (Note in step 4 where you should change originalSignature to secondarySignature.)

  6. Copy the resulting arrays (originalSignature and secondarySignature) to secure_fs.c above the thread function.

     _const char originalContent[] = "This is the original content!";
     _const char originalSignature[] = {0x28, 0xfa, 0x72, 0xea, 0x9e, 0xfe, 0xd2, 0x8c, 0x00, 0x72, 0x04, 0xd4, 0x02, 0xfb, 0x35, 0x16, 0x31, 0xc9, 0x6d, 0xd8, 0x6d, 0xc5, 0x26, 0x8e, 0x75, 0xe4, 0x36, 0xb2, 0xf3, 0xdf, 0xac, 0x1d, 0x5a, 0x4f, 0x80, 0x46, 0xf4, 0x1e, 0x65, 0xda, 0xc0, 0x05, 0xbf, 0x29, 0xff, 0x79, 0x6d, 0x7d, 0xa4, 0x29, 0x79, 0x54, 0xd5, 0x76, 0x58, 0x57, 0x5f, 0xf4, 0xf9, 0x53, 0x7b, 0x48, 0x6b, 0x1c, 0x7c, 0xd9, 0xb1, 0x8e, 0xdf, 0x92, 0xc0, 0x4e, 0x86, 0x0e, 0x0a, 0xa9, 0x1c, 0x76, 0xf0, 0x53, 0xf1, 0xef, 0xd2, 0x4a, 0xb9, 0xc6, 0xea, 0xb1, 0xd8, 0x11, 0xdf, 0x33, 0x4d, 0x74, 0x1a, 0xbe, 0xb8, 0x68, 0xae, 0xee, 0x2d, 0x5a, 0xb3, 0xa5, 0x12, 0x56, 0x96, 0x44, 0x53, 0xea, 0x3d, 0xe8, 0x5b, 0xb8, 0x85, 0x75, 0x70, 0xb9, 0x34, 0x50, 0xb0, 0xa5, 0xaf, 0x6a, 0x8d, 0xde, 0x4e, 0xe0, 0x15, 0x37, 0x2d, 0xe0, 0x39, 0xd4, 0xfa, 0x13, 0x73, 0x3a, 0x21, 0x9d, 0x16, 0xaa, 0xf3, 0xb8, 0xf0, 0x9b, 0x76, 0xfc, 0xc3, 0x7d, 0xee, 0xd4, 0x5e, 0x3c, 0x9c, 0x0e, 0xe7, 0xde, 0x88, 0x0d, 0x2f, 0x93, 0xe4, 0xb0, 0x42, 0xe9, 0x94, 0x10, 0x69, 0x3b, 0x4b, 0xb6, 0x06, 0x0d, 0x87, 0xd1, 0x74, 0x6e, 0x6c, 0xf4, 0x85, 0x6a, 0xf5, 0xa3, 0xac, 0x76, 0xe7, 0xb1, 0x86, 0xc3, 0xed, 0x69, 0x7a, 0x11, 0x71, 0x10, 0x26, 0xb9, 0xbc, 0x22, 0x71, 0x1e, 0x25, 0xb1, 0xc6, 0xec, 0xd4, 0xe0, 0x61, 0xa4, 0x7b, 0x26, 0x5a, 0xbf, 0x70, 0xbf, 0x62, 0x5e, 0xbf, 0x6f, 0xdc, 0x34, 0xdf, 0x89, 0x6a, 0x87, 0xf3, 0x6f, 0x37, 0x75, 0x2e, 0xa9, 0xa4, 0x67, 0xf6, 0x90, 0x97, 0xee, 0x36, 0xfb, 0x12, 0x5f, 0x23, 0xe7, 0x98, 0xa1, 0xf9, 0x4a, 0xe2, 0x48, 0xdc, 0xa1, 0x94, 0x6d, 0x81, 0x59};
     _const char secondaryContent[] = "Overridden by the secondary content...";
     _const char secondarySignature[] = {0xb4, 0x89, 0x06, 0xbc, 0x62, 0x8a, 0x68, 0x83, 0x04, 0xd4, 0x60, 0x08, 0xd6, 0xe4, 0xe7, 0x88, 0x81, 0xb6, 0xa0, 0xdc, 0x6c, 0x27, 0xc1, 0x40, 0x4f, 0x1f, 0xfd, 0x1a, 0xf4, 0xfd, 0x53, 0xb6, 0xfc, 0xd3, 0x0e, 0x6a, 0xfc, 0xb9, 0x0b, 0xe9, 0xa6, 0x54, 0x9b, 0xa5, 0x64, 0x36, 0xd6, 0x4d, 0x10, 0x32, 0x7d, 0x2f, 0x88, 0x70, 0x20, 0x16, 0x7e, 0x1c, 0xba, 0x6f, 0x4f, 0x4d, 0x05, 0xa0, 0x1b, 0xfb, 0x15, 0x11, 0x30, 0x8c, 0x45, 0x3c, 0xab, 0x5b, 0x9e, 0x4f, 0x8d, 0x6b, 0x9a, 0x19, 0xaf, 0x5d, 0x6f, 0x71, 0x47, 0x75, 0x3c, 0x62, 0x67, 0x51, 0xe9, 0xbf, 0xc4, 0x12, 0x6a, 0xb6, 0x30, 0xf3, 0xb4, 0x6b, 0x46, 0xb6, 0xa8, 0x79, 0x6f, 0xcf, 0x3a, 0x33, 0x3d, 0x36, 0x9a, 0x98, 0x73, 0x43, 0x5f, 0x16, 0x2d, 0x89, 0xfe, 0x5b, 0xbb, 0x5f, 0x80, 0x5c, 0x06, 0x53, 0xda, 0xa5, 0x04, 0x0e, 0x89, 0xb5, 0x85, 0x46, 0xd8, 0x15, 0x97, 0xef, 0xa0, 0x31, 0x5c, 0x67, 0x85, 0x8c, 0x61, 0x45, 0xf6, 0x07, 0x11, 0x56, 0xd0, 0xfc, 0xd4, 0x10, 0x07, 0xb5, 0x28, 0xc0, 0x9d, 0x10, 0xfc, 0xff, 0xf9, 0x33, 0xed, 0xb7, 0xc4, 0xf0, 0xc1, 0xef, 0x47, 0x36, 0x49, 0xd1, 0x7d, 0xc0, 0x5f, 0x67, 0xd7, 0xf1, 0x34, 0xa3, 0x22, 0x16, 0x66, 0x88, 0x46, 0x24, 0x9d, 0x35, 0xdb, 0xb3, 0xbd, 0x38, 0xe6, 0x27, 0x04, 0xaf, 0x31, 0xda, 0x4d, 0xdf, 0x7e, 0x5e, 0xda, 0x6f, 0x55, 0x6c, 0x5e, 0xe4, 0x61, 0x23, 0x62, 0xf9, 0x54, 0x9e, 0xfd, 0x4c, 0xd0, 0x5b, 0xbc, 0x62, 0x55, 0xb1, 0x6a, 0xbc, 0x3c, 0x11, 0x53, 0xda, 0x4d, 0x7d, 0xc5, 0xad, 0xad, 0x10, 0x52, 0xf2, 0x2b, 0x4f, 0xba, 0xd2, 0x1c, 0xe1, 0xec, 0x33, 0x51, 0xdf, 0x32, 0x3a, 0x12, 0x3d, 0xbf, 0x48, 0x35, 0x84};
    

    secure_fs.c

  7. The signature is authenticated when a file (opened for write) is closed. Add the signature with the certificate file to sl_FsClose() calls:

    RetVal = sl_FsClose(fd, TEST_CERTIFICATE, (const unsigned char *)originalSignature, sizeof(originalSignature));
    

    secure_fs :: secureFSExampleThread()

    RetVal = sl_FsClose(fd, TEST_CERTIFICATE, (const unsigned char *)secondarySignature, strlen(secondarySignature));
    

    secure_fs :: secureFSExampleThread()

  8. Compile and run the example.

Further Reading

For more information and implmentation options, see the following resources:

Technical support

For any questions you might have, please search on the TI SimpleLink Wi-Fi E2E Forum

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.