]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
nts: negotiate compliant export of AES-128-GCM-SIV keys
authorMiroslav Lichvar <mlichvar@redhat.com>
Thu, 19 Sep 2024 10:08:36 +0000 (12:08 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 26 Sep 2024 14:04:01 +0000 (16:04 +0200)
Add client and server support for a new NTS-KE record to negotiate use
of the compliant key exporter context with the AES-128-GCM-SIV AEAD as
specified here:

https://chrony-project.org/doc/spec/nts-compliant-128gcm.html

nts_ke.h
nts_ke_client.c
nts_ke_server.c
test/unit/nts_ke_client.c
test/unit/nts_ke_server.c

index 3ecadd9d752c469386ae5ecd07919cdfedd40b17..2cfbb085c8621937aa4feddb79857c26574bbb97 100644 (file)
--- a/nts_ke.h
+++ b/nts_ke.h
@@ -40,6 +40,7 @@
 #define NKE_RECORD_COOKIE               5
 #define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
 #define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
+#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
 
 #define NKE_NEXT_PROTOCOL_NTPV4         0
 
index f10aed5506cca0f571eb78068d840f8c8b4a3a00..26335021e48a1cd98d18cba3e4222a32d38c5c2c 100644 (file)
@@ -50,6 +50,7 @@ struct NKC_Instance_Record {
   int got_response;
   int resolving_name;
 
+  int compliant_128gcm;
   NKE_Context context;
   NKE_Cookie cookies[NKE_MAX_COOKIES];
   int num_cookies;
@@ -103,7 +104,7 @@ prepare_request(NKC_Instance inst)
 {
   NKSN_Instance session = inst->session;
   uint16_t data[2];
-  int length;
+  int i, length;
 
   NKSN_BeginMessage(session);
 
@@ -120,6 +121,14 @@ prepare_request(NKC_Instance inst)
                       length * sizeof (data[0])))
     return 0;
 
+  for (i = 0; i < length; i++) {
+    if (data[i] == htons(AEAD_AES_128_GCM_SIV)) {
+      if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
+        return 0;
+      break;
+    }
+  }
+
   if (!NKSN_EndMessage(session))
     return 0;
 
@@ -139,6 +148,7 @@ process_response(NKC_Instance inst)
   assert(sizeof (data) % sizeof (uint16_t) == 0);
   assert(sizeof (uint16_t) == 2);
 
+  inst->compliant_128gcm = 0;
   inst->num_cookies = 0;
   inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
   inst->ntp_address.port = 0;
@@ -175,6 +185,15 @@ process_response(NKC_Instance inst)
         aead_algorithm = ntohs(data[0]);
         inst->context.algorithm = aead_algorithm;
         break;
+      case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
+        if (length != 0) {
+          DEBUG_LOG("Non-empty compliant-128gcm record");
+          error = 1;
+          break;
+        }
+        DEBUG_LOG("Compliant AES-128-GCM-SIV export");
+        inst->compliant_128gcm = 1;
+        break;
       case NKE_RECORD_ERROR:
         if (length == 2)
           DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
@@ -266,8 +285,9 @@ handle_message(void *arg)
   exporter_algorithm = inst->context.algorithm;
 
   /* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
-     context incorrectly for compatibility with older chrony servers */
-  if (exporter_algorithm == AEAD_AES_128_GCM_SIV)
+     context incorrectly for compatibility with older chrony servers unless
+     the server confirmed support for the compliant context */
+  if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm)
     exporter_algorithm = AEAD_AES_SIV_CMAC_256;
 
   if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
index d5b240ba7d0025ca537d27c6e7602e36d7d5fc54..eeeece3804b44b11d1ac8212bebc8d85ea20ceba 100644 (file)
@@ -337,7 +337,8 @@ helper_signal(int x)
 /* ================================================== */
 
 static int
-prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
+prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm,
+                 int compliant_128gcm)
 {
   SIV_Algorithm exporter_algorithm;
   NKE_Context context;
@@ -372,6 +373,11 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
     if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
       return 0;
 
+    if (compliant_128gcm) {
+      if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
+        return 0;
+    }
+
     if (CNF_GetNTPPort() != NTP_PORT) {
       datum = htons(CNF_GetNTPPort());
       if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
@@ -389,8 +395,9 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
     exporter_algorithm = aead_algorithm;
 
     /* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
-       context incorrectly for compatibility with older chrony clients */
-    if (exporter_algorithm == AEAD_AES_128_GCM_SIV)
+       context incorrectly for compatibility with older chrony clients unless
+       the client requested the compliant context */
+    if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
       exporter_algorithm = AEAD_AES_SIV_CMAC_256;
 
     if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
@@ -419,6 +426,7 @@ process_request(NKSN_Instance session)
   int next_protocol_records = 0, aead_algorithm_records = 0;
   int next_protocol_values = 0, aead_algorithm_values = 0;
   int next_protocol = -1, aead_algorithm = -1, error = -1;
+  int compliant_128gcm = 0;
   int i, critical, type, length;
   uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
 
@@ -459,6 +467,13 @@ process_request(NKSN_Instance session)
             aead_algorithm = ntohs(data[i]);
         }
         break;
+      case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
+        if (length != 0) {
+          error = NKE_ERROR_BAD_REQUEST;
+          break;
+        }
+        compliant_128gcm = 1;
+        break;
       case NKE_RECORD_ERROR:
       case NKE_RECORD_WARNING:
       case NKE_RECORD_COOKIE:
@@ -477,7 +492,7 @@ process_request(NKSN_Instance session)
       error = NKE_ERROR_BAD_REQUEST;
   }
 
-  if (!prepare_response(session, error, next_protocol, aead_algorithm))
+  if (!prepare_response(session, error, next_protocol, aead_algorithm, compliant_128gcm))
     return 0;
 
   return 1;
index 3100d1dcf9f403376eccc89636486525c67a729c..e78cdc7164e4fcefb3cbefa21cbc8e0e812fe43b 100644 (file)
@@ -88,9 +88,12 @@ prepare_response(NKSN_Instance session, int valid)
 
   if (random() % 2) {
     length = random() % (sizeof (data) + 1);
-    TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
+    TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
   }
 
+  if (random() % 2)
+    TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
+
   if (index != 8) {
     for (i = 0; i < NKE_MAX_COOKIES; i++) {
       length = (random() % sizeof (data) + 1) / 4 * 4;
index be3f6d805726e03767001f8fb34e6fc1d267308d..5637f3e427cb82fc5c08de3ce90dee67743cbe8f 100644 (file)
@@ -92,7 +92,7 @@ prepare_request(NKSN_Instance session, int valid)
 
   if (index == 8) {
     length = random() % (sizeof (data) + 1);
-    TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length));
+    TEST_CHECK(NKSN_AddRecord(session, 1, 2000 + random() % 1000, data, length));
   }
 
   if (random() % 2) {
@@ -106,9 +106,12 @@ prepare_request(NKSN_Instance session, int valid)
     TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
   }
 
+  if (random() % 2)
+    TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
+
   if (random() % 2) {
     length = random() % (sizeof (data) + 1);
-    TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
+    TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
   }
 
   TEST_CHECK(NKSN_EndMessage(session));