esp_local_ctrl_service.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /* Local Ctrl Example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <stdlib.h>
  8. #include <stdint.h>
  9. #include <sys/param.h>
  10. #include <string.h>
  11. #include <inttypes.h>
  12. #include <freertos/FreeRTOS.h>
  13. #include <freertos/task.h>
  14. #include <mdns.h>
  15. #include <esp_log.h>
  16. #include <esp_timer.h>
  17. #include <esp_local_ctrl.h>
  18. #ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
  19. #include <esp_https_server.h>
  20. #else
  21. #include <esp_http_server.h>
  22. #endif
  23. static const char *TAG = "control";
  24. #define SERVICE_NAME "my_esp_ctrl_device"
  25. #if CONFIG_EXAMPLE_PROTOCOMM_SECURITY_VERSION_2
  26. #if CONFIG_EXAMPLE_PROTOCOMM_SEC2_DEV_MODE
  27. #define EXAMPLE_PROTOCOMM_SEC2_USERNAME "wifiprov"
  28. #define EXAMPLE_PROTOCOMM_SEC2_PWD "abcd1234"
  29. /* This salt,verifier has been generated for username = "localctrl" and password = "abcd1234"
  30. * IMPORTANT NOTE: For production cases, this must be unique to every device
  31. * and should come from device manufacturing partition.*/
  32. static const char sec2_salt[] = {
  33. 0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4
  34. };
  35. static const char sec2_verifier[] = {
  36. 0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43,
  37. 0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24,
  38. 0xa4, 0x70, 0x26, 0x1c, 0xdf, 0xa9, 0x03, 0xc2, 0xb2, 0x70, 0xe7, 0xb1, 0x32, 0x24, 0xda, 0x11,
  39. 0x1d, 0x97, 0x18, 0xdc, 0x60, 0x72, 0x08, 0xcc, 0x9a, 0xc9, 0x0c, 0x48, 0x27, 0xe2, 0xae, 0x89,
  40. 0xaa, 0x16, 0x25, 0xb8, 0x04, 0xd2, 0x1a, 0x9b, 0x3a, 0x8f, 0x37, 0xf6, 0xe4, 0x3a, 0x71, 0x2e,
  41. 0xe1, 0x27, 0x86, 0x6e, 0xad, 0xce, 0x28, 0xff, 0x54, 0x46, 0x60, 0x1f, 0xb9, 0x96, 0x87, 0xdc,
  42. 0x57, 0x40, 0xa7, 0xd4, 0x6c, 0xc9, 0x77, 0x54, 0xdc, 0x16, 0x82, 0xf0, 0xed, 0x35, 0x6a, 0xc4,
  43. 0x70, 0xad, 0x3d, 0x90, 0xb5, 0x81, 0x94, 0x70, 0xd7, 0xbc, 0x65, 0xb2, 0xd5, 0x18, 0xe0, 0x2e,
  44. 0xc3, 0xa5, 0xf9, 0x68, 0xdd, 0x64, 0x7b, 0xb8, 0xb7, 0x3c, 0x9c, 0xfc, 0x00, 0xd8, 0x71, 0x7e,
  45. 0xb7, 0x9a, 0x7c, 0xb1, 0xb7, 0xc2, 0xc3, 0x18, 0x34, 0x29, 0x32, 0x43, 0x3e, 0x00, 0x99, 0xe9,
  46. 0x82, 0x94, 0xe3, 0xd8, 0x2a, 0xb0, 0x96, 0x29, 0xb7, 0xdf, 0x0e, 0x5f, 0x08, 0x33, 0x40, 0x76,
  47. 0x52, 0x91, 0x32, 0x00, 0x9f, 0x97, 0x2c, 0x89, 0x6c, 0x39, 0x1e, 0xc8, 0x28, 0x05, 0x44, 0x17,
  48. 0x3f, 0x68, 0x02, 0x8a, 0x9f, 0x44, 0x61, 0xd1, 0xf5, 0xa1, 0x7e, 0x5a, 0x70, 0xd2, 0xc7, 0x23,
  49. 0x81, 0xcb, 0x38, 0x68, 0xe4, 0x2c, 0x20, 0xbc, 0x40, 0x57, 0x76, 0x17, 0xbd, 0x08, 0xb8, 0x96,
  50. 0xbc, 0x26, 0xeb, 0x32, 0x46, 0x69, 0x35, 0x05, 0x8c, 0x15, 0x70, 0xd9, 0x1b, 0xe9, 0xbe, 0xcc,
  51. 0xa9, 0x38, 0xa6, 0x67, 0xf0, 0xad, 0x50, 0x13, 0x19, 0x72, 0x64, 0xbf, 0x52, 0xc2, 0x34, 0xe2,
  52. 0x1b, 0x11, 0x79, 0x74, 0x72, 0xbd, 0x34, 0x5b, 0xb1, 0xe2, 0xfd, 0x66, 0x73, 0xfe, 0x71, 0x64,
  53. 0x74, 0xd0, 0x4e, 0xbc, 0x51, 0x24, 0x19, 0x40, 0x87, 0x0e, 0x92, 0x40, 0xe6, 0x21, 0xe7, 0x2d,
  54. 0x4e, 0x37, 0x76, 0x2f, 0x2e, 0xe2, 0x68, 0xc7, 0x89, 0xe8, 0x32, 0x13, 0x42, 0x06, 0x84, 0x84,
  55. 0x53, 0x4a, 0xb3, 0x0c, 0x1b, 0x4c, 0x8d, 0x1c, 0x51, 0x97, 0x19, 0xab, 0xae, 0x77, 0xff, 0xdb,
  56. 0xec, 0xf0, 0x10, 0x95, 0x34, 0x33, 0x6b, 0xcb, 0x3e, 0x84, 0x0f, 0xb9, 0xd8, 0x5f, 0xb8, 0xa0,
  57. 0xb8, 0x55, 0x53, 0x3e, 0x70, 0xf7, 0x18, 0xf5, 0xce, 0x7b, 0x4e, 0xbf, 0x27, 0xce, 0xce, 0xa8,
  58. 0xb3, 0xbe, 0x40, 0xc5, 0xc5, 0x32, 0x29, 0x3e, 0x71, 0x64, 0x9e, 0xde, 0x8c, 0xf6, 0x75, 0xa1,
  59. 0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba
  60. };
  61. #endif
  62. static esp_err_t example_get_sec2_salt(const char **salt, uint16_t *salt_len) {
  63. #if CONFIG_EXAMPLE_PROTOCOMM_SEC2_DEV_MODE
  64. ESP_LOGI(TAG, "Development mode: using hard coded salt");
  65. *salt = sec2_salt;
  66. *salt_len = sizeof(sec2_salt);
  67. return ESP_OK;
  68. #elif CONFIG_EXAMPLE_PROTOCOMM_SEC2_PROD_MODE
  69. ESP_LOGE(TAG, "Not implemented!");
  70. return ESP_FAIL;
  71. #endif
  72. }
  73. static esp_err_t example_get_sec2_verifier(const char **verifier, uint16_t *verifier_len) {
  74. #if CONFIG_EXAMPLE_PROTOCOMM_SEC2_DEV_MODE
  75. ESP_LOGI(TAG, "Development mode: using hard coded verifier");
  76. *verifier = sec2_verifier;
  77. *verifier_len = sizeof(sec2_verifier);
  78. return ESP_OK;
  79. #elif CONFIG_EXAMPLE_PROTOCOMM_SEC2_PROD_MODE
  80. /* This code needs to be updated with appropriate implementation to provide verifier */
  81. ESP_LOGE(TAG, "Not implemented!");
  82. return ESP_FAIL;
  83. #endif
  84. }
  85. #endif
  86. /* Custom allowed property types */
  87. enum property_types {
  88. PROP_TYPE_TIMESTAMP = 0,
  89. PROP_TYPE_INT32,
  90. PROP_TYPE_BOOLEAN,
  91. PROP_TYPE_STRING,
  92. };
  93. /* Custom flags that can be set for a property */
  94. enum property_flags {
  95. PROP_FLAG_READONLY = (1 << 0)
  96. };
  97. /********* Handler functions for responding to control requests / commands *********/
  98. static esp_err_t get_property_values(size_t props_count,
  99. const esp_local_ctrl_prop_t props[],
  100. esp_local_ctrl_prop_val_t prop_values[],
  101. void *usr_ctx)
  102. {
  103. for (uint32_t i = 0; i < props_count; i++) {
  104. ESP_LOGI(TAG, "Reading property : %s", props[i].name);
  105. /* For the purpose of this example, to keep things simple
  106. * we have set the context pointer of each property to
  107. * point to its value (except for timestamp) */
  108. switch (props[i].type) {
  109. case PROP_TYPE_INT32:
  110. case PROP_TYPE_BOOLEAN:
  111. /* No need to set size for these types as sizes where
  112. * specified when declaring the properties, unlike for
  113. * string type. */
  114. prop_values[i].data = props[i].ctx;
  115. break;
  116. case PROP_TYPE_TIMESTAMP: {
  117. /* Get the time stamp */
  118. static int64_t ts = 0;
  119. ts = esp_timer_get_time();
  120. /* Set the current time. Since this is statically
  121. * allocated, we don't need to provide a free_fn */
  122. prop_values[i].data = &ts;
  123. break;
  124. }
  125. case PROP_TYPE_STRING: {
  126. char **prop3_value = (char **) props[i].ctx;
  127. if (*prop3_value == NULL) {
  128. prop_values[i].size = 0;
  129. prop_values[i].data = NULL;
  130. } else {
  131. /* We could try dynamically allocating the output value,
  132. * and it should get freed automatically after use, as
  133. * `esp_local_ctrl` internally calls the provided `free_fn` */
  134. prop_values[i].size = strlen(*prop3_value);
  135. prop_values[i].data = strdup(*prop3_value);
  136. if (!prop_values[i].data) {
  137. return ESP_ERR_NO_MEM;
  138. }
  139. prop_values[i].free_fn = free;
  140. }
  141. }
  142. default:
  143. break;
  144. }
  145. }
  146. return ESP_OK;
  147. }
  148. static esp_err_t set_property_values(size_t props_count,
  149. const esp_local_ctrl_prop_t props[],
  150. const esp_local_ctrl_prop_val_t prop_values[],
  151. void *usr_ctx)
  152. {
  153. for (uint32_t i = 0; i < props_count; i++) {
  154. /* Cannot set the value of a read-only property */
  155. if (props[i].flags & PROP_FLAG_READONLY) {
  156. ESP_LOGE(TAG, "%s is read-only", props[i].name);
  157. return ESP_ERR_INVALID_ARG;
  158. }
  159. /* For the purpose of this example, to keep things simple
  160. * we have set the context pointer of each property to
  161. * point to its value (except for timestamp) */
  162. switch (props[i].type) {
  163. case PROP_TYPE_STRING: {
  164. /* Free the previously set string */
  165. char **prop3_value = (char **) props[i].ctx;
  166. free(*prop3_value);
  167. *prop3_value = NULL;
  168. /* Copy the input string */
  169. if (prop_values[i].size) {
  170. *prop3_value = strndup((const char *)prop_values[i].data, prop_values[i].size);
  171. if (*prop3_value == NULL) {
  172. return ESP_ERR_NO_MEM;
  173. }
  174. ESP_LOGI(TAG, "Setting %s value to %s", props[i].name, (const char*)*prop3_value);
  175. }
  176. }
  177. break;
  178. case PROP_TYPE_INT32: {
  179. const int32_t *new_value = (const int32_t *) prop_values[i].data;
  180. ESP_LOGI(TAG, "Setting %s value to %" PRId32, props[i].name, *new_value);
  181. memcpy(props[i].ctx, new_value, sizeof(int32_t));
  182. }
  183. break;
  184. case PROP_TYPE_BOOLEAN: {
  185. const bool *value = (const bool *) prop_values[i].data;
  186. ESP_LOGI(TAG, "Setting %s value to %d", props[i].name, *value);
  187. memcpy(props[i].ctx, value, sizeof(bool));
  188. }
  189. break;
  190. default:
  191. break;
  192. }
  193. }
  194. return ESP_OK;
  195. }
  196. /******************************************************************************/
  197. /* A custom free_fn to free a pointer to a string as
  198. * well as the string being pointed to */
  199. static void free_str(void *arg)
  200. {
  201. char **ptr_to_strptr = (char **)arg;
  202. if (ptr_to_strptr) {
  203. free(*ptr_to_strptr);
  204. free(ptr_to_strptr);
  205. }
  206. }
  207. /* Function used by app_main to start the esp_local_ctrl service */
  208. void start_esp_local_ctrl_service(void)
  209. {
  210. #ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
  211. /* Set the configuration */
  212. httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT();
  213. /* Load server certificate */
  214. extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");
  215. extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end");
  216. https_conf.servercert = servercert_start;
  217. https_conf.servercert_len = servercert_end - servercert_start;
  218. /* Load server private key */
  219. extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");
  220. extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end");
  221. https_conf.prvtkey_pem = prvtkey_pem_start;
  222. https_conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;
  223. #else
  224. httpd_config_t http_conf = HTTPD_DEFAULT_CONFIG();
  225. #endif
  226. #ifdef CONFIG_EXAMPLE_PROTOCOMM_SECURITY_VERSION_1
  227. /* What is the security level that we want (0, 1, 2):
  228. * - PROTOCOMM_SECURITY_0 is simply plain text communication.
  229. * - PROTOCOMM_SECURITY_1 is secure communication which consists of secure handshake
  230. * using X25519 key exchange and proof of possession (pop) and AES-CTR
  231. * for encryption/decryption of messages.
  232. * - PROTOCOMM_SECURITY_2 SRP6a based authentication and key exchange
  233. * + AES-GCM encryption/decryption of messages
  234. */
  235. esp_local_ctrl_proto_sec_t security = PROTOCOM_SEC1;
  236. /* Do we want a proof-of-possession (ignored if Security 0 is selected):
  237. * - this should be a string with length > 0
  238. * - NULL if not used
  239. */
  240. const char *pop = "abcd1234";
  241. /* This is the structure for passing security parameters
  242. * for the protocomm security 1.
  243. */
  244. protocomm_security1_params_t sec_params = {
  245. .data = (const uint8_t *)pop,
  246. .len = strlen(pop),
  247. };
  248. #elif CONFIG_EXAMPLE_PROTOCOMM_SECURITY_VERSION_2
  249. esp_local_ctrl_proto_sec_t security = PROTOCOM_SEC2;
  250. /* This is the structure for passing security parameters
  251. * for the protocomm security 2.
  252. */
  253. protocomm_security2_params_t sec_params = {};
  254. ESP_ERROR_CHECK(example_get_sec2_salt(&sec_params.salt, &sec_params.salt_len));
  255. ESP_ERROR_CHECK(example_get_sec2_verifier(&sec_params.verifier, &sec_params.verifier_len));
  256. #else /* CONFIG_EXAMPLE_PROTOCOMM_SECURITY_VERSION_0 */
  257. esp_local_ctrl_proto_sec_t security = PROTOCOM_SEC0;
  258. const void *sec_params = NULL;
  259. #endif
  260. esp_local_ctrl_config_t config = {
  261. .transport = ESP_LOCAL_CTRL_TRANSPORT_HTTPD,
  262. .transport_config = {
  263. #ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
  264. .httpd = &https_conf,
  265. #else
  266. .httpd = &http_conf,
  267. #endif
  268. },
  269. .proto_sec = {
  270. .version = security,
  271. .custom_handle = NULL,
  272. .sec_params = &sec_params,
  273. },
  274. .handlers = {
  275. /* User defined handler functions */
  276. .get_prop_values = get_property_values,
  277. .set_prop_values = set_property_values,
  278. .usr_ctx = NULL,
  279. .usr_ctx_free_fn = NULL
  280. },
  281. /* Maximum number of properties that may be set */
  282. .max_properties = 10
  283. };
  284. mdns_init();
  285. mdns_hostname_set(SERVICE_NAME);
  286. /* Start esp_local_ctrl service */
  287. ESP_ERROR_CHECK(esp_local_ctrl_start(&config));
  288. ESP_LOGI(TAG, "esp_local_ctrl service started with name : %s", SERVICE_NAME);
  289. /* Create a timestamp property. The client should see this as a read-only property.
  290. * Property value is fetched using `esp_timer_get_time()` in the `get_prop_values`
  291. * handler */
  292. esp_local_ctrl_prop_t timestamp = {
  293. .name = "timestamp (us)",
  294. .type = PROP_TYPE_TIMESTAMP,
  295. .size = sizeof(int64_t),
  296. .flags = PROP_FLAG_READONLY,
  297. .ctx = NULL,
  298. .ctx_free_fn = NULL
  299. };
  300. /* Create a writable integer property. Use dynamically allocated memory
  301. * for storing its value and pass it as context, so that it can be accessed
  302. * inside the set / get handlers. */
  303. int32_t *prop1_value = malloc(sizeof(int32_t));
  304. assert(prop1_value != NULL);
  305. /* Initialize the property value */
  306. *prop1_value = 123456789;
  307. /* Populate the property structure accordingly. Since, we would want the memory
  308. * occupied by the property value to be freed automatically upon call to
  309. * `esp_local_ctrl_stop()` or `esp_local_ctrl_remove_property()`, the `ctx_free_fn`
  310. * field will need to be set with the appropriate de-allocation function,
  311. * which in this case is simply `free()` */
  312. esp_local_ctrl_prop_t property1 = {
  313. .name = "property1",
  314. .type = PROP_TYPE_INT32,
  315. .size = sizeof(int32_t),
  316. .flags = 0,
  317. .ctx = prop1_value,
  318. .ctx_free_fn = free
  319. };
  320. /* Create another read-only property. Just for demonstration, we use statically
  321. * allocated value. No `ctx_free_fn` needs to be set for this */
  322. static bool prop2_value = false;
  323. esp_local_ctrl_prop_t property2 = {
  324. .name = "property2",
  325. .type = PROP_TYPE_BOOLEAN,
  326. .size = sizeof(bool),
  327. .flags = PROP_FLAG_READONLY,
  328. .ctx = &prop2_value,
  329. .ctx_free_fn = NULL
  330. };
  331. /* Create a variable sized property. Its context is a pointer for storing the
  332. * pointer to a dynamically allocate string, therefore it will require a
  333. * customized free function `free_str()` */
  334. char **prop3_value = calloc(1, sizeof(char *));
  335. assert(prop3_value != NULL);
  336. esp_local_ctrl_prop_t property3 = {
  337. .name = "property3",
  338. .type = PROP_TYPE_STRING,
  339. .size = 0, // When zero, this is assumed to be of variable size
  340. .flags = 0,
  341. .ctx = prop3_value,
  342. .ctx_free_fn = free_str
  343. };
  344. /* Now register the properties */
  345. ESP_ERROR_CHECK(esp_local_ctrl_add_property(&timestamp));
  346. ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property1));
  347. ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property2));
  348. ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property3));
  349. /* Just for fun, let us keep toggling the value
  350. * of the boolean property2, every 1 second */
  351. while (1) {
  352. vTaskDelay(1000 / portTICK_PERIOD_MS);
  353. prop2_value = !prop2_value;
  354. }
  355. }