Fork me on GitHub

Security features

The short overview of the security features is as follows:

  • Mongoose OS uses mbedTLS library from ARM with several patches:
    • RAM usage by each connection. By default, mbedTLS uses allocates >32k RAM for each TLS connection. Mongoose OS uses dynamic buffers, reducing RAM usage per connection down to 1k RAM.
    • ECC508A crypto chip integration. This patch makes mbedTLS to offload crypto to the hardware chip.
    • Dynamic CA certificates loading. By default, mbedTLS loads all CA certificates in RAM. Our patch makes it load on demand, saving a lot of RAM. Mongoose OS stores CA certificates in ca.pem file, where you can add your server's CA certificate without blowing RAM usage.
  • Easy configuration of ESP32 flash encryption built-in feature

ESP32 flash encryption

ESP32 chip comes with built-in security features, one of which is a transparent SPI flash encryption - for details, see Espressif documentation.

Mongoose OS makes ESP32 flash encryption setup easy. By default, Mongoose OS firmware is flashed in plain-text form:

mos flash esp32                                 # Flash Mongoose OS on ESP32
mos flash-read --arch esp32 0x190000 2000 -     # Dump filesystem area

The flash-read command dumps the flash memory into a file, and the output can show that the contents is not encrypted. Therefore, sensitive information like TLS private keys could be easily stolen from the flash. In this case, we see a part of the device's file system, not encrypted.

In order to enable flash encryption, use esp32-gen-key command. It enables flash encryption for the next flashing:

mos -X esp32-gen-key flash_encryption_key fe.key \
    --esp32-enable-flash-encryption --dry-run=false
mos flash esp32 --esp32-encryption-key-file fe.key

That is irreversible - once flash encryption is enabled, you cannot go back.

Note the extra flag --esp32-encryption-key-file fe.key for the flash command. From now on, a key file is required to re-flash the device. If the key file is lost, the module can't be reflashed. After flash encryption is enabled, the very first boot performs an encryption, which takes a while - up to a minute.

Note the extra flag --esp32-encryption-key-file fe.key. Once the encryption is enabled, the key file is required to re-flash the device. Make sure to keep the key file, cause if it's lost, the module can't be reflashed. After flash encryption is enabled, the very first boot actually performs an encryption, which takes a while - up to a minute. Subsequent boots will be normal, not doing any encryption.

Once the flash is encrypted, one can verify it using flash-read command to ensure there no plain-text parts are present:

mos flash-read --arch esp32 0x190000 2000 -

ATECC508A crypto chip

Often, IoT boards provide no built-in flash protection mechanism. Anyone with a physical access to the device can read the whole flash, including any sensitive information like TLS private keys.

Crypto chips are designed to mitigate that. Their main function is provide storage for private keys, which cannot be read. Private keys are stored inside the crypto chip, and all the crypto operations that require private key, are offloaded to the crypto chip which performs the operation and gives the result back.

ATECC508A crypto chip is designed with additional hardware protection mechanisms to make key extraction difficult. It is an impressive piece of hardware with many layers of protection, and important enough it is quite inexpensive, costing less than 80 cent a piece.

Wiring (ESP8266 NodeMCU example)

Get ATECC508A - either as an ATCRYPTOAUTH-XPRO board which requires no soldering, or a bare-bones ATECC508A which requires soldering.

Function ATECC508A pin ESP8266 pin NodeMCU pin ATCRYPTOAUTH pin
SDA 5 10 (GPIO12) D6 11 (yellow)
SCL 6 9 (GPIO14) D5 12 (white)
GND 4 Any GND Any GND 19 (black)
VCC 8 Any 3V3 Any 3V3 20 (red)

Wiring for ATCRYPTOAUTH-XPRO:

Wiring for the bare-bones ATECC508A:

Setup guide

