DeepCover Embedded Security in IoT: Public-key Secured Data Paths

Dependencies:   MaximInterface

The MAXREFDES155# is an internet-of-things (IoT) embedded-security reference design, built to authenticate and control a sensing node using elliptic-curve-based public-key cryptography with control and notification from a web server.

The hardware includes an ARM® mbed™ shield and attached sensor endpoint. The shield contains a DS2476 DeepCover® ECDSA/SHA-2 coprocessor, Wifi communication, LCD push-button controls, and status LEDs. The sensor endpoint is attached to the shield using a 300mm cable and contains a DS28C36 DeepCover ECDSA/SHA-2 authenticator, IR-thermal sensor, and aiming laser for the IR sensor. The MAXREFDES155# is equipped with a standard Arduino® form-factor shield connector for immediate testing using an mbed board such as the MAX32600MBED#. The combination of these two devices represent an IoT device. Communication to the web server is accomplished with the shield Wifi circuitry. Communication from the shield to the attached sensor module is accomplished over I2C . The sensor module represents an IoT endpoint that generates small data with a requirement for message authenticity/integrity and secure on/off operational control.

The design is hierarchical with each mbed platform and shield communicating data from the sensor node to a web server that maintains a centralized log and dispatches notifications as necessary. The simplicity of this design enables rapid integration into any star-topology IoT network to provide security with the low overhead and cost provided by the ECDSA-P256 asymmetric-key and SHA-256 symmetric-key algorithms.

More information about the MAXREFDES155# is available on the Maxim Integrated website.

src/CC3100.cpp

Committer:
IanBenzMaxim
Date:
2019-12-03
Revision:
18:c2631e985780
Parent:
17:5926077e5345

File content as of revision 18:c2631e985780:

/*******************************************************************************
* Copyright (C) Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************/

#include <cstdio>
#include <cstring>
#include <simplelink.h>
#include <mbed-os/drivers/InterruptIn.h>
#include <mbed-os/platform/mbed_wait_api.h>
#include "CC3100.hpp"

using std::printf;
using std::strlen;

static CC3100::State currentState = CC3100::Stopped;

static mbed::InterruptIn irq(MBED_CONF_APP_CC3100_INTR_PIN);
static mbed::DigitalOut nHIB(MBED_CONF_APP_CC3100_NHIB_PIN, 0);

static SL_P_EVENT_HANDLER pIrqEventHandler = NULL;
static void * pIrqEventHandlerValue = NULL;

_SlFd_t sl_IfOpen(const char * ifName, unsigned long flags) {
  _SlFd_t fd = NULL;
  if (ifName) {
    std::sscanf(ifName, "%p", &fd);
  }
  return fd;
}

int sl_IfClose(_SlFd_t fd) {
  return 0; // Success
}

int sl_IfRead(_SlFd_t fd, unsigned char * pBuff, int len) {
  if (!fd) {
    return 0; // Failure
  }
  std::memset(pBuff, 0xFF, len);
  static_cast<CC3100::SPI *>(fd)->transfer(pBuff, len, pBuff);
  return len; // Success
}

int sl_IfWrite(_SlFd_t fd, const unsigned char * pBuff, int len) {
  if (!fd) {
    return 0; // Failure
  }
  static_cast<CC3100::SPI *>(fd)->transfer(pBuff, len, NULL);
  return len; // Success
}

int sl_IfRegIntHdlr(SL_P_EVENT_HANDLER InterruptHdl, void * pValue) {
  pIrqEventHandler = InterruptHdl;
  pIrqEventHandlerValue = pValue;
  return 0; // Success
}

static void interruptHandler(void) {
  if (pIrqEventHandler) {
    pIrqEventHandler(pIrqEventHandlerValue);
  }
}

void sl_DeviceEnable(void) {
  irq.rise(&interruptHandler);
  nHIB = 1;
}

void sl_DeviceDisable(void) {
  irq.rise(NULL);
  nHIB = 0;
}

/*!
    \brief This function handles general error events indication

    \param[in]      pDevEvent is the event passed to the handler

    \return         None
*/
void SimpleLinkGeneralEventHandler(SlDeviceEvent_t * pDevEvent) {
  /*
   * Most of the general errors are not FATAL are are to be handled
   * appropriately by the application
   */
  printf(" [GENERAL EVENT] \n\r");
}

