Implemented libcoap's tinyDTLS interface
[contrib/iotivity.git] / resource / csdk / libcoap-4.1.1 / sec / netdtls.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 #include "netdtls.h"
22 #include "dtls.h"
23 #include "alert.h"
24 #include "debug.h"
25 #include "logger.h"
26 #include "mem.h"
27
28 #define MOD_NAME ("netdtls.c")
29
30 #define get_dtls_ctx(coap_ctx) (coap_ctx->coap_dtls_ctx->dtls_ctx)
31
32
33 /**
34  * An internal method to invoke tinyDTLS library 'dtls_write' method.
35  * Return value from this method will indicate if data was successfully sent
36  * to peer OR a new DTLS handshake session was invoked OR some error happened
37  * while processing.
38  *
39  */
40 static dtls_ret coap_dtls_encrypt_internal(coap_context_t *ctx, OCDevAddr* dst,
41             uint8_t *pt, uint16_t ptLen) {
42     int ret;
43     if (ptLen == 0)
44         return DTLS_OK;
45
46     ret = dtls_write(get_dtls_ctx(ctx), (session_t*)dst, pt, ptLen);
47     if (ret == 0) {
48         // A new DTLS session was initiated by tinyDTLS library
49         return DTLS_SESSION_INITIATED;
50     }
51
52     if (ret == ptLen) {
53         // tinyDTLS library successfully encrypted the data and
54         // sent it to the peer.
55         return DTLS_OK;
56     }
57
58     return DTLS_FAIL;
59 }
60
61 /**
62  * An internal method to invoke tinyDTLS library 'dtls_handle_message' method
63  * to decrypt packet received on secure port.
64  * Return value from this method will indicate if a valid application pdu was
65  * decypted OR a DTLS handshake message was received OR some error happened
66  * while processing.
67  *
68  */
69 static dtls_ret coap_dtls_decrypt_internal(coap_context_t *ctx, OCDevAddr* src,
70             uint8_t* ct, int ctLen, uint8_t** pt, int* ptLen) {
71     dtls_ret ret = DTLS_FAIL;
72     pt_info_t ptinfo;
73
74     ptinfo.pt =NULL;
75     ptinfo.ptlen = 0;
76     ctx->coap_dtls_ctx->pt_info = &ptinfo;
77
78     if (dtls_handle_message(get_dtls_ctx(ctx), (session_t*)src, ct, ctLen) == 0) {
79         ret = DTLS_HS_MSG;
80         if (ptinfo.pt && ptinfo.ptlen) {
81             *pt = ptinfo.pt;
82             *ptLen = ptinfo.ptlen;
83             ret = DTLS_OK;
84         }
85     }
86     return ret;
87 }
88
89
90 /**
91  * If tinyDTLS library starts a new DTLS handshake session with a peer, the pdu
92  * which was requested by application to encrypt will need to be cached until
93  * DTLS session is established. This method caches the pdu in cachedqueue.
94  *
95  */
96 static int coap_cache_pdu(coap_context_t *ctx,
97         coap_queue_t* existing_node,
98         OCDevAddr *dst,
99         coap_pdu_t *pdu,
100         coap_tid_t tid)
101 {
102     coap_queue_t *node;
103     coap_tick_t  now;
104
105     if (!ctx)
106         return -1;
107     /* Create a new node for caching the PDU in cachedqueue until
108      * DTLS session is established with peer.
109      */
110     node = coap_new_node();
111     if (!node) {
112         OC_LOG(DEBUG, MOD_NAME, PCF("Unable to allocate memory"));
113         return -1;
114     }
115
116     memcpy(&node->remote, dst, sizeof(coap_address_t));
117     node->pdu = pdu;
118     node->id = tid;
119     node->secure = 1;
120
121     coap_ticks(&now);
122     node->t = now + (COAP_DEFAULT_RESPONSE_TIMEOUT *2) * COAP_TICKS_PER_SECOND;
123
124     if (existing_node) {
125         node->timeout = existing_node->timeout;
126         node->delayedResponse = existing_node->delayedResponse;
127     }
128
129     // Add the node in cachedqueue list
130     // TODO : Do we need to add some limits on how many packets can be cached ?
131     if (ctx->coap_dtls_ctx->cachedqueue) {
132         coap_queue_t *p = ctx->coap_dtls_ctx->cachedqueue;
133         while(p->next != NULL) {
134             p = p->next;
135         }
136         p->next = node;
137     } else {
138         ctx->coap_dtls_ctx->cachedqueue = node;
139     }
140
141     return 0;
142 }
143
144 /**
145  * Once a DTLS session is established and cached pdu is send, this pdu needs to
146  * be saved in 'sendqueue' if this is a CON pdu for re-transmission purposes.
147  *
148  */
149 static void save_cached_con_pdu(coap_context_t *ctx,
150         coap_queue_t *node)
151 {
152     coap_tick_t now;
153
154     coap_ticks(&now);
155     if (ctx->sendqueue == NULL)
156     {
157         node->t = node->timeout;
158         ctx->sendqueue_basetime = now;
159     }
160     else
161     {
162         /* make node->t relative to context->sendqueue_basetime */
163         node->t = (now - ctx->sendqueue_basetime) + node->timeout;
164     }
165
166     node->delayedResponse = 0;
167     node->next = NULL;
168     coap_insert_node(&ctx->sendqueue, node);
169 }
170
171 /**
172  * Once a DTLS session is established, this method is invoked to retrieve any
173  * pdu's available in cachedqueue to be sent to the peer.
174  *
175  */
176 static coap_queue_t* get_cached_pdu( coap_context_t *ctx,
177         const coap_address_t *dst)
178 {
179     coap_queue_t *node, *prev;
180
181     node = ctx->coap_dtls_ctx->cachedqueue;
182     prev = NULL;
183     while(node) {
184         if (coap_address_equals(dst, &node->remote)) {
185             //disconnect the node from cachedqueue
186             if (node == ctx->coap_dtls_ctx->cachedqueue)
187                 ctx->coap_dtls_ctx->cachedqueue = node->next;
188             else if (node->next == NULL)
189                 prev->next = NULL;
190             else
191                 prev->next = node->next;
192
193             node->next = NULL;
194             return node;
195         }
196         prev = node;
197         node = node->next;
198     }
199     return NULL;
200 }
201
202 /**
203  * Once a DTLS session is established, this method takes care of sending
204  * pdu's available in cachedqueue to the peer.
205  *
206  */
207 static void coap_send_cached_pdu( coap_context_t *ctx,
208         const coap_address_t *dst )
209 {
210     coap_queue_t *node;
211
212     if (!ctx)
213         return ;
214
215     for (;node=get_cached_pdu(ctx, dst);) {
216         OC_LOG(DEBUG, MOD_NAME, PCF("Sending cached PDU"));
217         OC_LOG_BUFFER(DEBUG, MOD_NAME, (uint8_t*)node->pdu->hdr, node->pdu->length);
218         // Send this PDU to DTLS library for encryption
219         dtls_ret ret = coap_dtls_encrypt_internal(ctx, (OCDevAddr*)dst,
220                 (uint8_t*)node->pdu->hdr, node->pdu->length);
221         if (ret == DTLS_OK) {
222             OC_LOG(DEBUG, MOD_NAME, PCF("coap_send_cached_pdu: successully send cached pdu"));
223         } else {
224             OC_LOG(DEBUG, MOD_NAME, PCF("coap_send_cached_pdu: sending cached pdu failed."));
225             //TODO Notify application that packet send failed.
226         }
227
228         /* Add cache node in sendqueue if it is CON pdu,
229          * as it may be needed for retransmission
230          * else, delete it
231          */
232         if (node->pdu->hdr->type == COAP_MESSAGE_CON) {
233             save_cached_con_pdu(ctx, node);
234         } else {
235             coap_delete_node(node);
236         }
237     }
238 }
239
240
241 /**
242  * This is the tinyDTLS 'read' callback.
243  * It is invoked by tinyDTLS to provide the decrypted pdu.
244  *
245  */
246 static int read_decrypted_payload(dtls_context_t *dtls_ctx,
247             session_t *session,
248             uint8_t *buf,
249             size_t len )
250 {
251     if (!dtls_ctx)
252         return -1;
253
254     coap_dtls_context_t* coap_dtls_ctx =
255         ((coap_context_t*)dtls_get_app_data(dtls_ctx))->coap_dtls_ctx;
256
257     if (coap_dtls_ctx && coap_dtls_ctx->pt_info) {
258         coap_dtls_ctx->pt_info->pt = buf;
259         coap_dtls_ctx->pt_info->ptlen = len;
260         return len;
261     }
262
263     return -1;
264 }
265
266 /**
267  * This is the tinyDTLS 'write' callback.
268  * It is invoked by tinyDTLS to send encrypted data or handshake message to peer.
269  *
270  */
271 static int send_secure_data(dtls_context_t *dtls_ctx,
272         session_t *session,
273         uint8_t* buf,
274         size_t buflen)
275 {
276     if (!dtls_ctx)
277         return -1;
278
279     return OCSendTo( ((coap_context_t*)dtls_get_app_data(dtls_ctx))->sockfd_dtls,
280             buf, buflen, 0, (OCDevAddr*)session);
281 }
282
283
284 /**
285  * This is the tinyDTLS 'event' callback.
286  * It is invoked by tinyDTLS to notify any DTLS events or alerts.
287  *
288  */
289 static int handle_secure_event(dtls_context_t *dtls_ctx,
290         session_t *session,
291         dtls_alert_level_t level,
292         unsigned short code)
293 {
294     if (!dtls_ctx)
295         return -1;
296
297     OC_LOG_V(DEBUG, MOD_NAME, "level %d, code %u", level, code);
298
299     //Notify stack of any errors/connection state changes to upper layer
300     //application
301     if (!level && (code == DTLS_EVENT_CONNECTED))
302     {
303         coap_send_cached_pdu( (coap_context_t*)dtls_get_app_data(dtls_ctx),
304             (coap_address_t*)session);
305     }
306     return 0;
307 }
308
309 /**
310  * This is the tinyDTLS 'get_psk_info' callback.
311  * It is invoked by tinyDTLS to retrieve identity/credentials.
312  * This is currently a test version using stationary keys.
313  *
314  */
315 static int get_psk_credentials(dtls_context_t *ctx,
316               const session_t *session,
317               dtls_credentials_type_t type,
318               const unsigned char *desc, size_t descLen,
319               unsigned char *result, size_t resultLen)
320 {
321
322 #define RS_IDENTITY     ("1111111111111111")
323 #define CLIENT_IDENTITY ("2222222222222222")
324 #define RS_CLIENT_PSK   ("AAAAAAAAAAAAAAAA")
325
326     if (type == DTLS_PSK_HINT)
327     {
328         if (sizeof(RS_IDENTITY) < resultLen)
329         {
330             memcpy(result, RS_IDENTITY, sizeof(RS_IDENTITY));
331             return sizeof(RS_IDENTITY);
332         }
333     }
334     else if (type == DTLS_PSK_IDENTITY)
335     {
336         if (sizeof(CLIENT_IDENTITY) < resultLen)
337         {
338             memcpy(result, CLIENT_IDENTITY, sizeof(CLIENT_IDENTITY));
339             return sizeof(CLIENT_IDENTITY);
340         }
341     }
342     else if (type == DTLS_PSK_KEY && (desc))
343     {
344         if (descLen == sizeof(RS_IDENTITY) &&
345             !memcmp(desc, RS_IDENTITY, descLen))
346         {
347             memcpy(result, RS_CLIENT_PSK, sizeof(RS_CLIENT_PSK));
348             return sizeof(RS_CLIENT_PSK);
349         }
350         if (descLen == sizeof(CLIENT_IDENTITY) &&
351             !memcmp(desc, CLIENT_IDENTITY, descLen))
352         {
353             memcpy(result, RS_CLIENT_PSK, sizeof(RS_CLIENT_PSK));
354             return sizeof(RS_CLIENT_PSK);
355         }
356     }
357
358     return -1;
359 }
360
361
362 /**
363  * Open secure port and initialize tinyDTLS library.
364  *
365  * @param ctx - handle to global coap_context_t.
366  *
367  * @return A value less than zero on error, greater or
368  *           equal otherwise.
369  */
370 int coap_dtls_init(coap_context_t *ctx) {
371
372     int ret = -1;
373     coap_dtls_context_t *coap_dtls_ctx = NULL;
374     OCDevAddr dev_addr;
375
376     if (!ctx)
377         goto exit;
378
379     coap_dtls_ctx =
380         (coap_dtls_context_t*)coap_malloc(sizeof(coap_dtls_context_t));
381
382     if (!coap_dtls_ctx)
383         goto exit;
384     memset(coap_dtls_ctx, 0, sizeof(coap_dtls_ctx));
385     ctx->sockfd_dtls = -1;
386
387     //TODO : Initialize secure socket descriptor
388     OCBuildIPv4Address(0, 0, 0, 0, COAP_DTLS_DEFAULT_PORT, &dev_addr);
389     if (OCInitUDP((OCDevAddr *)&dev_addr, (int32_t *)&(ctx->sockfd_dtls)) != ERR_SUCCESS) {
390         OCBuildIPv4Address(0, 0, 0, 0, 5685, &dev_addr);
391         if (OCInitUDP((OCDevAddr *)&dev_addr, (int32_t *)&(ctx->sockfd_dtls)) != ERR_SUCCESS) {
392             goto exit;
393         }
394     }
395
396     // Initialize clock, crypto and other global vars in tinyDTLS library
397     dtls_init();
398
399     coap_dtls_ctx->dtls_ctx = dtls_new_context(ctx);
400     if (!coap_dtls_ctx->dtls_ctx)
401         goto exit;
402
403     coap_dtls_ctx->callbacks.write = send_secure_data;
404     coap_dtls_ctx->callbacks.read  = read_decrypted_payload;
405     coap_dtls_ctx->callbacks.event = handle_secure_event;
406     coap_dtls_ctx->callbacks.get_psk_info = get_psk_credentials;
407
408     dtls_set_handler(coap_dtls_ctx->dtls_ctx, &(coap_dtls_ctx->callbacks));
409     ctx->coap_dtls_ctx = coap_dtls_ctx;
410     ret = 0;
411
412 exit:
413     if (ret == -1 && coap_dtls_ctx) {
414         coap_dtls_deinit(ctx);
415     }
416     return ret;
417 }
418
419
420 /**
421  * Closes secure port and de-inits tinyDTLS library.
422  *
423  * @param ctx - handle to global coap_context_t.
424  *
425  */
426 void coap_dtls_deinit(coap_context_t *ctx) {
427     if (!ctx)
428         return;
429
430     coap_dtls_context_t *coap_dtls_ctx = ctx->coap_dtls_ctx;
431
432     if (coap_dtls_ctx) {
433         coap_delete_all(coap_dtls_ctx->cachedqueue);
434         if (ctx->sockfd_dtls != -1)
435             OCClose(ctx->sockfd_dtls);
436         dtls_free_context(coap_dtls_ctx->dtls_ctx);
437         coap_free(coap_dtls_ctx);
438     }
439     ctx->coap_dtls_ctx = NULL;
440 }
441
442
443 /**
444  * Performs DTLS encryption of the CoAP PDU. If a
445  * DTLS session does not exist yet with the @dst,
446  * a DTLS handshake will be started. In case where
447  * a new DTLS handshake is started, pdu info is
448  * cached to be send when session setup is finished.
449  *
450  * @param ctx    - handle to global coap_context_t.
451  * @param dst    - address of the receiver of the pdu.
452  * @param pdu    - pointer to CoAP pdu.
453  * @param node   - address of the node holding pdu.
454  * @param tid    - tid of the pdu.
455  * @param cached - output variable to indicate if pdu
456  *                  is cached and inform the caller to
457  *                  NOT free the memory holding pdu.
458  *
459  * @return A value less than zero on error, greater or
460  *           equal otherwise.
461  */
462 int coap_dtls_encrypt(coap_context_t *ctx,
463             OCDevAddr *dst,
464             coap_pdu_t *pdu,
465             coap_queue_t **node,
466             coap_tid_t tid,
467             uint8_t *cache_flag) {
468     OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt"));
469
470     if (!dst || !pdu)
471         return -1;
472
473     dtls_ret ret = coap_dtls_encrypt_internal( ctx, dst,
474             (uint8_t*)pdu->hdr, pdu->length);
475
476     if (ret == DTLS_SESSION_INITIATED) {
477         OC_LOG(DEBUG, MOD_NAME, PCF("Initiated new DTLS session"));
478         if (cache_flag && coap_cache_pdu(ctx, *node, dst, pdu, tid) == 0) {
479             /* Delete the node from sendqueue list as it has been
480              * added in cachedqueue list. It will be added
481              * again in sendqueue list when DTLS session is established
482              */
483             if (*node) {
484                 coap_queue_t* removed_node = NULL;
485                 coap_remove_from_queue(&(ctx->sendqueue),
486                    (*node)->id, &removed_node);
487                 if (removed_node == *node) {
488                     coap_free(*node);
489                     *node = NULL;
490                     OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt -- Removed correct node"));
491                 }
492             }
493             *cache_flag = 1;
494         }
495         return pdu->length;
496     }
497
498     if (ret == DTLS_OK) {
499         OC_LOG(DEBUG, MOD_NAME, PCF("Encrypted App PDU and send to peer"));
500         return pdu->length;
501     }
502     return -1;
503 }
504
505
506 /**
507  * Performs DTLS decryption of the CoAP PDU received on
508  * secure port. This method performs in-place decryption
509  * of the cipher-text buffer. If a DTLS handshake message
510  * is received or decryption failure happens, this method
511  * returns -1. If a valid CoAP pdu is received, it returns the
512  * length of the decrypted pdu.
513  *
514  * @param ctx    - handle to global coap_context_t.
515  * @param src    - address of the sender of the pdu.
516  * @param ct     - pointer to the cipher text buffer.
517  * @param ctlen  - length of the ciphertext buffer.
518  * @param pt     - output variable to store the starting address
519  *                  of decrypted plaintext.
520  * @param ptlen  - output variable to store the length of
521  *                  decrypted plaintext.
522  *
523  * @return A value less than zero on error, greater or
524  *           equal otherwise.
525  */
526 int coap_dtls_decrypt(coap_context_t *ctx,
527             OCDevAddr* src,
528             uint8_t* ct,
529             int ctlen,
530             uint8_t** pt,
531             int* ptlen) {
532     OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_decrypt"));
533
534     if (!src || !ct || !pt || !ptlen)
535         return -1;
536
537     dtls_ret ret = coap_dtls_decrypt_internal(ctx, src, ct, ctlen,
538             pt, ptlen);
539
540     if (ret == DTLS_OK)
541         return *ptlen;
542
543     if (ret == DTLS_HS_MSG)
544         OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_decrypt : Handshake msg recvd "));
545     if (ret == DTLS_FAIL)
546         OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_decrypt : Decryption failure "));
547
548     return -1;
549 }