Commit 5a3d21c0 authored by Emilia Kasper's avatar Emilia Kasper
Browse files

Constant-time utilities



Pull constant-time methods out to a separate header, add tests.

Reviewed-by: default avatarBodo Moeller <bodo@openssl.org>
parent f9fb43e1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ CPUID_OBJ=mem_clr.o
LIBS=

GENERAL=Makefile README crypto-lib.com install.com
TEST=constant_time_test.c

LIB= $(TOP)/libcrypto.a
SHARED_LIB= libcrypto$(SHLIB_EXT)
+169 −0
Original line number Diff line number Diff line
/* crypto/constant_time_locl.h */
/*
 * Utilities for constant-time cryptography.
 *
 * Author: Emilia Kasper (emilia@openssl.org)
 * Based on previous work by Bodo Moeller, Emilia Kasper, Adam Langley
 * (Google).
 * ====================================================================
 * Copyright (c) 2014 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#ifndef HEADER_CONSTANT_TIME_LOCL_H
#define HEADER_CONSTANT_TIME_LOCL_H

#include "e_os.h"  /* For 'inline' */

#ifdef __cplusplus
extern "C" {
#endif

/*
 * The following methods return a bitmask of all ones (0xff...f) for true
 * and 0 for false. This is useful for choosing a value based on the result
 * of a conditional in constant time. For example,
 *
 * if (a < b) {
 *   c = a;
 * } else {
 *   c = b;
 * }
 *
 * can be written as
 *
 * unsigned int lt = constant_time_lt(a, b);
 * c = a & lt | b & ~lt;
 */

/*
 * Returns the given value with the MSB copied to all the other
 * bits. Uses the fact that arithmetic shift shifts-in the sign bit.
 * However, this is not ensured by the C standard so you may need to
 * replace this with something else on odd CPUs.
 */
static inline unsigned int constant_time_msb(unsigned int a);

/*
 * Returns 0xff..f if a < b and 0 otherwise.
 */
inline unsigned int constant_time_lt(unsigned int a, unsigned int b);
/* Convenience method for getting an 8-bit mask. */
inline unsigned char constant_time_lt_8(unsigned int a, unsigned int b);

/*
 * Returns 0xff..f if a >= b and 0 otherwise.
 */
inline unsigned int constant_time_ge(unsigned int a, unsigned int b);
/* Convenience method for getting an 8-bit mask. */
inline unsigned char constant_time_ge_8(unsigned int a, unsigned int b);

/*
 * Returns 0xff..f if a == 0 and 0 otherwise.
 */
inline unsigned int constant_time_is_zero(unsigned int a);
/* Convenience method for getting an 8-bit mask. */
inline unsigned char constant_time_is_zero_8(unsigned int a);


/*
 * Returns 0xff..f if a == b and 0 otherwise.
 */
inline unsigned int constant_time_eq(unsigned int a, unsigned int b);
/* Convenience method for getting an 8-bit mask. */
inline unsigned char constant_time_eq_8(unsigned int a, unsigned int b);

static inline unsigned int constant_time_msb(unsigned int a)
	{
	return (unsigned int)((int)(a) >> (sizeof(int) * 8 - 1));
	}

inline unsigned int constant_time_lt(unsigned int a, unsigned int b)
	{
	unsigned int lt;
	/* Case 1: msb(a) == msb(b). a < b iff the MSB of a - b is set.*/
	lt = ~(a ^ b) & (a - b);
	/* Case 2: msb(a) != msb(b). a < b iff the MSB of b is set. */
	lt |= ~a & b;
	return constant_time_msb(lt);
	}

inline unsigned char constant_time_lt_8(unsigned int a, unsigned int b)
	{
	return (unsigned char)(constant_time_lt(a, b));
	}

inline unsigned int constant_time_ge(unsigned int a, unsigned int b)
	{
	unsigned int ge;
	/* Case 1: msb(a) == msb(b). a >= b iff the MSB of a - b is not set.*/
	ge = ~((a ^ b) | (a - b));
	/* Case 2: msb(a) != msb(b). a >= b iff the MSB of a is set. */
	ge |= a & ~b;
	return constant_time_msb(ge);
	}

inline unsigned char constant_time_ge_8(unsigned int a, unsigned int b)
	{
	return (unsigned char)(constant_time_ge(a, b));
	}

inline unsigned int constant_time_is_zero(unsigned int a)
	{
	return constant_time_msb(~a & (a - 1));
	}

inline unsigned char constant_time_is_zero_8(unsigned int a)
	{
	return (unsigned char)(constant_time_is_zero(a));
	}

inline unsigned int constant_time_eq(unsigned int a, unsigned int b)
	{
	return constant_time_is_zero(a ^ b);
	}

inline unsigned char constant_time_eq_8(unsigned int a, unsigned int b)
	{
	return (unsigned char)(constant_time_eq(a, b));
	}

#ifdef __cplusplus
}
#endif

