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