diff --git a/nts_ke.h b/nts_ke.h index 3ecadd9..2cfbb08 100644 --- 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 diff --git a/nts_ke_client.c b/nts_ke_client.c index f10aed5..2633502 100644 --- a/nts_ke_client.c +++ b/nts_ke_client.c @@ -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, diff --git a/nts_ke_server.c b/nts_ke_server.c index d5b240b..eeeece3 100644 --- a/nts_ke_server.c +++ b/nts_ke_server.c @@ -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; diff --git a/test/unit/nts_ke_client.c b/test/unit/nts_ke_client.c index 3100d1d..e78cdc7 100644 --- a/test/unit/nts_ke_client.c +++ b/test/unit/nts_ke_client.c @@ -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; diff --git a/test/unit/nts_ke_server.c b/test/unit/nts_ke_server.c index be3f6d8..5637f3e 100644 --- a/test/unit/nts_ke_server.c +++ b/test/unit/nts_ke_server.c @@ -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));