#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
-#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
-#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
#define NKE_MAX_MESSAGE_LENGTH 16384
#define NKE_MAX_RECORD_BODY_LENGTH 256
static int
handle_message(void *arg)
{
+ SIV_Algorithm exporter_algorithm;
NKC_Instance inst = arg;
if (!process_response(inst)) {
return 0;
}
- if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
- &inst->context.c2s, &inst->context.s2c))
+ 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)
+ exporter_algorithm = AEAD_AES_SIV_CMAC_256;
+
+ if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
+ NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
return 0;
if (inst->server_name[0] != '\0') {
static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
{
+ SIV_Algorithm exporter_algorithm;
NKE_Context context;
NKE_Cookie cookie;
char *ntp_server;
}
context.algorithm = aead_algorithm;
+ exporter_algorithm = aead_algorithm;
- if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
+ /* 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)
+ exporter_algorithm = AEAD_AES_SIV_CMAC_256;
+
+ if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
+ NKE_NEXT_PROTOCOL_NTPV4, &context.c2s, &context.s2c))
return 0;
for (i = 0; i < NKE_MAX_COOKIES; i++) {
/* ================================================== */
int
-NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
+NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
+ int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
- int length = SIV_GetKeyLength(siv);
+ int length = SIV_GetKeyLength(algorithm);
+ struct {
+ uint16_t next_protocol;
+ uint16_t algorithm;
+ uint8_t is_s2c;
+ uint8_t _pad;
+ } context;
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
DEBUG_LOG("Invalid algorithm");
return 0;
}
+ assert(sizeof (context) == 6);
+ context.next_protocol = htons(next_protocol);
+ context.algorithm = htons(exporter_algorithm);
+
+ context.is_s2c = 0;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
- sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
- length, (char *)c2s->key) < 0 ||
- gnutls_prf_rfc5705(inst->tls_session,
+ sizeof (context) - 1, (char *)&context,
+ length, (char *)c2s->key) < 0) {
+ DEBUG_LOG("Could not export key");
+ return 0;
+ }
+
+ context.is_s2c = 1;
+ if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
- sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
+ sizeof (context) - 1, (char *)&context,
length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key");
return 0;
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length);
-/* Export NTS keys for a specified algorithm */
-extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
+/* Export NTS keys for a specified algorithm (for compatibility reasons the
+ RFC5705 exporter context is allowed to have a different algorithm) */
+extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm,
+ SIV_Algorithm exporter_algorithm,
+ int next_protocol, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst);
#define NKSN_GetKeys get_keys
static int
-get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
+get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
+ int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
- c2s->length = SIV_GetKeyLength(siv);
+ c2s->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length);
- s2c->length = SIV_GetKeyLength(siv);
+ s2c->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length);
return 1;
}
for (i = 0; i < 10000; i++) {
context.algorithm = AEAD_AES_SIV_CMAC_256;
- get_keys(session, context.algorithm, &context.c2s, &context.s2c);
+ get_keys(session, context.algorithm, random() % 100, NKE_NEXT_PROTOCOL_NTPV4,
+ &context.c2s, &context.s2c);
memset(&cookie, 0, sizeof (cookie));
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));
TEST_CHECK(!NKSN_GetRecord(inst, &critical, &t, &length, buffer, sizeof (buffer)));
- TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, &c2s, &s2c));
- TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
- TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
+ for (i = 0; i < 10; i++) {
+ TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_SIV_CMAC_256, random(), random(), &c2s, &s2c));
+ TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
+ TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256));
+
+ if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0) {
+ TEST_CHECK(NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
+ TEST_CHECK(c2s.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
+ TEST_CHECK(s2c.length == SIV_GetKeyLength(AEAD_AES_128_GCM_SIV));
+ } else {
+ TEST_CHECK(!NKSN_GetKeys(inst, AEAD_AES_128_GCM_SIV, random(), random(), &c2s, &s2c));
+ }
+ }
}
static int