/*!
    \brief This function handles WLAN events

    \param[in]      pWlanEvent is the event passed to the handler

    \return         None

    \note

    \warning
*/
void SimpleLinkWlanEventHandler(SlWlanEvent_t * pWlanEvent) {
  if (!pWlanEvent) {
    printf(" [WLAN EVENT] NULL Pointer Error \n\r");
    return;
  }

  switch (pWlanEvent->Event) {
  case SL_WLAN_CONNECT_EVENT:
    currentState = CC3100::Connected;

    /*
     * Information about the connected AP (like name, MAC etc) will be
     * available in 'slWlanConnectAsyncResponse_t' - Applications
     * can use it if required
     *
     * slWlanConnectAsyncResponse_t *pEventData = NULL;
     * pEventData = &pWlanEvent->EventData.STAandP2PModeWlanConnected;
     *
     */
    break;

  case SL_WLAN_DISCONNECT_EVENT: {
    currentState = CC3100::Disconnected;

    slWlanConnectAsyncResponse_t & pEventData =
        pWlanEvent->EventData.STAandP2PModeDisconnected;

    /* If the user has initiated 'Disconnect' request, 'reason_code' is SL_USER_INITIATED_DISCONNECTION */
    if (SL_WLAN_DISCONNECT_USER_INITIATED_DISCONNECTION ==
        pEventData.reason_code) {
      printf(" Device disconnected from the AP on application's request \n\r");
    } else {
      printf(" Device disconnected from the AP on an ERROR..!! \n\r");
    }
  } break;

  default:
    printf(" [WLAN EVENT] Unexpected event \n\r");
    break;
  }
}

/*!
    \brief This function handles events for IP address acquisition via DHCP
           indication

    \param[in]      pNetAppEvent is the event passed to the handler

    \return         None

    \note

    \warning
*/
void SimpleLinkNetAppEventHandler(SlNetAppEvent_t * pNetAppEvent) {
  if (!pNetAppEvent) {
    printf(" [NETAPP EVENT] NULL Pointer Error \n\r");
    return;
  }

  switch (pNetAppEvent->Event) {
  case SL_NETAPP_IPV4_IPACQUIRED_EVENT:
    currentState = CC3100::Connected;

    //SlIpV4AcquiredAsync_t & pEventData = pNetAppEvent->EventData.ipAcquiredV4;
    break;

  default:
    printf(" [NETAPP EVENT] Unexpected event \n\r");
    break;
  }
}

/*!
    \brief This function handles socket events indication

    \param[in]      pSock is the event passed to the handler

    \return         None
*/
void SimpleLinkSockEventHandler(SlSockEvent_t * pSock) {
  if (!pSock) {
    printf(" [SOCK EVENT] NULL Pointer Error \n\r");
    return;
  }

  switch (pSock->Event) {
  case SL_SOCKET_TX_FAILED_EVENT:
    /*
     * TX Failed
     *
     * Information about the socket descriptor and status will be
     * available in 'SlSockEventData_t' - Applications can use it if
     * required
     *
     * SlSockEventData_u *pEventData = NULL;
     * pEventData = & pSock->socketAsyncEvent;
     */
    switch (pSock->socketAsyncEvent.SockTxFailData.status) {
    case SL_ECLOSE:
      printf(" [SOCK EVENT] Close socket operation failed to transmit all "
             "queued packets\n\r");
      break;

    default:
      printf(" [SOCK EVENT] Unexpected event \n\r");
      break;
    }
    break;

  default:
    printf(" [SOCK EVENT] Unexpected event \n\r");
    break;
  }
}

/*!
    \brief This function handles ping report events

    \param[in]      pPingReport holds the ping report statistics

    \return         None

    \note

    \warning
*/
static void SimpleLinkPingReport(SlPingReport_t * pPingReport) {
  currentState = CC3100::Connected;

  if (!pPingReport) {
    printf(" [PING REPORT] NULL Pointer Error\r\n");
    return;
  }

  printf(" [PING REPORT] Sent: %lu, Received: %lu\r\n",
         pPingReport->PacketsSent, pPingReport->PacketsReceived);
}

