Contiki-NG
Loading...
Searching...
No Matches
edhoc-server.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024, RISE Research Institutes of Sweden AB
3 * Copyright (c) 2020, Industrial Systems Institute (ISI), Patras, Greece
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holder nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32/**
33 * \file
34 * EDHOC server API [RFC9528] with CoAP Block-Wise Transfer [RFC7959]
35 * \author
36 * Lidia Pocero <pocero@isi.gr>, Peter A Jonsson, Rikard Höglund, Marco Tiloca, Niclas Finne <niclas.finne@ri.se>, Nicolas Tsiftes <nicolas.tsiftes@ri.se>
37 */
38
39#include "edhoc-server.h"
41#include "edhoc-msg-handlers.h"
42#include "edhoc-trace.h"
43
44#include "sys/log.h"
45#define LOG_MODULE "EDHOC"
46#define LOG_LEVEL LOG_LEVEL_EDHOC
47
48/* EDHOC Client protocol states */
49#define NON_MSG 0
50#define RX_MSG1 1
51#define RX_MSG3 2
52#define TX_MSG_ERR 3
53#define EXP_READY 4
54#define RESTART 5
55
56static coap_timer_t timer;
57static uint8_t msg_rx[EDHOC_MAX_PAYLOAD_LEN];
58static size_t msg_rx_len;
59static edhoc_server_t server;
60static edhoc_server_t *serv;
61static process_event_t new_ecc_event;
62static ecc_data_event_t new_ecc;
63
64static coap_message_t *request;
65static coap_message_t *response;
66static int err = 0;
67static edhoc_msg_3_t msg3;
68
69#if EDHOC_TEST == EDHOC_TEST_VECTOR_TRACE_DH
70/* Hard-wired ephemeral keys from RFC 9529 for verifying that all operations yield the same
71 intermediate results as the test vectors. */
72static const uint8_t eph_pub_x_r[ECC_KEY_LEN] = { 0x41, 0x97, 0x01, 0xd7, 0xf0, 0x0a, 0x26, 0xc2, 0xdc, 0x58, 0x7a, 0x36, 0xdd, 0x75, 0x25, 0x49, 0xf3, 0x37, 0x63, 0xc8, 0x93, 0x42, 0x2c,
73 0x8e, 0xa0, 0xf9, 0x55, 0xa1, 0x3a, 0x4f, 0xf5, 0xd5 };
74
75static const uint8_t eph_pub_y_r[ECC_KEY_LEN] = { 0x5e, 0x4f, 0x0d, 0xd8, 0xa3, 0xda, 0x0b, 0xaa, 0x16, 0xb9, 0xd3, 0xad, 0x56, 0xa0, 0xc1, 0x86, 0x0a, 0x94, 0x0a, 0xf8, 0x59, 0x14, 0x91,
76 0x5e, 0x25, 0x01, 0x9b, 0x40, 0x24, 0x17, 0xe9, 0x9d };
77
78static const uint8_t eph_private_r[ECC_KEY_LEN] = { 0xe2, 0xf4, 0x12, 0x67, 0x77, 0x20, 0x5e, 0x85, 0x3b, 0x43, 0x7d, 0x6e, 0xac, 0xa1, 0xe1, 0xf7, 0x53, 0xcd, 0xcc, 0x3e, 0x2c, 0x69, 0xfa,
79 0x88, 0x4b, 0x0a, 0x1a, 0x64, 0x09, 0x77, 0xe4, 0x18 };
80#endif
81/*---------------------------------------------------------------------------*/
82static void
83generate_ephemeral_key(uint8_t curve_id, uint8_t *pub_x, uint8_t *pub_y, uint8_t *priv)
84{
85 if(!ecdh_generate_keypair(curve_id, pub_x, pub_y, priv)) {
86 LOG_ERR("Failed to generate ephemeral key pair\n");
87 return;
88 }
89
90#if EDHOC_TEST == EDHOC_TEST_VECTOR_TRACE_DH
91 if(edhoc_ctx != NULL) {
92 memcpy(edhoc_ctx->creds.ephemeral_key.pub.x, eph_pub_x_r, ECC_KEY_LEN);
93 memcpy(edhoc_ctx->creds.ephemeral_key.pub.y, eph_pub_y_r, ECC_KEY_LEN);
94 memcpy(edhoc_ctx->creds.ephemeral_key.priv, eph_private_r, ECC_KEY_LEN);
95 } else {
96 LOG_ERR("Test vector key copy failed - invalid context\n");
97 }
98#endif
99
100 edhoc_trace_ephemeral_key("Responder",
101 edhoc_ctx->creds.ephemeral_key.pub.x,
102 edhoc_ctx->creds.ephemeral_key.pub.y,
103 edhoc_ctx->creds.ephemeral_key.priv);
104}
105/*---------------------------------------------------------------------------*/
106int8_t
107edhoc_server_callback(process_event_t ev, void *data)
108{
109 if(ev == new_ecc_event && new_ecc.val == SERV_HANDSHAKE_COMPLETE) {
110 return SERV_HANDSHAKE_COMPLETE;
111 }
112
113 if(ev == new_ecc_event && new_ecc.val == SERV_HANDSHAKE_RESET) {
114 LOG_DBG("server callback: SERV_HANDSHAKE_RESET\n");
115 return SERV_HANDSHAKE_RESET;
116 }
117 return 0;
118}
119/*---------------------------------------------------------------------------*/
120void
121edhoc_server_set_ad_2(const void *buf, uint8_t buf_sz)
122{
123 if(buf_sz > EDHOC_MAX_AD_SZ) {
124 LOG_ERR("AD_2 size (%u) exceeds maximum AD size (%d)\n", buf_sz, EDHOC_MAX_AD_SZ);
125 new_ecc.ad.ad_2_sz = 0;
126 return;
127 }
128 memcpy(new_ecc.ad.ad_2, (void *)buf, buf_sz);
129 new_ecc.ad.ad_2_sz = buf_sz;
130}
131/*---------------------------------------------------------------------------*/
132uint8_t
134{
135 memcpy(buf, (void *)new_ecc.ad.ad_1, new_ecc.ad.ad_1_sz);
136 return new_ecc.ad.ad_1_sz;
137}
138/*---------------------------------------------------------------------------*/
139uint8_t
141{
142 memcpy(buf, (void *)new_ecc.ad.ad_3, new_ecc.ad.ad_3_sz);
143 return new_ecc.ad.ad_3_sz;
144}
145/*---------------------------------------------------------------------------*/
146static void
147reset_handshake_with_error(void)
148{
151 new_ecc.val = SERV_HANDSHAKE_RESET;
152 process_post(PROCESS_BROADCAST, new_ecc_event, &new_ecc);
153}
154/*---------------------------------------------------------------------------*/
155static void
156server_timeout_callback(coap_timer_t *timer)
157{
158 LOG_ERR("Timeout\n");
159 reset_handshake_with_error();
160}
161/*---------------------------------------------------------------------------*/
162/*---------------------------------------------------------------------------*/
163static int
164handle_msg1_state(void)
165{
166 edhoc_trace_message(1, msg_rx, msg_rx_len, false);
167
168 err = edhoc_handler_msg_1(edhoc_ctx, msg_rx, msg_rx_len,
169 (uint8_t *)new_ecc.ad.ad_1);
170
171 if(err == EDHOC_ERR_MSG_MALFORMED) {
172 LOG_WARN("error code (%d)\n", err);
173 serv->state = NON_MSG;
174 reset_handshake_with_error();
175 return -1;
176 }
177
178 if(err < EDHOC_ERR_MSG_MALFORMED) {
179 LOG_WARN("Send MSG error with code (%d)\n", err);
180 edhoc_ctx->buffers.tx_sz =
182 edhoc_ctx, err);
183 if(edhoc_ctx->buffers.tx_sz == 0) {
184 LOG_ERR("Failed to generate error message\n");
185 }
186 serv->state = TX_MSG_ERR;
188 reset_handshake_with_error();
189 }
190 return -1;
191 }
192
193 memcpy(&serv->con_ipaddr, &request->src_ep->ipaddr, sizeof(uip_ipaddr_t));
194 new_ecc.ad.ad_1_sz = err;
195 if(new_ecc.ad.ad_1_sz > 0) {
196 EDHOC_DBG_VALUE("AD_1", new_ecc.ad.ad_1, new_ecc.ad.ad_1_sz);
197 }
198 serv->rx_msg1 = true;
199
200 EDHOC_TRACE_STEP("message_2 generation");
201 generate_ephemeral_key(edhoc_ctx->config.ecdh_curve,
202 edhoc_ctx->creds.ephemeral_key.pub.x,
203 edhoc_ctx->creds.ephemeral_key.pub.y,
204 edhoc_ctx->creds.ephemeral_key.priv);
205 edhoc_error_t result = edhoc_generate_message_2(edhoc_ctx, (uint8_t *)new_ecc.ad.ad_2,
206 new_ecc.ad.ad_2_sz);
207 if(result != EDHOC_SUCCESS) {
208 LOG_ERR("Failed to generate MSG2: %s\n", edhoc_error_string(result));
209 reset_handshake_with_error();
210 return -1;
211 }
212 edhoc_trace_message(2, edhoc_ctx->buffers.msg_tx, edhoc_ctx->buffers.tx_sz, true);
213 serv->state = RX_MSG3;
214 return 0;
215}
216/*---------------------------------------------------------------------------*/
217static int
218handle_msg3_state(void)
219{
220 edhoc_trace_message(3, msg_rx, msg_rx_len, false);
221
222 err = edhoc_handler_msg_3(&msg3, edhoc_ctx, msg_rx, msg_rx_len);
223
224 if(err > 0) {
225 err = edhoc_authenticate_msg(edhoc_ctx, (uint8_t *)new_ecc.ad.ad_3, false);
226 }
227
228 if(err == EDHOC_ERR_MSG_MALFORMED) {
229 serv->state = NON_MSG;
230 reset_handshake_with_error();
231 return -1;
232 }
233
234 if(err < EDHOC_ERR_MSG_MALFORMED) {
235 edhoc_ctx->buffers.tx_sz =
237 edhoc_ctx, err);
238 if(edhoc_ctx->buffers.tx_sz == 0) {
239 LOG_ERR("Failed to generate error message\n");
240 }
241 reset_handshake_with_error();
242 serv->state = TX_MSG_ERR;
243 return -1;
244 }
245
246 new_ecc.ad.ad_3_sz = err;
247 if(new_ecc.ad.ad_3_sz > 0) {
248 EDHOC_DBG_VALUE("AD_3", new_ecc.ad.ad_3, new_ecc.ad.ad_3_sz);
249 }
250
251 serv->state = EXP_READY;
252 serv->rx_msg3 = true;
253 return 0;
254}
255/*---------------------------------------------------------------------------*/
256static int
257handle_exp_ready_state(void)
258{
259 if(serv->rx_msg1 && serv->rx_msg3) {
260 EDHOC_TRACE_STEP("key export ready");
262 edhoc_ctx->buffers.tx_sz = 0;
263 new_ecc.val = SERV_HANDSHAKE_COMPLETE;
265 process_post(PROCESS_BROADCAST, new_ecc_event, &new_ecc);
266 return 0;
267 } else {
268 LOG_ERR("Protocol step missed\n");
269 serv->state = NON_MSG;
270 return -1;
271 }
272}
273/*---------------------------------------------------------------------------*/
274static void
275setup_coap_response(void)
276{
277 if(serv->state == NON_MSG) {
278 LOG_ERR("RX MSG ERROR response\n");
279 coap_set_payload(response, NULL, 0);
280 coap_set_status_code(response, DELETED_2_02);
281 return;
282 }
283
284 response->payload = (uint8_t *)edhoc_ctx->buffers.msg_tx;
285 response->payload_len = edhoc_ctx->buffers.tx_sz;
286 coap_set_status_code(response, CHANGED_2_04);
287
288 memset(&(response->options), 0, sizeof(response->options));
289
290 if(response->payload_len == 0) {
291 memset(&(request->options), 0, sizeof(request->options));
292 } else {
293 coap_set_header_block2(response, 0,
294 edhoc_ctx->buffers.tx_sz > COAP_MAX_CHUNK_SIZE ? 1 : 0,
295 COAP_MAX_CHUNK_SIZE);
296 }
297
298 if(serv->state == TX_MSG_ERR) {
299 serv->state = NON_MSG;
300 }
301
302 EDHOC_DBG_VALUE("Block1 info", (uint8_t*)&response->block1_num, 0);
303 EDHOC_DBG_VALUE("Block2 info", (uint8_t*)&response->block2_num, 0);
304}
305/*---------------------------------------------------------------------------*/
306uint8_t
308{
309 serv->con_num = 0;
310 serv->state = 0;
311 serv->rx_msg1 = false;
312 serv->rx_msg3 = false;
313 serv->state = NON_MSG;
315 return 0;
316 }
318}
319/*---------------------------------------------------------------------------*/
320uint8_t
322{
323 LOG_INFO("SERVER: EDHOC new\n");
324 edhoc_ctx = edhoc_new();
325 if(edhoc_ctx == NULL) {
326 LOG_ERR("Failed to create EDHOC context\n");
327 return 0;
328 }
329 serv = &server;
331}
332/*---------------------------------------------------------------------------*/
333void
335{
336 LOG_INFO("SERVER: CoAP active resource\n");
338 new_ecc_event = process_alloc_event();
339}
340/*---------------------------------------------------------------------------*/
341void
346/*---------------------------------------------------------------------------*/
347void
348edhoc_server_process(coap_message_t *req, coap_message_t *res,
349 edhoc_server_t *ser, uint8_t *msg, size_t len)
350{
351 if(len == 0) {
352 LOG_ERR("Message length is zero\n");
353 coap_set_payload(res, NULL, 0);
354 coap_set_status_code(res, BAD_REQUEST_4_00);
355 return;
356 }
357 if(len > EDHOC_MAX_PAYLOAD_LEN) {
358 LOG_ERR("Message too large: %zu bytes (max %u)\n", len, EDHOC_MAX_PAYLOAD_LEN);
359 coap_set_payload(res, NULL, 0);
360 coap_set_status_code(res, BAD_REQUEST_4_00);
361 return;
362 }
363
364 memcpy(msg_rx, msg, len);
365 msg_rx_len = len;
366 request = req;
367 response = res;
368 serv = ser;
369
370 if(serv->state == EXP_READY) {
371 EDHOC_TRACE_STATE("EXP_READY", "EXIT");
372 }
373
374 if(serv->state != NON_MSG &&
375 memcmp(&serv->con_ipaddr, &request->src_ep->ipaddr, sizeof(uip_ipaddr_t)) != 0) {
376 LOG_ERR("rx request from an error ipaddr\n");
377 coap_set_payload(response, NULL, 0);
378 coap_set_status_code(response, BAD_REQUEST_4_00);
379 return;
380 }
381
382 switch(serv->state) {
383 case NON_MSG:
384 coap_timer_set_callback(&timer, server_timeout_callback);
386 /* fallthrough to handle MSG1 */
387 case RX_MSG1:
388 if(handle_msg1_state() < 0) {
389 break;
390 }
391 break;
392
393 case RX_MSG3:
394 if(handle_msg3_state() < 0) {
395 break;
396 }
397 /* fallthrough to check if ready */
398 case EXP_READY:
399 handle_exp_ready_state();
400 break;
401
402 default:
403 LOG_ERR("Unknown server state: %d\n", serv->state);
404 serv->state = NON_MSG;
405 break;
406 }
407
408 setup_coap_response();
409}
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.
Definition ecdh.c:91
const char * edhoc_error_string(edhoc_error_t error)
Get a human-readable error message for an error code.
Definition edhoc-error.c:44
edhoc_error_t
Unified error type for EDHOC operations.
Definition edhoc-error.h:60
@ EDHOC_ERR_SUITE_NOT_SUPPORTED
Cipher suite not supported.
Definition edhoc-error.h:83
@ EDHOC_ERR_MSG_MALFORMED
Malformed message received.
Definition edhoc-error.h:84
Declarations for EDHOC message generators.
Declarations for the EDHOC message handlers.
EDHOC server API [RFC9528] with CoAP Block-Wise Transfer [RFC7959].
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.
Definition edhoc-trace.c:64
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.
Definition edhoc-trace.c:48
void edhoc_trace_session_summary(const edhoc_context_t *ctx)
Print session summary at protocol completion.
EDHOC tracing and logging header.
#define EDHOC_TRACE_STATE(from_state, to_state)
Print protocol state transition.
Definition edhoc-trace.h:74
#define EDHOC_TRACE_STEP(step_name)
Print protocol step header in RFC 9529 style.
Definition edhoc-trace.h:50
#define EDHOC_DBG_VALUE(label, data, len)
Print detailed debug value (only at DBG level).
Definition edhoc-trace.h:90
uint8_t edhoc_setup_suites(edhoc_context_t *ctx)
Initialize the EDHOC Context with the defined EDHOC parameters.
Definition edhoc.c:89
uint8_t edhoc_initialize_context(edhoc_context_t *ctx)
Internal API functions.
Definition edhoc.c:688
int edhoc_authenticate_msg(edhoc_context_t *ctx, uint8_t *ad, bool msg2)
Authenticate the rx message.
Definition edhoc.c:867
edhoc_context_t * edhoc_ctx
EDHOC context struct used in the EDHOC protocol.
Definition edhoc.c:62
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.
Definition edhoc.c:784
void edhoc_finalize(edhoc_context_t *ctx)
Close the EDHOC context.
Definition edhoc.c:83
void coap_timer_stop(coap_timer_t *timer)
Stop a pending CoAP timer.
Definition coap-timer.c:92
void coap_timer_set(coap_timer_t *timer, uint64_t time)
Set a CoAP timer to expire after the specified time.
Definition coap-timer.c:103
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.
Definition coap-timer.h:105
void coap_activate_resource(coap_resource_t *resource, const char *path)
Makes a resource available under the given URI path.
void edhoc_server_process(coap_message_t *req, coap_message_t *res, edhoc_server_t *ser, uint8_t *msg, size_t len)
Run the EDHOC Responder role process.
int8_t edhoc_server_callback(process_event_t ev, void *data)
Check if an EDHOC server session has finished.
int edhoc_handler_msg_3(edhoc_msg_3_t *msg3, edhoc_context_t *ctx, uint8_t *payload, size_t payload_size)
Handle the EDHOC Message 3 received.
void edhoc_server_close(void)
Close the EDHOC context.
void edhoc_server_set_ad_2(const void *buf, uint8_t buf_sz)
Set the Application Data to be carried in EDHOC message 2.
#define EDHOC_MAX_AD_SZ
The max length of the Application Data.
int edhoc_handler_msg_1(edhoc_context_t *ctx, uint8_t *payload, size_t payload_size, uint8_t *auth_data)
Handle the EDHOC Message 1 received.
struct ecc_data_event ecc_data_event_t
EDHOC server data event struct.
#define EDHOC_MAX_PAYLOAD_LEN
The max length of the EDHOC message.
struct edhoc_server edhoc_server_t
EDHOC Server Struct.
uint8_t edhoc_server_get_ad_3(char *buf)
Get the Application Data received in EDHOC message 3.
#define SERV_TIMEOUT_VAL
Time limit value for EDHOC protocol completion.
void edhoc_server_init(void)
Activate the EDHOC CoAP Resource.
coap_resource_t res_edhoc
CoAP resource.
uint8_t edhoc_server_reset_handshake(void)
Reset the EDHOC handshake state for a new client connection.
#define EDHOC_COAP_URI_PATH
EDHOC resource CoAP URI path.
uint8_t edhoc_server_start(void)
Create a new EDHOC context for a new EDHOC protocol session.
uint8_t edhoc_server_get_ad_1(char *buf)
Get the Application Data received in EDHOC message 1.
edhoc_error_t edhoc_generate_message_2(edhoc_context_t *ctx, const uint8_t *auth_data, size_t auth_data_size)
Generate the EDHOC Message 2 and set it in the EDHOC context.
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition process.c:325
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition process.c:111
Header file for the logging system.
A timer.
Definition timer.h:84