#endif  /* HEADER_CONSTANT_TIME_LOCL_H */
+205 −0
Original line number Diff line number Diff line
/* crypto/constant_time_test.c */
/*
 * Utilities for constant-time cryptography.
 *
 * Author: Emilia Kasper (emilia@openssl.org)
 * Based on previous work by Bodo Moeller, Emilia Kasper, Adam Langley
 * (Google).
 * ====================================================================
 * Copyright (c) 2014 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include "../crypto/constant_time_locl.h"

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

static const unsigned int CONSTTIME_TRUE = ~0;
static const unsigned int CONSTTIME_FALSE = 0;
static const unsigned char CONSTTIME_TRUE_8 = ~0;
static const unsigned char CONSTTIME_FALSE_8 = 0;

static int test_binary_op(unsigned int (*op)(unsigned int a, unsigned int b),
	const char* op_name, unsigned int a, unsigned int b, int is_true)
	{
	unsigned c = op(a, b);
	if (is_true && c != CONSTTIME_TRUE)
		{
		fprintf(stderr, "Test failed for %s(%du, %du): expected %du "
			"(TRUE), got %du\n", op_name, a, b, CONSTTIME_TRUE, c);
		return 1;
		}
	else if (!is_true && c != CONSTTIME_FALSE)
		{
		fprintf(stderr, "Test failed for  %s(%du, %du): expected %du "
			"(FALSE), got %du\n", op_name, a, b, CONSTTIME_FALSE,
			c);
		return 1;
		}
        return 0;
	}

static int test_binary_op_8(unsigned char (*op)(unsigned int a, unsigned int b),
	const char* op_name, unsigned int a, unsigned int b, int is_true)
	{
	unsigned char c = op(a, b);
	if (is_true && c != CONSTTIME_TRUE_8)
		{
		fprintf(stderr, "Test failed for %s(%du, %du): expected %u "
			"(TRUE), got %u\n", op_name, a, b, CONSTTIME_TRUE_8, c);
		return 1;
		}
	else if (!is_true && c != CONSTTIME_FALSE_8)
		{
		fprintf(stderr, "Test failed for  %s(%du, %du): expected %u "
			"(FALSE), got %u\n", op_name, a, b, CONSTTIME_FALSE_8,
			c);
		return 1;
		}
        return 0;
	}

static int test_is_zero(unsigned int a)
	{
	unsigned int c = constant_time_is_zero(a);
	if (a == 0 && c != CONSTTIME_TRUE)
		{
		fprintf(stderr, "Test failed for constant_time_is_zero(%du): "
			"expected %du (TRUE), got %du\n", a, CONSTTIME_TRUE, c);
		return 1;
		}
	else if (a != 0 && c != CONSTTIME_FALSE)
		{
		fprintf(stderr, "Test failed for constant_time_is_zero(%du): "
			"expected %du (FALSE), got %du\n", a, CONSTTIME_FALSE,
			c);
		return 1;
		}
        return 0;
	}

static int test_is_zero_8(unsigned int a)
	{
	unsigned char c = constant_time_is_zero_8(a);
	if (a == 0 && c != CONSTTIME_TRUE_8)
		{
		fprintf(stderr, "Test failed for constant_time_is_zero(%du): "
			"expected %u (TRUE), got %u\n", a, CONSTTIME_TRUE_8, c);
		return 1;
		}
	else if (a != 0 && c != CONSTTIME_FALSE)
		{
		fprintf(stderr, "Test failed for constant_time_is_zero(%du): "
			"expected %u (FALSE), got %u\n", a, CONSTTIME_FALSE_8,
			c);
		return 1;
		}
        return 0;
	}

static unsigned int test_values[] = {0, 1, 1024, 12345, 32000, UINT_MAX/2-1,
                                     UINT_MAX/2, UINT_MAX/2+1, UINT_MAX-1,
                                     UINT_MAX};

int main(int argc, char *argv[])
	{
	unsigned int a, b, i, j;
	int num_failed = 0, num_all = 0;
	fprintf(stdout, "Testing constant time operations...\n");

	for (i = 0; i < sizeof(test_values)/sizeof(int); ++i)
		{
		a = test_values[i];
		num_failed += test_is_zero(a);
		num_failed += test_is_zero_8(a);
		num_failed += test_binary_op(&constant_time_lt,
			"constant_time_lt", a, a, 0);
		num_failed += test_binary_op_8(&constant_time_lt_8,
			"constant_time_lt_8", a, a, 0);
		num_failed += test_binary_op(&constant_time_ge,
			"constant_time_ge", a, a, 1);
		num_failed += test_binary_op_8(&constant_time_ge_8,
			"constant_time_ge_8", a, a, 1);
		num_failed += test_binary_op(&constant_time_eq,
			"constant_time_eq", a, a, 1);
		num_failed += test_binary_op_8(&constant_time_eq_8,
			"constant_time_eq_8", a, a, 1);
		num_all += 8;
		for (j = i + 1; j < sizeof(test_values)/sizeof(int); ++j)
			{
			b = test_values[j];
			num_failed += test_binary_op(&constant_time_lt,
				"constant_time_lt", a, b, a < b);
			num_failed += test_binary_op_8(&constant_time_lt_8,
				"constant_time_lt_8", a, b, a < b);
			num_failed += test_binary_op(&constant_time_lt,
				"constant_time_lt_8", b, a, b < a);
			num_failed += test_binary_op_8(&constant_time_lt_8,
				"constant_time_lt_8", b, a, b < a);
			num_failed += test_binary_op(&constant_time_ge,
				"constant_time_ge", a, b, a >= b);
			num_failed += test_binary_op_8(&constant_time_ge_8,
				"constant_time_ge_8", a, b, a >= b);
			num_failed += test_binary_op(&constant_time_ge,
				"constant_time_ge", b, a, b >= a);
			num_failed += test_binary_op_8(&constant_time_ge_8,
				"constant_time_ge_8", b, a, b >= a);
			num_failed += test_binary_op(&constant_time_eq,
				"constant_time_eq", a, b, a == b);
			num_failed += test_binary_op_8(&constant_time_eq_8,
				"constant_time_eq_8", a, b, a == b);
			num_failed += test_binary_op(&constant_time_eq,
				"constant_time_eq", b, a, b == a);
			num_failed += test_binary_op_8(&constant_time_eq_8,
				"constant_time_eq_8", b, a, b == a);
			num_all += 12;
			}
		}

	if (!num_failed)
		{
		fprintf(stdout, "ok (ran %d tests)\n", num_all);
		return EXIT_SUCCESS;
		}
	else
		{
		fprintf(stdout, "%d of %d tests failed!\n", num_failed, num_all);
		return EXIT_FAILURE;
		}
	}
+21 −20
Original line number Diff line number Diff line
@@ -547,26 +547,27 @@ s3_both.o: ../include/openssl/ssl23.h ../include/openssl/ssl3.h
s3_both.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
s3_both.o: ../include/openssl/tls1.h ../include/openssl/x509.h
s3_both.o: ../include/openssl/x509_vfy.h s3_both.c ssl_locl.h
s3_cbc.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h
s3_cbc.o: ../include/openssl/buffer.h ../include/openssl/comp.h
s3_cbc.o: ../include/openssl/crypto.h ../include/openssl/dsa.h
s3_cbc.o: ../include/openssl/dtls1.h ../include/openssl/e_os2.h
s3_cbc.o: ../include/openssl/ec.h ../include/openssl/ecdh.h
s3_cbc.o: ../include/openssl/ecdsa.h ../include/openssl/err.h
s3_cbc.o: ../include/openssl/evp.h ../include/openssl/hmac.h
s3_cbc.o: ../include/openssl/kssl.h ../include/openssl/lhash.h
s3_cbc.o: ../include/openssl/md5.h ../include/openssl/obj_mac.h
s3_cbc.o: ../include/openssl/objects.h ../include/openssl/opensslconf.h
s3_cbc.o: ../include/openssl/opensslv.h ../include/openssl/ossl_typ.h
s3_cbc.o: ../include/openssl/pem.h ../include/openssl/pem2.h
s3_cbc.o: ../include/openssl/pkcs7.h ../include/openssl/pqueue.h
s3_cbc.o: ../include/openssl/rsa.h ../include/openssl/safestack.h
s3_cbc.o: ../include/openssl/sha.h ../include/openssl/srtp.h
s3_cbc.o: ../include/openssl/ssl.h ../include/openssl/ssl2.h
s3_cbc.o: ../include/openssl/ssl23.h ../include/openssl/ssl3.h
s3_cbc.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
s3_cbc.o: ../include/openssl/tls1.h ../include/openssl/x509.h
s3_cbc.o: ../include/openssl/x509_vfy.h s3_cbc.c ssl_locl.h
s3_cbc.o: ../crypto/constant_time_locl.h ../e_os.h ../include/openssl/asn1.h
s3_cbc.o: ../include/openssl/bio.h ../include/openssl/buffer.h
s3_cbc.o: ../include/openssl/comp.h ../include/openssl/crypto.h
s3_cbc.o: ../include/openssl/dsa.h ../include/openssl/dtls1.h
s3_cbc.o: ../include/openssl/e_os2.h ../include/openssl/ec.h
s3_cbc.o: ../include/openssl/ecdh.h ../include/openssl/ecdsa.h
s3_cbc.o: ../include/openssl/err.h ../include/openssl/evp.h
s3_cbc.o: ../include/openssl/hmac.h ../include/openssl/kssl.h
s3_cbc.o: ../include/openssl/lhash.h ../include/openssl/md5.h
s3_cbc.o: ../include/openssl/obj_mac.h ../include/openssl/objects.h
s3_cbc.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h
s3_cbc.o: ../include/openssl/ossl_typ.h ../include/openssl/pem.h
s3_cbc.o: ../include/openssl/pem2.h ../include/openssl/pkcs7.h
s3_cbc.o: ../include/openssl/pqueue.h ../include/openssl/rsa.h
s3_cbc.o: ../include/openssl/safestack.h ../include/openssl/sha.h
s3_cbc.o: ../include/openssl/srtp.h ../include/openssl/ssl.h
s3_cbc.o: ../include/openssl/ssl2.h ../include/openssl/ssl23.h
s3_cbc.o: ../include/openssl/ssl3.h ../include/openssl/stack.h
s3_cbc.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
s3_cbc.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h s3_cbc.c
s3_cbc.o: ssl_locl.h
s3_clnt.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h
s3_clnt.o: ../include/openssl/bn.h ../include/openssl/buffer.h
s3_clnt.o: ../include/openssl/comp.h ../include/openssl/crypto.h
+10 −46
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@
 *
 */

