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 ¤tTime );
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