From 73042494bd4864f4379a5454a22c33a52a1f68f5 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Mon, 10 Oct 2022 15:09:01 +0200 Subject: [PATCH] nts: add support for NTP authenticator field using AES-GCM-SIV Add support for SIV algorithms which have maximum nonce length shorter than 16 bytes. --- nts_ntp_auth.c | 19 +++--- nts_ntp_auth.h | 2 +- test/unit/nts_ntp_auth.c | 125 +++++++++++++++++++++++---------------- 3 files changed, 85 insertions(+), 61 deletions(-) diff --git a/nts_ntp_auth.c b/nts_ntp_auth.c index 78e54c2..2f502bb 100644 --- a/nts_ntp_auth.c +++ b/nts_ntp_auth.c @@ -61,23 +61,25 @@ get_padded_length(int length) int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, - const unsigned char *nonce, int nonce_length, + const unsigned char *nonce, int max_nonce_length, const unsigned char *plaintext, int plaintext_length, int min_ef_length) { - int auth_length, ciphertext_length, assoc_length; + int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length; int nonce_padding, ciphertext_padding, additional_padding; unsigned char *ciphertext, *body; struct AuthHeader *header; assert(sizeof (*header) == 4); - if (nonce_length <= 0 || plaintext_length < 0) { + if (max_nonce_length <= 0 || plaintext_length < 0) { DEBUG_LOG("Invalid nonce/plaintext length"); return 0; } assoc_length = info->length; + max_siv_nonce_length = SIV_GetMaxNonceLength(siv); + nonce_length = MIN(max_nonce_length, max_siv_nonce_length); ciphertext_length = SIV_GetTagLength(siv) + plaintext_length; nonce_padding = get_padding_length(nonce_length); ciphertext_padding = get_padding_length(ciphertext_length); @@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, auth_length = sizeof (*header) + nonce_length + nonce_padding + ciphertext_length + ciphertext_padding; additional_padding = MAX(min_ef_length - auth_length - 4, 0); - additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding, - additional_padding); + additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) - + nonce_length - nonce_padding, additional_padding); auth_length += additional_padding; if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length, @@ -127,7 +129,7 @@ int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start, unsigned char *plaintext, int buffer_length, int *plaintext_length) { - int siv_tag_length, nonce_length, ciphertext_length; + int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length; unsigned char *nonce, *ciphertext; int ef_type, ef_body_length; void *ef_body; @@ -155,6 +157,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in nonce = (unsigned char *)(header + 1); ciphertext = nonce + get_padded_length(nonce_length); + max_siv_nonce_length = SIV_GetMaxNonceLength(siv); siv_tag_length = SIV_GetTagLength(siv); if (nonce_length < 1 || @@ -164,8 +167,8 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in return 0; } - if (ef_body_length < sizeof (*header) + - NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) { + if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) + + get_padded_length(ciphertext_length) > ef_body_length) { DEBUG_LOG("Missing padding"); return 0; } diff --git a/nts_ntp_auth.h b/nts_ntp_auth.h index 856beb3..19d04bf 100644 --- a/nts_ntp_auth.h +++ b/nts_ntp_auth.h @@ -32,7 +32,7 @@ #include "siv.h" extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, - const unsigned char *nonce, int nonce_length, + const unsigned char *nonce, int max_nonce_length, const unsigned char *plaintext, int plaintext_length, int min_ef_length); diff --git a/test/unit/nts_ntp_auth.c b/test/unit/nts_ntp_auth.c index 307b93b..207ebeb 100644 --- a/test/unit/nts_ntp_auth.c +++ b/test/unit/nts_ntp_auth.c @@ -34,74 +34,95 @@ test_unit(void) unsigned char key[SIV_MAX_KEY_LENGTH], nonce[256], plaintext[256], plaintext2[256]; NTP_PacketInfo info; NTP_Packet packet; + SIV_Algorithm algo; SIV_Instance siv; int i, j, r, packet_length, nonce_length, key_length; int plaintext_length, plaintext2_length, min_ef_length; - siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256); - TEST_CHECK(siv); + for (algo = 1; algo < 100; algo++) { + siv = SIV_CreateInstance(algo); + if (!siv) { + TEST_CHECK(algo != AEAD_AES_SIV_CMAC_256); + continue; + } - for (i = 0; i < 10000; i++) { - key_length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256); - for (j = 0; j < key_length; j++) - key[j] = random() % 256; - TEST_CHECK(SIV_SetKey(siv, key, key_length)); + DEBUG_LOG("algo=%d", (int)algo); - nonce_length = random() % sizeof (nonce) + 1; - for (j = 0; j < nonce_length; j++) - nonce[j] = random() % 256; + for (i = 0; i < 10000; i++) { + key_length = SIV_GetKeyLength(algo); + for (j = 0; j < key_length; j++) + key[j] = random() % 256; + TEST_CHECK(SIV_SetKey(siv, key, key_length)); - plaintext_length = random() % (sizeof (plaintext) + 1); - for (j = 0; j < plaintext_length; j++) - plaintext[j] = random() % 256; + assert(sizeof (nonce) >= SIV_GetMinNonceLength(siv)); + nonce_length = SIV_GetMinNonceLength(siv) + + random() % (MIN(sizeof (nonce), SIV_GetMaxNonceLength(siv)) - + SIV_GetMinNonceLength(siv) + 1); + for (j = 0; j < nonce_length; j++) + nonce[j] = random() % 256; - packet_length = NTP_HEADER_LENGTH + random() % 100 * 4; - min_ef_length = random() % (sizeof (packet) - packet_length); + plaintext_length = random() % (sizeof (plaintext) + 1); + for (j = 0; j < plaintext_length; j++) + plaintext[j] = random() % 256; - memset(&packet, 0, sizeof (packet)); - packet.lvm = NTP_LVM(0, 4, 0); - memset(&info, 0, sizeof (info)); - info.version = 4; - info.length = packet_length; + packet_length = NTP_HEADER_LENGTH + random() % 100 * 4; + min_ef_length = random() % (sizeof (packet) - packet_length); - DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d", - packet_length, nonce_length, plaintext_length, min_ef_length); + memset(&packet, 0, sizeof (packet)); + packet.lvm = NTP_LVM(0, 4, 0); + memset(&info, 0, sizeof (info)); + info.version = 4; + info.length = packet_length; - r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, - -1, 0); - TEST_CHECK(!r); - r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext, - plaintext_length, 0); - TEST_CHECK(!r); - r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, - plaintext_length, sizeof (packet) - info.length + 1); - TEST_CHECK(!r); + DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d", + packet_length, nonce_length, plaintext_length, min_ef_length); - r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, - plaintext_length, min_ef_length); - TEST_CHECK(r); - TEST_CHECK(info.length - packet_length >= min_ef_length); + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + -1, 0); + TEST_CHECK(!r); + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext, + plaintext_length, 0); + TEST_CHECK(!r); + if (SIV_GetMinNonceLength(siv) > 1) { + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMinNonceLength(siv) - 1, + plaintext, plaintext_length, 0); + TEST_CHECK(!r); + } + if (SIV_GetMaxNonceLength(siv) <= sizeof (nonce)) { + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMaxNonceLength(siv) - 1, + plaintext, plaintext_length, 0); + TEST_CHECK(!r); + } + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + plaintext_length, sizeof (packet) - info.length + 1); + TEST_CHECK(!r); - r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, - -1, &plaintext2_length); - TEST_CHECK(!r); + r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext, + plaintext_length, min_ef_length); + TEST_CHECK(r); + TEST_CHECK(info.length - packet_length >= min_ef_length); - r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, - sizeof (plaintext2), &plaintext2_length); - TEST_CHECK(r); - TEST_CHECK(plaintext_length == plaintext2_length); - TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0); + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + -1, &plaintext2_length); + TEST_CHECK(!r); - j = random() % (packet_length + plaintext_length + - nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4; - ((unsigned char *)&packet)[j]++; - r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, - sizeof (plaintext2), &plaintext2_length); - TEST_CHECK(!r); - ((unsigned char *)&packet)[j]--; + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + sizeof (plaintext2), &plaintext2_length); + TEST_CHECK(r); + TEST_CHECK(plaintext_length == plaintext2_length); + TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0); + + j = random() % (packet_length + plaintext_length + + nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4; + ((unsigned char *)&packet)[j]++; + r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2, + sizeof (plaintext2), &plaintext2_length); + TEST_CHECK(!r); + ((unsigned char *)&packet)[j]--; + } + + SIV_DestroyInstance(siv); } - - SIV_DestroyInstance(siv); } #else void