*key* _ID_:::
The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
-The MAC is generated as a function of a password specified in the key file,
+The MAC is generated as a function of a key specified in the key file,
which is specified by the <<keyfile,*keyfile*>> directive.
+
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
----
[[keyfile]]*keyfile* _file_::
-This directive is used to specify the location of the file containing ID-key
-pairs for authentication of NTP packets.
+This directive is used to specify the location of the file containing symmetric
+keys which are shared between NTP servers and clients, or peers, in order to
+authenticate NTP packets with a message authentication code (MAC) using a
+cryptographic hash function or cipher.
+
The format of the directive is shown in the example below:
+
10 tulip
11 hyacinth
20 MD5 ASCII:crocus
-25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c
+25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70
+30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1
+31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39
...
----
+
-Each line consists of an ID, name of an authentication hash function (optional),
-and a password. The ID can be any unsigned integer in the range 1 through
-2^32-1. The default hash function is *MD5*, which is always supported.
+Each line consists of an ID, optional type, and key.
+
+The ID can be any positive integer in the range 1 through 2^32-1.
++
+The type is a name of a cryptographic hash function or cipher which is used to
+generate and verify the MAC. The default type is *MD5*, which is always
+supported.
If *chronyd* was built with enabled support for hashing using a crypto library
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
-*chronyd* using, some or all of the following functions may also be available:
-*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*.
+*chronyd* using, some of the following hash functions and ciphers may
+also be available:
+*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*, *AES128*,
+*AES256*.
+
-The password can be specified as a string of characters not containing white
+The key can be specified as a string of ASCII characters not containing white
space with an optional *ASCII:* prefix, or as a hexadecimal number with the
*HEX:* prefix. The maximum length of the line is 2047 characters.
+If the type is a cipher, the length of the key must match the cipher (i.e. 128
+bits for AES128 and 256 bits for AES256).
++
+It is recommended to use randomly generated keys, specified in the hexadecimal
+format, which are at least 128 bits long (i.e. they have at least 32 characters
+after the *HEX:* prefix). *chronyd* will log a warning to syslog on start if a
+source is specified in the configuration file with a key shorter than 80 bits.
+
-The password is used with the hash function to generate and verify a message
-authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
-stronger, hash function with random passwords specified in the hexadecimal
-format that have at least 128 bits. *chronyd* will log a warning to
-syslog on start if a source is specified in the configuration file with a key
-that has password shorter than 80 bits.
+The recommended key types are AES ciphers and SHA3 hash functions. MD5 should
+be avoided unless no other type is supported on the server and client, or
+peers.
+
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
generate random keys for the key file. By default, it generates 160-bit MD5 or
#include "array.h"
#include "keys.h"
+#include "cmac.h"
#include "cmdparse.h"
#include "conf.h"
#include "memory.h"
/* Consider 80 bits as the absolute minimum for a secure key */
#define MIN_SECURE_KEY_LENGTH 10
+typedef enum {
+ NTP_MAC,
+ CMAC,
+} KeyClass;
+
typedef struct {
uint32_t id;
- char *val;
- int len;
- int hash_id;
+ KeyClass class;
+ union {
+ struct {
+ unsigned char *value;
+ int length;
+ int hash_id;
+ } ntp_mac;
+ CMC_Instance cmac;
+ } data;
int auth_delay;
} Key;
free_keys(void)
{
unsigned int i;
+ Key *key;
- for (i = 0; i < ARR_GetSize(keys); i++)
- Free(((Key *)ARR_GetElement(keys, i))->val);
+ for (i = 0; i < ARR_GetSize(keys); i++) {
+ key = ARR_GetElement(keys, i);
+ switch (key->class) {
+ case NTP_MAC:
+ Free(key->data.ntp_mac.value);
+ break;
+ case CMAC:
+ CMC_DestroyInstance(key->data.cmac);
+ break;
+ default:
+ assert(0);
+ }
+ }
ARR_SetSize(keys, 0);
cache_valid = 0;
}
/* ================================================== */
-/* Decode password encoded in ASCII or HEX */
+/* Decode key encoded in ASCII or HEX */
static int
-decode_password(char *key)
+decode_key(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
void
KEY_Reload(void)
{
- unsigned int i, line_number;
+ unsigned int i, line_number, key_length, cmac_key_length;
FILE *in;
- uint32_t key_id;
- char line[2048], *keyval, *key_file;
- const char *hashname;
+ char line[2048], *key_file, *key_value;
+ const char *key_type;
+ int hash_id;
Key key;
free_keys();
if (!*line)
continue;
- if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
+ memset(&key, 0, sizeof (key));
+
+ if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) {
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
- key.hash_id = HSH_GetHashId(hashname);
- if (key.hash_id < 0) {
- LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
+ key_length = decode_key(key_value);
+ if (key_length == 0) {
+ LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id);
continue;
}
- key.len = decode_password(keyval);
- if (!key.len) {
- LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
+ hash_id = HSH_GetHashId(key_type);
+ cmac_key_length = CMC_GetKeyLength(key_type);
+
+ if (hash_id >= 0) {
+ key.class = NTP_MAC;
+ key.data.ntp_mac.value = MallocArray(unsigned char, key_length);
+ memcpy(key.data.ntp_mac.value, key_value, key_length);
+ key.data.ntp_mac.length = key_length;
+ key.data.ntp_mac.hash_id = hash_id;
+ } else if (cmac_key_length > 0) {
+ if (cmac_key_length != key_length) {
+ LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)",
+ key_type, key.id, 8 * cmac_key_length);
+ continue;
+ }
+
+ key.class = CMAC;
+ key.data.cmac = CMC_CreateInstance(key_type, (unsigned char *)key_value, key_length);
+ assert(key.data.cmac);
+ } else {
+ LOG(LOGS_WARN, "Unknown hash function or cipher in key %"PRIu32, key.id);
continue;
}
- key.id = key_id;
- key.val = MallocArray(char, key.len);
- memcpy(key.val, keyval, key.len);
ARR_AppendElement(keys, &key);
}
if (!key)
return 0;
- return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
+ switch (key->class) {
+ case NTP_MAC:
+ return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf));
+ case CMAC:
+ return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf));
+ default:
+ assert(0);
+ return 0;
+ }
}
/* ================================================== */
if (!key)
return 0;
- return key->len >= MIN_SECURE_KEY_LENGTH;
+ switch (key->class) {
+ case NTP_MAC:
+ return key->data.ntp_mac.length >= MIN_SECURE_KEY_LENGTH;
+ default:
+ return 1;
+ }
}
/* ================================================== */
static int
-generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len,
- unsigned char *auth, int auth_len)
+generate_auth(Key *key, const unsigned char *data, int data_len,
+ unsigned char *auth, int auth_len)
{
- return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
+ switch (key->class) {
+ case NTP_MAC:
+ return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value,
+ key->data.ntp_mac.length, data, data_len, auth, auth_len);
+ case CMAC:
+ return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
+ default:
+ return 0;
+ }
}
/* ================================================== */
static int
-check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len,
- const unsigned char *auth, int auth_len, int trunc_len)
+check_auth(Key *key, const unsigned char *data, int data_len,
+ const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
- hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
+ hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
- unsigned char *auth, int auth_len)
+ unsigned char *auth, int auth_len)
{
Key *key;
if (!key)
return 0;
- return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
- data, data_len, auth, auth_len);
+ return generate_auth(key, data, data_len, auth, auth_len);
}
/* ================================================== */
if (!key)
return 0;
- return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
- data, data_len, auth, auth_len, trunc_len);
+ return check_auth(key, data, data_len, auth, auth_len, trunc_len);
}