static int parse_int(char *line, int *result);
static int parse_double(char *line, double *result);
static int parse_null(char *line);
+static int parse_ints(char *line, ARR_Instance array);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_authselectmode(char *);
/* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */
+#define DEFAULT_NTS_AEADS "30 15"
+
/* NTS server and client configuration */
+static ARR_Instance nts_aeads; /* array of int */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
static ARR_Instance nts_server_cert_files; /* array of (char *) */
void
CNF_Initialise(int r, int client_only)
{
+ char buf[10];
+
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
+ nts_aeads = ARR_CreateInstance(sizeof (int));
+ snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
+ parse_ints(buf, nts_aeads);
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
ARR_DestroyInstance(ntp_restrictions);
ARR_DestroyInstance(cmd_restrictions);
+ ARR_DestroyInstance(nts_aeads);
ARR_DestroyInstance(nts_server_cert_files);
ARR_DestroyInstance(nts_server_key_files);
ARR_DestroyInstance(nts_trusted_certs_paths);
no_system_cert = parse_null(p);
} else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket);
+ } else if (!strcasecmp(command, "ntsaeads")) {
+ parse_ints(p, nts_aeads);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
/* ================================================== */
+static int
+parse_ints(char *line, ARR_Instance array)
+{
+ char *s;
+ int v;
+
+ ARR_SetSize(array, 0);
+
+ while (*line) {
+ s = line;
+ line = CPS_SplitWord(line);
+ parse_int(s, &v);
+ ARR_AppendElement(array, &v);
+ }
+ return 1;
+}
+
+/* ================================================== */
+
static void
parse_source(char *line, char *type, int fatal)
{
/* ================================================== */
+ARR_Instance
+CNF_GetNtsAeads(void)
+{
+ return nts_aeads;
+}
+
+/* ================================================== */
+
char *
CNF_GetNtsDumpDir(void)
{
#define GOT_CONF_H
#include "addressing.h"
+#include "array.h"
#include "reference.h"
#include "sources.h"
extern int CNF_GetRefresh(void);
+extern ARR_Instance CNF_GetNtsAeads(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
_statistics.log_ files) may be smaller than the actual offsets.
+[[ntsaeads1]]*ntsaeads* _ID_...::
+This directive specifies a list of IDs of Authenticated Encryption with
+Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
+messages. The algorithms are specified in decreasing order of priority.
+Algorithms that are not supported by the installed version of the crypto
+library (Nettle, GnuTLS) are ignored.
++
+The following IDs are supported:
++
+* 15: AES-SIV-CMAC-256
+* 30: AES-128-GCM-SIV
+{blank}::
++
+The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
+AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
+reliability of NTS in networks that block or limit rate of longer NTP messages.
++
+The ID of the used algorithm is reported for each server by the
+<<chronyc.adoc#authdata,*authdata*>> command.
++
+An example of the directive is:
++
+----
+ntsaeads 15
+----
++
+This list is used also by the <<ntsaeads2,NTS server>>.
+
[[ntsdumpdir1]]*ntsdumpdir* _directory_::
This directive specifies a directory for the client to save NTS cookies it
received from the server in order to avoid making an NTS-KE request when
maximum practical value is half of the system *FD_SETSIZE* constant (usually
1024).
+[[ntsaeads2]]*ntsaeads* _ID_...::
+This directive specifies a list of IDs of Authenticated Encryption with
+Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
+messages. *chronyd* as a server uses the first enabled algorithm from the list
+provided by the client. Algorithms that are not supported by the installed
+version of the crypto library (Nettle, GnuTLS) are ignored.
++
+The following IDs are supported:
++
+* 15: AES-SIV-CMAC-256
+* 30: AES-128-GCM-SIV
+{blank}::
++
+The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
+AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
+reliability of NTS in networks that block or limit rate of longer NTP messages.
++
+An example of the directive is:
++
+----
+ntsaeads 15
+----
++
+This list is used also by the <<ntsaeads1,NTS client>>.
++
+Note the the NTS specification (RFC 8915) requires servers to support
+AES-SIV-CMAC-256, i.e. 15 should be always included in the specified list.
++
+The AES-128-GCM-SIV keys used by *chronyd* do not comply to RFC 8915 for
+compatibility with older *chrony* clients unless the use of compliant keys is
+negotiated with an
+https://chrony-project.org/doc/spec/nts-compliant-128gcm.html[NTS-KE record].
+Support for this record was added in version 4.6.1. As a client, *chronyd* can
+interoperate with a server that uses compliant keys, but does not support the
+negotiation, if it responds to incorrectly authenticated requests with an NTS
+NAK.
+
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
This directive specifies a directory where *chronyd* operating as an NTS server
can save the keys which encrypt NTS cookies provided to clients. The keys are
/* ================================================== */
+#define MAX_AEAD_ALGORITHMS 4
+
static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
- uint16_t data[2];
- int i, length;
+ uint16_t data[MAX_AEAD_ALGORITHMS];
+ int i, aead_algorithm, length;
NKSN_BeginMessage(session);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
- length = 0;
- 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);
+ for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
+ i++) {
+ aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
+ if (SIV_GetKeyLength(aead_algorithm) > 0)
+ data[length++] = htons(aead_algorithm);
+ }
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0;
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
- 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");
+ if (length != 2) {
+ DEBUG_LOG("Unexpected AEAD algorithm");
error = 1;
break;
}
- aead_algorithm = ntohs(data[0]);
- inst->context.algorithm = aead_algorithm;
+ for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
+ if (ntohs(data[0]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), i) &&
+ SIV_GetKeyLength(ntohs(data[0])) > 0) {
+ aead_algorithm = ntohs(data[0]);
+ inst->context.algorithm = aead_algorithm;
+ }
+ }
+ if (aead_algorithm < 0) {
+ DEBUG_LOG("Unexpected AEAD algorithm");
+ error = 1;
+ }
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
if (length != 0) {
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 i, j, critical, type, length;
int compliant_128gcm = 0;
- int i, critical, type, length;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++;
- /* Use the first supported algorithm */
- if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
- aead_algorithm = ntohs(data[i]);
+ /* Use the first enabled and supported algorithm */
+ for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
+ if (ntohs(data[i]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), j) &&
+ aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
+ aead_algorithm = ntohs(data[i]);
+ }
}
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
+for server_aead in "" "15" "30"; do
+ for client_aead in "" "15" "30"; do
+ server_conf="
+ ntsaeads $server_aead
+ ntsserverkey tmp/server1.key
+ ntsservercert tmp/server1.crt
+ ntsprocesses 0"
+ client_conf="
+ nosystemcert
+ ntsaeads $client_aead
+ ntstrustedcerts tmp/server1.crt
+ ntstrustedcerts tmp/server2.crt"
+ client_server_conf=""
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ if [ -n "$server_aead" ] && [ "$server_aead" == "$client_aead" ] &&
+ ( [ "$server_aead" != "30" ] || check_config_h '.*_SIV_GCM 1' ); then
+ check_source_selection || test_fail
+ check_sync || test_fail
+ else
+ check_source_selection && test_fail
+ check_sync && test_fail
+ fi
+ done
+done
+
test_pass