[Git][NTPsec/ntpsec][master] 4 commits: Add CMAC authentication

Hal Murray gitlab at mg.gitlab.com
Wed Jun 13 09:09:58 UTC 2018


Hal Murray pushed to branch master at NTPsec / ntpsec


Commits:
d4691e60 by Hal Murray at 2018-06-13T07:56:52Z
Add CMAC authentication

Also includes significant cleanups scattered around.

This does not include any #ifdefs for /usr/include/openssl/cmac.h
It works on all my test systems but might fail on some really
old/conservative systems.  If so, we get to decide whether we
should add the #ifdefs or drop support.

- - - - -
a7a72bb7 by Hal Murray at 2018-06-13T07:56:52Z
Disable debugging logging.

- - - - -
41ec5a08 by Hal Murray at 2018-06-13T07:56:52Z
Check that keys for servers will work.  Issue #487

- - - - -
a04100c3 by Hal Murray at 2018-06-13T07:57:30Z
Check to be sure openssl/cmac.h exists

This gives an error message from waf configure
rather than waf build

- - - - -


25 changed files:

- attic/digest-timing.c
- docs/authentic.txt
- docs/includes/auth-commands.txt
- docs/mode6.txt
- + include/ntp_auth.h
- include/ntp_stdlib.h
- include/ntp_types.h
- include/ntpd.h
- libntp/authkeys.c
- libntp/authreadkeys.c
- libntp/macencrypt.c
- libntp/ssl_init.c
- ntpclients/ntpkeygen.py
- ntpclients/ntpq.py
- ntpd/ntp_config.c
- ntpd/ntp_control.c
- ntpd/ntp_peer.c
- ntpd/ntp_proto.c
- ntpd/ntp_util.c
- ntpd/ntpd.c
- tests/common/tests_main.c
- tests/common/tests_main.h
- tests/libntp/authkeys.c
- tests/libntp/macencrypt.c
- wscript


Changes:

=====================================
attic/digest-timing.c
=====================================
--- a/attic/digest-timing.c
+++ b/attic/digest-timing.c
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <time.h>
 
 #include <openssl/opensslv.h>
