1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdbool.h>
18 #include <esp_err.h>
19 #include <esp_log.h>
20 #include <esp_system.h>
21 #include <sys/random.h>
22 #include <unistd.h>
23 #include <unity.h>
24 
25 #include <mbedtls/aes.h>
26 #include <mbedtls/sha256.h>
27 #include <mbedtls/entropy.h>
28 #include <mbedtls/ctr_drbg.h>
29 #include <mbedtls/ecdh.h>
30 #include <mbedtls/error.h>
31 #include <mbedtls/ssl_internal.h>
32 
33 #include <protocomm.h>
34 #include <protocomm_security.h>
35 #include <protocomm_security0.h>
36 #include <protocomm_security1.h>
37 
38 #include "session.pb-c.h"
39 
40 #ifdef CONFIG_HEAP_TRACING
41     #include <esp_heap_trace.h>
42     #define NUM_RECORDS 100
43     static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
44 #endif
45 
46 #define PUBLIC_KEY_LEN  32
47 #define SZ_RANDOM       16
48 
49 typedef struct {
50     uint32_t id;
51     uint8_t  sec_ver;
52     uint8_t  weak;
53     const protocomm_security_pop_t *pop;
54     uint8_t device_pubkey[PUBLIC_KEY_LEN];
55     uint8_t client_pubkey[PUBLIC_KEY_LEN];
56     uint8_t sym_key[PUBLIC_KEY_LEN];
57     uint8_t rand[SZ_RANDOM];
58 
59     /* mbedtls context data for Curve25519 */
60     mbedtls_ecdh_context ctx_client;
61     mbedtls_entropy_context entropy;
62     mbedtls_ctr_drbg_context ctr_drbg;
63 
64     /* mbedtls context data for AES */
65     mbedtls_aes_context ctx_aes;
66     unsigned char stb[16];
67     size_t nc_off;
68 } session_t;
69 
70 static const char *TAG = "protocomm_test";
71 
72 static protocomm_t *test_pc = NULL;
73 static const protocomm_security_t *test_sec = NULL;
74 protocomm_security_handle_t sec_inst = NULL;
75 static uint32_t test_priv_data = 1234;
76 
flip_endian(uint8_t * data,size_t len)77 static void flip_endian(uint8_t *data, size_t len)
78 {
79     uint8_t swp_buf;
80     for (int i = 0; i < len/2; i++) {
81         swp_buf = data[i];
82         data[i] = data[len - i - 1];
83         data[len - i - 1] = swp_buf;
84     }
85 }
86 
hexdump(const char * msg,uint8_t * buf,int len)87 static void hexdump(const char *msg, uint8_t *buf, int len)
88 {
89     ESP_LOGI(TAG, "%s:", msg);
90     ESP_LOG_BUFFER_HEX(TAG, buf, len);
91 }
92 
prepare_command0(session_t * session,SessionData * req)93 static esp_err_t prepare_command0(session_t *session, SessionData *req)
94 {
95     Sec1Payload *in = (Sec1Payload *) malloc(sizeof(Sec1Payload));
96     if (in == NULL) {
97         ESP_LOGE(TAG, "Error allocating memory for request");
98         return ESP_ERR_NO_MEM;
99     }
100 
101     SessionCmd0 *in_req = (SessionCmd0 *) malloc(sizeof(SessionCmd0));
102     if (in_req == NULL) {
103         ESP_LOGE(TAG, "Error allocating memory for request");
104         free(in);
105         return ESP_ERR_NO_MEM;
106     }
107 
108     sec1_payload__init(in);
109     session_cmd0__init(in_req);
110 
111     in_req->client_pubkey.data = session->client_pubkey;
112     in_req->client_pubkey.len = PUBLIC_KEY_LEN;
113 
114     in->msg = SEC1_MSG_TYPE__Session_Command0;
115     in->payload_case = SEC1_PAYLOAD__PAYLOAD_SC0;
116     in->sc0 = in_req;
117 
118     req->proto_case = SESSION_DATA__PROTO_SEC1;
119     req->sec_ver = protocomm_security1.ver;
120     req->sec1 = in;
121 
122     return ESP_OK;
123 }
124 
cleanup_command0(SessionData * req)125 static void cleanup_command0(SessionData *req)
126 {
127     free(req->sec1->sc0);
128     free(req->sec1);
129 }
130 
verify_response0(session_t * session,SessionData * resp)131 static esp_err_t verify_response0(session_t *session, SessionData *resp)
132 {
133     if ((resp->proto_case != SESSION_DATA__PROTO_SEC1) ||
134         (resp->sec1->msg  != SEC1_MSG_TYPE__Session_Response0)) {
135         ESP_LOGE(TAG, "Invalid response type");
136         return ESP_ERR_INVALID_ARG;
137     }
138 
139     int ret;
140     Sec1Payload *in = (Sec1Payload *) resp->sec1;
141 
142     if (in->sr0->device_pubkey.len != PUBLIC_KEY_LEN) {
143         ESP_LOGE(TAG, "Device public key length as not as expected");
144         return ESP_FAIL;
145     }
146 
147     if (in->sr0->device_random.len != SZ_RANDOM) {
148         ESP_LOGE(TAG, "Device random data length is not as expected");
149         return ESP_FAIL;
150     }
151 
152     uint8_t *cli_pubkey = session->client_pubkey;
153     uint8_t *dev_pubkey = session->device_pubkey;
154     memcpy(session->device_pubkey, in->sr0->device_pubkey.data, in->sr0->device_pubkey.len);
155 
156     hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
157     hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
158 
159     ret = mbedtls_mpi_lset(&session->ctx_client.Qp.Z, 1);
160     if (ret != 0) {
161         ESP_LOGE(TAG, "Failed at mbedtls_mpi_lset with error code : %d", ret);
162         return ESP_FAIL;
163     }
164 
165     flip_endian(session->device_pubkey, PUBLIC_KEY_LEN);
166     ret = mbedtls_mpi_read_binary(&session->ctx_client.Qp.X, dev_pubkey, PUBLIC_KEY_LEN);
167     flip_endian(session->device_pubkey, PUBLIC_KEY_LEN);
168     if (ret != 0) {
169         ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret);
170         return ESP_FAIL;
171     }
172 
173     ret = mbedtls_ecdh_compute_shared(&session->ctx_client.grp,
174                                       &session->ctx_client.z,
175                                       &session->ctx_client.Qp,
176                                       &session->ctx_client.d,
177                                       mbedtls_ctr_drbg_random,
178                                       &session->ctr_drbg);
179     if (ret != 0) {
180         ESP_LOGE(TAG, "Failed at mbedtls_ecdh_compute_shared with error code : %d", ret);
181         return ESP_FAIL;
182     }
183 
184     ret = mbedtls_mpi_write_binary(&session->ctx_client.z, session->sym_key, PUBLIC_KEY_LEN);
185     if (ret != 0) {
186         ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret);
187         return ESP_FAIL;
188     }
189     flip_endian(session->sym_key, PUBLIC_KEY_LEN);
190 
191     const protocomm_security_pop_t *pop = session->pop;
192     if (pop != NULL && pop->data != NULL && pop->len != 0) {
193         ESP_LOGD(TAG, "Adding proof of possession");
194         uint8_t sha_out[PUBLIC_KEY_LEN];
195 
196         ret = mbedtls_sha256_ret((const unsigned char *) pop->data, pop->len, sha_out, 0);
197         if (ret != 0) {
198             ESP_LOGE(TAG, "Failed at mbedtls_sha256_ret with error code : %d", ret);
199             return ESP_FAIL;
200         }
201 
202         for (int i = 0; i < PUBLIC_KEY_LEN; i++) {
203             session->sym_key[i] ^= sha_out[i];
204         }
205     }
206 
207     hexdump("Shared key", session->sym_key, PUBLIC_KEY_LEN);
208 
209     memcpy(session->rand, in->sr0->device_random.data, in->sr0->device_random.len);
210     hexdump("Dev random", session->rand, sizeof(session->rand));
211     return ESP_OK;
212 }
213 
prepare_command1(session_t * session,SessionData * req)214 static esp_err_t prepare_command1(session_t *session, SessionData *req)
215 {
216     int ret;
217     uint8_t *outbuf = (uint8_t *) malloc(PUBLIC_KEY_LEN);
218     if (!outbuf) {
219         ESP_LOGE(TAG, "Error allocating ciphertext buffer");
220         return ESP_ERR_NO_MEM;
221     }
222 
223     /* Initialise crypto context */
224     mbedtls_aes_init(&session->ctx_aes);
225     memset(session->stb, 0, sizeof(session->stb));
226     session->nc_off = 0;
227 
228     ret = mbedtls_aes_setkey_enc(&session->ctx_aes, session->sym_key,
229                                  sizeof(session->sym_key)*8);
230     if (ret != 0) {
231         ESP_LOGE(TAG, "Failed at mbedtls_aes_setkey_enc with erro code : %d", ret);
232         free(outbuf);
233         return ESP_FAIL;
234     }
235 
236     ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN,
237                                 &session->nc_off, session->rand,
238                                 session->stb, session->device_pubkey, outbuf);
239     if (ret != 0) {
240         ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with erro code : %d", ret);
241         free(outbuf);
242         return ESP_FAIL;
243     }
244 
245     Sec1Payload *out = (Sec1Payload *) malloc(sizeof(Sec1Payload));
246     if (!out) {
247         ESP_LOGE(TAG, "Error allocating out buffer");
248         free(outbuf);
249         return ESP_ERR_NO_MEM;
250     }
251     sec1_payload__init(out);
252 
253     SessionCmd1 *out_req = (SessionCmd1 *) malloc(sizeof(SessionCmd1));
254     if (!out_req) {
255         ESP_LOGE(TAG, "Error allocating out_req buffer");
256         free(outbuf);
257         free(out);
258         return ESP_ERR_NO_MEM;
259     }
260     session_cmd1__init(out_req);
261 
262     out_req->client_verify_data.data = outbuf;
263     out_req->client_verify_data.len = PUBLIC_KEY_LEN;
264     hexdump("Client verify data", outbuf, PUBLIC_KEY_LEN);
265 
266     out->msg = SEC1_MSG_TYPE__Session_Command1;
267     out->payload_case = SEC1_PAYLOAD__PAYLOAD_SC1;
268     out->sc1 = out_req;
269 
270     req->proto_case = SESSION_DATA__PROTO_SEC1;
271     req->sec_ver = protocomm_security1.ver;
272     req->sec1 = out;
273 
274     return ESP_OK;
275 }
276 
cleanup_command1(SessionData * req)277 static void cleanup_command1(SessionData *req)
278 {
279     free(req->sec1->sc1->client_verify_data.data);
280     free(req->sec1->sc1);
281     free(req->sec1);
282 }
283 
verify_response1(session_t * session,SessionData * resp)284 static esp_err_t verify_response1(session_t *session, SessionData *resp)
285 {
286     uint8_t *cli_pubkey = session->client_pubkey;
287     uint8_t *dev_pubkey = session->device_pubkey;
288 
289     hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN);
290     hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN);
291 
292     if ((resp->proto_case != SESSION_DATA__PROTO_SEC1) ||
293         (resp->sec1->msg  != SEC1_MSG_TYPE__Session_Response1)) {
294         ESP_LOGE(TAG, "Invalid response type");
295         return ESP_ERR_INVALID_ARG;
296     }
297 
298     uint8_t check_buf[PUBLIC_KEY_LEN];
299     Sec1Payload *in = (Sec1Payload *) resp->sec1;
300 
301     int ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN,
302                                     &session->nc_off, session->rand, session->stb,
303                                     in->sr1->device_verify_data.data, check_buf);
304     if (ret != 0) {
305         ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with erro code : %d", ret);
306         return ESP_FAIL;
307     }
308     hexdump("Dec Device verifier", check_buf, sizeof(check_buf));
309 
310     if (memcmp(check_buf, session->client_pubkey, sizeof(session->client_pubkey)) != 0) {
311         ESP_LOGE(TAG, "Key mismatch. Close connection");
312         return ESP_FAIL;
313     }
314 
315     return ESP_OK;
316 }
317 
test_new_session(session_t * session)318 static esp_err_t test_new_session(session_t *session)
319 {
320     if (session->sec_ver == 0) {
321         return ESP_OK;
322     }
323 
324     if (!test_sec) {
325         return ESP_ERR_INVALID_STATE;
326     }
327 
328     if (test_sec->init && (test_sec->init(&sec_inst) != ESP_OK)) {
329         return ESP_ERR_NO_MEM;
330     }
331 
332     uint32_t session_id = session->id;
333     if (test_sec->new_transport_session &&
334         (test_sec->new_transport_session(sec_inst, session_id) != ESP_OK)) {
335         ESP_LOGE(TAG, "Failed to launch new transport session");
336         return ESP_FAIL;
337     }
338 
339     if (protocomm_open_session(test_pc, session_id) != ESP_OK) {
340         ESP_LOGE(TAG, "Failed to open new protocomm session");
341         return ESP_FAIL;
342     }
343     return ESP_OK;
344 }
345 
test_delete_session(session_t * session)346 static esp_err_t test_delete_session(session_t *session)
347 {
348     if (!test_sec) {
349         return ESP_ERR_INVALID_STATE;
350     }
351 
352     if (test_sec->cleanup && (test_sec->cleanup(sec_inst) != ESP_OK)) {
353         return ESP_FAIL;
354     }
355     return ESP_OK;
356 }
357 
test_sec_endpoint(session_t * session)358 static esp_err_t test_sec_endpoint(session_t *session)
359 {
360     if (session->sec_ver == 0) {
361         return ESP_OK;
362     }
363 
364     uint32_t session_id = session->id;
365 
366     int ret = ESP_FAIL;
367     SessionData req;
368     SessionData *resp;
369     ssize_t  inlen = 0;
370     uint8_t *inbuf = NULL;
371     ssize_t  outlen = 0;
372     uint8_t *outbuf = NULL;
373 
374     mbedtls_ecdh_init(&session->ctx_client);
375     mbedtls_ctr_drbg_init(&session->ctr_drbg);
376 
377     mbedtls_entropy_init(&session->entropy);
378     ret = mbedtls_ctr_drbg_seed(&session->ctr_drbg, mbedtls_entropy_func,
379                                 &session->entropy, NULL, 0);
380     if (ret != 0) {
381         ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_seed with error code : %d", ret);
382         goto abort_test_sec_endpoint;
383     }
384 
385     ret = mbedtls_ecp_group_load(&session->ctx_client.grp, MBEDTLS_ECP_DP_CURVE25519);
386     if (ret != 0) {
387         ESP_LOGE(TAG, "Failed at mbedtls_ecp_group_load with error code : %d", ret);
388         goto abort_test_sec_endpoint;
389     }
390 
391     ret = mbedtls_ecdh_gen_public(&session->ctx_client.grp,
392                                   &session->ctx_client.d,
393                                   &session->ctx_client.Q,
394                                   mbedtls_ctr_drbg_random,
395                                   &session->ctr_drbg);
396     if (ret != 0) {
397         ESP_LOGE(TAG, "Failed at mbedtls_ecdh_gen_public with error code : %d", ret);
398         goto abort_test_sec_endpoint;
399     }
400 
401     if (session->weak) {
402         /* Read zero client public key */
403         ret = mbedtls_mpi_read_binary(&session->ctx_client.Q.X,
404                                       session->client_pubkey,
405                                       PUBLIC_KEY_LEN);
406         if (ret != 0) {
407             ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret);
408             goto abort_test_sec_endpoint;
409         }
410     }
411     ret = mbedtls_mpi_write_binary(&session->ctx_client.Q.X,
412                                    session->client_pubkey,
413                                    PUBLIC_KEY_LEN);
414     if (ret != 0) {
415         ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret);
416         goto abort_test_sec_endpoint;
417     }
418     flip_endian(session->client_pubkey, PUBLIC_KEY_LEN);
419 
420     /*********** Transaction0 = SessionCmd0 + SessionResp0 ****************/
421     session_data__init(&req);
422     if (prepare_command0(session, &req) != ESP_OK) {
423         ESP_LOGE(TAG, "Failed in prepare_command0");
424         goto abort_test_sec_endpoint;
425     }
426 
427     inlen = session_data__get_packed_size(&req);
428     inbuf = (uint8_t *) malloc(inlen);
429     if (!inbuf) {
430         ESP_LOGE(TAG, "Failed to allocate inbuf");
431         goto abort_test_sec_endpoint;
432     }
433 
434     session_data__pack(&req, inbuf);
435     cleanup_command0(&req);
436 
437     outlen = 0;
438     outbuf = NULL;
439     ret = protocomm_req_handle(test_pc, "test-sec", session_id,
440                                inbuf, inlen, &outbuf, &outlen);
441 
442     free(inbuf);
443     if (ret != ESP_OK) {
444         ESP_LOGE(TAG, "test-sec handler failed");
445         free(outbuf);
446         goto abort_test_sec_endpoint;
447     }
448 
449     resp = session_data__unpack(NULL, outlen, outbuf);
450     free(outbuf);
451     if (!resp) {
452         ESP_LOGE(TAG, "Unable to unpack SessionResp0");
453         goto abort_test_sec_endpoint;
454     }
455 
456     if (verify_response0(session, resp) != ESP_OK) {
457         ESP_LOGE(TAG, "Invalid response 0");
458         session_data__free_unpacked(resp, NULL);
459         goto abort_test_sec_endpoint;
460     }
461 
462     session_data__free_unpacked(resp, NULL);
463 
464     /*********** Transaction1 = SessionCmd1 + SessionResp1 ****************/
465     session_data__init(&req);
466     if (prepare_command1(session, &req) != ESP_OK) {
467         ESP_LOGE(TAG, "Failed in prepare_command1");
468         goto abort_test_sec_endpoint;
469     }
470 
471     inlen = session_data__get_packed_size(&req);
472     inbuf = (uint8_t *) malloc(inlen);
473     if (!inbuf) {
474         ESP_LOGE(TAG, "Failed to allocate inbuf");
475         goto abort_test_sec_endpoint;
476     }
477 
478     session_data__pack(&req, inbuf);
479     cleanup_command1(&req);
480 
481     outlen = 0;
482     outbuf = NULL;
483     ret = protocomm_req_handle(test_pc, "test-sec", session_id,
484                                inbuf, inlen, &outbuf, &outlen);
485 
486     free(inbuf);
487     if (ret != ESP_OK) {
488         ESP_LOGE(TAG, "test-sec handler failed");
489         free(outbuf);
490         goto abort_test_sec_endpoint;
491     }
492 
493     resp = session_data__unpack(NULL, outlen, outbuf);
494     free(outbuf);
495     if (!resp) {
496         ESP_LOGE(TAG, "Unable to unpack SessionResp0");
497         goto abort_test_sec_endpoint;
498     }
499 
500     if (verify_response1(session, resp) != ESP_OK) {
501         ESP_LOGE(TAG, "Invalid response 1");
502         session_data__free_unpacked(resp, NULL);
503         goto abort_test_sec_endpoint;
504     }
505 
506     session_data__free_unpacked(resp, NULL);
507     mbedtls_ecdh_free(&session->ctx_client);
508     mbedtls_ctr_drbg_free(&session->ctr_drbg);
509     mbedtls_entropy_free(&session->entropy);
510 
511     return ESP_OK;
512 
513 abort_test_sec_endpoint:
514 
515     mbedtls_ecdh_free(&session->ctx_client);
516     mbedtls_ctr_drbg_free(&session->ctr_drbg);
517     mbedtls_entropy_free(&session->entropy);
518     return ESP_FAIL;
519 }
520 
521 #define TEST_VER_STR "<some version string>"
522 
test_ver_endpoint(session_t * session)523 static esp_err_t test_ver_endpoint(session_t *session)
524 {
525     ssize_t  ver_data_len = 0;
526     uint8_t *ver_data = NULL;
527 
528     esp_err_t ret = protocomm_req_handle(test_pc, "test-ver", session->id,
529                                          NULL, 0, &ver_data, &ver_data_len);
530 
531     if (ret != ESP_OK) {
532         ESP_LOGE(TAG, "test-ver handler failed");
533         return ESP_FAIL;
534     }
535 
536     if (ver_data_len != strlen(TEST_VER_STR) || memcmp(TEST_VER_STR, ver_data, ver_data_len)) {
537         ESP_LOGE(TAG, "incorrect response data from test-ver");
538         free(ver_data);
539         return ESP_FAIL;
540     }
541     free(ver_data);
542     return ESP_OK;
543 }
544 
test_req_endpoint(session_t * session)545 static esp_err_t test_req_endpoint(session_t *session)
546 {
547     uint32_t session_id = session->id;
548 
549     uint8_t rand_test_data[512], enc_test_data[512];
550     getrandom(rand_test_data, sizeof(rand_test_data), 0);
551 
552     if (session->sec_ver == 0) {
553         memcpy(enc_test_data, rand_test_data, sizeof(rand_test_data));
554     }
555     else if (session->sec_ver == 1) {
556 #if !CONFIG_MBEDTLS_HARDWARE_AES
557         // Check if the AES key is correctly set before calling the software encryption
558         // API. Without this check, the code will crash, resulting in a test case failure.
559         // For hardware AES, portability layer takes care of this.
560         if (session->ctx_aes.rk != NULL && session->ctx_aes.nr > 0) {
561 #endif
562 
563             mbedtls_aes_crypt_ctr(&session->ctx_aes, sizeof(rand_test_data), &session->nc_off,
564                     session->rand, session->stb, rand_test_data, enc_test_data);
565 #if !CONFIG_MBEDTLS_HARDWARE_AES
566         }
567 #endif
568     }
569 
570     ssize_t  verify_data_len = 0;
571     uint8_t *enc_verify_data = NULL;
572 
573     esp_err_t ret = protocomm_req_handle(test_pc, "test-ep", session_id,
574                                          enc_test_data, sizeof(enc_test_data),
575                                          &enc_verify_data, &verify_data_len);
576 
577     if (ret != ESP_OK || !verify_data_len) {
578         ESP_LOGE(TAG, "test-ep handler failed");
579         return ESP_FAIL;
580     }
581 
582     uint8_t *verify_data = malloc(verify_data_len);
583     if (!verify_data) {
584         ESP_LOGE(TAG, "error allocating memory for decrypted data");
585         free(enc_verify_data);
586         return ESP_FAIL;
587     }
588 
589     if (session->sec_ver == 0) {
590         memcpy(verify_data, enc_verify_data, verify_data_len);
591     }
592     else if (session->sec_ver == 1) {
593         mbedtls_aes_crypt_ctr(&session->ctx_aes, verify_data_len, &session->nc_off,
594                               session->rand, session->stb, enc_verify_data, verify_data);
595     }
596     free(enc_verify_data);
597 
598     hexdump("Sent data", rand_test_data, sizeof(rand_test_data));
599     hexdump("Recv data", verify_data,    verify_data_len);
600 
601     ESP_LOGI(TAG, "verify data len   : %d", verify_data_len);
602     ESP_LOGI(TAG, "expected data len : %d", sizeof(rand_test_data));
603 
604     if (verify_data_len != sizeof(rand_test_data)) {
605         ESP_LOGE(TAG, "incorrect response length from test-ep");
606         free(verify_data);
607         return ESP_FAIL;
608     }
609     if (memcmp(rand_test_data, verify_data, verify_data_len)) {
610         ESP_LOGE(TAG, "incorrect response data from test-ep");
611         free(verify_data);
612         return ESP_FAIL;
613     }
614     free(verify_data);
615     return ESP_OK;
616 }
617 
test_req_handler(uint32_t session_id,const uint8_t * inbuf,ssize_t inlen,uint8_t ** outbuf,ssize_t * outlen,void * priv_data)618 esp_err_t test_req_handler (uint32_t session_id,
619                             const uint8_t *inbuf, ssize_t inlen,
620                             uint8_t **outbuf, ssize_t *outlen,
621                             void *priv_data)
622 {
623     *outbuf = malloc(inlen);
624     if (*outbuf) {
625         *outlen = inlen;
626         memcpy(*outbuf, inbuf, inlen);
627     } else {
628         ESP_LOGE(TAG, "Error allocating response outbuf");
629         *outbuf = NULL;
630         *outlen = 0;
631     }
632 
633     uint32_t *priv = (uint32_t *) priv_data;
634     if ((&test_priv_data != priv) || (test_priv_data != *priv)) {
635         ESP_LOGE(TAG, "Handler private data doesn't match");
636         return ESP_FAIL;
637     }
638     return ESP_OK;
639 }
640 
start_test_service(uint8_t sec_ver,const protocomm_security_pop_t * pop)641 static esp_err_t start_test_service(uint8_t sec_ver, const protocomm_security_pop_t *pop)
642 {
643     test_pc = protocomm_new();
644     if (test_pc == NULL) {
645         ESP_LOGE(TAG, "Failed to create new protocomm instance");
646         return ESP_FAIL;
647     }
648 
649     if (sec_ver == 0) {
650         if (protocomm_set_security(test_pc, "test-sec", &protocomm_security0, NULL) != ESP_OK) {
651             ESP_LOGE(TAG, "Failed to set Security0");
652             return ESP_FAIL;
653         }
654         test_sec = &protocomm_security0;
655     } else if (sec_ver == 1) {
656         if (protocomm_set_security(test_pc, "test-sec", &protocomm_security1, pop) != ESP_OK) {
657             ESP_LOGE(TAG, "Failed to set Security1");
658             return ESP_FAIL;
659         }
660         test_sec = &protocomm_security1;
661     }
662 
663     if (protocomm_set_version(test_pc, "test-ver", TEST_VER_STR) != ESP_OK) {
664         ESP_LOGE(TAG, "Failed to set version");
665         return ESP_FAIL;
666     }
667 
668     if (protocomm_add_endpoint(test_pc, "test-ep",
669                                test_req_handler,
670                                (void *) &test_priv_data) != ESP_OK) {
671         ESP_LOGE(TAG, "Failed to set test-ep endpoint handler");
672         return ESP_FAIL;
673     }
674     return ESP_OK;
675 }
676 
stop_test_service(void)677 static void stop_test_service(void)
678 {
679     test_sec = NULL;
680     protocomm_delete(test_pc);
681     test_pc = NULL;
682 }
683 
test_security1_no_encryption(void)684 static esp_err_t test_security1_no_encryption (void)
685 {
686     ESP_LOGI(TAG, "Starting Security 1 no encryption test");
687 
688     const char *pop_data = "test pop";
689     protocomm_security_pop_t pop = {
690         .data = (const uint8_t *)pop_data,
691         .len  = strlen(pop_data)
692     };
693 
694     session_t *session = calloc(1, sizeof(session_t));
695     if (session == NULL) {
696         ESP_LOGE(TAG, "Error allocating session");
697         return ESP_ERR_NO_MEM;
698     }
699 
700     session->id        = 1;
701     session->sec_ver   = 1;
702     session->pop       = &pop;
703 
704     // Start protocomm service
705     if (start_test_service(1, &pop) != ESP_OK) {
706         ESP_LOGE(TAG, "Error starting test");
707         free(session);
708         return ESP_ERR_INVALID_STATE;
709     }
710 
711     // Intialise protocomm session with zero public keys
712     if (test_new_session(session) != ESP_OK) {
713         ESP_LOGE(TAG, "Error creating new session");
714         stop_test_service();
715         free(session);
716         return ESP_FAIL;
717     }
718 
719     // Perform 25519 security handshake to set public keys
720     if (test_sec_endpoint(session) != ESP_OK) {
721         ESP_LOGE(TAG, "Error testing security endpoint");
722         test_delete_session(session);
723         stop_test_service();
724         free(session);
725         return ESP_FAIL;
726     }
727 
728     // Force endpoint with un-encrypted data
729     session->sec_ver = 0;
730 
731     // Send unencrypted request data to echo endpoint.
732     // Response would be encrypted causing echoed back
733     // data to not match that which was sent, hence failing.
734     if (test_req_endpoint(session) == ESP_OK) {
735         ESP_LOGE(TAG, "Error testing request endpoint");
736         session->sec_ver = 1;
737         test_delete_session(session);
738         stop_test_service();
739         free(session);
740         return ESP_FAIL;
741     }
742 
743     session->sec_ver = 1;
744     test_delete_session(session);
745     stop_test_service();
746     free(session);
747     ESP_LOGI(TAG, "Protocomm test successful");
748     return ESP_OK;
749 }
750 
test_security1_session_overflow(void)751 static esp_err_t test_security1_session_overflow (void)
752 {
753     ESP_LOGI(TAG, "Starting Security 1 session overflow test");
754 
755     const char *pop_data = "test pop";
756     protocomm_security_pop_t pop = {
757         .data = (const uint8_t *)pop_data,
758         .len  = strlen(pop_data)
759     };
760 
761     session_t *session1 = calloc(1, sizeof(session_t));
762     if (session1 == NULL) {
763         ESP_LOGE(TAG, "Error allocating session");
764         return ESP_ERR_NO_MEM;
765     }
766 
767     session1->id        = 2;
768     session1->sec_ver   = 1;
769     session1->pop       = &pop;
770 
771     session_t *session2 = calloc(1, sizeof(session_t));
772     if (session2 == NULL) {
773         ESP_LOGE(TAG, "Error allocating session");
774         free(session1);
775         return ESP_ERR_NO_MEM;
776     }
777 
778     session2->id         = 3;
779     session2->sec_ver    = 1;
780     session2->pop        = NULL;
781 
782     // Start protocomm service
783     if (start_test_service(1, &pop) != ESP_OK) {
784         ESP_LOGE(TAG, "Error starting test");
785         free(session1);
786         free(session2);
787         return ESP_FAIL;
788     }
789 
790     // Intialise protocomm session with zero public keys
791     if (test_new_session(session1) != ESP_OK) {
792         ESP_LOGE(TAG, "Error creating new session");
793         stop_test_service();
794         free(session1);
795         free(session2);
796         return ESP_FAIL;
797     }
798 
799     // Perform 25519 security handshake to set public keys
800     if (test_sec_endpoint(session1) != ESP_OK) {
801         ESP_LOGE(TAG, "Error testing security endpoint");
802         test_delete_session(session1);
803         stop_test_service();
804         free(session1);
805         free(session2);
806         return ESP_FAIL;
807     }
808 
809     // Try to perform security handshake again with different
810     // session ID without registering new session, hence failing
811     if (test_sec_endpoint(session2) == ESP_OK) {
812         ESP_LOGE(TAG, "Error testing security endpoint");
813         test_delete_session(session1);
814         stop_test_service();
815         free(session1);
816         free(session2);
817         return ESP_FAIL;
818     }
819 
820     test_delete_session(session1);
821     stop_test_service();
822     free(session1);
823     free(session2);
824 
825     ESP_LOGI(TAG, "Protocomm test successful");
826     return ESP_OK;
827 }
828 
test_security1_wrong_pop(void)829 static esp_err_t test_security1_wrong_pop (void)
830 {
831     ESP_LOGI(TAG, "Starting Security 1 wrong auth test");
832 
833     const char *pop_data = "test pop";
834     protocomm_security_pop_t pop = {
835         .data = (const uint8_t *)pop_data,
836         .len  = strlen(pop_data)
837     };
838 
839     session_t *session = calloc(1, sizeof(session_t));
840     if (session == NULL) {
841         ESP_LOGE(TAG, "Error allocating session");
842         return ESP_ERR_NO_MEM;
843     }
844 
845     session->id        = 4;
846     session->sec_ver   = 1;
847     session->pop       = &pop;
848 
849     // Start protocomm service
850     if (start_test_service(1, &pop) != ESP_OK) {
851         ESP_LOGE(TAG, "Error starting test");
852         free(session);
853         return ESP_FAIL;
854     }
855 
856     // Intialise protocomm session with zero public keys
857     if (test_new_session(session) != ESP_OK) {
858         ESP_LOGE(TAG, "Error creating new session");
859         stop_test_service();
860         free(session);
861         return ESP_FAIL;
862     }
863 
864     const char *wrong_pop_data = "wrong pop";
865     protocomm_security_pop_t wrong_pop = {
866         .data = (const uint8_t *)wrong_pop_data,
867         .len  = strlen(wrong_pop_data)
868     };
869 
870     // Force wrong pop during authentication
871     session->pop = &wrong_pop;
872 
873     // Perform 25519 security handshake with
874     // wrong pop, hence failing
875     if (test_sec_endpoint(session) == ESP_OK) {
876         ESP_LOGE(TAG, "Error testing security endpoint");
877         test_delete_session(session);
878         stop_test_service();
879         free(session);
880         return ESP_FAIL;
881     }
882 
883     test_delete_session(session);
884     stop_test_service();
885     free(session);
886 
887     ESP_LOGI(TAG, "Protocomm test successful");
888     return ESP_OK;
889 }
890 
test_security1_insecure_client(void)891 __attribute__((unused)) static esp_err_t test_security1_insecure_client (void)
892 {
893     ESP_LOGI(TAG, "Starting Security 1 insecure client test");
894 
895     const char *pop_data = "test pop";
896     protocomm_security_pop_t pop = {
897         .data = (const uint8_t *)pop_data,
898         .len  = strlen(pop_data)
899     };
900 
901     session_t *session = calloc(1, sizeof(session_t));
902     if (session == NULL) {
903         ESP_LOGE(TAG, "Error allocating session");
904         return ESP_ERR_NO_MEM;
905     }
906 
907     session->id      = 5;
908     session->sec_ver = 1;
909     session->pop     = &pop;
910 
911     // Start protocomm service
912     if (start_test_service(1, &pop) != ESP_OK) {
913         ESP_LOGE(TAG, "Error starting test");
914         free(session);
915         return ESP_FAIL;
916     }
917 
918     // Perform 25519 security handshake without
919     // initialising session, hence failing
920     if (test_sec_endpoint(session) == ESP_OK) {
921         ESP_LOGE(TAG, "Error testing security endpoint");
922         stop_test_service();
923         free(session);
924         return ESP_FAIL;
925     }
926 
927     // Communicating with request endpoint without
928     // initialising session, hence failing
929     if (test_req_endpoint(session) == ESP_OK) {
930         ESP_LOGE(TAG, "Error testing request endpoint");
931         stop_test_service();
932         free(session);
933         return ESP_FAIL;
934     }
935 
936     stop_test_service();
937     free(session);
938 
939     ESP_LOGI(TAG, "Protocomm test successful");
940     return ESP_OK;
941 }
942 
test_security1_weak_session(void)943 __attribute__((unused)) static esp_err_t test_security1_weak_session (void)
944 {
945     ESP_LOGI(TAG, "Starting Security 1 weak session test");
946 
947     const char *pop_data = "test pop";
948     protocomm_security_pop_t pop = {
949         .data = (const uint8_t *)pop_data,
950         .len  = strlen(pop_data)
951     };
952 
953     session_t *session = calloc(1, sizeof(session_t));
954     if (session == NULL) {
955         ESP_LOGE(TAG, "Error allocating session");
956         return ESP_ERR_NO_MEM;
957     }
958 
959     session->id        = 6;
960     session->sec_ver   = 1;
961     session->pop       = &pop;
962     session->weak      = 1;
963 
964     // Start protocomm service
965     if (start_test_service(1, &pop) != ESP_OK) {
966         ESP_LOGE(TAG, "Error starting test");
967         free(session);
968         return ESP_FAIL;
969     }
970 
971     // Intialise protocomm session with zero public keys
972     if (test_new_session(session) != ESP_OK) {
973         ESP_LOGE(TAG, "Error creating new session");
974         stop_test_service();
975         free(session);
976         return ESP_FAIL;
977     }
978 
979     // Perform 25519 security handshake with weak (zero)
980     // client public key, hence failing
981     if (test_sec_endpoint(session) == ESP_OK) {
982         ESP_LOGE(TAG, "Error testing security endpoint");
983         test_delete_session(session);
984         stop_test_service();
985         free(session);
986         return ESP_FAIL;
987     }
988 
989     // Sending request data to echo endpoint encrypted with zero
990     // public keys on both client and server side should fail
991     if (test_req_endpoint(session) == ESP_OK) {
992         ESP_LOGE(TAG, "Error testing request endpoint");
993         test_delete_session(session);
994         stop_test_service();
995         free(session);
996         return ESP_FAIL;
997     }
998 
999     test_delete_session(session);
1000     stop_test_service();
1001     free(session);
1002 
1003     ESP_LOGI(TAG, "Protocomm test successful");
1004     return ESP_OK;
1005 }
1006 
test_protocomm(session_t * session)1007 static esp_err_t test_protocomm (session_t *session)
1008 {
1009     ESP_LOGI(TAG, "Starting Protocomm test");
1010 
1011     // Start protocomm service
1012     if (start_test_service(session->sec_ver, session->pop) != ESP_OK) {
1013         ESP_LOGE(TAG, "Error starting test");
1014         return ESP_FAIL;
1015     }
1016 
1017     // Check version endpoint
1018     if (test_ver_endpoint(session) != ESP_OK) {
1019         ESP_LOGE(TAG, "Error testing version endpoint");
1020         stop_test_service();
1021         return ESP_FAIL;
1022     }
1023 
1024     // Intialise protocomm session with zero public keys
1025     if (test_new_session(session) != ESP_OK) {
1026         ESP_LOGE(TAG, "Error creating new session");
1027         stop_test_service();
1028         return ESP_FAIL;
1029     }
1030 
1031     // Perform 25519 security handshake to set public keys
1032     if (test_sec_endpoint(session) != ESP_OK) {
1033         ESP_LOGE(TAG, "Error testing security endpoint");
1034         test_delete_session(session);
1035         stop_test_service();
1036         return ESP_FAIL;
1037     }
1038 
1039     // Send request data to echo endpoint encrypted with
1040     // the set public keys on both client and server side
1041     if (test_req_endpoint(session) != ESP_OK) {
1042         ESP_LOGE(TAG, "Error testing request endpoint");
1043         test_delete_session(session);
1044         stop_test_service();
1045         return ESP_FAIL;
1046     }
1047 
1048     // Stop protocomm service
1049     test_delete_session(session);
1050     stop_test_service();
1051     ESP_LOGI(TAG, "Protocomm test successful");
1052     return ESP_OK;
1053 }
1054 
test_security1(void)1055 static esp_err_t test_security1 (void)
1056 {
1057     ESP_LOGI(TAG, "Starting Sec1 test");
1058 
1059     const char *pop_data = "test pop";
1060     protocomm_security_pop_t pop = {
1061         .data = (const uint8_t *)pop_data,
1062         .len  = strlen(pop_data)
1063     };
1064 
1065     session_t *session = calloc(1, sizeof(session_t));
1066     if (session == NULL) {
1067         ESP_LOGE(TAG, "Error allocating session");
1068         return ESP_ERR_NO_MEM;
1069     }
1070 
1071     session->id        = 7;
1072     session->sec_ver   = 1;
1073     session->pop       = &pop;
1074 
1075     if (test_protocomm (session) != ESP_OK) {
1076         ESP_LOGE(TAG, "Sec1 test failed");
1077         free(session);
1078         return ESP_FAIL;
1079     }
1080 
1081     ESP_LOGI(TAG, "Sec1 test successful");
1082     free(session);
1083     return ESP_OK;
1084 }
1085 
test_security0(void)1086 static esp_err_t test_security0 (void)
1087 {
1088     ESP_LOGI(TAG, "Starting Sec0 test");
1089 
1090     session_t *session = calloc(1, sizeof(session_t));
1091     if (session == NULL) {
1092         ESP_LOGE(TAG, "Error allocating session");
1093         return ESP_ERR_NO_MEM;
1094     }
1095 
1096     session->id        = 8;
1097     session->sec_ver   = 0;
1098     session->pop       = NULL;
1099 
1100     if (test_protocomm (session) != ESP_OK) {
1101         ESP_LOGE(TAG, "Sec0 test failed");
1102         free(session);
1103         return ESP_FAIL;
1104     }
1105 
1106     ESP_LOGI(TAG, "Sec0 test successful");
1107     free(session);
1108     return ESP_OK;
1109 }
1110 
1111 TEST_CASE("leak test", "[PROTOCOMM]")
1112 {
1113 #ifdef CONFIG_HEAP_TRACING
1114     heap_trace_init_standalone(trace_record, NUM_RECORDS);
1115     heap_trace_start(HEAP_TRACE_LEAKS);
1116 #endif
1117 
1118     /* Run basic tests for the first time to allow for internal long
1119      * time allocations to happen (not related to protocomm) */
1120     test_security0();
1121     test_security1();
1122     usleep(1000);
1123 
1124 #ifdef CONFIG_HEAP_TRACING
1125     heap_trace_stop();
1126     heap_trace_dump();
1127 #endif
1128 
1129     /* Run all tests passively. Any leaks due
1130      * to protocomm should show  up now */
1131     unsigned pre_start_mem = esp_get_free_heap_size();
1132 
1133     test_security0();
1134     test_security1();
1135     test_security1_no_encryption();
1136     test_security1_session_overflow();
1137     test_security1_wrong_pop();
1138     test_security1_insecure_client();
1139     test_security1_weak_session();
1140 
1141     usleep(1000);
1142 
1143     unsigned post_stop_mem = esp_get_free_heap_size();
1144 
1145     if (pre_start_mem != post_stop_mem) {
1146         ESP_LOGE(TAG, "Mismatch in free heap size : %d bytes", post_stop_mem - pre_start_mem);
1147     }
1148 
1149     TEST_ASSERT(pre_start_mem == post_stop_mem);
1150 }
1151 
1152 TEST_CASE("security 0 basic test", "[PROTOCOMM]")
1153 {
1154     TEST_ASSERT(test_security0() == ESP_OK);
1155 }
1156 
1157 TEST_CASE("security 1 basic test", "[PROTOCOMM]")
1158 {
1159     TEST_ASSERT(test_security1() == ESP_OK);
1160 }
1161 
1162 TEST_CASE("security 1 no encryption test", "[PROTOCOMM]")
1163 {
1164     TEST_ASSERT(test_security1_no_encryption() == ESP_OK);
1165 }
1166 
1167 TEST_CASE("security 1 session overflow test", "[PROTOCOMM]")
1168 {
1169     TEST_ASSERT(test_security1_session_overflow() == ESP_OK);
1170 }
1171 
1172 TEST_CASE("security 1 wrong pop test", "[PROTOCOMM]")
1173 {
1174     TEST_ASSERT(test_security1_wrong_pop() == ESP_OK);
1175 }
1176 
1177 TEST_CASE("security 1 insecure client test", "[PROTOCOMM]")
1178 {
1179     TEST_ASSERT(test_security1_insecure_client() == ESP_OK);
1180 }
1181 
1182 TEST_CASE("security 1 weak session test", "[PROTOCOMM]")
1183 {
1184     TEST_ASSERT(test_security1_weak_session() == ESP_OK);
1185 }
1186