mirror of
https://github.com/nodemcu/nodemcu-firmware.git
synced 2025-01-16 20:52:57 +08:00
af426d0315
* espconn: remove unused espconn code, take 1 This is the easiest part of https://github.com/nodemcu/nodemcu-firmware/issues/3004 . It removes a bunch of functions that were never called in our tree. * espconn: De-orbit espconn_gethostbyname Further work on https://github.com/nodemcu/nodemcu-firmware/issues/3004 While here, remove `mqtt`'s charming DNS-retry logic (which is neither shared with nor duplicated in other modules) and update its :connect() return value behavior and documentation. * espconn: remove scary global pktinfo A write-only global! How about that. * net: remove deprecated methods All the TLS stuff moved over there a long time ago, and net_createUDPSocket should just do what it says on the tin. * espconn_secure: remove ESPCONN_SERVER support We can barely function as a TLS client; being a TLS server seems like a real stretch. This code was never called from Lua anyway. * espconn_secure: more code removal * espconn_secure: simplify ssl options structure There is nothing "ssl_packet" about this structure. Get rid of the terrifying "pbuffer" pointer. Squash two structure types together and eliminate an unused field. * espconn_secure: refactor mbedtls_msg_info_load Split out espconn_mbedtls_parse, which we can use as part of our effort towards addressing https://github.com/nodemcu/nodemcu-firmware/issues/3032 * espconn_secure: introduce TLS cert/key callbacks The new feature part of https://github.com/nodemcu/nodemcu-firmware/issues/3032 Subsequent work will remove the old mechanism. * tls: add deprecation warnings * luacheck: net.ifinfo is a thing now * tls: remove use of espconn->reverse * mqtt: stop using espconn->reverse Instead, just place the espconn structure itself at the top of the user data. This enlarges the structure somewhat but removes one more layer of dynamic heap usage and NULL checks. While here, simplify the code a bit. * mqtt: remove redundant pointer to connect_info Everywhere we have the mqtt_state_t we also have the lmqtt_userdata. * mqtt: doc fixes * mqtt: note bug * tls: allow :on(...,nil) to unregister a callback
410 lines
13 KiB
Markdown
410 lines
13 KiB
Markdown
# TLS Module
|
|
| Since | Origin / Contributor | Maintainer | Source |
|
|
| :----- | :-------------------- | :---------- | :------ |
|
|
| 2016-12-15 | [PhoeniX](https://github.com/djphoenix) | [PhoeniX](https://github.com/djphoenix) | [tls.c](../../app/modules/tls.c)|
|
|
|
|
**SSL/TLS support**
|
|
|
|
!!! attention
|
|
The TLS module depends on the [net](net.md) module, it is a required dependency.
|
|
|
|
NodeMCU includes the open-source version of [mbed TLS library](https://tls.mbed.org/).
|
|
|
|
With the NodeMCU default configuration it supports **TLS** 1.2 with
|
|
most common features supported. Specifically, it provides:
|
|
|
|
- ciphers: AES, Camellia
|
|
- chaining modes: CBC, CFB, CTR, GCM
|
|
- digest algorithms: RIPEMD-160, SHA1, SHA2
|
|
- signature algorithms: RSA, deterministic ECDSA
|
|
- key exchange algorithms: DHE and ECDHE
|
|
- elliptic curves: secp{256,384}r1, secp256k1, bp{256,384}.
|
|
|
|
!!! warning
|
|
|
|
The severe memory constraints of the ESP8266 mean that the `tls` module
|
|
is by far better suited for communication with custom, purpose-built
|
|
endpoints with small certificate chains (ideally, even self-signed)
|
|
than it is with the Internet at large. By default, our mbedTLS
|
|
configuration requests TLS fragments of at most 4KiB and is unwilling
|
|
to process fragmented messages, meaning that the entire ServerHello,
|
|
which includes the server's certificate chain, must conform to this
|
|
limit. We do not believe it useful or easy to be fully compliant with
|
|
the TLS specification, which requires a 16KiB recieve buffer and,
|
|
therefore, 32KiB of heap within mbedTLS, even in the steady-state.
|
|
While it is possible to slightly raise the buffer sizes with custom
|
|
NodeMCU builds, connecting to endpoints out of your control will remain
|
|
a precarious position, and so we strongly suggest that TLS connections
|
|
be made only to endpoints under your control, whose TLS configurations
|
|
can ensure that their ServerHello messages are small. A reasonable
|
|
compromise is to have a "real" computer do TLS proxying for you; the
|
|
[socat](http://www.dest-unreach.org/socat/) program is one possible
|
|
mechanism of achieving such a "bent pipe" with TLS on both halves.
|
|
|
|
!!! warning
|
|
|
|
The TLS glue provided by Espressif provides no interface to TLS SNI.
|
|
As such, NodeMCU TLS should not be expected to function with endpoints
|
|
requiring the use of SNI, which is a growing fraction of the Internet
|
|
and includes, for example, Cloudflare sites using their "universal SSL"
|
|
service and other, similar "virtual" TLS servers. TLS servers to which
|
|
you wish NodeMCU to connect should have their own, dedicated IP/port
|
|
pair.
|
|
|
|
!!! warning
|
|
|
|
The TLS handshake is very heap intensive, requiring between 25 and 30
|
|
**kilobytes** of heap, even with our reduced buffer sizes. Some, but
|
|
not all, of that is made available again once the handshake has
|
|
completed and the connection is open. Because of this, we have
|
|
disabled mbedTLS's support for connection renegotiation. You may find
|
|
it necessary to restructure your application so that connections happen
|
|
early in boot when heap is relatively plentiful, with connection
|
|
failures inducing reboots. LFS may also be of utility in freeing up
|
|
heap space, should you wish to attempt re-establishing connections
|
|
without rebooting.
|
|
|
|
!!! tip
|
|
|
|
If possible, you will likely be much better served by using the ECDSA
|
|
signature and key exchange algorithms than by using RSA. An
|
|
increasingly large fraction of the Internet understands ECDSA, and most
|
|
server software can speak it as well. The much smaller key size (at
|
|
equivalent security!) is beneficial for NodeMCU's limited RAM.
|
|
|
|
https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations
|
|
details how to create ECDSA keys and certificates.
|
|
|
|
!!! tip
|
|
|
|
The complete configuration is stored in
|
|
[user_mbedtls.h](../../app/include/user_mbedtls.h). This is the file to
|
|
edit if you build your own firmware and want to change mbed TLS
|
|
behavior.
|
|
|
|
For a list of possible features have a look at the
|
|
[mbed TLS features page](https://tls.mbed.org/core-features).
|
|
|
|
This module handles certificate verification when SSL/TLS is in use.
|
|
|
|
## tls.createConnection()
|
|
|
|
Creates TLS connection.
|
|
|
|
#### Syntax
|
|
`tls.createConnection()`
|
|
|
|
#### Parameters
|
|
none
|
|
|
|
#### Returns
|
|
tls.socket sub module
|
|
|
|
#### Example
|
|
|
|
```lua
|
|
tls.createConnection()
|
|
```
|
|
|
|
# tls.socket Module
|
|
|
|
## tls.socket:close()
|
|
|
|
Closes socket.
|
|
|
|
#### Syntax
|
|
`close()`
|
|
|
|
#### Parameters
|
|
none
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### See also
|
|
[`tls.createConnection()`](#tlscreateconnection)
|
|
|
|
## tls.socket:connect()
|
|
|
|
Connect to a remote server.
|
|
|
|
#### Syntax
|
|
`connect(port, ip|domain)`
|
|
|
|
#### Parameters
|
|
- `port` port number
|
|
- `ip` IP address or domain name string
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### See also
|
|
[`tls.socket:on()`](#tlssocketon)
|
|
|
|
## tls.socket:getpeer()
|
|
|
|
Retrieve port and ip of peer.
|
|
|
|
#### Syntax
|
|
`getpeer()`
|
|
|
|
#### Parameters
|
|
none
|
|
|
|
#### Returns
|
|
- `ip` of peer
|
|
- `port` of peer
|
|
|
|
## tls.socket:hold()
|
|
|
|
Throttle data reception by placing a request to block the TCP receive function.
|
|
This request is not effective immediately, Espressif recommends to call it while reserving 5*1460 bytes of memory.
|
|
|
|
#### Syntax
|
|
`hold()`
|
|
|
|
#### Parameters
|
|
none
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### See also
|
|
[`tls.socket:unhold()`](#tlssocketunhold)
|
|
|
|
## tls.socket:on()
|
|
|
|
Register callback functions for specific events.
|
|
|
|
#### Syntax
|
|
`on(event, function())`
|
|
|
|
#### Parameters
|
|
- `event` string, which can be "dns", "connection", "reconnection", "disconnection", "receive" or "sent"
|
|
- `function(tls.socket[, string])` callback function. The first parameter is the socket.
|
|
If event is "receive", the second parameter is the received data as string.
|
|
If event is "reconnection", the second parameter is the reason of connection error (string).
|
|
If event is "dns", the second parameter will be either `nil` or a string rendering of the resolved address.
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### Example
|
|
```lua
|
|
srv = tls.createConnection()
|
|
srv:on("receive", function(sck, c) print(c) end)
|
|
srv:on("connection", function(sck, c)
|
|
-- Wait for connection before sending.
|
|
sck:send("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
|
|
end)
|
|
srv:connect(443,"google.com")
|
|
```
|
|
!!! note
|
|
The `receive` event is fired for every network frame! See details at [net.socket:on()](net.md#netsocketon).
|
|
|
|
#### See also
|
|
- [`tls.createConnection()`](#tlscreateconnection)
|
|
- [`tls.socket:hold()`](#tlssockethold)
|
|
|
|
## tls.socket:send()
|
|
|
|
Sends data to remote peer.
|
|
|
|
#### Syntax
|
|
`send(string)`
|
|
|
|
#### Parameters
|
|
- `string` data in string which will be sent to server
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### Note
|
|
|
|
Multiple consecutive `send()` calls aren't guaranteed to work (and often don't) as
|
|
network requests are treated as separate tasks by the SDK.
|
|
Instead, subscribe to the "sent" event on the socket and send additional data (or close) in that callback.
|
|
See [#730](https://github.com/nodemcu/nodemcu-firmware/issues/730#issuecomment-154241161) for details.
|
|
|
|
#### See also
|
|
[`tls.socket:on()`](#tlssocketon)
|
|
|
|
## tls.socket:unhold()
|
|
|
|
Unblock TCP receiving data by revocation of a preceding `hold()`.
|
|
|
|
#### Syntax
|
|
`unhold()`
|
|
|
|
#### Parameters
|
|
none
|
|
|
|
#### Returns
|
|
`nil`
|
|
|
|
#### See also
|
|
[`tls.socket:hold()`](#tlssockethold)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# tls.cert Module
|
|
|
|
## tls.cert.verify()
|
|
|
|
Controls the certificate verification process when the NodeMCU makes a secure connection.
|
|
|
|
#### Syntax
|
|
`tls.cert.verify(enable)`
|
|
|
|
`tls.cert.verify(pemdata[, pemdata])`
|
|
|
|
`tls.cert.verify(callback)`
|
|
|
|
#### Parameters
|
|
- `enable` A boolean which indicates whether verification should be enabled or not. The default at boot is `false`.
|
|
- `pemdata` A string containing the CA certificate to use for verification. There can be several of these.
|
|
|
|
- `callback` A Lua function which returns TLS keys and certificates for use
|
|
with connections. The callback should expect one, integer argument; for
|
|
value k, the callback should return the k-th CA certificate (in either DER or
|
|
PEM form) it wishes to use to validate the remote endpoint, or `nil` if no
|
|
such CA certificate exists. If no certificates are returned, the device will
|
|
not validate the remote endpoint.
|
|
|
|
#### Returns
|
|
`true` if it worked.
|
|
|
|
Can throw a number of errors if invalid data is supplied.
|
|
|
|
#### Example
|
|
Make a secure https connection and verify that the certificate chain is valid.
|
|
```
|
|
tls.cert.verify(true)
|
|
http.get("https://example.com/info", nil, function (code, resp) print(code, resp) end)
|
|
```
|
|
|
|
Load a certificate into the flash chip and make a request. This is the
|
|
[IdenTrust](https://www.identrust.co.uk/) `DST Root CA X3` certificate; it is
|
|
used, for example, by [letsencrypt](https://letsencrypt.org), for their
|
|
intermediate X3 authority; letsencrypt is one option for obtaining your own SSL
|
|
certificates free of cost.
|
|
|
|
```
|
|
tls.cert.verify([[
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
|
|
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
|
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
|
|
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
|
|
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
|
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
|
|
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
|
|
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
|
|
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
|
|
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
|
|
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
|
|
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
|
|
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
|
|
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
|
|
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
|
|
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
|
|
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
|
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
|
-----END CERTIFICATE-----
|
|
]])
|
|
|
|
http.get("https://letsencrypt.org/", nil, function (code, resp) print(code, resp) end)
|
|
```
|
|
|
|
#### Notes
|
|
The certificate needed for verification is stored in the flash chip. The `tls.cert.verify` call with `true`
|
|
enables verification against the value stored in the flash.
|
|
|
|
The certificate can be loaded into the flash chip in two ways -- one at firmware build time, and the other at initial boot
|
|
of the firmware. In order to load the certificate at build time, just place a file containing the CA certificate (in PEM format)
|
|
at `server-ca.crt` in the root of the nodemcu-firmware build tree. The build scripts will incorporate this into the resulting
|
|
firmware image.
|
|
|
|
The alternative approach is easier for development, and that is to supply the PEM data as a string value to `tls.cert.verify`. This
|
|
will store the certificate into the flash chip and turn on verification for that certificate. Subsequent boots of the ESP can then
|
|
use `tls.cert.verify(true)` and use the stored certificate.
|
|
|
|
The `callback`-based version will override the in-flash information until the callback
|
|
is unregistered *or* one of the other call forms is made.
|
|
|
|
## tls.cert.auth()
|
|
|
|
Controls the client key and certificate used when the ESP creates a TLS connection (for example,
|
|
through `tls.createConnection` or `https` or `MQTT` connections with `secure = true`).
|
|
|
|
#### Syntax
|
|
`tls.cert.auth(enable)`
|
|
|
|
`tls.cert.auth(pemdata[, pemdata])`
|
|
|
|
`tls.cert.auth(callback)`
|
|
|
|
#### Parameters
|
|
- `enable` A boolean, specifying whether subsequent TLS connections will present a client certificate. The default at boot is `false`.
|
|
- `pemdata` Two strings, the first containing the PEM-encoded client's certificate and the second containing the PEM-encoded client's private key.
|
|
|
|
- `callback` A Lua function which returns TLS keys and certificates for use with connections.
|
|
The callback should expect one, integer argument; if that is 0, the callback should return
|
|
the device's private key. Otherwise, for argument k, the callback should return the k-th
|
|
certificate (in either DER or PEM form) in the devices' certificate chain.
|
|
|
|
#### Returns
|
|
`true` if it worked.
|
|
|
|
Can throw a number of errors if invalid data is supplied.
|
|
|
|
#### Example
|
|
Open an MQTT client.
|
|
```
|
|
tls.cert.auth(true)
|
|
tls.cert.verify(true)
|
|
|
|
m = mqtt.Client('basicPubSub', 1500, "admin", "admin", 1)
|
|
```
|
|
For further discussion see https://github.com/nodemcu/nodemcu-firmware/issues/2576
|
|
|
|
Load a certificate into the flash chip.
|
|
|
|
```
|
|
tls.cert.auth([[
|
|
-----BEGIN CERTIFICATE-----
|
|
CLIENT CERTIFICATE String (PEM file)
|
|
-----END CERTIFICATE-----
|
|
]]
|
|
,
|
|
[[
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
CLIENT PRIVATE KEY String (PEM file)
|
|
-----END RSA PRIVATE KEY-----
|
|
]])
|
|
```
|
|
|
|
#### Notes
|
|
The certificate needed for proofing is stored in the flash chip. The `tls.cert.auth` call with `true`
|
|
enables proofing against the value stored in the flash.
|
|
|
|
The certificate can not be defined at firmware build time but it can be loaded into the flash chip at initial boot of the firmware.
|
|
It can be supplied by passing the PEM data as a string value to `tls.cert.auth`. This
|
|
will store the certificate into the flash chip and turn on proofing with that certificate.
|
|
Subsequent boots of the ESP can then use `tls.cert.auth(true)` and use the stored certificate.
|
|
|
|
The `callback`-based version will override the in-flash information until the callback
|
|
is unregistered *or* one of the other call forms is made.
|
|
|
|
# tls.setDebug function
|
|
|
|
mbedTLS can be compiled with debug support. If so, the tls.setDebug
|
|
function is mapped to the `mbedtls_debug_set_threshold` function and
|
|
can be used to enable or disable debugging spew to the console.
|
|
See mbedTLS's documentation for more details.
|