nts_client.c
Go to the documentation of this file.
1 /**
2  * @file nts_client.c
3  * @brief NTS client (Network Time Security)
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  * @section Description
28  *
29  * Network Time Security (NTS) is a mechanism for using TLS and AEAD to provide
30  * cryptographic security for the client-server mode of the NTP. Refer to
31  * RFC 8915 for more details
32  *
33  * @author Oryx Embedded SARL (www.oryx-embedded.com)
34  * @version 2.4.4
35  **/
36 
37 //Switch to the appropriate trace level
38 #define TRACE_LEVEL NTS_TRACE_LEVEL
39 
40 //Dependencies
41 #include "core/net.h"
42 #include "nts/nts_client.h"
43 #include "nts/nts_client_misc.h"
44 #include "debug.h"
45 
46 //Check TCP/IP stack configuration
47 #if (NTS_CLIENT_SUPPORT == ENABLED)
48 
49 
50 /**
51  * @brief Initialize NTS client context
52  * @param[in] context Pointer to the NTS client context
53  * @return Error code
54  **/
55 
57 {
58  error_t error;
59 
60  //Make sure the NTS client context is valid
61  if(context == NULL)
63 
64  //Clear NTS client context
65  osMemset(context, 0, sizeof(NtsClientContext));
66 
67  //Initialize TLS session state
68  error = tlsInitSessionState(&context->tlsSession);
69  //Any error to report?
70  if(error)
71  return error;
72 
73  //Initialize NTS client state
74  context->state = NTS_CLIENT_STATE_INIT;
75  //Default timeout
76  context->timeout = NTS_CLIENT_DEFAULT_TIMEOUT;
77 
78  //Successful initialization
79  return NO_ERROR;
80 }
81 
82 
83 /**
84  * @brief Set communication timeout
85  * @param[in] context Pointer to the NTS client context
86  * @param[in] timeout Timeout value, in milliseconds
87  * @return Error code
88  **/
89 
91 {
92  //Make sure the NTS client context is valid
93  if(context == NULL)
95 
96  //Save timeout value
97  context->timeout = timeout;
98 
99  //Successful processing
100  return NO_ERROR;
101 }
102 
103 
104 /**
105  * @brief Register TLS initialization callback function
106  * @param[in] context Pointer to the NTS client context
107  * @param[in] callback TLS initialization callback function
108  * @return Error code
109  **/
110 
112  NtsClientTlsInitCallback callback)
113 {
114  //Make sure the NTS client context is valid
115  if(context == NULL)
117 
118  //Save callback function
119  context->tlsInitCallback = callback;
120 
121  //Successful processing
122  return NO_ERROR;
123 }
124 
125 
126 /**
127  * @brief Register random data generation callback function
128  * @param[in] context Pointer to the NTS client context
129  * @param[in] callback Random data generation callback function
130  * @return Error code
131  **/
132 
134  NtsClientRandCallback callback)
135 {
136  //Make sure the NTS client context is valid
137  if(context == NULL)
139 
140  //Save callback function
141  context->randCallback = callback;
142 
143  //Successful processing
144  return NO_ERROR;
145 }
146 
147 
148 /**
149  * @brief Bind the NTS client to a particular network interface
150  * @param[in] context Pointer to the NTS client context
151  * @param[in] interface Network interface to be used
152  * @return Error code
153  **/
154 
156  NetInterface *interface)
157 {
158  //Make sure the NTS client context is valid
159  if(context == NULL)
161 
162  //Explicitly associate the NTS client with the specified interface
163  context->interface = interface;
164 
165  //Successful processing
166  return NO_ERROR;
167 }
168 
169 
170 /**
171  * @brief Specify the IP address of the NTS server
172  * @param[in] context Pointer to the NTS client context
173  * @param[in] serverIpAddr IP address of the NTS server
174  * @param[in] serverPort Port number
175  * @return Error code
176  **/
177 
179  const IpAddr *serverIpAddr, uint16_t serverPort)
180 {
181  //Check parameters
182  if(context == NULL || serverIpAddr == NULL)
184 
185  //Save the IP address and the port number of the NTS server
186  context->ntsKeServerIpAddr = *serverIpAddr;
187  context->ntsKeServerPort = serverPort;
188 
189  //Close NTS-KE socket
191  //Close NTP socket
193 
194  //Revert to default state
196 
197  //Successful processing
198  return NO_ERROR;
199 }
200 
201 
202 /**
203  * @brief Retrieve current time from NTS server
204  * @param[in] context Pointer to the NTS client context
205  * @param[out] timestamp Pointer to the NTP timestamp
206  * @return Error code
207  **/
208 
210  NtpTimestamp *timestamp)
211 {
212  error_t error;
213 
214  //Check parameters
215  if(context == NULL || timestamp == NULL)
217 
218  //Initialize status code
219  error = NO_ERROR;
220 
221  //Send NTP request and wait for server's response
222  while(!error)
223  {
224  //Check current state
225  if(context->state == NTS_CLIENT_STATE_INIT)
226  {
227  //Initialize NTS key establishment
229  }
230  else if(context->state == NTS_CLIENT_STATE_NTS_KE_INIT)
231  {
232  //Open NTS-KE connection
233  error = ntsClientOpenNtsKeConnection(context);
234 
235  //Check status code
236  if(!error)
237  {
238  //Perform TLS handshake
240  }
241  }
242  else if(context->state == NTS_CLIENT_STATE_NTS_KE_CONNECTING)
243  {
244  //Establish NTS-KE connection
245  error = ntsClientEstablishNtsKeConnection(context);
246 
247  //Check status code
248  if(error == NO_ERROR)
249  {
250  //Immediately following a successful handshake, the client shall
251  //send a single request as Application Data encapsulated in the
252  //TLS-protected channel (refer to RFC 8915, section 4)
253  error = ntsClientFormatNtsKeRequest(context);
254 
255  //Send the request
257  }
258  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
259  {
260  //Check whether the timeout has elapsed
261  error = ntsClientCheckNtsKeTimeout(context);
262  }
263  else
264  {
265  //A communication error has occurred
266  }
267  }
268  else if(context->state == NTS_CLIENT_STATE_NTS_KE_SENDING)
269  {
270  //The client shall send a single request
271  error = ntsClientSendNtsKeRequest(context);
272 
273  //Check status code
274  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
275  {
276  //Check whether the timeout has elapsed
277  error = ntsClientCheckNtsKeTimeout(context);
278  }
279  }
280  else if(context->state == NTS_CLIENT_STATE_NTS_KE_RECEIVING)
281  {
282  //Then, the server shall send a single response
283  error = ntsClientReceiveNtsKeResponse(context);
284 
285  //Check status code
286  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
287  {
288  //Check whether the timeout has elapsed
289  error = ntsClientCheckNtsKeTimeout(context);
290  }
291  }
292  else if(context->state == NTS_CLIENT_STATE_NTS_KE_DISCONNECTING)
293  {
294  //Close the connection
295  error = ntsClientShutdownNtsKeConnection(context);
296 
297  //Check status code
298  if(error == NO_ERROR)
299  {
300  //At this point, the NTS-KE phase of the protocol is complete
302 
303  //Time synchronization proceeds with the indicated NTP server
305  }
306  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
307  {
308  //Check whether the timeout has elapsed
309  error = ntsClientCheckNtsKeTimeout(context);
310  }
311  else
312  {
313  //A communication error has occurred
314  }
315  }
316  else if(context->state == NTS_CLIENT_STATE_NTP_RESOLVING)
317  {
318  //The NTP server name shall be either an IPv4 address, an IPv6 address,
319  //or a fully qualified domain name (FQDN)
320  error = getHostByName(context->interface, context->ntpServerName,
321  &context->ntpServerIpAddr, 0);
322 
323  //Check status code
324  if(error == NO_ERROR)
325  {
326  //Successful host name resolution
328  }
329  else if(error == ERROR_IN_PROGRESS)
330  {
331  //Check whether the timeout has elapsed
332  error = ntsClientCheckNtsKeTimeout(context);
333  }
334  else
335  {
336  //Host name resolution failed
337  }
338  }
339  else if(context->state == NTS_CLIENT_STATE_NTP_INIT)
340  {
341  //Valid cookie?
342  if(context->cookieLen > 0)
343  {
344  //Open NTP connection
345  error = ntsClientOpenNtpConnection(context);
346 
347  //Check status code
348  if(!error)
349  {
350  //Save current time
351  context->startTime = osGetSystemTime();
352  //Initialize retransmission timeout
353  context->retransmitTimeout = NTS_CLIENT_INIT_NTP_RETRANSMIT_TIMEOUT;
354 
355  //Send the NTP request to the designated server
357  }
358  }
359  else
360  {
361  //If the client does not have any cookies that it has not already
362  //sent, it should initiate a rerun of the NTS-KE protocol (refer to
363  //RFC 5915, section 5.7)
365  }
366  }
367  else if(context->state == NTS_CLIENT_STATE_NTP_SENDING)
368  {
369  //Send the NTP request to the designated server
370  error = ntsClientSendNtpRequest(context);
371  }
372  else if(context->state == NTS_CLIENT_STATE_NTP_RECEIVING)
373  {
374  //Wait for server's response
375  error = ntsClientReceiveNtpResponse(context);
376  }
377  else if(context->state == NTS_CLIENT_STATE_COMPLETE)
378  {
379  //Extract NTP timestamp from server's response
380  error = ntsClientParseNtpResponse(context, timestamp);
381  //We are done
382  break;
383  }
384  else
385  {
386  //Invalid state
387  error = ERROR_WRONG_STATE;
388  }
389  }
390 
391  //Check status code
392  if(error == NO_ERROR)
393  {
394  //Close NTP connection
396 
397  //Ideally, the client never needs to connect to the NTS-KE server again
399  }
400  else if(error == ERROR_WOULD_BLOCK)
401  {
402  //The NTS-KE or NTP phase of the protocol is in progress
403  }
404  else
405  {
406  //Close NTS-KE connection
408  //Close NTP connection
410 
411  //Revert to default state
413  }
414 
415  //Return status code
416  return error;
417 }
418 
419 
420 /**
421  * @brief Retrieve the kiss code from a Kiss-of-Death message
422  * @param[in] context Pointer to the NTS client context
423  * @return Kiss code
424  **/
425 
427 {
428  uint32_t kissCode;
429 
430  //Make sure the NTS client context is valid
431  if(context != NULL)
432  {
433  //Get kiss code
434  kissCode = context->kissCode;
435  }
436  else
437  {
438  //The NTS client context is not valid
439  kissCode = 0;
440  }
441 
442  //Return kiss code
443  return kissCode;
444 }
445 
446 
447 /**
448  * @brief Release NTS client context
449  * @param[in] context Pointer to the NTS client context
450  **/
451 
453 {
454  //Make sure the NTS client context is valid
455  if(context != NULL)
456  {
457  //Close NTS-KE connection
459  //Close NTP connection
461 
462  //Release TLS session state
463  tlsFreeSessionState(&context->tlsSession);
464 
465  //Clear NTS client context
466  osMemset(context, 0, sizeof(NtsClientContext));
467  }
468 }
469 
470 #endif
error_t ntsClientGetTimestamp(NtsClientContext *context, NtpTimestamp *timestamp)
Retrieve current time from NTS server.
Definition: nts_client.c:209
error_t ntsClientRegisterTlsInitCallback(NtsClientContext *context, NtsClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: nts_client.c:111
void ntsClientChangeState(NtsClientContext *context, NtsClientState newState)
Update NTS client state.
@ NTS_CLIENT_STATE_INIT
Definition: nts_client.h:144
@ ERROR_WOULD_BLOCK
Definition: error.h:96
IP network address.
Definition: ip.h:90
error_t ntsClientOpenNtpConnection(NtsClientContext *context)
Open NTP connection.
NtpTimestamp
Definition: ntp_common.h:179
error_t ntsClientOpenNtsKeConnection(NtsClientContext *context)
Open NTS-KE connection.
error_t(* NtsClientTlsInitCallback)(NtsClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: nts_client.h:162
@ NTS_CLIENT_STATE_NTS_KE_DISCONNECTING
Definition: nts_client.h:149
void ntsClientDeinit(NtsClientContext *context)
Release NTS client context.
Definition: nts_client.c:452
@ NTS_CLIENT_STATE_NTS_KE_RECEIVING
Definition: nts_client.h:148
void ntsClientCloseNtpConnection(NtsClientContext *context)
Close NTP connection.
NTS client (Network Time Security)
void tlsFreeSessionState(TlsSessionState *session)
Properly dispose a session state.
Definition: tls.c:2753
error_t ntsClientRegisterRandCallback(NtsClientContext *context, NtsClientRandCallback callback)
Register random data generation callback function.
Definition: nts_client.c:133
@ ERROR_WRONG_STATE
Definition: error.h:209
error_t ntsClientInit(NtsClientContext *context)
Initialize NTS client context.
Definition: nts_client.c:56
@ ERROR_IN_PROGRESS
Definition: error.h:213
@ NTS_CLIENT_STATE_NTS_KE_INIT
Definition: nts_client.h:145
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t
Error codes.
Definition: error.h:43
#define NTS_CLIENT_DEFAULT_TIMEOUT
Definition: nts_client.h:55
error_t ntsClientSendNtpRequest(NtsClientContext *context)
Send NTP request to the server.
error_t ntsClientShutdownNtsKeConnection(NtsClientContext *context)
Shutdown NTS-KE connection.
#define NetInterface
Definition: net.h:36
@ NTS_CLIENT_STATE_NTP_INIT
Definition: nts_client.h:151
uint32_t ntsClientGetKissCode(NtsClientContext *context)
Retrieve the kiss code from a Kiss-of-Death message.
Definition: nts_client.c:426
error_t getHostByName(NetInterface *interface, const char_t *name, IpAddr *ipAddr, uint_t flags)
Resolve a host name into an IP address.
Definition: socket.c:2274
error_t ntsClientSetTimeout(NtsClientContext *context, systime_t timeout)
Set communication timeout.
Definition: nts_client.c:90
error_t ntsClientReceiveNtpResponse(NtsClientContext *context)
Wait for NTP response.
error_t ntsClientReceiveNtsKeResponse(NtsClientContext *context)
Receive NTS-KE response.
error_t ntsClientEstablishNtsKeConnection(NtsClientContext *context)
Establish NTS-KE connection.
@ NTS_CLIENT_STATE_NTP_RECEIVING
Definition: nts_client.h:153
#define NtsClientContext
Definition: nts_client.h:130
error_t ntsClientBindToInterface(NtsClientContext *context, NetInterface *interface)
Bind the NTS client to a particular network interface.
Definition: nts_client.c:155
@ NTS_CLIENT_STATE_NTP_SENDING
Definition: nts_client.h:152
@ NTS_CLIENT_STATE_NTP_RESOLVING
Definition: nts_client.h:150
uint32_t systime_t
System time.
@ ERROR_TIMEOUT
Definition: error.h:95
void ntsClientCloseNtsKeConnection(NtsClientContext *context)
Close NTS-KE connection.
@ NTS_CLIENT_STATE_NTS_KE_CONNECTING
Definition: nts_client.h:146
error_t ntsClientParseNtpResponse(NtsClientContext *context, NtpTimestamp *timestamp)
Parse NTP response.
error_t ntsClientSendNtsKeRequest(NtsClientContext *context)
Send NTS-KE request.
@ NTS_CLIENT_STATE_NTS_KE_SENDING
Definition: nts_client.h:147
error_t ntsClientCheckNtsKeTimeout(NtsClientContext *context)
Determine whether a timeout error has occurred (NTS-KE phase)
@ NTS_CLIENT_STATE_COMPLETE
Definition: nts_client.h:154
error_t(* NtsClientRandCallback)(uint8_t *data, size_t length)
Random data generation callback function.
Definition: nts_client.h:170
error_t ntsClientSetServerAddr(NtsClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Specify the IP address of the NTS server.
Definition: nts_client.c:178
#define osMemset(p, value, length)
Definition: os_port.h:135
TCP/IP stack core.
error_t tlsInitSessionState(TlsSessionState *session)
Initialize session state.
Definition: tls.c:2610
error_t ntsClientFormatNtsKeRequest(NtsClientContext *context)
Format NTS-KE request.
#define NTS_CLIENT_INIT_NTP_RETRANSMIT_TIMEOUT
Definition: nts_client.h:62
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
Helper functions for NTS client.
systime_t osGetSystemTime(void)
Retrieve system time.