nts: add server support for authentication with AES-128-GCM-SIV

Keep a server SIV instance for each available algorithm.

Select AES-128-GCM-SIV if requested by NTS-KE client as the first
supported algorithm.

Instead of encoding the AEAD ID in the cookie, select the algorithm
according to the length of decrypted keys. (This can work as a long as
all supported algorithms use keys with different lengths.)
This commit is contained in:
Miroslav Lichvar 2022-10-11 14:36:14 +02:00
parent cc706b50b9
commit 790a336eb2
4 changed files with 63 additions and 21 deletions

View file

@ -427,8 +427,9 @@ process_request(NKSN_Instance session)
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) { for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++; aead_algorithm_values++;
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256) /* Use the first supported algorithm */
aead_algorithm = AEAD_AES_SIV_CMAC_256; if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]);;
} }
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
@ -862,11 +863,9 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
return 0; return 0;
} }
/* The algorithm is hardcoded for now */ /* The AEAD ID is not encoded in the cookie. It is implied from the key
if (context->algorithm != AEAD_AES_SIV_CMAC_256) { length (as long as only algorithms with different key lengths are
DEBUG_LOG("Unexpected SIV algorithm"); supported). */
return 0;
}
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH || if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
context->s2c.length != context->c2s.length) { context->s2c.length != context->c2s.length) {
@ -954,7 +953,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0; return 0;
} }
/* 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; context->algorithm = AEAD_AES_SIV_CMAC_256;
break;
default:
DEBUG_LOG("Unknown key length");
return 0;
}
context->c2s.length = plaintext_length / 2; context->c2s.length = plaintext_length / 2;
context->s2c.length = plaintext_length / 2; context->s2c.length = plaintext_length / 2;

View file

@ -41,13 +41,15 @@
#include "siv.h" #include "siv.h"
#include "util.h" #include "util.h"
#define SERVER_SIV AEAD_AES_SIV_CMAC_256 #define MAX_SERVER_SIVS 2
struct NtsServer { 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]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES]; NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies; int num_cookies;
int siv_index;
NTP_int64 req_tx; NTP_int64 req_tx;
}; };
@ -60,6 +62,7 @@ void
NNS_Initialise(void) NNS_Initialise(void)
{ {
const char **certs, **keys; const char **certs, **keys;
int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */ /* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) { if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
@ -68,9 +71,17 @@ NNS_Initialise(void)
} }
server = Malloc(sizeof (struct NtsServer)); server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(SERVER_SIV);
if (!server->siv) server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
LOG_FATAL("Could not initialise SIV cipher"); 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 void
NNS_Finalise(void) NNS_Finalise(void)
{ {
int i;
if (!server) if (!server)
return; return;
SIV_DestroyInstance(server->siv); for (i = 0; i < MAX_SERVER_SIVS; i++) {
if (server->sivs[i])
SIV_DestroyInstance(server->sivs[i]);
}
Free(server); Free(server);
server = NULL; server = NULL;
} }
@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context; NKE_Context context;
NKE_Cookie cookie; NKE_Cookie cookie;
SIV_Instance siv;
void *ef_body; void *ef_body;
*kod = 0; *kod = 0;
@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0; return 0;
server->num_cookies = 0; server->num_cookies = 0;
server->siv_index = -1;
server->req_tx = packet->transmit_ts; server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT) 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; 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"); DEBUG_LOG("Unexpected SIV");
return 0; 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"); DEBUG_LOG("Could not set C2S key");
return 0; return 0;
} }
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start, if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) { plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK; *kod = NTP_KOD_NTS_NAK;
return 0; 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"); DEBUG_LOG("Could not set S2C key");
return 0; return 0;
} }
@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0; server->num_cookies = 0;
if (server->siv_index < 0)
return 0;
/* Generate an authenticator field which will make the length /* Generate an authenticator field which will make the length
of the response equal to the length of the request */ 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), server->nonce, sizeof (server->nonce),
plaintext, plaintext_length, plaintext, plaintext_length,
req_info->length - res_info->length)) req_info->length - res_info->length))

View file

@ -75,7 +75,8 @@ prepare_request(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length)); TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
if (index != 4) { 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) if (index == 5)
length = 0; length = 0;
else if (index == 6) else if (index == 6)

View file

@ -37,10 +37,13 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
NKE_Cookie cookie; NKE_Cookie cookie;
int i, index, cookie_start, auth_start; 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); context.c2s.length = SIV_GetKeyLength(context.algorithm);
assert(context.c2s.length <= sizeof (context.c2s.key));
UTI_GetRandomBytes(&context.c2s.key, context.c2s.length); UTI_GetRandomBytes(&context.c2s.key, context.c2s.length);
context.s2c.length = SIV_GetKeyLength(context.algorithm); context.s2c.length = SIV_GetKeyLength(context.algorithm);
assert(context.s2c.length <= sizeof (context.s2c.key));
UTI_GetRandomBytes(&context.s2c.key, context.s2c.length); UTI_GetRandomBytes(&context.s2c.key, context.s2c.length);
TEST_CHECK(NKS_GenerateCookie(&context, &cookie)); 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) { if (index != 2) {
siv = SIV_CreateInstance(context.algorithm); siv = SIV_CreateInstance(context.algorithm);
TEST_CHECK(siv);
TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length)); TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length));
TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce), TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce),
(const unsigned char *)"", 0, 0)); (const unsigned char *)"", 0, 0));