int compliant_128gcm;
NKE_Context context;
+ NKE_Context alt_context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
assert(sizeof (uint16_t) == 2);
inst->compliant_128gcm = 0;
+ inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0;
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
context incorrectly for compatibility with older chrony servers unless
- the server confirmed support for the compliant context */
- if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm)
+ the server confirmed support for the compliant context. Generate both
+ sets of keys in case the server uses the compliant context, but does not
+ support the negotiation record, assuming it will respond with an NTS NAK
+ to a request authenticated with the noncompliant key. */
+ if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm) {
+ inst->alt_context.algorithm = inst->context.algorithm;
+ if (!NKSN_GetKeys(inst->session, inst->alt_context.algorithm, exporter_algorithm,
+ NKE_NEXT_PROTOCOL_NTPV4, &inst->alt_context.c2s, &inst->alt_context.s2c))
+ return 0;
+
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
+ }
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
/* ================================================== */
int
-NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
+NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
return 0;
*context = inst->context;
+ *alt_context = inst->alt_context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
-extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
+extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
double last_nke_success;
NKE_Context context;
+ NKE_Context alt_context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
+ memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0;
if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
+
+ /* Before dropping the cookies, check whether there is an alternate set of
+ keys available (exported with the compliant context for AES-128-GCM-SIV)
+ and the NAK was the only valid response after the last NTS-KE session,
+ indicating we use incorrect keys and switching to the other set of keys
+ for the following NTP requests might work */
+ if (inst->alt_context.algorithm != AEAD_SIV_INVALID &&
+ inst->alt_context.algorithm == inst->context.algorithm &&
+ inst->nke_attempts > 0 && inst->nak_response && !inst->ok_response) {
+ inst->context = inst->alt_context;
+ inst->alt_context.algorithm = AEAD_SIV_INVALID;
+ DEBUG_LOG("Switched to compliant keys");
+ return 1;
+ }
+
inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies");
}
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */
- got_data = NKC_GetNtsData(inst->nke, &inst->context,
+ got_data = NKC_GetNtsData(inst->nke, &inst->context, &inst->alt_context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
new NTS-KE session to be started as soon as the cookies run out. */
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
+ inst->alt_context.algorithm = AEAD_SIV_INVALID;
return 1;
}
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error;
+ inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
fclose(f);
memset(&inst->context, 0, sizeof (inst->context));
+ memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->num_cookies = 0;
}
/* Identifiers of SIV algorithms following the IANA AEAD registry */
typedef enum {
+ AEAD_SIV_INVALID = 0,
AEAD_AES_SIV_CMAC_256 = 15,
AEAD_AES_SIV_CMAC_384 = 16,
AEAD_AES_SIV_CMAC_512 = 17,
#define NKC_IsActive(inst) (random() % 2)
#define NKC_GetRetryFactor(inst) (1)
-static int get_nts_data(NKC_Instance inst, NKE_Context *context,
+static int get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
#define NKC_GetNtsData get_nts_data
#include <nts_ntp_client.c>
static int
-get_nts_data(NKC_Instance inst, NKE_Context *context,
+get_nts_data(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
context->s2c.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
+ if (random() % 2) {
+ *alt_context = *context;
+ UTI_GetRandomBytes(alt_context->c2s.key, alt_context->c2s.length);
+ UTI_GetRandomBytes(alt_context->s2c.key, alt_context->s2c.length);
+ } else {
+ alt_context->algorithm = AEAD_SIV_INVALID;
+ }
+
*num_cookies = random() % max_cookies + 1;
for (i = 0; i < *num_cookies; i++) {
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);