OpenSSL has added AEAD-CBC mode ciphers like AES-128-CBC-HMAC-SHA1, which
have mode EVP_CIPH_CBC_MODE, but require a different API (the AEAD API).
So, add extra checks to filter out those AEAD-mode ciphers.

Adding these made the crypto library agnostic function cfb_ofb_mode()
superfuous, so removed that on the go.

Also update all cipher mode checks to use the new cipher_kt_mode_*()
functions for consistency.

Signed-off-by: Steffan Karger <stef...@karger.me>
---
 src/openvpn/crypto.c          | 36 +++++++++++++++---------------------
 src/openvpn/crypto.h          |  2 --
 src/openvpn/crypto_backend.h  | 30 ++++++++++++++++++++++++++++++
 src/openvpn/crypto_openssl.c  | 35 ++++++++++++++++++++++++++++++++---
 src/openvpn/crypto_polarssl.c | 21 +++++++++++++++++++++
 src/openvpn/init.c            |  2 +-
 6 files changed, 99 insertions(+), 27 deletions(-)

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 2a7fcb2..2a863b9 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -100,10 +100,10 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
        {
          uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH];
          const int iv_size = cipher_ctx_iv_length (ctx->cipher);
-         const unsigned int mode = cipher_ctx_mode (ctx->cipher);
+         const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
          int outlen;

-         if (mode == OPENVPN_MODE_CBC)
+         if (cipher_kt_mode_cbc(cipher_kt))
            {
              CLEAR (iv_buf);

@@ -119,7 +119,7 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
                  ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & 
CO_PACKET_ID_LONG_FORM), true));
                }
            }
