Skip to content

Commit

Permalink
Discourage using 64-bit block ciphers
Browse files Browse the repository at this point in the history
As discussed with the development team, we should start moving away from
ciphers with a small block size.  For OpenVPN in particular this means
moving away from 64-bit block ciphers, towards 128-bit block ciphers.
This patch makes a start with that by moving ciphers with a block
size < 128 bits to the bottom of the --show-ciphers output, and printing
a warning in the connection phase if such a cipher is used.

While touching this function, improve the output of --show-ciphers by
ordering the output alphabetically, and changing the output format
slightly.

[DS: Fixed C89 issues in patch, moving 'int nid' and 'size_t i' declaration
     to begining of function instead of in the for-loops.  This is also
     required to not break building on stricter compiler setups where C99
     must be enabled explicitly ]

Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1471358761-8828-1-git-send-email-steffan@karger.me>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg00030.html
CVE: 2016-6329
Signed-off-by: David Sommerseth <davids@openvpn.net>
  • Loading branch information
syzzer authored and dsommers committed Aug 22, 2016
1 parent d1c08ab commit 610fdbb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 29 deletions.
11 changes: 8 additions & 3 deletions src/openvpn/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,14 @@ init_key_ctx (struct key_ctx *ctx, struct key *key,
dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
format_hex (key->cipher, kt->cipher_length, 0, &gc));
dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d",
prefix,
cipher_kt_block_size(kt->cipher),
cipher_kt_iv_size(kt->cipher));
prefix, cipher_kt_block_size(kt->cipher),
cipher_kt_iv_size(kt->cipher));
if (cipher_kt_block_size(kt->cipher) < 128/8)
{
msg (M_WARN, "WARNING: this cipher's block size is less than 128 bit "
"(%d bit). Consider using a --cipher with a larger block size.",
cipher_kt_block_size(kt->cipher)*8);
}
}
if (kt->digest && kt->hmac_length > 0)
{
Expand Down
110 changes: 90 additions & 20 deletions src/openvpn/crypto_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,45 @@ translate_cipher_name_to_openvpn (const char *cipher_name) {
return cipher_name;
}

static int
cipher_name_cmp(const void *a, const void *b)
{
const EVP_CIPHER * const *cipher_a = a;
const EVP_CIPHER * const *cipher_b = b;

const char *cipher_name_a =
translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_a));
const char *cipher_name_b =
translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_b));

return strcmp(cipher_name_a, cipher_name_b);
}

