From b550e6090d26a19dc72b390f64e7b6573c214ef1 Mon Sep 17 00:00:00 2001 From: Archit Aggarwal Date: Thu, 15 Jul 2021 15:21:29 -0700 Subject: Add note in coreSNTP demo about Network Time Security (NTS) (#648) This PR makes the following changes in the coreSNTP demo: To suggest the most secure way of using SNTP communication, we are adding a documentation note about the Network Time Security in the SNTP demo. Hygiene update of using the FreeRTOS/backoffAlgorithm utility library for calculating time polling period interval backoff when time requests are rejected by a time server. --- .../DemoTasks/SNTPClientTask.c | 149 +++++++++++++++++++-- .../Demo/coreSNTP_Windows_Simulator/demo_config.h | 7 + lexicon.txt | 5 + 3 files changed, 149 insertions(+), 12 deletions(-) diff --git a/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/DemoTasks/SNTPClientTask.c b/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/DemoTasks/SNTPClientTask.c index 7a84f56de..a3497d463 100755 --- a/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/DemoTasks/SNTPClientTask.c +++ b/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/DemoTasks/SNTPClientTask.c @@ -31,13 +31,22 @@ * * This file contains the SNTP client (daemon) task as well as functionality for * maintaining wall-clock or UTC time in RAM. The SNTP client periodically synchronizes - * system clock with an SNTP/NTP servers. Any other task running an application in the + * system clock with SNTP/NTP server(s). Any other task running an application in the * system can query the system time. For an example of an application task querying time * from the system, refer to the SampleAppTask.c file in this project. * - * !!! NOTE !!! - * This SNTP demo does not authenticate the server nor the client. - * Hence, this demo should not be used as production ready code. + * This demo shows how the coreSNTP library can be used to communicate with SNTP/NTP + * servers in a mutually authenticated through the use of symmetric-key based AES-128-CMAC + * algorithm. To run this demo with an SNTP/NTP server in authenticated mode, the AES-128-CMAC + * symmetric key needs to be pre-shared between the client (i.e. this demo) and the server. + * + * !!!Note!!!: + * Even though this demo shows the use of AES-128-CMAC, a symmetric-key cryptographic based + * solution, for authenticating SNTP communication between the demo (SNTP client) and + * SNTP/NTP server, we instead RECOMMEND that production devices use the most secure authentication + * mechanism alternative available with the Network Time Security (NTS) protocol, an asymmetric-key + * cryptographic protocol. For more information, refer to the NTS specification here: + * https://datatracker.ietf.org/doc/html/rfc8915 */ /* Standard includes. */ @@ -70,6 +79,9 @@ #include "core_pkcs11_config.h" #include "core_pkcs11.h" +/* Backoff Algorithm include. */ +#include "backoff_algorithm.h" + /*-----------------------------------------------------------*/ /* Compile time error for undefined configs. */ @@ -120,7 +132,7 @@ * @note The size of the buffer MUST be large enough to hold an entire SNTP packet, which includes the standard SNTP * packet data of 48 bytes and authentication data for security mechanism, if used, in communication with time server. */ -#define SNTP_CONTEXT_NETWORK_BUFFER_SIZE ( SNTP_PACKET_BASE_SIZE ) +#define SNTP_CONTEXT_NETWORK_BUFFER_SIZE ( SNTP_PACKET_BASE_SIZE ) /** * @brief The constant for storing the number of milliseconds per FreeRTOS tick in the system. @@ -129,13 +141,13 @@ * internet time or UTC time. Thus, the actual time duration value per tick of the system will be * larger from the perspective of internet time. */ -#define MILLISECONDS_PER_TICK ( 1000 / configTICK_RATE_HZ ) +#define MILLISECONDS_PER_TICK ( 1000 / configTICK_RATE_HZ ) /** * @brief The fixed size of the key for the AES-128-CMAC algorithm used for authenticating communication * between the time server and the client. */ -#define AES_CMAC_AUTHENTICATION_KEY_SIZE ( 16 ) +#define AES_CMAC_AUTHENTICATION_KEY_SIZE ( 16 ) /** * @brief The size of the "Key Identifier" field in the SNTP packet when symmetric key authentication mode as @@ -147,7 +159,7 @@ * @note This demo uses the "Key Identifier" field to communicate with time servers that support authentication mechanism. * This field is stored with the Key ID of the AES-128-CMAC based authentication key stored in the time server. */ -#define SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH 4 +#define SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH 4 /** * @brief The offset for the starting byte of the "Key Identifier" field in an SNTPv4/NTPv4 packet. @@ -156,7 +168,7 @@ * For more information of the SNTP packet format, refer to the SNTPv4 specification * https://datatracker.ietf.org/doc/html/rfc4330#page-8 */ -#define SNTP_PACKET_SYMMETRIC_KEY_ID_OFFSET SNTP_PACKET_BASE_SIZE +#define SNTP_PACKET_SYMMETRIC_KEY_ID_OFFSET SNTP_PACKET_BASE_SIZE /** * @brief The total size of an SNTP packet (which remains same for both client request and server response in SNTP communication) @@ -168,7 +180,26 @@ * For more information of the SNTP packet format, refer to the SNTPv4 specification * https://datatracker.ietf.org/doc/html/rfc4330#page-8 */ -#define SNTP_PACKET_AUTHENTICATED_MODE_SIZE ( SNTP_PACKET_BASE_SIZE + SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH + pkcs11AES_CMAC_SIGNATURE_LENGTH ) +#define SNTP_PACKET_AUTHENTICATED_MODE_SIZE ( SNTP_PACKET_BASE_SIZE + SNTP_PACKET_SYMMETRIC_KEY_ID_LENGTH + pkcs11AES_CMAC_SIGNATURE_LENGTH ) + +/** + * @brief The maximum poll period that the SNTP client can use as back-off on receiving a rejection from a time server. + * + * @note This demo performs back-off in polling rate from time server ONLY for the case when a single time server being + * is configured through the democonfigLIST_OF_TIME_SERVERS macro. + * This is because when more than one time server is configured, the coreSNTP library automatically handles the case + * of server rejection of time request by rotating to the next configured server for subsequent time polling requests. + */ +#define SNTP_DEMO_POLL_MAX_BACKOFF_DELAY_SEC UINT16_MAX + +/** + * @brief The maximum number of times of retrying time requests at exponentially backed-off polling frequency + * from a server that rejects time requests. + * + * @note This macro is only relevant for the case when a single time server is configured in + * the demo through, democonfigLIST_OF_TIME_SERVERS. + */ +#define SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES 10 /*-----------------------------------------------------------*/ @@ -593,6 +624,31 @@ static bool createUdpSocket( Socket_t * pSocket ); */ static void closeUdpSocket( Socket_t * pSocket ); +/** + * @brief Utility to calculate new poll period with exponential backoff and jitter + * algorithm. + * + * @note The demo applies time polling frequency backoff only when a single time server + * is configured, through the democonfigLIST_OF_SERVERS macro, and the single server + * rejects time requests. + * + * @param[in, out] pContext The context representing the back-off parameters. This + * context is initialized by the function whenever the caller indicates it with the + * @p shouldInitializeContext flag. + * @param[in] shouldInitializeContext Flag to indicate if the passed context should be + * initialized to start a new sequence of backed-off time request retries. + * @param[in] minPollPeriod The minimum poll period + * @param[in] pPollPeriod The new calculated poll period. + * + * @return Return #true if a new poll interval is calculated to retry time request + * from the server; #false otherwise to indicate exhaustion of time request retry attempts + * with the server. + */ +static bool calculateBackoffForNextPoll( BackoffAlgorithmContext_t * pContext, + bool shouldInitializeContext, + uint32_t minPollPeriod, + uint32_t * pPollPeriod ); + /*------------------------------------------------------------------------------*/ static uint32_t translateYearToUnixSeconds( uint16_t year ) @@ -1379,6 +1435,46 @@ static void closeUdpSocket( Socket_t * pSocket ) /*-----------------------------------------------------------*/ +static bool calculateBackoffForNextPoll( BackoffAlgorithmContext_t * pBackoffContext, + bool shouldInitializeContext, + uint32_t minPollPeriod, + uint32_t * pPollPeriod ) +{ + uint16_t newPollPeriod = 0U; + BackoffAlgorithmStatus_t status; + + configASSERT( pBackoffContext != NULL ); + configASSERT( pPollPeriod != NULL ); + + if( shouldInitializeContext == true ) + { + /* Initialize reconnect attempts and interval.*/ + BackoffAlgorithm_InitializeParams( &pBackoffContext, + minPollPeriod, + SNTP_DEMO_POLL_MAX_BACKOFF_DELAY_SEC, + SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES ); + } + + /* Generate a random number and calculate the new backoff poll period to wait before the next + * time poll attempt. */ + status = BackoffAlgorithm_GetNextBackoff( &pBackoffContext, generateRandomNumber(), &newPollPeriod ); + + if( status == BackoffAlgorithmRetriesExhausted ) + { + LogError( ( "All backed-off attempts of polling time server have expired: MaxAttempts=%d", + SNTP_DEMO_MAX_SERVER_BACKOFF_RETRIES ) ); + } + else + { + /* Store the calculated backoff period as the new poll period. */ + *pPollPeriod = newPollPeriod; + } + + return( status == BackoffAlgorithmSuccess ); +} + +/*-----------------------------------------------------------*/ + void sntpTask( void * pParameters ) { SntpContext_t clientContext; @@ -1411,6 +1507,13 @@ void sntpTask( void * pParameters ) * between client and server, if the server supports authentication. */ static SntpAuthContext_t authContext; + /* Context used for calculating backoff that is applied to polling interval when the configured + * time server rejects time request. + * Note: Backoff is applied to polling interval ONLY when a single server is configured in the demo + * because in the case of multiple server configurations, the coreSNTP library handles server + * rejection by rotating server. */ + static BackoffAlgorithmContext_t backoffContext; + /* Initialize the authentication context for information for the first time server and its * keys configured in the demo. */ populateAuthContextForServer( pTimeServers[ 0 ], &authContext ); @@ -1426,6 +1529,7 @@ void sntpTask( void * pParameters ) if( initStatus == true ) { SntpStatus_t status; + bool backoffModeFlag = false; /* Calculate Poll interval of SNTP client based on desired accuracy and clock tolerance of the system. */ status = Sntp_CalculatePollInterval( democonfigSYSTEM_CLOCK_TOLERANCE_PPM, @@ -1433,7 +1537,7 @@ void sntpTask( void * pParameters ) &systemClock.pollPeriod ); configASSERT( status == SntpSuccess ); - LogDebug( ( "SNTP client polling interval calculated as %lus", systemClock.pollPeriod ) ); + LogDebug( ( "Minimum SNTP client polling interval calculated as %lus", systemClock.pollPeriod ) ); LogInfo( ( "Initialized SNTP Client context. Starting SNTP client loop to poll time every %lu seconds", systemClock.pollPeriod ) ); @@ -1475,17 +1579,38 @@ void sntpTask( void * pParameters ) * a single time server. */ if( ( status == SntpRejectedResponse ) && ( numOfServers == 1 ) ) { + bool backoffStatus = false; + + /* Determine if this is the first back-off attempt we are making since the most recent server rejection + * for time request. */ + bool firstBackoffAttempt = false; + + if( backoffModeFlag == false ) + { + firstBackoffAttempt = true; + + /* Set the flag to indicate we are in back-off retry mode for requesting time from the server. */ + backoffModeFlag = true; + } + LogInfo( ( "The single configured time server, %s, rejected time request. Backing-off before ", "next time poll....", strlen( pTimeServers[ 0 ] ) ) ); /* Add exponential back-off to polling period. */ - systemClock.pollPeriod *= 2; + backoffStatus = calculateBackoffForNextPoll( &backoffContext, + firstBackoffAttempt, + systemClock.pollPeriod, + &systemClock.pollPeriod ); + configASSERT( backoffStatus == true ); /* Wait for the increased poll interval before retrying request for time from server. */ vTaskDelay( pdMS_TO_TICKS( systemClock.pollPeriod * 1000 ) ); } else { + /* Reset flag to indicate that we are not backing-off for the next time poll. */ + backoffModeFlag = false; + /* Wait for the poll interval period before the next iteration of time synchronization. */ vTaskDelay( pdMS_TO_TICKS( systemClock.pollPeriod * 1000 ) ); } diff --git a/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/demo_config.h b/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/demo_config.h index b3fd9a058..3f62b4dc9 100755 --- a/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/demo_config.h +++ b/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator/demo_config.h @@ -109,6 +109,13 @@ extern void vLoggingPrintf( const char * pcFormatString, * It is RECOMMENDED to use an authentication mechanism for protecting devices against server spoofing * attacks. * + * @note Even though this demo shows the use of AES-128-CMAC, a symmetric-key cryptographic based + * solution, for authenticating SNTP communication between the demo (SNTP client) and + * SNTP/NTP server, we instead RECOMMEND that production devices use the most secure authentication + * mechanism alternative available with the Network Time Security (NTS) protocol, an asymmetric-key + * cryptographic protocol. For more information, refer to the NTS specification here: + * https://datatracker.ietf.org/doc/html/rfc8915 + * * @note Please provide the 128-bit keys as comma separated list of hexadecimal strings in the order matching * the list of time servers configured in democonfigLIST_OF_TIME_SERVERS configuration. If a time server does * not support authentication, then NULL should be used to indicate use of no authentication mechanism for the diff --git a/lexicon.txt b/lexicon.txt index 887e1f091..2db8f8a84 100644 --- a/lexicon.txt +++ b/lexicon.txt @@ -705,6 +705,7 @@ filteration findfirst findobjects fips +firstbackoffattempt fixme flase flasg @@ -1226,6 +1227,7 @@ mingw minilistitem minimise minimised +minpollperiod mips misc misra @@ -1325,6 +1327,7 @@ nsec nt ntp ntpdemo +nts nullptr num numaker @@ -1611,6 +1614,7 @@ ppass ppassword ppc ppcmessagetodisplay +ppollperiod ppr pprivatekey ppublishinfo @@ -2206,6 +2210,7 @@ shadownamelength shadowstatus shasum shigherprioritytaskwoken +shouldinitializecontext shouldn shtml sice -- cgit v1.2.1