1 /*
2  * FreeRTOS V202212.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26 
27 /*
28  * This file is part of the demo project that shows use of the coreSNTP library to create
29  * an SNTP client (daemon) task for synchronizing system time with internet time and
30  * maintaining Coordinated Universal Time (UTC) (or wall-clock time) in the system.
31  *
32  * This file contains the SNTP client (daemon) task as well as functionality for
33  * maintaining wall-clock or UTC time in RAM. The SNTP client periodically synchronizes
34  * system clock with SNTP/NTP server(s). Any other task running an application in the
35  * system can query the system time. For an example of an application task querying time
36  * from the system, refer to the SampleAppTask.c file in this project.
37  *
38  * This demo shows how the coreSNTP library can be used to communicate with SNTP/NTP
39  * servers in a mutually authenticated through the use of symmetric-key based AES-128-CMAC
40  * algorithm. To run this demo with an SNTP/NTP server in authenticated mode, the AES-128-CMAC
41  * symmetric key needs to be pre-shared between the client (i.e. this demo) and the server.
42  *
43  * !!!Note!!!:
44  * Even though this demo shows the use of AES-128-CMAC, a symmetric-key cryptographic based
45  * solution, for authenticating SNTP communication between the demo (SNTP client) and
46  * SNTP/NTP server, we instead RECOMMEND that production devices use the most secure authentication
47  * mechanism alternative available with the Network Time Security (NTS) protocol, an asymmetric-key
48  * cryptographic protocol. For more information, refer to the NTS specification here:
49  * https://datatracker.ietf.org/doc/html/rfc8915
50  */
51 
52 /* Standard includes. */
53 #include <string.h>
54 #include <stdio.h>
55 #include <stdint.h>
56 #include <time.h>
57 #include <math.h>
58 
59 /* Kernel includes. */
60 #include "FreeRTOS.h"
61 #include "task.h"
62 
63 /* Demo include. */
64 #include "common_demo_include.h"
65 
66 /* SNTP library include. */
67 #include "core_sntp_client.h"
68 
69 /* Synchronization primitive include. */
70 #include "semphr.h"
71 
72 /* FreeRTOS+TCP includes */
73 #include "FreeRTOS_IP.h"
74 #include "FreeRTOS_UDP_IP.h"
75 #include "FreeRTOS_Sockets.h"
76 
77 /* PKCS11 includes. */
78 #include "core_pki_utils.h"
79 #include "core_pkcs11_config.h"
80 #include "core_pkcs11.h"
81 
82 /* Backoff Algorithm include. */
83 #include "backoff_algorithm.h"
84 
85 /*-----------------------------------------------------------*/
86 
87 /* Compile time error for undefined configs. */
88 
89 #ifndef democonfigLIST_OF_TIME_SERVERS
90     #error "Define the democonfigLIST_OF_TIME_SERVERS config by following the instructions in demo_config.h file."
91 #endif
92 
93 #ifndef democonfigLIST_OF_AUTHENTICATION_SYMMETRIC_KEYS
94     #error "Define the democonfigLIST_OF_AUTHENTICATION_SYMMETRIC_KEYS config by following the instructions in demo_config.h file."
95 #endif
96 
97 #ifndef democonfigLIST_OF_AUTHENTICATION_KEY_IDS
98     #error "Define the democonfigLIST_OF_AUTHENTICATION_KEY_IDS config by following the instructions in demo_config.h file."
99 #endif
100 
101 #ifndef democonfigSNTP_CLIENT_POLLING_INTERVAL_SECONDS
102     #error "Define the democonfigSNTP_CLIENT_POLLING_INTERVAL_SECONDS config by following instructions in demo_config.h file."
103 #endif
104 
105 #ifndef democonfigSYSTEM_START_YEAR
106     #error "Define the democonfigSYSTEM_START_YEAR config by following instructions in demo_config.h file."
107 #endif
108 
109 /*-----------------------------------------------------------*/
110 /* Default values for timeout configurations . */
111 
112 #ifndef democonfigSERVER_RESPONSE_TIMEOUT_MS
113     #define democonfigSERVER_RESPONSE_TIMEOUT_MS    ( 5000 )
114 #endif
115 
116 #ifndef democonfigSEND_TIME_REQUEST_TIMEOUT_MS
117     #define democonfigSEND_TIME_REQUEST_TIMEOUT_MS    ( 50 )
118 #endif
119 
120 #ifndef democonfigRECEIVE_SERVER_RESPONSE_BLOCK_TIME_MS
121     #define democonfigRECEIVE_SERVER_RESPONSE_BLOCK_TIME_MS    ( 200 )
122 #endif
123 
124 /**
125  * @brief The size for network buffer that is allocated for initializing the coreSNTP library in the
126  * demo.
127  *
128  * @note The size of the buffer MUST be large enough to hold an entire SNTP packet, which includes the standard SNTP
129  * packet data of 48 bytes and authentication data for security mechanism, if used, in communication with time server.
130  */
131 #define SNTP_CONTEXT_NETWORK_BUFFER_SIZE        ( SNTP_PACKET_BASE_SIZE )
132 
133 /**
134  * @brief The constant for storing the number of milliseconds per FreeRTOS tick in the system.
135  * @note This value represents the time duration per tick from the perspective of the
136  * of Windows Simulator based FreeRTOS system that carries lagging clock drift in relation to
137  * internet time or UTC time. Thus, the actual time duration value per tick of the system will be
138  * larger from the perspective of internet time.
139  */
140 #define MILLISECONDS_PER_TICK                   ( 1000 / configTICK_RATE_HZ )
141 
142 /**
143  * @brief The fixed size of the key for the AES-128-CMAC algorithm used for authenticating communication
144  * between the time server and the client.
145  */
146 #define AES_CMAC_AUTHENTICATION_KEY_SIZE        ( 16 )
147 
148 /**
149  * @brief The size of the "Key Identifier" field in the SNTP packet when symmetric key authentication mode as
150  * security mechanism in communicating with time server.
151  *
152  * The "Key Identifier" field appears immediately after the 48 bytes of standard SNTP packet created by the coreSNTP
153  * library. For more information, refer to the SNTPv4 specification: https://datatracker.ietf.org/doc/html/rfc4330#page-8
154  *
155  * @note This demo uses the "Key Identifier" field to communicate with time servers that support authentication mechanism.
156  * This field is stored with the Key ID of the AES-128-CMAC based authentication key stored in the time server.
157  */
158 #define SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH     4
159 
160 /**
161  * @brief The offset for the starting byte of the "Key Identifier" field in an SNTPv4/NTPv4 packet.
162  * This field is only used when symmetric key authentication mode is used for communicating with time server.
163  *
164  * For more information of the SNTP packet format, refer to the SNTPv4 specification
165  * https://datatracker.ietf.org/doc/html/rfc4330#page-8
166  */
167 #define SNTP_PACKET_SYMMETRIC_KEY_ID_OFFSET     SNTP_PACKET_BASE_SIZE
168 
169 /**
170  * @brief The total size of an SNTP packet (which remains same for both client request and server response in SNTP communication)
171  * when using symmetric key based authentication mechanism.
172  *
173  * This value includes size of the 48 bytes of standard SNTP packet, and the "Key Identifier" and "Message Digest" fields
174  * that are used for authentication information in SNTP communication between client and server.
175  *
176  * For more information of the SNTP packet format, refer to the SNTPv4 specification
177  * https://datatracker.ietf.org/doc/html/rfc4330#page-8
178  */
179 #define SNTP_PACKET_AUTHENTICATED_MODE_SIZE     ( SNTP_PACKET_BASE_SIZE + SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH + pkcs11AES_CMAC_SIGNATURE_LENGTH )
180 
181 /**
182  * @brief The maximum poll period that the SNTP client can use as back-off on receiving a rejection from a time server.
183  *
184  * @note This demo performs back-off in polling rate from time server ONLY for the case when a single time server being
185  * is configured through the democonfigLIST_OF_TIME_SERVERS macro.
186  * This is because when more than one time server is configured, the coreSNTP library automatically handles the case
187  * of server rejection of time request by rotating to the next configured server for subsequent time polling requests.
188  */
189 #define SNTP_DEMO_POLL_MAX_BACKOFF_DELAY_SEC    UINT16_MAX
190 
191 /**
192  * @brief The maximum number of times of retrying time requests at exponentially backed-off polling frequency
193  * from a server that rejects time requests.
194  *
195  * @note This macro is only relevant for the case when a single time server is configured in
196  * the demo through, democonfigLIST_OF_TIME_SERVERS.
197  */
198 #define SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES    10
199 
200 /*-----------------------------------------------------------*/
201 
202 /**
203  * @brief The definition of the @ref NetworkContext_t structure for the demo.
204  * The structure wraps a FreeRTOS+TCP socket that is used for UDP communication
205  * with time servers.
206  *
207  * @note The context is used in the @ref UdpTransportInterface_t interface required
208  * by the coreSNTP library.
209  */
210 struct NetworkContext
211 {
212     Socket_t socket;
213 };
214 
215 /**
216  * @brief The definition of the @ref SntpAuthContext_t structure for the demo.
217  * This structure represents the symmetric key for the AES-128-CMAC algorithm based
218  * authentication mechanism shown in the demo for securing SNTP communication
219  * between client and time server.
220  *
221  * @note The context is used in the @ref SntpAuthInterface_t interface required
222  * by the coreSNTP library for enabling authentication.
223  */
224 struct SntpAuthContext
225 {
226     const char * pServer;
227     int32_t keyId;
228     uint8_t pAuthKey[ AES_CMAC_AUTHENTICATION_KEY_SIZE ];
229 };
230 
231 /**
232  * @brief Structure aggregating state variables for RAM-based wall-clock time
233  * in Coordinated Universal Time (UTC) for system.
234  *
235  * @note This demo uses the following mathematical model to represent current
236  * time in RAM.
237  *
238  *  BaseTime = Time set at boot or the last synchronized time
239  *  Slew Rate = Number of milliseconds to adjust per system time second
240  *  No. of ticks since last SNTP sync = Current FreeRTOS Tick Count -
241  *                                      Tick count at last SNTP sync
242  *
243  *  Time Elapsed since last SNTP sync = No. of ticks since last SNTP sync
244  *                                                    x
245  *                                      Number of milliseconds per FreeRTOS tick
246  *
247  *  Slew Adjustment = Slew Rate x Time Elapsed since last SNTP sync
248  *
249  *  Current Time = Base Time +
250  *                 Time Elapsed since last SNTP sync +
251  *                 Slew Adjustment
252  */
253 typedef struct SystemClock
254 {
255     UTCTime_t baseTime;
256     TickType_t lastSyncTickCount;
257     uint32_t pollPeriod;
258     uint64_t slewRate; /* Milliseconds/Seconds */
259     bool firstTimeSyncDone;
260 } SystemClock_t;
261 
262 /**
263  * @brief Shared global system clock object for representing UTC/wall-clock
264  * time in system.
265  */
266 static SystemClock_t systemClock;
267 
268 /**
269  * @brief Mutex for protecting access to the shared memory of the
270  * system clock parameters.
271  */
272 static SemaphoreHandle_t xMutex = NULL;
273 
274 /*
275  * @brief Stores the configured time servers in an array.
276  */
277 static const char * pTimeServers[] = { democonfigLIST_OF_TIME_SERVERS };
278 const size_t numOfServers = sizeof( pTimeServers ) / sizeof( char * );
279 
280 /**
281  * @brief Stores the list of configured AES-128-CMAC symmetric keys for authentication
282  * mechanism for corresponding time servers in democonfigLIST_OF_TIME_SERVERS.
283  */
284 static const char * pAESCMACAuthKeys[] = { democonfigLIST_OF_AUTHENTICATION_SYMMETRIC_KEYS };
285 
286 /**
287  * @brief Stores list of Key IDs corresponding to the authentication keys configured
288  * in democonfigLIST_OF_TIME_SERVERS.
289  */
290 static const int32_t pAuthKeyIds[] = { democonfigLIST_OF_AUTHENTICATION_KEY_IDS };
291 
292 /*-----------------------------------------------------------*/
293 
294 /**
295  * @brief Utility function to convert the passed year to UNIX time representation
296  * of seconds since 1st Jan 1970 00h:00m:00s seconds to 1st Jan 00h:00m:00s of the
297  * the passed year.
298  *
299  * This utility does account for leap years.
300  *
301  * @param[in] The year to translate.
302  */
303 static uint32_t translateYearToUnixSeconds( uint16_t year );
304 
305 /**
306  * @brief Calculates the current time in the system.
307  * It calculates the current time as:
308  *
309  *   BaseTime = Time set at device boot or the last synchronized time
310  *   SlewRate = Number of milliseconds to adjust per system time second
311  *
312  *   Current Time = Base Time +
313  *                  Time since last SNTP Synchronization +
314  *                  Slew Adjustment (if slew rate > 0) for time period since
315  *                  last SNTP synchronization
316  *
317  * @param[in] pBaseTime The base time in the system clock parameters.
318  * @param[in] lastSyncTickCount The tick count at the last time synchronization
319  * with a time server.
320  * @param[in] slewRate The slew rate as seconds of clock adjustment per FreeRTOS
321  * system time second.
322  * @param[out] pCurrentTime This will be populated with the calculated current
323  * UTC time in the system.
324  */
325 static void calculateCurrentTime( UTCTime_t * pBaseTime,
326                                   TickType_t lastSyncTickCount,
327                                   uint64_t slewRate,
328                                   UTCTime_t * pCurrentTime );
329 
330 /**
331  * @brief Initializes the SNTP context for the SNTP client task.
332  * This function generates an array of the configured time servers, creates a FreeRTOS UDP socket
333  * for the UDP transport interface and initializes the passed SNTP context by calling the
334  * Sntp_Init() API of the coreSNTP library.
335  *
336  * @param[in, out] pContext The memory for the SNTP client context that will be initialized with
337  * Sntp_Init API.
338  * @param[in] pTimeServers The list of time servers configured through the democonfigLIST_OF_TIME_SERVERS
339  * macro in demo_config.h.
340  * @param[in] numOfServers The number of time servers configured in democonfigLIST_OF_TIME_SERVERS.
341  * @param[in] pContextBuffer The allocated network buffer that will be initialized in the SNTP context.
342  * @param[in] pUdpContext The memory for the network context for the UDP transport interface that will
343  * be passed to the SNTP client context. This will be filled with a UDP context created by this function.
344  *
345  * @return Returns `true` if initialization of SNTP client context is successful; otherwise `false`.
346  */
347 static bool initializeSntpClient( SntpContext_t * pContext,
348                                   const char ** pTimeServers,
349                                   size_t numOfServers,
350                                   uint8_t * pContextBuffer,
351                                   size_t contextBufferSize,
352                                   NetworkContext_t * pUdpContext,
353                                   SntpAuthContext_t * pAuthContext );
354 
355 /**
356  * @brief The demo implementation of the @ref SntpResolveDns_t interface to
357  * allow the coreSNTP library to resolve DNS name of a time server being
358  * used for requesting time from.
359  *
360  * @param[in] pTimeServer The time-server whose IPv4 address is to be resolved.
361  * @param[out] pIpV4Addr This is filled with the resolved IPv4 address of
362  * @p pTimeServer.
363  */
364 static bool resolveDns( const SntpServerInfo_t * pServerAddr,
365                         uint32_t * pIpV4Addr );
366 
367 /**
368  * @brief The demo implementation of the @ref UdpTransportSendTo_t function
369  * of the UDP transport interface to allow the coreSNTP library to perform
370  * network operation of sending time request over UDP to the provided time server.
371  *
372  * @param[in] pNetworkContext This will be the NetworkContext_t context object
373  * representing the FreeRTOS UDP socket to use for network send operation.
374  * @param[in] serverAddr The IPv4 address of the time server.
375  * @param[in] serverPort The port of the server to send data to.
376  * @param[in] pBuffer The demo-supplied network buffer of size, SNTP_CONTEXT_NETWORK_BUFFER_SIZE,
377  * containing the data to send over the network.
378  * @param[in] bytesToSend The size of data in @p pBuffer to send.
379  *
380  * @return Returns the return code of FreeRTOS UDP send API, FreeRTOS_sendto, which returns
381  * 0 for error or timeout OR the number of bytes sent over the network.
382  */
383 static int32_t UdpTransport_Send( NetworkContext_t * pNetworkContext,
384                                   uint32_t serverAddr,
385                                   uint16_t serverPort,
386                                   const void * pBuffer,
387                                   uint16_t bytesToSend );
388 
389 /**
390  * @brief The demo implementation of the @ref UdpTransportRecvFrom_t function
391  * of the UDP transport interface to allow the coreSNTP library to perform
392  * network operation of reading expected time response over UDP from
393  * provided time server.
394  *
395  * @param[in] pNetworkContext This will be the NetworkContext_t context object
396  * representing the FreeRTOS UDP socket to use for network read operation.
397  * @param[in] pTimeServer The IPv4 address of the time server to receive data from.
398  * @param[in] serverPort The port of the server to receive data from.
399  * @param[out] pBuffer The demo-supplied network buffer of size, SNTP_CONTEXT_NETWORK_BUFFER_SIZE,
400  * that will be filled with data received from the network.
401  * @param[in] bytesToRecv The expected number of bytes to receive from the network
402  * for the server response server.
403  *
404  * @return Returns one of the following:
405  * - 0 for timeout in receiving any data from the network (by translating the
406  * -pdFREERTOS_ERRNO_EWOULDBLOCK return code from FreeRTOS_recvfrom API )
407  *                         OR
408  * - The number of bytes read from the network.
409  */
410 static int32_t UdpTransport_Recv( NetworkContext_t * pNetworkContext,
411                                   uint32_t serverAddr,
412                                   uint16_t serverPort,
413                                   void * pBuffer,
414                                   uint16_t bytesToRecv );
415 
416 /**
417  * @brief The demo implementation of the @ref SntpGetTime_t interface
418  * for obtaining system clock time for the coreSNTP library.
419  *
420  * @param[out] pTime This will be populated with the current time from
421  * the system.
422  */
423 static void sntpClient_GetTime( SntpTimestamp_t * pCurrentTime );
424 
425 /**
426  * @brief The demo implementation of the @ref SntpSetTime_t interface
427  * for correcting the system clock time based on the  time received
428  * from the server response and the clock-offset value calculated by
429  * the coreSNTP library.
430  *
431  * @note This demo uses a combination of "step" AND "slew" methodology
432  * for system clock correction.
433  * 1. "Step" correction is ALWAYS used to immediately correct the system clock
434  *    to match server time on every successful time synchronization with a
435  *    time server (that occurs periodically on the poll interval gaps).
436  *
437  * 2. "Slew" correction approach is used for compensating system clock drift
438  *    during the poll interval period between time synchronization attempts with
439  *    time server(s) when latest time server is not known. The "slew rate" is
440  *    calculated ONLY once on the occasion of the second successful time
441  *    synchronization with a time server. This is because the demo initializes
442  *    system time with (the first second of) the democonfigSYSTEM_START_YEAR
443  *    configuration, and thus, the the actual system clock drift over a period
444  *    of time can be calculated only AFTER the demo system time has been synchronized
445  *    with server time once. Thus, after the first time period of poll interval has
446  *    transpired, the system clock drift is calculated correctly on the subsequent
447  *    successful time synchronization with a time server.
448  *
449  * @note The above system clock correction algorithm is just one example of a correction
450  * approach. It can be modified to suit your application needs. For example, your
451  * application can use ONLY the "step" correction methodology for simplicity of system clock
452  * time calculation logic if the application is not sensitive to abrupt time changes
453  * (that occur at the instances of periodic time synchronization attempts). In such a case,
454  * the Sntp_CalculatePollInterval() API of coreSNTP library can be used to calculate
455  * the optimum time polling period for your application based on the factors of your
456  * system's clock drift rate and the maximum clock drift tolerable by your application.
457  *
458  *
459  * @param[in] pTimeServer The time server from whom the time has been received.
460  * @param[in] pServerTime The most recent time of the server, @p pTimeServer, sent in its
461  * time response.
462  * @param[in] clockOffsetMs The value, in milliseconds, of system clock offset relative
463  * to the server time calculated by the coreSNTP library. If the value is positive, then
464  * the system is BEHIND the server time, and a "slew" clock correction approach is used in
465  * this demo. If the value is negative, then the system time is AHEAD of the server time,
466  * and a "step" clock correction approach is used in this demo.
467  * @param[in] leapSecondInfo This indicates whether there is an upcoming leap second insertion
468  * or deletion (according to astronomical time) the last minute of the end of the month that the
469  * system time needs to adjust for. Leap second adjustment is valuable for applications that
470  * require non-abrupt increment of time for use cases like logging. This demo DOES NOT showcase
471  * leap second adjustment in system clock.
472  */
473 static void sntpClient_SetTime( const SntpServerInfo_t * pTimeServer,
474                                 const SntpTimestamp_t * pServerTime,
475                                 int64_t clockOffsetMs,
476                                 SntpLeapSecondInfo_t leapSecondInfo );
477 
478 /**
479  * @brief Utility function to create a PKCS11 session and a PKCS11 object, and obtain the PKCS11
480  * global function list for performing 128 bit AES-CMAC operations.
481  * This function is called by the definitions of both the authentication interface functions,
482  * @ref SntpGenerateAuthCode_t and @ref SntpValidateServerAuth_t, in this demo for the
483  * AES-CMAC operations.
484  *
485  * @param[in] pAuthContext The context representing the symmetric key for the AES-CMAC operation.
486  * @param[out] pPkcs11Session This is populated with the created PKCS11 session.
487  * @param[out] pFunctionList This is populated with the function list obtained from the created
488  * PKCS11 session.
489  * @param[out] pCmacKey This is populated with the created PKCS11 object handle for AES-CMAC
490  * operations.
491  *
492  * @return The return status code of PKCS11 API calls.
493  */
494 static CK_RV setupPkcs11ObjectForAesCmac( const SntpAuthContext_t * pAuthContext,
495                                           CK_SESSION_HANDLE * pPkcs11Session,
496                                           CK_FUNCTION_LIST_PTR * pFunctionList,
497                                           CK_OBJECT_HANDLE * pCmacKey );
498 
499 /**
500  * @brief Utility function for filling the authentication context with the time server and
501  * its associated authentication key information from the democonfigLIST_OF_AUTHENTICATION_SYMMETRIC_KEYS
502  * and democonfigLIST_OF_AUTHENTICATION_KEY_IDS configuration lists.
503  *
504  * The authentication context represents the server and its authentication key information being
505  * used by the SNTP client at a time for time query. This function is called to update the context
506  * at the time of SNTP client initialization as well as whenever the SNTP client rotates the time
507  * server of use.
508  *
509  * @param[in] pServer The time server whose information is filled in the context.
510  * @param[out] pAuthContext The authentication context to update with information about the @p pServer.
511  */
512 static void populateAuthContextForServer( const char * pServer,
513                                           SntpAuthContext_t * pAuthContext );
514 
515 /**
516  * @brief The demo implementation of the @ref SntpGenerateClientAuth_t function of the authentication
517  * interface required by the coreSNTP library to execute functionality of generating client-side
518  * message authentication code and appending it to the time request before sending to a time server.
519  *
520  * This function first determines whether the passed time server has an authentication key configured
521  * in the demo. If the time server supports authentication, the function utilizes the corePKCS11 library
522  * to generate the client authentication code as a signature using the AES-128-CMAC algorithm, and append
523  * it to the passed SNTP request packet buffer, @p pRequestBuffer.
524  *
525  * @note If the time server supports authentication, this function writes the "Key Identifier" and "Message
526  * Digest" fields of an SNTP packet.
527  *
528  * @param[in, out] pAuthContext The authentication context representing the time server and its authentication
529  * credentials. If the coreSNTP library rotated the time server of use, then this function updates the context
530  * to carry authentication information for the new server.
531  * @param[in] pTimeServer The current time server being used for sending time queries by the SNTP client.
532  * This is used to determine whether the @p pAuthContext carries stale information of a previously used server,
533  * and thus, needs to be updated with information of the current server, @p pTimeServer.
534  * @param[in, out] pRequestBuffer The buffer representing the SNTP request packet, which is already populated with
535  * the standard 48 bytes of packet data. If the time server supports authentication, then the 48 bytes of data
536  * and the authentication key are used to generated AES-128-CMAC signature, and the "Key Identifier" and "Message
537  * Digest" fields of the packet are filled in the buffer.
538  * @param[in] bufferSize The total buffer size of the @p pRequestBuffer for the SNTP request packet.
539  * @param[out] pAuthCodeSize This will be populated with the total bytes for authentication data written to the
540  * @p pRequestBuffer when
541  *
542  * @return Returns one of the following:
543  * - SntpSuccess if EITHER no authentication key information has been configured for the time
544  * server, and thus, no AES-CMAC operation was performed OR the time server supports authentication and
545  * the corePKCS11 operations are successful in generating and appending authentication information to the
546  * @p pRequestBuffer.
547  * - SntpErrorAuthFailure if there is failure in PKCS#11 operations in generating and appending the AES-128-CMAC
548  * signature as the authentication code to the @p pRequestBuffer.
549  */
550 static SntpStatus_t addClientAuthCode( SntpAuthContext_t * pAuthContext,
551                                        const SntpServerInfo_t * pTimeServer,
552                                        void * pRequestBuffer,
553                                        size_t bufferSize,
554                                        uint16_t * pAuthCodeSize );
555 
556 
557 /**
558  * @brief The demo implementation of the @ref SntpValidateServerAuth_t function of the authentication
559  * interface required by the coreSNTP library to execute validation of server as the source of the
560  * received SNTP response by verifying the authentication information present in the packet.
561  *
562  * This function first checks whether the passed time server has authentication key information configured
563  * in the demo to determine if the server supports authentication. If the time server supports authentication,
564  * the function utilizes the corePKCS11 library to verify the AES-128-CMAC signature in the packet, @p pResponseData
565  * that represents the server authentication code.
566  *
567  * @param[in] pAuthContext The authentication context representing the time server of use and its authentication
568  * credentials.
569  * @param[in] pTimeServer The current time server of use from which the response data, @p pResponseData has been
570  * received by the SNTP client. This SHOULD match the time server information carried by the authentication context.
571  * @param[in] pResponseData The buffer representing the SNTP response packet, received from the server, @p pTimeServer,
572  * which contains the server authentication code, if the server supports authentication. The authentication code, if present,
573  * is verified using corePKCS11 to be the expected AES-128-CMAC signature using the standard 48 bytes of SNTP packet data
574  * present in the buffer and the secret symmetric key configured for the server.
575  * @param[in] responseSize The total buffer size of the @p pResponseData for the SNTP response packet.
576  *
577  * @return Returns one of the following:
578  * - SntpSuccess if EITHER no authentication key information has been configured for the time server, and thus,
579  * no AES-CMAC validation operation is performed OR the time server supports authentication and the authentication
580  * code has been successfully validated @p pBuffer.
581  * - SntpErrorAuthFailure if there is internal failure in PKCS#11 operations in validating the server authentication code as
582  * as the AES-128-CMAC for the information present in the response packet, @p pResponseData.
583  * - SntpServerNotAuthenticated if the server is not validated from the response due to the authentication code not matching
584  * the expected AES-128-CMAC signature.
585  */
586 static SntpStatus_t validateServerAuth( SntpAuthContext_t * pAuthContext,
587                                         const SntpServerInfo_t * pTimeServer,
588                                         const void * pResponseData,
589                                         uint16_t responseSize );
590 
591 /**
592  * @brief Generates a random number using PKCS#11.
593  *
594  * @note It is RECOMMENDED to generate a random number for the call to Sntp_SendTimeRequest API
595  * of coreSNTP library to protect against server response spoofing attacks from "network off-path"
596  * attackers.
597  *
598  * @return The generated random number.
599  */
600 static uint32_t generateRandomNumber();
601 
602 /**
603  * @brief Utility to create a new FreeRTOS UDP socket and bind a random
604  * port to it.
605  * A random port is used for the created UDP socket as a protection mechanism
606  * against spoofing attacks from malicious actors that are off the network
607  * path of the client-server communication.
608  *
609  * @param[out] This will be populated with a new FreeRTOS UDP socket
610  * that is bound to a random port.
611  *
612  * @return Returns #true for successful creation of UDP socket; #false
613  * otherwise for failure.
614  */
615 static bool createUdpSocket( Socket_t * pSocket );
616 
617 /**
618  * @brief Utility to close the passed FreeRTOS UDP socket.
619  *
620  * @param pSocket The UDP socket to close.
621  */
622 static void closeUdpSocket( Socket_t * pSocket );
623 
624 /**
625  * @brief Utility to calculate new poll period with exponential backoff and jitter
626  * algorithm.
627  *
628  * @note The demo applies time polling frequency backoff only when a single time server
629  * is configured, through the democonfigLIST_OF_SERVERS macro, and the single server
630  * rejects time requests.
631  *
632  * @param[in, out] pContext The context representing the back-off parameters. This
633  * context is initialized by the function whenever the caller indicates it with the
634  * @p shouldInitializeContext flag.
635  * @param[in] shouldInitializeContext Flag to indicate if the passed context should be
636  * initialized to start a new sequence of backed-off time request retries.
637  * @param[in] minPollPeriod The minimum poll period
638  * @param[in] pPollPeriod The new calculated poll period.
639  *
640  * @return Return #true if a new poll interval is calculated to retry time request
641  * from the server; #false otherwise to indicate exhaustion of time request retry attempts
642  * with the server.
643  */
644 static bool calculateBackoffForNextPoll( BackoffAlgorithmContext_t * pContext,
645                                          bool shouldInitializeContext,
646                                          uint32_t minPollPeriod,
647                                          uint32_t * pPollPeriod );
648 
649 /*------------------------------------------------------------------------------*/
650 
translateYearToUnixSeconds(uint16_t year)651 static uint32_t translateYearToUnixSeconds( uint16_t year )
652 {
653     configASSERT( year >= 1970 );
654 
655     uint32_t numOfDaysSince1970 = ( year - 1970 ) * 365;
656 
657     /* Calculate the extra days in leap years (for February 29) over the time
658     * period from 1st Jan 1970 to 1st Jan of the passed year.
659     * By subtracting from the year 1969, the extra day in 1972 is covered. */
660     numOfDaysSince1970 += ( ( year - 1969 ) / 4 );
661 
662     return( numOfDaysSince1970 * 24 * 3600 );
663 }
664 
calculateCurrentTime(UTCTime_t * pBaseTime,TickType_t lastSyncTickCount,uint64_t slewRate,UTCTime_t * pCurrentTime)665 void calculateCurrentTime( UTCTime_t * pBaseTime,
666                            TickType_t lastSyncTickCount,
667                            uint64_t slewRate,
668                            UTCTime_t * pCurrentTime )
669 {
670     uint64_t msElapsedSinceLastSync = 0;
671     uint64_t currentTimeSecs;
672     TickType_t ticksElapsedSinceLastSync = xTaskGetTickCount() - lastSyncTickCount;
673 
674     /* Calculate time elapsed since last synchronization according to the number
675      * of system ticks passed. */
676     msElapsedSinceLastSync = ticksElapsedSinceLastSync * MILLISECONDS_PER_TICK;
677 
678     /* If slew rate is set, then apply the slew-based clock adjustment for the elapsed time. */
679     if( slewRate > 0 )
680     {
681         /* Slew Adjustment = Slew Rate ( Milliseconds/seconds )
682          *                                      x
683          *                   No. of seconds since last synchronization. */
684         msElapsedSinceLastSync += slewRate * ( msElapsedSinceLastSync / 1000 );
685     }
686 
687     /* Set the current UTC time in the output parameter. */
688     if( msElapsedSinceLastSync >= 1000 )
689     {
690         currentTimeSecs = ( uint64_t ) ( pBaseTime->secs ) + ( msElapsedSinceLastSync / 1000 );
691 
692         /* Support case of UTC timestamp rollover on 7 February 2038. */
693         if( currentTimeSecs > UINT32_MAX )
694         {
695             /* Assert when the UTC timestamp rollover. */
696             configASSERT( !( currentTimeSecs > UINT32_MAX ) );
697 
698             /* Subtract an extra second as timestamp 0 represents the epoch for
699              * UTC era 1. */
700             LogWarn( ( "UTC timestamp rollover." ) );
701             pCurrentTime->secs = ( uint32_t ) ( currentTimeSecs - UINT32_MAX - 1 );
702         }
703         else
704         {
705             pCurrentTime->secs = ( uint32_t ) ( currentTimeSecs );
706         }
707 
708         pCurrentTime->msecs = msElapsedSinceLastSync % 1000;
709     }
710     else
711     {
712         pCurrentTime->secs = pBaseTime->secs;
713         pCurrentTime->msecs = ( uint32_t ) ( msElapsedSinceLastSync );
714     }
715 }
716 
717 /********************** DNS Resolution Interface *******************************/
resolveDns(const SntpServerInfo_t * pServerAddr,uint32_t * pIpV4Addr)718 static bool resolveDns( const SntpServerInfo_t * pServerAddr,
719                         uint32_t * pIpV4Addr )
720 {
721     uint32_t resolvedAddr = 0;
722     bool status = false;
723 
724     resolvedAddr = FreeRTOS_gethostbyname( pServerAddr->pServerName );
725 
726     /* Set the output parameter if DNS look up succeeded. */
727     if( resolvedAddr != 0 )
728     {
729         /* DNS Look up succeeded. */
730         status = true;
731 
732         *pIpV4Addr = FreeRTOS_ntohl( resolvedAddr );
733 
734         #if defined( LIBRARY_LOG_LEVEL ) && ( LIBRARY_LOG_LEVEL != LOG_NONE )
735             uint8_t stringAddr[ 16 ];
736             FreeRTOS_inet_ntoa( resolvedAddr, stringAddr );
737             LogInfo( ( "Resolved time server as %s", stringAddr ) );
738         #endif
739     }
740 
741     return status;
742 }
743 
744 /********************** UDP Interface definition *******************************/
UdpTransport_Send(NetworkContext_t * pNetworkContext,uint32_t serverAddr,uint16_t serverPort,const void * pBuffer,uint16_t bytesToSend)745 int32_t UdpTransport_Send( NetworkContext_t * pNetworkContext,
746                            uint32_t serverAddr,
747                            uint16_t serverPort,
748                            const void * pBuffer,
749                            uint16_t bytesToSend )
750 {
751     struct freertos_sockaddr destinationAddress;
752     int32_t bytesSent;
753 
754     #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
755     {
756         destinationAddress.sin_address.ulIP_IPv4 = FreeRTOS_htonl( serverAddr );
757     }
758     #else
759     {
760         destinationAddress.sin_addr = FreeRTOS_htonl( serverAddr );
761     }
762     #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
763 
764     destinationAddress.sin_port = FreeRTOS_htons( serverPort );
765     destinationAddress.sin_family = FREERTOS_AF_INET;
766 
767     /* Send the buffer with ulFlags set to 0, so the FREERTOS_ZERO_COPY bit
768      * is clear. */
769     bytesSent = FreeRTOS_sendto( /* The socket being send to. */
770         pNetworkContext->socket,
771         /* The data being sent. */
772         pBuffer,
773         /* The length of the data being sent. */
774         bytesToSend,
775         /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
776         0,
777         /* Where the data is being sent. */
778         &destinationAddress,
779         /* Not used but should be set as shown. */
780         sizeof( destinationAddress )
781         );
782 
783     return bytesSent;
784 }
785 
UdpTransport_Recv(NetworkContext_t * pNetworkContext,uint32_t serverAddr,uint16_t serverPort,void * pBuffer,uint16_t bytesToRecv)786 static int32_t UdpTransport_Recv( NetworkContext_t * pNetworkContext,
787                                   uint32_t serverAddr,
788                                   uint16_t serverPort,
789                                   void * pBuffer,
790                                   uint16_t bytesToRecv )
791 {
792     struct freertos_sockaddr sourceAddress;
793     int32_t bytesReceived;
794     socklen_t addressLength = sizeof( struct freertos_sockaddr );
795 
796     /* Receive into the buffer with ulFlags set to 0, so the FREERTOS_ZERO_COPY bit
797      * is clear. */
798     bytesReceived = FreeRTOS_recvfrom( /* The socket data is being received on. */
799         pNetworkContext->socket,
800 
801         /* The buffer into which received data will be
802          * copied. */
803         pBuffer,
804 
805         /* The length of the buffer into which data will be
806          * copied. */
807         bytesToRecv,
808         /* ulFlags with the FREERTOS_ZERO_COPY bit clear. */
809         0,
810         /* Will get set to the source of the received data. */
811         &sourceAddress,
812         /* Not used but should be set as shown. */
813         &addressLength
814         );
815 
816     /* If data is received from the network, discard the data if  received from a different source than
817      * the server. */
818     #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
819         if( ( bytesReceived > 0 ) && ( ( FreeRTOS_ntohl( sourceAddress.sin_address.ulIP_IPv4 ) != serverAddr ) ||
820                                        ( FreeRTOS_ntohs( sourceAddress.sin_port ) != serverPort ) ) )
821     #else
822         if( ( bytesReceived > 0 ) && ( ( FreeRTOS_ntohl( sourceAddress.sin_addr ) != serverAddr ) ||
823                                        ( FreeRTOS_ntohs( sourceAddress.sin_port ) != serverPort ) ) )
824     #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
825     {
826         bytesReceived = 0;
827 
828         #if defined( LIBRARY_LOG_LEVEL ) && ( LIBRARY_LOG_LEVEL != LOG_NONE )
829             /* Convert the IP address of the sender's address to string for logging. */
830             char stringAddr[ 16 ];
831 
832         #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 )
833             {
834                 FreeRTOS_inet_ntoa( sourceAddress.sin_address.ulIP_IPv4, stringAddr );
835             }
836         #else
837             {
838                 FreeRTOS_inet_ntoa( sourceAddress.sin_addr, stringAddr );
839             }
840         #endif /* defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 0 ) */
841 
842         /* Log about reception of packet from unexpected sender. */
843         LogWarn( ( "Received UDP packet from unexpected source: Addr=%s Port=%u",
844                    stringAddr, FreeRTOS_ntohs( sourceAddress.sin_port ) ) );
845         #endif /* if defined( LIBRARY_LOG_LEVEL ) && ( LIBRARY_LOG_LEVEL != LOG_NONE ) */
846     }
847 
848     /* Translate the return code of timeout to the UDP transport interface expected
849      * code to indicate read retry. */
850     else if( bytesReceived == -pdFREERTOS_ERRNO_EWOULDBLOCK )
851     {
852         bytesReceived = 0;
853     }
854 
855     return bytesReceived;
856 }
857 
858 
859 /**************************** Time Interfaces ************************************************/
sntpClient_GetTime(SntpTimestamp_t * pCurrentTime)860 static void sntpClient_GetTime( SntpTimestamp_t * pCurrentTime )
861 {
862     UTCTime_t currentTime;
863     uint32_t ntpSecs;
864 
865     /* Obtain mutex for accessing system clock variables */
866     xSemaphoreTake( xMutex, portMAX_DELAY );
867 
868     calculateCurrentTime( &systemClock.baseTime,
869                           systemClock.lastSyncTickCount,
870                           systemClock.slewRate,
871                           &currentTime );
872 
873     /* Release mutex. */
874     xSemaphoreGive( xMutex );
875 
876     /* Convert UTC time from UNIX timescale to SNTP timestamp format. */
877     ntpSecs = currentTime.secs + SNTP_TIME_AT_UNIX_EPOCH_SECS;
878 
879     /* Support case of SNTP timestamp rollover on 7 February 2036 when
880      * converting from UNIX time to SNTP timestamp. */
881     if( ntpSecs > UINT32_MAX )
882     {
883         /* Assert when SNTP time rollover. */
884         configASSERT( !( ntpSecs > UINT32_MAX ) );
885 
886         /* Subtract an extra second as timestamp 0 represents the epoch for
887          * NTP era 1. */
888         LogWarn( ( "SNTP timestamp rollover." ) );
889         pCurrentTime->seconds = ntpSecs - UINT32_MAX - 1;
890     }
891     else
892     {
893         pCurrentTime->seconds = ntpSecs;
894     }
895 
896     pCurrentTime->fractions = MILLISECONDS_TO_SNTP_FRACTIONS( currentTime.msecs );
897 }
898 
sntpClient_SetTime(const SntpServerInfo_t * pTimeServer,const SntpTimestamp_t * pServerTime,int64_t clockOffsetMs,SntpLeapSecondInfo_t leapSecondInfo)899 static void sntpClient_SetTime( const SntpServerInfo_t * pTimeServer,
900                                 const SntpTimestamp_t * pServerTime,
901                                 int64_t clockOffsetMs,
902                                 SntpLeapSecondInfo_t leapSecondInfo )
903 {
904     /* Note: This demo DOES NOT show adjustment of leap second in system time,
905      * if an upcoming leap second adjustment is mentioned in server response.
906      * Leap second adjustment occurs at low frequency (only for the last minute of June
907      * or December) and can be useful for applications that require smooth system
908      * time continuum ALWAYS including the time of the leap second adjustment.
909      *
910      * For more information on leap seconds, refer to
911      * https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs.
912      */
913     ( void ) leapSecondInfo;
914 
915     LogInfo( ( "Received time from time server: %s", pTimeServer->pServerName ) );
916 
917     /* Obtain the mutext for accessing system clock variables. */
918     xSemaphoreTake( xMutex, portMAX_DELAY );
919 
920     /* Always correct the system base time on receiving time from server.*/
921     SntpStatus_t status;
922     uint32_t unixSecs;
923     uint32_t unixMicroSecs;
924 
925     /* Convert server time from NTP timestamp to UNIX format. */
926     status = Sntp_ConvertToUnixTime( pServerTime,
927                                      &unixSecs,
928                                      &unixMicroSecs );
929     configASSERT( status == SntpSuccess );
930 
931     /* Always correct the base time of the system clock as the time received from the server. */
932     systemClock.baseTime.secs = unixSecs;
933     systemClock.baseTime.msecs = unixMicroSecs / 1000;
934 
935     /* Set the clock adjustment "slew" rate of system clock if it wasn't set already and this is NOT
936      * the first clock synchronization since device boot-up. */
937     if( ( systemClock.firstTimeSyncDone == true ) && ( systemClock.slewRate == 0 ) )
938     {
939         /* We will use a "slew" correction approach to compensate for system clock
940          * drift over poll interval period that exists between consecutive time synchronizations
941          * with time server. */
942 
943         /* Calculate the "slew" rate for system clock as milliseconds of adjustment needed per second. */
944         systemClock.slewRate = clockOffsetMs / systemClock.pollPeriod;
945     }
946 
947     /* Set the system clock flag that indicates completion of the first time synchronization since device boot-up. */
948     if( systemClock.firstTimeSyncDone == false )
949     {
950         systemClock.firstTimeSyncDone = true;
951     }
952 
953     /* Store the tick count of the current time synchronization in the system clock. */
954     systemClock.lastSyncTickCount = xTaskGetTickCount();
955 
956     xSemaphoreGive( xMutex );
957 }
958 
959 /**************************** Authentication Utilities and Interface Functions ***********************************************/
populateAuthContextForServer(const char * pServer,SntpAuthContext_t * pAuthContext)960 static void populateAuthContextForServer( const char * pServer,
961                                           SntpAuthContext_t * pAuthContext )
962 
963 {
964     size_t index = 0;
965 
966     /* Look for the server in the list of configured servers to determine the
967      * index position of the server so that the server's corresponding information of credentials
968      * can be found from democonfigLIST_OF_AUTHENTICATION_SYMMETRIC_KEYS and democonfigLIST_OF_AUTHENTICATION_KEY_IDS
969      * lists. */
970     for( index = 0; index < numOfServers; index++ )
971     {
972         if( ( strlen( pServer ) == strlen( pTimeServers[ index ] ) ) && ( strncmp( pServer, pTimeServers[ index ], strlen( pServer ) ) == 0 ) )
973         {
974             /* The records for server in the demo configuration lists have been found. */
975             break;
976         }
977     }
978 
979     /* Make sure that record for server has been found. */
980     configASSERT( index != numOfServers );
981 
982     /* Fill the time server in the authentication context. */
983     pAuthContext->pServer = pServer;
984 
985     /* Determine if the time server has been configured to use authentication mechanism. */
986     if( ( pAESCMACAuthKeys[ index ] != NULL ) && ( pAuthKeyIds[ index ] != -1 ) )
987     {
988         const char * pKeyHexString = pAESCMACAuthKeys[ index ];
989 
990         /* Verify that the configured authentication key is 128 bits or 16 bytes in size. As
991          * the input format is a hex string, the string length should be 32 bytes. */
992         configASSERT( strlen( pKeyHexString ) == 2 * AES_CMAC_AUTHENTICATION_KEY_SIZE );
993 
994         /* Set the key ID in the context. */
995         pAuthContext->keyId = pAuthKeyIds[ index ];
996 
997         /* Store the configured AES-128-CMAC key for authentication in the SntpAuthContext_t context
998          * after converting it from hex string to binary. */
999         for( index = 0; index < strlen( pKeyHexString ); index += 2 )
1000         {
1001             char byteString[ 3 ] = { pKeyHexString[ index ], pKeyHexString[ index + 1 ], '\0' };
1002             uint8_t byteVal = ( uint8_t ) ( strtoul( byteString, NULL, 16 ) );
1003             pAuthContext->pAuthKey[ index / 2 ] = byteVal;
1004         }
1005     }
1006     else
1007     {
1008         /* No key information has been configured for the time server. Thus, communication with the time
1009          * server will not use authentication mechanism. */
1010         memset( pAuthContext->pAuthKey, 0, sizeof( pAuthContext->pAuthKey ) );
1011         pAuthContext->keyId = -1;
1012     }
1013 }
1014 
setupPkcs11ObjectForAesCmac(const SntpAuthContext_t * pAuthContext,CK_SESSION_HANDLE * pPkcs11Session,CK_FUNCTION_LIST_PTR * pFunctionList,CK_OBJECT_HANDLE * pCmacKey)1015 static CK_RV setupPkcs11ObjectForAesCmac( const SntpAuthContext_t * pAuthContext,
1016                                           CK_SESSION_HANDLE * pPkcs11Session,
1017                                           CK_FUNCTION_LIST_PTR * pFunctionList,
1018                                           CK_OBJECT_HANDLE * pCmacKey )
1019 {
1020     CK_RV result;
1021 
1022     static CK_BYTE label[] = pkcs11configLABEL_CMAC_KEY;
1023     static CK_KEY_TYPE cmacKeyType = CKK_AES;
1024     static CK_OBJECT_CLASS cmacKeyClass = CKO_SECRET_KEY;
1025     static CK_BBOOL trueObject = CK_TRUE;
1026 
1027     static CK_ATTRIBUTE aes_cmac_template[] =
1028     {
1029         { CKA_CLASS,    &cmacKeyClass, sizeof( CK_OBJECT_CLASS ) },
1030         { CKA_KEY_TYPE, &cmacKeyType,  sizeof( CK_KEY_TYPE )     },
1031         { CKA_LABEL,    label,         sizeof( label ) - 1       },
1032         { CKA_TOKEN,    &trueObject,   sizeof( CK_BBOOL )        },
1033         { CKA_SIGN,     &trueObject,   sizeof( CK_BBOOL )        },
1034         { CKA_VERIFY,   &trueObject,   sizeof( CK_BBOOL )        },
1035         { CKA_VALUE,    NULL,          0                         }
1036     };
1037 
1038     /* Update the attributes array with the key of AES-CMAC operation. */
1039     aes_cmac_template[ 6 ].pValue = ( uint8_t * ) ( pAuthContext->pAuthKey );
1040     aes_cmac_template[ 6 ].ulValueLen = sizeof( pAuthContext->pAuthKey );
1041 
1042     result = xInitializePkcs11Session( pPkcs11Session );
1043 
1044     if( result != CKR_OK )
1045     {
1046         LogError( ( "Failed to open PKCS #11 session." ) );
1047     }
1048 
1049     if( result == CKR_OK )
1050     {
1051         result = C_GetFunctionList( pFunctionList );
1052 
1053         if( result != CKR_OK )
1054         {
1055             LogError( ( "Failed to get PKCS #11 function list." ) );
1056         }
1057     }
1058 
1059     if( result == CKR_OK )
1060     {
1061         /* Create the template objects */
1062         result = ( *pFunctionList )->C_CreateObject( *pPkcs11Session,
1063                                                      ( CK_ATTRIBUTE_PTR ) &aes_cmac_template,
1064                                                      sizeof( aes_cmac_template ) / sizeof( CK_ATTRIBUTE ),
1065                                                      pCmacKey );
1066 
1067         if( result != CKR_OK )
1068         {
1069             LogError( ( "Failed to create AES CMAC object." ) );
1070         }
1071 
1072         configASSERT( *pCmacKey != CK_INVALID_HANDLE );
1073     }
1074 
1075     return result;
1076 }
1077 
addClientAuthCode(SntpAuthContext_t * pAuthContext,const SntpServerInfo_t * pTimeServer,void * pRequestBuffer,size_t bufferSize,uint16_t * pAuthCodeSize)1078 SntpStatus_t addClientAuthCode( SntpAuthContext_t * pAuthContext,
1079                                 const SntpServerInfo_t * pTimeServer,
1080                                 void * pRequestBuffer,
1081                                 size_t bufferSize,
1082                                 uint16_t * pAuthCodeSize )
1083 {
1084     CK_RV result = CKR_OK;
1085     CK_FUNCTION_LIST_PTR functionList;
1086     CK_SESSION_HANDLE pkcs11Session = 0;
1087 
1088     CK_OBJECT_HANDLE cMacKey;
1089     size_t macBytesWritten = pkcs11AES_CMAC_SIGNATURE_LENGTH;
1090 
1091     CK_MECHANISM mechanism =
1092     {
1093         CKM_AES_CMAC, NULL_PTR, 0
1094     };
1095 
1096     /* Determine whether the authentication context information needs to be updated to match
1097      * the passed time server that is to be used for querying time.
1098      * Note: The coreSNTP library will rotate the time server of use for communication to the next
1099      * in the list if either the time server rejects a time request OR times out in its response to the
1100      * time request. In such a case of rotating time server, the application (or user of the coreSNTP
1101      * library) is required to necessary updates to the authentication context to reflect the new
1102      * time server being used for SNTP communication by the SNTP client.*/
1103     if( ( strlen( pTimeServer->pServerName ) != strlen( pAuthContext->pServer ) ) ||
1104         ( strncmp( pTimeServer->pServerName, pAuthContext->pServer, strlen( pAuthContext->pServer ) ) != 0 ) )
1105     {
1106         /* Update the authentication context to represent the new time server of usage for
1107          *  time requests. */
1108         populateAuthContextForServer( pTimeServer->pServerName, pAuthContext );
1109     }
1110 
1111     /* Check if the time server supports AES-128-CMAC authentication scheme in communication.
1112      * If the time server supports authentication, then proceed with operation of generating client
1113      * authentication code from the SNTP request packet and appending it to the request buffer.  */
1114     if( pAuthContext->keyId != -1 )
1115     {
1116         /* Ensure that the buffer is large enough to hold the "Key Identifier" and "Message Digest" fields
1117          * for authentication information of the SNTP time request packet. */
1118         configASSERT( bufferSize >= SNTP_PACKET_AUTHENTICATED_MODE_SIZE );
1119 
1120         result = setupPkcs11ObjectForAesCmac( pAuthContext,
1121                                               &pkcs11Session,
1122                                               &functionList,
1123                                               &cMacKey );
1124 
1125         if( result == CKR_OK )
1126         {
1127             /* Test SignInit and Sign */
1128             result = functionList->C_SignInit( pkcs11Session, &mechanism, cMacKey );
1129 
1130             if( result != CKR_OK )
1131             {
1132                 LogError( ( "Failed to C_SignInit AES CMAC." ) );
1133             }
1134         }
1135 
1136         /* Append the Key ID of the signing key before appending the signature to the buffer. */
1137         *( uint32_t * ) ( ( uint8_t * ) pRequestBuffer + SNTP_PACKET_SYMMETRIC_KEY_ID_OFFSET ) = FreeRTOS_htonl( pAuthContext->keyId );
1138 
1139         /* Generate the authentication code as the signature of the time request packet
1140          * with the configured key. */
1141         if( result == CKR_OK )
1142         {
1143             result = functionList->C_Sign( pkcs11Session,
1144                                            ( CK_BYTE_PTR ) pRequestBuffer,
1145                                            SNTP_PACKET_BASE_SIZE,
1146                                            ( CK_BYTE_PTR ) pRequestBuffer + SNTP_PACKET_BASE_SIZE + SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH,
1147                                            &macBytesWritten );
1148 
1149             if( result != CKR_OK )
1150             {
1151                 LogError( ( "Failed to generate client auth code: Failed to generate AES-128-CMAC signature of SNTP request packet." ) );
1152             }
1153         }
1154 
1155         /* Close the PKCS #11 session as the AES-CMAC operation is completed. */
1156         if( result == CKR_OK )
1157         {
1158             result = functionList->C_CloseSession( pkcs11Session );
1159             configASSERT( result == CKR_OK );
1160 
1161             result = functionList->C_Finalize( NULL );
1162             configASSERT( result == CKR_OK );
1163         }
1164 
1165         if( result == CKR_OK )
1166         {
1167             *pAuthCodeSize = SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH + pkcs11AES_CMAC_SIGNATURE_LENGTH;
1168         }
1169     }
1170     else
1171     {
1172         /* Server has not been configured with authentication key information, thus, no data was appended to the
1173          * request packet buffer. */
1174         *pAuthCodeSize = 0;
1175     }
1176 
1177     return ( result == CKR_OK ) ? SntpSuccess : SntpErrorAuthFailure;
1178 }
1179 
validateServerAuth(SntpAuthContext_t * pAuthContext,const SntpServerInfo_t * pTimeServer,const void * pResponseData,uint16_t responseSize)1180 SntpStatus_t validateServerAuth( SntpAuthContext_t * pAuthContext,
1181                                  const SntpServerInfo_t * pTimeServer,
1182                                  const void * pResponseData,
1183                                  uint16_t responseSize )
1184 {
1185     CK_RV result = CKR_OK;
1186     CK_FUNCTION_LIST_PTR functionList;
1187     CK_SESSION_HANDLE pkcs11Session = 0;
1188     SntpStatus_t returnStatus = SntpSuccess;
1189 
1190     CK_OBJECT_HANDLE cMacKey;
1191     size_t macBytesWritten = pkcs11AES_CMAC_SIGNATURE_LENGTH;
1192 
1193     CK_MECHANISM mechanism =
1194     {
1195         CKM_AES_CMAC, NULL_PTR, 0
1196     };
1197 
1198     /* The time server information in the authentication context, managed by this demo application, and the
1199      * pTimeServer parameter, passed by the coreSNTP library, MUST be the same.
1200      * Note: The addClientAuthCode() function
1201      * is responsible for updating the authentication context to represent the time server being currently used
1202      * by the coreSNTP library for time querying. */
1203     configASSERT( ( strlen( pTimeServer->pServerName ) == strlen( pAuthContext->pServer ) ) &&
1204                   ( strncmp( pTimeServer->pServerName, pAuthContext->pServer, strlen( pAuthContext->pServer ) ) == 0 ) );
1205 
1206     /* Check if the time server supports AES-128-CMAC authentication scheme in communication.
1207      * If the time server supports authentication, then proceed with operation of validating server
1208      * from the authentication code in the response payload.  */
1209     if( pAuthContext->keyId != -1 )
1210     {
1211         /* As the server supports authentication mode of communication, the server response size
1212          * SHOULD contain the authentication code. */
1213         configASSERT( responseSize >= ( SNTP_PACKET_AUTHENTICATED_MODE_SIZE ) );
1214 
1215         result = setupPkcs11ObjectForAesCmac( pAuthContext,
1216                                               &pkcs11Session,
1217                                               &functionList,
1218                                               &cMacKey );
1219 
1220         if( result == CKR_OK )
1221         {
1222             /* Test SignInit and Sign */
1223             result = functionList->C_VerifyInit( pkcs11Session, &mechanism, cMacKey );
1224 
1225             if( result != CKR_OK )
1226             {
1227                 returnStatus = SntpErrorAuthFailure;
1228                 LogError( ( "Failed to C_VerifyInit AES CMAC." ) );
1229             }
1230         }
1231         else
1232         {
1233             returnStatus = SntpErrorAuthFailure;
1234         }
1235 
1236         /* Generate the authentication code as the signature of the time request packet
1237          * with the configured key. */
1238         if( result == CKR_OK )
1239         {
1240             result = functionList->C_Verify( pkcs11Session,
1241                                              ( CK_BYTE_PTR ) pResponseData,
1242                                              SNTP_PACKET_BASE_SIZE,
1243                                              ( CK_BYTE_PTR ) pResponseData + SNTP_PACKET_SYMMETRIC_KEY_ID_OFFSET + SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH,
1244                                              pkcs11AES_CMAC_SIGNATURE_LENGTH );
1245 
1246             if( result != CKR_OK )
1247             {
1248                 returnStatus = SntpServerNotAuthenticated;
1249                 LogError( ( "Server cannot be validated from received response: AES-128-CMAC signature in response packet does not match expected." ) );
1250             }
1251         }
1252 
1253         /* Close the PKCS #11 session as the AES-CMAC operation is completed. */
1254         if( result == CKR_OK )
1255         {
1256             result = functionList->C_CloseSession( pkcs11Session );
1257             configASSERT( result == CKR_OK );
1258 
1259             result = functionList->C_Finalize( NULL );
1260             configASSERT( result == CKR_OK );
1261         }
1262     }
1263 
1264     return returnStatus;
1265 }
1266 
1267 
1268 /*************************************************************************************/
1269 
generateRandomNumber()1270 static uint32_t generateRandomNumber()
1271 {
1272     CK_RV pkcs11Status = CKR_OK;
1273     CK_FUNCTION_LIST_PTR pFunctionList = NULL;
1274     CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
1275     uint32_t randomNum = 0;
1276 
1277     /* Get list of functions supported by the PKCS #11 port. */
1278     pkcs11Status = C_GetFunctionList( &pFunctionList );
1279 
1280     if( pkcs11Status != CKR_OK )
1281     {
1282         configASSERT( pFunctionList != NULL );
1283         LogError( ( "Failed to generate random number. "
1284                     "PKCS #11 API, C_GetFunctionList, failed." ) );
1285     }
1286 
1287     if( pkcs11Status == CKR_OK )
1288     {
1289         /* Initialize PKCS #11 module and create a new session. */
1290         pkcs11Status = xInitializePkcs11Session( &session );
1291 
1292         if( pkcs11Status != CKR_OK )
1293         {
1294             configASSERT( session != CK_INVALID_HANDLE );
1295 
1296             LogError( ( "Failed to generate random number. "
1297                         "Failed to initialize PKCS #11 session." ) );
1298         }
1299     }
1300 
1301     if( pkcs11Status == CKR_OK )
1302     {
1303         if( pFunctionList->C_GenerateRandom( session,
1304                                              ( uint8_t * ) ( &randomNum ),
1305                                              sizeof( randomNum ) ) != CKR_OK )
1306         {
1307             LogError( ( "Failed to generate random number. "
1308                         "PKCS #11 API, C_GenerateRandom, failed to generate random number." ) );
1309         }
1310     }
1311 
1312     if( pkcs11Status == CKR_OK )
1313     {
1314         if( pFunctionList->C_CloseSession( session ) != CKR_OK )
1315         {
1316             LogError( ( " Failed to close PKCS #11 session after generating random number." ) );
1317         }
1318     }
1319 
1320     return randomNum;
1321 }
1322 
1323 
1324 /*************************************************************************************/
1325 
initializeSystemClock(void)1326 void initializeSystemClock( void )
1327 {
1328     /* On boot-up initialize the system time as the first second in the configured year. */
1329     uint32_t startupTimeInUnixSecs = translateYearToUnixSeconds( democonfigSYSTEM_START_YEAR );
1330 
1331     systemClock.baseTime.secs = startupTimeInUnixSecs;
1332     systemClock.baseTime.msecs = 0;
1333 
1334     LogInfo( ( "System time has been initialized to the year %u", democonfigSYSTEM_START_YEAR ) );
1335     printTime( &systemClock.baseTime );
1336 
1337     /* Initialize semaphore for guarding access to system clock variables. */
1338     xMutex = xSemaphoreCreateMutex();
1339     configASSERT( xMutex );
1340 
1341     /* Clear the first time sync completed flag of the system clock object so that a "step" correction
1342      * of system time is utilized for the first time synchronization from a time server. */
1343     systemClock.firstTimeSyncDone = false;
1344 }
1345 
1346 /*-----------------------------------------------------------*/
1347 
initializeSntpClient(SntpContext_t * pContext,const char ** pTimeServers,size_t numOfServers,uint8_t * pContextBuffer,size_t contextBufferSize,NetworkContext_t * pUdpContext,SntpAuthContext_t * pAuthContext)1348 static bool initializeSntpClient( SntpContext_t * pContext,
1349                                   const char ** pTimeServers,
1350                                   size_t numOfServers,
1351                                   uint8_t * pContextBuffer,
1352                                   size_t contextBufferSize,
1353                                   NetworkContext_t * pUdpContext,
1354                                   SntpAuthContext_t * pAuthContext )
1355 {
1356     bool initStatus = false;
1357 
1358     /* Populate the list of time servers. */
1359     SntpServerInfo_t * pServers = pvPortMalloc( sizeof( SntpServerInfo_t ) * numOfServers );
1360 
1361     if( pServers == NULL )
1362     {
1363         LogError( ( "Unable to initialize SNTP client: Malloc failed for memory of configured time servers." ) );
1364     }
1365     else
1366     {
1367         UdpTransportInterface_t udpTransportIntf;
1368         SntpAuthenticationInterface_t symmetricKeyAuthIntf;
1369 
1370         for( uint8_t index = 0; index < numOfServers; index++ )
1371         {
1372             pServers[ index ].pServerName = pTimeServers[ index ];
1373             pServers[ index ].port = SNTP_DEFAULT_SERVER_PORT;
1374         }
1375 
1376         /* Set the UDP transport interface object. */
1377         udpTransportIntf.pUserContext = pUdpContext;
1378         udpTransportIntf.sendTo = UdpTransport_Send;
1379         udpTransportIntf.recvFrom = UdpTransport_Recv;
1380 
1381         /* Set the authentication interface object. */
1382         symmetricKeyAuthIntf.pAuthContext = pAuthContext;
1383         symmetricKeyAuthIntf.generateClientAuth = addClientAuthCode;
1384         symmetricKeyAuthIntf.validateServerAuth = validateServerAuth;
1385 
1386         /* Initialize context. */
1387         Sntp_Init( pContext,
1388                    pServers,
1389                    numOfServers,
1390                    democonfigSERVER_RESPONSE_TIMEOUT_MS,
1391                    pContextBuffer,
1392                    contextBufferSize,
1393                    resolveDns,
1394                    sntpClient_GetTime,
1395                    sntpClient_SetTime,
1396                    &udpTransportIntf,
1397                    &symmetricKeyAuthIntf );
1398 
1399         initStatus = true;
1400     }
1401 
1402     return initStatus;
1403 }
1404 
1405 /*-----------------------------------------------------------*/
1406 
createUdpSocket(Socket_t * pSocket)1407 static bool createUdpSocket( Socket_t * pSocket )
1408 {
1409     bool status = false;
1410     struct freertos_sockaddr bindAddress;
1411 
1412     configASSERT( pSocket != NULL );
1413 
1414     /* Call the FreeRTOS+TCP API to create a UDP socket. */
1415     *pSocket = FreeRTOS_socket( FREERTOS_AF_INET,
1416                                 FREERTOS_SOCK_DGRAM,
1417                                 FREERTOS_IPPROTO_UDP );
1418 
1419     /* Check the socket was created successfully. */
1420     if( *pSocket == FREERTOS_INVALID_SOCKET )
1421     {
1422         /* There was insufficient FreeRTOS heap memory available for the socket
1423          * to be created. */
1424         LogError( ( "Failed to create UDP socket for SNTP client due to insufficient memory." ) );
1425     }
1426     else
1427     {
1428         /* Use a random UDP port for SNTP communication with server for protection against
1429          * spoofing vulnerability from "network off-path" attackers. */
1430         uint16_t randomPort = ( generateRandomNumber() % UINT16_MAX );
1431         bindAddress.sin_port = FreeRTOS_htons( randomPort );
1432         bindAddress.sin_family = FREERTOS_AF_INET;
1433 
1434         if( FreeRTOS_bind( *pSocket, &bindAddress, sizeof( bindAddress ) ) == 0 )
1435         {
1436             /* The bind was successful. */
1437             LogDebug( ( "UDP socket has been bound to port %u", randomPort ) );
1438             status = true;
1439         }
1440         else
1441         {
1442             LogError( ( "Failed to bind UDP socket to port %u", randomPort ) );
1443         }
1444     }
1445 
1446     return status;
1447 }
1448 
1449 /*-----------------------------------------------------------*/
1450 
closeUdpSocket(Socket_t * pSocket)1451 static void closeUdpSocket( Socket_t * pSocket )
1452 {
1453     bool status = false;
1454 
1455     configASSERT( pSocket != NULL );
1456 
1457     FreeRTOS_shutdown( *pSocket, FREERTOS_SHUT_RDWR );
1458 
1459     /* Close the socket again. */
1460     FreeRTOS_closesocket( *pSocket );
1461 }
1462 
1463 /*-----------------------------------------------------------*/
1464 
calculateBackoffForNextPoll(BackoffAlgorithmContext_t * pBackoffContext,bool shouldInitializeContext,uint32_t minPollPeriod,uint32_t * pPollPeriod)1465 static bool calculateBackoffForNextPoll( BackoffAlgorithmContext_t * pBackoffContext,
1466                                          bool shouldInitializeContext,
1467                                          uint32_t minPollPeriod,
1468                                          uint32_t * pPollPeriod )
1469 {
1470     uint16_t newPollPeriod = 0U;
1471     BackoffAlgorithmStatus_t status;
1472 
1473     configASSERT( pBackoffContext != NULL );
1474     configASSERT( pPollPeriod != NULL );
1475 
1476     if( shouldInitializeContext == true )
1477     {
1478         /* Initialize reconnect attempts and interval.*/
1479         BackoffAlgorithm_InitializeParams( pBackoffContext,
1480                                            minPollPeriod,
1481                                            SNTP_DEMO_POLL_MAX_BACKOFF_DELAY_SEC,
1482                                            SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES );
1483     }
1484 
1485     /* Generate a random number and calculate the new backoff poll period to wait before the next
1486      * time poll attempt. */
1487     status = BackoffAlgorithm_GetNextBackoff( pBackoffContext, generateRandomNumber(), &newPollPeriod );
1488 
1489     if( status == BackoffAlgorithmRetriesExhausted )
1490     {
1491         LogError( ( "All backed-off attempts of polling time server have expired: MaxAttempts=%d",
1492                     SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES ) );
1493     }
1494     else
1495     {
1496         /* Store the calculated backoff period as the new poll period. */
1497         *pPollPeriod = newPollPeriod;
1498     }
1499 
1500     return( status == BackoffAlgorithmSuccess );
1501 }
1502 
1503 /*-----------------------------------------------------------*/
1504 
sntpTask(void * pParameters)1505 void sntpTask( void * pParameters )
1506 {
1507     SntpContext_t clientContext;
1508     bool initStatus = false;
1509     CK_RV pkcs11Status;
1510 
1511     /* Validate that the configured lists of time servers, authentication keys and key IDs
1512      * are of the same length. */
1513     configASSERT( numOfServers == ( sizeof( pAESCMACAuthKeys ) / sizeof( pAESCMACAuthKeys[ 0 ] ) ) );
1514     configASSERT( numOfServers == sizeof( pAuthKeyIds ) / sizeof( pAuthKeyIds[ 0 ] ) );
1515 
1516     /* Variable representing the SNTP client context. */
1517     static SntpContext_t context;
1518 
1519     /* Memory for the SNTP packet buffer in the SNTP context. */
1520     static uint8_t contextBuffer[ SNTP_PACKET_AUTHENTICATED_MODE_SIZE ];
1521 
1522     /* Memory for the network context representing the UDP socket that will be
1523      * passed to the SNTP client context. */
1524     static NetworkContext_t udpContext;
1525 
1526     /* Initialize PKCS11 module for cryptographic operations of AES-128-CMAC show
1527      * shown in this demo for authentication mechanism in SNTP communication with server. */
1528     pkcs11Status = xInitializePKCS11();
1529     configASSERT( pkcs11Status == CKR_OK );
1530 
1531     /* Memory for authentication context that will be passed to  the SNTP client context through
1532      * the authentication interface. This represents a combination of the time server and
1533      * its authentication key information that will be utilized for authentication communication
1534      * between client and server, if the server supports authentication. */
1535     static SntpAuthContext_t authContext;
1536 
1537     /* Context used for calculating backoff that is applied to polling interval when the configured
1538      * time server rejects time request.
1539      * Note: Backoff is applied to polling interval ONLY when a single server is configured in the demo
1540      * because in the case of multiple server configurations, the coreSNTP library handles server
1541      * rejection by rotating server. */
1542     static BackoffAlgorithmContext_t backoffContext;
1543 
1544     /* Initialize the authentication context for information for the first time server and its
1545      * keys configured in the demo. */
1546     populateAuthContextForServer( pTimeServers[ 0 ], &authContext );
1547 
1548     initStatus = initializeSntpClient( &clientContext,
1549                                        pTimeServers,
1550                                        numOfServers,
1551                                        contextBuffer,
1552                                        sizeof( contextBuffer ),
1553                                        &udpContext,
1554                                        &authContext );
1555 
1556     if( initStatus == true )
1557     {
1558         SntpStatus_t status;
1559         bool backoffModeFlag = false;
1560 
1561         /* Set the polling interval for periodic time synchronization attempts by the SNTP client. */
1562         systemClock.pollPeriod = democonfigSNTP_CLIENT_POLLING_INTERVAL_SECONDS;
1563 
1564         LogDebug( ( "Minimum SNTP client polling interval calculated as %lus", systemClock.pollPeriod ) );
1565 
1566         LogInfo( ( "Initialized SNTP Client context. Starting SNTP client loop to poll time every %lu seconds",
1567                    systemClock.pollPeriod ) );
1568 
1569         /* The loop of the SNTP Client task that synchronizes system time with a time server (in the configured list of time servers)
1570          * periodically at intervals of polling period. Each iteration of time synchronization is performed by calling the coreSNTP
1571          * APIs for sending time request to the server and receiving time response from the server. */
1572         while( 1 )
1573         {
1574             LogInfo( ( "---------STARTING DEMO---------\r\n" ) );
1575             bool socketStatus = false;
1576 
1577             /* For security, this demo keeps a UDP socket open only for one iteration of SNTP request-response cycle.
1578              * There is a security risk of a UDP socket being flooded with invalid or malicious server response packets
1579              * when a UDP socket is kept open across multiple time polling cycles. In such a scenario where the UDP
1580              * socket buffer has received multiple server response packets from a single time request, the extraneous
1581              * server response present in the UDP socket buffer will prevent the SNTP client application from correctly
1582              * reading network data of server responses that correspond to future time requests.
1583              * By closing the UDP socket after receiving the first acceptable server response (within the server response
1584              * timeout window), any extraneous or malicious server response packets for the same time request will be
1585              * ignored by the demo. */
1586 
1587             /* Wait for Networking */
1588             if( xPlatformIsNetworkUp() == pdFALSE )
1589             {
1590                 LogInfo( ( "Waiting for the network link up event..." ) );
1591 
1592                 while( xPlatformIsNetworkUp() == pdFALSE )
1593                 {
1594                     vTaskDelay( pdMS_TO_TICKS( 1000U ) );
1595                 }
1596             }
1597 
1598             /* Create a UDP socket for the current iteration of time polling. */
1599             socketStatus = createUdpSocket( &udpContext.socket );
1600             configASSERT( socketStatus == true );
1601 
1602             status = Sntp_SendTimeRequest( &clientContext, generateRandomNumber(), democonfigSEND_TIME_REQUEST_TIMEOUT_MS );
1603             configASSERT( status == SntpSuccess );
1604 
1605             /* Wait for server response for a maximum time of server response timeout. */
1606             do
1607             {
1608                 /* Attempt to receive server response each time for a smaller block time
1609                  * than the total duration for the server response to time out. */
1610                 status = Sntp_ReceiveTimeResponse( &clientContext, democonfigRECEIVE_SERVER_RESPONSE_BLOCK_TIME_MS );
1611             } while( status == SntpNoResponseReceived );
1612 
1613             /* Close the UDP socket irrespective of whether a server response is received. */
1614             closeUdpSocket( &udpContext.socket );
1615 
1616             /* Apply back-off delay before the next poll iteration if the demo has been configured with only
1617              * a single time server. */
1618             if( ( status == SntpRejectedResponse ) && ( numOfServers == 1 ) )
1619             {
1620                 bool backoffStatus = false;
1621 
1622                 /* Determine if this is the first back-off attempt we are making since the most recent server rejection
1623                  * for time request. */
1624                 bool firstBackoffAttempt = false;
1625 
1626                 if( backoffModeFlag == false )
1627                 {
1628                     firstBackoffAttempt = true;
1629 
1630                     /* Set the flag to indicate we are in back-off retry mode for requesting time from the server. */
1631                     backoffModeFlag = true;
1632                 }
1633 
1634                 LogInfo( ( "The single configured time server, %s, rejected time request. Backing-off before ",
1635                            "next time poll....", strlen( pTimeServers[ 0 ] ) ) );
1636 
1637                 /* Add exponential back-off to polling period. */
1638                 backoffStatus = calculateBackoffForNextPoll( &backoffContext,
1639                                                              firstBackoffAttempt,
1640                                                              systemClock.pollPeriod,
1641                                                              &systemClock.pollPeriod );
1642                 configASSERT( backoffStatus == true );
1643 
1644                 /* Wait for the increased poll interval before retrying request for time from server. */
1645                 vTaskDelay( pdMS_TO_TICKS( systemClock.pollPeriod * 1000 ) );
1646             }
1647             else
1648             {
1649                 /* Reset flag to indicate that we are not backing-off for the next time poll. */
1650                 backoffModeFlag = false;
1651 
1652                 /* Wait for the poll interval period before the next iteration of time synchronization. */
1653                 vTaskDelay( pdMS_TO_TICKS( systemClock.pollPeriod * 1000 ) );
1654             }
1655         }
1656     }
1657     else
1658     {
1659         configASSERT( false );
1660 
1661         /* Terminate the task as the SNTP client failed to be run. */
1662         LogError( ( "Failed to initialize SNTP client. Terminating SNTP client task.." ) );
1663 
1664         vTaskDelete( NULL );
1665     }
1666 }
1667 
1668 /*-----------------------------------------------------------*/
1669 
systemGetWallClockTime(UTCTime_t * pTime)1670 void systemGetWallClockTime( UTCTime_t * pTime )
1671 {
1672     TickType_t xTickCount = 0;
1673     uint32_t ulTimeMs = 0UL;
1674 
1675     /* Obtain the mutext for accessing system clock variables. */
1676     xSemaphoreTake( xMutex, portMAX_DELAY );
1677 
1678     /* Calculate the current RAM-based time using a mathematical formula using
1679      * system clock state parameters and the time transpired since last synchronization. */
1680     calculateCurrentTime( &systemClock.baseTime,
1681                           systemClock.lastSyncTickCount,
1682                           systemClock.slewRate,
1683                           pTime );
1684 
1685     xSemaphoreGive( xMutex );
1686 }
1687 
1688 /*-----------------------------------------------------------*/
1689