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