1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
27 #include "ocsecurityconfig.h"
29 #define MOD_NAME ("netdtls.c")
31 #define get_dtls_ctx(coap_ctx) (coap_ctx->coap_dtls_ctx->dtls_ctx)
33 extern void OCGetDtlsPskCredentials(OCDtlsPskCredsBlob **credInfo);
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
42 static dtls_ret coap_dtls_encrypt_internal(coap_context_t *ctx, OCDevAddr* dst,
43 uint8_t *pt, uint16_t ptLen) {
48 ret = dtls_write(get_dtls_ctx(ctx), (session_t*)dst, pt, ptLen);
50 // A new DTLS session was initiated by tinyDTLS library
51 return DTLS_SESSION_INITIATED;
55 // tinyDTLS library successfully encrypted the data and
56 // sent it to the peer.
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
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;
78 ctx->coap_dtls_ctx->pt_info = &ptinfo;
80 if (dtls_handle_message(get_dtls_ctx(ctx), (session_t*)src, ct, ctLen) == 0) {
82 if (ptinfo.pt && ptinfo.ptlen) {
84 *ptLen = ptinfo.ptlen;
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.
98 static int coap_cache_pdu(coap_context_t *ctx,
99 coap_queue_t* existing_node,
109 /* Create a new node for caching the PDU in cachedqueue until
110 * DTLS session is established with peer.
112 node = coap_new_node();
114 OC_LOG(DEBUG, MOD_NAME, PCF("Unable to allocate memory"));
118 memcpy(&node->remote, dst, sizeof(coap_address_t));
124 node->t = now + (COAP_DEFAULT_RESPONSE_TIMEOUT *2) * COAP_TICKS_PER_SECOND;
127 node->timeout = existing_node->timeout;
128 node->delayedResNeeded = existing_node->delayedResNeeded;
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) {
140 ctx->coap_dtls_ctx->cachedqueue = node;
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.
151 static void save_cached_con_pdu(coap_context_t *ctx,
157 if (ctx->sendqueue == NULL)
159 node->t = node->timeout;
160 ctx->sendqueue_basetime = now;
164 /* make node->t relative to context->sendqueue_basetime */
165 node->t = (now - ctx->sendqueue_basetime) + node->timeout;
168 node->delayedResNeeded = 0;
170 coap_insert_node(&ctx->sendqueue, node);
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.
178 static coap_queue_t* get_cached_pdu( coap_context_t *ctx,
179 const coap_address_t *dst)
181 coap_queue_t *node, *prev;
183 node = ctx->coap_dtls_ctx->cachedqueue;
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)
193 prev->next = node->next;
205 * Once a DTLS session is established, this method takes care of sending
206 * pdu's available in cachedqueue to the peer.
209 static void coap_send_cached_pdu( coap_context_t *ctx,
210 const coap_address_t *dst )
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"));
226 OC_LOG(DEBUG, MOD_NAME, PCF("coap_send_cached_pdu: sending cached pdu failed."));
227 //TODO Notify application that packet send failed.
230 /* Add cache node in sendqueue if it is CON pdu,
231 * as it may be needed for retransmission
234 if (node->pdu->hdr->type == COAP_MESSAGE_CON) {
235 save_cached_con_pdu(ctx, node);
237 coap_delete_node(node);
244 * This is the tinyDTLS 'read' callback.
245 * It is invoked by tinyDTLS to provide the decrypted pdu.
248 static int read_decrypted_payload(dtls_context_t *dtls_ctx,
256 coap_dtls_context_t* coap_dtls_ctx =
257 ((coap_context_t*)dtls_get_app_data(dtls_ctx))->coap_dtls_ctx;
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;
269 * This is the tinyDTLS 'write' callback.
270 * It is invoked by tinyDTLS to send encrypted data or handshake message to peer.
273 static int send_secure_data(dtls_context_t *dtls_ctx,
281 return OCSendTo( ((coap_context_t*)dtls_get_app_data(dtls_ctx))->sockfd_dtls,
282 buf, buflen, 0, (OCDevAddr*)session);
287 * This is the tinyDTLS 'event' callback.
288 * It is invoked by tinyDTLS to notify any DTLS events or alerts.
291 static int handle_secure_event(dtls_context_t *dtls_ctx,
293 dtls_alert_level_t level,
299 OC_LOG_V(DEBUG, MOD_NAME, "level %d, code %u", level, code);
301 //Notify stack of any errors/connection state changes to upper layer
303 if (!level && (code == DTLS_EVENT_CONNECTED))
305 coap_send_cached_pdu( (coap_context_t*)dtls_get_app_data(dtls_ctx),
306 (coap_address_t*)session);
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.
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)
324 OCDtlsPskCredsBlob *creds_blob = NULL;
326 //Retrieve the credentials blob from security module
327 OCGetDtlsPskCredentials(&creds_blob);
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;
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)
345 memcpy(result, creds_blob->creds[i].psk, DTLS_PSK_PSK_LEN);
346 ret = DTLS_PSK_PSK_LEN;
356 * Open secure port and initialize tinyDTLS library.
358 * @param ctx - handle to global coap_context_t.
360 * @param ipAddr - ip address.
362 * @return A value less than zero on error, greater or
365 int coap_dtls_init(coap_context_t *ctx, uint8_t ipAddr[]) {
368 coap_dtls_context_t *coap_dtls_ctx = NULL;
375 (coap_dtls_context_t*)coap_malloc(sizeof(coap_dtls_context_t));
379 memset(coap_dtls_ctx, 0, sizeof(coap_dtls_ctx));
380 ctx->sockfd_dtls = -1;
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) {
392 // Initialize clock, crypto and other global vars in tinyDTLS library
395 coap_dtls_ctx->dtls_ctx = dtls_new_context(ctx);
396 if (!coap_dtls_ctx->dtls_ctx)
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;
404 dtls_set_handler(coap_dtls_ctx->dtls_ctx, &(coap_dtls_ctx->callbacks));
405 ctx->coap_dtls_ctx = coap_dtls_ctx;
410 coap_dtls_deinit(ctx);
417 * Closes secure port and de-inits tinyDTLS library.
419 * @param ctx - handle to global coap_context_t.
422 void coap_dtls_deinit(coap_context_t *ctx) {
424 if (!ctx || !ctx->coap_dtls_ctx)
427 coap_dtls_context_t *coap_dtls_ctx = ctx->coap_dtls_ctx;
429 coap_delete_all(coap_dtls_ctx->cachedqueue);
431 dtls_free_context(coap_dtls_ctx->dtls_ctx);
432 coap_dtls_ctx->dtls_ctx = NULL;
434 if (ctx->sockfd_dtls != -1)
435 OCClose(ctx->sockfd_dtls);
437 coap_free(coap_dtls_ctx);
438 ctx->coap_dtls_ctx = NULL;
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.
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.
458 * @return A value less than zero on error, greater or
461 int coap_dtls_encrypt(coap_context_t *ctx,
466 uint8_t *cache_flag) {
467 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt"));
472 dtls_ret ret = coap_dtls_encrypt_internal( ctx, dst,
473 (uint8_t*)pdu->hdr, pdu->length);
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
483 coap_queue_t* removed_node = NULL;
484 coap_remove_from_queue(&(ctx->sendqueue),
485 (*node)->id, &removed_node);
486 if (removed_node == *node) {
489 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt -- Removed correct node"));
497 if (ret == DTLS_OK) {
498 OC_LOG(DEBUG, MOD_NAME, PCF("Encrypted App PDU and send to peer"));
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.
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.
522 * @return A value less than zero on error, greater or
525 int coap_dtls_decrypt(coap_context_t *ctx,
531 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_decrypt"));
533 if (!src || !ct || !pt || !ptlen)
536 dtls_ret ret = coap_dtls_decrypt_internal(ctx, src, ct, ctlen,
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 "));