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-2026 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.6.0
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  //Attach TCP/IP stack context
68  context->netContext = netGetDefaultContext();
69 
70  //Initialize TLS session state
71  error = tlsInitSessionState(&context->tlsSession);
72  //Any error to report?
73  if(error)
74  return error;
75 
76  //Initialize NTS client state
77  context->state = NTS_CLIENT_STATE_INIT;
78  //Default timeout
79  context->timeout = NTS_CLIENT_DEFAULT_TIMEOUT;
80 
81  //Successful initialization
82  return NO_ERROR;
83 }
84 
85 
86 /**
87  * @brief Set communication timeout
88  * @param[in] context Pointer to the NTS client context
89  * @param[in] timeout Timeout value, in milliseconds
90  * @return Error code
91  **/
92 
94 {
95  //Make sure the NTS client context is valid
96  if(context == NULL)
98 
99  //Save timeout value
100  context->timeout = timeout;
101 
102  //Successful processing
103  return NO_ERROR;
104 }
105 
106 
107 /**
108  * @brief Register TLS initialization callback function
109  * @param[in] context Pointer to the NTS client context
110  * @param[in] callback TLS initialization callback function
111  * @return Error code
112  **/
113 
115  NtsClientTlsInitCallback callback)
116 {
117  //Make sure the NTS client context is valid
118  if(context == NULL)
120 
121  //Save callback function
122  context->tlsInitCallback = callback;
123 
124  //Successful processing
125  return NO_ERROR;
126 }
127 
128 
129 /**
130  * @brief Register random data generation callback function
131  * @param[in] context Pointer to the NTS client context
132  * @param[in] callback Random data generation callback function
133  * @return Error code
134  **/
135 
137  NtsClientRandCallback callback)
138 {
139  //Make sure the NTS client context is valid
140  if(context == NULL)
142 
143  //Save callback function
144  context->randCallback = callback;
145 
146  //Successful processing
147  return NO_ERROR;
148 }
149 
150 
151 /**
152  * @brief Bind the NTS client to a particular network interface
153  * @param[in] context Pointer to the NTS client context
154  * @param[in] interface Network interface to be used
155  * @return Error code
156  **/
157 
159  NetInterface *interface)
160 {
161  //Make sure the NTS client context is valid
162  if(context == NULL)
164 
165  //Explicitly associate the NTS client with the specified interface
166  context->interface = interface;
167 
168  //Successful processing
169  return NO_ERROR;
170 }
171 
172 
173 /**
174  * @brief Specify the IP address of the NTS server
175  * @param[in] context Pointer to the NTS client context
176  * @param[in] serverIpAddr IP address of the NTS server
177  * @param[in] serverPort Port number
178  * @return Error code
179  **/
180 
182  const IpAddr *serverIpAddr, uint16_t serverPort)
183 {
184  //Check parameters
185  if(context == NULL || serverIpAddr == NULL)
187 
188  //Save the IP address and the port number of the NTS server
189  context->ntsKeServerIpAddr = *serverIpAddr;
190  context->ntsKeServerPort = serverPort;
191 
192  //Close NTS-KE socket
194  //Close NTP socket
196 
197  //Revert to default state
199 
200  //Successful processing
201  return NO_ERROR;
202 }
203 
204 
205 /**
206  * @brief Retrieve current time from NTS server
207  * @param[in] context Pointer to the NTS client context
208  * @param[out] timestamp Pointer to the NTP timestamp
209  * @return Error code
210  **/
211 
213  NtpTimestamp *timestamp)
214 {
215  error_t error;
216 
217  //Check parameters
218  if(context == NULL || timestamp == NULL)
220 
221  //Initialize status code
222  error = NO_ERROR;
223 
224  //Send NTP request and wait for server's response
225  while(!error)
226  {
227  //Check current state
228  if(context->state == NTS_CLIENT_STATE_INIT)
229  {
230  //Initialize NTS key establishment
232  }
233  else if(context->state == NTS_CLIENT_STATE_NTS_KE_INIT)
234  {
235  //Open NTS-KE connection
236  error = ntsClientOpenNtsKeConnection(context);
237 
238  //Check status code
239  if(!error)
240  {
241  //Perform TLS handshake
243  }
244  }
245  else if(context->state == NTS_CLIENT_STATE_NTS_KE_CONNECTING)
246  {
247  //Establish NTS-KE connection
248  error = ntsClientEstablishNtsKeConnection(context);
249 
250  //Check status code
251  if(error == NO_ERROR)
252  {
253  //Immediately following a successful handshake, the client shall
254  //send a single request as Application Data encapsulated in the
255  //TLS-protected channel (refer to RFC 8915, section 4)
256  error = ntsClientFormatNtsKeRequest(context);
257 
258  //Send the request
260  }
261  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
262  {
263  //Check whether the timeout has elapsed
264  error = ntsClientCheckNtsKeTimeout(context);
265  }
266  else
267  {
268  //A communication error has occurred
269  }
270  }
271  else if(context->state == NTS_CLIENT_STATE_NTS_KE_SENDING)
272  {
273  //The client shall send a single request
274  error = ntsClientSendNtsKeRequest(context);
275 
276  //Check status code
277  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
278  {
279  //Check whether the timeout has elapsed
280  error = ntsClientCheckNtsKeTimeout(context);
281  }
282  }
283  else if(context->state == NTS_CLIENT_STATE_NTS_KE_RECEIVING)
284  {
285  //Then, the server shall send a single response
286  error = ntsClientReceiveNtsKeResponse(context);
287 
288  //Check status code
289  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
290  {
291  //Check whether the timeout has elapsed
292  error = ntsClientCheckNtsKeTimeout(context);
293  }
294  }
295  else if(context->state == NTS_CLIENT_STATE_NTS_KE_DISCONNECTING)
296  {
297  //Close the connection
298  error = ntsClientShutdownNtsKeConnection(context);
299 
300  //Check status code
301  if(error == NO_ERROR)
302  {
303  //At this point, the NTS-KE phase of the protocol is complete
305 
306  //Time synchronization proceeds with the indicated NTP server
308  }
309  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
310  {
311  //Check whether the timeout has elapsed
312  error = ntsClientCheckNtsKeTimeout(context);
313  }
314  else
315  {
316  //A communication error has occurred
317  }
318  }
319  else if(context->state == NTS_CLIENT_STATE_NTP_RESOLVING)
320  {
321  //The NTP server name shall be either an IPv4 address, an IPv6 address,
322  //or a fully qualified domain name (FQDN)
323  error = getHostByName(context->interface, context->ntpServerName,
324  &context->ntpServerIpAddr, 0);
325 
326  //Check status code
327  if(error == NO_ERROR)
328  {
329  //Successful host name resolution
331  }
332  else if(error == ERROR_IN_PROGRESS)
333  {
334  //Check whether the timeout has elapsed
335  error = ntsClientCheckNtsKeTimeout(context);
336  }
337  else
338  {
339  //Host name resolution failed
340  }
341  }
342  else if(context->state == NTS_CLIENT_STATE_NTP_INIT)
343  {
344  //Valid cookie?
345  if(context->cookieLen > 0)
346  {
347  //Open NTP connection
348  error = ntsClientOpenNtpConnection(context);
349 
350  //Check status code
351  if(!error)
352  {
353  //Save current time
354  context->startTime = osGetSystemTime();
355  //Initialize retransmission timeout
356  context->retransmitTimeout = NTS_CLIENT_INIT_NTP_RETRANSMIT_TIMEOUT;
357 
358  //Send the NTP request to the designated server
360  }
361  }
362  else
363  {
364  //If the client does not have any cookies that it has not already
365  //sent, it should initiate a rerun of the NTS-KE protocol (refer to
366  //RFC 5915, section 5.7)
368  }
369  }
370  else if(context->state == NTS_CLIENT_STATE_NTP_SENDING)
371  {
372  //Send the NTP request to the designated server
373  error = ntsClientSendNtpRequest(context);
374  }
375  else if(context->state == NTS_CLIENT_STATE_NTP_RECEIVING)
376  {
377  //Wait for server's response
378  error = ntsClientReceiveNtpResponse(context);
379  }
380  else if(context->state == NTS_CLIENT_STATE_COMPLETE)
381  {
382  //Extract NTP timestamp from server's response
383  error = ntsClientParseNtpResponse(context, timestamp);
384  //We are done
385  break;
386  }
387  else
388  {
389  //Invalid state
390  error = ERROR_WRONG_STATE;
391  }
392  }
393 
394  //Check status code
395  if(error == NO_ERROR)
396  {
397  //Close NTP connection
399 
400  //Ideally, the client never needs to connect to the NTS-KE server again
402  }
403  else if(error == ERROR_WOULD_BLOCK)
404  {
405  //The NTS-KE or NTP phase of the protocol is in progress
406  }
407  else
408  {
409  //Close NTS-KE connection
411  //Close NTP connection
413 
414  //Revert to default state
416  }
417 
418  //Return status code
419  return error;
420 }
421 
422 
423 /**
424  * @brief Retrieve the kiss code from a Kiss-of-Death message
425  * @param[in] context Pointer to the NTS client context
426  * @return Kiss code
427  **/
428 
430 {
431  uint32_t kissCode;
432 
433  //Make sure the NTS client context is valid
434  if(context != NULL)
435  {
436  //Get kiss code
437  kissCode = context->kissCode;
438  }
439  else
440  {
441  //The NTS client context is not valid
442  kissCode = 0;
443  }
444 
445  //Return kiss code
446  return kissCode;
447 }
448 
449 
450 /**
451  * @brief Release NTS client context
452  * @param[in] context Pointer to the NTS client context
453  **/
454 
456 {
457  //Make sure the NTS client context is valid
458  if(context != NULL)
459  {
460  //Close NTS-KE connection
462  //Close NTP connection
464 
465  //Release TLS session state
466  tlsFreeSessionState(&context->tlsSession);
467 
468  //Clear NTS client context
469  osMemset(context, 0, sizeof(NtsClientContext));
470  }
471 }
472 
473 #endif
error_t ntsClientGetTimestamp(NtsClientContext *context, NtpTimestamp *timestamp)
Retrieve current time from NTS server.
Definition: nts_client.c:212
error_t ntsClientRegisterTlsInitCallback(NtsClientContext *context, NtsClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: nts_client.c:114
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:185
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:455
@ 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:3065
error_t ntsClientRegisterRandCallback(NtsClientContext *context, NtsClientRandCallback callback)
Register random data generation callback function.
Definition: nts_client.c:136
@ ERROR_WRONG_STATE
Definition: error.h:210
error_t ntsClientInit(NtsClientContext *context)
Initialize NTS client context.
Definition: nts_client.c:56
@ ERROR_IN_PROGRESS
Definition: error.h:214
@ 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:40
@ 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:429
NetContext * netGetDefaultContext(void)
Get default TCP/IP stack context.
Definition: net.c:527
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:2307
error_t ntsClientSetTimeout(NtsClientContext *context, systime_t timeout)
Set communication timeout.
Definition: nts_client.c:93
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:158
@ 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:181
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
error_t tlsInitSessionState(TlsSessionState *session)
Initialize session state.
Definition: tls.c:2922
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.