CC3100::SPI::SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel)
    : spi(mosi, miso, sclk), cs(ssel, 1) {}

void CC3100::SPI::transfer(const uint8_t * txData, size_t dataSize,
                           uint8_t * rxData) {
  const int cs_delay_ms = 5;

  cs = 0;
  wait_ms(cs_delay_ms);
  for (size_t i = 0; i < dataSize; ++i) {
    if (rxData) {
      rxData[i] = spi.write(txData[i]);
    } else {
      spi.write(txData[i]);
    }
  }
  wait_ms(cs_delay_ms);
  cs = 1;
}

CC3100::CC3100()
    : spi(MBED_CONF_APP_CC3100_MOSI_PIN, MBED_CONF_APP_CC3100_MISO_PIN,
          MBED_CONF_APP_CC3100_CLK_PIN, MBED_CONF_APP_CC3100_NCS_PIN) {}

CC3100 & CC3100::instance() {
  static CC3100 instance;
  return instance;
}

int CC3100::start() {
  // Adapter should be stopped.
  if (currentState != Stopped) {
    return invalidState;
  }

  const int result = sl_Start(&spi, NULL, NULL);
  if (result >= 0) {
    currentState = Disconnected;
  }
  return result;
}

CC3100::State CC3100::state() const { return currentState; }

int CC3100::stop() {
  // Adapter should not be stopped.
  if (currentState == Stopped) {
    return invalidState;
  }

  const int result = sl_Stop(0xFF);
  if (result >= 0) {
    currentState = Stopped;
  }
  return result;
}

void CC3100::update() { sl_Task(); }

int CC3100::setDateTime(const std::tm & dateTime) {
  const SlDateTime_t slDateTime = {
      static_cast<_u32>(dateTime.tm_sec),  // sl_tm_sec
      static_cast<_u32>(dateTime.tm_min),  // sl_tm_min
      static_cast<_u32>(dateTime.tm_hour), // sl_tm_hour
      static_cast<_u32>(dateTime.tm_mday), // sl_tm_day
      static_cast<_u32>(dateTime.tm_mon),  // sl_tm_mon
      static_cast<_u32>(dateTime.tm_year)  // sl_tm_year
  };
  return sl_DevSet(SL_DEVICE_GENERAL_CONFIGURATION,
                   SL_DEVICE_GENERAL_CONFIGURATION_DATE_TIME,
                   sizeof(SlDateTime_t),
                   reinterpret_cast<const _u8 *>(&slDateTime));
}

int CC3100::set_credentials(const char * ssid, const char * pass,
                            nsapi_security_t security) {
  this->ssid.assign(ssid);
  this->pass.assign(pass);
  this->security = security;
  return 0;
}

int CC3100::connect(const char * ssid, const char * pass,
                    nsapi_security_t security, uint8_t channel) {
  // Adapter should be disconnected.
  if (currentState != Disconnected) {
    return invalidState;
  }

  SlSecParams_t secParams = {
      SL_SEC_TYPE_OPEN,                                       // Type
      const_cast<_i8 *>(reinterpret_cast<const _i8 *>(pass)), // Key
      static_cast<_u8>(strlen(pass)),                         // KeyLen
  };
  switch (security) {
  case NSAPI_SECURITY_WPA2:
  case NSAPI_SECURITY_WPA:
  case NSAPI_SECURITY_WPA_WPA2:
    secParams.Type = SL_SEC_TYPE_WPA_WPA2;
    break;

  case NSAPI_SECURITY_WEP:
    secParams.Type = SL_SEC_TYPE_WEP;
    break;

  case NSAPI_SECURITY_NONE:
  case NSAPI_SECURITY_UNKNOWN:
    secParams.Type = SL_SEC_TYPE_OPEN;
    break;

  default:
    return invalidState;
  }
  int result = sl_WlanConnect(reinterpret_cast<const _i8 *>(ssid), strlen(ssid),
                              NULL, &secParams, NULL);
  if (result == SL_RET_CODE_OK) {
    // Wait for completion.
    int attempts = 1000;
    do {
      wait_ms(10);
      update();
    } while ((currentState != Connected) && (--attempts > 0));
    if (attempts == 0) {
      result = SL_RET_CODE_ABORT;
    }
  }
  return result;
}

