1 /*
2 * coap_tinydtls.c -- Datagram Transport Layer Support for libcoap with tinydtls
3 *
4 * Copyright (C) 2016 Olaf Bergmann <bergmann@tzi.org>
5 *
6 * This file is part of the CoAP library libcoap. Please see README for terms
7 * of use.
8 */
9
10 #include "coap_internal.h"
11
12 #ifdef HAVE_LIBTINYDTLS
13
14 /* We want TinyDTLS versions of these, not libcoap versions */
15 #undef PACKAGE_BUGREPORT
16 #undef PACKAGE_NAME
17 #undef PACKAGE_STRING
18 #undef PACKAGE_TARNAME
19 #undef PACKAGE_URL
20 #undef PACKAGE_VERSION
21
22 #include <tinydtls.h>
23 #include <dtls.h>
24 #include <dtls_debug.h>
25
26 static dtls_tick_t dtls_tick_0 = 0;
27 static coap_tick_t coap_tick_0 = 0;
28
29 int
coap_dtls_is_supported(void)30 coap_dtls_is_supported(void) {
31 return 1;
32 }
33
coap_dtls_startup(void)34 void coap_dtls_startup(void) {
35 dtls_init();
36 dtls_ticks(&dtls_tick_0);
37 coap_ticks(&coap_tick_0);
38 }
39
40 void
coap_dtls_set_log_level(int level)41 coap_dtls_set_log_level(int level) {
42 dtls_set_log_level(level);
43 }
44
45 int
coap_dtls_get_log_level(void)46 coap_dtls_get_log_level(void) {
47 return dtls_get_log_level();
48 }
49
get_session_addr(const session_t * s,coap_address_t * a)50 static void get_session_addr(const session_t *s, coap_address_t *a) {
51 #ifdef WITH_CONTIKI
52 a->addr = s->addr;
53 a->port = s->port;
54 #else
55 if (s->addr.sa.sa_family == AF_INET6) {
56 a->size = (socklen_t)sizeof(a->addr.sin6);
57 a->addr.sin6 = s->addr.sin6;
58 } else if (s->addr.sa.sa_family == AF_INET) {
59 a->size = (socklen_t)sizeof(a->addr.sin);
60 a->addr.sin = s->addr.sin;
61 } else {
62 a->size = (socklen_t)s->size;
63 a->addr.sa = s->addr.sa;
64 }
65 #endif
66 }
67
put_session_addr(const coap_address_t * a,session_t * s)68 static void put_session_addr(const coap_address_t *a, session_t *s) {
69 #ifdef WITH_CONTIKI
70 s->size = (unsigned char)sizeof(s->addr);
71 s->addr = a->addr;
72 s->port = a->port;
73 #else
74 if (a->addr.sa.sa_family == AF_INET6) {
75 s->size = (socklen_t)sizeof(s->addr.sin6);
76 s->addr.sin6 = a->addr.sin6;
77 } else if (a->addr.sa.sa_family == AF_INET) {
78 s->size = (socklen_t)sizeof(s->addr.sin);
79 s->addr.sin = a->addr.sin;
80 } else {
81 s->size = (socklen_t)a->size;
82 s->addr.sa = a->addr.sa;
83 }
84 #endif
85 }
86
87 static int
dtls_send_to_peer(struct dtls_context_t * dtls_context,session_t * dtls_session,uint8 * data,size_t len)88 dtls_send_to_peer(struct dtls_context_t *dtls_context,
89 session_t *dtls_session, uint8 *data, size_t len) {
90 coap_context_t *coap_context = (coap_context_t *)dtls_get_app_data(dtls_context);
91 coap_session_t *coap_session;
92 coap_address_t remote_addr;
93
94 get_session_addr(dtls_session, &remote_addr);
95 coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
96 if (!coap_session) {
97 coap_log(LOG_WARNING, "dtls_send_to_peer: cannot find local interface\n");
98 return -3;
99 }
100 return (int)coap_session_send(coap_session, data, len);
101 }
102
103 static int
dtls_application_data(struct dtls_context_t * dtls_context,session_t * dtls_session,uint8 * data,size_t len)104 dtls_application_data(struct dtls_context_t *dtls_context,
105 session_t *dtls_session, uint8 *data, size_t len) {
106 coap_context_t *coap_context = (coap_context_t *)dtls_get_app_data(dtls_context);
107 coap_session_t *coap_session;
108 coap_address_t remote_addr;
109
110 get_session_addr(dtls_session, &remote_addr);
111 coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
112 if (!coap_session) {
113 coap_log(LOG_DEBUG,
114 "dropped message that was received on invalid interface\n");
115 return -1;
116 }
117
118 return coap_handle_dgram(coap_context, coap_session, data, len);
119 }
120
121 static int coap_event_dtls = 0;
122
123 static int
dtls_event(struct dtls_context_t * dtls_context,session_t * dtls_session,dtls_alert_level_t level,uint16_t code)124 dtls_event(struct dtls_context_t *dtls_context,
125 session_t *dtls_session,
126 dtls_alert_level_t level,
127 uint16_t code) {
128 (void)dtls_context;
129 (void)dtls_session;
130
131 if (level == DTLS_ALERT_LEVEL_FATAL)
132 coap_event_dtls = COAP_EVENT_DTLS_ERROR;
133
134 /* handle DTLS events */
135 switch (code) {
136 case DTLS_ALERT_CLOSE_NOTIFY:
137 {
138 coap_event_dtls = COAP_EVENT_DTLS_CLOSED;
139 break;
140 }
141 case DTLS_EVENT_CONNECTED:
142 {
143 coap_event_dtls = COAP_EVENT_DTLS_CONNECTED;
144 break;
145 }
146 case DTLS_EVENT_RENEGOTIATE:
147 {
148 coap_event_dtls = COAP_EVENT_DTLS_RENEGOTIATE;
149 break;
150 }
151 default:
152 ;
153 }
154
155 return 0;
156 }
157
158 /* This function is the "key store" for tinyDTLS. It is called to
159 * retrieve a key for the given identity within this particular
160 * session. */
161 static int
get_psk_info(struct dtls_context_t * dtls_context,const session_t * dtls_session,dtls_credentials_type_t type,const uint8_t * id,size_t id_len,unsigned char * result,size_t result_length)162 get_psk_info(struct dtls_context_t *dtls_context,
163 const session_t *dtls_session,
164 dtls_credentials_type_t type,
165 const uint8_t *id, size_t id_len,
166 unsigned char *result, size_t result_length) {
167 coap_context_t *coap_context;
168 coap_session_t *coap_session;
169 int fatal_error = DTLS_ALERT_INTERNAL_ERROR;
170 size_t identity_length;
171 static int client = 0;
172 static uint8_t psk[128];
173 static size_t psk_len = 0;
174 coap_address_t remote_addr;
175
176
177 if (type == DTLS_PSK_KEY && client) {
178 if (psk_len > result_length) {
179 coap_log(LOG_WARNING, "cannot set psk -- buffer too small\n");
180 goto error;
181 }
182 memcpy(result, psk, psk_len);
183 client = 0;
184 return (int)psk_len;
185 }
186
187 client = 0;
188 coap_context = (coap_context_t *)dtls_get_app_data(dtls_context);
189 get_session_addr(dtls_session, &remote_addr);
190 coap_session = coap_session_get_by_peer(coap_context, &remote_addr, dtls_session->ifindex);
191 if (!coap_session) {
192 coap_log(LOG_DEBUG, "cannot get PSK, session not found\n");
193 goto error;
194 }
195
196 switch (type) {
197 case DTLS_PSK_IDENTITY:
198
199 if (id_len)
200 coap_log(LOG_DEBUG, "got psk_identity_hint: '%.*s'\n", (int)id_len, id);
201
202 if (!coap_context || !coap_context->get_client_psk)
203 goto error;
204
205 identity_length = 0;
206 psk_len = coap_context->get_client_psk(coap_session, (const uint8_t*)id, id_len, (uint8_t*)result, &identity_length, result_length, psk, sizeof(psk));
207 if (!psk_len) {
208 coap_log(LOG_WARNING, "no PSK identity for given realm\n");
209 fatal_error = DTLS_ALERT_CLOSE_NOTIFY;
210 goto error;
211 }
212 client = 1;
213 return (int)identity_length;
214
215 case DTLS_PSK_KEY:
216 if (coap_context->get_server_psk)
217 return (int)coap_context->get_server_psk(coap_session, (const uint8_t*)id, id_len, (uint8_t*)result, result_length);
218 return 0;
219 break;
220
221 case DTLS_PSK_HINT:
222 client = 0;
223 if (coap_context->get_server_hint)
224 return (int)coap_context->get_server_hint(coap_session, (uint8_t *)result, result_length);
225 return 0;
226
227 default:
228 coap_log(LOG_WARNING, "unsupported request type: %d\n", type);
229 }
230
231 error:
232 client = 0;
233 return dtls_alert_fatal_create(fatal_error);
234 }
235
236 static dtls_handler_t cb = {
237 .write = dtls_send_to_peer,
238 .read = dtls_application_data,
239 .event = dtls_event,
240 .get_psk_info = get_psk_info,
241 #ifdef WITH_ECC
242 .get_ecdsa_key = NULL,
243 .verify_ecdsa_key = NULL
244 #endif
245 };
246
247 void *
coap_dtls_new_context(struct coap_context_t * coap_context)248 coap_dtls_new_context(struct coap_context_t *coap_context) {
249 struct dtls_context_t *dtls_context = dtls_new_context(coap_context);
250 if (!dtls_context)
251 goto error;
252 dtls_set_handler(dtls_context, &cb);
253 return dtls_context;
254 error:
255 coap_dtls_free_context(dtls_context);
256 return NULL;
257 }
258
259 void
coap_dtls_free_context(void * handle)260 coap_dtls_free_context(void *handle) {
261 if (handle) {
262 struct dtls_context_t *dtls_context = (struct dtls_context_t *)handle;
263 dtls_free_context(dtls_context);
264 }
265 }
266
267 static session_t *
coap_dtls_new_session(coap_session_t * session)268 coap_dtls_new_session(coap_session_t *session) {
269 session_t *dtls_session = coap_malloc_type(COAP_DTLS_SESSION, sizeof(session_t));
270
271 if (dtls_session) {
272 /* create tinydtls session object from remote address and local
273 * endpoint handle */
274 dtls_session_init(dtls_session);
275 put_session_addr(&session->addr_info.remote, dtls_session);
276 dtls_session->ifindex = session->ifindex;
277 coap_log(LOG_DEBUG, "***new session %p\n", (void *)dtls_session);
278 }
279
280 return dtls_session;
281 }
282
coap_dtls_new_server_session(coap_session_t * session)283 void *coap_dtls_new_server_session(coap_session_t *session) {
284 return coap_dtls_new_session(session);
285 }
286
coap_dtls_new_client_session(coap_session_t * session)287 void *coap_dtls_new_client_session(coap_session_t *session) {
288 dtls_peer_t *peer;
289 session_t *dtls_session = coap_dtls_new_session(session);
290 if (!dtls_session)
291 return NULL;
292 peer =
293 dtls_get_peer((struct dtls_context_t *)session->context->dtls_context,
294 dtls_session);
295
296 if (!peer) {
297 /* The peer connection does not yet exist. */
298 /* dtls_connect() returns a value greater than zero if a new
299 * connection attempt is made, 0 for session reuse. */
300 if (dtls_connect((struct dtls_context_t *)session->context->dtls_context,
301 dtls_session) >= 0) {
302 peer =
303 dtls_get_peer((struct dtls_context_t *)session->context->dtls_context,
304 dtls_session);
305 }
306 }
307
308 if (!peer) {
309 /* delete existing session because the peer object has been invalidated */
310 coap_free_type(COAP_DTLS_SESSION, dtls_session);
311 dtls_session = NULL;
312 }
313
314 return dtls_session;
315 }
316
317 void
coap_dtls_session_update_mtu(coap_session_t * session)318 coap_dtls_session_update_mtu(coap_session_t *session) {
319 (void)session;
320 }
321
322 void
coap_dtls_free_session(coap_session_t * coap_session)323 coap_dtls_free_session(coap_session_t *coap_session) {
324 struct dtls_context_t *ctx;
325 if (coap_session->context == NULL)
326 return;
327 ctx = (struct dtls_context_t *)coap_session->context->dtls_context;
328 if (coap_session->tls && ctx) {
329 dtls_peer_t *peer = dtls_get_peer(ctx, (session_t *)coap_session->tls);
330 if ( peer )
331 dtls_reset_peer(ctx, peer);
332 else
333 dtls_close(ctx, (session_t *)coap_session->tls);
334 coap_log(LOG_DEBUG, "***removed session %p\n", coap_session->tls);
335 coap_free_type(COAP_DTLS_SESSION, coap_session->tls);
336 coap_session->tls = NULL;
337 coap_handle_event(coap_session->context, COAP_EVENT_DTLS_CLOSED, coap_session);
338 }
339 }
340
341 int
coap_dtls_send(coap_session_t * session,const uint8_t * data,size_t data_len)342 coap_dtls_send(coap_session_t *session,
343 const uint8_t *data,
344 size_t data_len
345 ) {
346 int res;
347 uint8_t *data_rw;
348
349 coap_log(LOG_DEBUG, "call dtls_write\n");
350
351 coap_event_dtls = -1;
352 /* Need to do this to not get a compiler warning about const parameters */
353 memcpy (&data_rw, &data, sizeof(data_rw));
354 res = dtls_write((struct dtls_context_t *)session->context->dtls_context,
355 (session_t *)session->tls, data_rw, data_len);
356
357 if (res < 0)
358 coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n");
359
360 if (coap_event_dtls >= 0) {
361 /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
362 if (coap_event_dtls != COAP_EVENT_DTLS_CLOSED)
363 coap_handle_event(session->context, coap_event_dtls, session);
364 if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED)
365 coap_session_connected(session);
366 else if (coap_event_dtls == COAP_EVENT_DTLS_CLOSED || coap_event_dtls == COAP_EVENT_DTLS_ERROR)
367 coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
368 }
369
370 return res;
371 }
372
coap_dtls_is_context_timeout(void)373 int coap_dtls_is_context_timeout(void) {
374 return 1;
375 }
376
coap_dtls_get_context_timeout(void * dtls_context)377 coap_tick_t coap_dtls_get_context_timeout(void *dtls_context) {
378 clock_time_t next = 0;
379 dtls_check_retransmit((struct dtls_context_t *)dtls_context, &next);
380 if (next > 0)
381 return ((coap_tick_t)(next - dtls_tick_0)) * COAP_TICKS_PER_SECOND / DTLS_TICKS_PER_SECOND + coap_tick_0;
382 return 0;
383 }
384
coap_dtls_get_timeout(coap_session_t * session,coap_tick_t now)385 coap_tick_t coap_dtls_get_timeout(coap_session_t *session, coap_tick_t now) {
386 (void)session;
387 (void)now;
388 return 0;
389 }
390
coap_dtls_handle_timeout(coap_session_t * session)391 void coap_dtls_handle_timeout(coap_session_t *session) {
392 (void)session;
393 return;
394 }
395
396 int
coap_dtls_receive(coap_session_t * session,const uint8_t * data,size_t data_len)397 coap_dtls_receive(coap_session_t *session,
398 const uint8_t *data,
399 size_t data_len
400 ) {
401 session_t *dtls_session = (session_t *)session->tls;
402 int err;
403 uint8_t *data_rw;
404
405 coap_event_dtls = -1;
406 /* Need to do this to not get a compiler warning about const parameters */
407 memcpy (&data_rw, &data, sizeof(data_rw));
408 err = dtls_handle_message(
409 (struct dtls_context_t *)session->context->dtls_context,
410 dtls_session, data_rw, (int)data_len);
411
412 if (err){
413 coap_event_dtls = COAP_EVENT_DTLS_ERROR;
414 }
415
416 if (coap_event_dtls >= 0) {
417 /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
418 if (coap_event_dtls != COAP_EVENT_DTLS_CLOSED)
419 coap_handle_event(session->context, coap_event_dtls, session);
420 if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED)
421 coap_session_connected(session);
422 else if (coap_event_dtls == COAP_EVENT_DTLS_CLOSED || coap_event_dtls == COAP_EVENT_DTLS_ERROR)
423 coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
424 }
425
426 return err;
427 }
428
429 int
coap_dtls_hello(coap_session_t * session,const uint8_t * data,size_t data_len)430 coap_dtls_hello(coap_session_t *session,
431 const uint8_t *data,
432 size_t data_len
433 ) {
434 session_t dtls_session;
435 struct dtls_context_t *dtls_context =
436 (struct dtls_context_t *)session->context->dtls_context;
437 uint8_t *data_rw;
438
439 dtls_session_init(&dtls_session);
440 put_session_addr(&session->addr_info.remote, &dtls_session);
441 dtls_session.ifindex = session->ifindex;
442 /* Need to do this to not get a compiler warning about const parameters */
443 memcpy (&data_rw, &data, sizeof(data_rw));
444 int res = dtls_handle_message(dtls_context, &dtls_session,
445 data_rw, (int)data_len);
446 if (res >= 0) {
447 if (dtls_get_peer(dtls_context, &dtls_session))
448 res = 1;
449 else
450 res = 0;
451 }
452 return res;
453 }
454
coap_dtls_get_overhead(coap_session_t * session)455 unsigned int coap_dtls_get_overhead(coap_session_t *session) {
456 (void)session;
457 return 13 + 8 + 8;
458 }
459
460 #ifdef __GNUC__
461 #define UNUSED __attribute__((unused))
462 #else /* __GNUC__ */
463 #define UNUSED
464 #endif /* __GNUC__ */
465
coap_tls_is_supported(void)466 int coap_tls_is_supported(void) {
467 return 0;
468 }
469
470 coap_tls_version_t *
coap_get_tls_library_version(void)471 coap_get_tls_library_version(void) {
472 static coap_tls_version_t version;
473 const char *vers = dtls_package_version();
474
475 version.version = 0;
476 if (vers) {
477 long int p1, p2 = 0, p3 = 0;
478 char* endptr;
479
480 p1 = strtol(vers, &endptr, 10);
481 if (*endptr == '.') {
482 p2 = strtol(endptr+1, &endptr, 10);
483 if (*endptr == '.') {
484 p3 = strtol(endptr+1, &endptr, 10);
485 }
486 }
487 version.version = (p1 << 16) | (p2 << 8) | p3;
488 }
489 version.built_version = version.version;
490 version.type = COAP_TLS_LIBRARY_TINYDTLS;
491 return &version;
492 }
493
494 int
coap_dtls_context_set_pki(coap_context_t * ctx UNUSED,coap_dtls_pki_t * setup_data UNUSED,coap_dtls_role_t role UNUSED)495 coap_dtls_context_set_pki(coap_context_t *ctx UNUSED,
496 coap_dtls_pki_t* setup_data UNUSED,
497 coap_dtls_role_t role UNUSED
498 ) {
499 return 0;
500 }
501
502 int
coap_dtls_context_set_pki_root_cas(struct coap_context_t * ctx UNUSED,const char * ca_file UNUSED,const char * ca_path UNUSED)503 coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx UNUSED,
504 const char *ca_file UNUSED,
505 const char *ca_path UNUSED
506 ) {
507 return 0;
508 }
509
510 int
coap_dtls_context_set_psk(coap_context_t * ctx UNUSED,const char * hint UNUSED,coap_dtls_role_t role UNUSED)511 coap_dtls_context_set_psk(coap_context_t *ctx UNUSED,
512 const char *hint UNUSED,
513 coap_dtls_role_t role UNUSED
514 ) {
515 return 1;
516 }
517
518 int
coap_dtls_context_check_keys_enabled(coap_context_t * ctx UNUSED)519 coap_dtls_context_check_keys_enabled(coap_context_t *ctx UNUSED)
520 {
521 return 1;
522 }
523
coap_tls_new_client_session(coap_session_t * session UNUSED,int * connected UNUSED)524 void *coap_tls_new_client_session(coap_session_t *session UNUSED, int *connected UNUSED) {
525 return NULL;
526 }
527
coap_tls_new_server_session(coap_session_t * session UNUSED,int * connected UNUSED)528 void *coap_tls_new_server_session(coap_session_t *session UNUSED, int *connected UNUSED) {
529 return NULL;
530 }
531
coap_tls_free_session(coap_session_t * coap_session UNUSED)532 void coap_tls_free_session(coap_session_t *coap_session UNUSED) {
533 }
534
coap_tls_write(coap_session_t * session UNUSED,const uint8_t * data UNUSED,size_t data_len UNUSED)535 ssize_t coap_tls_write(coap_session_t *session UNUSED,
536 const uint8_t *data UNUSED,
537 size_t data_len UNUSED
538 ) {
539 return -1;
540 }
541
coap_tls_read(coap_session_t * session UNUSED,uint8_t * data UNUSED,size_t data_len UNUSED)542 ssize_t coap_tls_read(coap_session_t *session UNUSED,
543 uint8_t *data UNUSED,
544 size_t data_len UNUSED
545 ) {
546 return -1;
547 }
548
549 #undef UNUSED
550
551 #else /* !HAVE_LIBTINYDTLS */
552
553 #ifdef __clang__
554 /* Make compilers happy that do not like empty modules. As this function is
555 * never used, we ignore -Wunused-function at the end of compiling this file
556 */
557 #pragma GCC diagnostic ignored "-Wunused-function"
558 #endif
dummy(void)559 static inline void dummy(void) {
560 }
561
562 #endif /* HAVE_LIBTINYDTLS */
563