static void
print_cipher(const EVP_CIPHER *cipher)
{
const char *var_key_size =
(EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ?
" by default" : "";
const char *ssl_only = cipher_kt_mode_cbc(cipher) ?
"" : ", TLS client/server mode only";

printf ("%s (%d bit key%s, %d bit block%s)\n",
translate_cipher_name_to_openvpn (EVP_CIPHER_name (cipher)),
EVP_CIPHER_key_length (cipher) * 8, var_key_size,
cipher_kt_block_size (cipher) * 8, ssl_only);
}

void
show_available_ciphers ()
{
int nid;
size_t i;

/* If we ever exceed this, we must be more selective */
const size_t cipher_list_len = 1000;
const EVP_CIPHER *cipher_list[cipher_list_len];
size_t num_ciphers = 0;
#ifndef ENABLE_SMALL
printf ("The following ciphers and cipher modes are available\n"
"for use with " PACKAGE_NAME ". Each cipher shown below may be\n"
Expand All @@ -302,29 +336,37 @@ show_available_ciphers ()
"is recommended. In static key mode only CBC mode is allowed.\n\n");
#endif

for (nid = 0; nid < 10000; ++nid) /* is there a better way to get the size of the nid list? */
for (nid = 0; nid < 10000; ++nid)
{
const EVP_CIPHER *cipher = EVP_get_cipherbynid (nid);
if (cipher)
{
if (cipher_kt_mode_cbc(cipher)
const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid);
if (cipher && (cipher_kt_mode_cbc(cipher)
#ifdef ENABLE_OFB_CFB_MODE
|| cipher_kt_mode_ofb_cfb(cipher)
#endif
)
{
const char *var_key_size =
(EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ?
"variable" : "fixed";
const char *ssl_only = cipher_kt_mode_ofb_cfb(cipher) ?
" (TLS client/server mode)" : "";

printf ("%s %d bit default key (%s)%s\n", OBJ_nid2sn (nid),
EVP_CIPHER_key_length (cipher) * 8, var_key_size,
ssl_only);
}
))
{
cipher_list[num_ciphers++] = cipher;
}
if (num_ciphers == cipher_list_len)
{
msg (M_WARN, "WARNING: Too many ciphers, not showing all");
break;
}
}

qsort (cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp);

for (i = 0; i < num_ciphers; i++) {
if (cipher_kt_block_size(cipher_list[i]) >= 128/8)
print_cipher(cipher_list[i]);
}

printf ("\nThe following ciphers have a block size of less than 128 bits, \n"
"and are therefore deprecated. Do not use unless you have to.\n\n");
for (i = 0; i < num_ciphers; i++) {
if (cipher_kt_block_size(cipher_list[i]) < 128/8)
print_cipher(cipher_list[i]);
}
printf ("\n");
}

Expand Down Expand Up @@ -530,9 +572,37 @@ cipher_kt_iv_size (const EVP_CIPHER *cipher_kt)
}

int
cipher_kt_block_size (const EVP_CIPHER *cipher_kt)
{
return EVP_CIPHER_block_size (cipher_kt);
cipher_kt_block_size (const EVP_CIPHER *cipher) {
/* OpenSSL reports OFB/CFB/GCM cipher block sizes as '1 byte'. To work
* around that, try to replace the mode with 'CBC' and return the block size
* reported for that cipher, if possible. If that doesn't work, just return
* the value reported by OpenSSL.
*/
char *name = NULL;
char *mode_str = NULL;
const char *orig_name = NULL;
const EVP_CIPHER *cbc_cipher = NULL;

int block_size = EVP_CIPHER_block_size(cipher);

orig_name = cipher_kt_name(cipher);
if (!orig_name)
goto cleanup;

name = string_alloc(translate_cipher_name_to_openvpn(orig_name), NULL);
mode_str = strrchr (name, '-');
if (!mode_str || strlen(mode_str) < 4)
goto cleanup;

strcpy (mode_str, "-CBC");

cbc_cipher = EVP_get_cipherbyname(translate_cipher_name_from_openvpn(name));
if (cbc_cipher)
block_size = EVP_CIPHER_block_size(cbc_cipher);

cleanup:
free (name);
return block_size;
}

int
Expand Down
41 changes: 36 additions & 5 deletions src/openvpn/crypto_polarssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,25 @@ translate_cipher_name_to_openvpn (const char *cipher_name) {
return pair->openvpn_name;
}

static void print_cipher(const cipher_kt_t *info)
{
if (info && (cipher_kt_mode_cbc(info)
#ifdef HAVE_AEAD_CIPHER_MODES
|| cipher_kt_mode_aead(info)
#endif
))
{
const char *ssl_only = cipher_kt_mode_cbc(info) ?
"" : ", TLS client/server mode only";
const char *var_key_size = info->flags & POLARSSL_CIPHER_VARIABLE_KEY_LEN
? " by default" : "";

printf ("%s (%d bit key%s, %d bit block%s)\n",
cipher_kt_name(info), cipher_kt_key_size(info) * 8, var_key_size,
cipher_kt_block_size(info) * 8, ssl_only);
}
}

void
show_available_ciphers ()
{
Expand All @@ -184,12 +203,24 @@ show_available_ciphers ()

while (*ciphers != 0)
{
const cipher_info_t *info = cipher_info_from_type(*ciphers);

if (info && info->mode == POLARSSL_MODE_CBC)
printf ("%s %d bit default key\n",
cipher_kt_name(info), cipher_kt_key_size(info) * 8);
const cipher_kt_t *info = cipher_info_from_type(*ciphers);
if (info && cipher_kt_block_size(info) >= 128/8)
{
print_cipher(info);
}
ciphers++;
}

printf ("\nThe following ciphers have a block size of less than 128 bits, \n"
"and are therefore deprecated. Do not use unless you have to.\n\n");
ciphers = cipher_list();
while (*ciphers != 0)
{
const cipher_kt_t *info = cipher_info_from_type(*ciphers);
if (info && cipher_kt_block_size(info) < 128/8)
{
print_cipher(info);
}
ciphers++;
}
printf ("\n");
Expand Down
2 changes: 1 addition & 1 deletion tests/t_lpback.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ trap "rm -f key.$$ log.$$ ; exit 1" 0 3

# Get list of supported ciphers from openvpn --show-ciphers output
CIPHERS=$(${top_builddir}/src/openvpn/openvpn --show-ciphers | \
sed -e '1,/^$/d' -e s'/ .*//' -e '/^\s*$/d' | sort)
sed -e '/The following/,/^$/d' -e s'/ .*//' -e '/^\s*$/d')

# SK, 2014-06-04: currently the DES-EDE3-CFB1 implementation of OpenSSL is
# broken (see http://rt.openssl.org/Ticket/Display.html?id=2867), so exclude
Expand Down

0 comments on commit 610fdbb

Please sign in to comment.