int CC3100::connect(const char * ssid, const char * username,
                    const char * password) {
  // Adapter should be disconnected.
  if (currentState != Disconnected) {
    return invalidState;
  }

  uint8_t values = 0;
  sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, 19, 1, &values);

  SlSecParams_t secParams = {
      SL_SEC_TYPE_WPA_ENT,                                        // Type
      const_cast<_i8 *>(reinterpret_cast<const _i8 *>(password)), // Key
      static_cast<_u8>(strlen(password))                          // KeyLen
  };
  SlSecParamsExt_t secParamsExt = {
      const_cast<_i8 *>(reinterpret_cast<const _i8 *>(username)), // User
      static_cast<_u8>(strlen(username)),                         // UserLen
      NULL,                                                       // AnonUser
      0,                                                          // AnonUserLen
      0,                                                          // CertIndex
      SL_ENT_EAP_METHOD_PEAP0_MSCHAPv2                            // EapMethod
  };
  int result = sl_WlanConnect(reinterpret_cast<const _i8 *>(ssid), strlen(ssid),
                              NULL, &secParams, &secParamsExt);
  if (result == SL_RET_CODE_OK) {
    // Wait for completion.
    int attempts = 1000;
    do {
      wait_ms(10);
      update();
    } while ((currentState != Connected) && (--attempts > 0));
    if (attempts == 0) {
      result = SL_RET_CODE_ABORT;
    }
  }
  return result;
}

int CC3100::disconnect() {
  // Adapter should be connected.
  if (currentState != Connected) {
    return invalidState;
  }

  return sl_WlanDisconnect();
}

int CC3100::gethostbyname(const char * host, SocketAddress * address,
                          nsapi_version_t version) {
  // TODO: Add IPv6 support

  // Adapter should be connected.
  if (currentState != Connected) {
    return invalidState;
  }

  uint32_t addressInt;
  int result;
  int attempts = 1000;
  // Wait for DNS servers.
  do {
    wait_ms(10);
    result = sl_NetAppDnsGetHostByName(
        const_cast<_i8 *>(reinterpret_cast<const _i8 *>(host)), strlen(host),
        reinterpret_cast<_u32 *>(&addressInt), SL_AF_INET);
  } while ((result == SL_NET_APP_DNS_NO_SERVER) && (--attempts > 0));
  if (result == SL_RET_CODE_OK) {
    nsapi_addr_t addressBytes = {
        NSAPI_IPv4, // version
        0,          // bytes
    };
    for (int i = 4; i > 0; --i) {
      addressBytes.bytes[i - 1] = addressInt;
      addressInt >>= 8;
    }
    address->set_addr(addressBytes);
  }
  return result;
}

int CC3100::ping(const SocketAddress & address) {
  // Adapter should be connected.
  if (currentState != Connected) {
    return invalidState;
  }

  SlPingStartCommand_t pingParams = {
      1000, // PingIntervalTime
      20,   // PingSize
      3000, // PingRequestTimeout
      3,    // TotalNumberOfAttempts
      0,    // Flags
      0     // Ip
  };
  for (int i = 0; i < 4; ++i) {
    pingParams.Ip <<= 8;
    pingParams.Ip |= address.get_addr().bytes[i];
  }
  SlPingReport_t pingReport;
  int result = sl_NetAppPingStart(
      (SlPingStartCommand_t *)&pingParams, SL_AF_INET,
      static_cast<SlPingReport_t *>(&pingReport), SimpleLinkPingReport);
  if (result == SL_RET_CODE_OK) {
    // Wait for completion.
    currentState = Pinging;
    int attempts = 1000;
    do {
      wait_ms(10);
      update();
    } while ((currentState == Pinging) && (--attempts > 0));
    if (attempts == 0) {
      result = SL_RET_CODE_ABORT;
    }
  }
  return result;
}

