File 0009-rsa-rsa_ssl.c-make-RSA_padding_check_SSLv23-constant.patch of Package compat-openssl098.14113

From 110ef88b99f1acc6b976f2e49153734924181db2 Mon Sep 17 00:00:00 2001
From: Andy Polyakov <appro@openssl.org>
Date: Fri, 14 Sep 2018 17:24:13 +0200
Subject: [PATCH 5/5] rsa/rsa_ssl.c: make RSA_padding_check_SSLv23
 constant-time.

Copy of RSA_padding_check_PKCS1_type_2 with a twist that rejects padding
if nul delimiter is preceded by 8 consecutive 0x03 bytes.

Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(cherry picked from commit 603221407ddc6404f8c417c6beadebf84449074c)

Resolved conflicts:
	crypto/rsa/rsa_ssl.c

(Merged from https://github.com/openssl/openssl/pull/7737)
---
 crypto/rsa/rsa_ssl.c | 134 +++++++++++++++++++++++++++++++------------
 1 file changed, 97 insertions(+), 37 deletions(-)

Index: openssl-1.0.1i/crypto/rsa/rsa_ssl.c
===================================================================
--- openssl-1.0.1i.orig/crypto/rsa/rsa_ssl.c
+++ openssl-1.0.1i/crypto/rsa/rsa_ssl.c
@@ -61,6 +61,7 @@
 #include <openssl/bn.h>
 #include <openssl/rsa.h>
 #include <openssl/rand.h>
+#include "constant_time_locl.h"
 
 int RSA_padding_add_SSLv23(unsigned char *to, int tlen,
 	const unsigned char *from, int flen)
@@ -105,58 +106,102 @@ int RSA_padding_add_SSLv23(unsigned char
 int RSA_padding_check_SSLv23(unsigned char *to, int tlen,
 	const unsigned char *from, int flen, int num)
 	{
-	int i,j,k;
-	const unsigned char *p;
+	int i;
+	/* |em| is the encoded message, zero-padded to exactly |num| bytes */
+	unsigned char *em = NULL;
+	unsigned int good, found_zero_byte, mask, threes_in_row;
+	int zero_index = 0, msg_index, mlen = -1, err;
 
-	p=from;
 	if (flen < 10)
 		{
 		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_DATA_TOO_SMALL);
 		return(-1);
 		}
-	/* Accept even zero-padded input */
-	if (flen == num) {
-		if (*(p++) != 0) {
-			RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, RSA_R_BLOCK_TYPE_IS_NOT_02);
-			return -1;
-		}
-		flen--;
+
+	em = OPENSSL_malloc(num);
+	if (em == NULL) {
+		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, ERR_R_MALLOC_FAILURE);
+		return -1;
 	}
-	if ((num != (flen+1)) || (*(p++) != 02))
-		{
-		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_BLOCK_TYPE_IS_NOT_02);
-		return(-1);
-		}
+	/*
+	 * Caller is encouraged to pass zero-padded message created with
+	 * BN_bn2binpad. Trouble is that since we can't read out of |from|'s
+	 * bounds, it's impossible to have an invariant memory access pattern
+	 * in case |from| was not zero-padded in advance.
+	 */
+	for (from += flen, em += num, i = 0; i < num; i++) {
+		mask = ~constant_time_is_zero(flen);
+		flen -= 1 & mask;
+		from -= 1 & mask;
+		*--em = *from & mask;
+	}
+	from = em;
 
-	/* scan over padding data */
-	j=flen-1; /* one for type */
-	for (i=0; i<j; i++)
-		if (*(p++) == 0) break;
+	good = constant_time_is_zero(from[0]);
+	good &= constant_time_eq(from[1], 2);
+	err = constant_time_select_int(good, 0, RSA_R_BLOCK_TYPE_IS_NOT_02);
+	mask = ~good;
 
-	if ((i == j) || (i < 8))
-		{
-		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_NULL_BEFORE_BLOCK_MISSING);
-		return(-1);
-		}
-	for (k = -9; k<-1; k++)
-		{
-		if (p[k] !=  0x03) break;
-		}
-	if (k == -1)
-		{
-		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_SSLV3_ROLLBACK_ATTACK);
-		return(-1);
-		}
+	/* scan over padding data */
+	found_zero_byte = 0;
+	threes_in_row = 0;
+	for (i = 2; i < num; i++) {
+		unsigned int equals0 = constant_time_is_zero(from[i]);
+		zero_index = constant_time_select_int(~found_zero_byte & equals0,
+						      i, zero_index);
+		found_zero_byte |= equals0;
+		threes_in_row += 1 & ~found_zero_byte;
+		threes_in_row &= found_zero_byte | constant_time_eq(from[i], 3);
+	}
 
-	i++; /* Skip over the '\0' */
-	j-=i;
-	if (j > tlen)
-		{
-		RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23,RSA_R_DATA_TOO_LARGE);
-		return(-1);
-		}
-	memcpy(to,p,(unsigned int)j);
+	/*
+	 * PS must be at least 8 bytes long, and it starts two bytes into |from|.
+	 * If we never found a 0-byte, then |zero_index| is 0 and the check
+	 * also fails.
+	 */
+	good &= constant_time_ge(zero_index, 2 + 8);
+	err = constant_time_select_int(mask | good, err,
+				       RSA_R_NULL_BEFORE_BLOCK_MISSING);
+	mask = ~good;
+	good &= constant_time_lt(threes_in_row, 8);
+	err = constant_time_select_int(mask | good, err,
+				       RSA_R_SSLV3_ROLLBACK_ATTACK);
+	mask = ~good;
+	/*
+	 * Skip the zero byte. This is incorrect if we never found a zero-byte
+	 * but in this case we also do not copy the message out.
+	 */
+	msg_index = zero_index + 1;
+	mlen = num - msg_index;
+	/*
+	 * For good measure, do this check in constant time as well.
+	 */
+	good &= constant_time_ge(tlen, mlen);
+	err = constant_time_select_int(mask | good, err, RSA_R_DATA_TOO_LARGE);
+	/*
+	 * Even though we can't fake result's length, we can pretend copying
+	 * |tlen| bytes where |mlen| bytes would be real. Last |tlen| of |num|
+	 * bytes are viewed as circular buffer with start at |tlen|-|mlen'|,
+	 * where |mlen'| is "saturated" |mlen| value. Deducing information
+	 * about failure or |mlen| would take attacker's ability to observe
+	 * memory access pattern with byte granularity *as it occurs*. It
+	 * should be noted that failure is indistinguishable from normal
+	 * operation if |tlen| is fixed by protocol.
+	 */
+	tlen = constant_time_select_int(constant_time_lt(num, tlen), num, tlen);
+	msg_index = constant_time_select_int(good, msg_index, num - tlen);
+	mlen = num - msg_index;
+	for (from += msg_index, mask = good, i = 0; i < tlen; i++) {
+		unsigned int equals = constant_time_eq(i, mlen);
+		from -= tlen & equals;  /* if (i == mlen) rewind   */
+		 mask &= mask ^ equals;  /* if (i == mlen) mask = 0 */
+		 to[i] = constant_time_select_8(mask, from[i], to[i]);
+	}
+	OPENSSL_cleanse(em, num);
+	OPENSSL_free(em);
+	RSAerr(RSA_F_RSA_PADDING_CHECK_SSLV23, err);
+	err_clear_last_constant_time(1 & good);
 
-	return(j);
+	return constant_time_select_int(good, mlen, -1);
 	}
openSUSE Build Service is sponsored by