]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
keys: add support for CMAC keys
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 17 Sep 2019 14:59:55 +0000 (16:59 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 24 Sep 2019 14:38:12 +0000 (16:38 +0200)
Allow a cipher (AES128 or AES256) to be specified as the type of a key
in the key file to authenticate NTP packets with a CMAC instead of the
NTPv4 (RFC 5905) MAC using a hash function. This follows RFC 8573.

cmdparse.c
cmdparse.h
doc/chrony.conf.adoc
keys.c

index 6fae81c8b0ef9da38b65e06b0117b21d34583dbd..37037d120e13e82859da9687219512ba530a4d39 100644 (file)
@@ -261,7 +261,7 @@ CPS_SplitWord(char *line)
 /* ================================================== */
 
 int
-CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
+CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
 {
   char *s1, *s2, *s3, *s4;
 
@@ -278,10 +278,10 @@ CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
     return 0;
 
   if (*s3) {
-    *hash = s2;
+    *type = s2;
     *key = s3;
   } else {
-    *hash = "MD5";
+    *type = "MD5";
     *key = s2;
   }
 
index 19f4bb7548cd5bd0854fa97d2cd6a44cf86a6555..694d12dd0c9c1011b9feac79e5ab470646d4e73e 100644 (file)
@@ -49,6 +49,6 @@ extern void CPS_NormalizeLine(char *line);
 extern char *CPS_SplitWord(char *line);
 
 /* Parse a key from keyfile */
-extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key);
+extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
 
 #endif /* GOT_CMDPARSE_H */
index 1516d7e3d8473350b7e3f460515d79e04b4018c4..117b650349075d7874c6cf6c4b56bd39a17d392d 100644 (file)
@@ -96,7 +96,7 @@ interval in order to allow a burst with two requests.
 *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)
@@ -2017,8 +2017,10 @@ include @SYSCONFDIR@/chrony.d/*.conf
 ----
 
 [[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:
 +
@@ -2033,30 +2035,41 @@ format of the file is shown 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
diff --git a/keys.c b/keys.c
index 44a3f7a0ce93fb21f1f2c72fa6fba77ec8d3a3de..912257e3750bf5fb6ca1c3af8b3468e72ff1b81d 100644 (file)
--- a/keys.c
+++ b/keys.c
@@ -32,6 +32,7 @@
 
 #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;
 
@@ -62,9 +74,21 @@ static void
 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;
@@ -129,10 +153,10 @@ determine_hash_delay(uint32_t key_id)
 }
 
 /* ================================================== */
-/* 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;
@@ -184,11 +208,11 @@ compare_keys_by_id(const void *a, const void *b)
 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();
@@ -212,26 +236,43 @@ KEY_Reload(void)
     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);
   }
 
@@ -334,7 +375,15 @@ KEY_GetAuthLength(uint32_t key_id)
   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;
+  }
 }
 
 /* ================================================== */
@@ -349,30 +398,41 @@ KEY_CheckKeyLength(uint32_t key_id)
   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);
 }
@@ -381,7 +441,7 @@ check_ntp_auth(int hash_id, const unsigned char *key, int key_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;
 
@@ -390,8 +450,7 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
   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);
 }
 
 /* ================================================== */
@@ -407,6 +466,5 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_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);
 }