nts: switch client to compliant key exporter on NTS NAK
Implement a fallback for the NTS-NTP client to switch to the compliant AES-128-GCM-SIV exporter context when the server is using the compliant context, but does not support the new NTS-KE record negotiating its use, assuming it can respond with an NTS NAK to the request authenticated with the incorrect key. Export both sets of keys when processing the NTS-KE response. If an NTS NAK is the only valid response from the server after the last NTS-KE session, switch to the keys exported with the compliant context for the following requests instead of dropping all cookies and restarting NTS-KE. Don't switch back to the original keys if an NTS NAK is received again.
This commit is contained in:
parent
0707865413
commit
689605b6a2
5 changed files with 48 additions and 7 deletions
|
@ -52,6 +52,7 @@ struct NKC_Instance_Record {
|
||||||
|
|
||||||
int compliant_128gcm;
|
int compliant_128gcm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
||||||
|
@ -149,6 +150,7 @@ process_response(NKC_Instance inst)
|
||||||
assert(sizeof (uint16_t) == 2);
|
assert(sizeof (uint16_t) == 2);
|
||||||
|
|
||||||
inst->compliant_128gcm = 0;
|
inst->compliant_128gcm = 0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
||||||
inst->ntp_address.port = 0;
|
inst->ntp_address.port = 0;
|
||||||
|
@ -286,9 +288,18 @@ handle_message(void *arg)
|
||||||
|
|
||||||
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
context incorrectly for compatibility with older chrony servers unless
|
context incorrectly for compatibility with older chrony servers unless
|
||||||
the server confirmed support for the compliant context */
|
the server confirmed support for the compliant context. Generate both
|
||||||
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm)
|
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;
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
}
|
||||||
|
|
||||||
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
||||||
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
|
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
|
||||||
|
@ -456,7 +467,7 @@ NKC_IsActive(NKC_Instance inst)
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
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,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address)
|
IPSockAddr *ntp_address)
|
||||||
{
|
{
|
||||||
|
@ -466,6 +477,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
*context = inst->context;
|
*context = inst->context;
|
||||||
|
*alt_context = inst->alt_context;
|
||||||
|
|
||||||
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
||||||
cookies[i] = inst->cookies[i];
|
cookies[i] = inst->cookies[i];
|
||||||
|
|
|
@ -46,7 +46,7 @@ extern int NKC_Start(NKC_Instance inst);
|
||||||
extern int NKC_IsActive(NKC_Instance inst);
|
extern int NKC_IsActive(NKC_Instance inst);
|
||||||
|
|
||||||
/* Get the NTS data if the session was successful */
|
/* 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,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address);
|
IPSockAddr *ntp_address);
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ struct NNC_Instance_Record {
|
||||||
double last_nke_success;
|
double last_nke_success;
|
||||||
|
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
unsigned int context_id;
|
unsigned int context_id;
|
||||||
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
|
@ -105,6 +106,7 @@ reset_instance(NNC_Instance inst)
|
||||||
inst->last_nke_success = 0.0;
|
inst->last_nke_success = 0.0;
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->context_id = 0;
|
inst->context_id = 0;
|
||||||
memset(inst->cookies, 0, sizeof (inst->cookies));
|
memset(inst->cookies, 0, sizeof (inst->cookies));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
|
@ -165,6 +167,21 @@ check_cookies(NNC_Instance inst)
|
||||||
if (inst->num_cookies > 0 &&
|
if (inst->num_cookies > 0 &&
|
||||||
((inst->nak_response && !inst->ok_response) ||
|
((inst->nak_response && !inst->ok_response) ||
|
||||||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
|
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;
|
inst->num_cookies = 0;
|
||||||
DEBUG_LOG("Dropped cookies");
|
DEBUG_LOG("Dropped cookies");
|
||||||
}
|
}
|
||||||
|
@ -261,7 +278,7 @@ get_cookies(NNC_Instance inst)
|
||||||
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_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 */
|
/* 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,
|
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
||||||
&ntp_address);
|
&ntp_address);
|
||||||
|
|
||||||
|
@ -520,6 +537,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
||||||
new NTS-KE session to be started as soon as the cookies run out. */
|
new NTS-KE session to be started as soon as the cookies run out. */
|
||||||
inst->nke_attempts = 0;
|
inst->nke_attempts = 0;
|
||||||
inst->next_nke_attempt = 0.0;
|
inst->next_nke_attempt = 0.0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -643,6 +661,7 @@ load_cookies(NNC_Instance inst)
|
||||||
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->context.algorithm = algorithm;
|
inst->context.algorithm = algorithm;
|
||||||
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
||||||
sizeof (inst->context.s2c.key));
|
sizeof (inst->context.s2c.key));
|
||||||
|
@ -687,6 +706,7 @@ error:
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
siv.h
1
siv.h
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
/* Identifiers of SIV algorithms following the IANA AEAD registry */
|
/* Identifiers of SIV algorithms following the IANA AEAD registry */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
AEAD_SIV_INVALID = 0,
|
||||||
AEAD_AES_SIV_CMAC_256 = 15,
|
AEAD_AES_SIV_CMAC_256 = 15,
|
||||||
AEAD_AES_SIV_CMAC_384 = 16,
|
AEAD_AES_SIV_CMAC_384 = 16,
|
||||||
AEAD_AES_SIV_CMAC_512 = 17,
|
AEAD_AES_SIV_CMAC_512 = 17,
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#define NKC_IsActive(inst) (random() % 2)
|
#define NKC_IsActive(inst) (random() % 2)
|
||||||
#define NKC_GetRetryFactor(inst) (1)
|
#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,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address);
|
IPSockAddr *ntp_address);
|
||||||
#define NKC_GetNtsData get_nts_data
|
#define NKC_GetNtsData get_nts_data
|
||||||
|
@ -41,7 +41,7 @@ static int get_nts_data(NKC_Instance inst, NKE_Context *context,
|
||||||
#include <nts_ntp_client.c>
|
#include <nts_ntp_client.c>
|
||||||
|
|
||||||
static int
|
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,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address)
|
IPSockAddr *ntp_address)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,14 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
|
||||||
context->s2c.length = SIV_GetKeyLength(context->algorithm);
|
context->s2c.length = SIV_GetKeyLength(context->algorithm);
|
||||||
UTI_GetRandomBytes(context->s2c.key, context->s2c.length);
|
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;
|
*num_cookies = random() % max_cookies + 1;
|
||||||
for (i = 0; i < *num_cookies; i++) {
|
for (i = 0; i < *num_cookies; i++) {
|
||||||
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
|
cookies[i].length = random() % (sizeof (cookies[i].cookie) + 1);
|
||||||
|
|
Loading…
Reference in a new issue