Lower tor-proto::util::ct::lookup to tor-llcrypto
This is mostly code movement; you may want to review it with `--color-moved`. I'm doing this so we can also use the function in netdoc for looking up hsdesc authentication.
This commit is contained in:
parent
a20c3eda6e
commit
25db56777c
|
@ -0,0 +1 @@
|
|||
ADDED: ct_lookup
|
|
@ -87,8 +87,55 @@ impl<const N: usize> AsMut<[u8; N]> for CtByteArray<N> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Try to find an item in a slice without leaking where and whether the
|
||||
/// item was found.
|
||||
///
|
||||
/// If there is any item `x` in the `array` for which `matches(x)`
|
||||
/// is true, this function will return a reference to one such
|
||||
/// item. (We don't specify which.)
|
||||
///
|
||||
/// Otherwise, this function returns none.
|
||||
///
|
||||
/// We evaluate `matches` on every item of the array, and try not to
|
||||
/// leak by timing which element (if any) matched.
|
||||
///
|
||||
/// Note that this doesn't necessarily do a constant-time comparison,
|
||||
/// and that it is not constant-time for found/not-found case.
|
||||
pub fn ct_lookup<T, F>(array: &[T], matches: F) -> Option<&T>
|
||||
where
|
||||
F: Fn(&T) -> Choice,
|
||||
{
|
||||
// ConditionallySelectable isn't implemented for usize, so we need
|
||||
// to use u64.
|
||||
let mut idx: u64 = 0;
|
||||
let mut found: Choice = 0.into();
|
||||
|
||||
for (i, x) in array.iter().enumerate() {
|
||||
let equal = matches(x);
|
||||
idx.conditional_assign(&(i as u64), equal);
|
||||
found.conditional_assign(&equal, equal);
|
||||
}
|
||||
|
||||
if found.into() {
|
||||
Some(&array[idx as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// @@ begin test lint list maintained by maint/add_warning @@
|
||||
#![allow(clippy::bool_assert_comparison)]
|
||||
#![allow(clippy::clone_on_copy)]
|
||||
#![allow(clippy::dbg_macro)]
|
||||
#![allow(clippy::print_stderr)]
|
||||
#![allow(clippy::print_stdout)]
|
||||
#![allow(clippy::single_char_pattern)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::unchecked_duration_subtraction)]
|
||||
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
|
||||
|
||||
use super::*;
|
||||
use rand::Rng;
|
||||
use tor_basic_utils::test_rng;
|
||||
|
@ -122,4 +169,23 @@ mod test {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup() {
|
||||
use super::ct_lookup as lookup;
|
||||
use subtle::ConstantTimeEq;
|
||||
let items = vec![
|
||||
"One".to_string(),
|
||||
"word".to_string(),
|
||||
"of".to_string(),
|
||||
"every".to_string(),
|
||||
"length".to_string(),
|
||||
];
|
||||
let of_word = lookup(&items[..], |i| i.len().ct_eq(&2));
|
||||
let every_word = lookup(&items[..], |i| i.len().ct_eq(&5));
|
||||
let no_word = lookup(&items[..], |i| i.len().ct_eq(&99));
|
||||
assert_eq!(of_word.unwrap(), "of");
|
||||
assert_eq!(every_word.unwrap(), "every");
|
||||
assert_eq!(no_word, None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use tor_error::into_internal;
|
|||
use tor_llcrypto::d;
|
||||
use tor_llcrypto::pk::curve25519::*;
|
||||
use tor_llcrypto::pk::rsa::RsaIdentity;
|
||||
use tor_llcrypto::util::ct::ct_lookup;
|
||||
|
||||
use digest::Mac;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
@ -285,7 +286,7 @@ where
|
|||
let my_key: PublicKey = cur.extract()?;
|
||||
let their_pk: PublicKey = cur.extract()?;
|
||||
|
||||
let keypair = ct::lookup(keys, |key| key.matches_pk(&my_key));
|
||||
let keypair = ct_lookup(keys, |key| key.matches_pk(&my_key));
|
||||
let keypair = match keypair {
|
||||
Some(k) => k,
|
||||
None => return Err(RelayHandshakeError::MissingKey),
|
||||
|
|
|
@ -19,7 +19,7 @@ use tor_bytes::{EncodeResult, Reader, SecretBuf, Writeable, Writer};
|
|||
use tor_error::into_internal;
|
||||
use tor_llcrypto::d::{Sha3_256, Shake256};
|
||||
use tor_llcrypto::pk::{curve25519, ed25519::Ed25519Identity};
|
||||
use tor_llcrypto::util::rand_compat::RngCompatExt;
|
||||
use tor_llcrypto::util::{ct::ct_lookup, rand_compat::RngCompatExt};
|
||||
|
||||
use cipher::{KeyIvInit, StreamCipher};
|
||||
|
||||
|
@ -444,7 +444,7 @@ fn server_handshake_ntor_v3_no_keygen<REPLY: MsgReply>(
|
|||
r.should_be_exhausted()?;
|
||||
|
||||
// See if we recognize the provided (id,requested_pk) pair.
|
||||
let keypair = ct::lookup(keys, |key| key.matches(id, requested_pk));
|
||||
let keypair = ct_lookup(keys, |key| key.matches(id, requested_pk));
|
||||
let keypair = match keypair {
|
||||
Some(k) => k,
|
||||
None => return Err(RelayHandshakeError::MissingKey),
|
||||
|
|
|
@ -1,41 +1,5 @@
|
|||
//! Constant-time utilities.
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
/// Try to find an item in a slice without leaking where and whether the
|
||||
/// item was found.
|
||||
///
|
||||
/// If there is any item `x` in the `array` for which `matches(x)`
|
||||
/// is true, this function will return a reference to one such
|
||||
/// item. (We don't specify which.)
|
||||
///
|
||||
/// Otherwise, this function returns none.
|
||||
///
|
||||
/// We evaluate `matches` on every item of the array, and try not to
|
||||
/// leak by timing which element (if any) matched.
|
||||
///
|
||||
/// Note that this doesn't necessarily do a constant-time comparison,
|
||||
/// and that it is not constant-time for found/not-found case.
|
||||
pub(crate) fn lookup<T, F>(array: &[T], matches: F) -> Option<&T>
|
||||
where
|
||||
F: Fn(&T) -> Choice,
|
||||
{
|
||||
// ConditionallySelectable isn't implemented for usize, so we need
|
||||
// to use u64.
|
||||
let mut idx: u64 = 0;
|
||||
let mut found: Choice = 0.into();
|
||||
|
||||
for (i, x) in array.iter().enumerate() {
|
||||
let equal = matches(x);
|
||||
idx.conditional_assign(&(i as u64), equal);
|
||||
found.conditional_assign(&equal, equal);
|
||||
}
|
||||
|
||||
if found.into() {
|
||||
Some(&array[idx as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
|
||||
/// Convert a boolean into a Choice.
|
||||
///
|
||||
|
@ -72,23 +36,4 @@ mod test {
|
|||
assert!(!bytes_eq(&b"hi"[..], &b"45"[..]));
|
||||
assert!(bytes_eq(&b""[..], &b""[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup() {
|
||||
use super::lookup;
|
||||
use subtle::ConstantTimeEq;
|
||||
let items = vec![
|
||||
"One".to_string(),
|
||||
"word".to_string(),
|
||||
"of".to_string(),
|
||||
"every".to_string(),
|
||||
"length".to_string(),
|
||||
];
|
||||
let of_word = lookup(&items[..], |i| i.len().ct_eq(&2));
|
||||
let every_word = lookup(&items[..], |i| i.len().ct_eq(&5));
|
||||
let no_word = lookup(&items[..], |i| i.len().ct_eq(&99));
|
||||
assert_eq!(of_word.unwrap(), "of");
|
||||
assert_eq!(every_word.unwrap(), "every");
|
||||
assert_eq!(no_word, None);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue