• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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