[Git][NTPsec/ntpsec][master] First pass at cookies

Hal Murray gitlab at mg.gitlab.com
Sun Feb 17 12:25:19 UTC 2019


Hal Murray pushed to branch master at NTPsec / ntpsec


Commits:
b0275b41 by Hal Murray at 2019-02-17T12:09:47Z
First pass at cookies

- - - - -


6 changed files:

- + devel/TODO-NTS
- include/nts.h
- ntpd/nts_client.c
- + ntpd/nts_cookie.c
- ntpd/nts_server.c
- ntpd/wscript


Changes:

=====================================
devel/TODO-NTS
=====================================
@@ -0,0 +1,18 @@
+flag for require NTS
+
+security level
+
+multithread msyslog
+
+fixup seccomp
+
+show NTS flag via ntpq
+  extra credit if we can find a place on the peers display
+show NTS statistics
+
+documentation:
+  HOWTO on NTS
+  HOWTO on certificates
+  glossary: https://letsencrypt.org/docs/glossary/
+
+client certificates


=====================================
include/nts.h
=====================================
@@ -6,8 +6,9 @@
 
 #include <openssl/ssl.h>
 
-#define NTS_MAX_COOKIES	8	/* RFC 4.1.6 */
-#define NTS_COOKIELEN	128	/* placeholder - see RFC 6 */
+#define NTS_MAX_KEYLEN		64	/* used in cookies */
+#define NTS_MAX_COOKIELEN	192	/* see nts_cookie.c */
+#define NTS_MAX_COOKIES		8	/* RFC 4.1.6 */
 
 #define FLAG_NTS	0x01u	/* use NTS (network time security) */
 #define FLAG_NTS_ASK	0x02u	/* NTS, ask for specified server */
@@ -23,13 +24,14 @@ struct ntscfg_t {
     uint32_t expire;
 };
 
-// FIXME AEAD_AES_SIV_CMAC_256
 // We are using AEAD_AES_SIV_CMAC_256, from RFC 5297
-// There is no clean API yet
 #define IANA_AEAD_AES_SIV_CMAC_256 15
+#define IANA_AEAD_AES_SIV_CMAC_384 16
+#define IANA_AEAD_AES_SIV_CMAC_512 17
 #define AEAD_AES_SIV_CMAC_256_KEYLEN 32
+#define AEAD_AES_SIV_CMAC_384_KEYLEN 48
+#define AEAD_AES_SIV_CMAC_512_KEYLEN 64
 