#include "../crypto/constant_time_locl.h"
#include "ssl_locl.h"

#include <openssl/md5.h>
@@ -67,37 +68,6 @@
 * supported by TLS.) */
#define MAX_HASH_BLOCK_SIZE 128

/* Some utility functions are needed:
 *
 * These macros return the given value with the MSB copied to all the other
 * bits. They use the fact that arithmetic shift shifts-in the sign bit.
 * However, this is not ensured by the C standard so you may need to replace
 * them with something else on odd CPUs. */
#define DUPLICATE_MSB_TO_ALL(x) ( (unsigned)( (int)(x) >> (sizeof(int)*8-1) ) )
#define DUPLICATE_MSB_TO_ALL_8(x) ((unsigned char)(DUPLICATE_MSB_TO_ALL(x)))

/* constant_time_lt returns 0xff if a<b and 0x00 otherwise. */
static unsigned constant_time_lt(unsigned a, unsigned b)
	{
	a -= b;
	return DUPLICATE_MSB_TO_ALL(a);
	}

/* constant_time_ge returns 0xff if a>=b and 0x00 otherwise. */
static unsigned constant_time_ge(unsigned a, unsigned b)
	{
	a -= b;
	return DUPLICATE_MSB_TO_ALL(~a);
	}

/* constant_time_eq_8 returns 0xff if a==b and 0x00 otherwise. */
static unsigned char constant_time_eq_8(unsigned a, unsigned b)
	{
	unsigned c = a ^ b;
	c--;
	return DUPLICATE_MSB_TO_ALL_8(c);
	}

/* ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC
 * record in |rec| by updating |rec->length| in constant time.
 *
@@ -207,7 +177,7 @@ int tls1_cbc_remove_padding(const SSL* s,

	for (i = 0; i < to_check; i++)
		{
		unsigned char mask = constant_time_ge(padding_length, i);
		unsigned char mask = constant_time_ge_8(padding_length, i);
		unsigned char b = rec->data[rec->length-1-i];
		/* The final |padding_length+1| bytes should all have the value
		 * |padding_length|. Therefore the XOR should be zero. */
@@ -215,15 +185,9 @@ int tls1_cbc_remove_padding(const SSL* s,
		}

	/* If any of the final |padding_length+1| bytes had the wrong value,
	 * one or more of the lower eight bits of |good| will be cleared. We
	 * AND the bottom 8 bits together and duplicate the result to all the
	 * bits. */
	good &= good >> 4;
	good &= good >> 2;
	good &= good >> 1;
	good <<= sizeof(good)*8-1;
	good = DUPLICATE_MSB_TO_ALL(good);

	 * one or more of the lower eight bits of |good| will be cleared.
	 */
	good = constant_time_eq(0xff, good & 0xff);
	rec->length -= good & (padding_length+1);

	return (int)((good & 1) | (~good & -1));
@@ -293,8 +257,8 @@ void ssl3_cbc_copy_mac(unsigned char* out,
	memset(rotated_mac, 0, md_size);
	for (i = scan_start, j = 0; i < rec->orig_len; i++)
		{
		unsigned char mac_started = constant_time_ge(i, mac_start);
		unsigned char mac_ended = constant_time_ge(i, mac_end);
		unsigned char mac_started = constant_time_ge_8(i, mac_start);
		unsigned char mac_ended = constant_time_ge_8(i, mac_end);
		unsigned char b = rec->data[i];
		rotated_mac[j++] |= b & mac_started & ~mac_ended;
		j &= constant_time_lt(j,md_size);
@@ -681,8 +645,8 @@ void ssl3_cbc_digest_record(
				b = data[k-header_length];
			k++;

			is_past_c = is_block_a & constant_time_ge(j, c);
			is_past_cp1 = is_block_a & constant_time_ge(j, c+1);
			is_past_c = is_block_a & constant_time_ge_8(j, c);
			is_past_cp1 = is_block_a & constant_time_ge_8(j, c+1);
			/* If this is the block containing the end of the
			 * application data, and we are at the offset for the
			 * 0x80 value, then overwrite b with 0x80. */
Loading