├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── main ├── component.mk ├── https_client.c ├── https_client.h ├── iap.c ├── iap.h ├── iap_https.c ├── iap_https.h ├── main.c ├── main.h ├── wifi_sta.c ├── wifi_sta.h ├── wifi_tls.c └── wifi_tls.h ├── meta └── ota.txt └── sdkconfig /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Classy Code GmbH 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := esp32-ota-https 7 | CFLAGS += -save-temps 8 | 9 | include $(IDF_PATH)/make/project.mk 10 | 11 | upload: 12 | scp build/esp32-ota-https.bin andreas@www.classycode.com:/data/httpd/classycode.io/esp32 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-ota-https (ESP32 Secure over-the-air update) 2 | 3 | esp32-ota-https is a demo application that shows how to securely download new firmware images to an ESP32 board. 4 | 5 | More information is available on our blog: https://blog.classycode.com/secure-over-the-air-updates-for-esp32-ec25ae00db43 6 | 7 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Main Makefile. This is basically the same as a component makefile. 3 | # 4 | -------------------------------------------------------------------------------- /main/https_client.c: -------------------------------------------------------------------------------- 1 | // 2 | // https_client.c 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module provides functions to execute HTTPS requests on an 8 | // existing TLS TCP connection. 9 | // 10 | // Created by Andreas Schweizer on 19.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #include 32 | #include 33 | #include 34 | #include "esp_log.h" 35 | 36 | #include "wifi_tls.h" 37 | #include "https_client.h" 38 | 39 | 40 | #define TAG "httpscl" 41 | 42 | 43 | // This object lives on the heap and is passed around in callbacks etc. 44 | // It contains the state for a single HTTP request. 45 | typedef struct http_request_context_ { 46 | 47 | uint32_t request_id; 48 | 49 | http_request_t *request; 50 | 51 | // Number of bytes used in the buffer. 52 | size_t response_buffer_count; 53 | 54 | // Total number of message body bytes that have been received. 55 | size_t response_body_total_count; 56 | 57 | size_t content_length; 58 | int is_processing_headers; 59 | 60 | char *tls_request_buffer; 61 | size_t tls_request_buffer_size; 62 | 63 | char *tls_response_buffer; 64 | size_t tls_response_buffer_size; 65 | 66 | } http_request_context_t; 67 | 68 | 69 | static const char *http_get_request_format_string = "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n"; 70 | static uint32_t request_nr; 71 | 72 | 73 | static int https_tls_callback(struct wifi_tls_context_ *context, struct wifi_tls_request_ *request, int index, size_t len); 74 | 75 | static http_err_t https_validate_request(http_request_t *httpRequest); 76 | static http_err_t https_create_context_for_request(http_request_context_t **httpContext, http_request_t *httpRequest); 77 | static void https_destroy_context(http_request_context_t *httpContext); 78 | 79 | 80 | // Send the specified HTTP request on the (connected and verified) tlsContext. 81 | http_err_t https_send_request(struct wifi_tls_context_ *tlsContext, http_request_t *httpRequest) 82 | { 83 | // Validate the input. 84 | 85 | if (!tlsContext) { 86 | ESP_LOGE(TAG, "https_send_request: tlsContext missing"); 87 | return HTTP_ERR_INVALID_ARGS; 88 | } 89 | 90 | http_err_t result = https_validate_request(httpRequest); 91 | if (result != HTTP_SUCCESS) { 92 | return result; 93 | } 94 | 95 | 96 | // Create the HTTP context. 97 | 98 | // This object lives on the heap and is passed around in callbacks etc. 99 | // It contains the state for a single HTTP request. 100 | 101 | http_request_context_t *httpContext; 102 | result = https_create_context_for_request(&httpContext, httpRequest); 103 | if (result != HTTP_SUCCESS) { 104 | return result; 105 | } 106 | 107 | 108 | // Create the TLS context. 109 | 110 | wifi_tls_request_t tlsRequest; 111 | 112 | tlsRequest.custom_data = httpContext; 113 | 114 | tlsRequest.request_len = httpContext->tls_request_buffer_size; 115 | tlsRequest.request_buffer = httpContext->tls_request_buffer; 116 | 117 | tlsRequest.response_buffer_size = httpContext->tls_response_buffer_size; 118 | tlsRequest.response_buffer = httpContext->tls_response_buffer; 119 | 120 | tlsRequest.response_callback = &https_tls_callback; 121 | 122 | 123 | // Submit the TLS request. 124 | 125 | int tlsResult = wifi_tls_send_request(tlsContext, &tlsRequest); 126 | 127 | 128 | // Cleanup. 129 | 130 | if (tlsResult == 0) { 131 | ESP_LOGD(TAG, "https_send_request: successfully completed HTTP request %d to the server: %s", 132 | httpContext->request_id, tlsRequest.request_buffer); 133 | 134 | https_destroy_context(httpContext); 135 | return HTTP_SUCCESS; 136 | } 137 | 138 | ESP_LOGE(TAG, "https_send_request: failed to complete HTTP request %d (wifi_tls_send_request returned %d)", 139 | httpContext->request_id, tlsResult); 140 | 141 | https_destroy_context(httpContext); 142 | return HTTP_ERR_SEND_FAILED; 143 | } 144 | 145 | static int https_tls_callback(struct wifi_tls_context_ *context, struct wifi_tls_request_ *request, int index, size_t len) 146 | { 147 | http_request_context_t *httpContext = (http_request_context_t*)request->custom_data; 148 | ESP_LOGD(TAG, "https_tls_callback: request_id = %d", httpContext->request_id); 149 | 150 | http_request_t *httpRequest = httpContext->request; 151 | 152 | // First packet resets the state 153 | if (index == 0) { 154 | httpContext->response_buffer_count = 0; 155 | httpContext->response_body_total_count = 0; 156 | httpContext->content_length = 0; 157 | httpContext->is_processing_headers = 1; 158 | bzero(httpRequest->response_buffer, httpRequest->response_buffer_len); 159 | } 160 | 161 | // If the received data would overflow our buffer, we simply stop processing the packet and drop it. 162 | if (httpContext->response_buffer_count + len > httpRequest->response_buffer_len) { 163 | ESP_LOGE(TAG, "https_tls_callback: packet buffer overflow (%d bytes), dropping the packet.", httpRequest->response_buffer_len + len); 164 | httpRequest->error_callback(httpRequest, HTTP_ERR_BUFFER_TOO_SMALL, 0); 165 | return 0; // Stop processing the packet. 166 | } 167 | 168 | // Accumulate the received data from the TLS buffer in the HTTP buffer. 169 | memcpy(&httpRequest->response_buffer[httpContext->response_buffer_count], request->response_buffer, len); 170 | httpContext->response_buffer_count += len; 171 | httpContext->response_body_total_count += len; 172 | //httpRequest->response_buffer[httpContext->response_buffer_count] = 0x00; 173 | ESP_LOGD(TAG, "https_tls_callback: packet index=%d length=%d inHeaders=%d", 174 | index, httpContext->response_buffer_count, httpContext->is_processing_headers); 175 | 176 | // ---------- Headers processing ---------- 177 | 178 | if (httpContext->is_processing_headers) { 179 | 180 | // Wait with processing until all headers have been completely received. 181 | char *endOfHeader = strstr(httpRequest->response_buffer, "\r\n\r\n"); 182 | if (!endOfHeader) { 183 | ESP_LOGD(TAG, "https_tls_callback: headers not yet complete, waiting for remaining header data."); 184 | return 1; 185 | } 186 | 187 | // --- All headers received. --- 188 | 189 | // TODO: use the headers callback!!!! handle missing Content-Length!!! 190 | 191 | // The last received packet may contain data that belongs to the message body. 192 | // Make sure we don't process the message body data as part of the headers processing. 193 | uint32_t nofHeaderBytes = endOfHeader - &httpRequest->response_buffer[0] + 4; 194 | *endOfHeader = 0x00; 195 | 196 | ESP_LOGD(TAG, "https_tls_callback: HTTP headers (%d bytes) successfully received. %d bytes of message body data received.", 197 | nofHeaderBytes, httpContext->response_buffer_count - nofHeaderBytes); 198 | 199 | // Check the HTTP status line. 200 | int httpVersionMajor = 0; 201 | int httpVersionMinor = 0; 202 | int httpStatusCode = 0; 203 | if (3 != sscanf(httpRequest->response_buffer, "HTTP/%d.%d %d ", &httpVersionMajor, &httpVersionMinor, &httpStatusCode)) { 204 | ESP_LOGE(TAG, "https_tls_callback: invalid HTTP status line, dropping packet. '%s'", httpRequest->response_buffer); 205 | httpRequest->error_callback(httpRequest, HTTP_ERR_INVALID_STATUS_LINE, 0); 206 | return 0; 207 | } 208 | ESP_LOGD(TAG, "https_tls_callback: HTTP status line: version = %d.%d, status code = %d", httpVersionMajor, httpVersionMinor, httpStatusCode); 209 | if (httpVersionMajor != 1) { 210 | ESP_LOGE(TAG, "https_tls_callback: HTTP version not supported, dropping packet. '%s'", httpRequest->response_buffer); 211 | httpRequest->error_callback(httpRequest, HTTP_ERR_VERSION_NOT_SUPPORTED, 0); 212 | return 0; 213 | } 214 | if (httpStatusCode != 200) { 215 | ESP_LOGE(TAG, "https_tls_callback: non-200 HTTP status code received, dropping packet. '%s'", httpRequest->response_buffer); 216 | httpRequest->error_callback(httpRequest, HTTP_ERR_NON_200_STATUS_CODE, httpStatusCode); 217 | return 0; 218 | } 219 | 220 | // We're mainly interested in the content length. 221 | // The server should either send the Content-Length header or should close the connection at the end. 222 | int contentLength = 0; 223 | if (!http_parse_key_value_int(httpRequest->response_buffer, "Content-Length:", &contentLength)) { 224 | ESP_LOGD(TAG, "Content-Length: %d", contentLength); 225 | httpContext->content_length = contentLength; 226 | } else { 227 | ESP_LOGW(TAG, "Content length header missing, dropping the packet. '%s'", httpRequest->response_buffer); 228 | // TODO error callback?? 229 | return 0; 230 | } 231 | 232 | // ----------------------------------------- 233 | 234 | // If the last received packet also contains message body data, we copy it to the beginning of the buffer. 235 | httpContext->response_buffer_count -= nofHeaderBytes; 236 | httpContext->response_body_total_count = httpContext->response_buffer_count; // Start counting bytes in the message body. 237 | if (httpContext->response_buffer_count > 0) { 238 | ESP_LOGD(TAG, "https_tls_callback: last packet contains data of the message body; copying to the beginning, new length = %d", httpContext->response_buffer_count); 239 | memcpy(httpRequest->response_buffer, &request->response_buffer[nofHeaderBytes], httpContext->response_buffer_count); 240 | } 241 | httpRequest->response_buffer[httpContext->response_buffer_count] = 0x00; 242 | 243 | // Continue with message body processing. 244 | httpContext->is_processing_headers = 0; 245 | } 246 | 247 | if (httpContext->is_processing_headers) { 248 | return 1; 249 | } 250 | 251 | // ---------- Message body processing ---------- 252 | 253 | if (httpRequest->response_mode == HTTP_WAIT_FOR_COMPLETE_BODY) { 254 | 255 | // Wait with processing until the message body has been completely received. 256 | if (httpContext->response_buffer_count < httpContext->content_length) { 257 | ESP_LOGD(TAG, "https_tls_callback: message body is not yet complete, waiting for remaining data (total = %d, received = %d).", 258 | httpContext->content_length, httpContext->response_buffer_count); 259 | return 1; 260 | } 261 | 262 | ESP_LOGD(TAG, "https_tls_callback: message body has been completely received, starting processing"); 263 | httpRequest->body_callback(httpRequest, httpContext->response_buffer_count); 264 | 265 | return 0; 266 | } 267 | 268 | // Provide partial message body fragments to the callback function. 269 | 270 | if (httpContext->response_buffer_count > 0) { 271 | ESP_LOGD(TAG, "https_tls_callback: message body fragment received (%d bytes, total %d of %d bytes), forwarding to callback", 272 | httpContext->response_buffer_count, httpContext->response_body_total_count, httpContext->content_length); 273 | 274 | http_continue_receiving_t cr = httpRequest->body_callback(httpRequest, httpContext->response_buffer_count); 275 | 276 | // The callback handler doesn't want to receive more packets. 277 | if (cr != HTTP_CONTINUE_RECEIVING) { 278 | return 0; 279 | } 280 | 281 | // Don't read after the end. 282 | if (httpContext->response_body_total_count >= httpContext->content_length) { 283 | // Invoke the callback with length 0 to indicate that all data has been received. 284 | httpRequest->body_callback(httpRequest, 0); 285 | return 0; 286 | } 287 | 288 | // The next fragment should start at the beginning of the packet. 289 | httpContext->response_buffer_count = 0; 290 | } 291 | 292 | return 1; 293 | } 294 | 295 | int http_parse_key_value_int(const char *buffer, const char *key, int *value) 296 | { 297 | const char *locKey = strstr(buffer, key); 298 | 299 | if (!locKey) { 300 | return -1; 301 | } 302 | 303 | *value = atoi(&locKey[strlen(key)]); 304 | return 0; 305 | } 306 | 307 | int http_parse_key_value_string(const char *buffer, const char *key, char *str, int strLen) 308 | { 309 | const char *locKey = strstr(buffer, key); 310 | 311 | if (!locKey) { 312 | return -1; 313 | } 314 | 315 | // Copy max. strLen characters up to end-of-string or newline. 316 | 317 | const char *src = &locKey[strlen(key)]; 318 | for (int i = 0; i < strLen - 1; i++) { 319 | if (*src == 0x00 || *src == '\r' || *src == '\n') { 320 | break; 321 | } 322 | *str++ = *src++; 323 | } 324 | *str++ = 0x00; 325 | 326 | return 0; 327 | } 328 | 329 | static http_err_t https_validate_request(http_request_t *httpRequest) 330 | { 331 | if (!httpRequest) { 332 | ESP_LOGE(TAG, "https_validate_request: httpRequest missing"); 333 | return HTTP_ERR_INVALID_ARGS; 334 | } 335 | 336 | if (!httpRequest->host) { 337 | ESP_LOGE(TAG, "https_validate_request: host name missing"); 338 | return HTTP_ERR_INVALID_ARGS; 339 | } 340 | 341 | if (!httpRequest->path || !httpRequest->path[0]) { 342 | ESP_LOGE(TAG, "https_send_request: resource path missing"); 343 | return HTTP_ERR_INVALID_ARGS; 344 | } 345 | 346 | if (!httpRequest->response_buffer) { 347 | ESP_LOGE(TAG, "https_send_request: no response buffer provided"); 348 | return HTTP_ERR_INVALID_ARGS; 349 | } 350 | 351 | if (!httpRequest->error_callback) { 352 | ESP_LOGE(TAG, "https_send_request: error callback missing"); 353 | return HTTP_ERR_INVALID_ARGS; 354 | } 355 | 356 | if (!httpRequest->body_callback) { 357 | ESP_LOGE(TAG, "https_send_request: body callback missing"); 358 | return HTTP_ERR_INVALID_ARGS; 359 | } 360 | 361 | // (This is only a partial implementation so far ;-) 362 | 363 | if (httpRequest->verb != HTTP_GET) { 364 | ESP_LOGE(TAG, "https_send_request: only GET is currently supported"); 365 | return HTTP_ERR_NOT_IMPLEMENTED; 366 | } 367 | 368 | return HTTP_SUCCESS; 369 | } 370 | 371 | static http_err_t https_create_context_for_request(http_request_context_t **httpContext, http_request_t *httpRequest) 372 | { 373 | // Create the HTTP context object. 374 | 375 | http_request_context_t *ctx = malloc(sizeof(http_request_context_t)); 376 | *httpContext = ctx; 377 | 378 | ctx->request_id = ++request_nr; 379 | 380 | ESP_LOGD(TAG, "https_create_context_for_request: request_id = %d", ctx->request_id); 381 | 382 | if (!ctx) { 383 | ESP_LOGE(TAG, "https_create_context_for_request: failed to allocate HTTP context object"); 384 | return HTTP_ERR_OUT_OF_MEMORY; 385 | } 386 | 387 | bzero(ctx, sizeof(http_request_context_t)); 388 | 389 | // Link the context to the HTTP request for which we create it. 390 | 391 | ctx->request = httpRequest; 392 | 393 | // Create the TLS request string. 394 | 395 | size_t requestLen = strlen(http_get_request_format_string) // strlen("%s%s") = 4 396 | + strlen(httpRequest->host) + strlen(httpRequest->path); 397 | 398 | ctx->tls_request_buffer_size = requestLen; 399 | ctx->tls_request_buffer = malloc(ctx->tls_request_buffer_size * sizeof(char)); 400 | 401 | if (!ctx->tls_request_buffer) { 402 | ESP_LOGE(TAG, "https_create_context_for_request: failed to allocate TLS request buffer"); 403 | https_destroy_context(ctx); 404 | *httpContext = NULL; 405 | return HTTP_ERR_OUT_OF_MEMORY; 406 | } 407 | 408 | sprintf(ctx->tls_request_buffer, http_get_request_format_string, httpRequest->path, httpRequest->host); 409 | ESP_LOGD(TAG, "https_create_context_for_request: request string = '%s'", ctx->tls_request_buffer); 410 | 411 | // Create a buffer for TLS responses. 412 | 413 | ctx->tls_response_buffer_size = 4096; 414 | ctx->tls_response_buffer = malloc(ctx->tls_response_buffer_size * sizeof(char)); 415 | if (!ctx->tls_response_buffer) { 416 | ESP_LOGE(TAG, "https_create_context_for_request: failed to allocate TLS response buffer"); 417 | https_destroy_context(ctx); 418 | *httpContext = NULL; 419 | return HTTP_ERR_OUT_OF_MEMORY; 420 | } 421 | 422 | return HTTP_SUCCESS; 423 | } 424 | 425 | static void https_destroy_context(http_request_context_t *httpContext) 426 | { 427 | if (!httpContext) { 428 | return; 429 | } 430 | 431 | ESP_LOGD(TAG, "https_destroy_context: request_id = %d", httpContext->request_id); 432 | 433 | free(httpContext->tls_request_buffer); 434 | free(httpContext->tls_response_buffer); 435 | free(httpContext); 436 | } 437 | -------------------------------------------------------------------------------- /main/https_client.h: -------------------------------------------------------------------------------- 1 | // 2 | // https_client.h 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module provides functions to execute HTTPS requests on an 8 | // existing TLS TCP connection. 9 | // 10 | // Created by Andreas Schweizer on 11.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #ifndef __HTTP_UTIL__ 32 | #define __HTTP_UTIL__ 1 33 | 34 | 35 | // This module depends on the wifi_tls module. 36 | // Forward declaration of the wifi_tls context structure. 37 | struct wifi_tls_context_; 38 | 39 | 40 | typedef int32_t http_err_t; 41 | 42 | #define HTTP_SUCCESS 0 43 | #define HTTP_ERR_INVALID_ARGS 0x101 44 | #define HTTP_ERR_OUT_OF_MEMORY 0x102 45 | #define HTTP_ERR_NOT_IMPLEMENTED 0x103 46 | #define HTTP_ERR_BUFFER_TOO_SMALL 0x104 47 | #define HTTP_ERR_SEND_FAILED 0x105 48 | #define HTTP_ERR_INVALID_STATUS_LINE 0x106 49 | #define HTTP_ERR_VERSION_NOT_SUPPORTED 0x107 50 | #define HTTP_ERR_NON_200_STATUS_CODE 0x108 // additional info = status code 51 | 52 | // HTTP methods to use in the requests. 53 | // TODO Right now, this is only a partial implementation. 54 | typedef enum { 55 | HTTP_GET = 0, 56 | // HTTP_POST, ... 57 | } http_request_verb_t; 58 | 59 | // Callback behaviour of a single request. 60 | // If you can provide a response buffer that you know is big enough, 61 | // you can let this module collect all data in the buffer before it 62 | // invokes your callback. Otherwise, for large downloads which don't 63 | // fit in the buffer, use HTTP_STREAM_BODY which causes the callback 64 | // to be invoked multiple times. 65 | typedef enum { 66 | HTTP_WAIT_FOR_COMPLETE_BODY, 67 | HTTP_STREAM_BODY, 68 | } http_response_mode_t; 69 | 70 | // Callback return values. 71 | // Specify HTTP_CONTINUE_RECEIVING if you're interested to receive 72 | // more data. The size of the content provided by the web server 73 | // in the Content-Length header overrides this value, i.e. if there's 74 | // no more content to be received, you can use HTTP_CONTINUE_RECEIVING 75 | // but won't get any more callbacks for the corresponding request. 76 | typedef enum { 77 | HTTP_CONTINUE_RECEIVING = 0, 78 | HTTP_STOP_RECEIVING 79 | } http_continue_receiving_t; 80 | 81 | 82 | struct http_request_; 83 | 84 | typedef http_continue_receiving_t (*http_request_headers_callback_t)(struct http_request_ *request, int statusCode, int contentLength); 85 | typedef http_continue_receiving_t (*http_request_body_callback_t)(struct http_request_ *request, size_t bytesReceived); 86 | typedef void (*http_request_error_callback_t)(struct http_request_ *request, http_err_t error, int additionalInfo); 87 | 88 | typedef struct http_request_ { 89 | 90 | // GET, POST, ... 91 | http_request_verb_t verb; 92 | 93 | // www.classycode.io 94 | const char *host; 95 | 96 | // /esp32/ota.txt 97 | const char *path; 98 | 99 | // Buffer to store the response. 100 | char *response_buffer; 101 | 102 | // Size of the response buffer. 103 | // Needs to be large enough to hold all HTTP headers! 104 | size_t response_buffer_len; 105 | 106 | // Invoked if something goes wrong. 107 | http_request_error_callback_t error_callback; 108 | 109 | // (Optional) callback handler invoked after all headers have been received. 110 | // Lets the application handle re-direction, authentication requests etc. 111 | http_request_headers_callback_t headers_callback; 112 | 113 | // Define if the body callback should be invoked once after the entire message body 114 | // has been received (response_buffer needs to be large enough to hold the entire body), 115 | // or if it should be invoked periodically after parts of the message body have been 116 | // stored in response_buffer. 117 | http_response_mode_t response_mode; 118 | 119 | // Callback handler to process the message body. 120 | // Invoked once after receiving the whole message body (HTTP_WAIT_FOR_COMPLETE_BODY) 121 | // or periodically after receiving more body data (HTTP_STREAM_BODY). In the latter case, 122 | // a callback with length 0 indicates the end of the body. 123 | http_request_body_callback_t body_callback; 124 | 125 | } http_request_t; 126 | 127 | 128 | // Send the specified HTTP request on the (connected and verified) tlsContext. 129 | // The httpRequest object needs to be kept in memory until the request has been completed. 130 | http_err_t https_send_request(struct wifi_tls_context_ *tlsContext, http_request_t *httpRequest); 131 | 132 | 133 | // Search the buffer for the specified key and try to parse an integer value right after the key. 134 | // Returns 0 on success. 135 | int http_parse_key_value_int(const char *buffer, const char *key, int *value); 136 | 137 | // Search the buffer for the specified key. If it exists, copy the string after the key up to 138 | // but without newline into the str buffer which has a size of strLen. 139 | // Returns 0 on success. 140 | int http_parse_key_value_string(const char *buffer, const char *key, char *str, int strLen); 141 | 142 | #endif // __HTTP_UTIL__ 143 | -------------------------------------------------------------------------------- /main/iap.c: -------------------------------------------------------------------------------- 1 | // 2 | // iap.c 3 | // esp32-ota-https 4 | // 5 | // In-application programming 6 | // 7 | // This module is responsible for writing the firmware to the flash. 8 | // It manages the write buffer, writing to the flash, selecting the 9 | // correct partition and activating the partition. 10 | // 11 | // Created by Andreas Schweizer on 11.01.2017. 12 | // Copyright © 2017 Classy Code GmbH 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 15 | // software and associated documentation files (the "Software"), to deal in the Software 16 | // without restriction, including without limitation the rights to use, copy, modify, 17 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 18 | // permit persons to whom the Software is furnished to do so, subject to the following 19 | // conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all copies 22 | // or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 25 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 26 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 27 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 28 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 29 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | // 31 | 32 | #include 33 | #include 34 | 35 | #include "esp_ota_ops.h" 36 | #include "esp_log.h" 37 | 38 | #include "iap.h" 39 | 40 | 41 | #define TAG "iap" 42 | 43 | 44 | #define IAP_STATE_INITIALIZED (1 << 0) 45 | #define IAP_STATE_SESSION_OPEN (1 << 1) 46 | 47 | // While the session is open ('iap_begin' called), this module uses a 48 | // heap-allocated page buffer to accumulate data for writing. 49 | #define IAP_PAGE_SIZE 4096 50 | 51 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 52 | 53 | 54 | // Internal state of this module. 55 | typedef struct iap_internal_state_ 56 | { 57 | // Set after init_iap has completed successfully. 58 | uint8_t module_state_flags; 59 | 60 | // Partition which will contain the new firmware image. 61 | const esp_partition_t *partition_to_program; 62 | 63 | // Handle for OTA functions. 64 | esp_ota_handle_t ota_handle; 65 | 66 | // Pointer to the next byte in flash memory that will be written by iap_write. 67 | uint32_t cur_flash_address; 68 | 69 | // Pointer to a 4k block to accumulate data for page writes. 70 | uint8_t *page_buffer; 71 | 72 | // Index into the page buffer. 73 | uint16_t page_buffer_ix; 74 | 75 | } iap_internal_state_t; 76 | static iap_internal_state_t iap_state; 77 | 78 | 79 | static iap_err_t iap_write_page_buffer(); 80 | static iap_err_t iap_finish(int commit); 81 | static const esp_partition_t *iap_find_next_boot_partition(); 82 | 83 | 84 | iap_err_t iap_init() 85 | { 86 | ESP_LOGD(TAG, "iap_init"); 87 | 88 | // Only allowed once. 89 | if (iap_state.module_state_flags & IAP_STATE_INITIALIZED) { 90 | ESP_LOGE(TAG, "iap_init: The module has already been initialized!"); 91 | return IAP_ERR_ALREADY_INITIALIZED; 92 | } 93 | 94 | iap_state.module_state_flags = IAP_STATE_INITIALIZED; 95 | 96 | return IAP_OK; 97 | } 98 | 99 | iap_err_t iap_begin() 100 | { 101 | ESP_LOGD(TAG, "iap_begin"); 102 | 103 | // The module needs to be initialized for this method to work. 104 | if (!(iap_state.module_state_flags & IAP_STATE_INITIALIZED)) { 105 | ESP_LOGE(TAG, "iap_begin: the module hasn't been initialized!"); 106 | return IAP_ERR_NOT_INITIALIZED; 107 | } 108 | 109 | // It's not permitted to call iap_begin if the previous programming session is still open. 110 | if (iap_state.module_state_flags & IAP_STATE_SESSION_OPEN) { 111 | ESP_LOGE(TAG, "iap_begin: Session already open!"); 112 | return IAP_ERR_SESSION_ALREADY_OPEN; 113 | } 114 | 115 | // We use a 4k page buffer to accumulate bytes for writing. 116 | iap_state.page_buffer_ix = 0; 117 | iap_state.page_buffer = malloc(IAP_PAGE_SIZE); 118 | if (!iap_state.page_buffer) { 119 | ESP_LOGE(TAG, "iap_begin: not enough heap memory to allocate the page buffer!"); 120 | return IAP_ERR_OUT_OF_MEMORY; 121 | } 122 | 123 | iap_state.partition_to_program = iap_find_next_boot_partition(); 124 | if (!iap_state.partition_to_program) { 125 | ESP_LOGE(TAG, "iap_begin: partition for firmware update not found!"); 126 | free(iap_state.page_buffer); 127 | return IAP_ERR_PARTITION_NOT_FOUND; 128 | } 129 | 130 | ESP_LOGD(TAG, "iap_begin: next boot partition is '%s'.", iap_state.partition_to_program->label); 131 | 132 | iap_state.cur_flash_address = iap_state.partition_to_program->address; 133 | 134 | esp_err_t result = esp_ota_begin(iap_state.partition_to_program, 0, &iap_state.ota_handle); 135 | if (result != ESP_OK) { 136 | ESP_LOGE(TAG, "iap_begin: esp_ota_begin failed (%d)!", result); 137 | free(iap_state.page_buffer); 138 | return IAP_FAIL; 139 | } 140 | 141 | ESP_LOGI(TAG, "iap_begin: opened IAP session for partition '%s', address 0x%08x.", 142 | iap_state.partition_to_program->label, iap_state.cur_flash_address); 143 | 144 | iap_state.module_state_flags |= IAP_STATE_SESSION_OPEN; 145 | return IAP_OK; 146 | } 147 | 148 | iap_err_t iap_write(uint8_t *bytes, uint16_t len) 149 | { 150 | ESP_LOGD(TAG, "iap_write(bytes = %p, len = %u)", bytes, len); 151 | 152 | // The module needs to be initialized for this method to work. 153 | if (!(iap_state.module_state_flags & IAP_STATE_INITIALIZED)) { 154 | ESP_LOGE(TAG, "iap_write: the module hasn't been initialized!"); 155 | return IAP_ERR_NOT_INITIALIZED; 156 | } 157 | 158 | // The session needs to be open for this method to work. 159 | if (!(iap_state.module_state_flags & IAP_STATE_SESSION_OPEN)) { 160 | ESP_LOGE(TAG, "iap_write: programming session not open!"); 161 | return IAP_ERR_NO_SESSION; 162 | } 163 | 164 | ESP_LOGD(TAG, "iap_write: cur_flash_address = 0x%08x", iap_state.cur_flash_address); 165 | 166 | while (len > 0) { 167 | 168 | uint16_t spaceRemaining = IAP_PAGE_SIZE - iap_state.page_buffer_ix; 169 | uint16_t nofBytesToCopy = MIN(spaceRemaining, len); 170 | 171 | memcpy(&iap_state.page_buffer[iap_state.page_buffer_ix], bytes, nofBytesToCopy); 172 | 173 | iap_state.page_buffer_ix += nofBytesToCopy; 174 | bytes += nofBytesToCopy; 175 | len -= nofBytesToCopy; 176 | 177 | // Page buffer full? 178 | if (iap_state.page_buffer_ix == IAP_PAGE_SIZE) { 179 | 180 | // Erase flash pages at 4k boundary. 181 | //int flashSectorToErase = iap_state.cur_flash_address / 0x1000; 182 | //ESP_LOGD(TAG, "iap_write: Erasing flash sector %d (0x%08x)", 183 | // flashSectorToErase, iap_state.cur_flash_address); 184 | //spi_flash_erase_sector(flashSectorToErase); 185 | 186 | // Write page buffer to flash memory. 187 | esp_err_t result = iap_write_page_buffer(); 188 | 189 | if (result != ESP_OK) { 190 | ESP_LOGE(TAG, "iap_write: write failed (%d)!", result); 191 | return IAP_ERR_WRITE_FAILED; 192 | } 193 | } 194 | } 195 | 196 | return IAP_OK; 197 | } 198 | 199 | iap_err_t iap_commit() 200 | { 201 | ESP_LOGD(TAG, "iap_commit"); 202 | 203 | iap_err_t result = iap_write_page_buffer(); 204 | if (result != IAP_OK) { 205 | ESP_LOGE(TAG, "iap_commit: programming session failed in final write."); 206 | } 207 | 208 | result = iap_finish(1); 209 | if (result != IAP_OK) { 210 | ESP_LOGE(TAG, "iap_commit: programming session failed in iap_finish."); 211 | } 212 | 213 | ESP_LOGI(TAG, "iap_commit: programming session successfully completed, partition activated."); 214 | return result; 215 | } 216 | 217 | iap_err_t iap_abort() 218 | { 219 | ESP_LOGD(TAG, "iap_abort"); 220 | 221 | iap_err_t result = iap_finish(0); 222 | if (result == IAP_OK) { 223 | ESP_LOGI(TAG, "iap_abort: programming session successfully aborted."); 224 | } 225 | 226 | return result; 227 | } 228 | 229 | static iap_err_t iap_write_page_buffer() 230 | { 231 | ESP_LOGD(TAG, "iap_write_page_buffer"); 232 | if (iap_state.page_buffer_ix == 0) { 233 | return IAP_OK; 234 | } 235 | 236 | ESP_LOGD(TAG, "iap_write_page_buffer: writing %u bytes to address 0x%08x", 237 | iap_state.page_buffer_ix, iap_state.cur_flash_address); 238 | esp_err_t result = esp_ota_write(iap_state.ota_handle, iap_state.page_buffer, iap_state.page_buffer_ix); 239 | if (result != ESP_OK) { 240 | ESP_LOGE(TAG, "iap_write_page_buffer: write failed in esp_ota_write (%d)!", result); 241 | return IAP_ERR_WRITE_FAILED; 242 | } 243 | 244 | iap_state.cur_flash_address += iap_state.page_buffer_ix; 245 | 246 | // Set page buffer index back to the start of the page to store more bytes. 247 | iap_state.page_buffer_ix = 0; 248 | 249 | return IAP_OK; 250 | } 251 | 252 | static iap_err_t iap_finish(int commit) 253 | { 254 | // The module needs to be initialized for this method to work. 255 | if (!(iap_state.module_state_flags & IAP_STATE_INITIALIZED)) { 256 | ESP_LOGE(TAG, "iap_finish: the module hasn't been initialized!"); 257 | return IAP_ERR_NOT_INITIALIZED; 258 | } 259 | 260 | // The session needs to be open for this method to work. 261 | if (!(iap_state.module_state_flags & IAP_STATE_SESSION_OPEN)) { 262 | ESP_LOGE(TAG, "iap_finish: programming session not open!"); 263 | return IAP_ERR_NO_SESSION; 264 | } 265 | 266 | free(iap_state.page_buffer); 267 | iap_state.page_buffer = NULL; 268 | iap_state.page_buffer_ix = 0; 269 | iap_state.cur_flash_address = 0; 270 | 271 | // TODO 272 | // There's currently no way to abort an on-going OTA update. 273 | // http://www.esp32.com/viewtopic.php?f=14&t=1093 274 | 275 | esp_err_t result = esp_ota_end(iap_state.ota_handle); 276 | 277 | if (commit) { 278 | if (result != ESP_OK) { 279 | ESP_LOGE(TAG, "iap_finish: esp_ota_end failed (%d)!", result); 280 | return IAP_FAIL; 281 | } 282 | 283 | result = esp_ota_set_boot_partition(iap_state.partition_to_program); 284 | if (result != ESP_OK) { 285 | ESP_LOGE(TAG, "iap_finish: esp_ota_set_boot_partition failed (%d)!", result); 286 | return IAP_FAIL; 287 | } 288 | } 289 | 290 | iap_state.ota_handle = 0; 291 | iap_state.partition_to_program = NULL; 292 | iap_state.module_state_flags = iap_state.module_state_flags & ~IAP_STATE_SESSION_OPEN; 293 | 294 | return IAP_OK; 295 | } 296 | 297 | static const esp_partition_t *iap_find_next_boot_partition() 298 | { 299 | // Factory -> OTA_0 300 | // OTA_0 -> OTA_1 301 | // OTA_1 -> OTA_0 302 | 303 | const esp_partition_t *currentBootPartition = esp_ota_get_boot_partition(); 304 | const esp_partition_t *nextBootPartition = NULL; 305 | 306 | if (!strcmp("factory", currentBootPartition->label)) { 307 | nextBootPartition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, "ota_0"); 308 | } 309 | 310 | if (!strcmp("ota_0", currentBootPartition->label)) { 311 | nextBootPartition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, "ota_1"); 312 | } 313 | 314 | if (!strcmp("ota_1", currentBootPartition->label)) { 315 | nextBootPartition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, "ota_0"); 316 | } 317 | 318 | return nextBootPartition; 319 | } 320 | -------------------------------------------------------------------------------- /main/iap.h: -------------------------------------------------------------------------------- 1 | // 2 | // iap.h 3 | // esp32-ota-https 4 | // 5 | // In-application programming 6 | // 7 | // This module is responsible for writing the firmware to the flash. 8 | // It manages the write buffer, writing to the flash, selecting the 9 | // correct partition and activating the partition. 10 | // 11 | // Created by Andreas Schweizer on 11.01.2017. 12 | // Copyright © 2017 Classy Code GmbH 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 15 | // software and associated documentation files (the "Software"), to deal in the Software 16 | // without restriction, including without limitation the rights to use, copy, modify, 17 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 18 | // permit persons to whom the Software is furnished to do so, subject to the following 19 | // conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all copies 22 | // or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 25 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 26 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 27 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 28 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 29 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | // 31 | 32 | #ifndef __IAP__ 33 | #define __IAP__ 1 34 | 35 | 36 | typedef int32_t iap_err_t; 37 | 38 | #define IAP_OK 0 39 | #define IAP_FAIL -1 40 | #define IAP_ERR_ALREADY_INITIALIZED 0x101 41 | #define IAP_ERR_NOT_INITIALIZED 0x102 42 | #define IAP_ERR_SESSION_ALREADY_OPEN 0x103 43 | #define IAP_ERR_OUT_OF_MEMORY 0x104 44 | #define IAP_ERR_NO_SESSION 0x105 45 | #define IAP_ERR_PARTITION_NOT_FOUND 0x106 46 | #define IAP_ERR_WRITE_FAILED 0x107 47 | 48 | 49 | // Call once at application startup, before calling any other function of this module. 50 | iap_err_t iap_init(); 51 | 52 | // Call to start a programming session. 53 | // Sets the programming pointer to the start of the next OTA flash partition. 54 | iap_err_t iap_begin(); 55 | 56 | // Call to write a block of data to the current location in flash. 57 | // If the write fails, you need to abort the current programming session 58 | // with 'iap_abort' and start again from the beginning. 59 | iap_err_t iap_write(uint8_t *bytes, uint16_t len); 60 | 61 | // Call to close a programming session and activate the programmed partition. 62 | iap_err_t iap_commit(); 63 | 64 | // Abort the current programming session. 65 | iap_err_t iap_abort(); 66 | 67 | 68 | #endif // __IAP__ 69 | -------------------------------------------------------------------------------- /main/iap_https.c: -------------------------------------------------------------------------------- 1 | // 2 | // iap_https.c 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module is responsible to trigger and coordinate firmware updates. 8 | // 9 | // Created by Andreas Schweizer on 11.01.2017. 10 | // Copyright © 2017 Classy Code GmbH 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 13 | // software and associated documentation files (the "Software"), to deal in the Software 14 | // without restriction, including without limitation the rights to use, copy, modify, 15 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to the following 17 | // conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in all copies 20 | // or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 23 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 24 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 27 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #include 31 | 32 | #include "esp_system.h" 33 | #include "esp_event_loop.h" 34 | #include "esp_log.h" 35 | 36 | #include "freertos/event_groups.h" 37 | 38 | #include "wifi_sta.h" 39 | #include "wifi_tls.h" 40 | #include "https_client.h" 41 | #include "iap.h" 42 | #include "iap_https.h" 43 | 44 | 45 | #define TAG "fwup_wifi" 46 | 47 | 48 | // The TLS context to communicate with the firmware update server. 49 | static struct wifi_tls_context_ *tls_context; 50 | 51 | // Module configuration. 52 | static iap_https_config_t *fwupdater_config; 53 | 54 | // The metadata request. 55 | static http_request_t http_metadata_request; 56 | 57 | // The firmware image request. 58 | static http_request_t http_firmware_data_request; 59 | 60 | // The event group for our processing task. 61 | #define FWUP_CHECK_FOR_UPDATE (1 << 0) 62 | #define FWUP_DOWNLOAD_IMAGE (1 << 1) 63 | static EventGroupHandle_t event_group; 64 | 65 | // The timer for the periodic checking. 66 | static TimerHandle_t check_for_updates_timer; 67 | 68 | static int has_iap_session; 69 | static int has_new_firmware; 70 | static int total_nof_bytes_received; 71 | 72 | static void iap_https_periodic_check_timer_callback(TimerHandle_t xTimer); 73 | static void iap_https_task(void *pvParameter); 74 | static void iap_https_prepare_timer(); 75 | static void iap_https_trigger_processing(); 76 | static void iap_https_check_for_update(); 77 | static void iap_https_download_image(); 78 | 79 | http_continue_receiving_t iap_https_metadata_headers_callback(struct http_request_ *request, int statusCode, int contentLength); 80 | http_continue_receiving_t iap_https_metadata_body_callback(struct http_request_ *request, size_t bytesReceived); 81 | http_continue_receiving_t iap_https_firmware_headers_callback(struct http_request_ *request, int statusCode, int contentLength); 82 | http_continue_receiving_t iap_https_firmware_body_callback(struct http_request_ *request, size_t bytesReceived); 83 | void iap_https_error_callback(struct http_request_ *request, http_err_t error, int additionalInfo); 84 | 85 | 86 | int iap_https_init(iap_https_config_t *config) 87 | { 88 | ESP_LOGD(TAG, "iap_https_init"); 89 | 90 | iap_init(); 91 | 92 | fwupdater_config = config; 93 | 94 | // Initialise the HTTPS context to the OTA server. 95 | 96 | wifi_tls_init_struct_t tlsInitStruct = { 97 | .server_host_name = config->server_host_name, 98 | .server_port = config->server_port, 99 | .server_root_ca_public_key_pem = config->server_root_ca_public_key_pem, 100 | .peer_public_key_pem = config->peer_public_key_pem 101 | }; 102 | tls_context = wifi_tls_create_context(&tlsInitStruct); 103 | 104 | 105 | // Initialise two requests, one to get the metadata and one to get the actual firmware image. 106 | 107 | http_metadata_request.verb = HTTP_GET; 108 | http_metadata_request.host = config->server_host_name; 109 | http_metadata_request.path = config->server_metadata_path; 110 | http_metadata_request.response_mode = HTTP_WAIT_FOR_COMPLETE_BODY; 111 | http_metadata_request.response_buffer_len = 512; 112 | http_metadata_request.response_buffer = malloc(http_metadata_request.response_buffer_len * sizeof(char)); 113 | http_metadata_request.error_callback = iap_https_error_callback; 114 | http_metadata_request.headers_callback = iap_https_metadata_headers_callback; 115 | http_metadata_request.body_callback = iap_https_metadata_body_callback; 116 | 117 | http_firmware_data_request.verb = HTTP_GET; 118 | http_firmware_data_request.host = config->server_host_name; 119 | http_firmware_data_request.path = config->server_firmware_path; 120 | http_firmware_data_request.response_mode = HTTP_STREAM_BODY; 121 | http_firmware_data_request.response_buffer_len = 4096; 122 | http_firmware_data_request.response_buffer = malloc(http_firmware_data_request.response_buffer_len * sizeof(char)); 123 | http_firmware_data_request.error_callback = iap_https_error_callback; 124 | http_firmware_data_request.headers_callback = iap_https_firmware_headers_callback; 125 | http_firmware_data_request.body_callback = iap_https_firmware_body_callback; 126 | 127 | // Start our processing task. 128 | 129 | event_group = xEventGroupCreate(); 130 | 131 | iap_https_prepare_timer(); 132 | 133 | xTaskCreate(&iap_https_task, "fwup_wifi_task", 4096, NULL, 1, NULL); 134 | 135 | return 0; 136 | } 137 | 138 | int iap_https_check_now() 139 | { 140 | ESP_LOGD(TAG, "iap_https_check_now"); 141 | iap_https_trigger_processing(); 142 | return 0; 143 | } 144 | 145 | int iap_https_update_in_progress() 146 | { 147 | return has_iap_session; 148 | } 149 | 150 | int iap_https_new_firmware_installed() 151 | { 152 | return has_new_firmware; 153 | } 154 | 155 | static void iap_https_periodic_check_timer_callback(TimerHandle_t xTimer) 156 | { 157 | xEventGroupSetBits(event_group, FWUP_CHECK_FOR_UPDATE); 158 | } 159 | 160 | static void iap_https_trigger_processing() 161 | { 162 | ESP_LOGD(TAG, "iap_https_trigger_processing: checking flag"); 163 | 164 | if (xEventGroupGetBits(event_group) & FWUP_CHECK_FOR_UPDATE) { 165 | ESP_LOGD(TAG, "iap_https_trigger_processing: flag is already set"); 166 | return; 167 | } 168 | 169 | ESP_LOGD(TAG, "iap_https_trigger_processing: flag is not set, setting it"); 170 | 171 | // Trigger processing in our task. 172 | xEventGroupSetBits(event_group, FWUP_CHECK_FOR_UPDATE); 173 | } 174 | 175 | static void iap_https_task(void *pvParameter) 176 | { 177 | ESP_LOGI(TAG, "Firmware updater task started."); 178 | 179 | // When the time has come, trigger the firmware update process. 180 | 181 | xEventGroupWaitBits(wifi_sta_get_event_group(), WIFI_STA_EVENT_GROUP_CONNECTED_FLAG, pdFALSE, pdFALSE, portMAX_DELAY); 182 | 183 | vTaskDelay(5000 / portTICK_PERIOD_MS); 184 | 185 | while (1) { 186 | // Wait until we get waked up (periodically or because somebody manually 187 | // requests a firmware update check) and until we're connected to the WIFI 188 | // network. 189 | 190 | BaseType_t clearOnExit = pdFALSE; 191 | BaseType_t waitForAllBits = pdFALSE; 192 | EventBits_t bits = xEventGroupWaitBits(event_group, FWUP_CHECK_FOR_UPDATE | FWUP_DOWNLOAD_IMAGE, clearOnExit, waitForAllBits, portMAX_DELAY); 193 | 194 | xEventGroupWaitBits(wifi_sta_get_event_group(), WIFI_STA_EVENT_GROUP_CONNECTED_FLAG, pdFALSE, pdFALSE, portMAX_DELAY); 195 | 196 | // We give FWUP_DOWNLOAD_IMAGE priority because if it's set, the check for update has 197 | // previously been executed and the result was that we should update the firmware. 198 | 199 | if (bits & FWUP_DOWNLOAD_IMAGE) { 200 | ESP_LOGI(TAG, "Firmware updater task will now download the new firmware image."); 201 | iap_https_download_image(); 202 | xEventGroupClearBits(event_group, FWUP_DOWNLOAD_IMAGE); 203 | 204 | } else if (bits & FWUP_CHECK_FOR_UPDATE) { 205 | ESP_LOGI(TAG, "Firmware updater task checking for firmware update."); 206 | iap_https_check_for_update(); 207 | 208 | // If periodic OTA update checks are enabled, re-start the timer. 209 | // Clear the bit *after* resetting the timer to avoid the race condition 210 | // where the timer could have elapsed during the update check and we would 211 | // immediately check again. 212 | 213 | iap_https_prepare_timer(); 214 | xEventGroupClearBits(event_group, FWUP_CHECK_FOR_UPDATE); 215 | } 216 | } 217 | } 218 | 219 | static void iap_https_prepare_timer() 220 | { 221 | // Make sure we have a timer if we need one and don't have one if we don't need one. 222 | 223 | if (fwupdater_config->polling_interval_s > 0) { 224 | if (!check_for_updates_timer) { 225 | // We need a timer but don't have one. Create it. 226 | BaseType_t autoReload = pdFALSE; 227 | check_for_updates_timer = xTimerCreate("fwup_periodic_check", 1000, autoReload, NULL, iap_https_periodic_check_timer_callback); 228 | if (!check_for_updates_timer) { 229 | ESP_LOGE(TAG, "iap_https_prepare_timer: failed to create the fwup_periodic_check timer!"); 230 | return; 231 | } 232 | } 233 | 234 | // We need and have a timer, so make sure it uses the correct interval, then start it. 235 | 236 | uint32_t timerMillisec = 1000 * fwupdater_config->polling_interval_s; 237 | ESP_LOGD(TAG, "iap_https_prepare_timer: timer interval = %d ms", timerMillisec); 238 | TickType_t timerPeriod = pdMS_TO_TICKS(timerMillisec); 239 | 240 | xTimerChangePeriod(check_for_updates_timer, timerPeriod, pdMS_TO_TICKS(5000)); 241 | 242 | if (pdFAIL == xTimerReset(check_for_updates_timer, pdMS_TO_TICKS(5000))) { 243 | ESP_LOGE(TAG, "iap_https_prepare_timer: failed to start the fwup_periodic_check timer!"); 244 | } 245 | 246 | return; 247 | } 248 | 249 | // We don't need a timer. 250 | 251 | if (check_for_updates_timer) { 252 | // We have a timer but don't need it. Delete it. 253 | xTimerDelete(check_for_updates_timer, pdMS_TO_TICKS(5000)); 254 | check_for_updates_timer = NULL; 255 | } 256 | } 257 | 258 | static void iap_https_check_for_update() 259 | { 260 | ESP_LOGD(TAG, "iap_https_check_for_update"); 261 | 262 | int tlsResult = wifi_tls_connect(tls_context); 263 | if (tlsResult) { 264 | ESP_LOGE(TAG, "iap_https_check_for_update: failed to initiate SSL/TLS connection; wifi_tls_connect returned %d", tlsResult); 265 | return; 266 | } 267 | 268 | ESP_LOGI(TAG, "Requesting firmware metadata from server."); 269 | http_err_t httpResult = https_send_request(tls_context, &http_metadata_request); 270 | if (httpResult != HTTP_SUCCESS) { 271 | ESP_LOGE(TAG, "iap_https_check_for_update: failed to send HTTPS metadata request; https_send_request returned %d", httpResult); 272 | } 273 | } 274 | 275 | static void iap_https_download_image() 276 | { 277 | int tlsResult = wifi_tls_connect(tls_context); 278 | if (tlsResult) { 279 | ESP_LOGE(TAG, "iap_https_download_image: failed to initiate SSL/TLS connection; wifi_tls_connect returned %d", tlsResult); 280 | } 281 | 282 | // Make sure we open a new IAP session in the callback. 283 | has_iap_session = 0; 284 | 285 | ESP_LOGI(TAG, "Requesting firmware image '%s' from web server.", fwupdater_config->server_firmware_path); 286 | http_err_t httpResult = https_send_request(tls_context, &http_firmware_data_request); 287 | if (httpResult != HTTP_SUCCESS) { 288 | ESP_LOGE(TAG, "iap_https_download_image: failed to send HTTPS firmware image request; https_send_request returned %d", httpResult); 289 | } 290 | } 291 | 292 | http_continue_receiving_t iap_https_metadata_body_callback(struct http_request_ *request, size_t bytesReceived) 293 | { 294 | ESP_LOGD(TAG, "iap_https_metadata_body_callback"); 295 | 296 | // --- Process the metadata information --- 297 | 298 | // (Optional) interval to check for firmware updates. 299 | int intervalSeconds = 0; 300 | if (!http_parse_key_value_int(request->response_buffer, "INTERVAL=", &intervalSeconds)) { 301 | ESP_LOGD(TAG, "[INTERVAL=] '%d'", intervalSeconds); 302 | if (intervalSeconds != fwupdater_config->polling_interval_s) { 303 | ESP_LOGD(TAG, "iap_https_metadata_body_callback: polling interval changed from %d s to %d s", 304 | fwupdater_config->polling_interval_s, intervalSeconds); 305 | fwupdater_config->polling_interval_s = intervalSeconds; 306 | } 307 | } 308 | 309 | int version = 0; 310 | if (!http_parse_key_value_int(request->response_buffer, "VERSION=", &version)) { 311 | ESP_LOGD(TAG, "[VERSION=] '%d'", version); 312 | } else { 313 | ESP_LOGW(TAG, "iap_https_metadata_body_callback: firmware version not provided, skipping firmware update"); 314 | return HTTP_STOP_RECEIVING; 315 | } 316 | 317 | char fileName[256]; 318 | if (!http_parse_key_value_string(request->response_buffer, "FILE=", fileName, sizeof(fileName) / sizeof(char))) { 319 | ESP_LOGD(TAG, "[FILE=] '%s'", fileName); 320 | strncpy(fwupdater_config->server_firmware_path, fileName, sizeof(fwupdater_config->server_firmware_path) / sizeof(char)); 321 | } else { 322 | ESP_LOGW(TAG, "iap_https_metadata_body_callback: firmware file name not provided, skipping firmware update"); 323 | return HTTP_STOP_RECEIVING; 324 | } 325 | 326 | 327 | // --- Check if the version on the server is the same as the currently installed version --- 328 | 329 | if (version == fwupdater_config->current_software_version) { 330 | ESP_LOGD(TAG, "iap_https_metadata_body_callback: we're up-to-date!"); 331 | return HTTP_STOP_RECEIVING; 332 | } 333 | 334 | ESP_LOGD(TAG, "iap_https_metadata_body_callback: our version is %d, the version on the server is %d", 335 | fwupdater_config->current_software_version, version); 336 | 337 | // --- Request the firmware image --- 338 | 339 | xEventGroupSetBits(event_group, FWUP_DOWNLOAD_IMAGE); 340 | 341 | return HTTP_STOP_RECEIVING; 342 | } 343 | 344 | http_continue_receiving_t iap_https_firmware_body_callback(struct http_request_ *request, size_t bytesReceived) 345 | { 346 | ESP_LOGD(TAG, "iap_https_firmware_body_callback"); 347 | 348 | // The first time we receive the callback, we neet to start the IAP session. 349 | if (!has_iap_session) { 350 | ESP_LOGD(TAG, "iap_https_firmware_body_callback: starting IPA session."); 351 | iap_err_t result = iap_begin(); 352 | if (result == IAP_ERR_SESSION_ALREADY_OPEN) { 353 | iap_abort(); 354 | result = iap_begin(); 355 | } 356 | if (result != IAP_OK) { 357 | ESP_LOGE(TAG, "iap_https_firmware_body_callback: iap_begin failed (%d)!", result); 358 | return HTTP_STOP_RECEIVING; 359 | } 360 | total_nof_bytes_received = 0; 361 | has_iap_session = 1; 362 | } 363 | 364 | if (bytesReceived > 0) { 365 | // Write the received data to the flash. 366 | iap_err_t result = iap_write((uint8_t*)request->response_buffer, bytesReceived); 367 | total_nof_bytes_received += bytesReceived; 368 | if (result != IAP_OK) { 369 | ESP_LOGE(TAG, "iap_https_firmware_body_callback: write failed (%d), aborting firmware update!", result); 370 | iap_abort(); 371 | return HTTP_STOP_RECEIVING; 372 | } 373 | return HTTP_CONTINUE_RECEIVING; 374 | } 375 | 376 | // After all data has been received, we get one last callback (with bytesReceived == 0). 377 | // If this happens, we need to finish the IAP session and, if configured, reboot the device. 378 | 379 | ESP_LOGD(TAG, "iap_https_firmware_body_callback: all data received (%d bytes), closing session", total_nof_bytes_received); 380 | has_iap_session = 0; 381 | 382 | if (total_nof_bytes_received > 0) { 383 | iap_err_t result = iap_commit(); 384 | if (result != IAP_OK) { 385 | ESP_LOGE(TAG, "iap_https_firmware_body_callback: closing the session has failed (%d)!", result); 386 | } 387 | 388 | has_new_firmware = 1; 389 | 390 | if (fwupdater_config->auto_reboot) { 391 | ESP_LOGI(TAG, "Automatic re-boot in 2 seconds - goodbye!..."); 392 | vTaskDelay(2000 / portTICK_RATE_MS); 393 | esp_restart(); 394 | } 395 | 396 | } else { 397 | ESP_LOGE(TAG, "iap_https_firmware_body_callback: something's not OK - the new firmware image is empty!"); 398 | iap_abort(); 399 | } 400 | 401 | return HTTP_STOP_RECEIVING; 402 | } 403 | 404 | http_continue_receiving_t iap_https_metadata_headers_callback(struct http_request_ *request, int statusCode, int contentLength) 405 | { 406 | ESP_LOGD(TAG, "iap_https_metadata_headers_callback"); 407 | return HTTP_CONTINUE_RECEIVING; 408 | } 409 | 410 | http_continue_receiving_t iap_https_firmware_headers_callback(struct http_request_ *request, int statusCode, int contentLength) 411 | { 412 | ESP_LOGD(TAG, "iap_https_firmware_headers_callback"); 413 | return HTTP_CONTINUE_RECEIVING; 414 | } 415 | 416 | void iap_https_error_callback(struct http_request_ *request, http_err_t error, int additionalInfo) 417 | { 418 | ESP_LOGE(TAG, "iap_https_error_callback: error=%d additionalInfo=%d", error, additionalInfo); 419 | 420 | if (error == HTTP_ERR_NON_200_STATUS_CODE) { 421 | switch (additionalInfo) { 422 | case 401: 423 | ESP_LOGE(TAG, "HTTP status code 401: Unauthorized."); 424 | break; 425 | case 403: 426 | ESP_LOGE(TAG, "HTTP status code 403: The server is refusing to provide the resource."); 427 | break; 428 | case 404: 429 | ESP_LOGE(TAG, "HTTP status code 404: Resource not found on the server."); 430 | break; 431 | default: 432 | ESP_LOGE(TAG, "Non-200 status code received: %d", additionalInfo); 433 | break; 434 | } 435 | } 436 | } 437 | 438 | 439 | -------------------------------------------------------------------------------- /main/iap_https.h: -------------------------------------------------------------------------------- 1 | // 2 | // iap_https.h 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module is responsible to trigger and coordinate firmware updates. 8 | // 9 | // Created by Andreas Schweizer on 11.01.2017. 10 | // Copyright © 2017 Classy Code GmbH 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 13 | // software and associated documentation files (the "Software"), to deal in the Software 14 | // without restriction, including without limitation the rights to use, copy, modify, 15 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to the following 17 | // conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in all copies 20 | // or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 23 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 24 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 27 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #ifndef __IAP_HTTPS__ 31 | #define __IAP_HTTPS__ 1 32 | 33 | 34 | typedef struct iap_https_config_ { 35 | 36 | // Version number of the running firmware image. 37 | int current_software_version; 38 | 39 | // Name of the host that provides the firmware images, e.g. "www.classycode.io". 40 | const char *server_host_name; 41 | 42 | // TCP port for TLS communication, e.g. "443". 43 | const char *server_port; 44 | 45 | // Public key of the server's root CA certificate. 46 | // Needs to be in PEM format (base64-encoded DER data with begin and end marker). 47 | const char *server_root_ca_public_key_pem; 48 | 49 | // Public key of the server's peer certificate (for certificate pinning). 50 | // Needs to be in PEM format (base64-encoded DER data with begin and end marker). 51 | const char *peer_public_key_pem; 52 | 53 | // Path to the metadata file which contains information on the firmware image, 54 | // e.g. /ota/meta.txt. We perform an HTTP/1.1 GET request on this file. 55 | char server_metadata_path[256]; 56 | 57 | // Path to the firmware image file. 58 | char server_firmware_path[256]; 59 | 60 | // Default time between two checks, in seconds. 61 | // If you want to trigger the check manually, set the value to 0 and call the 62 | // iap_https_check_now function. 63 | // During development, this is typically a small value, e.g. 10 seconds. 64 | // In production, especially with many devices, higher values make more sense 65 | // to keep the network traffic low (e.g. 3600 for 1 hour). 66 | uint32_t polling_interval_s; 67 | 68 | // Automatic re-boot after upgrade. 69 | // If the application can't handle arbitrary re-boots, set this to 'false' 70 | // and manually trigger the reboot. 71 | int auto_reboot; 72 | 73 | } iap_https_config_t; 74 | 75 | 76 | // Module initialisation, call once at application startup. 77 | int iap_https_init(iap_https_config_t *config); 78 | 79 | // Manually trigger a firmware update check. 80 | // Queries the server for a firmware update and, if one is available, installs it. 81 | // If automatic checks are enabled, calling this function causes the timer to be re-set. 82 | int iap_https_check_now(); 83 | 84 | // Returns 1 if an update is currently in progress, 0 otherwise. 85 | int iap_https_update_in_progress(); 86 | 87 | // Returns 1 if a new firmware has been installed but not yet booted, 0 otherwise. 88 | int iap_https_new_firmware_installed(); 89 | 90 | 91 | #endif // __IAP_HTTPS__ 92 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // Created by Andreas Schweizer on 11.01.2017. 8 | // Copyright © 2017 Classy Code GmbH 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 11 | // software and associated documentation files (the "Software"), to deal in the Software 12 | // without restriction, including without limitation the rights to use, copy, modify, 13 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all copies 18 | // or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 21 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 24 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 25 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | #include 29 | #include "esp_log.h" 30 | #include "esp_event_loop.h" 31 | #include "nvs_flash.h" 32 | #include "freertos/event_groups.h" 33 | #include "driver/gpio.h" 34 | 35 | #include "main.h" 36 | #include "wifi_sta.h" // WIFI module configuration, connecting to an access point. 37 | #include "iap_https.h" // Coordinating firmware updates 38 | 39 | 40 | #define TAG "main" 41 | 42 | 43 | static const char *server_root_ca_public_key_pem = OTA_SERVER_ROOT_CA_PEM; 44 | static const char *peer_public_key_pem = OTA_PEER_PEM; 45 | 46 | static wifi_sta_init_struct_t wifi_params; 47 | 48 | // Static because the scope of this object is the usage of the iap_https module. 49 | static iap_https_config_t ota_config; 50 | 51 | 52 | static void init_wifi(); 53 | static void init_ota(); 54 | static esp_err_t app_event_handler(void *ctx, system_event_t *event); 55 | 56 | 57 | void app_main() 58 | { 59 | ESP_LOGI(TAG, "---------- Intialization started ----------"); 60 | ESP_LOGI(TAG, "---------- Software version: %2d -----------", SOFTWARE_VERSION); 61 | 62 | 63 | nvs_flash_init(); 64 | 65 | 66 | // Configure the application event handler. 67 | // The handler is centrally implemented in this module. 68 | // From here, we delegate the event handling to the responsible modules. 69 | 70 | esp_event_loop_init(&app_event_handler, NULL); 71 | 72 | 73 | // Configure the WIFI module. This module maintains the connection to the 74 | // defined access point. 75 | 76 | init_wifi(); 77 | 78 | 79 | // Configure the over-the-air update module. This module periodically checks 80 | // for firmware updates by polling a web server. If an update is available, 81 | // the module downloads and installs it. 82 | 83 | init_ota(); 84 | 85 | 86 | // This application doesn't actually do anything useful. 87 | // It just lets an LED blink. You may need to adapt this for your own module 88 | // (GPIO5 is the blue LED on the "ESP32 Thing" module.) 89 | 90 | gpio_set_direction(GPIO_NUM_5, GPIO_MODE_OUTPUT); 91 | while (1) { 92 | 93 | int nofFlashes = 1; 94 | if (wifi_sta_is_connected()) { 95 | nofFlashes += 1; 96 | } 97 | if (iap_https_update_in_progress()) { 98 | nofFlashes += 2; // results in 3 (not connected) or 4 (connected) flashes 99 | } 100 | 101 | for (int i = 0; i < nofFlashes; i++) { 102 | gpio_set_level(GPIO_NUM_5, 1); 103 | vTaskDelay(150 / portTICK_PERIOD_MS); 104 | gpio_set_level(GPIO_NUM_5, 0); 105 | vTaskDelay(150 / portTICK_PERIOD_MS); 106 | } 107 | 108 | // If the application could only re-boot at certain points, you could 109 | // manually query iap_https_new_firmware_installed and manually trigger 110 | // the re-boot. What we do in this example is to let the firmware updater 111 | // re-boot automatically after installing the update (see init_ota below). 112 | // 113 | // if (iap_https_new_firmware_installed()) { 114 | // ESP_LOGI(TAG, "New firmware has been installed - rebooting..."); 115 | // esp_restart(); 116 | // } 117 | 118 | vTaskDelay(500 / portTICK_PERIOD_MS); 119 | } 120 | 121 | // Should never arrive here. 122 | } 123 | 124 | static void init_wifi() 125 | { 126 | ESP_LOGI(TAG, "Set up WIFI network connection."); 127 | 128 | wifi_params.network_ssid = WIFI_NETWORK_SSID; 129 | wifi_params.network_password = WIFI_NETWORK_PASSWORD; 130 | 131 | wifi_sta_init(&wifi_params); 132 | } 133 | 134 | static void init_ota() 135 | { 136 | ESP_LOGI(TAG, "Initialising OTA firmware updating."); 137 | 138 | ota_config.current_software_version = SOFTWARE_VERSION; 139 | ota_config.server_host_name = OTA_SERVER_HOST_NAME; 140 | ota_config.server_port = "443"; 141 | strncpy(ota_config.server_metadata_path, OTA_SERVER_METADATA_PATH, sizeof(ota_config.server_metadata_path) / sizeof(char)); 142 | bzero(ota_config.server_firmware_path, sizeof(ota_config.server_firmware_path) / sizeof(char)); 143 | ota_config.server_root_ca_public_key_pem = server_root_ca_public_key_pem; 144 | ota_config.peer_public_key_pem = peer_public_key_pem; 145 | ota_config.polling_interval_s = OTA_POLLING_INTERVAL_S; 146 | ota_config.auto_reboot = OTA_AUTO_REBOOT; 147 | 148 | iap_https_init(&ota_config); 149 | 150 | // Immediately check if there's a new firmware image available. 151 | iap_https_check_now(); 152 | } 153 | 154 | static esp_err_t app_event_handler(void *ctx, system_event_t *event) 155 | { 156 | esp_err_t result = ESP_OK; 157 | int handled = 0; 158 | 159 | ESP_LOGI(TAG, "app_event_handler: event: %d", event->event_id); 160 | 161 | // Let the wifi_sta module handle all WIFI STA events. 162 | 163 | result = wifi_sta_handle_event(ctx, event, &handled); 164 | if (ESP_OK != result || handled) { 165 | return result; 166 | } 167 | 168 | // TODO - handle other events 169 | 170 | ESP_LOGW(TAG, "app_event_handler: unhandled event: %d", event->event_id); 171 | return ESP_OK; 172 | } 173 | -------------------------------------------------------------------------------- /main/main.h: -------------------------------------------------------------------------------- 1 | // 2 | // main.h 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // Created by Andreas Schweizer on 11.01.2017. 8 | // Copyright © 2017 Classy Code GmbH 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 11 | // software and associated documentation files (the "Software"), to deal in the Software 12 | // without restriction, including without limitation the rights to use, copy, modify, 13 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all copies 18 | // or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 21 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 24 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 25 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | 29 | #ifndef __MAIN_H__ 30 | #define __MAIN_H__ 1 31 | 32 | 33 | // Adjust these values for your environment. 34 | // ------------------------------------------------------------------------------------- 35 | 36 | // Used by the OTA module to check if the current version is different from the version 37 | // on the server, i.e. if an upgrade or downgrade should be performed. 38 | #define SOFTWARE_VERSION 1 39 | 40 | // Provide the network name and password of your WIFI network. 41 | #define WIFI_NETWORK_SSID "CC-GUEST" 42 | #define WIFI_NETWORK_PASSWORD "xxxxxxxxxx" 43 | 44 | // Provide server name, path to metadata file and polling interval for OTA updates. 45 | #define OTA_SERVER_HOST_NAME "www.classycode.io" 46 | #define OTA_SERVER_METADATA_PATH "/esp32/ota.txt" 47 | #define OTA_POLLING_INTERVAL_S 5 48 | #define OTA_AUTO_REBOOT 1 49 | 50 | // Provide the Root CA certificate for chain validation. 51 | // (copied from gd_bundle-g2-g1.crt) 52 | #define OTA_SERVER_ROOT_CA_PEM \ 53 | "-----BEGIN CERTIFICATE-----\n" \ 54 | "MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT\n" \ 55 | "MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv\n" \ 56 | "IERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMTAx\n" \ 57 | "MDcwMDAwWhcNMzEwNTMwMDcwMDAwWjCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT\n" \ 58 | "B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHku\n" \ 59 | "Y29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1\n" \ 60 | "dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Fi\n" \ 61 | "CPH6WTT3G8kYo/eASVjpIoMTpsUgQwE7hPHmhUmfJ+r2hBtOoLTbcJjHMgGxBT4H\n" \ 62 | "Tu70+k8vWTAi56sZVmvigAf88xZ1gDlRe+X5NbZ0TqmNghPktj+pA4P6or6KFWp/\n" \ 63 | "3gvDthkUBcrqw6gElDtGfDIN8wBmIsiNaW02jBEYt9OyHGC0OPoCjM7T3UYH3go+\n" \ 64 | "6118yHz7sCtTpJJiaVElBWEaRIGMLKlDliPfrDqBmg4pxRyp6V0etp6eMAo5zvGI\n" \ 65 | "gPtLXcwy7IViQyU0AlYnAZG0O3AqP26x6JyIAX2f1PnbU21gnb8s51iruF9G/M7E\n" \ 66 | "GwM8CetJMVxpRrPgRwIDAQABo4IBFzCCARMwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" \ 67 | "HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMB8GA1Ud\n" \ 68 | "IwQYMBaAFNLEsNKR1EwRcbNhyz2h/t2oatTjMDQGCCsGAQUFBwEBBCgwJjAkBggr\n" \ 69 | "BgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMDIGA1UdHwQrMCkwJ6Al\n" \ 70 | "oCOGIWh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ryb290LmNybDBGBgNVHSAEPzA9\n" \ 71 | "MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0cHM6Ly9jZXJ0cy5nb2RhZGR5LmNv\n" \ 72 | "bS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAWQtTvZKGEacke+1bMc8d\n" \ 73 | "H2xwxbhuvk679r6XUOEwf7ooXGKUwuN+M/f7QnaF25UcjCJYdQkMiGVnOQoWCcWg\n" \ 74 | "OJekxSOTP7QYpgEGRJHjp2kntFolfzq3Ms3dhP8qOCkzpN1nsoX+oYggHFCJyNwq\n" \ 75 | "9kIDN0zmiN/VryTyscPfzLXs4Jlet0lUIDyUGAzHHFIYSaRt4bNYC8nY7NmuHDKO\n" \ 76 | "KHAN4v6mF56ED71XcLNa6R+ghlO773z/aQvgSMO3kwvIClTErF0UZzdsyqUvMQg3\n" \ 77 | "qm5vjLyb4lddJIGvl5echK1srDdMZvNhkREg5L4wn3qkKQmw4TRfZHcYQFHfjDCm\n" \ 78 | "rw==\n" \ 79 | "-----END CERTIFICATE-----\n" 80 | 81 | // Provide the Peer certificate for certificate pinning. 82 | // (copied from classycode.io.crt) 83 | #define OTA_PEER_PEM \ 84 | "-----BEGIN CERTIFICATE-----\n" \ 85 | "MIIFNzCCBB+gAwIBAgIJANYDzCSwNgryMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD\n" \ 86 | "VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa\n" \ 87 | "MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0\n" \ 88 | "cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj\n" \ 89 | "dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTE2MDcxMTE3NDE0MloX\n" \ 90 | "DTE5MDcxMTE3NDE0MlowPzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRh\n" \ 91 | "dGVkMRowGAYDVQQDExF3d3cuY2xhc3N5Y29kZS5pbzCCASIwDQYJKoZIhvcNAQEB\n" \ 92 | "BQADggEPADCCAQoCggEBAPKdu6d89pXjP0qVWG33xGSOyNRLak9zShtx/KaO9ftj\n" \ 93 | "TmBASCBlxk0aNba6gt1H+Hx2NPZqCd8BWRM/nt1fk9cCYYsRkjrJn/o8i0LmQTyr\n" \ 94 | "2jb/LVScQEApaCe3sNA7evz+F2jNodX45uvi+wiPoUZkoG3aToPkx3QjrphMGD1g\n" \ 95 | "lS2zSVy6Zg0AcnroHCMSdSlxo2DOHie8zfAyCQacyBjDNrZgcVZzIANyw6Y/YAeR\n" \ 96 | "bQwzDerm23daSMdkfc6ewsLvFmfsy7VZIdJYsPOOA7o0xyeeUW1C4NnWxvp8j4tR\n" \ 97 | "1X/JWSNAYki8pj2ChwUF6SPxwHMDnp//g1liuCR7Mh0CAwEAAaOCAb4wggG6MAwG\n" \ 98 | "A1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4GA1Ud\n" \ 99 | "DwEB/wQEAwIFoDA3BgNVHR8EMDAuMCygKqAohiZodHRwOi8vY3JsLmdvZGFkZHku\n" \ 100 | "Y29tL2dkaWcyczEtMjY1LmNybDBdBgNVHSAEVjBUMEgGC2CGSAGG/W0BBxcBMDkw\n" \ 101 | "NwYIKwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVw\n" \ 102 | "b3NpdG9yeS8wCAYGZ4EMAQIBMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYY\n" \ 103 | "aHR0cDovL29jc3AuZ29kYWRkeS5jb20vMEAGCCsGAQUFBzAChjRodHRwOi8vY2Vy\n" \ 104 | "dGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RpZzIuY3J0MB8GA1Ud\n" \ 105 | "IwQYMBaAFEDCvSeOzDSDMKIz1/tss/C0LIDOMCsGA1UdEQQkMCKCEXd3dy5jbGFz\n" \ 106 | "c3ljb2RlLmlvgg1jbGFzc3ljb2RlLmlvMB0GA1UdDgQWBBRSUKeJNe7VqzcoU22Q\n" \ 107 | "+2Cwm5LqjjANBgkqhkiG9w0BAQsFAAOCAQEAO4P8Pxbfdspwtg0zBi+d6AHkBEA8\n" \ 108 | "lDa/S6XGx+1fCbtDAm734eVvHDRQOIUtJjN8RI0vsV75Qn5M0hfm9l8BdTp/KZgI\n" \ 109 | "3yDAWmHp6djQs35vVHs00aFoyB2BHYgJ0/mdlnmwFeqYo0lJn3MrAT+mZ824KDKN\n" \ 110 | "SjOhhHUy2HFaWQ8p6t+r8Gwexc2UJVXQ5+0dIEVHb3E3ZYWkebG7yMZiJvpt6fgZ\n" \ 111 | "L2DbU95sIPiNCxICtTdf6BcU31JrIiBqozKenXs4cIEj4YBE6QVsiS8zJt/K3Sc5\n" \ 112 | "anfkm8wWGaEfaD16wcz7+7yi937RE1Q2EdyKMVhx4UNTln2HqxxIqmUSYQ==\n" \ 113 | "-----END CERTIFICATE-----\n" 114 | 115 | // ------------------------------------------------------------------------------------- 116 | 117 | 118 | #endif // __MAIN_H__ 119 | -------------------------------------------------------------------------------- /main/wifi_sta.c: -------------------------------------------------------------------------------- 1 | // 2 | // wifi_sta.c 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module is responsible for establishing and maintaining the 8 | // WIFI connection to the defined access point. 9 | // 10 | // Created by Andreas Schweizer on 11.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #include 32 | #include "freertos/FreeRTOS.h" 33 | #include "freertos/event_groups.h" 34 | #include "esp_wifi.h" 35 | #include "esp_log.h" 36 | 37 | #include "wifi_sta.h" 38 | 39 | 40 | #define TAG "wifi_sta" 41 | 42 | 43 | // Our event group to manage the "connected" state. 44 | static EventGroupHandle_t wifi_sta_event_group; 45 | 46 | 47 | static void wifi_sta_set_connected(bool c); 48 | 49 | esp_err_t wifi_sta_init(wifi_sta_init_struct_t *param) 50 | { 51 | // Let's first validate the input parameters. 52 | 53 | static wifi_config_t wifi_sta_config_struct; 54 | 55 | if (strlen(param->network_ssid) >= sizeof(wifi_sta_config_struct.sta.ssid) / sizeof(char)) { 56 | ESP_LOGE(TAG, "wifi_sta_init: invalid parameter: network_ssid too long"); 57 | return ESP_ERR_INVALID_ARG; 58 | } 59 | 60 | if (strlen(param->network_password) >= sizeof(wifi_sta_config_struct.sta.password) / sizeof(char)) { 61 | ESP_LOGE(TAG, "wifi_sta_init: invalid parameter: network_password too long"); 62 | return ESP_ERR_INVALID_ARG; 63 | } 64 | 65 | ESP_LOGI(TAG, "wifi_sta_init: network = '%s'", param->network_ssid); 66 | 67 | // Initialize the TCP/IP and WIFI functionalities. 68 | 69 | tcpip_adapter_init(); 70 | 71 | // Init WIFI (driver memory, buffers and so on). 72 | ESP_LOGD(TAG, "wifi_sta_init: esp_wifi_init"); 73 | wifi_init_config_t init_config_struct = WIFI_INIT_CONFIG_DEFAULT(); 74 | esp_err_t init_result = esp_wifi_init(&init_config_struct); 75 | if (ESP_OK != init_result) { 76 | // typically ESP_ERR_WIFI_NO_MEM, ... 77 | ESP_LOGE(TAG, "wifi_sta_init: esp_wifi_init failed: %d", init_result); 78 | return init_result; 79 | } 80 | 81 | // Define the configuration storage. 82 | ESP_LOGD(TAG, "wifi_sta_init: esp_wifi_set_storage"); 83 | esp_err_t storage_result = esp_wifi_set_storage(WIFI_STORAGE_RAM); 84 | if (ESP_OK != storage_result) { 85 | // typically ESP_ERR_WIFI_NOT_INIT, ESP_ERR_WIFI_ARG, ... 86 | ESP_LOGE(TAG, "wifi_sta_init: esp_wifi_set_storage failed: %d", storage_result); 87 | return storage_result; 88 | } 89 | 90 | // Put the ESP32 WIFI in STA mode. 91 | ESP_LOGD(TAG, "wifi_sta_init: esp_wifi_set_mode"); 92 | esp_err_t set_mode_result = esp_wifi_set_mode(WIFI_MODE_STA); 93 | if (ESP_OK != set_mode_result) { 94 | // typically ESP_ERR_WIFI_NOT_INIT, ESP_ERR_WIFI_ARG, ... 95 | ESP_LOGE(TAG, "wifi_sta_init: esp_wifi_set_mode failed: %d", set_mode_result); 96 | return set_mode_result; 97 | } 98 | 99 | // Define the configuration for the ESP32 STA mode. 100 | ESP_LOGD(TAG, "wifi_sta_init: esp_wifi_set_config"); 101 | strcpy((char*)wifi_sta_config_struct.sta.ssid, param->network_ssid); // max 32 bytes 102 | strcpy((char*)wifi_sta_config_struct.sta.password, param->network_password); // max 32 bytes 103 | wifi_sta_config_struct.sta.bssid_set = false; 104 | esp_err_t set_config_result = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config_struct); 105 | if (ESP_OK != set_config_result) { 106 | ESP_LOGE(TAG, "wifi_sta_init: esp_wifi_set_config failed: %d", set_config_result); 107 | return set_config_result; 108 | } 109 | 110 | vTaskDelay(200 / portTICK_PERIOD_MS); // WORKAROUND 111 | wifi_sta_event_group = xEventGroupCreate(); 112 | 113 | // Start WIFI according to the current configuration. 114 | ESP_LOGD(TAG, "wifi_sta_init: esp_wifi_start"); 115 | esp_err_t start_result = esp_wifi_start(); 116 | if (ESP_OK != start_result) { 117 | ESP_LOGE(TAG, "wifi_sta_init: esp_wifi_set_config failed: %d", start_result); 118 | return start_result; 119 | } 120 | 121 | vTaskDelay(200 / portTICK_PERIOD_MS); // WORKAROUND 122 | 123 | return ESP_OK; 124 | } 125 | 126 | esp_err_t wifi_sta_handle_event(void *ctx, system_event_t *event, int *handled) 127 | { 128 | esp_err_t result = ESP_OK; 129 | *handled = 1; 130 | 131 | switch(event->event_id) { 132 | 133 | case SYSTEM_EVENT_STA_START: 134 | ESP_LOGD(TAG, "wifi_sta_handle_event: SYSTEM_EVENT_STA_START"); 135 | result = esp_wifi_connect(); 136 | break; 137 | 138 | case SYSTEM_EVENT_STA_GOT_IP: 139 | ESP_LOGD(TAG, "wifi_sta_handle_event: SYSTEM_EVENT_STA_GOT_IP"); 140 | wifi_sta_set_connected(true); 141 | break; 142 | 143 | case SYSTEM_EVENT_STA_CONNECTED: 144 | ESP_LOGD(TAG, "wifi_sta_handle_event: SYSTEM_EVENT_STA_CONNECTED"); 145 | break; 146 | 147 | case SYSTEM_EVENT_STA_DISCONNECTED: 148 | ESP_LOGD(TAG, "wifi_sta_handle_event: SYSTEM_EVENT_STA_DISCONNECTED"); 149 | wifi_sta_set_connected(false); 150 | // try to re-connect 151 | result = esp_wifi_connect(); 152 | break; 153 | 154 | default: 155 | ESP_LOGD(TAG, "wifi_sta_handle_event: event is not for us: %d", event->event_id); 156 | *handled = 0; 157 | break; 158 | } 159 | 160 | return result; 161 | } 162 | 163 | EventGroupHandle_t wifi_sta_get_event_group() 164 | { 165 | return wifi_sta_event_group; 166 | } 167 | 168 | int wifi_sta_is_connected() 169 | { 170 | return (xEventGroupGetBits(wifi_sta_event_group) & WIFI_STA_EVENT_GROUP_CONNECTED_FLAG) ? 1 : 0; 171 | } 172 | 173 | 174 | static void wifi_sta_set_connected(bool c) 175 | { 176 | if (wifi_sta_is_connected() == c) { 177 | return; 178 | } 179 | 180 | if (c) { 181 | xEventGroupSetBits(wifi_sta_event_group, WIFI_STA_EVENT_GROUP_CONNECTED_FLAG); 182 | } else { 183 | xEventGroupClearBits(wifi_sta_event_group, WIFI_STA_EVENT_GROUP_CONNECTED_FLAG); 184 | } 185 | 186 | ESP_LOGI(TAG, "Device is now %s WIFI network", c ? "connected to" : "disconnected from"); 187 | } 188 | -------------------------------------------------------------------------------- /main/wifi_sta.h: -------------------------------------------------------------------------------- 1 | // 2 | // wifi_sta.h 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module is responsible for establishing and maintaining the 8 | // WIFI connection to the defined access point. 9 | // 10 | // Created by Andreas Schweizer on 11.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #ifndef __WIFI_STA__ 32 | #define __WIFI_STA__ 1 33 | 34 | 35 | #define WIFI_STA_EVENT_GROUP_CONNECTED_FLAG (1 << 0) 36 | 37 | 38 | typedef struct wifi_sta_init_struct_ { 39 | 40 | // Network SSID to connect to. 41 | const char *network_ssid; 42 | 43 | // Network password. 44 | const char *network_password; 45 | 46 | } wifi_sta_init_struct_t; 47 | 48 | 49 | // Configure this device in 'station' mode and connect to the specified network. 50 | esp_err_t wifi_sta_init(wifi_sta_init_struct_t *param); 51 | 52 | // Sets "handled" to 1 if the event was handled, 0 if it was not for us. 53 | esp_err_t wifi_sta_handle_event(void *ctx, system_event_t *event, int *handled); 54 | 55 | // Returns 1 if the device is currently connected to the specified network, 0 otherwise. 56 | int wifi_sta_is_connected(); 57 | 58 | // Let other modules wait on connectivity changes. 59 | EventGroupHandle_t wifi_sta_get_event_group(); 60 | 61 | 62 | #endif // __WIFI_STA__ 63 | -------------------------------------------------------------------------------- /main/wifi_tls.c: -------------------------------------------------------------------------------- 1 | // 2 | // wifi_tls.c 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module provides TLS connections with certificate pinning and 8 | // callback-based request/response functionality. 9 | // 10 | // Created by Andreas Schweizer on 11.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #include 32 | #include "freertos/FreeRTOS.h" 33 | #include "esp_event_loop.h" 34 | #include "esp_log.h" 35 | 36 | #include "mbedtls/platform.h" 37 | #include "mbedtls/net.h" 38 | #include "mbedtls/ssl.h" 39 | #include "mbedtls/entropy.h" 40 | #include "mbedtls/ctr_drbg.h" 41 | #include "mbedtls/error.h" 42 | 43 | #include "wifi_tls.h" 44 | 45 | 46 | #define TAG "wifi_tls" 47 | 48 | 49 | // Internal state for a single TLS context (single connection). 50 | typedef struct wifi_tls_context_ { 51 | 52 | // We transparently clean up the context in case of errors. 53 | // This flag indicates if the context is ready to use. 54 | bool is_valid; 55 | 56 | char *server_host_name; 57 | char *server_root_ca_public_key_pem; 58 | char *peer_public_key_pem; 59 | int server_port; 60 | 61 | // mbedTLS SSL context. 62 | mbedtls_ssl_context ssl; 63 | 64 | // mbedTLS SSL configuration. 65 | mbedtls_ssl_config ssl_conf; 66 | 67 | // Counter mode deterministic random byte generator. 68 | mbedtls_ctr_drbg_context ctr_drbg; 69 | 70 | // Context for the generic entropy collector. 71 | mbedtls_entropy_context entropy; 72 | 73 | // Container for the X.509 Root CA certificate. 74 | mbedtls_x509_crt root_ca_cert; 75 | 76 | // Container for the X.509 Peer certificate. 77 | mbedtls_x509_crt peer_cert; 78 | 79 | // Server file descriptor. 80 | mbedtls_net_context server_fd; 81 | 82 | } wifi_tls_context_t; 83 | 84 | 85 | static int wifi_tls_init_context(wifi_tls_context_t *ctx); 86 | static int wifi_tls_reset_context(wifi_tls_context_t *ctx); 87 | static int wifi_tls_handshake(wifi_tls_context_t *ctx); 88 | static int wifi_tls_cert_pinning(wifi_tls_context_t *ctx); 89 | static void wifi_tls_print_mbedtls_error(char *message, int code); 90 | static void wifi_tls_dump_hex_buffer(char *buf, int len); 91 | 92 | 93 | wifi_tls_context_t *wifi_tls_create_context(wifi_tls_init_struct_t *params) 94 | { 95 | // Validate the input parameters. 96 | 97 | if (!params->server_port || !params->server_host_name 98 | || !params->server_root_ca_public_key_pem 99 | || !params->peer_public_key_pem) 100 | { 101 | ESP_LOGE(TAG, "wifi_tls_create_context: parameter missing"); 102 | return NULL; 103 | } 104 | 105 | int server_port = atoi(params->server_port); 106 | if (server_port < 1 || server_port > 65535) { 107 | ESP_LOGE(TAG, "wifi_tls_create_context: invalid server port"); 108 | return NULL; 109 | } 110 | 111 | // Allocate the context structure. 112 | 113 | wifi_tls_context_t *ctx = malloc(sizeof(wifi_tls_context_t)); 114 | if (!ctx) { 115 | ESP_LOGE(TAG, "wifi_tls_create_context: out of memory"); 116 | return NULL; 117 | } 118 | 119 | memset(ctx, 0, sizeof(wifi_tls_context_t)); 120 | 121 | // Configure the context structure. 122 | 123 | ctx->server_host_name = malloc(strlen(params->server_host_name) + 1); 124 | if (!ctx->server_host_name) { 125 | ESP_LOGE(TAG, "wifi_tls_create_context: out of memory"); 126 | free(ctx); 127 | return NULL; 128 | } 129 | strcpy(ctx->server_host_name, params->server_host_name); 130 | 131 | ctx->server_root_ca_public_key_pem = malloc(strlen(params->server_root_ca_public_key_pem) + 1); 132 | if (!ctx->server_root_ca_public_key_pem) { 133 | ESP_LOGE(TAG, "wifi_tls_create_context: out of memory"); 134 | free(ctx->server_host_name); 135 | free(ctx); 136 | return NULL; 137 | } 138 | strcpy(ctx->server_root_ca_public_key_pem, params->server_root_ca_public_key_pem); 139 | 140 | ctx->peer_public_key_pem = malloc(strlen(params->peer_public_key_pem) + 1); 141 | if (!ctx->peer_public_key_pem) { 142 | ESP_LOGE(TAG, "wifi_tls_create_context: out of memory"); 143 | free(ctx->server_root_ca_public_key_pem); 144 | free(ctx->server_host_name); 145 | free(ctx); 146 | return NULL; 147 | } 148 | strcpy(ctx->peer_public_key_pem, params->peer_public_key_pem); 149 | 150 | ctx->is_valid = false; 151 | ctx->server_port = server_port; 152 | 153 | ESP_LOGD(TAG, "wifi_tls_create_context: context created for server: %s", ctx->server_host_name); 154 | return ctx; 155 | } 156 | 157 | void wifi_tls_free_context(wifi_tls_context_t *ctx) 158 | { 159 | free(ctx->peer_public_key_pem); 160 | free(ctx->server_root_ca_public_key_pem); 161 | free(ctx->server_host_name); 162 | memset(ctx, 0, sizeof(wifi_tls_context_t)); 163 | 164 | free(ctx); 165 | } 166 | 167 | int wifi_tls_connect(wifi_tls_context_t *ctx) 168 | { 169 | // Make sure the context is valid. 170 | int init_context_result = wifi_tls_init_context(ctx); 171 | if (init_context_result) { 172 | ESP_LOGE(TAG, "wifi_tls_connect: failed to initialise the module context"); 173 | return init_context_result; 174 | } 175 | 176 | 177 | // Connect to the server 178 | 179 | mbedtls_net_init(&ctx->server_fd); 180 | 181 | char portBuf[16]; 182 | sprintf(portBuf, "%d", ctx->server_port); 183 | 184 | int net_connect_result = mbedtls_net_connect(&ctx->server_fd, ctx->server_host_name, portBuf, MBEDTLS_NET_PROTO_TCP); 185 | if (net_connect_result != 0) { 186 | wifi_tls_print_mbedtls_error("wifi_tls_connect: failed to connect to server", net_connect_result); 187 | wifi_tls_reset_context(ctx); 188 | return -1; 189 | } 190 | 191 | ESP_LOGD(TAG, "wifi_tls_connect: connected to server '%s', fd = %d", ctx->server_host_name, ctx->server_fd.fd); 192 | 193 | // WORKAROUND 194 | // http://www.esp32.com/viewtopic.php?f=14&t=1007 195 | vTaskDelay(200 / portTICK_PERIOD_MS); 196 | 197 | 198 | // Define input and output functions for sending and receiving network data. 199 | 200 | mbedtls_ssl_set_bio(&ctx->ssl, &ctx->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); 201 | 202 | 203 | // Perform SSL/TLS handshake. 204 | 205 | ESP_LOGD(TAG, "wifi_tls_connect: starting handshake"); 206 | int handshakeResult = wifi_tls_handshake(ctx); 207 | if (handshakeResult != 0) { 208 | ESP_LOGE(TAG, "wifi_tls_connect: handshake failed"); 209 | wifi_tls_disconnect(ctx); 210 | return -1; 211 | } 212 | 213 | // Verify Root CA Certificate 214 | 215 | ESP_LOGD(TAG, "wifi_tls_connect: verifying root CA certificate"); 216 | uint32_t caCertVerificationResult = mbedtls_ssl_get_verify_result(&ctx->ssl); 217 | if (caCertVerificationResult != 0) { 218 | wifi_tls_print_mbedtls_error("wifi_tls_connect: mbedtls_ssl_get_verify_result", caCertVerificationResult); 219 | wifi_tls_disconnect(ctx); 220 | return -1; 221 | } 222 | 223 | // Verify Peer Certificate (Certificate Pinning) 224 | 225 | ESP_LOGD(TAG, "wifi_tls_connect: certificate pinning"); 226 | int pinningResult = wifi_tls_cert_pinning(ctx); 227 | if (pinningResult != 0) { 228 | ESP_LOGE(TAG, "wifi_tls_connect: certificate pinning failed"); 229 | wifi_tls_disconnect(ctx); 230 | return -1; 231 | } 232 | 233 | ESP_LOGI(TAG, "Started valid TLS/SSL session with server '%s'.", ctx->server_host_name); 234 | return 0; 235 | } 236 | 237 | void wifi_tls_disconnect(wifi_tls_context_t *ctx) 238 | { 239 | mbedtls_net_free(&ctx->server_fd); 240 | ESP_LOGI(TAG, "Ended TLS/SSL session with server '%s'.", ctx->server_host_name); 241 | 242 | wifi_tls_reset_context(ctx); 243 | } 244 | 245 | int wifi_tls_send_request(wifi_tls_context_t *ctx, wifi_tls_request_t *request) 246 | { 247 | size_t lenRemaining = request->request_len; 248 | char *p = request->request_buffer; 249 | 250 | ESP_LOGD(TAG, "wifi_tls_send_request: '%s'", request->request_buffer); 251 | 252 | while (lenRemaining > 0) { 253 | int ret = mbedtls_ssl_write(&ctx->ssl, (unsigned char *)p, lenRemaining); 254 | if (ret > 0) { 255 | lenRemaining -= ret; 256 | p += ret; 257 | ESP_LOGD(TAG, "wifi_tls_send_request: partial write: %d bytes written, %d bytes remaining", ret, lenRemaining); 258 | continue; 259 | } 260 | 261 | if (ret == MBEDTLS_ERR_SSL_WANT_READ) { 262 | ESP_LOGD(TAG, "wifi_tls_send_request: write: MBEDTLS_ERR_SSL_WANT_READ"); 263 | continue; 264 | } 265 | 266 | if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { 267 | ESP_LOGD(TAG, "wifi_tls_send_request: write: MBEDTLS_ERR_SSL_WANT_WRITE"); 268 | continue; 269 | } 270 | 271 | // Context is invalid, need to disconnect. 272 | wifi_tls_print_mbedtls_error("wifi_tls_send_request: write: error, disconnecting, context is invalid", ret); 273 | wifi_tls_disconnect(ctx); 274 | return -1; 275 | } 276 | 277 | // INV: Request successfully written. 278 | // Read the response. 279 | 280 | int callbackIndex = 0; 281 | while (1) { 282 | int ret = mbedtls_ssl_read(&ctx->ssl, (unsigned char *)request->response_buffer, request->response_buffer_size); 283 | 284 | if (ret == 0) { 285 | ESP_LOGD(TAG, "wifi_tls_send_request: EOF"); 286 | // EOF 287 | wifi_tls_disconnect(ctx); 288 | return 0; 289 | } 290 | 291 | if (ret > 0) { 292 | // Partial read 293 | ESP_LOGD(TAG, "wifi_tls_send_request: partial read: %d bytes read", ret); 294 | int continueReading = request->response_callback(ctx, request, callbackIndex, ret); 295 | if (!continueReading) { 296 | wifi_tls_disconnect(ctx); 297 | return 0; 298 | } 299 | callbackIndex++; 300 | continue; 301 | } 302 | 303 | if (ret == MBEDTLS_ERR_SSL_WANT_READ) { 304 | ESP_LOGD(TAG, "wifi_tls_send_request: read: MBEDTLS_ERR_SSL_WANT_READ"); 305 | continue; 306 | } 307 | 308 | if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { 309 | ESP_LOGD(TAG, "wifi_tls_send_request: read: MBEDTLS_ERR_SSL_WANT_WRITE"); 310 | continue; 311 | } 312 | 313 | // Context is invalid, need to disconnect. 314 | wifi_tls_print_mbedtls_error("wifi_tls_send_request: read: error, disconnecting, context is invalid", ret); 315 | wifi_tls_disconnect(ctx); 316 | return -1; 317 | } 318 | 319 | return 0; 320 | } 321 | 322 | 323 | static int wifi_tls_init_context(wifi_tls_context_t *ctx) 324 | { 325 | if (ctx->is_valid) { 326 | return 0; 327 | } 328 | 329 | mbedtls_ssl_init(&ctx->ssl); 330 | mbedtls_x509_crt_init(&ctx->root_ca_cert); 331 | mbedtls_x509_crt_init(&ctx->peer_cert); 332 | mbedtls_ctr_drbg_init(&ctx->ctr_drbg); 333 | mbedtls_ssl_config_init(&ctx->ssl_conf); 334 | mbedtls_entropy_init(&ctx->entropy); 335 | 336 | 337 | // Random number generator. 338 | int drbg_seed_result = mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy, NULL, 0); 339 | if (drbg_seed_result != 0) { 340 | wifi_tls_print_mbedtls_error("wifi_tls_init_context: mbedtls_ctr_drbg_seed failed", drbg_seed_result); 341 | wifi_tls_reset_context(ctx); 342 | return -1; 343 | } 344 | 345 | // Root CA certificate. 346 | size_t buf_len = strlen(ctx->server_root_ca_public_key_pem) + 1; // needs to include the trailing 0x00! 347 | int cert_parse_result = mbedtls_x509_crt_parse(&ctx->root_ca_cert, (const unsigned char *)ctx->server_root_ca_public_key_pem, buf_len); 348 | if (cert_parse_result != 0) { 349 | wifi_tls_print_mbedtls_error("wifi_tls_init_context: mbedtls_x509_crt_parse failed for Root CA Cert", cert_parse_result); 350 | wifi_tls_reset_context(ctx); 351 | return -1; 352 | } 353 | 354 | // Peer certificate (for certificate pinning). 355 | buf_len = strlen(ctx->peer_public_key_pem) + 1; // needs to include the trailing 0x00! 356 | cert_parse_result = mbedtls_x509_crt_parse(&ctx->peer_cert, (const unsigned char *)ctx->peer_public_key_pem, buf_len); 357 | if (cert_parse_result != 0) { 358 | wifi_tls_print_mbedtls_error("wifi_tls_init_context: mbedtls_x509_crt_parse failed for Peer Cert", cert_parse_result); 359 | wifi_tls_reset_context(ctx); 360 | return -1; 361 | } 362 | 363 | // SSL configuration shared between SSL context structures. 364 | mbedtls_ssl_config_defaults(&ctx->ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); 365 | mbedtls_ssl_conf_authmode(&ctx->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); 366 | mbedtls_ssl_conf_ca_chain(&ctx->ssl_conf, &ctx->root_ca_cert, NULL); 367 | mbedtls_ssl_conf_rng(&ctx->ssl_conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg); 368 | 369 | // SSL Context 370 | mbedtls_ssl_set_hostname(&ctx->ssl, ctx->server_host_name); 371 | int ssl_setup_result = mbedtls_ssl_setup(&ctx->ssl, &ctx->ssl_conf); 372 | if (ssl_setup_result) { 373 | wifi_tls_print_mbedtls_error("wifi_tls_init_context: mbedtls_ssl_setup failed", ssl_setup_result); 374 | wifi_tls_reset_context(ctx); 375 | return -1; 376 | } 377 | 378 | ESP_LOGD(TAG, "wifi_tls_init_context: context initialised for server: %s", ctx->server_host_name); 379 | ctx->is_valid = true; 380 | 381 | return 0; 382 | } 383 | 384 | static int wifi_tls_reset_context(wifi_tls_context_t *ctx) 385 | { 386 | ctx->is_valid = false; 387 | mbedtls_entropy_free(&ctx->entropy); 388 | mbedtls_ssl_config_free(&ctx->ssl_conf); 389 | mbedtls_ctr_drbg_free(&ctx->ctr_drbg); 390 | mbedtls_x509_crt_free(&ctx->root_ca_cert); 391 | mbedtls_x509_crt_free(&ctx->peer_cert); 392 | mbedtls_ssl_free(&ctx->ssl); 393 | 394 | ESP_LOGD(TAG, "wifi_tls_reset_context: context reset for server: %s", ctx->server_host_name); 395 | return 0; 396 | } 397 | 398 | static int wifi_tls_handshake(wifi_tls_context_t *ctx) 399 | { 400 | while (1) { 401 | ESP_LOGD(TAG, "wifi_tls_handshake: mbedtls_ssl_handshake"); 402 | int handshakeResult = mbedtls_ssl_handshake(&ctx->ssl); 403 | ESP_LOGD(TAG, "wifi_tls_handshake: mbedtls_ssl_handshake: %d", handshakeResult); 404 | if (handshakeResult == 0) { 405 | // Handshake completed. 406 | ESP_LOGD(TAG, "wifi_tls_handshake: handshake completed successfully"); 407 | break; 408 | } 409 | 410 | if (handshakeResult == MBEDTLS_ERR_SSL_WANT_READ 411 | || handshakeResult == MBEDTLS_ERR_SSL_WANT_WRITE) 412 | { 413 | ESP_LOGD(TAG, "wifi_tls_handshake: handshake continuing (%d)", handshakeResult); 414 | continue; 415 | } 416 | 417 | wifi_tls_print_mbedtls_error("wifi_tls_handshake: handshake failed", handshakeResult); 418 | return -1; 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | // returns 0 on success, -1 on error 425 | static int wifi_tls_cert_pinning(wifi_tls_context_t *ctx) 426 | { 427 | // Get the peer certificate from the connection. 428 | 429 | const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&ctx->ssl); 430 | if (!cert) { 431 | ESP_LOGE(TAG, "wifi_tls_cert_pinning: failed to get peer certificate"); 432 | return -1; 433 | } 434 | 435 | 436 | // Allocate memory to store the actual and the expected public keys. 437 | 438 | int bufSize = 512; // in practice, the length was 294 bytes 439 | uint8_t *certExpectedPubKey = calloc(bufSize, 1); 440 | uint8_t *certActualPubKey = calloc(bufSize, 1); 441 | if (!certExpectedPubKey || !certActualPubKey) { 442 | ESP_LOGE(TAG, "wifi_tls_cert_pinning: failed to allocate memory for the public key"); 443 | if (certExpectedPubKey) { 444 | free(certExpectedPubKey); 445 | } 446 | if (certActualPubKey) { 447 | free(certActualPubKey); 448 | } 449 | return -1; 450 | } 451 | 452 | 453 | // Extract the public keys from the certificates. 454 | 455 | // mbedTLS writes the data at the *end* of the buffer...! 456 | int lenExpected = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *) &(ctx->peer_cert.pk), certExpectedPubKey, bufSize); 457 | int lenActual = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *) &(cert->pk), certActualPubKey, bufSize); 458 | 459 | 460 | // Compare the expected to the actual public key. 461 | 462 | int result = -1; // assume failure 463 | if (lenExpected == lenActual) { 464 | if (!memcmp(certExpectedPubKey, certActualPubKey, lenActual)) { 465 | result = 0; // length and content matches 466 | } 467 | } 468 | 469 | // In case of a mismatch, we print the two public keys to simplify debugging. 470 | if (result) { 471 | ESP_LOGE(TAG, "wifi_tls_cert_pinning: actual public key different from expected public key!\n"); 472 | 473 | ESP_LOGE(TAG, "EXPECTED public key (%d bytes):", lenExpected); 474 | wifi_tls_dump_hex_buffer((char*)&certExpectedPubKey[bufSize - lenExpected], lenExpected); 475 | 476 | ESP_LOGE(TAG, "ACTUAL public key (%d bytes):", lenActual); 477 | wifi_tls_dump_hex_buffer((char*)&certActualPubKey[bufSize - lenActual], lenActual); 478 | } 479 | 480 | free(certExpectedPubKey); 481 | free(certActualPubKey); 482 | 483 | return result; 484 | } 485 | 486 | static void wifi_tls_print_mbedtls_error(char *message, int code) 487 | { 488 | char errorDescBuf[256]; 489 | mbedtls_strerror(code, errorDescBuf, sizeof(errorDescBuf) / sizeof(char)); 490 | ESP_LOGE(TAG, "%s: %d %s", message, code, errorDescBuf); 491 | } 492 | 493 | static void wifi_tls_dump_hex_buffer(char *buf, int len) 494 | { 495 | char bufAscii[17]; 496 | bufAscii[16] = 0x00; 497 | 498 | for (int i = 0; i < len; i++) { 499 | uint8_t c = buf[i]; 500 | bufAscii[i % 16] = (c >= 32 && c < 127) ? (char)c : '.'; 501 | printf("%02x ", (char)c); 502 | if ((i + 1) == len) { 503 | for (int j = 0; j < (15 - (i % 16)); j++) { 504 | printf(" "); 505 | } 506 | } 507 | if (i % 16 == 15 || (i + 1) == len) { 508 | printf(" %s\n", bufAscii); 509 | for (int j = 0; j < 16; j++) { 510 | bufAscii[j] = ' '; 511 | } 512 | } 513 | } 514 | printf("\n"); 515 | } 516 | 517 | -------------------------------------------------------------------------------- /main/wifi_tls.h: -------------------------------------------------------------------------------- 1 | // 2 | // wifi_tls.h 3 | // esp32-ota-https 4 | // 5 | // Updating the firmware over the air. 6 | // 7 | // This module provides TLS connections with certificate pinning and 8 | // callback-based request/response functionality. 9 | // 10 | // Created by Andreas Schweizer on 11.01.2017. 11 | // Copyright © 2017 Classy Code GmbH 12 | // 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | // software and associated documentation files (the "Software"), to deal in the Software 15 | // without restriction, including without limitation the rights to use, copy, modify, 16 | // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | // permit persons to whom the Software is furnished to do so, subject to the following 18 | // conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies 21 | // or substantial portions of the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #ifndef __WIFI_TLS__ 32 | #define __WIFI_TLS__ 1 33 | 34 | 35 | // Forward declaration of the opaque context object. 36 | struct wifi_tls_context_; 37 | 38 | typedef struct wifi_tls_init_struct_ { 39 | 40 | // Name of the host that provides the firmware images, e.g. "www.classycode.io". 41 | const char *server_host_name; 42 | 43 | // Port for the connection, e.g. "443". 44 | const char *server_port; 45 | 46 | // Public key of the server's root CA certificate. 47 | // Needs to be in PEM format (base64-encoded DER data with begin and end marker). 48 | const char *server_root_ca_public_key_pem; 49 | 50 | // Public key of the server's peer certificate for certificate pinning. 51 | // Needs to be in PEM format (base64-encoded DER data with begin and end marker). 52 | const char *peer_public_key_pem; 53 | 54 | } wifi_tls_init_struct_t; 55 | 56 | typedef struct wifi_tls_request_ { 57 | 58 | // Request buffer. 59 | // Example: "GET https://www.classycode.io/esp32/ota.txt HTTP/1.1\nHost: www.classycode.io\n\n" 60 | // Not necessarily zero-terminated. 61 | char *request_buffer; 62 | 63 | // Number of bytes in the request buffer. 64 | uint32_t request_len; 65 | 66 | // Response buffer. 67 | // This buffer will be filled with the data received from the server. 68 | // Data may be received in chunks. Every chunk is stored in the buffer and provided 69 | // to the client via the response_callback function (see below). 70 | char *response_buffer; 71 | 72 | // Size of the response buffer. 73 | // Defines the maximum number of bytes that will be read into the response buffer. 74 | uint32_t response_buffer_size; 75 | 76 | // Make custom data available to the callback. 77 | void *custom_data; 78 | 79 | // Callback function to handle the response. 80 | // Return 1 to continue reading, 0 to end reading. 81 | int (*response_callback)(struct wifi_tls_context_ *context, struct wifi_tls_request_ *request, int index, size_t len); 82 | 83 | } wifi_tls_request_t; 84 | 85 | 86 | // Create a context for TLS communication to a server. 87 | // The context can be re-used for multiple connections to the same server on the same port. 88 | // The init structure and all fields can be released after calling this function. 89 | struct wifi_tls_context_ *wifi_tls_create_context(wifi_tls_init_struct_t *params); 90 | 91 | // Release the context. 92 | // Performs necessary cleanup and releases all memory associated with the context. 93 | void wifi_tls_free_context(struct wifi_tls_context_ *context); 94 | 95 | // Connects to the server, performs the TLS handshake and certificate verification. 96 | // Returns 0 on success. 97 | int wifi_tls_connect(struct wifi_tls_context_ *context); 98 | 99 | // Disconnects from the server. 100 | void wifi_tls_disconnect(struct wifi_tls_context_ *context); 101 | 102 | // Send a request to the server. 103 | // Calls the response callback function defined in the request structure. 104 | // Returns 0 on success. 105 | int wifi_tls_send_request(struct wifi_tls_context_ *context, wifi_tls_request_t *request); 106 | 107 | 108 | #endif // __WIFI_TLS__ 109 | -------------------------------------------------------------------------------- /meta/ota.txt: -------------------------------------------------------------------------------- 1 | FILE=/esp32/esp32-ota-https.bin 2 | VERSION=1 3 | INTERVAL=10 4 | -------------------------------------------------------------------------------- /sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Espressif IoT Development Framework Configuration 4 | # 5 | 6 | # 7 | # SDK tool configuration 8 | # 9 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 10 | CONFIG_PYTHON="python" 11 | CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y 12 | 13 | # 14 | # Bootloader config 15 | # 16 | CONFIG_LOG_BOOTLOADER_LEVEL_NONE= 17 | CONFIG_LOG_BOOTLOADER_LEVEL_ERROR= 18 | CONFIG_LOG_BOOTLOADER_LEVEL_WARN= 19 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y 20 | CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG= 21 | CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE= 22 | CONFIG_LOG_BOOTLOADER_LEVEL=3 23 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V= 24 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 25 | CONFIG_BOOTLOADER_FACTORY_RESET= 26 | CONFIG_BOOTLOADER_APP_TEST= 27 | 28 | # 29 | # Security features 30 | # 31 | CONFIG_SECURE_BOOT_ENABLED= 32 | CONFIG_FLASH_ENCRYPTION_ENABLED= 33 | 34 | # 35 | # Serial flasher config 36 | # 37 | CONFIG_ESPTOOLPY_PORT="/dev/tty.usbserial-DN027YY7" 38 | CONFIG_ESPTOOLPY_BAUD_115200B= 39 | CONFIG_ESPTOOLPY_BAUD_230400B= 40 | CONFIG_ESPTOOLPY_BAUD_921600B=y 41 | CONFIG_ESPTOOLPY_BAUD_2MB= 42 | CONFIG_ESPTOOLPY_BAUD_OTHER= 43 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 44 | CONFIG_ESPTOOLPY_BAUD=921600 45 | CONFIG_ESPTOOLPY_COMPRESSED=y 46 | CONFIG_FLASHMODE_QIO= 47 | CONFIG_FLASHMODE_QOUT= 48 | CONFIG_FLASHMODE_DIO=y 49 | CONFIG_FLASHMODE_DOUT= 50 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 51 | CONFIG_ESPTOOLPY_FLASHFREQ_80M= 52 | CONFIG_ESPTOOLPY_FLASHFREQ_40M=y 53 | CONFIG_ESPTOOLPY_FLASHFREQ_26M= 54 | CONFIG_ESPTOOLPY_FLASHFREQ_20M= 55 | CONFIG_ESPTOOLPY_FLASHFREQ="40m" 56 | CONFIG_ESPTOOLPY_FLASHSIZE_1MB= 57 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB= 58 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 59 | CONFIG_ESPTOOLPY_FLASHSIZE_8MB= 60 | CONFIG_ESPTOOLPY_FLASHSIZE_16MB= 61 | CONFIG_ESPTOOLPY_FLASHSIZE="4MB" 62 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 63 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 64 | CONFIG_ESPTOOLPY_BEFORE_NORESET= 65 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 66 | CONFIG_ESPTOOLPY_AFTER_RESET=y 67 | CONFIG_ESPTOOLPY_AFTER_NORESET= 68 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 69 | CONFIG_MONITOR_BAUD_9600B= 70 | CONFIG_MONITOR_BAUD_57600B= 71 | CONFIG_MONITOR_BAUD_115200B=y 72 | CONFIG_MONITOR_BAUD_230400B= 73 | CONFIG_MONITOR_BAUD_921600B= 74 | CONFIG_MONITOR_BAUD_2MB= 75 | CONFIG_MONITOR_BAUD_OTHER= 76 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 77 | CONFIG_MONITOR_BAUD=115200 78 | 79 | # 80 | # Partition Table 81 | # 82 | CONFIG_PARTITION_TABLE_SINGLE_APP= 83 | CONFIG_PARTITION_TABLE_TWO_OTA=y 84 | CONFIG_PARTITION_TABLE_CUSTOM= 85 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 86 | CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 87 | CONFIG_PARTITION_TABLE_FILENAME="partitions_two_ota.csv" 88 | CONFIG_APP_OFFSET=0x10000 89 | CONFIG_PARTITION_TABLE_MD5=y 90 | 91 | # 92 | # Compiler options 93 | # 94 | CONFIG_OPTIMIZATION_LEVEL_DEBUG=y 95 | CONFIG_OPTIMIZATION_LEVEL_RELEASE= 96 | CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y 97 | CONFIG_OPTIMIZATION_ASSERTIONS_SILENT= 98 | CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED= 99 | CONFIG_CXX_EXCEPTIONS= 100 | CONFIG_STACK_CHECK_NONE=y 101 | CONFIG_STACK_CHECK_NORM= 102 | CONFIG_STACK_CHECK_STRONG= 103 | CONFIG_STACK_CHECK_ALL= 104 | CONFIG_STACK_CHECK= 105 | CONFIG_WARN_WRITE_STRINGS= 106 | 107 | # 108 | # Component config 109 | # 110 | 111 | # 112 | # Application Level Tracing 113 | # 114 | CONFIG_ESP32_APPTRACE_DEST_TRAX= 115 | CONFIG_ESP32_APPTRACE_DEST_NONE=y 116 | CONFIG_ESP32_APPTRACE_ENABLE= 117 | CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y 118 | CONFIG_AWS_IOT_SDK= 119 | 120 | # 121 | # Bluetooth 122 | # 123 | CONFIG_BT_ENABLED= 124 | CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 125 | CONFIG_BT_RESERVE_DRAM=0 126 | 127 | # 128 | # ADC configuration 129 | # 130 | CONFIG_ADC_FORCE_XPD_FSM= 131 | CONFIG_ADC2_DISABLE_DAC=y 132 | 133 | # 134 | # ESP32-specific 135 | # 136 | CONFIG_ESP32_DEFAULT_CPU_FREQ_80= 137 | CONFIG_ESP32_DEFAULT_CPU_FREQ_160= 138 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 139 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 140 | CONFIG_SPIRAM_SUPPORT= 141 | CONFIG_MEMMAP_TRACEMEM= 142 | CONFIG_MEMMAP_TRACEMEM_TWOBANKS= 143 | CONFIG_ESP32_TRAX= 144 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 145 | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH= 146 | CONFIG_ESP32_ENABLE_COREDUMP_TO_UART= 147 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 148 | CONFIG_ESP32_ENABLE_COREDUMP= 149 | CONFIG_TWO_UNIVERSAL_MAC_ADDRESS= 150 | CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y 151 | CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 152 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 153 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4096 154 | CONFIG_MAIN_TASK_STACK_SIZE=4096 155 | CONFIG_IPC_TASK_STACK_SIZE=1024 156 | CONFIG_TIMER_TASK_STACK_SIZE=3584 157 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 158 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF= 159 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR= 160 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF= 161 | CONFIG_NEWLIB_STDIN_LINE_ENDING_LF= 162 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y 163 | CONFIG_NEWLIB_NANO_FORMAT= 164 | CONFIG_CONSOLE_UART_DEFAULT=y 165 | CONFIG_CONSOLE_UART_CUSTOM= 166 | CONFIG_CONSOLE_UART_NONE= 167 | CONFIG_CONSOLE_UART_NUM=0 168 | CONFIG_CONSOLE_UART_BAUDRATE=115200 169 | CONFIG_ULP_COPROC_ENABLED= 170 | CONFIG_ULP_COPROC_RESERVE_MEM=0 171 | CONFIG_ESP32_PANIC_PRINT_HALT= 172 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 173 | CONFIG_ESP32_PANIC_SILENT_REBOOT= 174 | CONFIG_ESP32_PANIC_GDBSTUB= 175 | CONFIG_ESP32_DEBUG_OCDAWARE=y 176 | CONFIG_ESP32_DEBUG_STUBS_ENABLE=y 177 | CONFIG_INT_WDT=y 178 | CONFIG_INT_WDT_TIMEOUT_MS=300 179 | CONFIG_TASK_WDT=y 180 | CONFIG_TASK_WDT_PANIC= 181 | CONFIG_TASK_WDT_TIMEOUT_S=5 182 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 183 | CONFIG_BROWNOUT_DET=y 184 | CONFIG_BROWNOUT_DET_LVL_SEL_0=y 185 | CONFIG_BROWNOUT_DET_LVL_SEL_1= 186 | CONFIG_BROWNOUT_DET_LVL_SEL_2= 187 | CONFIG_BROWNOUT_DET_LVL_SEL_3= 188 | CONFIG_BROWNOUT_DET_LVL_SEL_4= 189 | CONFIG_BROWNOUT_DET_LVL_SEL_5= 190 | CONFIG_BROWNOUT_DET_LVL_SEL_6= 191 | CONFIG_BROWNOUT_DET_LVL_SEL_7= 192 | CONFIG_BROWNOUT_DET_LVL=0 193 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 194 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC= 195 | CONFIG_ESP32_TIME_SYSCALL_USE_FRC1= 196 | CONFIG_ESP32_TIME_SYSCALL_USE_NONE= 197 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 198 | CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL= 199 | CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 200 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=0 201 | CONFIG_ESP32_XTAL_FREQ_40=y 202 | CONFIG_ESP32_XTAL_FREQ_26= 203 | CONFIG_ESP32_XTAL_FREQ_AUTO= 204 | CONFIG_ESP32_XTAL_FREQ=40 205 | CONFIG_DISABLE_BASIC_ROM_CONSOLE= 206 | CONFIG_NO_BLOBS= 207 | CONFIG_ESP_TIMER_PROFILING= 208 | CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS= 209 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 210 | 211 | # 212 | # Wi-Fi 213 | # 214 | CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 215 | CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 216 | CONFIG_ESP32_WIFI_STATIC_TX_BUFFER= 217 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y 218 | CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 219 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 220 | CONFIG_ESP32_WIFI_CSI_ENABLED= 221 | CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y 222 | CONFIG_ESP32_WIFI_TX_BA_WIN=6 223 | CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y 224 | CONFIG_ESP32_WIFI_RX_BA_WIN=6 225 | CONFIG_ESP32_WIFI_NVS_ENABLED=y 226 | 227 | # 228 | # PHY 229 | # 230 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 231 | CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION= 232 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 233 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 234 | 235 | # 236 | # Power Management 237 | # 238 | CONFIG_PM_ENABLE= 239 | 240 | # 241 | # ADC-Calibration 242 | # 243 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 244 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 245 | CONFIG_ADC_CAL_LUT_ENABLE=y 246 | 247 | # 248 | # ESP HTTP client 249 | # 250 | CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y 251 | 252 | # 253 | # Ethernet 254 | # 255 | CONFIG_DMA_RX_BUF_NUM=10 256 | CONFIG_DMA_TX_BUF_NUM=10 257 | CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE= 258 | CONFIG_EMAC_TASK_PRIORITY=20 259 | 260 | # 261 | # FAT Filesystem support 262 | # 263 | CONFIG_FATFS_CODEPAGE_DYNAMIC= 264 | CONFIG_FATFS_CODEPAGE_437=y 265 | CONFIG_FATFS_CODEPAGE_720= 266 | CONFIG_FATFS_CODEPAGE_737= 267 | CONFIG_FATFS_CODEPAGE_771= 268 | CONFIG_FATFS_CODEPAGE_775= 269 | CONFIG_FATFS_CODEPAGE_850= 270 | CONFIG_FATFS_CODEPAGE_852= 271 | CONFIG_FATFS_CODEPAGE_855= 272 | CONFIG_FATFS_CODEPAGE_857= 273 | CONFIG_FATFS_CODEPAGE_860= 274 | CONFIG_FATFS_CODEPAGE_861= 275 | CONFIG_FATFS_CODEPAGE_862= 276 | CONFIG_FATFS_CODEPAGE_863= 277 | CONFIG_FATFS_CODEPAGE_864= 278 | CONFIG_FATFS_CODEPAGE_865= 279 | CONFIG_FATFS_CODEPAGE_866= 280 | CONFIG_FATFS_CODEPAGE_869= 281 | CONFIG_FATFS_CODEPAGE_932= 282 | CONFIG_FATFS_CODEPAGE_936= 283 | CONFIG_FATFS_CODEPAGE_949= 284 | CONFIG_FATFS_CODEPAGE_950= 285 | CONFIG_FATFS_CODEPAGE=437 286 | CONFIG_FATFS_LFN_NONE=y 287 | CONFIG_FATFS_LFN_HEAP= 288 | CONFIG_FATFS_LFN_STACK= 289 | CONFIG_FATFS_FS_LOCK=0 290 | CONFIG_FATFS_TIMEOUT_MS=10000 291 | CONFIG_FATFS_PER_FILE_CACHE=y 292 | 293 | # 294 | # FreeRTOS 295 | # 296 | CONFIG_FREERTOS_UNICORE=y 297 | CONFIG_FREERTOS_CORETIMER_0=y 298 | CONFIG_FREERTOS_CORETIMER_1= 299 | CONFIG_FREERTOS_HZ=100 300 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 301 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE= 302 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL= 303 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y 304 | CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK= 305 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y 306 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 307 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 308 | CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE= 309 | CONFIG_FREERTOS_ASSERT_DISABLE= 310 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 311 | CONFIG_FREERTOS_ISR_STACKSIZE=1536 312 | CONFIG_FREERTOS_LEGACY_HOOKS= 313 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 314 | CONFIG_SUPPORT_STATIC_ALLOCATION= 315 | CONFIG_TIMER_TASK_PRIORITY=1 316 | CONFIG_TIMER_TASK_STACK_DEPTH=2048 317 | CONFIG_TIMER_QUEUE_LENGTH=10 318 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 319 | CONFIG_FREERTOS_USE_TRACE_FACILITY= 320 | CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS= 321 | CONFIG_FREERTOS_DEBUG_INTERNALS= 322 | 323 | # 324 | # Heap memory debugging 325 | # 326 | CONFIG_HEAP_POISONING_DISABLED=y 327 | CONFIG_HEAP_POISONING_LIGHT= 328 | CONFIG_HEAP_POISONING_COMPREHENSIVE= 329 | CONFIG_HEAP_TRACING= 330 | 331 | # 332 | # libsodium 333 | # 334 | 335 | # 336 | # Log output 337 | # 338 | CONFIG_LOG_DEFAULT_LEVEL_NONE= 339 | CONFIG_LOG_DEFAULT_LEVEL_ERROR= 340 | CONFIG_LOG_DEFAULT_LEVEL_WARN= 341 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 342 | CONFIG_LOG_DEFAULT_LEVEL_DEBUG= 343 | CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= 344 | CONFIG_LOG_DEFAULT_LEVEL=3 345 | CONFIG_LOG_COLORS=y 346 | 347 | # 348 | # LWIP 349 | # 350 | CONFIG_L2_TO_L3_COPY= 351 | CONFIG_LWIP_IRAM_OPTIMIZATION= 352 | CONFIG_LWIP_MAX_SOCKETS=10 353 | CONFIG_USE_ONLY_LWIP_SELECT= 354 | CONFIG_LWIP_SO_REUSE= 355 | CONFIG_LWIP_SO_RCVBUF= 356 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 357 | CONFIG_LWIP_IP_FRAG= 358 | CONFIG_LWIP_IP_REASSEMBLY= 359 | CONFIG_LWIP_STATS= 360 | CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y 361 | CONFIG_TCPIP_RECVMBOX_SIZE=32 362 | CONFIG_LWIP_DHCP_DOES_ARP_CHECK= 363 | 364 | # 365 | # DHCP server 366 | # 367 | CONFIG_LWIP_DHCPS_LEASE_UNIT=60 368 | CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 369 | CONFIG_LWIP_AUTOIP= 370 | CONFIG_LWIP_NETIF_LOOPBACK=y 371 | CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 372 | 373 | # 374 | # TCP 375 | # 376 | CONFIG_LWIP_MAX_ACTIVE_TCP=16 377 | CONFIG_LWIP_MAX_LISTENING_TCP=16 378 | CONFIG_TCP_MAXRTX=12 379 | CONFIG_TCP_SYNMAXRTX=6 380 | CONFIG_TCP_MSS=1436 381 | CONFIG_TCP_MSL=60000 382 | CONFIG_TCP_SND_BUF_DEFAULT=5744 383 | CONFIG_TCP_WND_DEFAULT=5744 384 | CONFIG_TCP_RECVMBOX_SIZE=6 385 | CONFIG_TCP_QUEUE_OOSEQ=y 386 | CONFIG_TCP_OVERSIZE_MSS=y 387 | CONFIG_TCP_OVERSIZE_QUARTER_MSS= 388 | CONFIG_TCP_OVERSIZE_DISABLE= 389 | 390 | # 391 | # UDP 392 | # 393 | CONFIG_LWIP_MAX_UDP_PCBS=16 394 | CONFIG_UDP_RECVMBOX_SIZE=6 395 | CONFIG_TCPIP_TASK_STACK_SIZE=2048 396 | CONFIG_PPP_SUPPORT= 397 | 398 | # 399 | # ICMP 400 | # 401 | CONFIG_LWIP_MULTICAST_PING= 402 | CONFIG_LWIP_BROADCAST_PING= 403 | 404 | # 405 | # LWIP RAW API 406 | # 407 | CONFIG_LWIP_MAX_RAW_PCBS=16 408 | 409 | # 410 | # mbedTLS 411 | # 412 | CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 413 | CONFIG_MBEDTLS_DEBUG= 414 | CONFIG_MBEDTLS_HARDWARE_AES=y 415 | CONFIG_MBEDTLS_HARDWARE_MPI=y 416 | CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y 417 | CONFIG_MBEDTLS_HARDWARE_SHA=y 418 | CONFIG_MBEDTLS_HAVE_TIME=y 419 | CONFIG_MBEDTLS_HAVE_TIME_DATE= 420 | CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y 421 | CONFIG_MBEDTLS_TLS_SERVER_ONLY= 422 | CONFIG_MBEDTLS_TLS_CLIENT_ONLY= 423 | CONFIG_MBEDTLS_TLS_DISABLED= 424 | CONFIG_MBEDTLS_TLS_SERVER=y 425 | CONFIG_MBEDTLS_TLS_CLIENT=y 426 | CONFIG_MBEDTLS_TLS_ENABLED=y 427 | 428 | # 429 | # TLS Key Exchange Methods 430 | # 431 | CONFIG_MBEDTLS_PSK_MODES= 432 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y 433 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y 434 | CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y 435 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y 436 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y 437 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y 438 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y 439 | CONFIG_MBEDTLS_SSL_RENEGOTIATION=y 440 | CONFIG_MBEDTLS_SSL_PROTO_SSL3= 441 | CONFIG_MBEDTLS_SSL_PROTO_TLS1=y 442 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y 443 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y 444 | CONFIG_MBEDTLS_SSL_PROTO_DTLS= 445 | CONFIG_MBEDTLS_SSL_ALPN=y 446 | CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y 447 | 448 | # 449 | # Symmetric Ciphers 450 | # 451 | CONFIG_MBEDTLS_AES_C=y 452 | CONFIG_MBEDTLS_CAMELLIA_C= 453 | CONFIG_MBEDTLS_DES_C= 454 | CONFIG_MBEDTLS_RC4_DISABLED=y 455 | CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT= 456 | CONFIG_MBEDTLS_RC4_ENABLED= 457 | CONFIG_MBEDTLS_BLOWFISH_C= 458 | CONFIG_MBEDTLS_XTEA_C= 459 | CONFIG_MBEDTLS_CCM_C=y 460 | CONFIG_MBEDTLS_GCM_C=y 461 | CONFIG_MBEDTLS_RIPEMD160_C= 462 | 463 | # 464 | # Certificates 465 | # 466 | CONFIG_MBEDTLS_PEM_PARSE_C=y 467 | CONFIG_MBEDTLS_PEM_WRITE_C=y 468 | CONFIG_MBEDTLS_X509_CRL_PARSE_C=y 469 | CONFIG_MBEDTLS_X509_CSR_PARSE_C=y 470 | CONFIG_MBEDTLS_ECP_C=y 471 | CONFIG_MBEDTLS_ECDH_C=y 472 | CONFIG_MBEDTLS_ECDSA_C=y 473 | CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y 474 | CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y 475 | CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y 476 | CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y 477 | CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y 478 | CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y 479 | CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y 480 | CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y 481 | CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y 482 | CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y 483 | CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y 484 | CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y 485 | CONFIG_MBEDTLS_ECP_NIST_OPTIM=y 486 | 487 | # 488 | # OpenSSL 489 | # 490 | CONFIG_OPENSSL_DEBUG= 491 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 492 | CONFIG_OPENSSL_ASSERT_EXIT= 493 | 494 | # 495 | # PThreads 496 | # 497 | CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 498 | CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 499 | 500 | # 501 | # SPI Flash driver 502 | # 503 | CONFIG_SPI_FLASH_VERIFY_WRITE= 504 | CONFIG_SPI_FLASH_ENABLE_COUNTERS= 505 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 506 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y 507 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS= 508 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED= 509 | 510 | # 511 | # SPIFFS Configuration 512 | # 513 | CONFIG_SPIFFS_MAX_PARTITIONS=3 514 | 515 | # 516 | # SPIFFS Cache Configuration 517 | # 518 | CONFIG_SPIFFS_CACHE=y 519 | CONFIG_SPIFFS_CACHE_WR=y 520 | CONFIG_SPIFFS_CACHE_STATS= 521 | CONFIG_SPIFFS_PAGE_CHECK=y 522 | CONFIG_SPIFFS_GC_MAX_RUNS=10 523 | CONFIG_SPIFFS_GC_STATS= 524 | CONFIG_SPIFFS_PAGE_SIZE=256 525 | CONFIG_SPIFFS_OBJ_NAME_LEN=32 526 | CONFIG_SPIFFS_USE_MAGIC=y 527 | CONFIG_SPIFFS_USE_MAGIC_LENGTH=y 528 | CONFIG_SPIFFS_META_LENGTH=4 529 | CONFIG_SPIFFS_USE_MTIME=y 530 | 531 | # 532 | # Debug Configuration 533 | # 534 | CONFIG_SPIFFS_DBG= 535 | CONFIG_SPIFFS_API_DBG= 536 | CONFIG_SPIFFS_GC_DBG= 537 | CONFIG_SPIFFS_CACHE_DBG= 538 | CONFIG_SPIFFS_CHECK_DBG= 539 | CONFIG_SPIFFS_TEST_VISUALISATION= 540 | 541 | # 542 | # tcpip adapter 543 | # 544 | CONFIG_IP_LOST_TIMER_INTERVAL=120 545 | 546 | # 547 | # Virtual file system 548 | # 549 | CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y 550 | 551 | # 552 | # Wear Levelling 553 | # 554 | CONFIG_WL_SECTOR_SIZE_512= 555 | CONFIG_WL_SECTOR_SIZE_4096=y 556 | CONFIG_WL_SECTOR_SIZE=4096 557 | --------------------------------------------------------------------------------