Explorar o código

Adds support for verifying the certificate offered by an SSL server.

Squashed commit of the following:

commit 4439b8c45192f6dee1222df78bbb59f74509e0ee
Author: philip <philip@gladstonefamily.net>
Date:   Sun Mar 6 20:23:21 2016 -0500

    Fix the ignore

commit a07ee5acdf91286607c3e2dce128c9b8bfd7bd80
Author: philip <philip@gladstonefamily.net>
Date:   Sun Mar 6 20:20:41 2016 -0500

    Remove uneeded stuff

commit b3604ace92fc13b30161d385c354b0f1c5fe4046
Author: philip <philip@gladstonefamily.net>
Date:   Sun Mar 6 20:15:26 2016 -0500

    Remove client cert auth

commit 6e48c633569630736a986cd07a59a12de954391e
Author: philip <philip@gladstonefamily.net>
Date:   Sun Mar 6 20:11:42 2016 -0500

    More cleanup

commit d40eade405ef071d0d1b60d038566b5b8f2cafa3
Author: philip <philip@gladstonefamily.net>
Date:   Sat Mar 5 10:56:56 2016 -0500

    Move to almost working version

commit 1860a2d90afa94461c53bd41251d4870d6527f9d
Author: philip <philip@gladstonefamily.net>
Date:   Fri Mar 4 08:04:09 2016 -0500

    Changed the naem to server-ca.crt

commit e7a315660843273fe62943b7fe8ee6c0541dada2
Author: philip <philip@gladstonefamily.net>
Date:   Thu Mar 3 21:16:26 2016 -0500

    Update gitignores

commit 2b037d185c396209b64381399c40821c15e1840e
Author: philip <philip@gladstonefamily.net>
Date:   Thu Mar 3 08:56:17 2016 -0500

    Getting better

commit 763255cffba8e279158cd7f43391a3573efdeca8
Author: philip <philip@gladstonefamily.net>
Date:   Wed Mar 2 22:28:21 2016 -0500

    Works a bit better

commit a38325d1a47dbad255cb3e681da8415e8cf699ea
Author: philip <philip@gladstonefamily.net>
Date:   Wed Mar 2 09:11:04 2016 -0500

    First building version

commit 4aef13da33470ed954f2eaf5f7ac0ac3dcdf3774
Merge: 180e147 ebb0c33
Author: philip <philip@gladstonefamily.net>
Date:   Tue Mar 1 22:03:06 2016 -0500

    Merge remote-tracking branch 'upstream/dev' into ssl-client

commit 180e147c1abdcf4046ad9be9b3c1a48f4a875312
Author: philip <philip@gladstonefamily.net>
Date:   Sun Feb 28 21:34:21 2016 -0500

    Missing files from espressif

Try to imporve layout

Align the file names with the contents

Missing file

Review comments

More review coments
philip %!s(int64=8) %!d(string=hai) anos
pai
achega
501bd1fb9a
Modificáronse 11 ficheiros con 538 adicións e 18 borrados
  1. 1 0
      .gitignore
  2. 13 2
      Makefile
  3. 8 3
      app/http/httpclient.c
  4. 1 0
      app/modules/.gitignore
  5. 215 7
      app/modules/net.c
  6. 97 0
      docs/en/modules/net.md
  7. 4 2
      ld/nodemcu.ld
  8. 3 0
      tools/.gitignore
  9. 42 0
      tools/make_cert.py
  10. 69 0
      tools/make_server_cert.py
  11. 85 4
      tools/makefile.sh

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 sdk/
 cache/
 user_config.h
+server-ca.crt

+ 13 - 2
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 $<

+ 8 - 3
app/http/httpclient.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 );

+ 1 - 0
app/modules/.gitignore

@@ -0,0 +1 @@
+server-ca.crt.h

+ 215 - 7
app/modules/net.c

@@ -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 ) },

+ 97 - 0
docs/en/modules/net.md

@@ -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.
+

+ 4 - 2
ld/nodemcu.ld

@@ -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)
   {

+ 3 - 0
tools/.gitignore

@@ -0,0 +1,3 @@
+TLS.*
+private_key.h
+cert.h

+ 42 - 0
tools/make_cert.py

@@ -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()
+

+ 69 - 0
tools/make_server_cert.py

@@ -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()
+

+ 85 - 4
tools/makefile.sh

@@ -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