Skip to content

Commit

Permalink
Implement constant-time byte array comparison in Rust.
Browse files Browse the repository at this point in the history
Add `RING_value_barrier_w` so that Rust code can use `value_barrier_w`.

Then use it to implement the constant-time comparison in Rust.
  • Loading branch information
briansmith committed Jan 13, 2024
1 parent accca78 commit 55f1cd9
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 5 deletions.
3 changes: 2 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down Expand Up @@ -885,7 +886,7 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
];

static SYMBOLS_TO_PREFIX: &[&str] = &[
"CRYPTO_memcmp",
"RING_value_barrier_w",
"CRYPTO_poly1305_finish",
"CRYPTO_poly1305_finish_neon",
"CRYPTO_poly1305_init",
Expand Down
19 changes: 19 additions & 0 deletions crypto/constant_time.c
Original file line number Diff line number Diff line change
@@ -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);
}
27 changes: 23 additions & 4 deletions src/constant_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,44 @@

//! 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_all_bytes_are_equal(a.iter().copied(), b.iter().copied())
}

// TODO: Use this in internal callers, in favor of `verify_slices_are_equal`.
#[inline]
pub(crate) fn verify_all_bytes_are_equal(
a: impl ExactSizeIterator<Item = u8>,
b: impl ExactSizeIterator<Item = u8>,
) -> Result<(), error::Unspecified> {
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(0, |accum, (a, b)| accum | (a ^ b));
let zero_if_equal = unsafe { RING_value_barrier_w(Word::from(zero_if_equal)) };
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)]
Expand Down

0 comments on commit 55f1cd9

Please sign in to comment.