#![no_std]
#![doc = include_str!("../README.md")]
#![forbid(unsafe_code, clippy::unwrap_used)]
#![warn(missing_docs, rust_2018_idioms, unreachable_pub)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
pub use cipher::Array;
pub use cipher::typenum::consts;
use cipher::{BlockCipherEncrypt, BlockSizeUser, KeyInit, array::ArraySize, typenum::Unsigned};
use digest::Digest;
use digest::const_oid::AssociatedOid;
mod ct;
type BlockArray<C> = <<C as BlockSizeUser>::BlockSize as ArraySize>::ArrayType<u8>;
#[inline]
pub fn generate_k<D, C, N>(
x: &Array<u8, N>,
q: &Array<u8, N>,
h: &Array<u8, N>,
data: &[u8],
) -> Array<u8, N>
where
D: BlockSizeUser + Clone + Digest + AssociatedOid,
C: BlockSizeUser + Clone + BlockCipherEncrypt + KeyInit,
N: ArraySize,
BlockArray<C>: Copy,
{
let mut k = Array::default();
generate_k_mut::<D, C>(x, q, h, data, &mut k);
k
}
#[inline]
pub fn generate_k_mut<D, C>(x: &[u8], q: &[u8], h: &[u8], data: &[u8], k: &mut [u8])
where
D: BlockSizeUser + Clone + Digest + AssociatedOid,
C: BlockSizeUser + Clone + BlockCipherEncrypt + KeyInit,
BlockArray<C>: Copy,
{
let len = k.len();
assert_eq!(len, x.len());
assert_eq!(len, q.len());
assert_eq!(len, h.len());
assert!(len == 32 || len == 48 || len == 64);
let n = len / C::BlockSize::USIZE;
let mut hasher: D = Digest::new();
hasher.update([0x06, (D::OID.len() - 1) as u8]);
hasher.update(D::OID);
hasher.update(x);
hasher.update(data);
let theta = hasher.finalize();
let cipher = C::new_from_slice(&theta).expect("Invalid key length");
let mut r = [Array::<u8, C::BlockSize>::default(); 4];
for (i, chunk) in h.chunks(C::BlockSize::USIZE).enumerate().take(n) {
r[i][..chunk.len()].copy_from_slice(chunk);
}
let mut i = 1u32;
loop {
let s = match n {
2 => {
r[0]
}
3 => {
let s = xor_blocks::<C>(&r[0], &r[1]);
r[0] = r[1];
s
}
4 => {
let mut s = xor_blocks::<C>(&r[0], &r[1]);
xor_assign(&mut s, &r[2]);
r[0] = r[1];
r[1] = r[2];
s
}
_ => unreachable!(),
};
let mut encrypted = s;
cipher.encrypt_block(&mut encrypted);
xor_assign(&mut encrypted, &r[n - 1]);
xor_assign(&mut encrypted, &i.to_le_bytes());
r[n - 2] = encrypted;
r[n - 1] = s;
if i % (2 * n as u32) == 0 {
for (j, chunk) in r.iter().enumerate().take(n) {
k[j * C::BlockSize::USIZE..(j + 1) * C::BlockSize::USIZE].copy_from_slice(chunk);
}
if (!ct::is_zero(k) & ct::lt(k, q)).into() {
return; }
}
i = i.wrapping_add(1);
}
}
#[inline]
fn xor_blocks<C>(
a: &Array<u8, C::BlockSize>,
b: &Array<u8, C::BlockSize>,
) -> Array<u8, C::BlockSize>
where
C: BlockSizeUser,
BlockArray<C>: Copy,
{
let mut result = *a;
xor_assign(&mut result, b);
result
}
#[inline]
fn xor_assign(a: &mut [u8], b: &[u8]) {
for (a_byte, b_byte) in a.iter_mut().zip(b.iter()) {
*a_byte ^= b_byte;
}
}
#[cfg(test)]
mod tests {
use super::*;
use belt_block::{BeltBlock, cipher::typenum::U32};
use belt_hash::BeltHash;
use hex_literal::hex;
#[test]
fn stb_table_g7() {
let d = hex!("1F66B5B8 4B733967 4533F032 9C74F218 34281FED 0732429E 0C79235F C273E269");
let q = hex!("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF C95E2EAB 40309C49 56129C2E F129D6CC");
let h = hex!("9D02EE44 6FB6A29F E5C982D4 B13AF9D3 E90861BC 4CEF27CF 306BFB0B 174A154A");
let t = hex!("BE329713 43FC9A48 A02A885F 194B09A1 7ECDA4D0 1544AF");
let expected_k =
hex!("7ADC8713 283EBFA5 47A2AD9C DFB245AE 0F7B968D F0F91CB7 85D1F932 A3583107");
let k = generate_k::<BeltHash, BeltBlock, U32>(&d.into(), &q.into(), &h.into(), &t);
assert_eq!(k.as_slice(), &expected_k);
}
}