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

If AES-128-GCM-SIV is available on the client, add it to the requested
algorithms in NTS-KE as the first (preferred) entry.

If supported on the server, it will make the cookies shorter, which
will get the length of NTP messages containing only one cookie below
200 octets. This should make NTS more reliable in networks where longer
NTP packets are filtered as a mitigation against amplification attacks
exploiting the ntpd mode 6/7 protocol.
This commit is contained in:
Miroslav Lichvar 2022-10-10 16:35:20 +02:00
parent 73042494bd
commit cc706b50b9
4 changed files with 46 additions and 16 deletions

View file

@ -610,6 +610,7 @@ be reported:
* 13: AES128 * 13: AES128
* 14: AES256 * 14: AES256
* 15: AEAD-AES-SIV-CMAC-256 * 15: AEAD-AES-SIV-CMAC-256
* 30: AEAD-AES-128-GCM-SIV
*KLen*::: *KLen*:::
This column shows the length of the key in bits. This column shows the length of the key in bits.
*Last*::: *Last*:::

View file

@ -102,16 +102,22 @@ static int
prepare_request(NKC_Instance inst) prepare_request(NKC_Instance inst)
{ {
NKSN_Instance session = inst->session; NKSN_Instance session = inst->session;
uint16_t datum; uint16_t data[2];
int length;
NKSN_BeginMessage(session); NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4); data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0; return 0;
datum = htons(AEAD_AES_SIV_CMAC_256); length = 0;
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
data[length++] = htons(AEAD_AES_128_GCM_SIV);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0; return 0;
if (!NKSN_EndMessage(session)) if (!NKSN_EndMessage(session))
@ -159,12 +165,14 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4; next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break; break;
case NKE_RECORD_AEAD_ALGORITHM: case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) { if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm"); DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1; error = 1;
break; break;
} }
aead_algorithm = AEAD_AES_SIV_CMAC_256; aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm; inst->context.algorithm = aead_algorithm;
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
@ -236,7 +244,7 @@ process_response(NKC_Instance inst)
if (error || inst->num_cookies == 0 || if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 || next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256) aead_algorithm < 0)
return 0; return 0;
return 1; return 1;

View file

@ -64,9 +64,12 @@ prepare_response(NKSN_Instance session, int valid)
if (index != 5) { if (index != 5) {
if (index == 6) if (index == 6)
data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1); do {
data[0] = htons(random() % 100);
} while (SIV_GetKeyLength(ntohs(data[0])) > 0);
else else
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 == 7) if (index == 7)
length = 3 + random() % 10; length = 3 + random() % 10;
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length)); TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));

View file

@ -50,7 +50,10 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
if (random() % 2) if (random() % 2)
return 0; return 0;
context->algorithm = AEAD_AES_SIV_CMAC_256; do {
context->algorithm = AEAD_AES_SIV_CMAC_256 + random() %
(AEAD_AES_256_GCM_SIV - AEAD_AES_SIV_CMAC_256 + 10);
} while (SIV_GetKeyLength(context->algorithm) <= 0);
context->c2s.length = SIV_GetKeyLength(context->algorithm); context->c2s.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->c2s.key, context->c2s.length); UTI_GetRandomBytes(context->c2s.key, context->c2s.length);
@ -75,9 +78,9 @@ static int
get_request(NNC_Instance inst) get_request(NNC_Instance inst)
{ {
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
int nonce_length, expected_length, req_cookies;
NTP_PacketInfo info; NTP_PacketInfo info;
NTP_Packet packet; NTP_Packet packet;
int expected_length, req_cookies;
memset(&packet, 0, sizeof (packet)); memset(&packet, 0, sizeof (packet));
memset(&info, 0, sizeof (info)); memset(&info, 0, sizeof (info));
@ -100,6 +103,17 @@ get_request(NNC_Instance inst)
TEST_CHECK(inst->num_cookies > 0); TEST_CHECK(inst->num_cookies > 0);
TEST_CHECK(inst->siv); TEST_CHECK(inst->siv);
switch (inst->context.algorithm) {
case AEAD_AES_SIV_CMAC_256:
nonce_length = 16;
break;
case AEAD_AES_128_GCM_SIV:
nonce_length = 12;
break;
default:
assert(0);
}
memcpy(nonce, inst->nonce, sizeof (nonce)); memcpy(nonce, inst->nonce, sizeof (nonce));
memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id)); memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id));
TEST_CHECK(NNC_PrepareForAuth(inst)); TEST_CHECK(NNC_PrepareForAuth(inst));
@ -111,9 +125,10 @@ get_request(NNC_Instance inst)
(inst->cookies[inst->cookie_index].length + 4)); (inst->cookies[inst->cookie_index].length + 4));
expected_length = info.length + 4 + sizeof (inst->uniq_id) + expected_length = info.length + 4 + sizeof (inst->uniq_id) +
req_cookies * (4 + inst->cookies[inst->cookie_index].length) + req_cookies * (4 + inst->cookies[inst->cookie_index].length) +
4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv); 4 + 4 + nonce_length + SIV_GetTagLength(inst->siv);
DEBUG_LOG("length=%d cookie_length=%d expected_length=%d", DEBUG_LOG("algo=%d length=%d cookie_length=%d expected_length=%d",
info.length, inst->cookies[inst->cookie_index].length, expected_length); (int)inst->context.algorithm, info.length,
inst->cookies[inst->cookie_index].length, expected_length);
if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH && if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH &&
inst->cookies[inst->cookie_index].length % 4 == 0 && inst->cookies[inst->cookie_index].length % 4 == 0 &&
@ -162,7 +177,10 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
return; return;
} }
nonce_length = random() % (sizeof (nonce)) + 1; nonce_length = SIV_GetMinNonceLength(inst->siv) + random() %
(MIN(SIV_GetMaxNonceLength(inst->siv), sizeof (nonce)) -
SIV_GetMinNonceLength(inst->siv) + 1);
assert(nonce_length >= 1 && nonce_length <= sizeof (nonce));
do { do {
cookie_length = random() % (sizeof (cookie) + 1); cookie_length = random() % (sizeof (cookie) + 1);