diff --git a/nts_ke_server.c b/nts_ke_server.c index 948058a..4ec00be 100644 --- a/nts_ke_server.c +++ b/nts_ke_server.c @@ -427,8 +427,9 @@ process_request(NKSN_Instance session) for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { aead_algorithm_values++; - if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256) - aead_algorithm = AEAD_AES_SIV_CMAC_256; + /* Use the first supported algorithm */ + if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0) + aead_algorithm = ntohs(data[i]);; } break; case NKE_RECORD_ERROR: @@ -862,11 +863,9 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie) return 0; } - /* The algorithm is hardcoded for now */ - if (context->algorithm != AEAD_AES_SIV_CMAC_256) { - DEBUG_LOG("Unexpected SIV algorithm"); - return 0; - } + /* The AEAD ID is not encoded in the cookie. It is implied from the key + length (as long as only algorithms with different key lengths are + supported). */ if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH || context->s2c.length != context->c2s.length) { @@ -954,7 +953,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context) return 0; } - context->algorithm = AEAD_AES_SIV_CMAC_256; + /* Select a supported algorithm corresponding to the key length, avoiding + potentially slow SIV_GetKeyLength() */ + switch (plaintext_length / 2) { + case 16: + context->algorithm = AEAD_AES_128_GCM_SIV; + break; + case 32: + context->algorithm = AEAD_AES_SIV_CMAC_256; + break; + default: + DEBUG_LOG("Unknown key length"); + return 0; + } context->c2s.length = plaintext_length / 2; context->s2c.length = plaintext_length / 2; diff --git a/nts_ntp_server.c b/nts_ntp_server.c index 9226c89..7db3998 100644 --- a/nts_ntp_server.c +++ b/nts_ntp_server.c @@ -41,13 +41,15 @@ #include "siv.h" #include "util.h" -#define SERVER_SIV AEAD_AES_SIV_CMAC_256 +#define MAX_SERVER_SIVS 2 struct NtsServer { - SIV_Instance siv; + SIV_Instance sivs[MAX_SERVER_SIVS]; + SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH]; NKE_Cookie cookies[NTS_MAX_COOKIES]; int num_cookies; + int siv_index; NTP_int64 req_tx; }; @@ -60,6 +62,7 @@ void NNS_Initialise(void) { const char **certs, **keys; + int i; /* Create an NTS-NTP server instance only if NTS-KE server is enabled */ if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) { @@ -68,9 +71,17 @@ NNS_Initialise(void) } server = Malloc(sizeof (struct NtsServer)); - server->siv = SIV_CreateInstance(SERVER_SIV); - if (!server->siv) - LOG_FATAL("Could not initialise SIV cipher"); + + server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256; + server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV; + assert(MAX_SERVER_SIVS == 2); + + for (i = 0; i < 2; i++) + server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]); + + /* AES-SIV-CMAC-256 is required on servers */ + if (!server->sivs[0]) + LOG_FATAL("Missing AES-SIV-CMAC-256"); } /* ================================================== */ @@ -78,10 +89,15 @@ NNS_Initialise(void) void NNS_Finalise(void) { + int i; + if (!server) return; - SIV_DestroyInstance(server->siv); + for (i = 0; i < MAX_SERVER_SIVS; i++) { + if (server->sivs[i]) + SIV_DestroyInstance(server->sivs[i]); + } Free(server); server = NULL; } @@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; NKE_Context context; NKE_Cookie cookie; + SIV_Instance siv; void *ef_body; *kod = 0; @@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) return 0; server->num_cookies = 0; + server->siv_index = -1; server->req_tx = packet->transmit_ts; if (info->ext_fields == 0 || info->mode != MODE_CLIENT) @@ -163,17 +181,22 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) return 0; } - if (context.algorithm != SERVER_SIV) { + /* Find the SIV instance needed for authentication */ + for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++) + ; + if (i == MAX_SERVER_SIVS || !server->sivs[i]) { DEBUG_LOG("Unexpected SIV"); return 0; } + server->siv_index = i; + siv = server->sivs[i]; - if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) { + if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) { DEBUG_LOG("Could not set C2S key"); return 0; } - if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start, + if (!NNA_DecryptAuthEF(packet, info, siv, auth_start, plaintext, sizeof (plaintext), &plaintext_length)) { *kod = NTP_KOD_NTS_NAK; return 0; @@ -199,7 +222,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod) } } - if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) { + if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) { DEBUG_LOG("Could not set S2C key"); return 0; } @@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info, server->num_cookies = 0; + if (server->siv_index < 0) + return 0; + /* Generate an authenticator field which will make the length of the response equal to the length of the request */ - if (!NNA_GenerateAuthEF(response, res_info, server->siv, + if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index], server->nonce, sizeof (server->nonce), plaintext, plaintext_length, req_info->length - res_info->length)) diff --git a/test/unit/nts_ke_server.c b/test/unit/nts_ke_server.c index f4f03a1..01156c1 100644 --- a/test/unit/nts_ke_server.c +++ b/test/unit/nts_ke_server.c @@ -75,7 +75,8 @@ prepare_request(NKSN_Instance session, int valid) TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length)); if (index != 4) { - 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 == 5) length = 0; else if (index == 6) diff --git a/test/unit/nts_ntp_server.c b/test/unit/nts_ntp_server.c index 40938d6..2777942 100644 --- a/test/unit/nts_ntp_server.c +++ b/test/unit/nts_ntp_server.c @@ -37,10 +37,13 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak) NKE_Cookie cookie; int i, index, cookie_start, auth_start; - context.algorithm = SERVER_SIV; + context.algorithm = random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ? + AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256; context.c2s.length = SIV_GetKeyLength(context.algorithm); + assert(context.c2s.length <= sizeof (context.c2s.key)); UTI_GetRandomBytes(&context.c2s.key, context.c2s.length); context.s2c.length = SIV_GetKeyLength(context.algorithm); + assert(context.s2c.length <= sizeof (context.s2c.key)); UTI_GetRandomBytes(&context.s2c.key, context.s2c.length); TEST_CHECK(NKS_GenerateCookie(&context, &cookie)); @@ -80,6 +83,7 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak) if (index != 2) { siv = SIV_CreateInstance(context.algorithm); + TEST_CHECK(siv); TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length)); TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce), (const unsigned char *)"", 0, 0));