51#define LOG_MODULE "EDHOC"
52#define LOG_LEVEL LOG_LEVEL_EDHOC
56enum edhoc_protocol_state {
59 PROTO_RX_RSP_MSG3 = 2,
64enum edhoc_event_type {
75static coap_callback_request_state_t state;
76static uint8_t msg_num;
78static uint8_t *rx_ptr;
81static coap_timer_t
timer;
83static process_event_t edhoc_event = PROCESS_EVENT_NONE;
85static uint8_t attempt = 0;
87static edhoc_msg_2_t msg2;
91#if EDHOC_TEST == EDHOC_TEST_VECTOR_TRACE_DH
96static const uint8_t eph_pub_x_i[ECC_KEY_LEN] = { 0x8a, 0xf6, 0xf4, 0x30, 0xeb, 0xe1, 0x8d, 0x34, 0x18, 0x40, 0x17, 0xa9, 0xa1, 0x1b, 0xf5, 0x11, 0xc8, 0xdf, 0xf8, 0xf8, 0x34, 0x73, 0x0b,
97 0x96, 0xc1, 0xb7, 0xc8, 0xdb, 0xca, 0x2f, 0xc3, 0xb6 };
100static const uint8_t eph_pub_y_i[ECC_KEY_LEN] = { 0x51, 0xe8, 0xaf, 0x6c, 0x6e, 0xdb, 0x78, 0x16, 0x01, 0xad, 0x1d, 0x9c, 0x5f, 0xa8, 0xbf, 0x7a, 0xa1, 0x57, 0x16, 0xc7, 0xc0, 0x6a, 0x5d,
101 0x03, 0x85, 0x03, 0xc6, 0x14, 0xff, 0x80, 0xc9, 0xb3 };
104static const uint8_t eph_private_i[ECC_KEY_LEN] = { 0x36, 0x8e, 0xc1, 0xf6, 0x9a, 0xeb, 0x65, 0x9b, 0xa3, 0x7d, 0x5a, 0x8d, 0x45, 0xb2, 0x1b, 0xdc, 0x02, 0x99, 0xdc, 0xea, 0xa8, 0xef, 0x23,
105 0x5f, 0x3c, 0xa4, 0x2c, 0xe3, 0x53, 0x0f, 0x95, 0x25 };
111 if(ev == edhoc_event && data == &edhoc_state) {
112 LOG_DBG(
"client callback: ev=%d, state=%d\n", ev, edhoc_state.val);
113 if(edhoc_state.val == EVT_FINISHED) {
114 LOG_INFO(
"client callback: CL_FINISHED\n");
117 if(edhoc_state.val == EVT_TRIES_EXPIRE) {
118 LOG_WARN(
"client callback: CL_TRIES_EXPIRE\n");
135 LOG_ERR(
"AD_1 size (%d) exceeds maximum allowed (%d)\n", buffer_size,
EDHOC_MAX_AD_SZ);
138 if(data_buffer == NULL) {
139 LOG_ERR(
"Invalid buffer pointer for AD_1\n");
142 memcpy(edhoc_state.ad.ad_1, data_buffer, buffer_size);
143 edhoc_state.ad.ad_1_sz = buffer_size;
150 LOG_ERR(
"AD_3 size (%d) exceeds maximum allowed (%d)\n", buffer_size,
EDHOC_MAX_AD_SZ);
153 if(data_buffer == NULL) {
154 LOG_ERR(
"Invalid buffer pointer for AD_3\n");
157 memcpy(edhoc_state.ad.ad_3, data_buffer, buffer_size);
158 edhoc_state.ad.ad_3_sz = buffer_size;
165 LOG_ERR(
"Invalid buffer pointer for getting AD_2\n");
168 if(edhoc_state.ad.ad_2_sz > buf_sz) {
169 LOG_ERR(
"Destination buffer size (%zu) too small for AD_2 (%d)\n", buf_sz, edhoc_state.ad.ad_2_sz);
172 memcpy(buf, edhoc_state.ad.ad_2, edhoc_state.ad.ad_2_sz);
173 return edhoc_state.ad.ad_2_sz;
177client_timeout_callback(coap_timer_t *
timer)
179 LOG_ERR(
"EDHOC client timeout: no response received\n");
181 edhoc_state.val = EVT_TIMEOUT;
190client_context_new(
void)
207client_block2_handler(coap_message_t *response, uint8_t *target,
208 size_t *len,
size_t max_len)
210 const uint8_t *payload = NULL;
211 int pay_len = coap_get_payload(response, &payload);
213 if(response->block2_offset + pay_len > max_len) {
214 LOG_ERR(
"EDHOC message size (%d) exceeds max buffer (%d)\n", (
int)pay_len,
EDHOC_MAX_BUFFER);
215 coap_status_code = REQUEST_ENTITY_TOO_LARGE_4_13;
216 coap_error_message =
"Message too big";
221 memcpy(target + response->block2_offset, payload, pay_len);
222 *len = response->block2_offset + pay_len;
223 LOG_DBG_BYTES((uint8_t *)payload, (
unsigned long)pay_len);
230validate_response(coap_callback_request_state_t *callback_state)
232 if(callback_state->state.response == NULL) {
233 LOG_WARN(
"Request timed out\n");
237 if(memcmp(callback_state->state.request->token,
238 callback_state->state.response->token,
239 callback_state->state.request->token_len) != 0) {
240 LOG_ERR(
"Response token mismatch\n");
241 }
else if(memcmp(&client->server_ep.ipaddr,
242 &callback_state->state.remote_endpoint->ipaddr,
243 sizeof(uip_ipaddr_t)) != 0) {
244 LOG_ERR(
"Response from unexpected server\n");
249 edhoc_state.val = EVT_RESTART;
256client_response_handler(coap_callback_request_state_t *callback_state)
258 if(!validate_response(callback_state)) {
262 if(callback_state->state.response->code != CHANGED_2_04) {
263 LOG_WARN(
"Response code is not CHANGED_2_04\n");
266 coap_set_option(callback_state->state.response, COAP_OPTION_BLOCK2);
268 client_block2_handler(callback_state->state.response,
270 if(!callback_state->state.more) {
272 edhoc_state.val = EVT_BLOCKING;
273 process_post(PROCESS_BROADCAST, edhoc_event, &edhoc_state);
278client_chunk_handler(coap_callback_request_state_t *callback_state)
280 if(!validate_response(callback_state)) {
284 edhoc_state.val = EVT_BLOCK1;
289edhoc_client_post(
void)
291 coap_init_message(state.state.request, COAP_TYPE_CON, COAP_POST, 0);
296 state.state.block_num = 0;
300edhoc_client_post_blocks(
void)
302 if(
edhoc_ctx->buffers.tx_sz - send_sz > COAP_MAX_CHUNK_SIZE) {
303 coap_set_payload(state.state.request,
304 (uint8_t *)
edhoc_ctx->buffers.msg_tx + send_sz,
305 COAP_MAX_CHUNK_SIZE);
306 coap_set_header_block1(state.state.request, msg_num,
307 1, COAP_MAX_CHUNK_SIZE);
309 send_sz += COAP_MAX_CHUNK_SIZE;
311 state.state.request, client_chunk_handler);
313 }
else if(
edhoc_ctx->buffers.tx_sz < COAP_MAX_CHUNK_SIZE) {
314 coap_set_payload(state.state.request,
319 state.state.block_num = 0;
321 state.state.request, client_response_handler);
324 coap_set_payload(state.state.request,
325 (uint8_t *)
edhoc_ctx->buffers.msg_tx + send_sz,
327 coap_set_header_block1(state.state.request, msg_num, 0,
328 COAP_MAX_CHUNK_SIZE);
329 send_sz += (
edhoc_ctx->buffers.tx_sz - send_sz);
333 state.state.request, client_response_handler);
339generate_msg3_response(
void)
343 edhoc_state.ad.ad_3_sz);
344 if(result != EDHOC_SUCCESS) {
353handle_msg2_error(
int err)
355 LOG_ERR(
"Client: Send MSG error with code (%d)\n", err);
360 LOG_ERR(
"Failed to generate error message\n");
362 client->state = PROTO_IDLE;
364 edhoc_client_post_blocks();
369edhoc_send_msg1(uint8_t *ad, uint8_t ad_sz,
bool suite_array)
373 if(result != EDHOC_SUCCESS) {
378 client->state = PROTO_RX_MSG2;
379 return edhoc_client_post_blocks();
392 edhoc_send_msg1((uint8_t *)edhoc_state.ad.ad_1, edhoc_state.ad.ad_1_sz,
true);
397 assert(msg2->gy_ciphertext_2_sz >= ECC_KEY_LEN);
403 LOG_ERR(
"error code (%d)\n", err);
406 handle_msg2_error(err);
411 edhoc_state.ad.ad_2_sz = err;
412 if(edhoc_state.ad.ad_2_sz > 0) {
417 if(generate_msg3_response() < 0) {
418 client->state = PROTO_IDLE;
422 client->rx_msg2 =
true;
423 client->state = PROTO_RX_RSP_MSG3;
424 client->tx_msg3 =
true;
426 edhoc_client_post_blocks();
436 if(error != EDHOC_SUCCESS) {
437 edhoc_state.val = EVT_RESTART;
438 client->state = PROTO_IDLE;
446 if(client->tx_msg1 && client->rx_msg2) {
447 client->rx_msg3_response =
true;
449 LOG_ERR(
"The EDHOC process escape steps\n");
450 edhoc_state.val = EVT_RESTART;
457 client->state = PROTO_EXP_READY;
458 return handle_exp_ready(client);
466 edhoc_state.val = EVT_FINISHED;
468 process_post(PROCESS_BROADCAST, edhoc_event, &edhoc_state);
474edhoc_client_protocol_run(
void)
476 switch(client->state) {
478 handle_rx_msg2(client, &msg2);
480 case PROTO_RX_RSP_MSG3:
481 handle_rx_response_msg3(client);
483 case PROTO_EXP_READY:
484 handle_exp_ready(client);
492edhoc_client_init(
void)
494 if(edhoc_event == PROCESS_EVENT_NONE) {
497 client = client_context_new();
499 LOG_ERR(
"Failed to allocate client context\n");
505 LOG_ERR(
"Failed to create EDHOC context\n");
509 state.state.request = client->request;
510 state.state.response = client->response;
511 state.state.remote_endpoint = &client->server_ep;
515edhoc_client_start(uint8_t *ad, uint8_t ad_sz)
517 client->tx_msg3 =
false;
518 client->rx_msg3_response =
false;
519 client->tx_msg1 =
true;
524 return edhoc_send_msg1(ad, ad_sz,
false);
528generate_ephemeral_key(uint8_t curve_id, uint8_t *pub_x,
529 uint8_t *pub_y, uint8_t *priv)
532 LOG_ERR(
"Failed to generate ephemeral key pair\n");
536#if EDHOC_TEST == EDHOC_TEST_VECTOR_TRACE_DH
537 memcpy(
edhoc_ctx->creds.ephemeral_key.pub.x, eph_pub_x_i, ECC_KEY_LEN);
538 memcpy(
edhoc_ctx->creds.ephemeral_key.pub.y, eph_pub_y_i, ECC_KEY_LEN);
539 memcpy(
edhoc_ctx->creds.ephemeral_key.priv, eph_private_i, ECC_KEY_LEN);
550handle_retry(
struct etimer *wait_timer, uint8_t *attempt)
553 LOG_INFO(
"Attempt %d\n", *attempt);
557 LOG_ERR(
"Expire EDHOC client attempts\n");
558 edhoc_state.val = EVT_TRIES_EXPIRE;
559 process_post(PROCESS_BROADCAST, edhoc_event, &edhoc_state);
569 client_context_free(client);
577 static struct etimer wait_timer;
585 generate_ephemeral_key(
edhoc_ctx->config.ecdh_curve,
590 edhoc_client_start((uint8_t *)edhoc_state.ad.ad_1, edhoc_state.ad.ad_1_sz);
594 if(ev == edhoc_event && data == &edhoc_state) {
595 if(edhoc_state.val == EVT_RESTART || edhoc_state.val == EVT_TIMEOUT) {
596 LOG_ERR(
"%s\n", edhoc_state.val == EVT_RESTART ?
"Error" :
"Expire timeout");
597 handle_retry(&wait_timer, &attempt);
601 edhoc_client_start((uint8_t *)edhoc_state.ad.ad_1,
602 edhoc_state.ad.ad_1_sz);
606 }
else if(edhoc_state.val == EVT_FINISHED) {
607 LOG_INFO(
"Client has finished\n");
609 }
else if(edhoc_state.val == EVT_BLOCK1) {
610 edhoc_client_post_blocks();
611 }
else if(edhoc_state.val == EVT_POST) {
613 edhoc_client_post_blocks();
614 }
else if(edhoc_state.val == EVT_BLOCKING) {
615 edhoc_client_protocol_run();
616 }
else if(edhoc_state.val == EVT_TRIES_EXPIRE) {
Callback API for doing CoAP requests Adapted from the blocking API.
bool ecdh_generate_keypair(uint8_t curve_id, uint8_t *pub_x, uint8_t *pub_y, uint8_t *priv)
Generates a fresh ECDH key pair.
EDHOC client functionality [RFC9528].
const char * edhoc_error_string(edhoc_error_t error)
Get a human-readable error message for an error code.
edhoc_error_t
Unified error type for EDHOC operations.
@ EDHOC_ERR_MSG_MALFORMED
Malformed message received.
@ EDHOC_ERR_SEQUENCE_ERROR
Message sequence error.
Declarations for EDHOC message generators.
Declarations for the EDHOC message handlers.
void edhoc_trace_ephemeral_key(const char *role_label, const uint8_t *pub_x, const uint8_t *pub_y, const uint8_t *priv)
Print ephemeral key generation step.
void edhoc_trace_message(uint8_t msg_num, const uint8_t *msg_data, size_t msg_len, bool is_tx)
Print EDHOC message in RFC 9529 trace format.
void edhoc_trace_session_summary(const edhoc_context_t *ctx)
Print session summary at protocol completion.
EDHOC tracing and logging header.
#define EDHOC_TRACE_STEP(step_name)
Print protocol step header in RFC 9529 style.
#define EDHOC_DBG_VALUE(label, data, len)
Print detailed debug value (only at DBG level).
uint8_t edhoc_initialize_context(edhoc_context_t *ctx)
Internal API functions.
int edhoc_authenticate_msg(edhoc_context_t *ctx, uint8_t *ad, bool msg2)
Authenticate the rx message.
edhoc_context_t * edhoc_ctx
EDHOC context struct used in the EDHOC protocol.
void edhoc_storage_init(void)
Reserve memory for the EDHOC context struct.
uint8_t edhoc_generate_error_message(uint8_t *msg_er, size_t msg_er_sz, const edhoc_context_t *ctx, int8_t err)
Generate the EDHOC ERROR Message.
void edhoc_finalize(edhoc_context_t *ctx)
Close the EDHOC context.
#define CLOCK_SECOND
A second, measured in system clock time.
void coap_timer_stop(coap_timer_t *timer)
Stop a pending CoAP timer.
void coap_timer_set(coap_timer_t *timer, uint64_t time)
Set a CoAP timer to expire after the specified time.
static void coap_timer_set_callback(coap_timer_t *timer, void(*callback)(coap_timer_t *))
Set a callback function to be called when a CoAP timer expires.
int coap_endpoint_parse(const char *text, size_t size, coap_endpoint_t *ep)
Parse a CoAP endpoint.
int coap_send_request(coap_callback_request_state_t *callback_state, coap_endpoint_t *endpoint, coap_message_t *request, void(*callback)(coap_callback_request_state_t *callback_state))
Send a CoAP request to a remote endpoint.
#define CL_TIMEOUT_VAL
Time limit value to EDHOC protocol finished.
void edhoc_client_run(void)
Run the EDHOC Initiator role.
#define SERVER_EP
The CoAP Server IP where run the EDHOC Responder.
#define EDHOC_MAX_AD_SZ
The max length of the Application Data.
#define EDHOC_MAX_PAYLOAD_LEN
The max length of the EDHOC message.
struct edhoc_data_event edhoc_data_event_t
EDHOC data event struct.
int edhoc_handler_msg_2(edhoc_msg_2_t *msg2, edhoc_context_t *ctx, uint8_t *payload, size_t payload_size)
Handle the EDHOC Message 2 received.
void edhoc_client_set_ad_1(const void *data_buffer, uint8_t buffer_size)
Set the Application Data to be carried in EDHOC message 1.
void edhoc_client_set_ad_3(const void *data_buffer, uint8_t buffer_size)
Set the Application Data to be carried in EDHOC message 3.
int8_t edhoc_client_callback(process_event_t ev, void *data)
Check if the EDHOC client have finished.
edhoc_error_t edhoc_generate_message_1(edhoc_context_t *ctx, uint8_t *ad, size_t ad_sz, bool suite_array)
Generate the EDHOC Message 1 and set it in the EDHOC context.
#define EDHOC_CONF_ATTEMPTS
The number of attempts to try to connect with the EDHOC server successfully.
edhoc_error_t edhoc_check_err_rx_msg(uint8_t message, const uint8_t *data, size_t data_size)
Try to deserialize data as an EDHOC error message.
uint8_t edhoc_client_get_ad_2(char *buf, size_t buf_sz)
Get the Application Data received in EDHOC message 2.
void edhoc_client_close(void)
Close the EDHOC context.
#define EDHOC_COAP_URI_PATH
EDHOC resource CoAP URI path.
edhoc_error_t edhoc_generate_message_3(edhoc_context_t *ctx, const uint8_t *auth_data, size_t auth_data_size)
Generate the EDHOC Message 3 and set it in the EDHOC context.
#define EDHOC_MAX_BUFFER
The max size of the buffers.
struct edhoc_client edhoc_client_t
EDHOC client struct.
void etimer_stop(struct etimer *et)
Stop a pending event timer.
static bool etimer_expired(struct etimer *et)
Check if an event timer has expired.
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
#define MEMB(name, structure, num)
Declare a memory block.
#define PROCESS(name, strname)
Declare a process.
#define PROCESS_EXIT()
Exit the currently running process.
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
process_event_t process_alloc_event(void)
Allocate a global event number.
#define PROCESS_BEGIN()
Define the beginning of a process.
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
#define PROCESS_END()
Define the end of a process.
void process_start(struct process *p, process_data_t data)
Start a process.
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Header file for the logging system.
Memory block allocation routines.