Harden SSLv2-supporting servers against Bleichenbacher's attack.
authorEmilia Kasper <emilia@openssl.org>
Wed, 1 Apr 2015 15:08:45 +0000 (17:08 +0200)
committerEmilia Kasper <emilia@openssl.org>
Wed, 8 Apr 2015 14:28:42 +0000 (16:28 +0200)
There is no indication that the timing differences are exploitable in
OpenSSL, and indeed there is some indication (Usenix '14) that they
are too small to be exploitable. Nevertheless, be careful and apply
the same countermeasures as in s3_srvr.c

Thanks to Nimrod Aviram, Sebastian Schinzel and Yuval Shavitt for
reporting this issue.

Reviewed-by: Richard Levitte <levitte@openssl.org>
ssl/s2_srvr.c

index 19bb48c9cd464702fd58924fdfb03f24995437d5..4289272b73d3dc29691de99d29a37ecf8090fefd 100644 (file)
 
 #include "ssl_locl.h"
 #ifndef OPENSSL_NO_SSL2
+#include "../crypto/constant_time_locl.h"
 # include <stdio.h>
 # include <openssl/bio.h>
 # include <openssl/rand.h>
@@ -372,12 +373,15 @@ int ssl2_accept(SSL *s)
 static int get_client_master_key(SSL *s)
 {
     int is_export, i, n, keya;
-    unsigned int ek;
+    unsigned int num_encrypted_key_bytes, key_length;
     unsigned long len;
     unsigned char *p;
     const SSL_CIPHER *cp;
     const EVP_CIPHER *c;
     const EVP_MD *md;
+    unsigned char rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH];
+    unsigned char decrypt_good;
+    size_t j;
 
     p = (unsigned char *)s->init_buf->data;
     if (s->state == SSL2_ST_GET_CLIENT_MASTER_KEY_A) {
@@ -465,12 +469,6 @@ static int get_client_master_key(SSL *s)
         return (0);
     }
 
-    if (s->session->cipher->algorithm2 & SSL2_CF_8_BYTE_ENC) {
-        is_export = 1;
-        ek = 8;
-    } else
-        ek = 5;
-
     /*
      * The format of the CLIENT-MASTER-KEY message is
      * 1 byte message type
@@ -484,12 +482,27 @@ static int get_client_master_key(SSL *s)
      *
      * If the cipher is an export cipher, then the encrypted key bytes
      * are a fixed portion of the total key (5 or 8 bytes). The size of
-     * this portion is in |ek|. If the cipher is not an export cipher,
-     * then the entire key material is encrypted (i.e., clear key length
-     * must be zero).
+     * this portion is in |num_encrypted_key_bytes|. If the cipher is not an
+     * export cipher, then the entire key material is encrypted (i.e., clear
+     * key length must be zero).
      */
-    if ((!is_export && s->s2->tmp.clear != 0) ||
-        (is_export && s->s2->tmp.clear + ek != (unsigned int)EVP_CIPHER_key_length(c))) {
+    key_length = (unsigned int)EVP_CIPHER_key_length(c);
+    if (key_length > SSL_MAX_MASTER_KEY_LENGTH) {
+        ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
+        SSLerr(SSL_F_GET_CLIENT_MASTER_KEY, ERR_R_INTERNAL_ERROR);
+        return -1;
+    }
+
+    if (s->session->cipher->algorithm2 & SSL2_CF_8_BYTE_ENC) {
+        is_export = 1;
+        num_encrypted_key_bytes = 8;
+    } else if (is_export) {
+        num_encrypted_key_bytes = 5;
+    } else {
+        num_encrypted_key_bytes = key_length;
+    }
+
+    if (s->s2->tmp.clear + num_encrypted_key_bytes != key_length) {
         ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
         SSLerr(SSL_F_GET_CLIENT_MASTER_KEY,SSL_R_BAD_LENGTH);
         return -1;
@@ -499,64 +512,49 @@ static int get_client_master_key(SSL *s)
      * Decryption can't be expanding, so if we don't have enough encrypted
      * bytes to fit the key in the buffer, stop now.
      */
-    if ((is_export && s->s2->tmp.enc < ek) ||
-        (!is_export && s->s2->tmp.enc < (unsigned int)EVP_CIPHER_key_length(c))) {
+    if (s->s2->tmp.enc < num_encrypted_key_bytes) {
         ssl2_return_error(s,SSL2_PE_UNDEFINED_ERROR);
         SSLerr(SSL_F_GET_CLIENT_MASTER_KEY,SSL_R_LENGTH_TOO_SHORT);
         return -1;
     }
 
+    /*
+     * We must not leak whether a decryption failure occurs because of
+     * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246,
+     * section 7.4.7.1). The code follows that advice of the TLS RFC and
+     * generates a random premaster secret for the case that the decrypt
+     * fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1
+     */
+
+    /*
+     * should be RAND_bytes, but we cannot work around a failure.
+     */
+    if (RAND_pseudo_bytes(rand_premaster_secret,
+                          (int)num_encrypted_key_bytes) <= 0)
+        return 0;
+
     i = ssl_rsa_private_decrypt(s->cert, s->s2->tmp.enc,
                                 &(p[s->s2->tmp.clear]),
                                 &(p[s->s2->tmp.clear]),
                                 (s->s2->ssl2_rollback) ? RSA_SSLV23_PADDING :
                                 RSA_PKCS1_PADDING);
-
-    /* bad decrypt */
-# if 1
+    ERR_clear_error();
     /*
      * If a bad decrypt, continue with protocol but with a random master
      * secret (Bleichenbacher attack)
      */
-    if ((i < 0) || ((!is_export && i != EVP_CIPHER_key_length(c))
-                    || (is_export && i != (int)ek))) {
-        ERR_clear_error();
-        if (is_export)
-            i = ek;
-        else
-            i = EVP_CIPHER_key_length(c);
-        if (RAND_pseudo_bytes(&p[s->s2->tmp.clear], i) <= 0)
-            return 0;
-    }
-# else
-    if (i < 0) {
-        error = 1;
-        SSLerr(SSL_F_GET_CLIENT_MASTER_KEY, SSL_R_BAD_RSA_DECRYPT);
-    }
-    /* incorrect number of key bytes for non export cipher */
-    else if ((!is_export && (i != EVP_CIPHER_key_length(c)))
-             || (is_export && ((i != ek) || (s->s2->tmp.clear + i !=
-                                             EVP_CIPHER_key_length(c))))) {
-        error = 1;
-        SSLerr(SSL_F_GET_CLIENT_MASTER_KEY, SSL_R_WRONG_NUMBER_OF_KEY_BITS);
-    }
-    if (error) {
-        ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
-        return (-1);
+    decrypt_good = constant_time_eq_int_8(i, (int)num_encrypted_key_bytes);
+    for (j = 0; j < num_encrypted_key_bytes; j++) {
+        p[s->s2->tmp.clear + j] =
+                constant_time_select_8(decrypt_good, p[s->s2->tmp.clear + j],
+                                       rand_premaster_secret[j]);
     }
-# endif
 
-    if (is_export)
-        i = EVP_CIPHER_key_length(c);
+    s->session->master_key_length = (int)key_length;
+    memcpy(s->session->master_key, p, key_length);
+    OPENSSL_cleanse(p, key_length);
 
-    if (i > SSL_MAX_MASTER_KEY_LENGTH) {
-        ssl2_return_error(s, SSL2_PE_UNDEFINED_ERROR);
-        SSLerr(SSL_F_GET_CLIENT_MASTER_KEY, ERR_R_INTERNAL_ERROR);
-        return -1;
-    }
-    s->session->master_key_length = i;
-    memcpy(s->session->master_key, p, (unsigned int)i);
-    return (1);
+    return 1;
 }
 
 static int get_client_hello(SSL *s)