@@ -67,6 +68,7 @@ static void ssl_init(void)
 {
   ERR_load_crypto_strings();
   OpenSSL_add_all_digests();
+  OpenSSL_add_all_ciphers();
   ctx = EVP_MD_CTX_new();
 #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF
   cmac = CMAC_CTX_new();
@@ -93,7 +95,7 @@ static unsigned int SSL_Digest(
 static unsigned int SSL_DigestSlow(
   int type,               /* hash algorithm */
   uint8_t *key,           /* key pointer */
-  int     keylength,       /* key size */
+  int     keylength,      /* key size */
   uint8_t *pkt,           /* packet pointer */
   int     pktlength       /* packet length */
 ) {


=====================================
docs/authentic.txt
=====================================
--- a/docs/authentic.txt
+++ b/docs/authentic.txt
@@ -154,9 +154,16 @@ special message called a _crypto-NAK_. Since the crypto-NAK is protected
 by the loopback test, an intruder cannot disrupt the protocol by sending
 a bogus crypto-NAK.
 
-MD5 digests are 16 bytes.  SHA1 digests are 20 bytes.
-Longer digests don't work yet.  2018-Jan-07
-FIXME: long digests
++ntpd+'s digest mode can use any digest supported by libcrypto
+from the OpenSSL project.  MD5 digests are 16 bytes.  SHA1 digests are 20 bytes.
+Longer digests are truncated.
+
++ntpd+'s mode can use any cypher with a CBC mode that is supported by
+libcrypto from the OpenSSL project.  AES is short for AES-128.
+AES needs a 16 byte key.  Longer keys are truncated.  Shorter
+keys are padded with 0s.  AES MACs are 16 bytes long.  MACs longer
+than 20 bytes will be truncated.
+
 
 Keys and related information are specified in a keys file, which must be
 distributed and stored using secure means beyond the scope of the NTP


=====================================
docs/includes/auth-commands.txt
=====================================
--- a/docs/includes/auth-commands.txt
+++ b/docs/includes/auth-commands.txt
@@ -9,7 +9,7 @@
 
 [[keys]]
 +keys+ _keyfile_::
-  Specifies the complete path and location of the MD5/SHA1 key file
+  Specifies the complete path and location of the key file
   containing the keys and key identifiers used by {ntpdman},
   and {ntpqman} when operating with symmetric-key cryptography.
   This is the same operation as the +-k+ command line option.


=====================================
docs/mode6.txt
=====================================
--- a/docs/mode6.txt
+++ b/docs/mode6.txt
@@ -508,13 +508,18 @@ The contents of the MAC trailer consists of:
 
 1. The 32-bit identifier of the signing key in network byte order.
 
-2. A cryptographic hash of the following octet spans, in order.
-First, the password entered to use the signing key, then the request
-header fields, then the payload.
+2a. In digest mode, a cryptographic hash of the following octet spans,
+in order. First, the password entered to use the signing key, then the
+request header fields, then the payload.
 
-The cryptographic hash is 16 octets for MD5 and 20 octets for SHA1.
-Longer digests don't work yet.  2018-Jan-06
-FIXME:  long digests
+2b. In CMAC mode, a cryptographic hash of the packet header and
+payload with the crypto algorithim using the key.
+
+The cryptographic hash is 16 octets for MD5 and AES and 20 octets for SHA1.
+Longer digests are truncated.
+
+The key length for AES is 16 bytes.  Longer keys are truncated.  Shorter
+keys are padded with 0s.  MD5 and SHA1 can use any key length.
 
 == Compatibility Notes ==
 


=====================================
include/ntp_auth.h
=====================================
--- /dev/null
+++ b/include/ntp_auth.h
@@ -0,0 +1,75 @@
+#ifndef GUARD_AUTH_H
+#define GUARD_AUTH_H
+
+#include "ntp_types.h"
+#include "ntp_lists.h"
+
+#include <openssl/evp.h>
+#include <openssl/cmac.h>
+
+typedef enum {AUTH_NONE, AUTH_CMAC, AUTH_DIGEST} AUTH_Type;
+
+/*
+ * Structure to store auth data in the hash table.
+ */
+typedef struct auth_data auth_info;
+
+struct auth_data {
+	auth_info *	hlink;			/* next in hash bucket */
+	DECL_DLIST_LINK(auth_info, llink);	/* for overall & free lists */
+	keyid_t		keyid;			/* key identifier */
+	AUTH_Type	type;			/* CMAC or old digest */
+	unsigned short	flags;			/* KEY_ flags that wave */
+	uint8_t *	key;			/* shared secret */
+	unsigned short	key_size;		/* secret length */
+	const EVP_MD *	digest;			/* Digest mode only */
+	const EVP_CIPHER *cipher;		/* CMAC mode only */
+};
+
+extern  void    auth_init       (void);
+extern  void    auth_prealloc	(int);
+extern  void    auth_reset_stats(uptime_t reset_time);
+
+
+extern  void	auth_setkey	(keyid_t, AUTH_Type, const char *, const uint8_t *, size_t);
+extern  void    auth_delkeys    (void);
+extern  bool    authreadkeys    (const char *);
+extern  void    authtrust       (keyid_t, bool);
+
+extern  auth_info *    authlookup   (keyid_t, bool);
+
+extern  bool    authdecrypt     (auth_info*, uint32_t *, int, int);
+extern  int     authencrypt     (auth_info*, uint32_t *, int);
+
+extern   bool    digest_decrypt (auth_info*, uint32_t *, int, int);
+extern   int     digest_encrypt (auth_info*, uint32_t *, int);
+
+extern   bool    cmac_decrypt (auth_info*, uint32_t *, int, int);
+extern   int     cmac_encrypt (auth_info*, uint32_t *, int);
+
+
+extern	unsigned int authnumkeys;	/* number of active keys */
+extern	unsigned int authnumfreekeys;	/* number of free keys */
+extern	unsigned long authkeylookups;	/* calls to lookup keys */
+extern	unsigned long authkeynotfound;	/* keys not found */
+extern	unsigned long authencryptions;	/* calls to authencrypt */
+extern	unsigned long authdigestencrypt;/* calls to digest_encrypt */
+extern	unsigned long authcmacencrypt;	/* calls to cmac_encrypt */
+extern	unsigned long authdecryptions;	/* calls to authdecrypt */
+extern	unsigned long authdigestdecrypt;/* calls to digest_decrypt */
+extern	unsigned long authdigestfail;	/* fails from digest_decrypt */
+extern	unsigned long authcmacdecrypt;	/* calls to cmac_decrypt*/
+extern	unsigned long authcmacfail;	/* fails from cmac_decrypt*/
+extern	uptime_t auth_timereset;	/* current_time when stats reset */
+
+
+/* EVP_get_cipherbyname() is broken on
+ * old CentOS, NetBSD, FreeBSD, and maybe others.
+ */
+extern const EVP_CIPHER * MY_get_cipherbyname(const char * name);
+
+
+/* Not in CMAC API */
+#define CMAC_MAX_MAC_LENGTH 64
+
+#endif	/* GUARD_AUTH_H */


=====================================
include/ntp_stdlib.h
=====================================
--- a/include/ntp_stdlib.h
+++ b/include/ntp_stdlib.h
@@ -38,20 +38,8 @@ extern	int	change_logfile	(const char *, bool);
 extern	void	reopen_logfile  (void);
 extern	void	setup_logfile	(const char *);
 
-/* authkeys.c */
-extern	void	auth_delkeys	(void);
-extern	bool	authdecrypt	(keyid_t, uint32_t *, int, int);
-extern	int	authencrypt	(keyid_t, uint32_t *, int);
-extern	bool	authhavekey	(keyid_t);
-extern	bool	authistrusted	(keyid_t);
-extern	bool	authreadkeys	(const char *);
-extern	void	authtrust	(keyid_t, bool);
-extern	bool	authusekey	(keyid_t, int, const uint8_t *);
-
 extern	int	clocktime	(int, int, int, int, int, time_t, uint32_t, uint32_t *, uint32_t *);
-extern	void	init_auth	(void);
 extern	void	init_network	(void);
-extern	void	auth_prealloc_symkeys(int);
 extern	int	ymd2yd		(int, int, int);
 
 /* getopt.c */
@@ -68,9 +56,6 @@ int ntp_getopt_long(int argc, char* const argv[], const char *optstring,
 		    const struct option *longopts, int *longindex);
 
 /* mac_md5encrypt.c */
-extern	bool	mac_authdecrypt	(int, uint8_t *, int, uint32_t *, int, int);
-extern	int	mac_authencrypt	(int, uint8_t *, int, uint32_t *, int);
-extern	void	mac_setkey	(keyid_t, int, const uint8_t *, size_t);
 extern	uint32_t	addr2refid	(sockaddr_u *);
 
 /* emalloc.c */
@@ -133,16 +118,6 @@ extern	void	getauthkeys 	(const char *);
  * Variable declarations for libntp.
  */
 
-/* authkeys.c */
-extern unsigned int	authkeynotfound;	/* keys not found */
-extern unsigned int	authkeylookups;		/* calls to lookup keys */
-extern unsigned int	authnumkeys;		/* number of active keys */
-extern unsigned int	authkeyuncached;	/* cache misses */
-extern unsigned int	authencryptions;	/* calls to encrypt */
-extern unsigned int	authdecryptions;	/* calls to decrypt */
-
-extern int	authnumfreekeys;
-
 /* getopt.c */
 extern char *	ntp_optarg;		/* global argument pointer */
 extern int	ntp_optind;		/* global argv index */


=====================================
include/ntp_types.h
=====================================
--- a/include/ntp_types.h
+++ b/include/ntp_types.h
@@ -68,8 +68,8 @@ typedef uint16_t	associd_t; /* association ID */
 typedef uint32_t keyid_t;	/* cryptographic key ID */
 #define NTP_MAXKEY 0xffff	/* max authentication key number */
 
-/* Max digest length in non-extension MACs, add 4 for keyID */
-#define MAX_BARE_DIGEST_LENGTH 20
+/* Max MAC length in non-extension MACs, add 4 for keyID */
+#define MAX_BARE_MAC_LENGTH 20
 
 /*
  * Ordinary double has only 53 bits  of precision in IEEE754.  But l_fp


=====================================
include/ntpd.h
=====================================
--- a/include/ntpd.h
+++ b/include/ntpd.h
@@ -226,7 +226,6 @@ extern char *ntp_signd_socket;
 
 /* ntp_control.c */
 extern keyid_t	ctl_auth_keyid;		/* keyid used for authenticating write requests */
-extern	void	reset_auth_stats(void);
 
 /*
  * Other statistics of possible interest


=====================================
libntp/authkeys.c
=====================================
--- a/libntp/authkeys.c
+++ b/libntp/authkeys.c
@@ -12,49 +12,37 @@
 #include "ntp_lists.h"
 #include "ntp_malloc.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 
-/*
- * Structure to store keys in in the hash table.
- */
-typedef struct savekey symkey;
-
-struct savekey {
-	symkey *	hlink;		/* next in hash bucket */
-	DECL_DLIST_LINK(symkey, llink);	/* for overall & free lists */
-	uint8_t *	secret;		/* shared secret */
-	keyid_t		keyid;		/* key identifier */
-	unsigned short	type;		/* OpenSSL digest NID */
-	unsigned short	secretsize;	/* secret octets */
-	unsigned short	flags;		/* KEY_ flags that wave */
-};
 
-/* define the payload region of symkey beyond the list pointers */
-#define symkey_payload	secret
+/* define the payload region of auth_data beyond the list pointers */
+#define auth_info_payload	keyid
 
 #define	KEY_TRUSTED	0x001	/* this key is trusted */
 
 #ifdef DEBUG
-typedef struct symkey_alloc_tag symkey_alloc;
+typedef struct auth_alloc_tag auth_alloc;
 
-struct symkey_alloc_tag {
-	symkey_alloc *	link;
+struct auth_alloc_tag {
+	auth_alloc *	link;
 	void *		mem;		/* enable free() atexit */
 };
 
-symkey_alloc *	authallocs;
+auth_alloc *	auth_allocs;
 #endif	/* DEBUG */
 
 static inline unsigned short	auth_log2(double x);
 static void	auth_moremem	(int);
-static void		auth_resize_hashtable(void);
-static void		allocsymkey(symkey **, keyid_t,	unsigned short,
+static void	auth_resize_hashtable(void);
+static void	alloc_auth_info(auth_info **, keyid_t,	AUTH_Type,
+				    const char *,
 				    unsigned short, unsigned short, uint8_t *);
-static void		freesymkey(symkey *, symkey **);
+static void	free_auth_info(auth_info *, auth_info **);
 #ifdef DEBUG
-static void		free_auth_mem(void);
+static void	free_auth_mem(void);
 #endif
 
-static symkey key_listhead;		/* list of all in-use keys */
+static auth_info key_listhead;		/* list of all in-use keys */
 /*
  * The hash table. This is indexed by the low order bits of the
  * keyid. This gets updated in auth_resize_hashtable
@@ -63,40 +51,46 @@ static symkey key_listhead;		/* list of all in-use keys */
 #define INIT_AUTHHASHSIZE 64
 static unsigned short authhashbuckets = INIT_AUTHHASHSIZE;
 static unsigned short authhashmask = INIT_AUTHHASHSIZE - 1;
-static symkey **key_hash;
-
-unsigned int authkeynotfound;		/* keys not found */
-unsigned int authkeylookups;		/* calls to lookup keys */
-unsigned int authnumkeys;		/* number of active keys */
-unsigned int authkeyuncached;		/* cache misses */
-static unsigned int authnokey;		/* calls to encrypt with no key */
-unsigned int authencryptions;		/* calls to encrypt */
-unsigned int authdecryptions;		/* calls to decrypt */
+static auth_info **key_hash;
+
+unsigned int authnumkeys;	/* number of active keys */
+unsigned int authnumfreekeys;	/* number of free keys */
+unsigned long authkeylookups;	/* calls to lookup keys */
+unsigned long authkeynotfound;	/* keys not found */
+unsigned long authencryptions;	/* calls to authencrypt */
+unsigned long authdigestencrypt;/* calls to digest_encrypt */
+unsigned long authcmacencrypt;	/* calls to cmac_encrypt */
+unsigned long authdecryptions;	/* calls to authdecrypt */
+unsigned long authdigestdecrypt;/* calls to digest_decrypt */
+unsigned long authdigestfail;	/* fails from digest_decrypt */
+unsigned long authcmacdecrypt;	/* calls to cmac_decrypt*/
+unsigned long authcmacfail;	/* fails from cmac_decrypt*/
+uptime_t auth_timereset;	/* current_time when stats reset */
 
 /*
- * Storage for free symkey structures.  We malloc() such things but
+ * Storage for free auth_info structures.  We malloc() such things but
  * never free them.
  */
-static symkey *authfreekeys;
-int authnumfreekeys;
+static auth_info *authfreekeys;
 
 #define	MEMINC	16		/* number of new free ones to get */
 
 /*
- * The key cache. We cache the last key we looked at here.
+ * There used to be a cache for the last key we used.
+ * It was also a kludge to pass arguments.
+ * We now pass a pointer to auth_data.
+ * A cache isn't all that useful:
+ *   On a busy server, we can use the same auth_data for the reply.
+ *   On a client, where the reply might hit the cache from the request,
+ *     the extra cost of a lookup isn't significant.
  */
-static keyid_t	cache_keyid;		/* key identifier */
-static uint8_t *cache_secret;		/* secret */
-static unsigned short cache_secretsize;	/* secret length */
-static int cache_type;			/* OpenSSL digest NID */
-static unsigned short cache_flags;	/* flags that wave */
 
 
 /*
- * init_auth - initialize internal data
+ * auth_init - initialize internal data
  */
 void
-init_auth(void)
+auth_init(void)
 {
 	size_t newalloc;
 
@@ -117,6 +111,28 @@ init_auth(void)
 
 
 /*
+ * auth_reset_stats - reset the authentication stat counters.
+ * can't use global current_time - not in library.
+ */
+void
+auth_reset_stats(uptime_t reset_time)
+{
+	authkeylookups = 0;
+	authkeynotfound = 0;
+	authencryptions = 0;
+	authdigestencrypt = 0;
+	authcmacencrypt = 0;
+	authdecryptions = 0;
+	authdigestdecrypt = 0;
+	authdigestfail = 0;
+	authcmacdecrypt = 0;
+	authcmacfail = 0;
+	auth_timereset = reset_time;
+}
+
+
+
+/*
  * free_auth_mem - assist in leak detection by freeing all dynamic
  *		   allocations from this module.
  */
@@ -124,18 +140,16 @@ init_auth(void)
 static void
 free_auth_mem(void)
 {
-	symkey *	sk;
-	symkey_alloc *	alloc;
-	symkey_alloc *	next_alloc;
+	auth_info *	sk;
+	auth_alloc *	alloc;
+	auth_alloc *	next_alloc;
 
 	while (NULL != (sk = HEAD_DLIST(key_listhead, llink))) {
-		freesymkey(sk, &key_hash[KEYHASH(sk->keyid)]);
+		free_auth_info(sk, &key_hash[KEYHASH(sk->keyid)]);
 	}
 	free(key_hash);
 	key_hash = NULL;
-	cache_keyid = 0;
-	cache_flags = 0;
-	for (alloc = authallocs; alloc != NULL; alloc = next_alloc) {
+	for (alloc = auth_allocs; NULL != alloc; alloc = next_alloc) {
 		next_alloc = alloc->link;
 		free(alloc->mem);	
 	}
@@ -153,11 +167,11 @@ auth_moremem(
 	int	keycount
 	)
 {
-	symkey *	sk;
+	auth_info *	auth;
 	int		i;
 #ifdef DEBUG
 	void *		base;
-	symkey_alloc *	allocrec;
+	auth_alloc *	allocrec;
 # define MOREMEM_EXTRA_ALLOC	(sizeof(*allocrec))
 #else
 # define MOREMEM_EXTRA_ALLOC	(0)
@@ -166,29 +180,29 @@ auth_moremem(
 	i = (keycount > 0)
 		? keycount
 		: MEMINC;
-	sk = emalloc_zero((unsigned int)i * sizeof(*sk) + MOREMEM_EXTRA_ALLOC);
+	auth = emalloc_zero((unsigned int)i * sizeof(*auth) + MOREMEM_EXTRA_ALLOC);
 #ifdef DEBUG
-	base = sk;
+	base = auth;
 #endif
 	authnumfreekeys += i;
 
-	for (; i > 0; i--, sk++) {
-		LINK_SLIST(authfreekeys, sk, llink.f);
+	for (; i > 0; i--, auth++) {
+		LINK_SLIST(authfreekeys, auth, llink.f);
 	}
 
 #ifdef DEBUG
-	allocrec = (void *)sk;
+	allocrec = (void *)auth;
 	allocrec->mem = base;
-	LINK_SLIST(authallocs, allocrec, link);
+	LINK_SLIST(auth_allocs, allocrec, link);
 #endif
 }
 
 
 /*
- * auth_prealloc_symkeys
+ * auth_prealloc
  */
 void
-auth_prealloc_symkeys(
+auth_prealloc(
 	int	keycount
 	)
 {
@@ -224,7 +238,7 @@ auth_resize_hashtable(void)
 	unsigned short	hashbits;
 	unsigned short	hash;
 	size_t		newalloc;
-	symkey *	sk;
+	auth_info *	auth;
 
 	totalkeys = authnumkeys + (unsigned int)authnumfreekeys;
 	hashbits = auth_log2(totalkeys / 4.0) + 1;
@@ -238,135 +252,92 @@ auth_resize_hashtable(void)
 	key_hash = erealloc(key_hash, newalloc);
 	memset(key_hash, '\0', newalloc);
 
-	ITER_DLIST_BEGIN(key_listhead, sk, llink, symkey)
-		hash = KEYHASH(sk->keyid);
-		LINK_SLIST(key_hash[hash], sk, hlink);
+	ITER_DLIST_BEGIN(key_listhead, auth, llink, auth_info)
+		hash = KEYHASH(auth->keyid);
+		LINK_SLIST(key_hash[hash], auth, hlink);
 	ITER_DLIST_END()
 }
 
 
 /*
- * allocsymkey - common code to allocate and link in symkey
- *
- * secret must be allocated with a free-compatible allocator.  It is
- * owned by the referring symkey structure, and will be free()d by
- * freesymkey().
+ * alloc_auth_info - allocate and link in an auth_info slot.
+ * secret must be allocated with a free-compatible allocator.
+ * It is owned by the the new auth_info and will be free()d by
+ * free_auth_info().
  */
 static void
-allocsymkey(
-	symkey **	bucket,
-	keyid_t		id,
+alloc_auth_info(
+	auth_info **	bucket,
+	keyid_t		keyid,
+	AUTH_Type	type,
+	const char *	name,
 	unsigned short	flags,
-	unsigned short	type,
-	unsigned short	secretsize,
-	uint8_t *	secret
+	unsigned short	key_size,
+	uint8_t *	key
 	)
 {
-	symkey *	sk;
+	auth_info *	auth;
 
 	if (authnumfreekeys < 1)
 		auth_moremem(-1);
-	UNLINK_HEAD_SLIST(sk, authfreekeys, llink.f);
+	UNLINK_HEAD_SLIST(auth, authfreekeys, llink.f);
 	//ENSURE(sk != NULL);
-	sk->keyid = id;
-	sk->flags = flags;
-	sk->type = type;
-	sk->secretsize = secretsize;
-	sk->secret = secret;
-	LINK_SLIST(*bucket, sk, hlink);
-	LINK_TAIL_DLIST(key_listhead, sk, llink);
+	auth->keyid = keyid;
+	auth->type = type;
+	auth->flags = flags;
+	auth->key_size = key_size;
+	auth->key = key;
+	switch (type) {
+	  case AUTH_NONE:
+		auth->digest = NULL;
+		auth->cipher = NULL;
+		break;
+	  case AUTH_DIGEST:
+		auth->digest = EVP_get_digestbyname(name);
+		auth->cipher = NULL;
+		break;
+	  case AUTH_CMAC:
+		auth->digest = NULL;
+		auth->cipher = EVP_get_cipherbyname(name);
+		break;
+	}
+	LINK_SLIST(*bucket, auth, hlink);
+	LINK_TAIL_DLIST(key_listhead, auth, llink);
 	authnumfreekeys--;
 	authnumkeys++;
 }
 
 
 /*
- * freesymkey - common code to remove a symkey and recycle its entry.
+ * free_auth_info - common code to remove a auth_info and recycle its entry.
  */
 static void
-freesymkey(
-	symkey *	sk,
-	symkey **	bucket
+free_auth_info(
+	auth_info *	auth,
+	auth_info **	bucket
 	)
 {
-	symkey *	unlinked;
+	auth_info *	unlinked;
 
-	if (sk->secret != NULL) {
-		memset(sk->secret, '\0', sk->secretsize);
-		free(sk->secret);
-                sk->secret = NULL;
+	if (NULL != auth->key) {
+		memset(auth->key, '\0', auth->key_size);
+		free(auth->key);
+                auth->key = NULL;
 	}
-	UNLINK_SLIST(unlinked, *bucket, sk, hlink, symkey);
+	UNLINK_SLIST(unlinked, *bucket, auth, hlink, auth_info);
 	//ENSURE(sk == unlinked);
-	UNLINK_DLIST(sk, llink);
-	memset((char *)sk + offsetof(symkey, symkey_payload), '\0',
-	       sizeof(*sk) - offsetof(symkey, symkey_payload));
-	LINK_SLIST(authfreekeys, sk, llink.f);
+	UNLINK_DLIST(auth, llink);
+	memset((char *)auth + offsetof(auth_info, auth_info_payload), '\0',
+	       sizeof(*auth) - offsetof(auth_info, auth_info_payload));
+	LINK_SLIST(authfreekeys, auth, llink.f);
 	authnumkeys--;
 	authnumfreekeys++;
 }
 
 
 /*
- * authhavekey - return true and cache the key, if zero or both known
- *		 and trusted.
- */
-bool
-authhavekey(
-	keyid_t		id
-	)
-{
-	symkey *	sk;
-
-	authkeylookups++;
-	if (0 == id || cache_keyid == id) {
-		return true;
-	}
-
-	/*
-	 * Search the bin for the key. If found and the key type
-	 * is zero, somebody marked it trusted without specifying
-	 * a key or key type. In this case consider the key missing.
-	 */
-	authkeyuncached++;
-	for (sk = key_hash[KEYHASH(id)]; sk != NULL; sk = sk->hlink) {
-		if (id == sk->keyid) {
-			if (0 == sk->type) {
-				authkeynotfound++;
-				return false;
-			}
-			break;
-		}
-	}
-
-	/*
-	 * If the key is not found, or if it is found but not trusted,
-	 * the key is not considered found.
-	 */
-	if (NULL == sk) {
-		authkeynotfound++;
-		return false;
-	}
-	if (!(KEY_TRUSTED & sk->flags)) {
-		authnokey++;
-		return false;
-	}
-
-	/*
-	 * The key is found and trusted. Initialize the key cache.
-	 */
-	cache_keyid = sk->keyid;
-	cache_type = sk->type;
-	cache_flags = sk->flags;
-	cache_secret = sk->secret;
-	cache_secretsize = sk->secretsize;
-
-	return true;
-}
-
-
-/*
  * authtrust - declare a key to be trusted/untrusted
+ * untrusted case not used (except for test code)  2018-Jun
  */
 void
 authtrust(
@@ -374,19 +345,19 @@ authtrust(
 	bool		trust
 	)
 {
-	symkey **	bucket;
-	symkey *	sk;
+	auth_info **	bucket;
+	auth_info *	auth;
 
 	/*
 	 * Search bin for key; if it does not exist and is untrusted,
 	 * forget it.
 	 */
 	bucket = &key_hash[KEYHASH(id)];
-	for (sk = *bucket; sk != NULL; sk = sk->hlink) {
-		if (id == sk->keyid)
+	for (auth = *bucket; NULL != auth; auth = auth->hlink) {
+		if (id == auth->keyid)
 			break;
 	}
-	if (!trust && NULL == sk)
+	if (!trust && NULL == auth)
 		return;
 
 	/*
@@ -394,88 +365,97 @@ authtrust(
 	 * exist and is to be trusted or it does exist and is or is
 	 * not to be trusted.
 	 */	
-	if (sk != NULL) {
-		if (cache_keyid == id) {
-			cache_flags = 0;
-			cache_keyid = 0;
-		}
-
-		/*
-		 * Key exists. If it is to be trusted, say so.
-		 */
+	if (NULL != auth) {
+		/* Key exists. Leave it around so we can trust it again. */
 		if (trust) {
-			sk->flags |= KEY_TRUSTED;
-			return;
+			auth->flags |= KEY_TRUSTED;
+		} else {
+			auth->flags &= ~KEY_TRUSTED;
 		}
-
-		/* No longer trusted, return it to the free list. */
-		freesymkey(sk, bucket);
 		return;
+
 	}
 
-	allocsymkey(bucket, id, KEY_TRUSTED, 0, 0, NULL);
+	/* Create empty slot to hold trusted flag.  No key.  */
+	alloc_auth_info(bucket, id, AUTH_NONE, 0, KEY_TRUSTED, 0, NULL);
 }
 
-
 /*
- * authistrusted - determine whether a key is trusted
+ * authlookup - find key, check trust
  */
-bool
-authistrusted(
-	keyid_t		keyno
-	)
+auth_info *
+authlookup(
+        keyid_t	keyno,
+        bool	needtrust
+        )
 {
-	symkey *	sk;
-	symkey **	bucket;
-
-	if (keyno == cache_keyid)
-		return !!(KEY_TRUSTED & cache_flags);
+        auth_info *     auth;
+        auth_info **    bucket;
 
-	authkeyuncached++;
-	bucket = &key_hash[KEYHASH(keyno)];
-	for (sk = *bucket; sk != NULL; sk = sk->hlink) {
-		if (keyno == sk->keyid)
-			break;
-	}
-	if (NULL == sk || !(KEY_TRUSTED & sk->flags)) {
-		authkeynotfound++;
-		return false;
-	}
-	return true;
+	authkeylookups++;
+        bucket = &key_hash[KEYHASH(keyno)];
+        for (auth = *bucket; NULL != auth; auth = auth->hlink) {
+                if (keyno == auth->keyid)
+                        break;
+        }
+        if (NULL == auth ||
+	   (AUTH_NONE == auth->type) ||
+	   (needtrust && !(KEY_TRUSTED & auth->flags))) {
+		if (0) msyslog(LOG_INFO, "DEBUG: authlookup fail: key %u, %s, auth: %s",
+			keyno, needtrust? "T" : "F",
+			(NULL == auth)? "NULL:" : "Ok");
+                authkeynotfound++;
+                return NULL;
+        }
+        return auth;
 }
 
-
 void
-mac_setkey(
+auth_setkey(
 	keyid_t keyno,
-	int	keytype,
+	AUTH_Type type,
+	const char * name,
 	const uint8_t *key,
-	size_t len
+	size_t key_size
 	)
 {
-	symkey **	bucket;
-	uint8_t *	secret;
-	size_t		secretsize;
-	
-	//ENSURE(keytype <= USHRT_MAX);
+	auth_info **	bucket;
+	uint8_t *	newkey;
+
+	if (0) msyslog(LOG_INFO, "DEBUG: auth_setkey: key %u, %s, length %zu",
+	    keyno, name, key_size);
+
+	//ENSURE(??? <= USHRT_MAX);
 	//ENSURE(len < 4 * 1024);
 	/*
 	 * See if we already have the key.  If so just stick in the
 	 * new value.
 	 */
 	bucket = &key_hash[KEYHASH(keyno)];
-	for (symkey * sk = *bucket; sk != NULL; sk = sk->hlink) {
-		if (keyno == sk->keyid) {
-			sk->type = (unsigned short)keytype;
-			secretsize = len;
-			sk->secretsize = (unsigned short)secretsize;
-                        free(sk->secret);
-                        sk->secret = emalloc(secretsize);
-			memcpy(sk->secret, key, secretsize);
-			if (cache_keyid == keyno) {
-				cache_flags = 0;
-				cache_keyid = 0;
+	for (auth_info * auth = *bucket; NULL != auth; auth = auth->hlink) {
+		if (keyno == auth->keyid) {
+			auth->type = type;
+			switch (type) {
+			  case AUTH_NONE:
+				auth->digest = NULL;
+				auth->cipher = NULL;
+				break;
+			  case AUTH_DIGEST:
+				auth->digest = EVP_get_digestbyname(name);
+				auth->cipher = NULL;
+				break;
+			  case AUTH_CMAC:
+				auth->digest = NULL;
+				auth->cipher = EVP_get_cipherbyname(name);
+				break;
+			}
+			if (NULL != auth->key) {
+				memset(auth->key, '\0', auth->key_size);
+                        	free(auth->key);
 			}
+			auth->key_size = (unsigned short)key_size;
+                        auth->key = emalloc(key_size);
+			memcpy(auth->key, key, key_size);
 			return;
 		}
 	}
@@ -483,17 +463,16 @@ mac_setkey(
 	/*
 	 * Need to allocate new structure.  Do it.
 	 */
-	secretsize = len;
-	secret = emalloc(secretsize);
-	memcpy(secret, key, secretsize);
-	allocsymkey(bucket, keyno, 0, (unsigned short)keytype,
-		    (unsigned short)secretsize, secret);
+	newkey = emalloc(key_size);
+	memcpy(newkey, key, key_size);
+	alloc_auth_info(bucket, keyno, type, name, 0,
+		    (unsigned short)key_size, newkey);
 #ifdef DEBUG
 	if (debug >= 4) { /* SPECIAL DEBUG */
-		printf("auth_setkey: key %d type %d len %d ", (int)keyno,
-		    keytype, (int)secretsize);
-		for (size_t j = 0; j < secretsize; j++)
-			printf("%02x", secret[j]);
+		printf("auth_setkey: key %d type %s len %d ", (int)keyno,
+		    name, (int)key_size);
+		for (size_t j = 0; j < key_size; j++)
+			printf("%02x", key[j]);
 		printf("\n");
 	}	
 #endif
@@ -508,21 +487,24 @@ mac_setkey(
 void
 auth_delkeys(void)
 {
-	symkey *	sk;
+	auth_info * auth;
 
-	ITER_DLIST_BEGIN(key_listhead, sk, llink, symkey)
+	ITER_DLIST_BEGIN(key_listhead, auth, llink, auth_info)
 		/*
 		 * Don't lose info as to which keys are trusted.
 		 */
-		if (KEY_TRUSTED & sk->flags) {
-			if (sk->secret != NULL) {
-				memset(sk->secret, '\0', sk->secretsize);
-				free(sk->secret);
-				sk->secret = NULL;
+		if (KEY_TRUSTED & auth->flags) {
+			if (NULL != auth->key) {
+				memset(auth->key, '\0', auth->key_size);
+				free(auth->key);
+				auth->key = NULL;
 			}
-			sk->secretsize = 0;
+			auth->key_size = 0;
+			auth->type = AUTH_NONE;
+			auth->digest = NULL;
+			auth->cipher = NULL;
 		} else {
-			freesymkey(sk, &key_hash[KEYHASH(sk->keyid)]);
+			free_auth_info(auth, &key_hash[KEYHASH(auth->keyid)]);
 		}
 	ITER_DLIST_END()
 }
@@ -530,60 +512,70 @@ auth_delkeys(void)
 
 /*
  * authencrypt - generate message authenticator
- *
- * Returns length of authenticator field, zero if key not found.
+ * fills in keyid in packet
+ * Returns length of authenticator field
  */
 int
 authencrypt(
-	keyid_t		keyno,
+	auth_info *	auth,	/* assumed setup correctly */
 	uint32_t *	pkt,
 	int		length
 	)
-{\
-	/*
-	 * A zero key identifier means the sender has not verified
-	 * the last message was correctly authenticated. The MAC
-	 * consists of a single word with value zero.
+{
+
+	/* Pre-CMAC versions of this code had checking here.
+	 * That logic has been pushed up a layer.  2018-June
 	 */
+
 	authencryptions++;
-	pkt[length / 4] = htonl(keyno);
-	if (0 == keyno) {
-		return 4;
-	}
-	if (!authhavekey(keyno)) {
+	pkt[length / 4] = htonl(auth->keyid);
+	switch (auth->type) {
+	    case AUTH_DIGEST:
+		authdigestencrypt++;
+		return digest_encrypt(auth, pkt, length);
+	    case AUTH_CMAC:
+		authcmacencrypt++;
+		return cmac_encrypt(auth, pkt, length);
+	    case AUTH_NONE:
 		return 0;
 	}
-
-	return mac_authencrypt(cache_type,
-		cache_secret, cache_secretsize,
-		pkt, length);
+	return 0;
 }
 
 
 /*
  * authdecrypt - verify message authenticator
  *
- * Returns true if authenticator valid, false if invalid or not found.
+ * Returns true if authenticator valid.
  */
 bool
 authdecrypt(
-	keyid_t		keyno,
+	auth_info *	auth,	/* assumed setup correctly */
 	uint32_t *	pkt,
 	int		length,
 	int		size
 	)
 {
-	/*
-	 * A zero key identifier means the sender has not verified
-	 * the last message was correctly authenticated.  For our
-	 * purpose this is an invalid authenticator.
+	bool answer;
+
+	/* Pre-CMAC versions of this code had checking here.
+	 * That logic has been pushed up a layer.  2018-June
 	 */
+
 	authdecryptions++;
-	if (0 == keyno || !authhavekey(keyno) || size < 4) {
+	switch (auth->type) {
+	    case AUTH_DIGEST:
+		authdigestdecrypt++;
+		answer = digest_decrypt(auth, pkt, length, size);
+		if (!answer) authdigestfail++;
+		return answer;
+	    case AUTH_CMAC:
+		authcmacdecrypt++;
+		answer = cmac_decrypt(auth, pkt, length, size);
+		if (!answer) authcmacfail++;
+		return answer;
+	    case AUTH_NONE:
 		return false;
 	}
-
-	return mac_authdecrypt(cache_type,
-		cache_secret, cache_secretsize,
-		pkt, length, size);
+	return false;
 }


=====================================
libntp/authreadkeys.c
=====================================
--- a/libntp/authreadkeys.c
+++ b/libntp/authreadkeys.c
@@ -9,10 +9,15 @@
 #include "ntp_syslog.h"
 #include "ntp_stdlib.h"
 #include "lib_strbuf.h"
+#include "ntp_auth.h"
 
 #include <openssl/objects.h>
 #include <openssl/evp.h>
 
+#include <openssl/cmac.h>
+
+#define NAMEBUFSIZE 100
+
 /* Forwards */
 static char *nexttok (char **);
 
@@ -59,28 +64,155 @@ nexttok(
 	return starttok;
 }
 
+static char*
+try_cmac(const char *upcased, char* namebuf) {
+	strncpy(namebuf, upcased, NAMEBUFSIZE);
+	if ((strcmp(namebuf, "AES") == 0) || (strcmp(namebuf, "AES128CMAC") == 0))
+		strncpy(namebuf, "AES-128", NAMEBUFSIZE);
+	strlcat(namebuf, "-CBC", NAMEBUFSIZE);
+	namebuf[NAMEBUFSIZE-1] = '\0';
+	if (0) msyslog(LOG_INFO, "DEBUG try_cmac: %s=>%s", upcased, namebuf);
+	if (EVP_get_cipherbyname(namebuf) == NULL)
+		return NULL;
+	return namebuf;
+}
+
+static char*
+try_digest(char *upcased, char *namebuf) {
+	strncpy(namebuf, upcased, NAMEBUFSIZE);
+	if (EVP_get_digestbyname(namebuf) != NULL)
+	  return namebuf;
+	if ('M' == upcased[0]) {
+		/* hack for backward compatibility */
+		strncpy(namebuf, "MD5", NAMEBUFSIZE);
+		if (EVP_get_digestbyname(namebuf) != NULL)
+	 		return namebuf;
+	}
+	return NULL;
+}
 
 static void
-check_digest_length(
+check_digest_mac_length(
 	keyid_t keyno,
-	int keytype,
 	char *name) {
     unsigned char digest[EVP_MAX_MD_SIZE];
     unsigned int length = 0;
     EVP_MD_CTX *ctx;
     const EVP_MD *md;
 
-    md = EVP_get_digestbynid(keytype);
+    md = EVP_get_digestbyname(name);
     ctx = EVP_MD_CTX_create();
     EVP_DigestInit_ex(ctx, md, NULL);
     EVP_DigestFinal_ex(ctx, digest, &length);
     EVP_MD_CTX_destroy(ctx);
 
-    if (MAX_BARE_DIGEST_LENGTH < length) {
+    if (MAX_BARE_MAC_LENGTH < length) {
 	msyslog(LOG_ERR, "AUTH: authreadkeys: digest for key %u, %s will be truncated.", keyno, name);
     }
 }
 
+static void
+check_cmac_mac_length(
+	keyid_t keyno,
+	char *name) {
+    unsigned char mac[CMAC_MAX_MAC_LENGTH+1024];
+    size_t length = 0;
+    char key[EVP_MAX_KEY_LENGTH];  /* garbage is OK */
+    CMAC_CTX *ctx;
+    const EVP_CIPHER *cipher = EVP_get_cipherbyname(name);
+
+    ctx = CMAC_CTX_new();
+    CMAC_Init(ctx, key, EVP_CIPHER_key_length(cipher), cipher, NULL);
+    CMAC_Final(ctx, mac, &length);
+    CMAC_CTX_free(ctx);
+
+    /* CMAC_MAX_MAC_LENGTH isn't in API
+     * Check here to avoid buffer overrun in cmac_decrypt and cmac_encrypt
+     */
+    if (CMAC_MAX_MAC_LENGTH < length) {
+	msyslog(LOG_ERR,
+		"AUTH: authreadkeys: CMAC for key %u, %s is too big: %lu",
+		keyno, name, (long unsigned int)length);
+	exit(1);
+    }
+
+    if (MAX_BARE_MAC_LENGTH < length) {
+	msyslog(LOG_ERR, "AUTH: authreadkeys: CMAC for key %u, %s will be truncated.", keyno, name);
+    }
+}
+
+/* check_mac_length - Check for CMAC/digest too long.
+ * maybe should check for too short.
+ */
+static void
+check_mac_length(
+	keyid_t keyno,
+	AUTH_Type type,
+	char * name,
+	char *upcased) {
+    switch (type) {
+	case AUTH_CMAC:
+	    check_cmac_mac_length(keyno, name);
+	    break;
+    	case AUTH_DIGEST:
+	    check_digest_mac_length(keyno, name);
+	    break;
+    	case AUTH_NONE:
+	    msyslog(LOG_ERR, "BUG: authreadkeys: unknown AUTH type for key %u, %s", keyno, upcased);
+    }
+}
+
+/* check_cmac_key_length: check and fix CMAC key length
+ * Ciphers require a specific key length.
+ * Truncate or pad if necessary.
+ * This may modify the key string - assumes enough storage.
+ * AES-128 is 128 bits or 16 bytes.
+ */
+static int
+check_cmac_key_length(
+	keyid_t keyno,
+	char *name,
+	char *key,
+	int keylength) {
+    const EVP_CIPHER *cipher = EVP_get_cipherbyname(name);
+    int len = EVP_CIPHER_key_length(cipher);
+    int i;
+
+    if (len < keylength) {
+	    msyslog(LOG_ERR, "AUTH: CMAC key %u will be truncated %d=>%d",
+		keyno, keylength, len);
+    } else if ( len > keylength) {
+	    msyslog(LOG_ERR, "AUTH: CMAC key %u will be padded %d=>%d",
+		keyno, keylength, len);
+	    for (i=keylength; i<len; i++) key[i] = 0;
+    } else {
+	    if (0) msyslog(LOG_ERR, "AUTH: CMAC key %u is right size", keyno);
+    }
+
+    return len;
+}
+
+static int
+check_key_length(
+	keyid_t keyno,
+	AUTH_Type type,
+	char *name,
+	char *key,
+	int keylength) {
+    int length = keylength;
+    switch (type) {
+	case AUTH_CMAC:
+	    length = check_cmac_key_length(keyno, name, key, keylength);
+	    break;
+    	case AUTH_DIGEST:
+	    /* any length key works */
+	    break;
+    	case AUTH_NONE:
+	    msyslog(LOG_ERR, "BUG: authreadkeys: unknown AUTH type for key %u", keyno);
+    }
+    return length;
+}
+
 
 /*
  * authreadkeys - (re)read keys from a file.
@@ -93,9 +225,11 @@ authreadkeys(
 	FILE	*fp;
 	char	*line;
 	keyid_t	keyno;
-	int	keytype;
+	AUTH_Type type;
 	char	buf[512];		/* lots of room for line */
 	uint8_t	keystr[32];		/* Bug 2537 */
+	char *	name;
+	char	namebuf[NAMEBUFSIZE];
 	size_t	len;
 	size_t	j;
 	int	keys = 0;
@@ -120,7 +254,7 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 	/*
 	 * Now read lines from the file, looking for key entries
 	 */
-	while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
+	while ((line = fgets(buf, sizeof(buf), fp)) != NULL) {
 		char *token = nexttok(&line);
 		if (token == NULL)
 			continue;
@@ -143,7 +277,7 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 		}
 
 		/*
-		 * Next is keytype. See if that is all right.
+		 * Next is CMAC or Digest type. See if that is all right.
 		 */
 		token = nexttok(&line);
 		if (token == NULL) {
@@ -152,15 +286,24 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 			continue;
 		}
 		/*
-		 * The key type is the NID used by the message digest 
-		 * algorithm. There are a number of inconsistencies in
-		 * the OpenSSL database. We attempt to discover them
-		 * here and prevent use of inconsistent data later.
+		 * This area used to pass around nids.
+		 * There is a bug in older versions of libcrypt
+		 * where some nid routine doesn't work so AES
+		 * didn't work on old CentOS, NetBSD, or FreeBSD.
+		 * 2018-Jun-08
+		 *
+		 * OpenSSL names are capitalized.
+		 * So uppercase the name before passing to
+		 * EVP_get_digestbyname() or EVP_get_cipherbyname().
+		 *
+		 * AES is short for AES-128.
+		 * CMAC names get "-CBC" appended.
+		 * ntp classic uses AES128CMAC, so we support that too.
 		 *
-		 * OpenSSL digest short names are capitalized, so uppercase the
-		 * digest name before passing to OBJ_sn2nid().  If it is not
-		 * recognized but begins with 'M' use NID_md5 to be consistent
-		 * with past behavior.
+		 * If it is not recognized but begins with 'M' use
+		 * NID_md5 digest to be consistent with past behavior.
+		 * Try CMAC names first to dodge this hack in case future
+		 * cipher names begin with M.
 		 */
 		char *upcased;
 		char *pch;
@@ -169,27 +312,31 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 		for (pch = upcased; '\0' != *pch; pch++)
 			*pch = (char)toupper((unsigned char)*pch);
 
-		keytype = OBJ_sn2nid(upcased);
-		if ((NID_undef == keytype) && ('M' == upcased[0]))
-			keytype = NID_md5;
-		if (NID_undef == keytype) {
-			msyslog(LOG_ERR,
-			    "AUTH: authreadkeys: invalid type for key %u, %s",
-				keyno, token);
-			continue;
+		name = NULL;
+		if (NULL == name) {
+			name = try_cmac(upcased, namebuf);
+			if (NULL != name)
+				type = AUTH_CMAC;
 		}
-		if (EVP_get_digestbynid(keytype) == NULL) {
+		if (NULL == name) {
+			name = try_digest(upcased, namebuf);
+			if (NULL != name)
+				type = AUTH_DIGEST;
+		}
+                if (NULL == name) {
 			msyslog(LOG_ERR,
-			    "AUTH: authreadkeys: no algorithm for key %u, %s",
+			    "AUTH: authreadkeys: unknown auth type %u, %s",
 				keyno, token);
-			continue;
-		}
+                        continue;
+                }
+
+
 
 		/*
-		 * Finally, get key and insert it. If it is longer than 20
-		 * characters, it is a binary string encoded in hex;
-		 * otherwise, it is a text string of printable ASCII
-		 * characters.
+		 * Finally, get key and insert it.
+		 * If it is longer than 20 characters, it is a binary
+		 * string encoded in hex; otherwise, it is a text string
+		 * of printable ASCII characters.
 		 */
 		token = nexttok(&line);
 		if (token == NULL) {
@@ -199,8 +346,9 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 		}
 		len = strlen(token);
 		if (len <= 20) {	/* Bug 2537 */
-			check_digest_length(keyno, keytype, upcased);
-			mac_setkey(keyno, keytype, (uint8_t *)token, len);
+			len = check_key_length(keyno, type, name, upcased, len);
+			check_mac_length(keyno, type, name, upcased);
+			auth_setkey(keyno, type, name, (uint8_t *)token, len);
 			keys++;
 		} else {
 			char	hex[] = "0123456789abcdef";
@@ -230,8 +378,10 @@ msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file);
 				keyno);
 			    continue;
 			}
-			check_digest_length(keyno, keytype, upcased);
-			mac_setkey(keyno, keytype, keystr, jlim / 2);
+			len = jlim / 2;
+			len = check_key_length(keyno, type, name, upcased, len);
+			check_mac_length(keyno, type, name, upcased);
+			auth_setkey(keyno, type, name, keystr, len);
 			keys++;
 		}
 	}


=====================================
libntp/macencrypt.c
=====================================
--- a/libntp/macencrypt.c
+++ b/libntp/macencrypt.c
@@ -1,5 +1,5 @@
 /*
- *	digest support for NTP
+ *	CMAC and digest support for NTP
  */
 #include "config.h"
 
@@ -13,6 +13,7 @@
 
 #include "ntp_fp.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 #include "ntp.h"
 
 #ifndef EVP_MD_CTX_reset
@@ -23,6 +24,7 @@
 
 /* Need one per thread. */
 extern EVP_MD_CTX *digest_ctx;
+extern CMAC_CTX *cmac_ctx;
 
 /* ctmemeq - test two blocks memory for equality without leaking
  * timing information.
@@ -47,15 +49,88 @@ static bool ctmemeq(const void *s1, const void *s2, size_t n) {
 }
 
 /*
- * mac_authencrypt - generate message digest
+ * cmac_encrypt - generate CMAC authenticator
  *
  * Returns length of MAC including key ID and digest.
  */
 int
-mac_authencrypt(
-	int	type,		/* hash algorithm */
-	uint8_t	*key,		/* key pointer */
-	int	key_size,	/* key size */
+cmac_encrypt(
+	auth_info* auth,
+	uint32_t *pkt,		/* packet pointer */
+	int	length		/* packet length */
+	)
+{
+	uint8_t	mac[CMAC_MAX_MAC_LENGTH];
+	size_t	len;
+	CMAC_CTX *ctx = cmac_ctx;
+
+	CMAC_resume(ctx);
+	if (!CMAC_Init(ctx, auth->key, auth->key_size, auth->cipher, NULL)) {
+		/* Shouldn't happen.  Does if wrong key_size. */
+		msyslog(LOG_ERR,
+		    "CMAC: encrypt: CMAC init failed, %u, %u",
+			auth->keyid, auth->key_size);
+		return (0);
+	}
+	CMAC_Update(ctx, (uint8_t *)pkt, (unsigned int)length);
+	CMAC_Final(ctx, mac, &len);
+	if (MAX_BARE_MAC_LENGTH < len)
+		len = MAX_BARE_MAC_LENGTH;
+	memmove((uint8_t *)pkt + length + 4, mac, len);
+	return (int)(len + 4);
+}
+
+
+/*
+ * cmac_decrypt - verify CMAC authenticator
+ *
+ * Returns true if valid, false if invalid.
+ */
+bool
+cmac_decrypt(
+	auth_info*	auth,
+	uint32_t	*pkt,	/* packet pointer */
+	int	length,	 	/* packet length */
+	int	size		/* MAC size */
+	)
+{
+	uint8_t	mac[CMAC_MAX_MAC_LENGTH];
+	size_t	len;
+	CMAC_CTX *ctx = cmac_ctx;
+
+	CMAC_resume(ctx);
+	if (!CMAC_Init(ctx, auth->key, auth->key_size, auth->cipher, NULL)) {
+		/* Shouldn't happen.  Does if wrong key_size. */
+		msyslog(LOG_ERR,
+		    "CMAC: decrypt: CMAC init failed, %u, %u",
+			auth->keyid, auth->key_size);
+		return false;
+	}
+	CMAC_Update(ctx, (uint8_t *)pkt, (unsigned int)length);
+	CMAC_Final(ctx, mac, &len);
+	if (MAX_BARE_MAC_LENGTH < len)
+		len = MAX_BARE_MAC_LENGTH;
+	if ((unsigned int)size != len + 4) {
+		/* Beware of DoS attack.
+		 * This indicates either the sender is broken
+		 * or some admin fatfingered things.
+		 * Similar code at digest_decrypt.
+		 */
+		if (0) msyslog(LOG_ERR,
+		    "CMAC: decrypt: MAC length error");
+		return false;
+	}
+	return ctmemeq(mac, (char *)pkt + length + 4, len);
+}
+
+/*
+ * digest_encrypt - generate message digest
+ *
+ * Returns length of MAC including key ID and digest.
+ */
+int
+digest_encrypt(
+	auth_info* auth,
 	uint32_t *pkt,		/* packet pointer */
 	int	length		/* packet length */
 	)
@@ -70,31 +145,29 @@ mac_authencrypt(
 	 * was created.
 	 */
 	EVP_MD_CTX_reset(ctx);
-	if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) {
+	if (!EVP_DigestInit_ex(ctx, auth->digest, NULL)) {
 		msyslog(LOG_ERR,
 		    "MAC: encrypt: digest init failed");
 		return (0);
 	}
-	EVP_DigestUpdate(ctx, key, key_size);
+	EVP_DigestUpdate(ctx, auth->key, auth->key_size);
 	EVP_DigestUpdate(ctx, (uint8_t *)pkt, (unsigned int)length);
 	EVP_DigestFinal_ex(ctx, digest, &len);
-	if (MAX_BARE_DIGEST_LENGTH < len)
-		len = MAX_BARE_DIGEST_LENGTH;
+	if (MAX_BARE_MAC_LENGTH < len)
+		len = MAX_BARE_MAC_LENGTH;
 	memmove((uint8_t *)pkt + length + 4, digest, len);
 	return (int)(len + 4);
 }
 
 
 /*
- * mac_authdecrypt - verify message authenticator
+ * digest_decrypt - verify message authenticator
  *
  * Returns true if digest valid, false if invalid.
  */
 bool
-mac_authdecrypt(
-	int	type,		/* hash algorithm */
-	uint8_t	*key,		/* key pointer */
-	int	key_size,	/* key size */
+digest_decrypt(
+	auth_info*	auth,
 	uint32_t	*pkt,	/* packet pointer */
 	int	length,	 	/* packet length */
 	int	size		/* MAC size */
@@ -110,19 +183,24 @@ mac_authdecrypt(
 	 * was created.
 	 */
 	EVP_MD_CTX_reset(ctx);
-	if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) {
+	if (!EVP_DigestInit_ex(ctx, auth->digest, NULL)) {
 		msyslog(LOG_ERR,
 		    "MAC: decrypt: digest init failed");
 		return false;
 	}
-	EVP_DigestUpdate(ctx, key, key_size);
+	EVP_DigestUpdate(ctx, auth->key, auth->key_size);
 	EVP_DigestUpdate(ctx, (uint8_t *)pkt, (unsigned int)length);
 	EVP_DigestFinal_ex(ctx, digest, &len);
-	if (MAX_BARE_DIGEST_LENGTH < len)
-		len = MAX_BARE_DIGEST_LENGTH;
+	if (MAX_BARE_MAC_LENGTH < len)
+		len = MAX_BARE_MAC_LENGTH;
 	if ((unsigned int)size != len + 4) {
-		msyslog(LOG_ERR,
-		    "MAC: decrypt: MAC length error");
+		/* Beware of DoS attack.
+		 * This indicates either the sender is broken
+		 * or some admin fatfingered things.
+		 * Similar code at cmac_decrypt.
+		 */
+		if (0) msyslog(LOG_ERR,
+		    "ERR: decrypt: digest length error");
 		return false;
 	}
 	return ctmemeq(digest, (char *)pkt + length + 4, len);


=====================================
libntp/ssl_init.c
=====================================
--- a/libntp/ssl_init.c
+++ b/libntp/ssl_init.c
@@ -10,6 +10,7 @@
 
 #include <stdbool.h>
 #include <openssl/evp.h>
+#include <openssl/cmac.h>
 
 #ifndef EVP_MD_CTX_new
 /* Slightly older version of OpenSSL */
@@ -23,6 +24,7 @@ static void	atexit_ssl_cleanup(void);
 
 static bool ssl_init_done;
 EVP_MD_CTX *digest_ctx;
+CMAC_CTX *cmac_ctx;
 
 void
 ssl_init(void)
@@ -32,10 +34,12 @@ ssl_init(void)
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	OpenSSL_add_all_digests();
+	OpenSSL_add_all_ciphers();
 	atexit(&atexit_ssl_cleanup);
 #endif
 
 	digest_ctx = EVP_MD_CTX_new();
+	cmac_ctx = CMAC_CTX_new();
 	ssl_init_done = true;
 }
 


=====================================
ntpclients/ntpkeygen.py
=====================================
--- a/ntpclients/ntpkeygen.py
+++ b/ntpclients/ntpkeygen.py
@@ -10,9 +10,12 @@
 # association maintained by soft links. Following is a list of file
 # types.
 #
-# ntpkey_MD5key_<hostname>.<filestamp>
-# MD5 (128-bit) keys used to compute message digests in symmetric
-# key cryptography
+# ntpkey_AES_<hostname>.<filestamp>
+# AES (128-bit) keys used to compute CMAC mode authentcation
+# using shared key cryptography
+
+# The file can be edited by hand to support MD5 and SHA1 for
+# old digest mode authentcation.
 
 from __future__ import print_function
 
@@ -27,27 +30,27 @@ import stat
 #
 # Cryptodefines
 #
-MD5KEYS = 10    # number of keys generated of each type
-MD5SIZE = 20    # maximum key size
+NUMKEYS = 10    # number of keys generated of each type
+KEYSIZE = 16    # maximum key size
 
 
-def gen_md5(id, groupname):
-    "Generate semi-random MD5 and SHA1 keys compatible with NTPv3 and NTPv4."
-    with fheader("MD5key", id, groupname) as wp:
-        for i in range(1, MD5KEYS+1):
-            md5key = ""
-            for j in range(MD5SIZE):
+def gen_keys(id, groupname):
+    "Generate semi-random AES keys for versions of ntpd with CMAC support."
+    with fheader("AES", id, groupname) as wp:
+        for i in range(1, NUMKEYS+1):
+            key = ""
+            for j in range(KEYSIZE):
                 while True:
                     r = randomizer.randint(0x21, 0x7e)
                     if r != ord('#'):
                         break
-                md5key += chr(r)
-            wp.write("%2d MD5 %s\n" % (i, md5key))
-        for i in range(1, MD5KEYS+1):
-            sha1key = ""
-            for j in range(MD5SIZE):
-                sha1key += "%02x" % randomizer.randint(0x00, 0xff)
-            wp.write("%2d SHA1 %s\n" % (i + MD5KEYS, sha1key))
+                key += chr(r)
+            wp.write("%2d AES %s\n" % (i, key))
+        for i in range(1, NUMKEYS+1):
+            key = ""
+            for j in range(KEYSIZE):
+                key += "%02x" % randomizer.randint(0x00, 0xff)
+            wp.write("%2d AES %s\n" % (i + NUMKEYS, key))
 
 
 #
@@ -85,16 +88,16 @@ if __name__ == '__main__':
 
     for (switch, val) in options:
         if switch == '-M':
-            # dummy MD5 option for backwards compatibility
+            # dummy MD5 option for backwards compatibility, ignored
             pass
         elif switch in ("-h", "--help"):
-            print("usage: ntpkeygen [-M]")
+            print("usage: ntpkeygen")
             raise SystemExit(0)
 
     # The seed is ignored by random.SystemRandom,
     # even though the docs do not say so.
     randomizer = random.SystemRandom()
-    gen_md5("md5", socket.gethostname())
+    gen_keys("AES", socket.gethostname())
     raise SystemExit(0)
 
 # end


=====================================
ntpclients/ntpq.py
=====================================
--- a/ntpclients/ntpq.py
+++ b/ntpclients/ntpq.py
@@ -1405,6 +1405,8 @@ function: show ntpd access control list
 usage: reslist
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_sysinfo(self, _line):
         "display system summary"
         sysinfo = (
@@ -1431,6 +1433,8 @@ function: display system summary
 usage: sysinfo
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_kerninfo(self, _line):
         "display kernel loop and PPS statistics"
         kerninfo = (
@@ -1459,6 +1463,8 @@ function: display kernel loop and PPS statistics
 usage: kerninfo
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_sysstats(self, _line):
         "display system uptime and packet counts"
         sysstats = (
@@ -1484,6 +1490,8 @@ function: display system uptime and packet counts
 usage: sysstats
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_monstats(self, _line):
         "display monitor (mrulist) counters and limits"
         monstats = (
@@ -1511,18 +1519,28 @@ function: display monitor (mrulist) counters and limits
 usage: monstats
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_authinfo(self, _line):
         "display symmetric authentication counters"
         authinfo = (
-            ("authreset", "time since reset:", NTP_INT),
-            ("authkeys", "stored keys:     ", NTP_INT),
-            ("authfreek", "free keys:       ", NTP_INT),
-            ("authklookups", "key lookups:     ", NTP_INT),
-            ("authknotfound", "keys not found:  ", NTP_INT),
-            ("authkuncached", "uncached keys:   ", NTP_INT),
-            ("authkexpired", "expired keys:    ", NTP_INT),
-            ("authencrypts", "encryptions:     ", NTP_INT),
-            ("authdecrypts", "decryptions:     ", NTP_INT),
+            ("authreset",          "time since reset:    ", NTP_INT),
+            ("authkeys",           "stored keys:         ", NTP_INT),
+            ("authfreek",          "free keys:           ", NTP_INT),
+            ("authklookups",       "key lookups:         ", NTP_INT),
+            ("authknotfound",      "keys not found:      ", NTP_INT),
+            ("authencrypts",       "encryptions:         ", NTP_INT),
+            ("authdigestencrypts", "digest encryptions:  ", NTP_INT),
+            ("authcmacencrypts",   "CMAC encryptions:    ", NTP_INT),
+            ("authdecrypts",       "decryptions:         ", NTP_INT),
+            ("authdigestdecrypts", "digest decryptions:  ", NTP_INT),
+            ("authdigestfails",    "digest failures:     ", NTP_INT),
+            ("authcmacdecrypts",   "CMAC decryptions:    ", NTP_INT),
+            ("authcmacfails",      "CMAC failures:       ", NTP_INT),
+        # Old variables no longer supported.
+        # Interesting if looking at an old system.
+            ("authkuncached",      "uncached keys:       ", NTP_INT),
+            ("authkexpired",       "expired keys:        ", NTP_INT),
         )
         self.collect_display(associd=0, variables=authinfo, decodestatus=False)
 
@@ -1532,6 +1550,8 @@ function: display symmetric authentication counters
 usage: authinfo
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_iostats(self, _line):
         "display network input and output counters"
         iostats = (
@@ -1556,6 +1576,8 @@ function: display network input and output counters
 usage: iostats
 """)
 
+# FIXME: This table should move to ntpd
+#          so the answers track when ntpd is updated
     def do_timerstats(self, line):
         "display interval timer counters"
         timerstats = (


=====================================
ntpd/ntp_config.c
=====================================
--- a/ntpd/ntp_config.c
+++ b/ntpd/ntp_config.c
@@ -33,6 +33,7 @@
 #include "lib_strbuf.h"
 #include "ntp_assert.h"
 #include "ntp_dns.h"
+#include "ntp_auth.h"
 
 /*
  * [Classic Bug 467]: Some linux headers collide with CONFIG_PHONE and
@@ -1135,7 +1136,7 @@ config_auth(
 	}
 	if (0 < count)
 		msyslog(LOG_INFO, "Found %d trusted keys.", count);
-	auth_prealloc_symkeys(count);
+	auth_prealloc(count);
 
 	/* Keys Command */
 	if (ptree->auth.keys)
@@ -2897,7 +2898,7 @@ config_reset_counters(
 			break;
 
 		case T_Auth:
-			reset_auth_stats();
+			auth_reset_stats(current_time);
 			break;
 
 		case T_Ctl:


=====================================
ntpd/ntp_control.c
=====================================
--- a/ntpd/ntp_control.c
+++ b/ntpd/ntp_control.c
@@ -25,6 +25,7 @@
 #include "ntp_leapsec.h"
 #include "lib_strbuf.h"
 #include "ntp_syscall.h"
+#include "ntp_auth.h"
 #include "timespecops.h"
 
 /* undefine to suppress random tags and get fixed emission order */
@@ -174,9 +175,9 @@ static const struct ctl_proc control_codes[] = {
 #define	CS_AUTHFREEK		49
 #define	CS_AUTHKLOOKUPS		50
 #define	CS_AUTHKNOTFOUND	51
-#define	CS_AUTHKUNCACHED	52
-#define	CS_AUTHKEXPIRED		53
-#define	CS_AUTHENCRYPTS		54
+#define	CS_AUTHENCRYPTS		52
+#define CS_AUTHDIGESTENCRYPT	53
+#define CS_AUTHCMACENCRYPT	54
 #define	CS_AUTHDECRYPTS		55
 #define	CS_AUTHRESET		56
 #define	CS_K_OFFSET		57
@@ -225,7 +226,11 @@ static const struct ctl_proc control_codes[] = {
 #define	CS_TICK                 98
 #define	CS_NUMCTLREQ		99
 #define CS_ROOTDISTANCE		100
-#define	CS_MAXCODE		CS_ROOTDISTANCE
+#define CS_AUTHDIGESTDECRYPT	101
+#define CS_AUTHDIGESTFAIL	102
+#define CS_AUTHCMACDECRYPT	103
+#define CS_AUTHCMACFAIL		104
+#define	CS_MAXCODE		CS_AUTHCMACFAIL
 
 /*
  * Peer variables we understand
@@ -357,9 +362,9 @@ static const struct ctl_var sys_var[] = {
 	{ CS_AUTHFREEK,		RO, "authfreek" },	/* 49 */
 	{ CS_AUTHKLOOKUPS,	RO, "authklookups" },	/* 50 */
 	{ CS_AUTHKNOTFOUND,	RO, "authknotfound" },	/* 51 */
-	{ CS_AUTHKUNCACHED,	RO, "authkuncached" },	/* 52 */
-	{ CS_AUTHKEXPIRED,	RO, "authkexpired" },	/* 53 */
-	{ CS_AUTHENCRYPTS,	RO, "authencrypts" },	/* 54 */
+	{ CS_AUTHENCRYPTS,	RO, "authencrypts" },	/* 52 */
+	{ CS_AUTHDIGESTENCRYPT,	RO, "authdigestencrypts" },	/* 53 */
+	{ CS_AUTHCMACENCRYPT,	RO, "authcmacencrypts" },	/* 54 */
 	{ CS_AUTHDECRYPTS,	RO, "authdecrypts" },	/* 55 */
 	{ CS_AUTHRESET,		RO, "authreset" },	/* 56 */
 	{ CS_K_OFFSET,		RO, "koffset" },	/* 57 */
@@ -407,6 +412,10 @@ static const struct ctl_var sys_var[] = {
 	/* new in NTPsec */
 	{ CS_NUMCTLREQ,		RO, "ss_numctlreq" },	/* 99 */
 	{ CS_ROOTDISTANCE,	RO, "rootdist" },	/* 100 */
+	{ CS_AUTHDIGESTDECRYPT,	RO, "authdigestdecrypts" },	/* 101 */
+	{ CS_AUTHDIGESTFAIL,	RO, "authdigestfails" },	/* 102 */
+	{ CS_AUTHCMACDECRYPT,	RO, "authcmacdecrypts" },	/* 103 */
+	{ CS_AUTHCMACFAIL,	RO, "authcmacfails" },		/* 104 */
 	{ 0,                    EOV, "" }
 };
 
@@ -611,13 +620,6 @@ static struct utsname utsnamebuf;
 keyid_t ctl_auth_keyid;
 
 /*
- *  * A hack.  To keep the authentication module clear of ntp-ism's, we
- *   * include a time reset variable for its stats here.
- *    */
-static unsigned long auth_timereset;
-
-
-/*
  * We keep track of the last error reported by the system internally
  */
 static	uint8_t ctl_sys_last_event;
@@ -663,9 +665,7 @@ static bool	datanotbinflag;
 static sockaddr_u *rmt_addr;
 static endpt *lcl_inter;
 
-static bool	res_authenticate;
-static bool	res_authokay;
-static keyid_t	res_keyid;
+static auth_info* res_auth;  /* !NULL => authenticate */
 
 #define MAXDATALINELEN	(72)
 
@@ -754,8 +754,8 @@ ctl_error(
 	/*
 	 * send packet and bump counters
 	 */
-	if (res_authenticate) {
-		maclen = authencrypt(res_keyid, (uint32_t *)&rpkt,
+	if (NULL != res_auth) {
+		maclen = authencrypt(res_auth, (uint32_t *)&rpkt,
 				     CTL_HEADER_LEN);
 		sendpkt(rmt_addr, lcl_inter, &rpkt,
                         (int)CTL_HEADER_LEN + maclen);
@@ -833,9 +833,7 @@ process_control(
 	res_frags = 1;
 	res_offset = 0;
 	res_associd = ntohs(pkt->associd);
-	res_authenticate = false;
-	res_keyid = 0;
-	res_authokay = false;
+	res_auth = NULL;
 	req_count = (int)ntohs(pkt->count);
 	datanotbinflag = false;
 	datalinelen = 0;
@@ -864,22 +862,22 @@ process_control(
 	maclen = rbufp->recv_length - (size_t)properlen;
 	if ((rbufp->recv_length & 3) == 0 &&
 	    maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN) {
-		res_authenticate = true;
+		keyid_t keyid;
 		pkid = (void *)((char *)pkt + properlen);
-		res_keyid = ntohl(*pkid);
+		keyid = ntohl(*pkid);
 		DPRINT(3, ("recv_len %zu, properlen %d, wants auth with keyid %08x, MAC length=%zu\n",
-			   rbufp->recv_length, properlen, res_keyid,
+			   rbufp->recv_length, properlen, keyid,
 			   maclen));
 
-		if (!authistrusted(res_keyid))
-			DPRINT(3, ("invalid keyid %08x\n", res_keyid));
-		else if (authdecrypt(res_keyid, (uint32_t *)pkt,
+		res_auth = authlookup(keyid, true);  // FIXME
+		if (NULL == res_auth)
+			DPRINT(3, ("invalid keyid %08x\n", keyid));
+		else if (authdecrypt(res_auth, (uint32_t *)pkt,
 				     (int)rbufp->recv_length - (int)maclen,
 				     (int)maclen)) {
-			res_authokay = true;
 			DPRINT(3, ("authenticated okay\n"));
 		} else {
-			res_keyid = 0;
+			res_auth = NULL;
 			DPRINT(3, ("authentication failed\n"));
 		}
 	}
@@ -898,8 +896,8 @@ process_control(
 			DPRINT(3, ("opcode %d, found command handler\n",
 				   res_opcode));
 			if (cc->flags == AUTH
-			    && (!res_authokay
-				|| res_keyid != ctl_auth_keyid)) {
+			    && (NULL == res_auth
+				|| res_auth->keyid != ctl_auth_keyid)) {
 				ctl_error(CERR_PERMISSION);
 				return;
 			}
@@ -993,7 +991,6 @@ ctl_flushpkt(
 	int sendlen;
 	int maclen;
 	int totlen;
-	keyid_t keyid;
 
 	dlen = datapt - rpkt.data;
 	if (!more && datanotbinflag && dlen + 2 < CTL_MAX_DATA_LEN) {
@@ -1029,7 +1026,8 @@ ctl_flushpkt(
 			(res_opcode & CTL_OP_MASK);
 	rpkt.count = htons((unsigned short)dlen);
 	rpkt.offset = htons((unsigned short)res_offset);
-	if (res_authenticate) {
+	if (NULL != res_auth) {
+		keyid_t keyid;
 		totlen = sendlen;
 		/*
 		 * If we are going to authenticate, then there
@@ -1039,9 +1037,9 @@ ctl_flushpkt(
 		while (totlen & 7) {
 			totlen++;
 		}
-		keyid = htonl(res_keyid);
+		keyid = htonl(res_auth->keyid);
 		memcpy(datapt, &keyid, sizeof(keyid));
-		maclen = authencrypt(res_keyid,
+		maclen = authencrypt(res_auth,
 				     (uint32_t *)&rpkt, totlen);
 		sendpkt(rmt_addr, lcl_inter, &rpkt, totlen + maclen);
 	} else {
@@ -1833,23 +1831,38 @@ ctl_putsys(
 		ctl_putuint(sys_var[varid].text, authkeynotfound);
 		break;
 
-	case CS_AUTHKUNCACHED:
-		ctl_putuint(sys_var[varid].text, authkeyuncached);
+	case CS_AUTHENCRYPTS:
+		ctl_putuint(sys_var[varid].text, authencryptions);
 		break;
 
-	case CS_AUTHKEXPIRED:
-	    /* historical relic - autokey used to expire keys */
-		ctl_putuint(sys_var[varid].text, 0);
+	case CS_AUTHDIGESTENCRYPT:
+		ctl_putuint(sys_var[varid].text, authdigestencrypt);
 		break;
 
-	case CS_AUTHENCRYPTS:
-		ctl_putuint(sys_var[varid].text, authencryptions);
+	case CS_AUTHCMACENCRYPT:
+		ctl_putuint(sys_var[varid].text, authcmacencrypt);
 		break;
 
 	case CS_AUTHDECRYPTS:
 		ctl_putuint(sys_var[varid].text, authdecryptions);
 		break;
 
+	case CS_AUTHDIGESTDECRYPT:
+		ctl_putuint(sys_var[varid].text, authdigestdecrypt);
+		break;
+
+	case CS_AUTHDIGESTFAIL:
+		ctl_putuint(sys_var[varid].text, authdigestfail);
+		break;
+
+	case CS_AUTHCMACDECRYPT:
+		ctl_putuint(sys_var[varid].text, authcmacdecrypt);
+		break;
+
+	case CS_AUTHCMACFAIL:
+		ctl_putuint(sys_var[varid].text, authcmacfail);
+		break;
+
 	case CS_AUTHRESET:
 		ctl_putuint(sys_var[varid].text,
 			    current_time - auth_timereset);
@@ -2698,7 +2711,7 @@ read_status(
 			return;
 		}
 		rpkt.status = htons(ctlpeerstatus(peer));
-		if (res_authokay)
+		if (NULL != res_auth)  /* FIXME: what's this for? */
 			peer->num_events = 0;
 		/*
 		 * For now, output everything we know about the
@@ -2751,7 +2764,7 @@ read_peervars(void)
 		return;
 	}
 	rpkt.status = htons(ctlpeerstatus(peer));
-	if (res_authokay)
+	if (NULL != res_auth)  /* FIXME: What's this for?? */
 		peer->num_events = 0;
 	ZERO(wants);
 	gotvar = false;
@@ -2796,7 +2809,7 @@ read_sysvars(void)
 	 * and give them to him.
 	 */
 	rpkt.status = htons(ctlsysstatus());
-	if (res_authokay)
+	if (NULL != res_auth)  /* FIXME: what's this for?? */
 		ctl_sys_num_events = 0;
 	wants_count = CS_MAXCODE + 1 + count_var(ext_sys_var);
 	wants = emalloc_zero(wants_count);
@@ -4460,21 +4473,3 @@ free_varlist(
 	}
 }
 
-
-/* from ntp_request.c when ntpdc was nuked */
-
-/*
- *  * reset_auth_stats - reset the authentication stat counters.  Done here
- *   *                    to keep ntp-isms out of the authentication module
- *    */
-void
-reset_auth_stats(void)
-{
-        authkeylookups = 0;
-        authkeynotfound = 0;
-        authencryptions = 0;
-        authdecryptions = 0;
-        authkeyuncached = 0;
-        auth_timereset = current_time;
-}
-


=====================================
ntpd/ntp_peer.c
=====================================
--- a/ntpd/ntp_peer.c
+++ b/ntpd/ntp_peer.c
@@ -9,6 +9,7 @@
 #include "ntpd.h"
 #include "ntp_lists.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 
 
 /*
@@ -553,7 +554,13 @@ newpeer(
 	)
 {
 	struct peer *	peer;
-	unsigned int		hash;
+	unsigned int	hash;
+	const char *	name;	/* for error messages */
+
+	if (NULL != hostname)
+		name = hostname;
+	else
+		name = socktoa(srcadr);
 
 	/*
 	 * First search from the beginning for an association with given
@@ -589,10 +596,7 @@ newpeer(
 	 * associations.
 	 */
 	if (peer != NULL) {
-		DPRINT(2, ("newpeer(%s) found existing association\n",
-			   (hostname)
-			   ? hostname
-			   : socktoa(srcadr)));
+		DPRINT(2, ("newpeer(%s) found existing association\n", name));
 		return NULL;
 	}
 
@@ -656,6 +660,16 @@ newpeer(
 	if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
 		enable_broadcast(peer->dstadr, srcadr);
 
+	/* if a key specified, verify that it will work */
+	if (0 != peer->cfg.peerkey) {
+		if (NULL == authlookup(peer->cfg.peerkey, false))
+			msyslog(LOG_ERR, "ERR: key %u not found for server %s",
+				peer->cfg.peerkey, name);
+		else if (NULL == authlookup(peer->cfg.peerkey, true))
+			msyslog(LOG_ERR, "ERR: key %u found but not trusted for server %s",
+				peer->cfg.peerkey, name); 
+		}
+
 	peer->precision = sys_vars.sys_precision;
 	peer->hpoll = peer->cfg.minpoll;
 	if (cast_flags & MDF_POOL)


=====================================
ntpd/ntp_proto.c
=====================================
--- a/ntpd/ntp_proto.c
+++ b/ntpd/ntp_proto.c
@@ -9,6 +9,7 @@
 #include "ntp_stdlib.h"
 #include "ntp_leapsec.h"
 #include "ntp_dns.h"
+#include "ntp_auth.h"
 #include "timespecops.h"
 
 #include <string.h>
@@ -154,7 +155,7 @@ double	measured_tick;		/* non-overridable sys_tick (s) */
 static	void	clock_combine	(peer_select *, int, int);
 static	void	clock_select	(void);
 static	void	clock_update	(struct peer *);
-static	void	fast_xmit	(struct recvbuf *, int, keyid_t, int);
+static	void	fast_xmit	(struct recvbuf *, int, auth_info*, int);
 static	int	local_refid	(struct peer *);
 static	void	measure_precision(const bool);
 static	double	measure_tick_fuzz(void);
@@ -344,7 +345,7 @@ parse_packet(
 		rbufp->mac_len = 0;
 		break;
 	    case 20:
-		/* MD5 authenticator */
+		/* AES-128 CMAC, MD5 digest */
 		if(PKT_VERSION(pkt->li_vn_mode) < 3) {
 			/* Only allowed as of NTPv3 */
 			goto fail;
@@ -354,7 +355,7 @@ parse_packet(
 		rbufp->mac_len = 16;
 		break;
 	    case 24:
-		/* SHA-1 authenticator */
+		/* SHA-1 digest */
 		if(PKT_VERSION(pkt->li_vn_mode) < 3) {
 			/* Only allowed as of NTPv3 */
 			goto fail;
@@ -407,20 +408,13 @@ parse_packet(
 static bool
 i_require_authentication(
 	struct peer const* peer,
-	struct recvbuf const* rbufp,
 	unsigned short restrict_mask
 	)
 {
         bool restrict_notrust = restrict_mask & RES_DONTTRUST;
         bool peer_has_key = peer != NULL && peer->cfg.peerkey != 0;
-        bool wants_association =
-            PKT_MODE(rbufp->pkt.li_vn_mode) == MODE_BROADCAST ||
-            (peer == NULL && PKT_MODE(rbufp->pkt.li_vn_mode == MODE_ACTIVE));
-        bool restrict_nopeer =
-            (restrict_mask & RES_NOPEER) &&
-            wants_association;
-
-        return restrict_notrust || peer_has_key || restrict_nopeer;
+
+        return restrict_notrust || peer_has_key;
 }
 
 static bool
@@ -462,32 +456,12 @@ static void
 handle_fastxmit(
 	struct recvbuf *rbufp,
 	unsigned short restrict_mask,
-	bool request_already_authenticated
+	auth_info* auth
 	)
 {
-	uint32_t xkeyid;
-
-	if (rbufp->dstadr->flags & INT_MCASTOPEN) {
-			stat_count.sys_restricted++;
-	}
-
-	/* To prevent exposing an authentication oracle, only MAC
-	   the response if the request passed authentication.
-	*/
-	if(request_already_authenticated ||
-	   (rbufp->keyid_present &&
-	    authdecrypt(rbufp->keyid,
-			(uint32_t*)rbufp->recv_space.X_recv_buffer,
-			(int)(rbufp->recv_length - (rbufp->mac_len + 4)),
-			(int)(rbufp->mac_len + 4)))) {
-		xkeyid = rbufp->keyid;
-	} else {
-		xkeyid = 0;
-	}
-
         int xmode =
             PKT_MODE(rbufp->pkt.li_vn_mode) == MODE_ACTIVE ? MODE_PASSIVE : MODE_SERVER;
-	fast_xmit(rbufp, xmode, xkeyid, restrict_mask);
+	fast_xmit(rbufp, xmode, auth, restrict_mask);
 }
 
 static void
@@ -668,7 +642,7 @@ receive(
 {
 	struct peer *peer = NULL;
 	unsigned short restrict_mask;
-	bool authenticated = false;
+	auth_info* auth = NULL;  /* !NULL if authenticated */
 
 	stat_count.sys_received++;
 
@@ -738,7 +712,16 @@ receive(
 	    }
 	}
 
-	if(i_require_authentication(peer, rbufp, restrict_mask)) {
+	if(i_require_authentication(peer, restrict_mask) ||
+	    /* He wants authentication */
+	    rbufp->keyid_present) {
+		auth = authlookup(rbufp->keyid, true);
+		if (0) msyslog(LOG_INFO, "DEBUG: receive: key %u %s%s, length %d, %s",
+		    rbufp->keyid,
+		    (NULL == auth)? "N" : "A",
+		    (NULL == peer)? "N" : "P",
+		    rbufp->mac_len, socktoa(&rbufp->recv_srcadr) );
+		// FIXME: crypto-NAK?
 		if(
 			/* Check whether an authenticator is even present. */
 			!rbufp->keyid_present || is_crypto_nak(rbufp) ||
@@ -746,12 +729,13 @@ receive(
 			   check that it matches. */
 			(peer != NULL && peer->cfg.peerkey != 0 &&
 			 peer->cfg.peerkey != rbufp->keyid) ||
+			(auth == NULL) ||
 			/* Verify the MAC.
 			   TODO: rewrite authdecrypt() to give it a
 			   better name and a saner interface so we don't
 			   have to do this screwy buffer-length
 			   arithmetic in order to call it. */
-			!authdecrypt(rbufp->keyid,
+			!authdecrypt(auth,
 				 (uint32_t*)rbufp->recv_space.X_recv_buffer,
 				 (int)(rbufp->recv_length - (rbufp->mac_len + 4)),
 				 (int)(rbufp->mac_len + 4))) {
@@ -762,8 +746,6 @@ receive(
 				peer->flash |= BOGON5;
 			}
 			goto done;
-		} else {
-			authenticated = true;
 		}
 	}
 
@@ -775,7 +757,7 @@ receive(
 	switch (PKT_MODE(rbufp->pkt.li_vn_mode)) {
 	    case MODE_ACTIVE:  /* remote site using "peer" in config file */
 	    case MODE_CLIENT:  /* Request for us as a server. */
-		handle_fastxmit(rbufp, restrict_mask, authenticated);
+		handle_fastxmit(rbufp, restrict_mask, auth);
 		stat_count.sys_processed++;
 		break;
 	    case MODE_SERVER:  /* Reply to our request. */
@@ -2088,7 +2070,7 @@ peer_xmit(
 {
 	struct pkt xpkt;	/* transmit packet */
 	size_t	sendlen, authlen;
-	keyid_t	xkeyid = 0;	/* transmit key ID */
+	auth_info *auth;	/* !NULL for authentication */
 	l_fp	xmt_tx;
 
 	if (!peer->dstadr)	/* drop peers without interface */
@@ -2107,9 +2089,8 @@ peer_xmit(
 	xpkt.rec = htonl_fp(peer->dst);
 
 	/*
-	 * If the received packet contains a MAC, the transmitted packet
-	 * is authenticated and contains a MAC. If not, the transmitted
-	 * packet is not authenticated.
+	 * If the peer (aka server) was configured with a key authenticate
+	 * the packet.  Else, the packet is not authenticated.
 	 */
 	sendlen = LEN_PKT_NOMAC;
 	if (peer->cfg.peerkey == 0) {
@@ -2144,14 +2125,14 @@ peer_xmit(
 	get_systime(&xmt_tx);
 	peer->org = xmt_tx;
 	xpkt.xmt = htonl_fp(xmt_tx);
-	xkeyid = peer->cfg.peerkey;
-	authlen = (size_t)authencrypt(xkeyid, (uint32_t *)&xpkt, (int)sendlen);
-	if (authlen == 0) {
+	auth = authlookup(peer->cfg.peerkey, true);
+	if (NULL == auth) {
 		report_event(PEVNT_AUTH, peer, "no key");
 		peer->flash |= BOGON5;		/* auth error */
 		peer->badauth++;
 		return;
 	}
+	authlen = (size_t)authencrypt(auth, (uint32_t *)&xpkt, (int)sendlen);
 	sendlen += authlen;
 	if (sendlen > sizeof(xpkt)) {
 		msyslog(LOG_ERR, "PROTO: buffer overflow %zu", sendlen);
@@ -2165,7 +2146,8 @@ peer_xmit(
 	DPRINT(1, ("transmit: at %u %s->%s mode %d keyid %08x len %zu\n",
 		   current_time, peer->dstadr ?
 		   socktoa(&peer->dstadr->sin) : "-",
-		   socktoa(&peer->srcadr), peer->hmode, xkeyid, sendlen));
+		   socktoa(&peer->srcadr), peer->hmode,
+		   peer->cfg.peerkey, sendlen));
 }
 
 
@@ -2187,7 +2169,7 @@ static void
 fast_xmit(
 	struct recvbuf *rbufp,	/* receive packet pointer */
 	int	xmode,		/* receive mode */
-	keyid_t	xkeyid,		/* transmit key ID */
+	auth_info *auth,	/* !NULL for authentication */
 	int	flags		/* restrict mask */
 	)
 {
@@ -2295,7 +2277,9 @@ fast_xmit(
 
 #ifdef ENABLE_MSSNTP
 	if (flags & RES_MSSNTP) {
-		send_via_ntp_signd(rbufp, xmode, xkeyid, flags, &xpkt);
+		keyid_t keyid = 0;
+		if (NULL != auth) keyid = auth->keyid;
+		send_via_ntp_signd(rbufp, xmode, keyid, flags, &xpkt);
 		return;
 	}
 #endif /* ENABLE_MSSNTP */
@@ -2306,7 +2290,7 @@ fast_xmit(
 	 * packet is not authenticated.
 	 */
 	sendlen = LEN_PKT_NOMAC;
-	if (rbufp->recv_length == sendlen) {
+	if (NULL == auth) {
 		sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, (int)sendlen);
 		DPRINT(1, ("transmit: at %u %s->%s mode %d len %zu\n",
 			   current_time, socktoa(&rbufp->dstadr->sin),
@@ -2321,14 +2305,14 @@ fast_xmit(
 	 * cryptosum.
 	 */
 	get_systime(&xmt_tx);
-	sendlen += (size_t)authencrypt(xkeyid, (uint32_t *)&xpkt, (int)sendlen);
+	sendlen += (size_t)authencrypt(auth, (uint32_t *)&xpkt, (int)sendlen);
 	sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, (int)sendlen);
 	get_systime(&xmt_ty);
 	xmt_ty -= xmt_tx;
 	sys_authdelay = xmt_ty;
 	DPRINT(1, ("transmit: at %u %s->%s mode %d keyid %08x len %zu\n",
 		   current_time, socktoa(&rbufp->dstadr->sin),
-		   socktoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen));
+		   socktoa(&rbufp->recv_srcadr), xmode, auth->keyid, sendlen));
 }
 
 


=====================================
ntpd/ntp_util.c
=====================================
--- a/ntpd/ntp_util.c
+++ b/ntpd/ntp_util.c
@@ -11,6 +11,7 @@
 #include "ntp_leapsec.h"
 #include "ntp_stdlib.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 #include "ntpd.h"
 #include "timespecops.h"
 


=====================================
ntpd/ntpd.c
=====================================
--- a/ntpd/ntpd.c
+++ b/ntpd/ntpd.c
@@ -12,6 +12,7 @@
 #include "ntp_config.h"
 #include "ntp_syslog.h"
 #include "ntp_assert.h"
+#include "ntp_auth.h"
 #ifdef ENABLE_DNS_LOOKUP
 #include "ntp_dns.h"
 #endif
@@ -663,7 +664,7 @@ ntpdmain(
 	 * Exactly what command-line options are we expecting here?
 	 */
 	ssl_init();
-	init_auth();
+	auth_init();
 	init_util();
 	init_restrict();
 	init_mon();


=====================================
tests/common/tests_main.c
=====================================
--- a/tests/common/tests_main.c
+++ b/tests/common/tests_main.c
@@ -78,7 +78,7 @@ static void RunAllTests(void)
 int main(int argc, const char * argv[]) {
 
 	ssl_init();
-	init_auth();
+	auth_init();
 	init_network();
 
 	args_argc = argc;


=====================================
tests/common/tests_main.h
=====================================
--- a/tests/common/tests_main.h
+++ b/tests/common/tests_main.h
@@ -7,6 +7,7 @@
 #include "unity_fixture.h"
 
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 
 const char* tests_main_args(int arg);
 


=====================================
tests/libntp/authkeys.c
=====================================
--- a/tests/libntp/authkeys.c
+++ b/tests/libntp/authkeys.c
@@ -1,5 +1,6 @@
 #include "config.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 
 #include "unity.h"
 #include "unity_fixture.h"
@@ -26,19 +27,15 @@ TEST_TEAR_DOWN(authkeys) {}
 
 
 
-static const int KEYTYPE = NID_md5;
-
-
 static void AddTrustedKey(keyid_t);
 static void AddUntrustedKey(keyid_t);
 
 static void AddTrustedKey(keyid_t keyno) {
 	/*
-	 * We need to add a MD5-key in addition to setting the
-	 * trust, because authhavekey() requires type != 0.
+	 * We need to add a type and key in addition to setting the
+	 * trust, because authlookup() requires type != AUTH_NONE.
 	 */
-	mac_setkey(keyno, KEYTYPE, NULL, 0);
-
+	auth_setkey(keyno, AUTH_DIGEST, "MD5", NULL, 0);
 	authtrust(keyno, true);
 }
 
@@ -54,16 +51,24 @@ TEST(authkeys, AddTrustedKeys) {
 	AddTrustedKey(KEYNO1);
 	AddTrustedKey(KEYNO2);
 
-	TEST_ASSERT_TRUE(authistrusted(KEYNO1));
-	TEST_ASSERT_TRUE(authistrusted(KEYNO2));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO1, true));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO1, false));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO2, true));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO2, false));
 }
 
 TEST(authkeys, AddUntrustedKey) {
 	const keyid_t KEYNO = 3;
 
-	AddUntrustedKey(KEYNO);
+	AddUntrustedKey(KEYNO);  /* gets type of AUTH_NULL */
+
+	TEST_ASSERT_NULL(authlookup(KEYNO, true));
+	TEST_ASSERT_NULL(authlookup(KEYNO, false));
+
+	auth_setkey(KEYNO, AUTH_DIGEST, "MD5", NULL, 0);
 
-	TEST_ASSERT_FALSE(authistrusted(KEYNO));
+	TEST_ASSERT_NULL(authlookup(KEYNO, true));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO, false));
 }
 
 TEST(authkeys, HaveKeyCorrect) {
@@ -71,13 +76,15 @@ TEST(authkeys, HaveKeyCorrect) {
 
 	AddTrustedKey(KEYNO);
 
-	TEST_ASSERT_TRUE(authhavekey(KEYNO));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO, true));
+	TEST_ASSERT_NOT_NULL(authlookup(KEYNO, false));
 }
 
 TEST(authkeys, HaveKeyIncorrect) {
 	const keyid_t KEYNO = 2;
 
-	TEST_ASSERT_FALSE(authhavekey(KEYNO));
+	TEST_ASSERT_NULL(authlookup(KEYNO, true));
+	TEST_ASSERT_NULL(authlookup(KEYNO, false));
 }
 
 TEST_GROUP_RUNNER(authkeys) {


=====================================
tests/libntp/macencrypt.c
=====================================
--- a/tests/libntp/macencrypt.c
+++ b/tests/libntp/macencrypt.c
@@ -1,5 +1,6 @@
 #include "config.h"
 #include "ntp_stdlib.h"
+#include "ntp_auth.h"
 
 #include "unity.h"
 #include "unity_fixture.h"
@@ -17,45 +18,112 @@ TEST_TEAR_DOWN(macencrypt) {}
 /*
  * Example packet with MD5 hash calculated manually.
  */
-const int keytype = NID_md5;
-char key[] = "abcdefgh";
-const unsigned short keyLength = 8;
+char MD5key[] = "abcdefgh";
+char CMACkey[] = "0123456789abcdef";  /* AES-128 needs 16 bytes */
 const char *packet = "ijklmnopqrstuvwx";
 const int packetLength = 16;
 const int keyIdLength = 4;
 const int digestLength = 16;
 const int totalLength = 36; //error: initializer element is not constant packetLength + keyIdLength + digestLength;
-char expectedPacket[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x53";
+char expectedMD5Packet[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x53";
+char expectedCMACPacket[] = "ijklmnopqrstuvwx\0\0\0\0\xb0\xa1\xcf\xd2\x7f\x69\x0c\x43\xa7\x5d\x6c\x55\x91\x4b\x15\x14";
+
+auth_info auth;
 
 TEST(macencrypt, Encrypt) {
-	char *packetPtr[totalLength];
+	char packetPtr[totalLength];
+	memset(packetPtr+packetLength, 0, (size_t)keyIdLength);
+	memcpy(packetPtr, packet, (size_t)packetLength);
+
+	/* FIXME: Initialization stuff. Needed by other digest tests. */
+	auth.keyid = 123;
+	auth.type = AUTH_DIGEST;
+	auth.digest = EVP_get_digestbyname("MD5");
+	auth.cipher = NULL;
+	auth.key = (uint8_t *)MD5key;
+	auth.key_size = (unsigned short)strlen(MD5key);
+
+	TEST_ASSERT_NOT_NULL(auth.digest);
+
+	int length = digest_encrypt(&auth,
+		(uint32_t*)packetPtr, packetLength);
+
+	TEST_ASSERT_EQUAL(4+16, length);
+
+	TEST_ASSERT_TRUE(digest_decrypt(&auth,
+		(uint32_t*)packetPtr, packetLength, length));
+
+if (0) {
+	int i;
+        printf("\n");
+	for (i = 0; i< totalLength; i++)
+	  printf("%02x ", (unsigned int)expectedMD5Packet[i] & 0xff);
+        printf("\n");
+	for (i = 0; i< totalLength; i++)
+	  printf("%02x ", (unsigned int)packetPtr[i] & 0xff);
+        printf("\n");
+}
+	TEST_ASSERT_TRUE(memcmp(expectedMD5Packet, packetPtr, totalLength) == 0);
+
+}
+TEST(macencrypt, CMAC_Encrypt) {
+	char packetPtr[totalLength];
 	memset(packetPtr+packetLength, 0, (size_t)keyIdLength);
 	memcpy(packetPtr, packet, (size_t)packetLength);
 
-	int length = mac_authencrypt(keytype,
-		(unsigned char*)key, keyLength,
+	/* FIXME: Initialization stuff. Needed by other CMAC tests. */
+	auth.keyid = 1234;
+	auth.type = AUTH_CMAC;
+	auth.digest = NULL;
+	auth.cipher = EVP_get_cipherbyname("AES-128-CBC");
+	auth.key = (uint8_t *)CMACkey;
+	auth.key_size = (unsigned short)strlen(CMACkey);
+
+	TEST_ASSERT_NOT_NULL(auth.cipher);
+
+	int length = cmac_encrypt(&auth,
 		(uint32_t*)packetPtr, packetLength);
 
-	TEST_ASSERT_TRUE(mac_authdecrypt(keytype,
-		(unsigned char*)key, keyLength,
+	TEST_ASSERT_EQUAL(4+16, length);            /* AES-128 */
+
+	TEST_ASSERT_TRUE(cmac_decrypt(&auth,
 		(uint32_t*)packetPtr, packetLength, length));
 
-	TEST_ASSERT_EQUAL(20, length);
-//XXX	TEST_ASSERT_TRUE(memcmp(expectedPacket, packetPtr, totalLength) == 0);  Does not pass
+if (0) {
+	int i;
+        printf("\n");
+	for (i = 0; i< totalLength; i++)
+	  printf("%02x ", (unsigned int)expectedCMACPacket[i] & 0xff);
+        printf("\n");
+	for (i = 0; i< totalLength; i++)
+	  printf("%02x ", (unsigned int)packetPtr[i] & 0xff);
+        printf("\n");
+}
+	TEST_ASSERT_TRUE(memcmp(expectedCMACPacket, packetPtr, totalLength) == 0);
 
 }
 
 TEST(macencrypt, DecryptValid) {
-	TEST_ASSERT_TRUE(mac_authdecrypt(keytype,
-		(unsigned char*)key, keyLength,
-		(uint32_t*)expectedPacket, packetLength, 20));
+	TEST_ASSERT_TRUE(digest_decrypt(&auth,
+		(uint32_t*)expectedMD5Packet, packetLength, 20));
+}
+
+TEST(macencrypt, DecryptValidCMAC) {
+	TEST_ASSERT_TRUE(cmac_decrypt(&auth,
+		(uint32_t*)expectedCMACPacket, packetLength, 20));
 }
 
 TEST(macencrypt, DecryptInvalid) {
 	char invalidPacket[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x54";
 
-	TEST_ASSERT_FALSE(mac_authdecrypt(keytype,
-		(unsigned char*)key, keyLength,
+	TEST_ASSERT_FALSE(digest_decrypt(&auth,
+		(uint32_t*)invalidPacket, packetLength, 20));
+}
+
+TEST(macencrypt, DecryptInvalidCMAC) {
+	char invalidPacket[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x54";
+
+	TEST_ASSERT_FALSE(cmac_decrypt(&auth,
 		(uint32_t*)invalidPacket, packetLength, 20));
 }
 
@@ -72,9 +140,9 @@ TEST(macencrypt, IPv4AddressToRefId) {
 TEST(macencrypt, IPv6AddressToRefId) {
 	const struct in6_addr address = {{{
 		0x20, 0x01, 0x0d, 0xb8,
-        0x85, 0xa3, 0x08, 0xd3,
-        0x13, 0x19, 0x8a, 0x2e,
-        0x03, 0x70, 0x73, 0x34
+		0x85, 0xa3, 0x08, 0xd3,
+		0x13, 0x19, 0x8a, 0x2e,
+		0x03, 0x70, 0x73, 0x34
 	}}};
 
 
@@ -87,10 +155,17 @@ TEST(macencrypt, IPv6AddressToRefId) {
 	TEST_ASSERT_EQUAL(expected, addr2refid(&addr));
 }
 
+/* Both digest and CMAC tests share some global variables
+ * that get setup by Encrypt or CMAC_Encrypt
+ * Thus the tests must be run in the right order.
+ */
 TEST_GROUP_RUNNER(macencrypt) {
 	RUN_TEST_CASE(macencrypt, Encrypt);
 	RUN_TEST_CASE(macencrypt, DecryptValid);
 	RUN_TEST_CASE(macencrypt, DecryptInvalid);
+	RUN_TEST_CASE(macencrypt, CMAC_Encrypt);
+	RUN_TEST_CASE(macencrypt, DecryptValidCMAC);
+	RUN_TEST_CASE(macencrypt, DecryptInvalidCMAC);
 	RUN_TEST_CASE(macencrypt, IPv4AddressToRefId);
 	RUN_TEST_CASE(macencrypt, IPv6AddressToRefId);
 }


=====================================
wscript
=====================================
--- a/wscript
+++ b/wscript
@@ -609,8 +609,9 @@ int main(int argc, char **argv) {
     ):
         openssl_headers = (
             "openssl/evp.h",
-            "openssl/rand.h",
+            "openssl/cmac.h",
             "openssl/objects.h",
+            "openssl/rand.h",
         )
         for hdr in openssl_headers:
             ctx.check_cc(header_name=hdr, includes=ctx.env.PLATFORM_INCLUDES)
@@ -661,6 +662,13 @@ int main(int argc, char **argv) {
     ctx.check_cc(header_name="stdbool.h", mandatory=True,
                  comment="Sanity check.")
 
+    # Very old versions of OpenSSL don't have cmac support.
+    # This gives a sane(er) error message.
+    # It would be possible to make CMAC support optional by adding
+    # appropriate #ifdefs to the code.
+    ctx.check_cc(header_name="openssl/cmac.h", mandatory=True,
+                 comment="Sanity check.")
+
     # This is a list of every optional include header in the
     # codebase that is guarded by a directly corresponding HAVE_*_H symbol.
     #



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/84660a737666758363bb536396e6eff90ea529d5...a04100c3f004e643ba4911fce3cf7a031531a2c9

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/84660a737666758363bb536396e6eff90ea529d5...a04100c3f004e643ba4911fce3cf7a031531a2c9
You're receiving this email because of your account on gitlab.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20180613/ad6ea0e6/attachment.html>


More information about the vc mailing list