Networking and Security
ZentriOS includes a full IPv4 networking stack and SSL/TLS security suite that supports a range of popular networking protocols including TCP, UDP, DNS, DHCP. Additional network application libraries are provided for native HTTP/S and secure cloud access.
Procedure for Joining a WLAN Network
Network features include ...
- DHCP client and server
- TCP/UDP/TLS client and server
- HTTP & HTTPS library
- HTTP Server with RESTful API & Websockets
- Network status indication using GPIOs
- Network Connections and Streams
- Remote Terminal Access
- Network Discovery including mDNS, LLMNR & Netbios
- Broadcast status announcement
- Network time management
- Sending email using SMTP
Security features include ...
- SSL/TLS client and server
- HTTPS webserver
- HTTPS file upload and download
- CA certificate store
Procedure for Joining a WLAN Network
Setting WLAN Variables
To join a WLAN network, you need to pre-set some variables. In all cases, save the variable values to NVM with the save command.
- Set the wlan.ssid and wlan.passkey variables:
- Optionally set wlan.bssid and wlan.security
- to find the WLAN SSID, you can use the network_up -s or scan commands
- After the network credentials are
set
, save them to NVM with the save command
- If you want ZentriOS to attempt to join the nework on boot, set wlan.auto_join.enabled to true. You can also specify wlan.auto_join.retry_delay and wlan.auto_join.retries
- To control the timeout on join attempts, specify wlan.join.timeout. To control the number of retries ZentriOS attempts, set wlan.join.retries
- If you want to specify static IP address, DNS and gateway values, set the wlan.static.* variables
Starting the Network Up Sequence
To start the network up sequence, do one of the following:
- Set wlan.auto_join.enabled and reboot/powerup
- Invoke the network_up command
- Invoke any command that requires the network interface
The Network Up Sequence
ZentriOS performs the following sequence of operations to bring the network up:
check retry count:
- IF join attempt count >= wlan.join.retries, GOTO step 6: join attempt failed.
- ELSE continue to next step.
first time?:
- IF network has previously been joined successfully: skip probe and attempt to join with existing network details. GOTO step 4: attempt to join.
- ELSE continue to next step.
probe for network details:
- IF join attempt count == 1, ZentriOS probes for network with wlan.bssid and/or wlan.security if set, else wlan.ssid. Probing uses all wlan.scan.* variables
- IF probe NOT successful, GOTO step 6: join attempt failed.
- ELSE continue to next step: attempt to join
attempt to join: ZentriOS attempts to associate using network details found in probe or in stored in previous successful join:
- IF ZentriOS has network details (i.e. bssid, channel and security), ZentriOS attempts to associate to network, with timeout after wlan.join.timeout ms.
- IF association successful, goto step 5, get IP address, DNS and gateway values
- ELSE IF association failed, ZentriOS failed to associate to network. Increment join retry count and GOTO step 1
- ELSE if network not found in probing, the join attempt failed. GOTO step 6.
- IF ZentriOS has network details (i.e. bssid, channel and security), ZentriOS attempts to associate to network, with timeout after wlan.join.timeout ms.
get IP address, DNS and gateway values:
- IF the device successfully joins the network:
- IF wlan.dhcp.enabled is TRUE, ZentriOS attempts to get an IP address from the AP, timeout after wlan.dhcp.timeout seconds
- ELSE IF wlan.dhcp.enabled is FALSE then ZentriOS uses the wlan.static.* variables, which you should preset to appropriate values
network up
sequence successful. GOTO step 7: network up sequence complete.
- ELSE the join attempt failed. GOTO step 6, join attempt failed.
- IF the device successfully joins the network:
join attempt failed: Try again if auto join enabled, WLAN interface not just forced down, and auto join retries not exceeded:
- IF the join attempt failed AND wlan.auto_join.enabled is TRUE, AND auto join retry count < wlan.auto_join.retries, ZentriOS waits wlan.auto_join.retry_delay seconds and starts the
network up
sequence again. Increment auto join retry count and join retry count, then GOTO step 1. - ELSE GOTO step 7.
- IF the join attempt failed AND wlan.auto_join.enabled is TRUE, AND auto join retry count < wlan.auto_join.retries, ZentriOS waits wlan.auto_join.retry_delay seconds and starts the
network up sequence complete
Zentrios has either succeeded or failed in joining the network. You can check wlan.join.result to determine the final result of the network_up sequence.
DHCP Client and Server
DHCP Server
The ZentriOS DHCP server supplies IP addresses to clients connecting to the soft AP when the network is brought up with the softap
interface.
See command:
- network_up See variables:
- softap.auto_start
- softap.channel
- softap.client_list
- softap.dhcp_server.enabled
DHCP Client
The ZentriOS DHCP client obtains an IP address from the wlan DHCP server when the network is brought up with the wlan
interface.
See command:
TCP, UDP and TLS Client and Server
See also Network Connections and Streams.
See commands:
- TCP client variables
- TCP keepalive variables
- TCP server variables
- UDP client variables
- UDP server variables
TCP Client Auto Connect
See variables:
- tcp.client.auto_interface
- tcp.client.auto_retries
- tcp.client.auto_start
- tcp.client.remote_host
- tcp.client.remote_port
UDP Client Auto Connect
See variables:
Increasing Available Memory for TLS
Low memory may result in TLS errors such as TLS malloc failed
. TLS connections require a considerable amount of RAM. To establish a TLS connection, other memory intensive features may need to be disabled. You can return the device to the default state with a factory reset.
For a full description of TLS errors and the commands that use TLS, see TLS Errors.
TLS Errors
Commands that use TLS respond to TLS failure with a specially formatted error message. Commands include:
In the event of TLS failure, the response includes an error message with a format similar to the following:
Error with TLS handshake: <error code> (state: <tls state>, code: <internal error code>)
where:
<error code>
- general error code. See the TLS Error Codes table below.<tls state>
- the current parsing state when the error occurred. See the TLS States table below.<internal error code>
- internal error code used for debugging.
Error Example
> http_get https://google.com
[2015-03-25 | 02:50:33: Opening: https://google.com]
Request GET /
Connecting (https): google.com:443
Starting TLS
TLS malloc failed
Error with TLS handshake: 5035
[2015-03-25 | 02:50:33: Open failed]
Command failed
TLS Error Codes
Code | Error |
---|---|
2 | TIMEOUT |
5001 | RECEIVE_FAILED |
5002 | ALERT_NO_CERTIFICATE |
5003 | ERROR_OUT_OF_MEMORY |
5004 | ERROR_FEATURE_UNAVAILABLE |
5005 | ERROR_BAD_INPUT_DATA |
5006 | ERROR_INVALID_MAC |
5007 | ERROR_INVALID_RECORD |
5008 | ERROR_INVALID_MODULUS_SIZE |
5009 | ERROR_UNKNOWN_CIPHER |
5010 | ERROR_NO_CIPHER_CHOSEN |
5011 | ERROR_NO_SESSION_FOUND |
5012 | ERROR_NO_CLIENT_CERTIFICATE |
5013 | ERROR_CERTIFICATE_TOO_LARGE |
5014 | ERROR_CERTIFICATE_REQUIRED |
5015 | ERROR_PRIVATE_KEY_REQUIRED |
5016 | ERROR_CA_CHAIN_REQUIRED |
5017 | ERROR_UNEXPECTED_MESSAGE |
5018 | ERROR_FATAL_ALERT_MESSAGE |
5019 | ERROR_PEER_VERIFY_FAILED |
5020 | ERROR_PEER_CLOSE_NOTIFY |
5021 | ERROR_BAD_HS_CLIENT_HELLO |
5022 | ERROR_BAD_HS_SERVER_HELLO |
5023 | ERROR_BAD_HS_CERTIFICATE |
5024 | ERROR_BAD_HS_CERTIFICATE_REQUEST |
5025 | ERROR_BAD_HS_SERVER_KEY_EXCHANGE |
5026 | ERROR_BAD_HS_SERVER_HELLO_DONE |
5027 | ERROR_BAD_HS_CLIENT_KEY_EXCHANGE |
5028 | ERROR_BAD_HS_CERTIFICATE_VERIFY |
5029 | ERROR_BAD_HS_CHANGE_CIPHER_SPEC |
5030 | ERROR_BAD_HS_FINISHED |
5031 | HANDSHAKE_TIMEOUT |
5032 | HANDSHAKE_ERROR |
5033 | INIT_FAIL |
5034 | BAD_MESSAGE |
5035 | UNTRUSTED_CERTIFICATE |
5036 | EXPIRED_CERTIFICATE |
5037 | CERTIFICATE_NAME_MISMATCH |
5038 | CERTIFICATE_REVOKED |
5039 | NO_DATA |
5040 | ERROR_UNSUPPORTED_EXTENSION |
TLS States
Code | TLS State |
---|---|
0 | SSL_HELLO_REQUEST |
1 | SSL_CLIENT_HELLO |
2 | SSL_SERVER_HELLO |
3 | SSL_SERVER_CERTIFICATE |
4 | SSL_SERVER_KEY_EXCHANGE |
5 | SSL_CERTIFICATE_REQUEST |
6 | SSL_SERVER_HELLO_DONE |
7 | SSL_CLIENT_CERTIFICATE |
8 | SSL_CLIENT_KEY_EXCHANGE |
9 | SSL_CERTIFICATE_VERIFY |
10 | SSL_CLIENT_CHANGE_CIPHER_SPEC |
11 | SSL_CLIENT_FINISHED |
12 | SSL_SERVER_CHANGE_CIPHER_SPEC |
13 | SSL_SERVER_FINISHED |
14 | SSL_FLUSH_BUFFERS |
15 | SSL_HANDSHAKE_OVER |
TLS 1.0-1.2
Supported TLS Cipher Suites
OpenSSL Name | Key Exchange | Encryption | Hashing Algorithm |
---|---|---|---|
DHE_RSA_WITH_SEED_CBC_SHA | DHE RSA | SEED | SHA-1 |
DHE_RSA_WITH_AES_256_CBC_SHA256 | DHE RSA | AES-256 | SHA-256 |
DHE_RSA_WITH_AES_256_CBC_SHA | DHE RSA | AES-256 | SHA-1 |
DHE_RSA_WITH_AES_128_CBC_SHA256 | DHE RSA | AES-128 | SHA-256 |
DHE_RSA_WITH_AES_128_CBC_SHA | DHE RSA | AES-128 | SHA-1 |
RSA_WITH_SEED_CBC_SHA | RSA | SEED | SHA-1 |
RSA_WITH_AES_256_CBC_SHA256 | RSA | AES-256 | SHA-256 |
RSA_WITH_AES_256_CBC_SHA | RSA | AES-256 | SHA-1 |
RSA_WITH_AES_128_CBC_SHA256 | RSA | AES-128 | SHA-256 |
RSA_WITH_AES_128_CBC_SHA | RSA | AES-128 | SHA-1 |
Acronyms
- CBC - Cipher Block Chaining
- DH - Diffie-Hellman
- DHE - Diffie-Hellman Ephemeral
- AES - Advanced Encryption Standard
- RSA - Rivest, Shamir, Adleman
- SEED - Block cipher developed by Korean Information Security Agency
- SHA - Secure Hash Algorithm
HTTP Library
The HTTP Library provides commands for HTTP methods:
HTTP Post Example
The following (fictitious) HTTP post example shows how to post data to an HTTP web server using the ZentriOS HTTP API. The HTTP body data posted in this example is a small piece of JSON (sent using stream_write 0 7
). Since the -o
option is used with the http_post command, a connection stream to the HTTP server is opened, but the HTTP post is queued locally on the module.
Queuing the HTTP post locally provides the ability to add HTTP headers using the http_add_header command, and to post data in the HTTP body using the stream_write command. Once all headers and body data are queued, the HTTP post is sent to the server and completed using the http_post command.
Any response data received from the server may be read using stream_read.
> http_post -o example.com/hello application/json
[2014-04-23 | 19:40:23: Opening: example.com]
Request POST /hello
Connecting (HTTP): example.com:80
[2014-04-23 | 19:40:23: Opened: 0]
0
> stream_write 0 7
... JSON data goes here ...
Success
> http_read 0
HTTP response: 200
Chunked response
200
> stream_read 0 1000
{
"response": "howdy!"
}
> stream_close 0
Closing: 0
[2014-04-23 | 19:40:40: Closed: 0]
Success
See Native API HTTP Client group.
HTTP Server with RESTful and WebSocket API
The ZentriOS HTTP webserver may be configured to run as a service on either the softAP or wlan interface. The server supports HTTP Basic Authentication with (or without) HTTPS security.
ZentriOS provides a simple RESTful API on top of the HTTP server. The API allows for a remote HTTP(S) client to issue any ZentriOS command. The result of the command is returned in a simple JSON format.
The RESTful API can be used in a number of ways:
- GET and POST requests - see the HTTP Server RESTful API app note
- The ZentriOS Web App provides complete control of a module via the RESTful API
- Javascript: for more sophisticated scripting, the ZentriOS JavaScript API provides a JavaScript wrapper around the HTTP server REST API
- Python: the ZentriOSpy module provides a Python wrapper around the HTTP server RESTful API.
The HTTP Server is configured with the following variables:
- http.server.api_enabled
- http.server.cors_origin
- http.server.denied_filename
- http.server.enabled
- http.server.interface
- http.server.max_clients
- http.server.notfound_filename
- http.server.password
- http.server.port
- http.server.root_filename
- http.server.tls_cert
- http.server.tls_enabled
- http.server.tls_key
- http.server.tls_verify_peer
- http.server.username
A client can use the RESTful API to issue ZentriOS commands and receive responses, and also for retrieval of module log messages.
The available requests are as follows:
GET /command
POST /command
GET /log
POST /stream
See the HTTP Server RESTful API application note for low-level examples.
The ZentriOS Web App provides a complete demonstration of the HTTP Server RESTful API, and can be customized as required. See Customizing the ZentriOS Web App.
Notes
- Request size is limited to 4 KBytes, not including headers.
- The HTTP server does not check headers.
Command Request/Response
The API supports either a simple GET request or a slightly more complex POST request.
GET Request
GET /command/<ZentriOS command>
POST Request
POST /command
{
"flags" : <flags>,
"command" : "<ZentriOS command>",
"data" : "<command data>"
}
where:
<flags>
0x01
- "command" field is base64 encoded0x02
- base64 encode response data0x04
- "data" field is base64 encoded
<ZentriOS command>
- any ZentriOS serial command<command data>
- optional, specific to certain ZentriOS commands that require additional data (write, file_create, etc)
HTTP Response Codes
200
- the command transaction executed successfully400
- malformed request500
- server error
Response body
{
"id" : <unique id>,
"code" : <response code>,
"flags" : <flags>,
"response" : "<command response>"
}
where:
<unique id>
- unique id given to each command.<response code>
- the ZentriOS command response code.<flags>
0x01
- the command response is base64 encoded
<command response>
- the command response data
Log Request/Response
The API also buffers log messages. This is the request to retrieve the log messages.
GET Request
GET /log
HTTP Response Codes
200
- the command transaction executed successfully400
- malformed request
Response
{
"logs" : [ "<log data>", "<log data>", ....] }
}
Note: the log buffer has limited space. Older logs are replaced by newer ones. This should be called periodically to avoid losing logs.
WebSocket Stream
Issuing the API call POST /stream
opens a websocket and a 'stream' is created by ZentriOS. An MCU can read/write data using the ZentriOS stream_read and stream_write commands.
POST /stream
<raw data .... >
See HTTP Server Simple WebSocket Demonstration.
WebSocket URI
When connecting to a Command API WebSocket server, the WebSocket URI is /stream
.
When connecting to a ZAP WebSocket server, the WebSocket URI is /zapstreams
.
See Native API, HTTP Server Stream.
HTTP Response Codes
200
- the command transaction executed successfully400
- malformed request404
- no available stream handles
HTTP Server Security and Authorization
The HTTP server is secured using HTTP Basic Authentication. This requires that a client supplies a username and password. Note that the username/password are sent in the HTTP request header which is encrypted only if the HTTP request itself is encrypted.
Client Authorization
When the http server username/password settings are set, authorization is required to access certain files/api commands. Client authorization uses HTTP Basic Authentication.
Client authorization requires a username and password that matches the http.server.username and http.server.password variables.
If authorization fails, the server returns a 401
error code and the following response header:
WWW-Authenticate: Basic realm=ZentriOS
The authorization feature is enabled when both the http.server.username and http.server.password variables are set.
Protecting or Securing a File
When client authorization is enabled, authorization is required to download all files (except unprotected files).
To unprotect a file, the -u
(unprotected) flag must be explicitly specified when the file is created using the file_create or http_download commands.
When client authorization is disabled, all files may be downloaded from the ZentriOS web server.
Securing the REST API, Whitelisting API Calls
When the authorization feature is enabled all REST API calls require authorization. It is possible to 'whitelist' certain API calls.
This is done by creating the file: http_whitelist.csv
which contains a comma separated list of REST API calls that do not require authorization.
This file also supports a trailing wildcard character *
. Some examples of this file are as follows:
- Whitelist ALL REST API calls:
*
- Whitelist the
log
andstream
API calls:
/log,/stream
- Whitelist all
get wlan
calls:
get wl*
- Whitelist the
help variables
andhelp commands
commands:
help variables,help commands
- Whitelist
/stream
,help
, and all getters:
/stream,help *,get *
CORS (Cross Origin Resource Sharing)
The ZentriOS HTTP server supports CORS (Cross-Origin Resource Sharing).
The http.server.cors_origin variable allows you to specify origins for which the same-origin policy is relaxed.
This allows control of the module, via the HTTP server, from a remote site provided the module has originally been set up with a http.server.cors_origin domain that allows access from that site. Via the ZentriOS HTTP server ZentriOS JavaScript API, the remote site can issue all ZentriOS commands, including reboot.
Setting the http.server.cors_origin results in the ZentriOS HTTP server inserting a corresponding CORS Access-Control-Allow-Origin (ACAO) response header into resources it delivers.
It also results in the ZentriOS HTTP server responding to an OPTION
request with a set of options supporting remote control.
HTTP Server Security Variables
Network Status Indication Using GPIOs
See Peripherals, System Indicator Functions.
See variables system.indicator.gpio and system.indicator.state.
Network Connections and Streams
Streams are associated with:
- network connections, including HTTP, HTTPS, TCP, TLS, WebSocket and UDP server and client connections
- open files
- SPI serial connections
- I2C serial connections
- the command buffering feature used in some serial apps. See system.cmd.buffered.
Stream Limitations
ZentriOS-WZ supports up to 8 simultaneous TCP or UDP streams.
Each stream type in the table below uses a single stream, with the following exceptions and qualifications:
- External UDP clients connecting to a ZentriOS UDP server do not use an additional stream. That is, a single UDP server takes only one stream and allows an unlimited number of UDP clients to be connected.
- The TCP server, with no client connected, uses no streams. Each external TCP or TLS client connecting to a ZentriOS TCP or TLS server uses an additional stream.
- Only one TLS stream can be open at a time.
- When in STREAM mode, the TCP/TLS/WebSocket servers are limited to one client connection.
Stream Handles
When a stream is open, it is assigned a handle number. This handle is used to read/write/poll/close the stream. ZentriOS supports many types of streams.
Stream Commands
Refer to the following stream commands to use the stream handle:
Serial STREAM Mode
In serial STREAM mode, a single network connection (TCP/TLS/UDP/WebSocket client/server) streams to the serial bus. No stream read or write commands are required, as data flows automatically between the serial bus and the open stream. See Serial Interface, Serial Bus Mode.
STREAM mode does not support the following streams:
- File
- SPI
- I2C
Note: When in STREAM mode, a TCP/TLS/WebSocket server is limited to one client connection.
Stream Types
List the currently open streams with the stream_list command. Streams are listed with stream handle, type, and information about the stream source.
Note that some stream types support a limited set of operations. The following is a list of stream types, and for each type the command to create it, and the operations it supports.
Type | Related Commands | Stream Operations | Description |
---|---|---|---|
UDPC | udp_client | read/write/poll/close | UDP client |
UDPS | udp_server | read/write/poll/close | UDP server. Note that one stream is used for all server clients |
TCPC | tcp_client | read/write/poll/close | TCP client |
TCPS | tcp_server | read/write/poll/close | TCP server client stream. Note that one stream is used per client |
TLSC | tls_client | read/write/poll/close | TLS client |
TLSS | tls_server | read/write/poll/close | TLS server client stream. Note that one stream is used per client |
HTTP | http_get/http_post/http_head | read/write/poll/close | HTTP client |
HTTPS | http_get/http_post/http_head | read/write/poll/close | Secure HTTP client |
WEBS | - | read/write/poll/close | Websocket server with a connected client |
WEBC | - | read/write/poll/close | Websocket client |
FILE | file_open/file_create -o | read/poll/close | File system handle |
CMD | buffered commands | read/poll/close | Buffered command data for commands that are buffered when the variable system.cmd.buffered = '1' |
SPI | spi_master_open | read/write/poll/close | SPI master serial connection |
I2C | i2c_master_open | read/write/poll/close | I2C master serial connection |
Remote Terminal Access
See the Wi-Fi Remote Terminal application note and the Remote Terminal variables.
Network Discovery
Overview
In simple terms, network discovery is used to give the module a name on the local network.
So, for instance, if the module has a domain name mymodule.local
, a remote client on the same network can connect to the module using the domain mymodule.local
even though the domain mymodule.local
is not registered with a DNS server.
This is useful because the remote client doesn't need to know the IP address of the module.
ZentriOS supports three network discovery protocols: mDNS (multicast Domain Name System), LLMNR (Link-Local Multicast Name Resolution) and NetBIOS. The latter two protocols are used by Windows systems only.
OS Support
The remote client must support one or more network discovery protocols for device discovery to work.
Apple
Mac OS X & iOS support mDNS by default, with Bonjour.
Windows
Windows has support for mDNS, but by default it uses the LLMNR and NetBIOS protocols. These protocols are very similar to mDNS so their basic domain resolution features are supported by the module as well. When a domain is entered into the web browser, Windows follows this sequence to resolve the domain:
- Check local cache
- Issue LLMNR query
- Issue NetBIOS query
- Issue DNS query
The LLMNR and NetBIOS queries are broadcast to the local network only. The DNS query is typically sent to the internet.
If network discovery is enabled on the module, the module will receive the LLMNR and NetBIOS queries, compare the query domain to its mdns.name, and if they match respond with the module's IP address. In this way the module can easily be found on the local network from a Windows machine.
A NetBIOS domain can have up to 15 characters (including the '.local'). If mdns.name is longer than 15 characters then the NetBIOS protocol is not used.
Finally, some ISPs hijack the .local
domain for their own purposes, including advertising. This may cause a Windows PC to fail to resolve the domain.
The solution (for Windows only) is to drop .local
from the URL and simply use http://mymodule/
directly.
Linux
Linux needs an additional package installed for mDNS support. The most common package is Avahi.
Android
Android does not support mDNS (or other ZeroConf protocols) at the platform level, so third party Android apps like Chrome may not support mDNS. An Android SDK with mDNS support is available, so you can create an Android app with mDNS support. The Zentri Android Discovery
app, available from the Google Play Store, demonstrates using the Android mDNS SDK to provide mDNS discovery on Android devices.
In a Nutshell
Network discovery provides a way for remote clients to resolve the IP address of the module on a local network using a standard protocol. mDNS has other features which allow the module to advertise various services including HTTP, TCP & UDP servers. ZentriOS supports configuration of these additional features with the mdns.service variable.
Commands
APIs
Variables
Broadcast Status Announcement
ZentriOS devices broadcast module properties in JSON format. The properties can be sent either as UDP packets to a UDP host or by a post request to an HTTP host. Properties include by default the IP address and the MAC address.
See Broadcast variables:
- broadcast.data
- broadcast.http.host
- broadcast.interface
- broadcast.interval
- broadcast.udp.ip
- broadcast.udp.port
See also the Broadcast UDP Packet Application Note.
Network Time Management
ZentriOS devices can obtain time data from an NTP (Network Time Protocol) server.
See NTP variables:
Sending Email using SMTP
ZentriOS devices can send email messages via an external SMTP server.
See email SMTP variables:
See the Sending a Secure SMTP Email application note.