Skip to contentSkip to conversation

Added support for new PolarSSL 1.1 RNG

PATCH In review Adriaan de Jong 2 participants 3 messages Feb 28, 2012

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 Added support for new PolarSSL 1.1 RNG

This patch, while retaining PolarSSL 1.0 support, introduces the PolarSSL 1.1 DRBG. This RNG adds a number of features, including support for personalisation strings and multiple entropy sources.

Personalisation strings have been implemented, based on PID, program name, place within memory, and a hash of the user's certificate.

The entropy sources used are the platform default ones. Which ones these are depends on how PolarSSL was built, but usually this includes:

- /dev/urandom or the Windows CryptoAPI RNG
- the HAVEGE RNG
- the output of PolarSSL's hardclock() call (usually RDTSC)

Finally, this patch moves to only one instance of the RNG per OpenVPN instance, instead of one per keystate

---
 crypto_polarssl.c |   84 ++++++++++++++++++++++++++++++++++++++++++++++------
 crypto_polarssl.h |   25 ++++++++++++++++
 ssl.c             |    5 +++
 ssl_backend.h     |   10 ++++++
 ssl_polarssl.c    |   44 +++++++++++++++++++++------
 ssl_polarssl.h    |    2 -
 6 files changed, 148 insertions(+), 22 deletions(-)
crypto_polarssl.c +74 -10
index e7470d5..8eb47db 100644
@@ -36,12 +36,18 @@
 #include "buffer.h"
 #include "integer.h"
 #include "crypto_backend.h"
+#include "otime.h"
+#include "misc.h"
 
 #include <polarssl/des.h>
 #include <polarssl/md5.h>
 #include <polarssl/cipher.h>
 #include <polarssl/havege.h>
 
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+#include <polarssl/entropy.h>
+#endif
+
 /*
  *
  * Hardware engine support. Allows loading/unloading of engines.
@@ -143,7 +149,6 @@ show_available_engines ()
       "available\n");
 }
 
-
 /*
  *
  * Random number functions, used in cases where we want
@@ -153,29 +158,88 @@ show_available_engines ()
  *
  */
 
-int
-rand_bytes (uint8_t *output, int len)
+/*
+ * Initialise the given ctr_drbg context, using a personalisation string and an
+ * entropy gathering function.
+ */
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+ctr_drbg_context * rand_ctx_get()
+{
+  static entropy_context ec = {0};
+  static ctr_drbg_context cd_ctx = {0};
+  static bool rand_initialised = false;
+
+  if (!rand_initialised)
+    {
+      struct gc_arena gc = gc_new();
+      struct buffer pers_string = alloc_buf_gc(100, &gc);
+
+      /*
+       * Personalisation string, should be as unique as possible (see NIST
+       * 800-90 section 8.7.1). We have very little information at this stage.
+       * Include Program Name, memory address of the context and PID.
+       */
+      buf_printf(&pers_string, "OpenVPN %0u %p %s", openvpn_getpid(), &cd_ctx, time_string(0, 0, 0, &gc));
+
+      /* Initialise PolarSSL RNG, and built-in entropy sources */
+      entropy_init(&ec);
+
+      if (0 != ctr_drbg_init(&cd_ctx, entropy_func, &ec, BPTR(&pers_string), BLEN(&pers_string)))
+        msg (M_FATAL, "Failed to initialize random generator");
+
+      gc_free(&gc);
+      rand_initialised = true;
+  }
+
+  return &cd_ctx;
+}
+
+#else /* (POLARSSL_VERSION_NUMBER < 0x01010000) */
+
+havege_state * rand_ctx_get()
 {
   static havege_state hs = {0};
-  static bool hs_initialised = false;
-  const int int_size = sizeof(int);
+  static bool rand_initialised = false;
 
-  if (!hs_initialised)
+  if (!rand_initialised)
     {
       /* Initialise PolarSSL RNG */
       havege_init(&hs);
-      hs_initialised = true;
+      rand_initialised = true;
     }
 
+  return &hs;
+}
+
+#endif /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
+
+int
+rand_bytes (uint8_t *output, int len)
+{
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+  ctr_drbg_context *rng_ctx = rand_ctx_get();
+#else /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
+  havege_state *rng_ctx = rand_ctx_get();
+#endif /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
+
   while (len > 0)
     {
-      const int blen 	= min_int (len, int_size);
-      const int rand_int 	= havege_rand(&hs);
-
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+      const size_t blen = min_int (len, CTR_DRBG_MAX_REQUEST);
+      if (0 != ctr_drbg_random(rng_ctx, output, blen))
+	return 0;
+
+#else /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
+      const size_t blen = min_int (len, sizeof(int));
+      const int rand_int = havege_rand(rng_ctx);
       memcpy (output, &rand_int, blen);
+
+#endif /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
+
       output += blen;
       len -= blen;
     }
+
   return 1;
 }
 
crypto_polarssl.h +25 -0
index 358483a..2f303db 100644
@@ -30,9 +30,16 @@
 #ifndef CRYPTO_POLARSSL_H_
 #define CRYPTO_POLARSSL_H_
 
