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 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
28 #define MOD_NAME ("netdtls.c")
30 #define get_dtls_ctx(coap_ctx) (coap_ctx->coap_dtls_ctx->dtls_ctx)
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
40 static dtls_ret coap_dtls_encrypt_internal(coap_context_t *ctx, OCDevAddr* dst,
41 uint8_t *pt, uint16_t ptLen) {
46 ret = dtls_write(get_dtls_ctx(ctx), (session_t*)dst, pt, ptLen);
48 // A new DTLS session was initiated by tinyDTLS library
49 return DTLS_SESSION_INITIATED;
53 // tinyDTLS library successfully encrypted the data and
54 // sent it to the peer.
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
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;
76 ctx->coap_dtls_ctx->pt_info = &ptinfo;
78 if (dtls_handle_message(get_dtls_ctx(ctx), (session_t*)src, ct, ctLen) == 0) {
80 if (ptinfo.pt && ptinfo.ptlen) {
82 *ptLen = ptinfo.ptlen;
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.
96 static int coap_cache_pdu(coap_context_t *ctx,
97 coap_queue_t* existing_node,
107 /* Create a new node for caching the PDU in cachedqueue until
108 * DTLS session is established with peer.
110 node = coap_new_node();
112 OC_LOG(DEBUG, MOD_NAME, PCF("Unable to allocate memory"));
116 memcpy(&node->remote, dst, sizeof(coap_address_t));
122 node->t = now + (COAP_DEFAULT_RESPONSE_TIMEOUT *2) * COAP_TICKS_PER_SECOND;
125 node->timeout = existing_node->timeout;
126 node->delayedResponse = existing_node->delayedResponse;
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) {
138 ctx->coap_dtls_ctx->cachedqueue = node;
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.
149 static void save_cached_con_pdu(coap_context_t *ctx,
155 if (ctx->sendqueue == NULL)
157 node->t = node->timeout;
158 ctx->sendqueue_basetime = now;
162 /* make node->t relative to context->sendqueue_basetime */
163 node->t = (now - ctx->sendqueue_basetime) + node->timeout;
166 node->delayedResponse = 0;
168 coap_insert_node(&ctx->sendqueue, node);
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.
176 static coap_queue_t* get_cached_pdu( coap_context_t *ctx,
177 const coap_address_t *dst)
179 coap_queue_t *node, *prev;
181 node = ctx->coap_dtls_ctx->cachedqueue;
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)
191 prev->next = node->next;
203 * Once a DTLS session is established, this method takes care of sending
204 * pdu's available in cachedqueue to the peer.
207 static void coap_send_cached_pdu( coap_context_t *ctx,
208 const coap_address_t *dst )
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"));
224 OC_LOG(DEBUG, MOD_NAME, PCF("coap_send_cached_pdu: sending cached pdu failed."));
225 //TODO Notify application that packet send failed.
228 /* Add cache node in sendqueue if it is CON pdu,
229 * as it may be needed for retransmission
232 if (node->pdu->hdr->type == COAP_MESSAGE_CON) {
233 save_cached_con_pdu(ctx, node);
235 coap_delete_node(node);
242 * This is the tinyDTLS 'read' callback.
243 * It is invoked by tinyDTLS to provide the decrypted pdu.
246 static int read_decrypted_payload(dtls_context_t *dtls_ctx,
254 coap_dtls_context_t* coap_dtls_ctx =
255 ((coap_context_t*)dtls_get_app_data(dtls_ctx))->coap_dtls_ctx;
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;
267 * This is the tinyDTLS 'write' callback.
268 * It is invoked by tinyDTLS to send encrypted data or handshake message to peer.
271 static int send_secure_data(dtls_context_t *dtls_ctx,
279 return OCSendTo( ((coap_context_t*)dtls_get_app_data(dtls_ctx))->sockfd_dtls,
280 buf, buflen, 0, (OCDevAddr*)session);
285 * This is the tinyDTLS 'event' callback.
286 * It is invoked by tinyDTLS to notify any DTLS events or alerts.
289 static int handle_secure_event(dtls_context_t *dtls_ctx,
291 dtls_alert_level_t level,
297 OC_LOG_V(DEBUG, MOD_NAME, "level %d, code %u", level, code);
299 //Notify stack of any errors/connection state changes to upper layer
301 if (!level && (code == DTLS_EVENT_CONNECTED))
303 coap_send_cached_pdu( (coap_context_t*)dtls_get_app_data(dtls_ctx),
304 (coap_address_t*)session);
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.
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)
322 #define RS_IDENTITY ("1111111111111111")
323 #define CLIENT_IDENTITY ("2222222222222222")
324 #define RS_CLIENT_PSK ("AAAAAAAAAAAAAAAA")
326 if (type == DTLS_PSK_HINT)
328 if (sizeof(RS_IDENTITY) < resultLen)
330 memcpy(result, RS_IDENTITY, sizeof(RS_IDENTITY));
331 return sizeof(RS_IDENTITY);
334 else if (type == DTLS_PSK_IDENTITY)
336 if (sizeof(CLIENT_IDENTITY) < resultLen)
338 memcpy(result, CLIENT_IDENTITY, sizeof(CLIENT_IDENTITY));
339 return sizeof(CLIENT_IDENTITY);
342 else if (type == DTLS_PSK_KEY && (desc))
344 if (descLen == sizeof(RS_IDENTITY) &&
345 !memcmp(desc, RS_IDENTITY, descLen))
347 memcpy(result, RS_CLIENT_PSK, sizeof(RS_CLIENT_PSK));
348 return sizeof(RS_CLIENT_PSK);
350 if (descLen == sizeof(CLIENT_IDENTITY) &&
351 !memcmp(desc, CLIENT_IDENTITY, descLen))
353 memcpy(result, RS_CLIENT_PSK, sizeof(RS_CLIENT_PSK));
354 return sizeof(RS_CLIENT_PSK);
363 * Open secure port and initialize tinyDTLS library.
365 * @param ctx - handle to global coap_context_t.
367 * @return A value less than zero on error, greater or
370 int coap_dtls_init(coap_context_t *ctx) {
373 coap_dtls_context_t *coap_dtls_ctx = NULL;
380 (coap_dtls_context_t*)coap_malloc(sizeof(coap_dtls_context_t));
384 memset(coap_dtls_ctx, 0, sizeof(coap_dtls_ctx));
385 ctx->sockfd_dtls = -1;
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) {
396 // Initialize clock, crypto and other global vars in tinyDTLS library
399 coap_dtls_ctx->dtls_ctx = dtls_new_context(ctx);
400 if (!coap_dtls_ctx->dtls_ctx)
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;
408 dtls_set_handler(coap_dtls_ctx->dtls_ctx, &(coap_dtls_ctx->callbacks));
409 ctx->coap_dtls_ctx = coap_dtls_ctx;
413 if (ret == -1 && coap_dtls_ctx) {
414 coap_dtls_deinit(ctx);
421 * Closes secure port and de-inits tinyDTLS library.
423 * @param ctx - handle to global coap_context_t.
426 void coap_dtls_deinit(coap_context_t *ctx) {
430 coap_dtls_context_t *coap_dtls_ctx = ctx->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);
439 ctx->coap_dtls_ctx = NULL;
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.
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.
459 * @return A value less than zero on error, greater or
462 int coap_dtls_encrypt(coap_context_t *ctx,
467 uint8_t *cache_flag) {
468 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt"));
473 dtls_ret ret = coap_dtls_encrypt_internal( ctx, dst,
474 (uint8_t*)pdu->hdr, pdu->length);
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
484 coap_queue_t* removed_node = NULL;
485 coap_remove_from_queue(&(ctx->sendqueue),
486 (*node)->id, &removed_node);
487 if (removed_node == *node) {
490 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_encrypt -- Removed correct node"));
498 if (ret == DTLS_OK) {
499 OC_LOG(DEBUG, MOD_NAME, PCF("Encrypted App PDU and send to peer"));
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.
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.
523 * @return A value less than zero on error, greater or
526 int coap_dtls_decrypt(coap_context_t *ctx,
532 OC_LOG(DEBUG, MOD_NAME, PCF("coap_dtls_decrypt"));
534 if (!src || !ct || !pt || !ptlen)
537 dtls_ret ret = coap_dtls_decrypt_internal(ctx, src, ct, ctlen,
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 "));