-         else if (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB)
+         else if (cipher_kt_mode_ofb_cfb(cipher_kt))
            {
              struct packet_id_net pin;
              struct buffer b;
@@ -171,7 +171,10 @@ openvpn_encrypt (struct buffer *buf, struct buffer work,
          /* Flush the encryption buffer */
          ASSERT(cipher_ctx_final(ctx->cipher, BPTR (&work) + outlen, &outlen));
          work.len += outlen;
-         ASSERT (mode != OPENVPN_MODE_CBC || outlen == iv_size);
+
+         /* For all CBC mode ciphers, check the last block is complete */
+         ASSERT (cipher_kt_mode (cipher_kt) != OPENVPN_MODE_CBC ||
+             outlen == iv_size);

          /* prepend the IV to the ciphertext */
          if (opt->flags & CO_USE_IV)
@@ -272,8 +275,8 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,

       if (ctx->cipher)
        {
-         const unsigned int mode = cipher_ctx_mode (ctx->cipher);
          const int iv_size = cipher_ctx_iv_length (ctx->cipher);
+         const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt (ctx->cipher);
          uint8_t iv_buf[OPENVPN_MAX_IV_LENGTH];
          int outlen;

@@ -320,7 +323,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,

          /* Get packet ID from plaintext buffer or IV, depending on cipher 
mode */
          {
-           if (mode == OPENVPN_MODE_CBC)
+           if (cipher_kt_mode_cbc(cipher_kt))
              {
                if (opt->packet_id)
                  {
@@ -329,7 +332,7 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
                    have_pin = true;
                  }
              }
-           else if (mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB)
+           else if (cipher_kt_mode_ofb_cfb(cipher_kt))
              {
                struct buffer b;

@@ -426,10 +429,9 @@ init_key_type (struct key_type *kt, const char *ciphername,

       /* check legal cipher mode */
       {
-       const unsigned int mode = cipher_kt_mode (kt->cipher);
-       if (!(mode == OPENVPN_MODE_CBC
+       if (!(cipher_kt_mode_cbc(kt->cipher)
 #ifdef ENABLE_OFB_CFB_MODE
-             || (cfb_ofb_allowed && (mode == OPENVPN_MODE_CFB || mode == 
OPENVPN_MODE_OFB))
+             || (cfb_ofb_allowed && cipher_kt_mode_ofb_cfb(kt->cipher))
 #endif
              ))
 #ifdef ENABLE_SMALL
@@ -606,18 +608,10 @@ fixup_key (struct key *key, const struct key_type *kt)
 void
 check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool 
use_iv)
 {
-  if (cfb_ofb_mode (kt) && !(packet_id && use_iv))
-    msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB 
mode cipher");
-}
+  ASSERT(kt);

-bool
-cfb_ofb_mode (const struct key_type* kt)
-{
-  if (kt && kt->cipher) {
-      const unsigned int mode = cipher_kt_mode (kt->cipher);
-      return mode == OPENVPN_MODE_CFB || mode == OPENVPN_MODE_OFB;
-  }
-  return false;
+  if (cipher_kt_mode_ofb_cfb(kt->cipher) && !(packet_id && use_iv))
+    msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB 
mode cipher");
 }

 /*
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 1f1e1b6..bf2f802 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -187,8 +187,6 @@ bool write_key (const struct key *key, const struct 
key_type *kt,

 int read_key (struct key *key, const struct key_type *kt, struct buffer *buf);

-bool cfb_ofb_mode (const struct key_type* kt);
-
 void init_key_type (struct key_type *kt, const char *ciphername,
     bool ciphername_defined, const char *authname, bool authname_defined,
     int keysize, bool cfb_ofb_allowed, bool warn);
diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h
index 5ae47e6..a48ad6c 100644
--- a/src/openvpn/crypto_backend.h
+++ b/src/openvpn/crypto_backend.h
@@ -230,6 +230,26 @@ int cipher_kt_block_size (const cipher_kt_t *cipher_kt);
  */
 int cipher_kt_mode (const cipher_kt_t *cipher_kt);

+/**
+ * Check of the supplied cipher is a supported CBC mode cipher.
+ *
+ * @param cipher       Static cipher parameters. May not be NULL.
+ *
+ * @return             true iff the cipher is a CBC mode cipher.
+ */
+bool cipher_kt_mode_cbc(const cipher_kt_t *cipher)
+  __attribute__((nonnull));
+
+/**
+ * Check of the supplied cipher is a supported OFB or CFB mode cipher.
+ *
+ * @param cipher       Static cipher parameters. May not be NULL.
+ *
+ * @return             true iff the cipher is a OFB or CFB mode cipher.
+ */
+bool cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher)
+  __attribute__((nonnull));
+

 /**
  *
@@ -288,6 +308,16 @@ int cipher_ctx_block_size (const cipher_ctx_t *ctx);
 int cipher_ctx_mode (const cipher_ctx_t *ctx);

 /**
+ * Returns the static cipher parameters for this context.
+ *
+ * @param ctx          Cipher's context. May not be NULL.
+ *
+ * @return             Static cipher parameters for the supplied context.
+ */
+const cipher_kt_t *cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
+  __attribute__((nonnull));
+
+/**
  * Resets the given cipher context, setting the IV to the specified value.
  * Preserves the associated key information.
  *
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c
index 74539b6..1159299 100644
--- a/src/openvpn/crypto_openssl.c
+++ b/src/openvpn/crypto_openssl.c
@@ -261,10 +261,9 @@ show_available_ciphers ()
       const EVP_CIPHER *cipher = EVP_get_cipherbynid (nid);
       if (cipher)
        {
-         const unsigned int mode = EVP_CIPHER_mode (cipher);
-         if (mode == EVP_CIPH_CBC_MODE
+         if (cipher_kt_mode_cbc(cipher)
 #ifdef ENABLE_OFB_CFB_MODE
-             || mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE
+             || cipher_kt_mode_ofb_cfb(cipher)
 #endif
              )
            printf ("%s %d bit default key (%s)\n",
@@ -483,6 +482,29 @@ cipher_kt_mode (const EVP_CIPHER *cipher_kt)
   return EVP_CIPHER_mode (cipher_kt);
 }

+bool
+cipher_kt_mode_cbc(const cipher_kt_t *cipher)
+{
+  return cipher_kt_mode(cipher) == OPENVPN_MODE_CBC
+#ifdef EVP_CIPH_FLAG_AEAD_CIPHER
+      /* Exclude AEAD cipher modes, they require a different API */
+      && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)
+#endif
+    ;
+}
+
+bool
+cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher)
+{
+  return (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB ||
+         cipher_kt_mode(cipher) == OPENVPN_MODE_CFB)
+#ifdef EVP_CIPH_FLAG_AEAD_CIPHER
+      /* Exclude AEAD cipher modes, they require a different API */
+      && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)
+#endif
+    ;
+}
+
 /*
  *
  * Generic cipher context functions
@@ -536,6 +558,13 @@ cipher_ctx_mode (const EVP_CIPHER_CTX *ctx)
   return EVP_CIPHER_CTX_mode (ctx);
 }

+const cipher_kt_t *
+cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
+{
+  return EVP_CIPHER_CTX_cipher(ctx);
+}
+
+
 int
 cipher_ctx_reset (EVP_CIPHER_CTX *ctx, uint8_t *iv_buf)
 {
diff --git a/src/openvpn/crypto_polarssl.c b/src/openvpn/crypto_polarssl.c
index 7dc8aa5..1a986db 100644
--- a/src/openvpn/crypto_polarssl.c
+++ b/src/openvpn/crypto_polarssl.c
@@ -416,6 +416,19 @@ cipher_kt_mode (const cipher_info_t *cipher_kt)
   return cipher_kt->mode;
 }

+bool
+cipher_kt_mode_cbc(const cipher_kt_t *cipher)
+{
+  return cipher_kt_mode(cipher) == OPENVPN_MODE_CBC;
+}
+
+bool
+cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher)
+{
+  return (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB ||
+         cipher_kt_mode(cipher) == OPENVPN_MODE_CFB);
+}
+

 /*
  *
@@ -464,6 +477,14 @@ int cipher_ctx_mode (const cipher_context_t *ctx)
   return cipher_kt_mode(ctx->cipher_info);
 }

+const cipher_kt_t *
+cipher_ctx_get_cipher_kt (const cipher_ctx_t *ctx)
+{
+  ASSERT(NULL != ctx);
+
+  return ctx->cipher_info;
+}
+
 int cipher_ctx_reset (cipher_context_t *ctx, uint8_t *iv_buf)
 {
   int retval = cipher_reset(ctx);
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 76b1bbe..2bd18d8 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2186,7 +2186,7 @@ do_init_crypto_tls (struct context *c, const unsigned int 
flags)
                               options->use_iv);

   /* In short form, unique datagram identifier is 32 bits, in long form 64 
bits */
-  packet_id_long_form = cfb_ofb_mode (&c->c1.ks.key_type);
+  packet_id_long_form = cipher_kt_mode_ofb_cfb (c->c1.ks.key_type.cipher);

   /* Compute MTU parameters */
   crypto_adjust_frame_parameters (&c->c2.frame,
-- 
1.9.1


Reply via email to