+#include <polarssl/version.h>
 #include <polarssl/cipher.h>
 #include <polarssl/md.h>
 
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+#  include <polarssl/ctr_drbg.h>
+#else
+#  include <polarssl/havege.h>
+#endif
+
 /** Generic cipher key type %context. */
 typedef cipher_info_t cipher_kt_t;
 
@@ -71,4 +78,22 @@ typedef md_context_t hmac_ctx_t;
 #define SHA_DIGEST_LENGTH 	20
 #define DES_KEY_LENGTH 8
 
+/**
+ * Returns a singleton instance of the PolarSSL random number generator.
+ *
+ * For PolarSSL 1.0, this is the HAVEGE random number generator.
+ *
+ * For PolarSSL 1.1+, this is the CTR_DRBG random number generator. If it
+ * hasn't been initialised yet, the RNG will be initialised using the default
+ * entropy sources. Aside from the default platform entropy sources, an
+ * additional entropy source, the HAVEGE random number generator will also be
+ * added. During initialisation, a personalisation string will be added based
+ * on the time, the PID, and a pointer to the random context.
+ */
+#if (POLARSSL_VERSION_NUMBER >= 0x01010000)
+ctr_drbg_context * rand_ctx_get();
+#else
+havege_state * rand_ctx_get();
+#endif
+
 #endif /* CRYPTO_POLARSSL_H_ */
ssl.c +5 -0
index c26756e..e852485 100644
@@ -385,6 +385,11 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx)
       tls_ctx_restrict_ciphers(new_ctx, options->cipher_list);
     }
 
+#ifdef USE_POLARSSL
+  /* Fox-IT hardening: Personalise the random by mixing in the certificate */
+  tls_ctx_personalise_random (new_ctx);
+#endif
+
   tls_clear_error ();
   return;
 
ssl_backend.h +10 -0
index 243c9e3..b3e0c68 100644
@@ -272,6 +272,16 @@ void tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs
 #endif
     );
 
+#ifdef USE_POLARSSL
+/**
+ * Add a personalisation string to the PolarSSL RNG, based on the certificate
+ * loaded into the given context.
+ *
+ * @param ctx			TLS context to use
+ */
+void tls_ctx_personalise_random(struct tls_root_ctx *ctx);
+#endif
+
 /* **************************************
  *
  * Key-state specific functions
ssl_polarssl.c +34 -10

82 lines not on this page · raw message

ssl_polarssl.h +0 -2
index e6149b6..2b02a6f 100644
@@ -30,7 +30,6 @@
 #ifndef SSL_POLARSSL_H_
 #define SSL_POLARSSL_H_
 
-#include <polarssl/havege.h>
 #include <polarssl/ssl.h>
 #include "config.h"
 
@@ -63,7 +62,6 @@ struct tls_root_ctx {
 
     int endpoint; 		/**< Whether or not this is a server or a client */
 
-    havege_state *hs;		/**< HAVEGE random number state */
     dhm_context *dhm_ctx;	/**< Diffie-Helmann-Merkle context */
     x509_cert *crt_chain;	/**< Local Certificate chain */
     x509_cert *ca_chain;	/**< CA chain for remote verification */
Signature · 1 line
1.7.5.4
#1 lore raw

PATCH 2/2 Added a configuration option to enable prediction resistance in the PolarSSL random number generator.

---
 crypto_polarssl.c |    9 +++++++++
 crypto_polarssl.h |    7 +++++++
 init.c            |    6 ++++++
 openvpn.8         |   14 ++++++++++++++
 options.c         |   22 ++++++++++++++++++++++
 options.h         |    3 +++
 syshead.h         |    8 ++++++++
 7 files changed, 69 insertions(+), 0 deletions(-)
crypto_polarssl.c +9 -0
index 8eb47db..b37c8b2 100644
@@ -213,6 +213,15 @@ havege_state * rand_ctx_get()
 
 #endif /* (POLARSSL_VERSION_NUMBER >= 0x01010000) */
 