-#define NTS_MAX_KEYLEN 64
 /* Client-side state per connection to server */
 struct ntsstate_t {
     int aead;
@@ -38,7 +40,7 @@ struct ntsstate_t {
     int cookie_count;
     int cookie_length;
     bool valid[NTS_MAX_COOKIES];
-    uint8_t cookies[NTS_MAX_COOKIES][NTS_COOKIELEN];
+    uint8_t cookies[NTS_MAX_COOKIES][NTS_MAX_COOKIELEN];
     uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
 };
 
@@ -91,8 +93,8 @@ enum aead_ciphers {
   AEAD_AES_128_CCM_SHORT_12 = 13,
   AEAD_AES_256_CCM_SHORT_12 = 14,
 
-  AEAD_AES_SIV_CMAC_256 = 15,
-  AEAD_AES_SIV_CMAC_384 = 16,
+  AEAD_AES_SIV_CMAC_256 = 15,     /* RFC 5297 */
+  AEAD_AES_SIV_CMAC_384 = 16,     /* These 3 are the ones we use */
   AEAD_AES_SIV_CMAC_512 = 17,
 
   AEAD_AES_128_CCM_8 = 18,
@@ -117,14 +119,21 @@ extern struct ntsconfig_t ntsconfig;
 
 bool nts_server_init(void);
 bool nts_client_init(void);
+bool nts_cookie_init(void);
 void nts_log_ssl_error(void);
 
-int nts_get_key_length(int aead);
 bool nts_load_ciphers(SSL_CTX *ctx);
 bool nts_load_versions(SSL_CTX *ctx);
+
+int nts_get_key_length(int aead);
 bool nts_make_keys(SSL *ssl, uint8_t *c2s, uint8_t *s2c, int keylen);
-int nts_make_cookie(uint8_t *cookie, uint16_t aead,
+
+int nts_make_cookie(uint8_t *cookie,
+  uint16_t aead,
   uint8_t *c2s, uint8_t *s2c, int keylen);
+bool nts_unpack_cookie(uint8_t *cookie, int cookielen,
+  uint16_t *aead,
+  uint8_t *c2s, uint8_t *s2c, int *keylen);
 
 #define NO_OLD_VERSIONS SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1
 


=====================================
ntpd/nts_client.c
=====================================
@@ -351,7 +351,7 @@ bool nts_client_process_response(struct peer* peer, SSL *ssl) {
         peer->nts_state.aead = data;
         break;
       case nts_new_cookie:
-        if (NTS_COOKIELEN < length) {
+        if (NTS_MAX_COOKIELEN < length) {
           msyslog(LOG_ERR, "NTSc: NC cookie too big: %d", length);
           return false;
         }


=====================================
ntpd/nts_cookie.c
=====================================
@@ -0,0 +1,201 @@
+/*
+ * nts_cookie.c - Network Time Security (NTS) cookie processing
+ *
+ * Section references are to
+ * https://tools.ietf.org/html/draft-ietf-ntp-using-nts-for-ntp-15
+ *
+ * This follows section 6, Suggested Format for NTS Cookies
+ * It uses AEAD_AES_SIV_CMAC_256/384/512 from RFC 5297
+ * It is currently a stand-alone library
+ * but will probably migrate to OpenSSL/libcrypto.
+ *
+ * The selection is done by the key length.
+ *
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+#include <aes_siv.h>
+
+#include "ntp_stdlib.h"
+#include "nts.h"
+
+/* Cookie format:
+ *  cookie is I,N,CMAC,C
+ *    I Key index, see below
+ *    N nonce
+ *  C is encrypt(K, N, P)
+ *  P is AEAD, C2S, S2C
+ *  length of C2S and S2C depends upon AEAD
+ *  CMAC is 16 bytes
+ */
+
+/* K and I should be preserved across boots, and rotated every day or so.
+ * We need to support the old K/I for another day.
+ * Encryption within cookies uses AEAD_AES_SIV_CMAC_nnn.  That's the
+ * same family of algorithims as NTS uses on the wire.
+ * The nnn is selected by the key length.
+ *   32 => 256
+ *   48 => 384
+ *   64 => 512
+ */
+
+/* Max length:
+ *   4 I
+ *  16 N
+ *  16 CMAC
+ *   4 AEAD
+ *  64 C2S    NTS_MAX_KEYLEN
+ *  64 S2C    NTS_MAX_KEYLEN
+ * ------
+ * 168
+ */
+
+/* cookies use same algorithms as wire */
+uint8_t K[NTS_MAX_KEYLEN];
+uint32_t I;
+
+AES_SIV_CTX* cookie_ctx;  /* need one per thread */
+
+/* This determines which algorithm we use. */
+/* making this a variable rather than #define
+ * opens up the opportunity to pick one at run time. */
+int K_length = AEAD_AES_SIV_CMAC_256_KEYLEN;
+#define NONCE_LENGTH 16
+
+/* Associated data: aead (rounded up to 4) plus NONCE */
+#define AD_LENGTH 20
+#define AEAD_LENGTH 4
+
+bool nts_cookie_init(void) {
+  bool OK = true;
+#if (OPENSSL_VERSION_NUMBER > 0x1010100fL)
+  OK &= RAND_priv_bytes(K, sizeof(K));
+  OK &= RAND_bytes((uint8_t *)&I, sizeof(I));
+#else
+  OK &= RAND_bytes(K, sizeof(K));
+  OK &= RAND_bytes((uint8_t *)&I, sizeof(I));
+#endif
+  cookie_ctx = AES_SIV_CTX_new();
+  if (NULL == cookie_ctx)
+    OK = false;
+  return OK;
+}
+
+/* returns actual length */
+int nts_make_cookie(uint8_t *cookie,
+  uint16_t aead,
+  uint8_t *c2s, uint8_t *s2c, int keylen) {
+  uint8_t plaintext[NTS_MAX_COOKIELEN];
+  uint8_t *nonce;
+  int used, plainlength;
+  bool ok;
+
+  // ASSERT(keylen<NTS_MAX_KEYLEN);
+  
+  uint8_t * finger;
+  uint32_t temp;      /* keep 4 byte alignment */
+  size_t left;
+
+  /* collect plaintext
+   * separate buffer avoids encrypt in place
+   * but costs cache space
+   */
+  finger = plaintext;
+  temp = aead;
+  memcpy(finger, &temp, AEAD_LENGTH);
+  finger += AEAD_LENGTH;
+  memcpy(finger, c2s, keylen);
+  finger += keylen;
+  memcpy(finger, s2c, keylen);
+  finger += keylen;
+  plainlength = finger-plaintext;
+
+  /* collect associated data */
+  finger = cookie;
+
+  memcpy(finger, &I, sizeof(I));
+  finger += sizeof(I);
+
+  nonce = finger;
+  RAND_bytes(finger, NONCE_LENGTH);
+  finger += NONCE_LENGTH;
+
+  // require(AD_LENGTH==finger-cookie);
+
+  used = finger-cookie;
+  left = NTS_MAX_COOKIELEN-used;
+
+  ok = AES_SIV_Encrypt(cookie_ctx,
+           finger, &left,   /* left: in: max out length, out: length used */
+           K, K_length,
+           nonce, NONCE_LENGTH,
+           plaintext, plainlength,
+           cookie, AD_LENGTH);
+  if (!ok) {
+    msyslog(LOG_ERR, "NTS: Error from AES_SIV_Encrypt");
+    exit(1);
+  }
+
+  used += left;
+  // ASSERT(length < NTS_MAX_COOKIELEN);
+  // Need to encrypt
+
+  return used;
+}
+
+/* can't decrypt in place - that would trash the unauthenticated packet */
+bool nts_unpack_cookie(uint8_t *cookie, int cookielen,
+  uint16_t *aead,
+  uint8_t *c2s, uint8_t *s2c, int *keylen) {
+  uint8_t *finger;
+  uint8_t plaintext[NTS_MAX_COOKIELEN];
+  uint8_t *nonce;
+  uint32_t temp;
+  size_t plainlength;
+  int cipherlength;
+  bool ok;
+
+  finger = cookie;
+  // FIXME should call routine to return key
+  if (0 != memcmp(finger, &I, sizeof(I)))
+    return false;
+  finger += sizeof(I);
+  nonce = finger;
+  finger += NONCE_LENGTH;
+
+  // require(AD_LENGTH==finger-cookie);
+  
+  cipherlength = cookielen - AD_LENGTH;
+  plainlength = NTS_MAX_COOKIELEN;
+
+  ok = AES_SIV_Decrypt(cookie_ctx,
+           plaintext, &plainlength,
+           K, K_length,
+           nonce, NONCE_LENGTH,
+           finger, cipherlength,
+           cookie, AEAD_LENGTH);
+  if (!ok)
+    return false;
+
+  *keylen = (plainlength-AEAD_LENGTH)/2;
+  finger = plaintext;
+  memcpy(&temp, finger, AEAD_LENGTH);
+  *aead = temp;
+  finger += AEAD_LENGTH;
+  memcpy(c2s, finger, *keylen);
+  finger += *keylen;
+  memcpy(s2c, finger, *keylen);
+  finger += *keylen;
+
+  return true;
+}
+
+
+
+/* end */


=====================================
ntpd/nts_server.c
=====================================
@@ -36,6 +36,7 @@ static SSL_CTX *server_ctx = NULL;
 
 void nts_init(void) {
     bool ok = true;
+    ok &= nts_cookie_init();
     if (ntsconfig.ntsenable)
         ok &= nts_server_init();
     ok &= nts_client_init();
@@ -148,7 +149,7 @@ void nts_ke_request(SSL *ssl) {
     uint8_t buff[1000];
     int bytes_read, bytes_written;
     uint8_t c2s[NTS_MAX_KEYLEN], s2c[NTS_MAX_KEYLEN];
-    uint8_t cookie[NTS_COOKIELEN];
+    uint8_t cookie[NTS_MAX_COOKIELEN];
     int aead, keylen, cookielen;
     struct BufCtl_t buf;
     int used;
@@ -218,35 +219,21 @@ int create_listener(int port) {
     return sock;
 }
 
+/* returns key length, 0 if unknown arg */
 int nts_get_key_length(int aead) {
   switch (aead) {
     case IANA_AEAD_AES_SIV_CMAC_256:
       return AEAD_AES_SIV_CMAC_256_KEYLEN;
+    case IANA_AEAD_AES_SIV_CMAC_384:
+      return AEAD_AES_SIV_CMAC_384_KEYLEN;
+    case IANA_AEAD_AES_SIV_CMAC_512:
+      return AEAD_AES_SIV_CMAC_512_KEYLEN;
     default:
       msyslog(LOG_ERR, "NTS: Strange AEAD code: %d", aead);
-      return 16;
+      return 0;
   }
 }
 
-// FIXME - this is a total hack to test pack/unpack
-/* returns actual length */
-int nts_make_cookie(uint8_t *cookie,
-  uint16_t aead,
-  uint8_t *c2s, uint8_t *s2c, int keylen) {
-
-  int length = NTS_COOKIELEN/2;
-
-  if (keylen < length)
-    length = keylen;
-  *cookie = aead & 0xFF;
-  for (int i=0; i<length; i++) {
-    *cookie++ = *c2s++^*s2c++;
-  }
-
-  return length;
-}
-
-
 bool nts_load_versions(SSL_CTX *ctx) {
   int minver, maxver;
   minver = nts_translate_version(ntsconfig.mintls);


=====================================
ntpd/wscript
=====================================
@@ -59,6 +59,7 @@ def build(ctx):
         "nts.c",
         "nts_server.c",
         "nts_client.c",
+        "nts_cookie.c",
         "nts_lib.c",
     ]
 
@@ -67,7 +68,7 @@ def build(ctx):
         includes=ctx.env.PLATFORM_INCLUDES,
         source=libntpd_source,
         target="libntpd_obj",
-        use="SSL CRYPTO",
+        use="SSL CRYPTO AES_SIV",
     )
 
     ctx(
@@ -127,7 +128,7 @@ def build(ctx):
         source=ntpd_source,
         target="ntpd",
         use="libntpd_obj ntp M parse RT CAP SECCOMP PTHREAD NTPD "
-            "SSL CRYPTO DNS_SD %s SOCKET NSL SCF" % use_refclock,
+            "SSL CRYPTO AES_SIV DNS_SD %s SOCKET NSL SCF" % use_refclock,
     )
 
     ctx.manpage(8, "ntpd-man.adoc")



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/commit/b0275b41016407ad20db0f7bdb3c14103c3acda4

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/commit/b0275b41016407ad20db0f7bdb3c14103c3acda4
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/20190217/2167fa7d/attachment-0001.html>


More information about the vc mailing list