use crypto_bigint::{Choice, CtAssign, CtEq, CtGt, CtSelect};
pub(crate) fn select_al(cl: &[u8], max_message_len: u16) -> u16 {
debug_assert_eq!(cl.len(), 256);
let mut mask = max_message_len;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
cl.chunks_exact(2).fold(0u16, |al, bytes| {
let candidate = u16::from_be_bytes([bytes[0], bytes[1]]) & mask;
al.ct_select(&candidate, !candidate.ct_gt(&max_message_len))
})
}
#[inline]
#[allow(
clippy::cast_possible_truncation,
reason = "k fits in u32 by the public precondition"
)]
pub(crate) fn eme_pkcs1v15_decode(em: &[u8], k: usize) -> (Choice, u32) {
debug_assert!(k >= 11);
debug_assert_eq!(em.len(), k);
debug_assert!(u32::try_from(k).is_ok());
let mut check = !em[0].ct_eq(&0u8) | !em[1].ct_eq(&2u8);
let mut looking_for_separator = Choice::TRUE;
let mut separator = 0u32;
for (i, octet) in em.iter().enumerate().skip(2) {
let is_zero = octet.ct_eq(&0u8);
separator.ct_assign(&(i as u32), looking_for_separator & is_zero);
looking_for_separator &= !is_zero;
}
check |= looking_for_separator | !separator.ct_gt(&9);
let l = 0u32.ct_select(&(k as u32 - separator - 1), !looking_for_separator);
(check, l)
}
#[inline]
pub(crate) fn select_message(em: &mut [u8], am: &[u8], al: u16, l: u32, check: Choice) -> u32 {
debug_assert_eq!(em.len(), am.len());
let length = l.ct_select(&u32::from(al), check);
for (em_octet, am_octet) in em.iter_mut().zip(am) {
em_octet.ct_assign(am_octet, check);
}
length
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn last_admissible_length() {
let mut cl = [0xff; 256];
cl[..2].copy_from_slice(&5u16.to_be_bytes());
cl[252..254].copy_from_slice(&20u16.to_be_bytes());
assert_eq!(select_al(&cl, 20), 20);
cl[..2].fill(0xff);
cl[252..254].fill(0xff);
assert_eq!(select_al(&cl, 20), 0);
assert_eq!(select_al(&cl, 0), 0);
}
#[test]
fn every_separator_and_header() {
const K: usize = 32;
let am = [0xa5; K];
for header in [[0, 2], [1, 2], [0, 1], [1, 1], [0, 0]] {
for separator in 2..=K {
for later_zero in [false, true] {
let mut original = [0x42; K];
original[..2].copy_from_slice(&header);
if separator < K {
original[separator] = 0;
if later_zero {
original[K - 1] = 0;
}
}
let (check, l) = eme_pkcs1v15_decode(&original, K);
let invalid = header != [0, 2] || !(10..K).contains(&separator);
let expected_l = if separator < K { K - separator - 1 } else { 0 };
assert_eq!(check.to_bool(), invalid);
assert_eq!(l as usize, expected_l);
for al in [0, 1, 21] {
let mut em = original;
let length = select_message(&mut em, &am, al, l, check) as usize;
let expected = if invalid {
&am[K - usize::from(al)..]
} else {
&original[K - expected_l..]
};
assert_eq!(&em[K - length..], expected);
}
}
}
}
}
#[test]
fn minimum_modulus_length() {
let em = [0, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0];
let (check, l) = eme_pkcs1v15_decode(&em, em.len());
assert!(!check.to_bool());
assert_eq!(l, 0);
}
}