use const_oid::AssociatedOid;
use digest::Digest;
use rand_core::CryptoRngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use zeroize::Zeroizing;
use crate::Prefix;
use crate::errors::{Error, Result};
#[inline]
fn non_zero_random_bytes<R: CryptoRngCore + ?Sized>(rng: &mut R, data: &mut [u8]) {
rng.fill_bytes(data);
for el in data {
if *el == 0u8 {
while *el == 0u8 {
rng.fill_bytes(core::slice::from_mut(el));
}
}
}
}
pub(crate) fn pkcs1v15_encrypt_pad<'a, R>(
rng: &mut R,
msg: &[u8],
k: usize,
storage: &'a mut [u8],
) -> Result<&'a [u8]>
where
R: CryptoRngCore + ?Sized,
{
if msg.len() + 11 > k {
return Err(Error::MessageTooLong);
}
let em = storage.get_mut(..k).ok_or(Error::OutputBufferTooSmall)?;
em[0] = 0;
em[1] = 2;
non_zero_random_bytes(rng, &mut em[2..k - msg.len() - 1]);
em[k - msg.len() - 1] = 0;
em[k - msg.len()..].copy_from_slice(msg);
Ok(em)
}
#[inline]
pub(crate) fn pkcs1v15_encrypt_unpad<'a>(
em: &'a [u8],
k: usize,
storage: &'a mut [u8],
) -> Result<&'a [u8]> {
let (valid, out, index) = decrypt_inner(em, k, storage)?;
if valid == 0 {
return Err(Error::Decryption);
}
let out_slice = storage.get_mut(..k).ok_or(Error::OutputBufferTooSmall)?;
todo!()
}
#[inline]
fn decrypt_inner<'a>(em: &'a [u8], k: usize, storage: &'a mut [u8]) -> Result<(u8, &'a [u8], u32)> {
if k < 11 {
return Err(Error::Decryption);
}
let first_byte_is_zero = em[0].ct_eq(&0u8);
let second_byte_is_two = em[1].ct_eq(&2u8);
let mut looking_for_index = 1u8;
let mut index = 0u32;
for (i, el) in em.iter().enumerate().skip(2) {
let equals0 = el.ct_eq(&0u8);
index.conditional_assign(&(i as u32), Choice::from(looking_for_index) & equals0);
looking_for_index.conditional_assign(&0u8, equals0);
}
let valid_ps = Choice::from((((2i32 + 8i32 - index as i32 - 1i32) >> 31) & 1) as u8);
let valid =
first_byte_is_zero & second_byte_is_two & Choice::from(!looking_for_index & 1) & valid_ps;
index = u32::conditional_select(&0, &(index + 1), valid);
Ok((valid.unwrap_u8(), em, index))
}
#[inline]
pub(crate) fn pkcs1v15_sign_pad<'a>(
prefix: &[u8],
hashed: &[u8],
k: usize,
storage: &'a mut [u8],
) -> Result<&'a [u8]> {
let hash_len = hashed.len();
let t_len = prefix.len() + hashed.len();
if k < t_len + 11 {
return Err(Error::MessageTooLong);
}
let em = storage.get_mut(..k).ok_or(Error::OutputBufferTooSmall)?;
em.iter_mut().for_each(|el| *el = 0xff);
em[0] = 0;
em[1] = 1;
em[k - t_len - 1] = 0;
em[k - t_len..k - hash_len].copy_from_slice(prefix);
em[k - hash_len..k].copy_from_slice(hashed);
Ok(em)
}
#[inline]
pub(crate) fn pkcs1v15_sign_unpad(prefix: &[u8], hashed: &[u8], em: &[u8], k: usize) -> Result<()> {
let hash_len = hashed.len();
let t_len = prefix.len() + hashed.len();
if k < t_len + 11 {
return Err(Error::Verification);
}
let mut ok = em[0].ct_eq(&0u8);
ok &= em[1].ct_eq(&1u8);
ok &= em[k - hash_len..k].ct_eq(hashed);
ok &= em[k - t_len..k - hash_len].ct_eq(prefix);
ok &= em[k - t_len - 1].ct_eq(&0u8);
for el in em.iter().skip(2).take(k - t_len - 3) {
ok &= el.ct_eq(&0xff)
}
if ok.unwrap_u8() != 1 {
return Err(Error::Verification);
}
Ok(())
}
#[inline]
pub(crate) fn pkcs1v15_generate_prefix<D>() -> Prefix
where
D: Digest + AssociatedOid,
{
let oid = D::OID.as_bytes();
let oid_len = oid.len() as u8;
let digest_len = <D as Digest>::output_size() as u8;
let mut v = Prefix::new();
v.extend([
0x30,
oid_len + 8 + digest_len,
0x30,
oid_len + 4,
0x6,
oid_len,
]);
v.extend_from_slice(oid).unwrap();
v.extend_from_slice(&[0x05, 0x00, 0x04, digest_len])
.unwrap();
v
}
#[cfg(test)]
mod tests {
use super::*;
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
#[test]
fn test_non_zero_bytes() {
for _ in 0..10 {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let mut b = vec![0u8; 512];
non_zero_random_bytes(&mut rng, &mut b);
for el in &b {
assert_ne!(*el, 0u8);
}
}
}
#[test]
fn test_encrypt_tiny_no_crash() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = 8;
let message = vec![1u8; 4];
let mut buffer = [0u8; 32];
let res = pkcs1v15_encrypt_pad(&mut rng, &message, k, &mut buffer);
assert_eq!(res, Err(Error::MessageTooLong));
}
}