int CC3100::socket_open(nsapi_socket_t * handle, nsapi_protocol_t proto) {
  int16_t result;
  switch (proto) {
  case NSAPI_TCP:
    result = sl_Socket(SL_AF_INET, SL_SOCK_STREAM, 0);
    break;

  case NSAPI_UDP:
    result = sl_Socket(SL_AF_INET, SL_SOCK_DGRAM, 0);
    break;

  /*case SL_SEC_SOCKET:
    result = sl_Socket(SL_AF_INET, SL_SOCK_STREAM, SL_SEC_SOCKET);
    break;*/

  default:
    result = SL_SOC_ERROR;
    break;
  }
  if (result >= 0) {
    *handle = new int16_t(result);

    // Set non-blocking.
    SlSockNonblocking_t enableOption = {
        1 // NonblockingEnabled
    };
    result = sl_SetSockOpt(
        *static_cast<int16_t *>(*handle), SL_SOL_SOCKET, SL_SO_NONBLOCKING,
        reinterpret_cast<_u8 *>(&enableOption), sizeof(enableOption));

    if (result < 0) {
      socket_close(*handle);
    }
  }
  return result;
}

int CC3100::socket_set_cert_path(nsapi_socket_t handle, const char * certPath) {
  // Set CA certificate.
  return sl_SetSockOpt(*static_cast<int16_t *>(handle), SL_SOL_SOCKET,
                       SL_SO_SECURE_FILES_CA_FILE_NAME, certPath,
                       strlen(certPath));
}

int CC3100::socket_close(nsapi_socket_t handle) {
  int16_t * castedHandle = static_cast<int16_t *>(handle);
  const int result = sl_Close(*castedHandle);
  delete castedHandle;
  return result;
}

int CC3100::socket_bind(nsapi_socket_t handle, const SocketAddress & address) {
  // TODO: Add IPv6 support
  if (address.get_addr().version != NSAPI_IPv4) {
    return SL_SOC_ERROR;
  }

  SlSockAddrIn_t localAddr = {AF_INET,                      // sin_family
                              sl_Htons(address.get_port()), // sin_port
                              {                             // sin_addr
                                  0 // s_addr
                              }};
  for (int i = 4; i > 0; --i) {
    localAddr.sin_addr.s_addr <<= 8;
    localAddr.sin_addr.s_addr |= address.get_addr().bytes[i - 1];
  }
  return sl_Bind(*static_cast<int16_t *>(handle),
                 reinterpret_cast<SlSockAddr_t *>(&localAddr),
                 sizeof(localAddr));
}

int CC3100::socket_listen(nsapi_socket_t handle, int backlog) {
  return sl_Listen(*static_cast<int16_t *>(handle), backlog);
}

int CC3100::socket_connect(nsapi_socket_t handle,
                           const SocketAddress & address) {
  // TODO: Add IPv6 support
  if (address.get_addr().version != NSAPI_IPv4) {
    return SL_SOC_ERROR;
  }

  SlSockAddrIn_t addr = {AF_INET,                      // sin_family
                         sl_Htons(address.get_port()), // sin_port
                         {                             // sin_addr 
                             0 // s_addr
                         }};
  for (int i = 4; i > 0; --i) {
    addr.sin_addr.s_addr <<= 8;
    addr.sin_addr.s_addr |= address.get_addr().bytes[i - 1];
  }
  int result;
  int attempts = 1000;
  do {
    wait_ms(10);
    result = sl_Connect(*static_cast<int16_t *>(handle),
                        reinterpret_cast<SlSockAddr_t *>(&addr), sizeof(addr));
  } while ((result == SL_EALREADY) && (--attempts > 0));
  if (result > 0) {
    result = 0;
  }
  return result;
}

int CC3100::socket_send(nsapi_socket_t handle, const void * data,
                        unsigned size) {
  return sl_Send(*static_cast<int16_t *>(handle), data, size, 0);
}

int CC3100::socket_recv(nsapi_socket_t handle, void * data, unsigned size) {
  int result = sl_Recv(*static_cast<int16_t *>(handle), data, size, 0);
  if (result == SL_EAGAIN) {
    result = NSAPI_ERROR_WOULD_BLOCK;
  }
  return result;
}