]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
nts: add server support for authentication with AES-128-GCM-SIV
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 11 Oct 2022 12:36:14 +0000 (14:36 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Wed, 19 Oct 2022 13:50:39 +0000 (15:50 +0200)
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.)

nts_ke_server.c
nts_ntp_server.c
test/unit/nts_ke_server.c
test/unit/nts_ntp_server.c

index 948058ae4dee46e0423bfb29c9d849305f6bc195..4ec00be615406aef8480f4c6a2ebb2ebc85917c2 100644 (file)
@@ -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;
index 9226c895005019c703f2dea95fc10709e84d48df..7db39985680e5d7c5d6dfefd32edd5fc979f14df 100644 (file)
 #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))
index f4f03a102ad6ebde381954cb2f46f71d16c98cbe..01156c14ae03e82b59776862387106ebd195c23e 100644 (file)
@@ -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)
index 40938d6095f3cdb3c40945387f7e319b001339a8..27779427ddd35a8ba3ac0096394a5023343988b0 100644 (file)
@@ -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));