+#ifdef ENABLE_PREDICTION_RESISTANCE
+void rand_ctx_enable_prediction_resistance()
+{
+  ctr_drbg_context *cd_ctx = rand_ctx_get();
+
+  ctr_drbg_set_prediction_resistance(cd_ctx, 1);
+}
+#endif /* ENABLE_PREDICTION_RESISTANCE */
+
 int
 rand_bytes (uint8_t *output, int len)
 {
crypto_polarssl.h +7 -0
index 2f303db..6152878 100644
@@ -96,4 +96,11 @@ ctr_drbg_context * rand_ctx_get();
 havege_state * rand_ctx_get();
 #endif
 
+#ifdef ENABLE_PREDICTION_RESISTANCE
+/**
+ * Enable prediction resistance on the random number generator.
+ */
+void rand_ctx_enable_prediction_resistance();
+#endif
+
 #endif /* CRYPTO_POLARSSL_H_ */
init.c +6 -0
index fb8fe00..5259690 100644
@@ -2002,6 +2002,12 @@ init_crypto_pre (struct context *c, const unsigned int flags)
 
   if (c->options.mute_replay_warnings)
     c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS;
+
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  if (c->options.use_prediction_resistance)
+    rand_ctx_enable_prediction_resistance();
+#endif
+
 }
 
 /*
openvpn.8 +14 -0
index 53d6bdb..ee46de6 100644
@@ -3846,6 +3846,20 @@ space-saving optimization that uses the unique identifier for
 datagram replay protection as the IV.
 .\"*********************************************************
 .TP
+.B \-\-use-prediction-resistance
+Enable prediction resistance on PolarSSL's RNG.
+
+Enabling prediction resistance causes the RNG to reseed in each
+call for random. Reseeding this often can quickly deplete the kernel
+entropy pool.
+
+If you need this option, please consider running a daemon that adds
+entropy to the kernel pool.
+
+Note that this option only works with PolarSSL versions greater
+than 1.1.
+.\"*********************************************************
+.TP
 .B \-\-test-crypto
 Do a self-test of OpenVPN's crypto options by encrypting and
 decrypting test packets using the data channel encryption options
options.c +22 -0
index 5e972b1..c2896da 100644
@@ -537,6 +537,10 @@ static const char usage_message[] =
   "                  using file.\n"
   "--test-crypto   : Run a self-test of crypto features enabled.\n"
   "                  For debugging only.\n"
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  "--use-prediction-resistance: Enable prediction resistance on the random\n"
+  "                             number generator.\n"
+#endif
 #ifdef USE_SSL
   "\n"
   "TLS Key Negotiation Options:\n"
@@ -829,6 +833,9 @@ init_options (struct options *o, const bool init_gc)
   o->replay_time = DEFAULT_TIME_BACKTRACK;
   o->use_iv = true;
   o->key_direction = KEY_DIRECTION_BIDIRECTIONAL;
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  o->use_prediction_resistance = false;
+#endif
 #ifdef USE_SSL
   o->key_method = 2;
   o->tls_timeout = 2;
@@ -1573,6 +1580,9 @@ show_settings (const struct options *o)
   SHOW_STR (packet_id_file);
   SHOW_BOOL (use_iv);
   SHOW_BOOL (test_crypto);
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  SHOW_BOOL (use_prediction_resistance);
+#endif
 
 #ifdef USE_SSL
   SHOW_BOOL (tls_server);
@@ -3000,6 +3010,11 @@ options_string (const struct options *o,
 	  buf_printf (&out, ",no-replay");
 	if (!o->use_iv)
 	  buf_printf (&out, ",no-iv");
+
+#ifdef ENABLE_PREDICTION_RESISTANCE
+        if (o->use_prediction_resistance)
+          buf_printf (&out, ",use-prediction-resistance");
+#endif
       }
 
 #ifdef USE_SSL
@@ -6401,6 +6416,13 @@ add_option (struct options *options,
       options->keysize = keysize;
     }
 #endif
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  else if (streq (p[0], "use-prediction-resistance"))
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->use_prediction_resistance = true;
+    }
+#endif
 #ifdef USE_SSL
   else if (streq (p[0], "show-tls"))
     {
options.h +3 -0
index 6af4b3a..4794a08 100644
@@ -520,6 +520,9 @@ struct options
   const char *packet_id_file;
   bool use_iv;
   bool test_crypto;
+#ifdef ENABLE_PREDICTION_RESISTANCE
+  bool use_prediction_resistance;
+#endif
 
 #ifdef USE_SSL
   /* TLS (control channel) parms */
syshead.h +8 -0
index 0235abd..6c8329b 100644
@@ -546,6 +546,14 @@ socket_defined (const socket_descriptor_t sd)
 #define MANAGMENT_EXTERNAL_KEY
 #endif
 
+/* Enable PolarSSL RNG prediction resistance support */
+#ifdef USE_POLARSSL
+#include <polarssl/version.h>
+#if POLARSSL_VERSION_NUMBER >= 0x01010000
+#define ENABLE_PREDICTION_RESISTANCE
+#endif
+#endif /* USE_POLARSSL */
+
 /*
  * MANAGEMENT_IN_EXTRA allows the management interface to
  * read multi-line inputs from clients.
Signature · 1 line
1.7.5.4
#2 lore raw
In reply to Adriaan de Jong

Hi Adriaan,

I only found a minor nit:

2012/2/28 Adriaan de Jong <[email protected]...>:

ssl.c init_ssl (const struct options *options, struct tls_root_ctx *new_ctx) · line 391

Quoted 10 lines
ssl.c +4 -0
@@ -385,6 +385,11 @@ init_ssl (const struct options *options, struct tls_root_ctx *new_ctx)
       tls_ctx_restrict_ciphers(new_ctx, options->cipher_list);
     }
 
+#ifdef USE_POLARSSL
+  /* Fox-IT hardening: Personalise the random by mixing in the certificate */
+  tls_ctx_personalise_random (new_ctx);
+#endif

Unless it's intentional, the "Fox-IT hardening" string is probably from an earlier, internal iteration?

The rest looks fine AFAICT. :)

Cheers
Fabian