coap_server_transport.c
Go to the documentation of this file.
1 /**
2  * @file coap_server_transport.c
3  * @brief Transport protocol abstraction layer
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL COAP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "coap/coap_server.h"
38 #include "coap/coap_server_misc.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (COAP_SERVER_SUPPORT == ENABLED && COAP_SERVER_DTLS_SUPPORT == ENABLED)
43 
44 //Forward declaration of functions
45 error_t coapServerSendCallback(void *handle, const void *data,
46  size_t length, size_t *written, uint_t flags);
47 
48 error_t coapServerReceiveCallback(void *handle, void *data,
49  size_t size, size_t *received, uint_t flags);
50 
52  const DtlsClientParameters *clientParams, uint8_t *cookie,
53  size_t *length, void *param);
54 
56  const DtlsClientParameters *clientParams, const uint8_t *cookie,
57  size_t length, void *param);
58 
59 
60 /**
61  * @brief Accept a new connection from a client
62  * @param[in] context Pointer to the CoAP server context
63  * @param[in] session Pointer to the DTLS session
64  * @param[in] remoteIpAddr Client IP address
65  * @param[in] remotePort Client port number
66  * @return Error code
67  **/
68 
70  CoapDtlsSession *session, const IpAddr *remoteIpAddr, uint16_t remotePort)
71 {
72  error_t error;
73  TlsState state;
74 
75  //Clear DTLS session
76  osMemset(session, 0, sizeof(CoapDtlsSession));
77 
78  //Initialize session parameters
79  session->context = context;
80  session->serverIpAddr = context->serverIpAddr;
81  session->clientIpAddr = context->clientIpAddr;
82  session->clientPort = context->clientPort;
83  session->timestamp = osGetSystemTime();
84 
85  //Allocate DTLS context
86  session->dtlsContext = tlsInit();
87 
88  //DTLS context successfully created?
89  if(session->dtlsContext != NULL)
90  {
91  //Start of exception handling block
92  do
93  {
94  //Select server operation mode
95  error = tlsSetConnectionEnd(session->dtlsContext,
97  //Any error to report?
98  if(error)
99  break;
100 
101  //Use datagram transport protocol
102  error = tlsSetTransportProtocol(session->dtlsContext,
104  //Any error to report?
105  if(error)
106  break;
107 
108  //Set send and receive callbacks (I/O abstraction layer)
109  error = tlsSetSocketCallbacks(session->dtlsContext, coapServerSendCallback,
111  //Any error to report?
112  if(error)
113  break;
114 
115  //Set cookie generation/verification callbacks
116  error = tlsSetCookieCallbacks(session->dtlsContext,
118  session);
119  //Any error to report?
120  if(error)
121  break;
122 
123  //Invoke user-defined callback, if any
124  if(context->settings.dtlsInitCallback != NULL)
125  {
126  //Perform DTLS related initialization
127  error = context->settings.dtlsInitCallback(context,
128  session->dtlsContext);
129  //Any error to report?
130  if(error)
131  break;
132  }
133 
134  //Initiate DTLS handshake
135  error = tlsConnect(session->dtlsContext);
136  //Any error to report?
137  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
138  break;
139 
140  //Retrieve current state
141  state = tlsGetState(session->dtlsContext);
142 
143  //The DTLS server verifies the cookie and proceeds with the handshake
144  //only if it is valid
145  if(state == TLS_STATE_INIT ||
146  state == TLS_STATE_CLIENT_HELLO ||
147  state == TLS_STATE_CLOSED)
148  {
149  //Do not allocate connection state yet if the stateless cookie
150  //exchange is being performed
151  error = ERROR_WRONG_COOKIE;
152  break;
153  }
154 
155  //The DTLS implementation decides to continue with the connection
156  error = NO_ERROR;
157 
158  //Debug message
159  TRACE_INFO("CoAP Server: DTLS session established with client %s port %"
160  PRIu16 "...\r\n", ipAddrToString(remoteIpAddr, NULL), ntohs(remotePort));
161 
162  //End of exception handling block
163  } while(0);
164 
165  //Check status code
166  if(error)
167  {
168  //Release DTLS context
169  tlsFree(session->dtlsContext);
170  session->dtlsContext = NULL;
171  }
172  }
173  else
174  {
175  //Failed to allocate DTLS context
176  error = ERROR_OUT_OF_MEMORY;
177  }
178 
179  //Return status code
180  return error;
181 }
182 
183 
184 /**
185  * @brief DTLS session demultiplexing
186  * @param[in] context Pointer to the CoAP server context
187  * @return Error code
188  **/
189 
191 {
192  error_t error;
193  uint_t i;
194  size_t length;
195  systime_t time;
196  CoapDtlsSession *session;
197  CoapDtlsSession *firstFreeSession;
198  CoapDtlsSession *oldestSession;
199 
200  //Initialize status code
201  error = NO_ERROR;
202 
203  //Get current time
204  time = osGetSystemTime();
205 
206  //Keep track of the first free entry
207  firstFreeSession = NULL;
208  //Keep track of the oldest entry
209  oldestSession = NULL;
210 
211  //Demultiplexing of incoming datagrams into separate DTLS sessions
212  for(i = 0; i < COAP_SERVER_MAX_SESSIONS; i++)
213  {
214  //Point to the current DTLS session
215  session = &context->session[i];
216 
217  //Valid DTLS session?
218  if(session->dtlsContext != NULL)
219  {
220  //Determine if a DTLS session matches the incoming datagram
221  if(ipCompAddr(&session->serverIpAddr, &context->serverIpAddr) &&
222  ipCompAddr(&session->clientIpAddr, &context->clientIpAddr) &&
223  session->clientPort == context->clientPort)
224  {
225  //Save current time
226  session->timestamp = osGetSystemTime();
227 
228  //The UDP datagram is passed to the DTLS implementation
229  error = tlsRead(session->dtlsContext, context->buffer,
231 
232  //Check status code
233  if(!error)
234  {
235  //Process the received CoAP message
236  error = coapServerProcessRequest(context, context->buffer,
237  length);
238  }
239  else if(error == ERROR_TIMEOUT || error == ERROR_WOULD_BLOCK)
240  {
241  //The UDP datagram contains DTLS handshake messages
242  }
243  else
244  {
245  //Debug message
246  TRACE_INFO("CoAP Server: Failed to read DTLS datagram!\r\n");
247 
248  //Release DTLS session
249  coapServerDeleteSession(session);
250  }
251 
252  //We are done
253  break;
254  }
255  else
256  {
257  //Keep track of the oldest entry
258  if(oldestSession == NULL)
259  {
260  oldestSession = session;
261  }
262  else if((time - session->timestamp) > (time - oldestSession->timestamp))
263  {
264  oldestSession = session;
265  }
266  }
267  }
268  else
269  {
270  //Keep track of the first free entry
271  if(firstFreeSession == NULL)
272  {
273  firstFreeSession = session;
274  }
275  }
276  }
277 
278  //No matching DTLS session?
279  if(i >= COAP_SERVER_MAX_SESSIONS)
280  {
281  //Any DTLS session available for use in the table?
282  if(firstFreeSession != NULL)
283  {
284  session = firstFreeSession;
285  }
286  else
287  {
288  //The oldest DTLS session is closed whenever the table runs out of space
289  tlsShutdown(oldestSession->dtlsContext);
290  coapServerDeleteSession(oldestSession);
291 
292  //Point to the DTLS session to be reused
293  session = oldestSession;
294  }
295 
296  //Process the new connection attempt
297  error = coapServerAcceptSession(context, session, &context->clientIpAddr,
298  context->clientPort);
299  }
300 
301  //Return status code
302  return error;
303 }
304 
305 
306 /**
307  * @brief Delete DTLS session
308  * @param[in] session Pointer to the DTLS session
309  **/
310 
312 {
313  //Debug message
314  TRACE_INFO("CoAP Server: DTLS session closed...\r\n");
315 
316  //Valid DTLS context?
317  if(session->dtlsContext != NULL)
318  {
319  //Release DTLS context
320  tlsFree(session->dtlsContext);
321  session->dtlsContext = NULL;
322  }
323 }
324 
325 
326 /**
327  * @brief DTLS send callback
328  * @param[in] handle Handle referencing a client connection
329  * @param[in] data Pointer to a buffer containing the data to be transmitted
330  * @param[in] length Number of data bytes to send
331  * @param[out] written Number of bytes that have been transmitted
332  * @param[in] flags Unused parameter
333  * @return Error code
334  **/
335 
336 error_t coapServerSendCallback(void *handle, const void *data,
337  size_t length, size_t *written, uint_t flags)
338 {
339  error_t error;
340  CoapServerContext *context;
341  CoapDtlsSession *session;
342 
343  //Point to the DTLS session
344  session = handle;
345  //Point to the CoAP server context
346  context = session->context;
347 
348  //Send datagram
349  error = socketSendTo(context->socket, &session->clientIpAddr,
350  session->clientPort, data, length, written, flags);
351 
352  //Return status code
353  return error;
354 }
355 
356 
357 /**
358  * @brief DTLS receive callback
359  * @param[in] handle Handle referencing a client connection
360  * @param[out] data Buffer where to store the incoming data
361  * @param[in] size Maximum number of bytes that can be received
362  * @param[out] received Number of bytes that have been received
363  * @param[in] flags Unused parameter
364  * @return Error code
365  **/
366 
368  size_t size, size_t *received, uint_t flags)
369 {
370  error_t error;
371  CoapServerContext *context;
372  CoapDtlsSession *session;
373 
374  //Initialize status code
375  error = ERROR_WOULD_BLOCK;
376 
377  //Point to the DTLS session
378  session = (CoapDtlsSession *) handle;
379  //Point to the CoAP server context
380  context = session->context;
381 
382  //Any pending datagram?
383  if(context->bufferLen > 0)
384  {
385  //Pass incoming datagram to the proper connection
386  if(ipCompAddr(&context->serverIpAddr, &session->serverIpAddr) &&
387  ipCompAddr(&context->clientIpAddr, &session->clientIpAddr) &&
388  context->clientPort == session->clientPort)
389  {
390  //Make sure the length of the datagram is acceptable
391  if(context->bufferLen < size)
392  {
393  //Copy incoming datagram
394  osMemcpy(data, context->buffer, context->bufferLen);
395  //Return the length of the datagram
396  *received = context->bufferLen;
397 
398  //Successful processing
399  error = NO_ERROR;
400  }
401 
402  //Flush the receive buffer
403  context->bufferLen = 0;
404  }
405  }
406 
407  //Return status code
408  return error;
409 }
410 
411 
412 /**
413  * @brief DTLS cookie generation callback function
414  * @param[in] context Pointer to the DTLS context
415  * @param[in] clientParams Client's parameters
416  * @param[out] cookie Pointer to the first byte of the cookie
417  * @param[in,out] length Length of the cookie, in bytes
418  * @param[in] param Pointer to the DTLS session
419  * @return Error code
420  **/
421 
423  const DtlsClientParameters *clientParams, uint8_t *cookie,
424  size_t *length, void *param)
425 {
426  error_t error;
427  CoapDtlsSession *session;
428  HmacContext hmacContext;
429 
430  //Point to the DTLS session
431  session = (CoapDtlsSession *) param;
432 
433  //Debug message
434  TRACE_INFO("CoAP Server: DTLS cookie generation...\r\n");
435 
436  //Make sure the output buffer is large enough to hold the cookie
438  return ERROR_BUFFER_OVERFLOW;
439 
440  //Invalid cookie secret?
441  if(session->context->cookieSecretLen == 0)
442  {
443  //Generate a cookie secret
444  error = context->prngAlgo->read(context->prngContext,
445  session->context->cookieSecret, COAP_SERVER_MAX_COOKIE_SECRET_SIZE);
446  //Any error to report?
447  if(error)
448  return error;
449 
450  //Save the length of the generated secret
451  session->context->cookieSecretLen = COAP_SERVER_MAX_COOKIE_SECRET_SIZE;
452  }
453 
454  //Initialize HMAC context
455  hmacInit(&hmacContext, SHA256_HASH_ALGO, session->context->cookieSecret,
456  session->context->cookieSecretLen);
457 
458  //Generate stateless cookie
459  hmacUpdate(&hmacContext, (uint8_t *) &session->clientIpAddr + sizeof(size_t),
460  session->clientIpAddr.length);
461 
462  //The server should use client parameters (version, random, session_id,
463  //cipher_suites, compression_method) to generate its cookie
464  hmacUpdate(&hmacContext, &clientParams->version, sizeof(uint16_t));
465  hmacUpdate(&hmacContext, clientParams->random, clientParams->randomLen);
466  hmacUpdate(&hmacContext, clientParams->sessionId, clientParams->sessionIdLen);
467  hmacUpdate(&hmacContext, clientParams->cipherSuites, clientParams->cipherSuitesLen);
468  hmacUpdate(&hmacContext, clientParams->compressMethods, clientParams->compressMethodsLen);
469 
470  //Finalize HMAC computation
471  hmacFinal(&hmacContext, cookie);
472 
473  //Return the length of the cookie
475 
476  //Successful processing
477  return NO_ERROR;
478 }
479 
480 
481 /**
482  * @brief DTLS cookie verification callback function
483  * @param[in] context Pointer to the DTLS context
484  * @param[in] clientParams Client's parameters
485  * @param[in] cookie Pointer to the first byte of the cookie
486  * @param[in] length Length of the cookie, in bytes
487  * @param[in] param Pointer to the DTLS session
488  * @return Error code
489  **/
490 
492  const DtlsClientParameters *clientParams, const uint8_t *cookie,
493  size_t length, void *param)
494 {
495  error_t error;
496  CoapDtlsSession *session;
497  HmacContext hmacContext;
498 
499  //Point to the DTLS session
500  session = (CoapDtlsSession *) param;
501 
502  //Debug message
503  TRACE_INFO("CoAP Server: DTLS cookie verification...\r\n");
504 
505  //Make sure the length of the cookie is acceptable
507  return ERROR_WRONG_COOKIE;
508 
509  //Invalid cookie secret?
510  if(session->context->cookieSecretLen == 0)
511  {
512  //Generate a cookie secret
513  error = context->prngAlgo->read(context->prngContext,
514  session->context->cookieSecret, COAP_SERVER_MAX_COOKIE_SECRET_SIZE);
515  //Any error to report?
516  if(error)
517  return error;
518 
519  //Save the length of the generated secret
520  session->context->cookieSecretLen = COAP_SERVER_MAX_COOKIE_SECRET_SIZE;
521  }
522 
523  //Initialize HMAC context
524  hmacInit(&hmacContext, SHA256_HASH_ALGO, session->context->cookieSecret,
525  session->context->cookieSecretLen);
526 
527  //Generate stateless cookie
528  hmacUpdate(&hmacContext, (uint8_t *) &session->clientIpAddr + sizeof(size_t),
529  session->clientIpAddr.length);
530 
531  //The server should use client parameters (version, random, session_id,
532  //cipher_suites, compression_method) to generate its cookie
533  hmacUpdate(&hmacContext, &clientParams->version, sizeof(uint16_t));
534  hmacUpdate(&hmacContext, clientParams->random, clientParams->randomLen);
535  hmacUpdate(&hmacContext, clientParams->sessionId, clientParams->sessionIdLen);
536  hmacUpdate(&hmacContext, clientParams->cipherSuites, clientParams->cipherSuitesLen);
537  hmacUpdate(&hmacContext, clientParams->compressMethods, clientParams->compressMethodsLen);
538 
539  //Finalize HMAC computation
540  hmacFinal(&hmacContext, NULL);
541 
542  //Compare the received cookie against the expected value
543  if(!osMemcmp(cookie, hmacContext.digest, length))
544  {
545  //The cookie is valid
546  error = NO_ERROR;
547  }
548  else
549  {
550  //The cookie is invalid
551  error = ERROR_WRONG_COOKIE;
552  }
553 
554  //Return status code
555  return error;
556 }
557 
558 #endif
CoAP server.
#define COAP_SERVER_MAX_COOKIE_SECRET_SIZE
Definition: coap_server.h:91
#define CoapServerContext
Definition: coap_server.h:121
#define CoapDtlsSession
Definition: coap_server.h:125
#define COAP_SERVER_MAX_SESSIONS
Definition: coap_server.h:63
#define COAP_SERVER_BUFFER_SIZE
Definition: coap_server.h:84
error_t coapServerProcessRequest(CoapServerContext *context, const uint8_t *data, size_t length)
Process CoAP request.
Helper functions for CoAP server.
error_t coapServerCookieGenerateCallback(TlsContext *context, const DtlsClientParameters *clientParams, uint8_t *cookie, size_t *length, void *param)
DTLS cookie generation callback function.
error_t coapServerReceiveCallback(void *handle, void *data, size_t size, size_t *received, uint_t flags)
DTLS receive callback.
error_t coapServerCookieVerifyCallback(TlsContext *context, const DtlsClientParameters *clientParams, const uint8_t *cookie, size_t length, void *param)
DTLS cookie verification callback function.
error_t coapServerDemultiplexSession(CoapServerContext *context)
DTLS session demultiplexing.
error_t coapServerAcceptSession(CoapServerContext *context, CoapDtlsSession *session, const IpAddr *remoteIpAddr, uint16_t remotePort)
Accept a new connection from a client.
error_t coapServerSendCallback(void *handle, const void *data, size_t length, size_t *written, uint_t flags)
DTLS send callback.
void coapServerDeleteSession(CoapDtlsSession *session)
Delete DTLS session.
Transport protocol abstraction layer.
unsigned int uint_t
Definition: compiler_port.h:50
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
#define TRACE_INFO(...)
Definition: debug.h:95
uint32_t time
uint8_t cookie[]
Definition: dtls_misc.h:206
error_t
Error codes.
Definition: error.h:43
@ ERROR_WOULD_BLOCK
Definition: error.h:96
@ ERROR_TIMEOUT
Definition: error.h:95
@ ERROR_WRONG_COOKIE
Definition: error.h:92
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
uint8_t data[]
Definition: ethernet.h:222
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
__weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length)
Update the HMAC context with a portion of the message being hashed.
Definition: hmac.c:201
bool_t ipCompAddr(const IpAddr *ipAddr1, const IpAddr *ipAddr2)
Compare IP addresses.
Definition: ip.c:315
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:838
#define osMemset(p, value, length)
Definition: os_port.h:135
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t systime_t
System time.
#define SHA256_DIGEST_SIZE
Definition: sha256.h:45
#define SHA256_HASH_ALGO
Definition: sha256.h:49
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags)
Send a datagram to a specific destination.
Definition: socket.c:967
Client parameters.
Definition: dtls_misc.h:223
const uint8_t * compressMethods
Definition: dtls_misc.h:231
const uint8_t * cipherSuites
Definition: dtls_misc.h:229
size_t compressMethodsLen
Definition: dtls_misc.h:232
const uint8_t * random
Definition: dtls_misc.h:225
const uint8_t * sessionId
Definition: dtls_misc.h:227
HMAC algorithm context.
Definition: hmac.h:59
uint8_t digest[MAX_HASH_DIGEST_SIZE]
Definition: hmac.h:63
IP network address.
Definition: ip.h:79
uint8_t length
Definition: tcp.h:368
uint8_t flags
Definition: tcp.h:351
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1758
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
error_t tlsRead(TlsContext *context, void *data, size_t size, size_t *received, uint_t flags)
Receive application data from a the remote host using TLS.
Definition: tls.c:1984
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:344
error_t tlsSetSocketCallbacks(TlsContext *context, TlsSocketSendCallback socketSendCallback, TlsSocketReceiveCallback socketReceiveCallback, TlsSocketHandle handle)
Set socket send and receive callbacks.
Definition: tls.c:248
TlsState tlsGetState(TlsContext *context)
Retrieve current TLS state.
Definition: tls.c:198
error_t tlsSetCookieCallbacks(TlsContext *context, DtlsCookieGenerateCallback cookieGenerateCallback, DtlsCookieVerifyCallback cookieVerifyCallback, void *param)
Set cookie generation/verification callbacks (for DTLS only)
Definition: tls.c:1602
error_t tlsShutdown(TlsContext *context)
Gracefully close TLS session.
Definition: tls.c:2302
void tlsFree(TlsContext *context)
Release TLS context.
Definition: tls.c:2464
error_t tlsSetTransportProtocol(TlsContext *context, TlsTransportProtocol transportProtocol)
Set the transport protocol to be used.
Definition: tls.c:314
TlsState
TLS FSM states.
Definition: tls.h:1437
@ TLS_STATE_CLOSED
Definition: tls.h:1470
@ TLS_STATE_INIT
Definition: tls.h:1438
@ TLS_STATE_CLIENT_HELLO
Definition: tls.h:1439
@ TLS_TRANSPORT_PROTOCOL_DATAGRAM
Definition: tls.h:942
void * TlsSocketHandle
Socket handle.
Definition: tls.h:1888
#define TlsContext
Definition: tls.h:36
@ TLS_CONNECTION_END_SERVER
Definition: tls.h:954