Skip to content

Commit

Permalink
espconn_secure: introduce TLS cert/key callbacks
Browse files Browse the repository at this point in the history
The new feature part of nodemcu#3032
Subsequent work will remove the old mechanism.
  • Loading branch information
nwf committed Apr 2, 2020
1 parent e1fdc2f commit 5a57f66
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 4 deletions.
3 changes: 3 additions & 0 deletions app/include/sys/espconn_mbedtls.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ struct ssl_options {
uint16 buffer_size;
ssl_sector cert_ca_sector;
ssl_sector cert_req_sector;

int cert_verify_callback;
int cert_auth_callback;
};

#define SSL_KEEP_INTVL 1
Expand Down
88 changes: 86 additions & 2 deletions app/mbedtls/app/espconn_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

#include "mem.h"

#include "lauxlib.h"

#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif
Expand Down Expand Up @@ -476,6 +478,77 @@ espconn_mbedtls_parse(mbedtls_msg *msg, mbedtls_auth_type auth_type, const uint8
return (ret >= 0);
}

/*
* Three-way return:
* 0 for no commitment, -1 to fail the connection, 1 on success
*/
static int
nodemcu_tls_cert_get(mbedtls_msg *msg, mbedtls_auth_type auth_type)
{
int cbref;
int cbarg;
int loop = 0;

switch(auth_type) {
case ESPCONN_CERT_AUTH:
loop = 1;
cbarg = 1;
cbref = ssl_client_options.cert_verify_callback;
break;
case ESPCONN_PK:
loop = 0;
cbarg = 0;
cbref = ssl_client_options.cert_auth_callback;
break;
case ESPCONN_CERT_OWN:
loop = 1;
cbarg = 1;
cbref = ssl_client_options.cert_auth_callback;
break;
default:
return 0;
}

if (cbref == LUA_NOREF)
return 0;

lua_State *L = lua_getstate();

do {
lua_rawgeti(L, LUA_REGISTRYINDEX, cbref);
lua_pushinteger(L, cbarg);
if (lua_pcall(L, 1, 1, 0) != 0) {
/* call failure; fail the connection attempt */
return -1;
}
if (lua_isnil(L, -1)) {
/* nil return; stop iteration */
break;
}
size_t resl;
const char *res = lua_tolstring(L, -1, &resl);
if (res == NULL) {
/* conversion failure; fail the connection attempt */
lua_pop(L, 1);
return -1;
}
if (!espconn_mbedtls_parse(msg, auth_type, res, resl+1)) {
/* parsing failure; fail the connction attempt */
lua_pop(L, 1);
return -1;
}

/*
* Otherwise, parsing successful; if this is a loopy kind of
* callback, then increment the argument and loop.
*/
lua_pop(L, 1);
cbarg++;
} while (loop);

return 1;
}

static bool mbedtls_msg_info_load(mbedtls_msg *msg, mbedtls_auth_type auth_type)
{
const char* const begin = "-----BEGIN";
Expand All @@ -487,6 +560,14 @@ static bool mbedtls_msg_info_load(mbedtls_msg *msg, mbedtls_auth_type auth_type)
size_t load_len = 0;
file_param file_param;

/* Override with Lua callbacks, if registered */
switch(nodemcu_tls_cert_get(msg, auth_type)) {
case -1:
return false;
case 1:
return true;
}

bzero(&file_param, sizeof(file_param));

again:
Expand Down Expand Up @@ -551,15 +632,18 @@ static bool mbedtls_msg_config(mbedtls_msg *msg)
lwIP_REQUIRE_NOERROR(ret, exit);

/*Load the certificate and private RSA key*/
if (ssl_client_options.cert_req_sector.flag) {
if (ssl_client_options.cert_req_sector.flag
|| (ssl_client_options.cert_auth_callback != LUA_NOREF)) {

load_flag = mbedtls_msg_info_load(msg, ESPCONN_CERT_OWN);
lwIP_REQUIRE_ACTION(load_flag, exit, ret = ESPCONN_MEM);
load_flag = mbedtls_msg_info_load(msg, ESPCONN_PK);
lwIP_REQUIRE_ACTION(load_flag, exit, ret = ESPCONN_MEM);
}

/*Load the trusted CA*/
if(ssl_client_options.cert_ca_sector.flag) {
if(ssl_client_options.cert_ca_sector.flag
|| (ssl_client_options.cert_verify_callback != LUA_NOREF)) {
load_flag = mbedtls_msg_info_load(msg, ESPCONN_CERT_AUTH);
lwIP_REQUIRE_ACTION(load_flag, exit, ret = ESPCONN_MEM);
}
Expand Down
4 changes: 3 additions & 1 deletion app/mbedtls/app/espconn_secure.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "ets_sys.h"
#include "os_type.h"

#include "lauxlib.h"

#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif
Expand All @@ -39,7 +41,7 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;

#include "sys/espconn_mbedtls.h"

struct ssl_options ssl_client_options = {SSL_BUFFER_SIZE, 0, false, 0, false};
struct ssl_options ssl_client_options = {SSL_BUFFER_SIZE, 0, false, 0, false, LUA_NOREF, LUA_NOREF};

/******************************************************************************
* FunctionName : espconn_encry_connect
Expand Down
22 changes: 22 additions & 0 deletions app/modules/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,17 @@ static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_m
// Lua: tls.cert.auth(true / false)
static int tls_cert_auth(lua_State *L)
{
if (ssl_client_options.cert_auth_callback != LUA_NOREF) {
lua_unref(L, ssl_client_options.cert_auth_callback);
ssl_client_options.cert_auth_callback = LUA_NOREF;
}
if ((lua_type(L, 1) == LUA_TFUNCTION)
|| (lua_type(L, 1) == LUA_TLIGHTFUNCTION)) {
ssl_client_options.cert_auth_callback = lua_ref(L, 1);
lua_pushboolean(L, true);
return 1;
}

int enable;

uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &tls_client_cert_area[0]);
Expand Down Expand Up @@ -570,6 +581,17 @@ static int tls_cert_auth(lua_State *L)
// Lua: tls.cert.verify(true / false)
static int tls_cert_verify(lua_State *L)
{
if (ssl_client_options.cert_auth_callback != LUA_NOREF) {
lua_unref(L, ssl_client_options.cert_verify_callback);
ssl_client_options.cert_verify_callback = LUA_NOREF;
}
if ((lua_type(L, 1) == LUA_TFUNCTION)
|| (lua_type(L, 1) == LUA_TLIGHTFUNCTION)) {
ssl_client_options.cert_verify_callback = lua_ref(L, 1);
lua_pushboolean(L, true);
return 1;
}

int enable;

uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &tls_server_cert_area[0]);
Expand Down
22 changes: 21 additions & 1 deletion docs/modules/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,19 @@ Controls the certificate verification process when the NodeMCU makes a secure co

`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.

Expand Down Expand Up @@ -325,6 +334,9 @@ The alternative approach is easier for development, and that is to supply the PE
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,
Expand All @@ -335,10 +347,17 @@ through `tls.createConnection` or `https` or `MQTT` connections with `secure = t

`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.

Expand Down Expand Up @@ -379,7 +398,8 @@ It can be supplied by passing the PEM data as a string value to `tls.cert.auth`.
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

Expand Down

0 comments on commit 5a57f66

Please sign in to comment.