From cc706b50b9f84715eba80ee99bbe3a166dad01c7 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Mon, 10 Oct 2022 16:35:20 +0200 Subject: [PATCH] nts: add client support for authentication with AES-128-GCM-SIV If AES-128-GCM-SIV is available on the client, add it to the requested algorithms in NTS-KE as the first (preferred) entry. If supported on the server, it will make the cookies shorter, which will get the length of NTP messages containing only one cookie below 200 octets. This should make NTS more reliable in networks where longer NTP packets are filtered as a mitigation against amplification attacks exploiting the ntpd mode 6/7 protocol. --- doc/chronyc.adoc | 1 + nts_ke_client.c | 24 ++++++++++++++++-------- test/unit/nts_ke_client.c | 7 +++++-- test/unit/nts_ntp_client.c | 30 ++++++++++++++++++++++++------ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc index 9f4e804..57300ee 100644 --- a/doc/chronyc.adoc +++ b/doc/chronyc.adoc @@ -610,6 +610,7 @@ be reported: * 13: AES128 * 14: AES256 * 15: AEAD-AES-SIV-CMAC-256 +* 30: AEAD-AES-128-GCM-SIV *KLen*::: This column shows the length of the key in bits. *Last*::: diff --git a/nts_ke_client.c b/nts_ke_client.c index 2ad8106..3891f71 100644 --- a/nts_ke_client.c +++ b/nts_ke_client.c @@ -102,16 +102,22 @@ static int prepare_request(NKC_Instance inst) { NKSN_Instance session = inst->session; - uint16_t datum; + uint16_t data[2]; + int length; NKSN_BeginMessage(session); - datum = htons(NKE_NEXT_PROTOCOL_NTPV4); - if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) + data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0]))) return 0; - datum = htons(AEAD_AES_SIV_CMAC_256); - if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) + length = 0; + if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0) + data[length++] = htons(AEAD_AES_128_GCM_SIV); + if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0) + data[length++] = htons(AEAD_AES_SIV_CMAC_256); + if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, + length * sizeof (data[0]))) return 0; if (!NKSN_EndMessage(session)) @@ -159,12 +165,14 @@ process_response(NKC_Instance inst) next_protocol = NKE_NEXT_PROTOCOL_NTPV4; break; case NKE_RECORD_AEAD_ALGORITHM: - if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) { + if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 && + ntohs(data[0]) != AEAD_AES_128_GCM_SIV) || + SIV_GetKeyLength(ntohs(data[0])) <= 0) { DEBUG_LOG("Unexpected NTS-KE AEAD algorithm"); error = 1; break; } - aead_algorithm = AEAD_AES_SIV_CMAC_256; + aead_algorithm = ntohs(data[0]); inst->context.algorithm = aead_algorithm; break; case NKE_RECORD_ERROR: @@ -236,7 +244,7 @@ process_response(NKC_Instance inst) if (error || inst->num_cookies == 0 || next_protocol != NKE_NEXT_PROTOCOL_NTPV4 || - aead_algorithm != AEAD_AES_SIV_CMAC_256) + aead_algorithm < 0) return 0; return 1; diff --git a/test/unit/nts_ke_client.c b/test/unit/nts_ke_client.c index 72690bf..3100d1d 100644 --- a/test/unit/nts_ke_client.c +++ b/test/unit/nts_ke_client.c @@ -64,9 +64,12 @@ prepare_response(NKSN_Instance session, int valid) if (index != 5) { if (index == 6) - data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1); + do { + data[0] = htons(random() % 100); + } while (SIV_GetKeyLength(ntohs(data[0])) > 0); else - data[0] = htons(AEAD_AES_SIV_CMAC_256); + data[0] = htons(random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ? + AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256); if (index == 7) length = 3 + random() % 10; TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length)); diff --git a/test/unit/nts_ntp_client.c b/test/unit/nts_ntp_client.c index c14fd41..7953413 100644 --- a/test/unit/nts_ntp_client.c +++ b/test/unit/nts_ntp_client.c @@ -50,7 +50,10 @@ get_nts_data(NKC_Instance inst, NKE_Context *context, if (random() % 2) return 0; - context->algorithm = AEAD_AES_SIV_CMAC_256; + do { + context->algorithm = AEAD_AES_SIV_CMAC_256 + random() % + (AEAD_AES_256_GCM_SIV - AEAD_AES_SIV_CMAC_256 + 10); + } while (SIV_GetKeyLength(context->algorithm) <= 0); context->c2s.length = SIV_GetKeyLength(context->algorithm); UTI_GetRandomBytes(context->c2s.key, context->c2s.length); @@ -75,9 +78,9 @@ static int get_request(NNC_Instance inst) { unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH]; + int nonce_length, expected_length, req_cookies; NTP_PacketInfo info; NTP_Packet packet; - int expected_length, req_cookies; memset(&packet, 0, sizeof (packet)); memset(&info, 0, sizeof (info)); @@ -100,6 +103,17 @@ get_request(NNC_Instance inst) TEST_CHECK(inst->num_cookies > 0); TEST_CHECK(inst->siv); + switch (inst->context.algorithm) { + case AEAD_AES_SIV_CMAC_256: + nonce_length = 16; + break; + case AEAD_AES_128_GCM_SIV: + nonce_length = 12; + break; + default: + assert(0); + } + memcpy(nonce, inst->nonce, sizeof (nonce)); memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id)); TEST_CHECK(NNC_PrepareForAuth(inst)); @@ -111,9 +125,10 @@ get_request(NNC_Instance inst) (inst->cookies[inst->cookie_index].length + 4)); expected_length = info.length + 4 + sizeof (inst->uniq_id) + req_cookies * (4 + inst->cookies[inst->cookie_index].length) + - 4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv); - DEBUG_LOG("length=%d cookie_length=%d expected_length=%d", - info.length, inst->cookies[inst->cookie_index].length, expected_length); + 4 + 4 + nonce_length + SIV_GetTagLength(inst->siv); + DEBUG_LOG("algo=%d length=%d cookie_length=%d expected_length=%d", + (int)inst->context.algorithm, info.length, + inst->cookies[inst->cookie_index].length, expected_length); if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH && inst->cookies[inst->cookie_index].length % 4 == 0 && @@ -162,7 +177,10 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in return; } - nonce_length = random() % (sizeof (nonce)) + 1; + nonce_length = SIV_GetMinNonceLength(inst->siv) + random() % + (MIN(SIV_GetMaxNonceLength(inst->siv), sizeof (nonce)) - + SIV_GetMinNonceLength(inst->siv) + 1); + assert(nonce_length >= 1 && nonce_length <= sizeof (nonce)); do { cookie_length = random() % (sizeof (cookie) + 1);