Mongoose OS has native support for ATECC508A crypto chip. This section is a quick guide to get it up and running. For a more detailed reference, especially of chip configuration, please refer to Microchip documentation.

  1. Generate a cert and key as normal. An example below shows a self-signed certificate, but of course it doesn't have to be. The importnat thing is that it's a ECDSA certificate using P256 curve, since that is what the chip supports.

    openssl ecparam -out ecc.key.pem -name prime256v1 -genkey
    openssl req -new -subj \
     "/C=IE/L=Dublin/O=ACME Ltd/OU=Testing/CN=test.acme.com" \
     -sha256 -key ecc.key.pem -text -out ecc.csr.tmpl
    openssl x509 -in ecc.csr.pem -text -out ecc.crt.pem \
     -req -signkey ecc.key.pem -days 3650
    
  2. Configure the chip. You can use our sample configuration. To set it, use extended mos commands:

    mos -X atca-set-config atca-aws-test.yaml --dry-run=false
    mos -X atca-lock-zone config --dry-run=false
    mos -X atca-lock-zone data --dry-run=false
    

    Note: these changes are irreversible: once locked, zones cannot be unlocked anymore. Also, this sample config is very permissive and is only suitable for testing, NOT for production deployments. Please refer to Microchip manual and other documentation to come up with more secure configuration (we may be able to assist with that too - ask a question on our forum).

  3. Write the generated key into the device. Assuming you are using our sample configuration described in the previous section, this is a two-step process:

    3.1. Generate and set the key encryption key in slot 4

     openssl rand -hex 32 > slot4.key
     mos -X atca-set-key 4 slot4.key --dry-run=false
     AECC508A rev 0x5000 S/N 0x012352aad1bbf378ee, config is locked, data is locked
     Slot 4 is a non-ECC private key slot
     SetKey successful.
    

    3.2. Set the actual ECC key in slot 0

     mos -X atca-set-key 0 ecc.key.pem --write-key=slot4.key --dry-run=false
     AECC508A rev 0x5000 S/N 0x012352aad1bbf378ee, config is locked, data is locked
    
     Slot 0 is a ECC private key slot
     Parsed EC PRIVATE KEY
     Data zone is locked, will perform encrypted write using slot 4 using slot4.key
     SetKey successful.
    
  4. Upload the certificate to the device

    mos put ecc.crt.pem
    
  5. Set HTTP server configuration to use the uploaded certificate and private key from device's slot 0:

    mos config-set http.listen_addr=:443 \
     http.ssl_cert=ecc.crt.pem http.ssl_key=ATCA:0
    Getting configuration...
    Setting new configuration...
    Saving and rebooting...
    

    At startup you should see in the device's log:

    mgos_sys_config_init_http HTTP server started on [443] (SSL)
    

    And when connecting with the browser:

    ATCA:2 ECDH get pubkey ok
    ATCA:0 ECDSA sign ok
    ATCA:2 ECDH ok
    

Configuring TLS on device's HTTP/WS server

The system HTTP server has the following configuration options (we've added comments to the mos tool output):

mos config-get http
{
  "enable": true,         # Set to false to disable default HTTP server
  "hidden_files": "",     # Glob pattern for files to hide from serving
  "listen_addr": "80",    # Port to listen on
  "ssl_ca_cert": "",      # CA certificate for mutual TLS authentication
  "ssl_cert": "",         # Certificate file
  "ssl_key": "",          # Private key file
  "upload_acl": "*"       # ACL for which files can be uploaded via /upload
}

In order to setup one-way SSL/TLS on the system HTTP server, create a certificate, upload the certificate an the key file to the device, and change HTTP server configuration.

Here is a procedure that creates a self-signed certificate:

openssl req  -nodes -new -x509  -keyout key.pem -out cert.pem
mos put cert.pem
mos put key.pem
mos config-set http.listen_addr=443 http.ssl_key=key.pem http.ssl_cert=cert.pem
mos config-set wifi.........   # Configure WiFi on a device
curl -k https://IP_ADDRESS     # Test it !

If you want to use mutual (two-way) TLS with the device, follow this procedure to use a self-signed certificate:

# Common parameters
SUBJ="/C=IE/ST=Dublin/L=Docks/O=MyCompany/CN=howdy"

# Generate CA
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -subj $SUBJ -key ca.key -out ca.crt

# Generate client cert
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj $SUBJ
openssl x509 -req -days 365 -in client.csr -CA ca.crt \
    -CAkey ca.key -set_serial 01 -out client.crt

# Generate server cert
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj $SUBJ
openssl x509 -req -days 365 -in server.csr -CA ca.crt \
    -CAkey ca.key -set_serial 01 -out server.crt

# Upload server key, cert & ca cert to the device
mos put ca.crt
mos put server.key
mos put server.crt

# Update HTTP server settings to use mutual TLS
mos config-set http.ssl_ca_cert=ca.crt http.ssl_cert=server.crt \
    http.ssl_key=server.key http.listen_addr=443

From that point on, the device should be accessible via secure Websocket:

mos config-set wifi.........   # Configure WiFi on a device
mos --cert-file client.crt --key-file client.key \
    --port wss://IPADDR/rpc call RPC.List