nts: negotiate compliant export of AES-128-GCM-SIV keys
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
This commit is contained in:
parent
2adda9c12c
commit
0707865413
5 changed files with 52 additions and 10 deletions
1
nts_ke.h
1
nts_ke.h
|
@ -40,6 +40,7 @@
|
||||||
#define NKE_RECORD_COOKIE 5
|
#define NKE_RECORD_COOKIE 5
|
||||||
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
||||||
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
||||||
|
#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
|
||||||
|
|
||||||
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct NKC_Instance_Record {
|
||||||
int got_response;
|
int got_response;
|
||||||
int resolving_name;
|
int resolving_name;
|
||||||
|
|
||||||
|
int compliant_128gcm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
|
@ -103,7 +104,7 @@ prepare_request(NKC_Instance inst)
|
||||||
{
|
{
|
||||||
NKSN_Instance session = inst->session;
|
NKSN_Instance session = inst->session;
|
||||||
uint16_t data[2];
|
uint16_t data[2];
|
||||||
int length;
|
int i, length;
|
||||||
|
|
||||||
NKSN_BeginMessage(session);
|
NKSN_BeginMessage(session);
|
||||||
|
|
||||||
|
@ -120,6 +121,14 @@ prepare_request(NKC_Instance inst)
|
||||||
length * sizeof (data[0])))
|
length * sizeof (data[0])))
|
||||||
return 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))
|
if (!NKSN_EndMessage(session))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -139,6 +148,7 @@ process_response(NKC_Instance inst)
|
||||||
assert(sizeof (data) % sizeof (uint16_t) == 0);
|
assert(sizeof (data) % sizeof (uint16_t) == 0);
|
||||||
assert(sizeof (uint16_t) == 2);
|
assert(sizeof (uint16_t) == 2);
|
||||||
|
|
||||||
|
inst->compliant_128gcm = 0;
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
||||||
inst->ntp_address.port = 0;
|
inst->ntp_address.port = 0;
|
||||||
|
@ -175,6 +185,15 @@ process_response(NKC_Instance inst)
|
||||||
aead_algorithm = ntohs(data[0]);
|
aead_algorithm = ntohs(data[0]);
|
||||||
inst->context.algorithm = aead_algorithm;
|
inst->context.algorithm = aead_algorithm;
|
||||||
break;
|
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:
|
case NKE_RECORD_ERROR:
|
||||||
if (length == 2)
|
if (length == 2)
|
||||||
DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
|
DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
|
||||||
|
@ -266,8 +285,9 @@ handle_message(void *arg)
|
||||||
exporter_algorithm = inst->context.algorithm;
|
exporter_algorithm = inst->context.algorithm;
|
||||||
|
|
||||||
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
context incorrectly for compatibility with older chrony servers */
|
context incorrectly for compatibility with older chrony servers unless
|
||||||
if (exporter_algorithm == AEAD_AES_128_GCM_SIV)
|
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;
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
||||||
|
|
|
@ -337,7 +337,8 @@ helper_signal(int x)
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
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;
|
SIV_Algorithm exporter_algorithm;
|
||||||
NKE_Context context;
|
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)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (compliant_128gcm) {
|
||||||
|
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (CNF_GetNTPPort() != NTP_PORT) {
|
if (CNF_GetNTPPort() != NTP_PORT) {
|
||||||
datum = htons(CNF_GetNTPPort());
|
datum = htons(CNF_GetNTPPort());
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
|
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;
|
exporter_algorithm = aead_algorithm;
|
||||||
|
|
||||||
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
context incorrectly for compatibility with older chrony clients */
|
context incorrectly for compatibility with older chrony clients unless
|
||||||
if (exporter_algorithm == AEAD_AES_128_GCM_SIV)
|
the client requested the compliant context */
|
||||||
|
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
|
||||||
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
|
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_records = 0, aead_algorithm_records = 0;
|
||||||
int next_protocol_values = 0, aead_algorithm_values = 0;
|
int next_protocol_values = 0, aead_algorithm_values = 0;
|
||||||
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
||||||
|
int compliant_128gcm = 0;
|
||||||
int i, critical, type, length;
|
int i, critical, type, length;
|
||||||
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
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]);
|
aead_algorithm = ntohs(data[i]);
|
||||||
}
|
}
|
||||||
break;
|
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_ERROR:
|
||||||
case NKE_RECORD_WARNING:
|
case NKE_RECORD_WARNING:
|
||||||
case NKE_RECORD_COOKIE:
|
case NKE_RECORD_COOKIE:
|
||||||
|
@ -477,7 +492,7 @@ process_request(NKSN_Instance session)
|
||||||
error = NKE_ERROR_BAD_REQUEST;
|
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 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -88,9 +88,12 @@ prepare_response(NKSN_Instance session, int valid)
|
||||||
|
|
||||||
if (random() % 2) {
|
if (random() % 2) {
|
||||||
length = random() % (sizeof (data) + 1);
|
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) {
|
if (index != 8) {
|
||||||
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
||||||
length = (random() % sizeof (data) + 1) / 4 * 4;
|
length = (random() % sizeof (data) + 1) / 4 * 4;
|
||||||
|
|
|
@ -92,7 +92,7 @@ prepare_request(NKSN_Instance session, int valid)
|
||||||
|
|
||||||
if (index == 8) {
|
if (index == 8) {
|
||||||
length = random() % (sizeof (data) + 1);
|
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) {
|
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));
|
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) {
|
if (random() % 2) {
|
||||||
length = random() % (sizeof (data) + 1);
|
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));
|
TEST_CHECK(NKSN_EndMessage(session));
|
||||||
|
|
Loading…
Reference in a new issue