diff --git a/Cargo.toml b/Cargo.toml index c8ac2a3978..5702eaa29f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ include = [ "crypto/chacha/asm/chacha-x86_64.pl", "crypto/cipher_extra/test/aes_128_gcm_siv_tests.txt", "crypto/cipher_extra/test/aes_256_gcm_siv_tests.txt", + "crypto/constant_time.c", "crypto/constant_time_test.c", "crypto/cpu_intel.c", "crypto/crypto.c", diff --git a/build.rs b/build.rs index f7b94108b7..872666e882 100644 --- a/build.rs +++ b/build.rs @@ -34,6 +34,7 @@ const ARM: &str = "arm"; #[rustfmt::skip] const RING_SRCS: &[(&[&str], &str)] = &[ + (&[], "crypto/constant_time.c"), (&[], "crypto/curve25519/curve25519.c"), (&[], "crypto/fipsmodule/aes/aes_nohw.c"), (&[], "crypto/fipsmodule/bn/montgomery.c"), @@ -910,6 +911,7 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String { "OPENSSL_armcap_P", "OPENSSL_cpuid_setup", "OPENSSL_ia32cap_P", + "RING_value_barrier_w", "aes_hw_ctr32_encrypt_blocks", "aes_hw_encrypt", "aes_hw_set_encrypt_key", diff --git a/crypto/constant_time.c b/crypto/constant_time.c new file mode 100644 index 0000000000..56bfef6288 --- /dev/null +++ b/crypto/constant_time.c @@ -0,0 +1,19 @@ +/* Copyright 2023 Brian Smith. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "internal.h" + +RING_NOINLINE crypto_word_t RING_value_barrier_w(crypto_word_t a) { + return value_barrier_w(a); +} diff --git a/src/constant_time.rs b/src/constant_time.rs index 3ac6ad4c21..b0e99f628a 100644 --- a/src/constant_time.rs +++ b/src/constant_time.rs @@ -14,25 +14,67 @@ //! Constant-time operations. -use crate::{c, error}; +use crate::error; /// Returns `Ok(())` if `a == b` and `Err(error::Unspecified)` otherwise. /// The comparison of `a` and `b` is done in constant time with respect to the /// contents of each, but NOT in constant time with respect to the lengths of /// `a` and `b`. pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<(), error::Unspecified> { + verify_equal(a.iter().copied(), b.iter().copied()) +} + +/// Types that have a zero value. +pub(crate) trait Zero { + /// The zero value. + fn zero() -> Self; +} + +/// All operations in the supertraits are assumed to be constant time. +pub(crate) trait CryptoValue: + Zero + Into + core::ops::BitXor + core::ops::BitOr +{ +} + +impl Zero for u8 { + fn zero() -> Self { + 0 + } +} + +impl CryptoValue for u8 {} + +// TODO: Use this in internal callers, in favor of `verify_slices_are_equal`. +#[inline] +pub(crate) fn verify_equal( + a: impl ExactSizeIterator, + b: impl ExactSizeIterator, +) -> Result<(), error::Unspecified> +where + T: CryptoValue, +{ if a.len() != b.len() { return Err(error::Unspecified); } - let result = unsafe { CRYPTO_memcmp(a.as_ptr(), b.as_ptr(), a.len()) }; - match result { + let zero_if_equal = a.zip(b).fold(T::zero(), |accum, (a, b)| accum | (a ^ b)); + let zero_if_equal = unsafe { RING_value_barrier_w(zero_if_equal.into()) }; + match zero_if_equal { 0 => Ok(()), _ => Err(error::Unspecified), } } +/// The integer type that's the "natural" unsigned machine word size. +pub(crate) type Word = CryptoWord; + +// Keep in sync with `crypto_word_t` in crypto/internal.h. +#[cfg(target_pointer_width = "32")] +type CryptoWord = u32; +#[cfg(target_pointer_width = "64")] +type CryptoWord = u64; + prefixed_extern! { - fn CRYPTO_memcmp(a: *const u8, b: *const u8, len: c::size_t) -> c::int; + fn RING_value_barrier_w(a: CryptoWord) -> CryptoWord; } #[cfg(test)]