39#include "contiki-lib.h"
46#define LOG_MODULE "EDHOC"
47#define LOG_LEVEL LOG_LEVEL_EDHOC
53 LOG_DBG(
"Type: %d\n", msg->method);
55 LOG_DBG_BYTES(msg->suites_i, msg->suites_i_num);
58 LOG_DBG_BYTES(msg->g_x, ECC_KEY_LEN);
61 LOG_DBG_BYTES(msg->c_i, msg->c_i_sz);
63 LOG_DBG(
"EAD (label: %" PRId32
"): ", msg->uad.ead_label);
64 LOG_DBG_BYTES(msg->uad.ead_value, msg->uad.ead_value_sz);
71 LOG_DBG(
"gy_ciphertext_2: ");
72 LOG_DBG_BYTES(msg->gy_ciphertext_2, msg->gy_ciphertext_2_sz);
79 LOG_DBG(
"CIPHERTEXT_3: ");
80 LOG_DBG_BYTES(msg->ciphertext_3, msg->ciphertext_3_sz);
88 if(next == CBOR_MAJOR_TYPE_BYTE_STRING) {
103 LOG_ERR(
"Unsupported CID: value outside ranges (0x00-0x17, 0x20-0x37)\n");
117 if(byte_length == 1 && ((*bytes <= 0x17) || (*bytes >= 0x20 && *bytes <= 0x37))) {
130 if(suite_count == 1) {
134 for(
int idx = 0; idx < suite_count; idx++) {
142 size_t buffer_size, uint8_t *suite_count)
144 if(!suites_buffer || !buffer_size) {
148 if(suite_array_size < SIZE_MAX) {
149 if(suite_array_size > buffer_size) {
151 LOG_WARN(
"Too many suites (truncating): %zu/%zu\n", suite_array_size, buffer_size);
152 suite_array_size = buffer_size;
154 *suite_count = suite_array_size;
155 for(
int i = 0; i < suite_array_size; i++) {
161 suites_buffer[i] = (uint8_t)
value;
168 suites_buffer[0] = (uint8_t)
value;
178 size_t buffer_sz,
bool suite_array)
184 edhoc_write_suites(&writer, msg->suites_i, msg->suites_i_num);
188 if(msg->uad.ead_value_sz > 0) {
194 LOG_ERR(
"failed to serialize msg 1\n");
207 switch(msg->err_code) {
208 case EDHOC_MSG_ERR_CODE_UNSPECIFIED_ERROR:
210 msg->info.tstr.err_info_sz);
212 case EDHOC_MSG_ERR_CODE_WRONG_CIPHER_SUITE:
213 edhoc_write_suites(&writer, msg->info.suites.suites,
214 msg->info.suites.suites_num);
216 case EDHOC_MSG_ERR_CODE_UNKNOWN_CREDENTIAL_SELECTION:
220 LOG_ERR(
"edhoc_serialize_err: unhandled err code: %d\n", msg->err_code);
238 int32_t ret = (int32_t)
value;
240 memset(msg, 0,
sizeof(*msg));
241 msg->err_code = (uint8_t)ret;
243 switch(msg->err_code) {
244 case EDHOC_MSG_ERR_CODE_RESERVED_FOR_SUCCESS:
247 case EDHOC_MSG_ERR_CODE_UNSPECIFIED_ERROR:
249 &msg->info.tstr.err_info_sz);
250 if(msg->info.tstr.err_info && msg->info.tstr.err_info_sz > 0
256 case EDHOC_MSG_ERR_CODE_WRONG_CIPHER_SUITE:
257 if(edhoc_deserialize_suites(&reader, msg->info.suites.suites,
258 sizeof(msg->info.suites.suites),
259 &msg->info.suites.suites_num)
265 case EDHOC_MSG_ERR_CODE_UNKNOWN_CREDENTIAL_SELECTION: {
274 case EDHOC_MSG_ERR_CODE_RESERVED:
284 if((ret < -65536) || (ret >= -24 && ret <= 0) || (ret > 65535)) {
287 if(msg->err_code >= 4 && msg->err_code <= 22) {
288 LOG_DBG(
"edhoc_deserialize_err: unassigned error code %d\n", msg->err_code);
290 LOG_DBG(
"edhoc_deserialize_err: unhandled error code %d\n", msg->err_code);
307 msg->method = (int8_t)
value;
308 LOG_INFO(
"msg1: method: %d\n", msg->method);
311 if(!edhoc_deserialize_suites(&reader, msg->suites_i,
sizeof(msg->suites_i),
312 &msg->suites_i_num)) {
313 LOG_ERR(
"Failed to deserialize cipher suites from MSG_1\n");
316 LOG_INFO(
"msg1: suites: %u\n", msg->suites_i_num);
321 if(!data || data_size != ECC_KEY_LEN) {
322 LOG_ERR(
"Invalid G_X in MSG_1: expected %d bytes, got %zu\n", ECC_KEY_LEN, data_size);
330 LOG_ERR(
"Invalid C_I in MSG_1: size %zu exceeds max %d\n", data_size,
EDHOC_MAX_CID_LEN);
334 msg->c_i_sz = data_size;
340 int64_t signed_label;
344 msg->uad.ead_label = (int32_t)signed_label;
346 LOG_DBG(
"MSG_1: No EAD label found, using legacy format\n");
353 edhoc_deserialize_suites(&legacy_reader, msg->suites_i,
sizeof(msg->suites_i), &msg->suites_i_num);
359 if(data && data_size) {
360 msg->uad.ead_label = 0;
361 msg->uad.ead_value = data;
362 msg->uad.ead_value_sz = data_size;
364 return EDHOC_SUCCESS;
371 msg->uad.ead_value = ead_value;
372 msg->uad.ead_value_sz = data_size;
376 msg->uad.ead_value = NULL;
377 msg->uad.ead_value_sz = 0;
380 LOG_DBG(
"MSG_1: Parsed EAD - label: %" PRId32
", value_sz: %zu\n",
381 msg->uad.ead_label, msg->uad.ead_value_sz);
384 return EDHOC_SUCCESS;
391 LOG_ERR(
"Invalid msg parameter for edhoc_deserialize_msg_2\n");
401 LOG_ERR(
"Failed to read MSG_2 data\n");
406 if(data_size < ECC_KEY_LEN + 1) {
407 LOG_ERR(
"MSG_2 size (%zu) too small (minimum %d bytes)\n",
408 data_size, ECC_KEY_LEN + 1);
414 LOG_ERR(
"MSG_2 size (%zu) exceeds maximum payload length (%d)\n",
419 msg->gy_ciphertext_2 = (uint8_t *)data;
420 msg->gy_ciphertext_2_sz = data_size;
421 return EDHOC_SUCCESS;
428 LOG_ERR(
"Invalid msg parameter for edhoc_deserialize_msg_3\n");
438 LOG_ERR(
"Failed to read MSG_3 data\n");
443 if(data_size < EDHOC_MAC_LEN_8) {
444 LOG_ERR(
"MSG_3 size (%zu) too small (minimum %d bytes)\n",
445 data_size, EDHOC_MAC_LEN_8);
451 LOG_ERR(
"MSG_3 size (%zu) exceeds maximum payload length (%d)\n",
456 msg->ciphertext_3 = (uint8_t *)data;
457 msg->ciphertext_3_sz = data_size;
458 return EDHOC_SUCCESS;
464 LOG_DBG(
"Looking for auth key with KID ");
465 LOG_DBG_BYTES(kid, kid_sz);
466 LOG_DBG_(
" (sz=%d)\n", kid_sz);
470 if(result != EDHOC_SUCCESS) {
471 LOG_ERR(
"Auth key ID not found. Searched for KID ");
472 LOG_ERR_BYTES(kid, kid_sz);
473 LOG_ERR_(
" (sz=%d)\n", kid_sz);
476 LOG_DBG(
"Auth key found with KID ");
477 LOG_DBG_BYTES(kid, kid_sz);
480 return EDHOC_SUCCESS;
486 LOG_ERR(
"Insufficient data for compact encoding\n");
491 key->kid[0] = *current_pos;
497 LOG_ERR(
"Failed to read compact encoding as integer\n");
504 if(result != EDHOC_SUCCESS) {
509 return EDHOC_SUCCESS;
517 if(!data || data_size == 0) {
518 LOG_ERR(
"Failed to read key ID data from CBOR map in ID_CRED_X\n");
523 if(data_size >
sizeof(key->kid)) {
524 LOG_ERR(
"Key ID size (%zu) exceeds maximum (%zu)\n", data_size,
sizeof(key->kid));
530 if(result != EDHOC_SUCCESS) {
535 return EDHOC_SUCCESS;
541 LOG_DBG(
"**** ID_CRED_R = CRED_R");
546 LOG_ERR(
"Failed to read key type\n");
549 key->kty = (uint8_t)kty_val;
554 LOG_ERR(
"Expected curve parameter (-1)\n");
560 LOG_ERR(
"Failed to read curve value\n");
563 key->crv = (uint8_t)crv_val;
567 LOG_ERR(
"Expected x coordinate parameter (-2)\n");
573 if(!x_data || x_size != ECC_KEY_LEN) {
574 LOG_ERR(
"Invalid x coord: expected %d bytes, got %zu\n", ECC_KEY_LEN, x_size);
577 memcpy(key->ecc.pub.x, x_data, ECC_KEY_LEN);
581 LOG_ERR(
"Expected y coordinate parameter (-3)\n");
587 if(!y_data || y_size != ECC_KEY_LEN) {
588 LOG_ERR(
"Invalid y coord: expected %d bytes, got %zu\n", ECC_KEY_LEN, y_size);
591 memcpy(key->ecc.pub.y, y_data, ECC_KEY_LEN);
596 if(identity_data && identity_sz > 0) {
598 if(identity_sz >
sizeof(key->identity)) {
599 LOG_ERR(
"Identity size (%zu) exceeds maximum (%zu)\n", identity_sz,
sizeof(key->identity));
602 key->identity_sz = identity_sz;
603 memcpy(key->identity, identity_data, identity_sz);
606 return EDHOC_SUCCESS;
613 if(next == CBOR_MAJOR_TYPE_MAP) {
615 if(map_entries != SIZE_MAX && map_entries > 0) {
618 return (uint8_t)label_val;
623 return COSE_HEADER_LABEL_COMPACT_ENCODING;
627copy_id_cred_x_to_output(
const uint8_t *
start,
const uint8_t *end,
628 uint8_t *out_id_cred_x,
size_t id_cred_x_buffer_size,
631 uint16_t id_cred_x_sz = end -
start;
638 if(id_cred_x_buffer_size == 0) {
639 LOG_ERR(
"Output buffer size is zero\n");
643 if(id_cred_x_sz > id_cred_x_buffer_size) {
644 LOG_ERR(
"ID_CRED_X size (%u) exceeds output buffer size (%zu)\n",
645 id_cred_x_sz, id_cred_x_buffer_size);
649 memcpy(out_id_cred_x,
start, id_cred_x_sz);
652 if(id_cred_x_sz == 1) {
661 LOG_ERR(
"Failed to rebuild compact encoding\n");
671 size_t id_cred_x_buffer_size,
cose_key_t *key)
673 if(!reader || !key) {
674 LOG_ERR(
"Invalid parameters for edhoc_get_key_id_cred_x\n");
679 uint8_t label = determine_credential_label(reader);
684 case COSE_HEADER_LABEL_COMPACT_ENCODING:
685 key_sz = parse_compact_encoding(reader, key);
687 case COSE_HEADER_LABEL_KID:
688 key_sz = parse_kid_map(reader, key);
690 case COSE_HEADER_LABEL_CRED_FULL:
691 key_sz = parse_full_credential(reader, key);
694 LOG_ERR(
"Unknown credential label %d\n", label);
699 LOG_ERR(
"Failed to parse credential: %d\n", key_sz);
704 return copy_id_cred_x_to_output(
start, end, out_id_cred_x,
705 id_cred_x_buffer_size, key);
711 if(!reader || !sign) {
712 LOG_ERR(
"Invalid parameters for edhoc_get_sign\n");
719 LOG_ERR(
"Failed to read signature data\n");
724 if(sign_sz > P384_SIGNATURE_LEN) {
725 LOG_ERR(
"Signature size (%zu) exceeds maximum allowed (%d)\n", sign_sz, P384_SIGNATURE_LEN);
729 *sign = (uint8_t *)data;
736 if(!reader || !ad || ad_buffer_size == 0) {
737 LOG_ERR(
"Invalid parameters for edhoc_get_ad\n");
744 LOG_ERR(
"Failed to read AD data\n");
749 if(ad_sz > ad_buffer_size) {
750 LOG_ERR(
"AD size (%zu) exceeds buffer size (%zu)\n", ad_sz, ad_buffer_size);
756 LOG_ERR(
"AD size (%zu) exceeds maximum allowed (%d)\n", ad_sz,
EDHOC_MAX_AD_SZ);
760 memcpy(ad, data, ad_sz);
772 if(ead_data->ead_label == 0) {
773 LOG_DBG(
"EAD: Ignoring padding item (label 0)\n");
774 return EDHOC_SUCCESS;
780 bool is_critical = (ead_data->ead_label < 0);
782 LOG_DBG(
"EAD: Processing %s item with label %" PRId32
", value_sz=%zu\n",
783 is_critical ?
"critical" :
"non-critical",
784 ead_data->ead_label, ead_data->ead_value_sz);
811 LOG_ERR(
"EAD: Critical EAD item with label %" PRId32
" cannot be processed\n",
812 ead_data->ead_label);
816 LOG_DBG(
"EAD: Ignoring non-critical item (label %" PRId32
")\n",
817 ead_data->ead_label);
818 return EDHOC_SUCCESS;
edhoc_error_t
Unified error type for EDHOC operations.
@ EDHOC_ERR_NULL_POINTER
Null pointer provided.
@ EDHOC_ERR_ID_CRED_MALFORMED
Malformed credential identifier.
@ EDHOC_ERR_CRITICAL_EAD_UNSUPPORTED
Critical EAD item cannot be processed (RFC 9528 Section 6).
@ EDHOC_ERR_BUFFER_OVERFLOW
Buffer overflow detected.
@ EDHOC_ERR_MSG_MALFORMED
Malformed message received.
#define EDHOC_FAILED(result)
Check if an operation failed.
edhoc_error_t edhoc_check_key_list_kid(uint8_t *key_id, uint8_t key_id_size, cose_key_t **authentication_key)
Check in the keys repository for the key with the specific KID.
struct cose_key cose_key_t
KEY length in bytes.
edhoc_error_t edhoc_deserialize_msg_1(edhoc_msg_1_t *msg, unsigned char *buffer, size_t buff_sz)
Deserialize EDHOC message 1 from CBOR data.
void print_msg_3(edhoc_msg_3_t *msg)
Print EDHOC message 3 contents for debugging.
size_t edhoc_serialize_err(const edhoc_msg_error_t *msg, unsigned char *buffer, size_t buffer_sz)
Serialize EDHOC error message to CBOR format.
void print_msg_1(edhoc_msg_1_t *msg)
Print EDHOC message 1 contents for debugging.
const uint8_t * edhoc_read_byte_identifier(cbor_reader_state_t *reader, size_t *size)
Read byte identifier from CBOR reader.
uint8_t edhoc_get_ad(cbor_reader_state_t *reader, uint8_t *ad, size_t ad_buffer_size)
Parse Additional Data (AD) from CBOR reader.
void print_msg_2(edhoc_msg_2_t *msg)
Print EDHOC message 2 contents for debugging.
int8_t edhoc_get_key_id_cred_x(cbor_reader_state_t *reader, uint8_t *out_id_cred_x, size_t id_cred_x_buffer_size, cose_key_t *key)
Parse ID_CRED_X from CBOR data and extract authentication key.
edhoc_error_t edhoc_deserialize_msg_3(edhoc_msg_3_t *msg, unsigned char *buffer, size_t buff_sz)
Deserialize EDHOC message 3 from CBOR data.
edhoc_error_t edhoc_get_auth_key_from_kid(uint8_t *kid, uint8_t kid_sz, cose_key_t **key)
Get authentication key by Key ID (KID).
size_t edhoc_serialize_msg_1(edhoc_msg_1_t *msg, unsigned char *buffer, size_t buffer_sz, bool suite_array)
Serialize EDHOC message 1 to CBOR format.
uint8_t edhoc_get_sign(cbor_reader_state_t *reader, uint8_t **sign)
Parse signature data from CBOR reader.
edhoc_error_t edhoc_deserialize_msg_2(edhoc_msg_2_t *msg, unsigned char *buffer, size_t buff_sz)
Deserialize EDHOC message 2 from CBOR data.
edhoc_error_t edhoc_process_ead_item(const edhoc_ead_data_t *ead_data)
Process and validate EAD (External Authorization Data) items.
void edhoc_write_byte_identifier(cbor_writer_state_t *state, const uint8_t *bytes, size_t byte_length)
Write EDHOC byte identifier according to RFC 9528 rules.
bool edhoc_deserialize_err(edhoc_msg_error_t *msg, const uint8_t *data, size_t data_size)
Deserialize EDHOC error message from CBOR data.
EDHOC message serialization and parsing API.
size_t cbor_end_writer(cbor_writer_state_t *state)
Finishes writing CBOR output.
size_t cbor_get_remaining(cbor_reader_state_t *state)
Gets the remaining bytes in the CBOR buffer.
void cbor_open_map(cbor_writer_state_t *state)
Adds subsequent entries to a map.
cbor_size_t cbor_read_signed(cbor_reader_state_t *state, int64_t *value)
Reads a signed integer.
size_t cbor_read_array(cbor_reader_state_t *state)
Reads the number of elements of an array.
cbor_major_type_t
Enumeration of major types.
cbor_simple_value_t
Enumeration of simple values.
cbor_size_t cbor_read_unsigned(cbor_reader_state_t *state, uint64_t *value)
Reads an unsigned integer.
cbor_simple_value_t cbor_read_simple(cbor_reader_state_t *state)
Reads a simple value.
void cbor_write_text(cbor_writer_state_t *state, const char *text, size_t text_size)
Appends a text string.
void cbor_write_unsigned(cbor_writer_state_t *state, uint64_t value)
Appends an unsigned integer.
cbor_major_type_t cbor_peek_next(cbor_reader_state_t *state)
Inspects the next major type.
void cbor_write_bool(cbor_writer_state_t *state, bool boolean)
Appends a boolean simple value.
void cbor_open_array(cbor_writer_state_t *state)
Adds subsequent CBOR objects to an array.
bool cbor_skip_next(cbor_reader_state_t *state)
Skips the next CBOR object in the buffer.
const uint8_t * cbor_read_data(cbor_reader_state_t *state, size_t *data_size)
Reads a byte string.
void cbor_close_map(cbor_writer_state_t *state)
Stops adding subsequent entries to the innermost map.
void cbor_write_object(cbor_writer_state_t *state, const void *object, size_t object_size)
Appends an arbitrary CBOR object.
void cbor_init_writer(cbor_writer_state_t *state, uint8_t *buffer, size_t buffer_size)
Prepares for writing CBOR output.
cbor_size_t
Enumeration of size information in various major types.
const uint8_t * cbor_get_position(cbor_reader_state_t *state)
Gets the current position in the CBOR buffer.
void cbor_close_array(cbor_writer_state_t *state)
Stops adding subsequent CBOR objects to the innermost array.
void cbor_init_reader(cbor_reader_state_t *state, const uint8_t *cbor, size_t cbor_size)
Prepares for reading CBOR input.
void cbor_write_data(cbor_writer_state_t *state, const uint8_t *data, size_t data_size)
Appends a byte string.
size_t cbor_read_map(cbor_reader_state_t *state)
Reads the number of entries of a map.
const char * cbor_read_text(cbor_reader_state_t *state, size_t *text_size)
Reads a text string.
@ CBOR_SIZE_NONE
error condition
static int value(int type)
#define EDHOC_MAX_AD_SZ
The max length of the Application Data.
#define EDHOC_MAX_PAYLOAD_LEN
The max length of the EDHOC message.
#define EDHOC_MAX_CID_LEN
Maximum length of connection identifiers RFC 9528 allows variable-length CIDs including negative inte...
static void start(void)
Start measurement.
Header file for the logging system.
Structure of the internal state of a CBOR reader.
Structure of the internal state of a CBOR writer.