Merge pull request #1125 from pjsg/ssl-verify
Adds support for verifying the certificate offered by an SSL server.
This commit is contained in:
commit
3490ffb285
|
@ -1,3 +1,4 @@
|
|||
sdk/
|
||||
cache/
|
||||
user_config.h
|
||||
server-ca.crt
|
||||
|
|
15
Makefile
15
Makefile
|
@ -132,7 +132,7 @@ CCFLAGS += \
|
|||
# -Wall
|
||||
|
||||
ifneq ($(wildcard $(TOP_DIR)/user_config.h),)
|
||||
INCLUDES := $(INCLUDES) -include "$(TOP_DIR)/user_config.h"
|
||||
INCLUDES += -include "$(TOP_DIR)/user_config.h"
|
||||
endif
|
||||
|
||||
CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
|
||||
|
@ -175,7 +175,7 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out
|
|||
# Should be done in top-level makefile only
|
||||
#
|
||||
|
||||
all: sdk_extracted .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
|
||||
all: sdk_extracted pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
|
||||
|
||||
.PHONY: sdk_extracted
|
||||
|
||||
|
@ -222,6 +222,17 @@ endif
|
|||
endif
|
||||
endif
|
||||
|
||||
.PHONY: pre_build
|
||||
|
||||
pre_build:
|
||||
ifneq ($(wildcard $(TOP_DIR)/server-ca.crt),)
|
||||
python $(TOP_DIR)/tools/make_server_cert.py $(TOP_DIR)/server-ca.crt > $(TOP_DIR)/app/modules/server-ca.crt.h
|
||||
DEFINES += -DHAVE_SSL_SERVER_CRT=\"server-ca.crt.h\"
|
||||
else
|
||||
@-rm -f $(TOP_DIR)/app/modules/server-ca.crt.h
|
||||
endif
|
||||
|
||||
|
||||
$(OBJODIR)/%.o: %.c
|
||||
@mkdir -p $(OBJODIR);
|
||||
$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<
|
||||
|
|
|
@ -437,10 +437,10 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
|
|||
{
|
||||
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
|
||||
|
||||
struct espconn * conn = (struct espconn *) os_malloc( sizeof(struct espconn) );
|
||||
struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) );
|
||||
conn->type = ESPCONN_TCP;
|
||||
conn->state = ESPCONN_NONE;
|
||||
conn->proto.tcp = (esp_tcp *) os_malloc( sizeof(esp_tcp) );
|
||||
conn->proto.tcp = (esp_tcp *) os_zalloc( sizeof(esp_tcp) );
|
||||
conn->proto.tcp->local_port = espconn_port();
|
||||
conn->proto.tcp->remote_port = req->port;
|
||||
conn->reverse = req;
|
||||
|
@ -473,7 +473,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s
|
|||
{
|
||||
HTTPCLIENT_DEBUG( "DNS request\n" );
|
||||
|
||||
request_args_t * req = (request_args_t *) os_malloc( sizeof(request_args_t) );
|
||||
request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) );
|
||||
req->hostname = esp_strdup( hostname );
|
||||
req->port = port;
|
||||
req->secure = secure;
|
||||
|
@ -558,6 +558,11 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
|
|||
colon = NULL; /* Limit the search to characters before the path. */
|
||||
}
|
||||
|
||||
if (path - url >= sizeof(hostname)) {
|
||||
HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( colon == NULL ) /* The port is not present. */
|
||||
{
|
||||
os_memcpy( hostname, url, path - url );
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
server-ca.crt.h
|
|
@ -3,6 +3,7 @@
|
|||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "platform.h"
|
||||
#include "lmem.h"
|
||||
|
||||
#include "c_string.h"
|
||||
#include "c_stdlib.h"
|
||||
|
@ -13,18 +14,19 @@
|
|||
#include "espconn.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#ifdef CLIENT_SSL_ENABLE
|
||||
unsigned char *default_certificate;
|
||||
unsigned int default_certificate_len = 0;
|
||||
unsigned char *default_private_key;
|
||||
unsigned int default_private_key_len = 0;
|
||||
#endif
|
||||
|
||||
#define TCP ESPCONN_TCP
|
||||
#define UDP ESPCONN_UDP
|
||||
|
||||
static ip_addr_t host_ip; // for dns
|
||||
|
||||
#ifdef HAVE_SSL_SERVER_CRT
|
||||
#include HAVE_SSL_SERVER_CRT
|
||||
#else
|
||||
__attribute__((section(".servercert.flash"))) unsigned char net_server_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
|
||||
#endif
|
||||
|
||||
__attribute__((section(".clientcert.flash"))) unsigned char net_client_cert_area[INTERNAL_FLASH_SECTOR_SIZE];
|
||||
|
||||
#if 0
|
||||
static int expose_array(lua_State* L, char *array, unsigned short len);
|
||||
#endif
|
||||
|
@ -1432,6 +1434,201 @@ static int net_multicastLeave( lua_State* L )
|
|||
return net_multicastJoinLeave(L,0);
|
||||
}
|
||||
|
||||
// Returns NULL on success, error message otherwise
|
||||
static const char *append_pem_blob(const char *pem, const char *type, uint8_t **buffer_p, uint8_t *buffer_limit, const char *name) {
|
||||
char unb64[256];
|
||||
memset(unb64, 0xff, sizeof(unb64));
|
||||
int i;
|
||||
for (i = 0; i < 64; i++) {
|
||||
unb64["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
|
||||
}
|
||||
|
||||
if (!pem) {
|
||||
return "No PEM blob";
|
||||
}
|
||||
|
||||
// Scan for -----BEGIN CERT
|
||||
pem = strstr(pem, "-----BEGIN ");
|
||||
if (!pem) {
|
||||
return "No PEM header";
|
||||
}
|
||||
|
||||
if (strncmp(pem + 11, type, strlen(type))) {
|
||||
return "Wrong PEM type";
|
||||
}
|
||||
|
||||
pem = strchr(pem, '\n');
|
||||
if (!pem) {
|
||||
return "Incorrect PEM format";
|
||||
}
|
||||
//
|
||||
// Base64 encoded data starts here
|
||||
// Get all the base64 data into a single buffer....
|
||||
// We will use the back end of the buffer....
|
||||
//
|
||||
|
||||
uint8_t *buffer = *buffer_p;
|
||||
|
||||
uint8_t *dest = buffer + 32 + 2; // Leave space for name and length
|
||||
int bitcount = 0;
|
||||
int accumulator = 0;
|
||||
for (; *pem && dest < buffer_limit; pem++) {
|
||||
int val = unb64[*(uint8_t*) pem];
|
||||
if (val & 0xC0) {
|
||||
// not a base64 character
|
||||
if (isspace(*(uint8_t*) pem)) {
|
||||
continue;
|
||||
}
|
||||
if (*pem == '=') {
|
||||
// just ignore -- at the end
|
||||
bitcount = 0;
|
||||
continue;
|
||||
}
|
||||
if (*pem == '-') {
|
||||
break;
|
||||
}
|
||||
return "Invalid character in PEM";
|
||||
} else {
|
||||
bitcount += 6;
|
||||
accumulator = (accumulator << 6) + val;
|
||||
if (bitcount >= 8) {
|
||||
bitcount -= 8;
|
||||
*dest++ = accumulator >> bitcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dest >= buffer_limit || strncmp(pem, "-----END ", 9) || strncmp(pem + 9, type, strlen(type)) || bitcount) {
|
||||
return "Invalid PEM format data";
|
||||
}
|
||||
size_t len = dest - (buffer + 32 + 2);
|
||||
|
||||
memset(buffer, 0, 32);
|
||||
strcpy(buffer, name);
|
||||
buffer[32] = len & 0xff;
|
||||
buffer[33] = (len >> 8) & 0xff;
|
||||
*buffer_p = dest;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *fill_page_with_pem(lua_State *L, const unsigned char *flash_memory, int flash_offset, const char **types, const char **names)
|
||||
{
|
||||
uint8_t *buffer = luaM_malloc(L, INTERNAL_FLASH_SECTOR_SIZE);
|
||||
uint8_t *buffer_base = buffer;
|
||||
uint8_t *buffer_limit = buffer + INTERNAL_FLASH_SECTOR_SIZE;
|
||||
|
||||
int argno;
|
||||
|
||||
for (argno = 1; argno <= lua_gettop(L) && types[argno - 1]; argno++) {
|
||||
const char *pem = lua_tostring(L, argno);
|
||||
|
||||
const char *error = append_pem_blob(pem, types[argno - 1], &buffer, buffer_limit, names[argno - 1]);
|
||||
if (error) {
|
||||
luaM_free(L, buffer_base);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
memset(buffer, 0xff, buffer_limit - buffer);
|
||||
|
||||
// Lets see if it matches what is already there....
|
||||
if (c_memcmp(buffer_base, flash_memory, INTERNAL_FLASH_SECTOR_SIZE) != 0) {
|
||||
// Starts being dangerous
|
||||
if (platform_flash_erase_sector(flash_offset / INTERNAL_FLASH_SECTOR_SIZE) != PLATFORM_OK) {
|
||||
luaM_free(L, buffer_base);
|
||||
return "Failed to erase sector";
|
||||
}
|
||||
if (platform_s_flash_write(buffer_base, flash_offset, INTERNAL_FLASH_SECTOR_SIZE) != INTERNAL_FLASH_SECTOR_SIZE) {
|
||||
luaM_free(L, buffer_base);
|
||||
return "Failed to write sector";
|
||||
}
|
||||
// ends being dangerous
|
||||
}
|
||||
|
||||
luaM_free(L, buffer_base);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Lua: net.cert.auth(true / false | PEM data [, PEM data] )
|
||||
static int net_cert_auth(lua_State *L)
|
||||
{
|
||||
int enable;
|
||||
|
||||
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_client_cert_area[0]);
|
||||
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
|
||||
// THis should never happen
|
||||
return luaL_error( L, "bad offset" );
|
||||
}
|
||||
|
||||
if (lua_type(L, 1) == LUA_TSTRING) {
|
||||
const char *types[3] = { "CERTIFICATE", "RSA PRIVATE KEY", NULL };
|
||||
const char *names[2] = { "certificate", "private_key" };
|
||||
const char *error = fill_page_with_pem(L, &net_client_cert_area[0], flash_offset, types, names);
|
||||
if (error) {
|
||||
return luaL_error(L, error);
|
||||
}
|
||||
|
||||
enable = 1;
|
||||
} else {
|
||||
enable = lua_toboolean(L, 1);
|
||||
}
|
||||
|
||||
bool rc;
|
||||
|
||||
if (enable) {
|
||||
// See if there is a cert there
|
||||
if (net_client_cert_area[0] == 0x00 || net_client_cert_area[0] == 0xff) {
|
||||
return luaL_error( L, "no certificates found" );
|
||||
}
|
||||
rc = espconn_secure_cert_req_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
|
||||
} else {
|
||||
rc = espconn_secure_cert_req_disable(1);
|
||||
}
|
||||
|
||||
lua_pushboolean(L, rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: net.cert.verify(true / false | PEM data [, PEM data] )
|
||||
static int net_cert_verify(lua_State *L)
|
||||
{
|
||||
int enable;
|
||||
|
||||
uint32_t flash_offset = platform_flash_mapped2phys((uint32_t) &net_server_cert_area[0]);
|
||||
if ((flash_offset & 0xfff) || flash_offset > 0xff000 || INTERNAL_FLASH_SECTOR_SIZE != 0x1000) {
|
||||
// THis should never happen
|
||||
return luaL_error( L, "bad offset" );
|
||||
}
|
||||
|
||||
if (lua_type(L, 1) == LUA_TSTRING) {
|
||||
const char *types[2] = { "CERTIFICATE", NULL };
|
||||
const char *names[1] = { "certificate" };
|
||||
|
||||
const char *error = fill_page_with_pem(L, &net_server_cert_area[0], flash_offset, types, names);
|
||||
if (error) {
|
||||
return luaL_error(L, error);
|
||||
}
|
||||
|
||||
enable = 1;
|
||||
} else {
|
||||
enable = lua_toboolean(L, 1);
|
||||
}
|
||||
|
||||
bool rc;
|
||||
|
||||
if (enable) {
|
||||
// See if there is a cert there
|
||||
if (net_server_cert_area[0] == 0x00 || net_server_cert_area[0] == 0xff) {
|
||||
return luaL_error( L, "no certificates found" );
|
||||
}
|
||||
rc = espconn_secure_ca_enable(1, flash_offset / INTERNAL_FLASH_SECTOR_SIZE);
|
||||
} else {
|
||||
rc = espconn_secure_ca_disable(1);
|
||||
}
|
||||
|
||||
lua_pushboolean(L, rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: s = net.dns.setdnsserver(ip_addr, [index])
|
||||
static int net_setdnsserver( lua_State* L )
|
||||
|
@ -1539,6 +1736,14 @@ static const LUA_REG_TYPE net_array_map[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
static const LUA_REG_TYPE net_cert_map[] = {
|
||||
{ LSTRKEY( "verify" ), LFUNCVAL( net_cert_verify ) },
|
||||
#ifdef CLIENT_SSL_CERT_AUTH_ENABLE
|
||||
{ LSTRKEY( "auth" ), LFUNCVAL( net_cert_auth ) },
|
||||
#endif
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
static const LUA_REG_TYPE net_dns_map[] = {
|
||||
{ LSTRKEY( "setdnsserver" ), LFUNCVAL( net_setdnsserver ) },
|
||||
{ LSTRKEY( "getdnsserver" ), LFUNCVAL( net_getdnsserver ) },
|
||||
|
@ -1552,6 +1757,9 @@ static const LUA_REG_TYPE net_map[] = {
|
|||
{ LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) },
|
||||
{ LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) },
|
||||
{ LSTRKEY( "dns" ), LROVAL( net_dns_map ) },
|
||||
#ifdef CLIENT_SSL_ENABLE
|
||||
{ LSTRKEY( "cert" ), LROVAL(net_cert_map) },
|
||||
#endif
|
||||
{ LSTRKEY( "TCP" ), LNUMVAL( TCP ) },
|
||||
{ LSTRKEY( "UDP" ), LNUMVAL( UDP ) },
|
||||
{ LSTRKEY( "__metatable" ), LROVAL( net_map ) },
|
||||
|
|
|
@ -286,3 +286,100 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open
|
|||
#### See also
|
||||
[`net.dns:getdnsserver()`](#netdnsgetdnsserver)
|
||||
|
||||
# net.cert Module
|
||||
|
||||
This controls certificate verification when SSL is in use.
|
||||
|
||||
## net.cert.verify()
|
||||
|
||||
Controls the vertificate verification process when the Nodemcu makes a secure connection.
|
||||
|
||||
#### Syntax
|
||||
`net.cert.verify(enable)`
|
||||
|
||||
`net.cert.verify(pemdata)`
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
```
|
||||
net.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 [startssl](https://startssl.com) root certificate. They provide free
|
||||
certificates.
|
||||
|
||||
```
|
||||
net.cert.verify([[
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
|
||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
|
||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
|
||||
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
|
||||
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
|
||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
|
||||
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
|
||||
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
|
||||
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
|
||||
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
|
||||
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
|
||||
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
|
||||
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
|
||||
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
|
||||
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
|
||||
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
|
||||
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
|
||||
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
|
||||
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
|
||||
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
|
||||
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
|
||||
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
|
||||
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
|
||||
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
|
||||
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
|
||||
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
|
||||
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
|
||||
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
|
||||
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
|
||||
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
|
||||
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
|
||||
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
|
||||
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
|
||||
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
|
||||
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
|
||||
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
|
||||
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
|
||||
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
|
||||
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
|
||||
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
|
||||
-----END CERTIFICATE-----
|
||||
]])
|
||||
|
||||
http.get("https://pskreporter.info/gen404", nil, function (code, resp) print(code, resp) end)
|
||||
```
|
||||
|
||||
|
||||
#### Notes
|
||||
The certificate needed for verification is stored in the flash chip. The `net.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 `net.cert.verify`. This
|
||||
will store the certificate into the flash chip and turn on verification for that certificate. Subsequent boots of the nodemcu can then
|
||||
use `net.cert.verify(true)` and use the stored certificate.
|
||||
|
||||
|
|
|
@ -71,9 +71,11 @@ SECTIONS
|
|||
_dport0_data_end = ABSOLUTE(.);
|
||||
} >dport0_0_seg :dport0_0_phdr
|
||||
|
||||
.irom0.text : ALIGN(4)
|
||||
.irom0.text : ALIGN(0x1000)
|
||||
{
|
||||
_irom0_text_start = ABSOLUTE(.);
|
||||
*(.servercert.flash)
|
||||
*(.clientcert.flash)
|
||||
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
|
||||
*(.literal.* .text.*)
|
||||
*(.rodata*)
|
||||
|
@ -95,7 +97,7 @@ SECTIONS
|
|||
|
||||
_irom0_text_end = ABSOLUTE(.);
|
||||
_flash_used_end = ABSOLUTE(.);
|
||||
} >irom0_0_seg :irom0_0_phdr
|
||||
} >irom0_0_seg :irom0_0_phdr =0xffffffff
|
||||
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
TLS.*
|
||||
private_key.h
|
||||
cert.h
|
|
@ -0,0 +1,42 @@
|
|||
import os
|
||||
|
||||
|
||||
class Cert(object):
|
||||
def __init__(self, name, buff):
|
||||
self.name = name
|
||||
self.len = len(buff)
|
||||
self.buff = buff
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
out_str = ['\0']*32
|
||||
for i in range(len(self.name)):
|
||||
out_str[i] = self.name[i]
|
||||
out_str = "".join(out_str)
|
||||
out_str += str(chr(self.len & 0xFF))
|
||||
out_str += str(chr((self.len & 0xFF00) >> 8))
|
||||
out_str += self.buff
|
||||
return out_str
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
cert_list = []
|
||||
file_list = os.listdir(os.getcwd())
|
||||
cert_file_list = []
|
||||
for _file in file_list:
|
||||
pos = _file.find(".cer")
|
||||
if pos != -1:
|
||||
cert_file_list.append(_file[:pos])
|
||||
|
||||
for cert_file in cert_file_list:
|
||||
with open(cert_file+".cer", 'rb') as f:
|
||||
buff = f.read()
|
||||
cert_list.append(Cert(cert_file, buff))
|
||||
with open('esp_ca_cert.bin', 'wb+') as f:
|
||||
for _cert in cert_list:
|
||||
f.write("%s" % _cert)
|
||||
pass
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
import argparse
|
||||
import base64
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
class Cert(object):
|
||||
def __init__(self, name, buff):
|
||||
self.name = name
|
||||
self.len = len(buff)
|
||||
self.buff = buff
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
out_str = ['\0']*32
|
||||
for i in range(len(self.name)):
|
||||
out_str[i] = self.name[i]
|
||||
out_str = "".join(out_str)
|
||||
out_str += str(chr(self.len & 0xFF))
|
||||
out_str += str(chr((self.len & 0xFF00) >> 8))
|
||||
out_str += self.buff
|
||||
return out_str
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Convert PEM file(s) into C source file.')
|
||||
|
||||
parser.add_argument('--section',
|
||||
default='.servercert.flash',
|
||||
help='specify the section for the data (default is .servercert.flash)')
|
||||
|
||||
parser.add_argument('--name',
|
||||
default='net_server_cert_area',
|
||||
help='specify the variable name for the data (default is net_server_cert_area)')
|
||||
|
||||
parser.add_argument('file', nargs='+',
|
||||
help='One or more PEM files')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
cert_list = []
|
||||
cert_file_list = []
|
||||
|
||||
for cert_file in args.file:
|
||||
with open(cert_file, 'r') as f:
|
||||
buff = f.read()
|
||||
m = re.search(r"-----BEGIN ([A-Z ]+)-----([^-]+?)-----END \1-----", buff, flags=re.DOTALL)
|
||||
if not m:
|
||||
sys.exit("Input file was not in PEM format")
|
||||
|
||||
if "----BEGIN" in buff[m.end(0):]:
|
||||
sys.exit("Input file contains more than one PEM object")
|
||||
|
||||
cert_list.append(Cert(m.group(1), base64.b64decode(''.join(m.group(2).split()))))
|
||||
|
||||
print '__attribute__((section("%s"))) unsigned char %s[INTERNAL_FLASH_SECTOR_SIZE] = {' % (args.section, args.name)
|
||||
for _cert in cert_list:
|
||||
col = 0
|
||||
for ch in str(_cert):
|
||||
print ("0x%02x," % ord(ch)),
|
||||
if col & 15 == 15:
|
||||
print
|
||||
col = col + 1
|
||||
print '\n0xff};\n'
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,11 +1,92 @@
|
|||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the axTLS project nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
#
|
||||
# Generate the certificates and keys for testing.
|
||||
#
|
||||
|
||||
PROJECT_NAME="Nodemcu Project"
|
||||
|
||||
# Generate the openssl configuration files.
|
||||
cat > ca_cert.conf << EOF
|
||||
[ req ]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
O = $PROJECT_NAME Dodgy Certificate Authority
|
||||
EOF
|
||||
|
||||
cat > certs.conf << EOF
|
||||
[ req ]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
O = $PROJECT_NAME
|
||||
CN = Nodemcu Client cert
|
||||
EOF
|
||||
|
||||
cat > device_cert.conf << EOF
|
||||
[ req ]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
O = $PROJECT_NAME Device Certificate
|
||||
EOF
|
||||
|
||||
# private key generation
|
||||
openssl genrsa -out TLS.ca_key.pem 2048
|
||||
openssl genrsa -out TLS.key_2048.pem 2048
|
||||
|
||||
# convert private keys into DER format
|
||||
openssl rsa -in TLS.key_2048.pem -out TLS.key_2048 -outform DER
|
||||
|
||||
# cert requests
|
||||
openssl req -out TLS.ca_x509.req -sha256 -key TLS.ca_key.pem -new \
|
||||
-config ./ca_cert.conf
|
||||
openssl req -out TLS.x509_2048.req -sha256 -key TLS.key_2048.pem -new \
|
||||
-config ./certs.conf
|
||||
|
||||
# generate the actual certs.
|
||||
openssl x509 -req -in TLS.ca_x509.req -sha256 -out TLS.ca_x509.pem \
|
||||
-sha1 -days 5000 -signkey TLS.ca_key.pem
|
||||
openssl x509 -req -in TLS.x509_2048.req -sha256 -out TLS.x509_2048.pem \
|
||||
-sha1 -CAcreateserial -days 5000 \
|
||||
-CA TLS.ca_x509.pem -CAkey TLS.ca_key.pem
|
||||
|
||||
# some cleanup
|
||||
rm TLS*.req
|
||||
rm *.conf
|
||||
|
||||
openssl x509 -in TLS.ca_x509.pem -outform DER -out TLS.ca_x509.cer
|
||||
openssl x509 -in TLS.x509_2048.pem -outform DER -out TLS.x509_2048.cer
|
||||
|
||||
#
|
||||
# Generate the certificates and keys for encrypt.
|
||||
#
|
||||
|
||||
# set default cert for use in the client
|
||||
xxd -i client.cer | sed -e \
|
||||
"s/client_cer/default_certificate/" > cert.h
|
||||
xxd -i TLS.x509_2048.cer | sed -e \
|
||||
"s/TLS_x509_2048_cer/default_certificate/" > cert.h
|
||||
# set default key for use in the server
|
||||
xxd -i server.key_1024 | sed -e \
|
||||
"s/server_key_1024/default_private_key/" > private_key.h
|
||||
xxd -i TLS.key_2048 | sed -e \
|
||||
"s/TLS_key_2048/default_private_key/" > private_key.h
|
||||
|
|
Loading…
Reference in New Issue