#[rustfmt::skip]
const SR: [u8; 256] = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
];
#[rustfmt::skip]
const SQ: [u8; 256] = [
0x25, 0x24, 0x73, 0x67, 0xD7, 0xAE, 0x5C, 0x30, 0xA4, 0xEE, 0x6E, 0xCB, 0x7D, 0xB5, 0x82, 0xDB,
0xE4, 0x8E, 0x48, 0x49, 0x4F, 0x5D, 0x6A, 0x78, 0x70, 0x88, 0xE8, 0x5F, 0x5E, 0x84, 0x65, 0xE2,
0xD8, 0xE9, 0xCC, 0xED, 0x40, 0x2F, 0x11, 0x28, 0x57, 0xD2, 0xAC, 0xE3, 0x4A, 0x15, 0x1B, 0xB9,
0xB2, 0x80, 0x85, 0xA6, 0x2E, 0x02, 0x47, 0x29, 0x07, 0x4B, 0x0E, 0xC1, 0x51, 0xAA, 0x89, 0xD4,
0xCA, 0x01, 0x46, 0xB3, 0xEF, 0xDD, 0x44, 0x7B, 0xC2, 0x7F, 0xBE, 0xC3, 0x9F, 0x20, 0x4C, 0x64,
0x83, 0xA2, 0x68, 0x42, 0x13, 0xB4, 0x41, 0xCD, 0xBA, 0xC6, 0xBB, 0x6D, 0x4D, 0x71, 0x21, 0xF4,
0x8D, 0xB0, 0xE5, 0x93, 0xFE, 0x8F, 0xE6, 0xCF, 0x43, 0x45, 0x31, 0x22, 0x37, 0x36, 0x96, 0xFA,
0xBC, 0x0F, 0x08, 0x52, 0x1D, 0x55, 0x1A, 0xC5, 0x4E, 0x23, 0x69, 0x7A, 0x92, 0xFF, 0x5B, 0x5A,
0xEB, 0x9A, 0x1C, 0xA9, 0xD1, 0x7E, 0x0D, 0xFC, 0x50, 0x8A, 0xB6, 0x62, 0xF5, 0x0A, 0xF8, 0xDC,
0x03, 0x3C, 0x0C, 0x39, 0xF1, 0xB8, 0xF3, 0x3D, 0xF2, 0xD5, 0x97, 0x66, 0x81, 0x32, 0xA0, 0x00,
0x06, 0xCE, 0xF6, 0xEA, 0xB7, 0x17, 0xF7, 0x8C, 0x79, 0xD6, 0xA7, 0xBF, 0x8B, 0x3F, 0x1F, 0x53,
0x63, 0x75, 0x35, 0x2C, 0x60, 0xFD, 0x27, 0xD3, 0x94, 0xA5, 0x7C, 0xA1, 0x05, 0x58, 0x2D, 0xBD,
0xD9, 0xC7, 0xAF, 0x6B, 0x54, 0x0B, 0xE0, 0x38, 0x04, 0xC8, 0x9D, 0xE7, 0x14, 0xB1, 0x87, 0x9C,
0xDF, 0x6F, 0xF9, 0xDA, 0x2A, 0xC4, 0x59, 0x16, 0x74, 0x91, 0xAB, 0x26, 0x61, 0x76, 0x34, 0x2B,
0xAD, 0x99, 0xFB, 0x72, 0xEC, 0x33, 0x12, 0xDE, 0x98, 0x3B, 0xC0, 0x9B, 0x3E, 0x18, 0x10, 0x3A,
0x56, 0xE1, 0x77, 0xC9, 0x1E, 0x9E, 0x95, 0xA3, 0x90, 0x19, 0xA8, 0x6C, 0x09, 0xD0, 0xF0, 0x86,
];
const SR_ANF: [[u128; 2]; 8] = crate::ct::build_byte_sbox_anf(&SR);
const SQ_ANF: [[u128; 2]; 8] = crate::ct::build_byte_sbox_anf(&SQ);
const MUL_ALPHA: [u32; 256] = build_alpha_table([23, 245, 48, 239]);
const DIV_ALPHA: [u32; 256] = build_alpha_table([16, 39, 6, 64]);
const MUL_ALPHA_FACTORS: [u8; 4] = alpha_factors([23, 245, 48, 239]);
const DIV_ALPHA_FACTORS: [u8; 4] = alpha_factors([16, 39, 6, 64]);
#[inline]
fn load_be_u32(bytes: &[u8]) -> u32 {
let mut word = [0u8; 4];
word.copy_from_slice(bytes);
u32::from_be_bytes(word)
}
#[inline]
const fn mulx(v: u8, c: u8) -> u8 {
if (v & 0x80) != 0 {
(v << 1) ^ c
} else {
v << 1
}
}
#[inline]
fn mulx_ct(v: u8, c: u8) -> u8 {
let hi = 0u8.wrapping_sub(v >> 7);
(v << 1) ^ (c & hi)
}
#[inline]
const fn mulx_pow(mut v: u8, i: u8, c: u8) -> u8 {
let mut n = 0;
while n < i {
v = mulx(v, c);
n += 1;
}
v
}
const fn build_alpha_table(pows: [u8; 4]) -> [u32; 256] {
let mut table = [0u32; 256];
let mut i = 0;
while i < 256 {
let c = i as u8;
table[i] = u32::from_be_bytes([
mulx_pow(c, pows[0], 0xA9),
mulx_pow(c, pows[1], 0xA9),
mulx_pow(c, pows[2], 0xA9),
mulx_pow(c, pows[3], 0xA9),
]);
i += 1;
}
table
}
const fn alpha_factors(pows: [u8; 4]) -> [u8; 4] {
[
mulx_pow(1, pows[0], 0xA9),
mulx_pow(1, pows[1], 0xA9),
mulx_pow(1, pows[2], 0xA9),
mulx_pow(1, pows[3], 0xA9),
]
}
#[inline]
fn gf_mul_const_ct(mut value: u8, mut factor: u8, poly: u8) -> u8 {
let mut out = 0u8;
let mut i = 0u8;
while i < 8 {
let bit_mask = 0u8.wrapping_sub(factor & 1);
out ^= value & bit_mask;
value = mulx_ct(value, poly);
factor >>= 1;
i = i.wrapping_add(1);
}
out
}
#[inline]
fn alpha_word_ct(input: u8, factors: [u8; 4]) -> u32 {
u32::from_be_bytes([
gf_mul_const_ct(input, factors[0], 0xA9),
gf_mul_const_ct(input, factors[1], 0xA9),
gf_mul_const_ct(input, factors[2], 0xA9),
gf_mul_const_ct(input, factors[3], 0xA9),
])
}
#[inline]
fn sbox_eval(coeffs: &[[u128; 2]; 8], input: u8) -> u8 {
crate::ct::eval_byte_sbox(coeffs, input)
}
#[inline]
fn s1<const CT: bool>(w: u32) -> u32 {
let (srw0, srw1, srw2, srw3) = if CT {
(
sbox_eval(&SR_ANF, (w >> 24) as u8),
sbox_eval(&SR_ANF, ((w >> 16) & 0xFF) as u8),
sbox_eval(&SR_ANF, ((w >> 8) & 0xFF) as u8),
sbox_eval(&SR_ANF, (w & 0xFF) as u8),
)
} else {
(
SR[(w >> 24) as usize],
SR[((w >> 16) & 0xFF) as usize],
SR[((w >> 8) & 0xFF) as usize],
SR[(w & 0xFF) as usize],
)
};
let mul = if CT { mulx_ct } else { mulx };
let r0 = mul(srw0, 0x1B) ^ srw1 ^ srw2 ^ mul(srw3, 0x1B) ^ srw3;
let r1 = mul(srw0, 0x1B) ^ srw0 ^ mul(srw1, 0x1B) ^ srw2 ^ srw3;
let r2 = srw0 ^ mul(srw1, 0x1B) ^ srw1 ^ mul(srw2, 0x1B) ^ srw3;
let r3 = srw0 ^ srw1 ^ mul(srw2, 0x1B) ^ srw2 ^ mul(srw3, 0x1B);
u32::from_be_bytes([r0, r1, r2, r3])
}
#[inline]
fn s2<const CT: bool>(w: u32) -> u32 {
let (sqw0, sqw1, sqw2, sqw3) = if CT {
(
sbox_eval(&SQ_ANF, (w >> 24) as u8),
sbox_eval(&SQ_ANF, ((w >> 16) & 0xFF) as u8),
sbox_eval(&SQ_ANF, ((w >> 8) & 0xFF) as u8),
sbox_eval(&SQ_ANF, (w & 0xFF) as u8),
)
} else {
(
SQ[(w >> 24) as usize],
SQ[((w >> 16) & 0xFF) as usize],
SQ[((w >> 8) & 0xFF) as usize],
SQ[(w & 0xFF) as usize],
)
};
let mul = if CT { mulx_ct } else { mulx };
let r0 = mul(sqw0, 0x69) ^ sqw1 ^ sqw2 ^ mul(sqw3, 0x69) ^ sqw3;
let r1 = mul(sqw0, 0x69) ^ sqw0 ^ mul(sqw1, 0x69) ^ sqw2 ^ sqw3;
let r2 = sqw0 ^ mul(sqw1, 0x69) ^ sqw1 ^ mul(sqw2, 0x69) ^ sqw3;
let r3 = sqw0 ^ sqw1 ^ mul(sqw2, 0x69) ^ sqw2 ^ mul(sqw3, 0x69);
u32::from_be_bytes([r0, r1, r2, r3])
}
struct Snow3gCore {
s: [u32; 16],
r1: u32,
r2: u32,
r3: u32,
}
#[inline]
fn clock_fsm<const CT: bool>(core: &mut Snow3gCore) -> u32 {
let f = core.s[15].wrapping_add(core.r1) ^ core.r2;
let r = core.r2.wrapping_add(core.r3 ^ core.s[5]);
core.r3 = s2::<CT>(core.r2);
core.r2 = s1::<CT>(core.r1);
core.r1 = r;
f
}
#[inline]
fn lfsr_feedback<const CT: bool>(core: &Snow3gCore) -> u32 {
let mul_alpha = if CT {
alpha_word_ct((core.s[0] >> 24) as u8, MUL_ALPHA_FACTORS)
} else {
MUL_ALPHA[(core.s[0] >> 24) as usize]
};
let div_alpha = if CT {
alpha_word_ct((core.s[11] & 0xFF) as u8, DIV_ALPHA_FACTORS)
} else {
DIV_ALPHA[(core.s[11] & 0xFF) as usize]
};
((core.s[0] << 8) & 0xFFFF_FF00)
^ mul_alpha
^ core.s[2]
^ ((core.s[11] >> 8) & 0x00FF_FFFF)
^ div_alpha
}
#[inline]
fn clock_lfsr<const CT: bool>(core: &mut Snow3gCore, f: Option<u32>) {
let mut v = lfsr_feedback::<CT>(core);
if let Some(fsm_word) = f {
v ^= fsm_word;
}
core.s.copy_within(1..16, 0);
core.s[15] = v;
}
fn init_core<const CT: bool>(key: &[u8; 16], iv: &[u8; 16]) -> Snow3gCore {
let k = [
load_be_u32(&key[0..4]),
load_be_u32(&key[4..8]),
load_be_u32(&key[8..12]),
load_be_u32(&key[12..16]),
];
let iv = [
load_be_u32(&iv[0..4]),
load_be_u32(&iv[4..8]),
load_be_u32(&iv[8..12]),
load_be_u32(&iv[12..16]),
];
let mut core = Snow3gCore {
s: [
k[0] ^ 0xFFFF_FFFF,
k[1] ^ 0xFFFF_FFFF,
k[2] ^ 0xFFFF_FFFF,
k[3] ^ 0xFFFF_FFFF,
k[0],
k[1],
k[2],
k[3],
k[0] ^ 0xFFFF_FFFF,
k[1] ^ 0xFFFF_FFFF ^ iv[3],
k[2] ^ 0xFFFF_FFFF ^ iv[2],
k[3] ^ 0xFFFF_FFFF,
k[0] ^ iv[1],
k[1],
k[2],
k[3] ^ iv[0],
],
r1: 0,
r2: 0,
r3: 0,
};
for _ in 0..32 {
let f = clock_fsm::<CT>(&mut core);
clock_lfsr::<CT>(&mut core, Some(f));
}
let _ = clock_fsm::<CT>(&mut core);
clock_lfsr::<CT>(&mut core, None);
core
}
#[inline]
fn next_word_core<const CT: bool>(core: &mut Snow3gCore) -> u32 {
let z = clock_fsm::<CT>(core) ^ core.s[0];
clock_lfsr::<CT>(core, None);
z
}
fn fill_core<const CT: bool>(core: &mut Snow3gCore, buf: &mut [u8]) {
let mut chunks = buf.chunks_exact_mut(4);
for chunk in &mut chunks {
let ks = next_word_core::<CT>(core).to_be_bytes();
for (b, k) in chunk.iter_mut().zip(ks.iter()) {
*b ^= k;
}
}
let rem = chunks.into_remainder();
if !rem.is_empty() {
let ks = next_word_core::<CT>(core).to_be_bytes();
for (b, k) in rem.iter_mut().zip(ks.iter()) {
*b ^= k;
}
}
}
pub struct Snow3g {
core: Snow3gCore,
}
pub struct Snow3gCt {
core: Snow3gCore,
}
impl Snow3g {
#[must_use]
pub fn new(key: &[u8; 16], iv: &[u8; 16]) -> Self {
Self {
core: init_core::<false>(key, iv),
}
}
pub fn new_wiping(key: &mut [u8; 16], iv: &mut [u8; 16]) -> Self {
let out = Self::new(key, iv);
crate::ct::zeroize_slice(key.as_mut_slice());
crate::ct::zeroize_slice(iv.as_mut_slice());
out
}
pub fn next_word(&mut self) -> u32 {
next_word_core::<false>(&mut self.core)
}
pub fn fill(&mut self, buf: &mut [u8]) {
fill_core::<false>(&mut self.core, buf);
}
}
impl Snow3gCt {
#[must_use]
pub fn new(key: &[u8; 16], iv: &[u8; 16]) -> Self {
Self {
core: init_core::<true>(key, iv),
}
}
pub fn new_wiping(key: &mut [u8; 16], iv: &mut [u8; 16]) -> Self {
let out = Self::new(key, iv);
crate::ct::zeroize_slice(key.as_mut_slice());
crate::ct::zeroize_slice(iv.as_mut_slice());
out
}
pub fn next_word(&mut self) -> u32 {
next_word_core::<true>(&mut self.core)
}
pub fn fill(&mut self, buf: &mut [u8]) {
fill_core::<true>(&mut self.core, buf);
}
}
impl Drop for Snow3g {
fn drop(&mut self) {
crate::ct::zeroize_slice(self.core.s.as_mut_slice());
self.core.r1 = 0;
self.core.r2 = 0;
self.core.r3 = 0;
}
}
impl Drop for Snow3gCt {
fn drop(&mut self) {
crate::ct::zeroize_slice(self.core.s.as_mut_slice());
self.core.r1 = 0;
self.core.r2 = 0;
self.core.r3 = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn xorshift64(state: &mut u64) -> u64 {
let mut x = *state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*state = x;
x
}
fn fill_bytes(state: &mut u64, out: &mut [u8]) {
for chunk in out.chunks_mut(8) {
let bytes = xorshift64(state).to_le_bytes();
let n = chunk.len();
chunk.copy_from_slice(&bytes[..n]);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct TraceRow {
s0: u32,
s2: u32,
s5: u32,
s11: u32,
s15: u32,
r1: u32,
r2: u32,
r3: u32,
}
fn trace_row(core: &Snow3gCore) -> TraceRow {
TraceRow {
s0: core.s[0],
s2: core.s[2],
s5: core.s[5],
s11: core.s[11],
s15: core.s[15],
r1: core.r1,
r2: core.r2,
r3: core.r3,
}
}
fn initial_lfsr_from_key_iv(key: &[u8; 16], iv: &[u8; 16]) -> [u32; 16] {
let k = [
load_be_u32(&key[0..4]),
load_be_u32(&key[4..8]),
load_be_u32(&key[8..12]),
load_be_u32(&key[12..16]),
];
let iv = [
load_be_u32(&iv[0..4]),
load_be_u32(&iv[4..8]),
load_be_u32(&iv[8..12]),
load_be_u32(&iv[12..16]),
];
[
k[0] ^ 0xFFFF_FFFF,
k[1] ^ 0xFFFF_FFFF,
k[2] ^ 0xFFFF_FFFF,
k[3] ^ 0xFFFF_FFFF,
k[0],
k[1],
k[2],
k[3],
k[0] ^ 0xFFFF_FFFF,
k[1] ^ 0xFFFF_FFFF ^ iv[3],
k[2] ^ 0xFFFF_FFFF ^ iv[2],
k[3] ^ 0xFFFF_FFFF,
k[0] ^ iv[1],
k[1],
k[2],
k[3] ^ iv[0],
]
}
#[derive(Clone, Copy)]
struct OfficialTraceCase {
key: [u8; 16],
iv: [u8; 16],
initial_lfsr: [u32; 16],
init_rows: [TraceRow; 8],
final_lfsr: [u32; 16],
final_fsm: [u32; 3],
keystream_rows: [TraceRow; 3],
outputs: [u32; 2],
}
fn assert_official_trace<const CT: bool>(case: &OfficialTraceCase) {
assert_eq!(
initial_lfsr_from_key_iv(&case.key, &case.iv),
case.initial_lfsr
);
let mut core = Snow3gCore {
s: case.initial_lfsr,
r1: 0,
r2: 0,
r3: 0,
};
assert_eq!(trace_row(&core), case.init_rows[0], "initial row");
for (i, expected) in case.init_rows.iter().enumerate().skip(1) {
let f = clock_fsm::<CT>(&mut core);
clock_lfsr::<CT>(&mut core, Some(f));
assert_eq!(trace_row(&core), *expected, "init row {i}");
}
for _ in (case.init_rows.len() - 1)..32 {
let f = clock_fsm::<CT>(&mut core);
clock_lfsr::<CT>(&mut core, Some(f));
}
assert_eq!(core.s, case.final_lfsr, "final LFSR after init");
assert_eq!(
[core.r1, core.r2, core.r3],
case.final_fsm,
"final FSM after init"
);
let _ = clock_fsm::<CT>(&mut core);
clock_lfsr::<CT>(&mut core, None);
assert_eq!(trace_row(&core), case.keystream_rows[0], "keystream row 0");
let z1 = next_word_core::<CT>(&mut core);
assert_eq!(z1, case.outputs[0], "z1");
assert_eq!(trace_row(&core), case.keystream_rows[1], "keystream row 1");
let z2 = next_word_core::<CT>(&mut core);
assert_eq!(z2, case.outputs[1], "z2");
assert_eq!(trace_row(&core), case.keystream_rows[2], "keystream row 2");
}
fn assert_iterated_test_set_4<const CT: bool>() {
let key = [
0x0D, 0xED, 0x72, 0x63, 0x10, 0x9C, 0xF9, 0x2E, 0x33, 0x52, 0x25, 0x5A, 0x14, 0x0E,
0x0F, 0x76,
];
let iv = [
0x6B, 0x68, 0x07, 0x9A, 0x41, 0xA7, 0xC4, 0xC9, 0x1B, 0xEF, 0xD7, 0x9F, 0x7F, 0xDC,
0xC2, 0x33,
];
let mut core = init_core::<CT>(&key, &iv);
assert_eq!(next_word_core::<CT>(&mut core), 0xD712_C05C, "z1");
assert_eq!(next_word_core::<CT>(&mut core), 0xA937_C2A6, "z2");
assert_eq!(next_word_core::<CT>(&mut core), 0xEB7E_AAE3, "z3");
for _ in 0..2496 {
let _ = next_word_core::<CT>(&mut core);
}
assert_eq!(next_word_core::<CT>(&mut core), 0x9C0D_B3AA, "z2500");
}
#[test]
fn keystream_test_set_1() {
let key = [
0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81,
0xFF, 0x48,
];
let iv = [
0xEA, 0x02, 0x47, 0x14, 0xAD, 0x5C, 0x4D, 0x84, 0xDF, 0x1F, 0x9B, 0x25, 0x1C, 0x0B,
0xF4, 0x5F,
];
let mut snow = Snow3g::new(&key, &iv);
assert_eq!(snow.next_word(), 0xABEE_9704);
assert_eq!(snow.next_word(), 0x7AC3_1373);
}
#[test]
fn keystream_test_set_2() {
let key = [
0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66,
0xB1, 0xF3,
];
let iv = [
0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0xDE, 0x55, 0x19, 0x88, 0xCE, 0xB2,
0xF9, 0xB7,
];
let mut snow = Snow3g::new(&key, &iv);
assert_eq!(snow.next_word(), 0xEFF8_A342);
assert_eq!(snow.next_word(), 0xF751_480F);
}
#[test]
fn keystream_test_set_3() {
let key = [
0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71,
0x40, 0x13,
];
let iv = [
0x62, 0xA5, 0x40, 0x98, 0x1B, 0xA6, 0xF9, 0xB7, 0x45, 0x92, 0xB0, 0xE7, 0x86, 0x90,
0xF7, 0x1B,
];
let mut snow = Snow3g::new(&key, &iv);
assert_eq!(snow.next_word(), 0xA8C8_74A9);
assert_eq!(snow.next_word(), 0x7AE7_C4F8);
}
#[test]
fn keystream_test_set_1_ct() {
let key = [
0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81,
0xFF, 0x48,
];
let iv = [
0xEA, 0x02, 0x47, 0x14, 0xAD, 0x5C, 0x4D, 0x84, 0xDF, 0x1F, 0x9B, 0x25, 0x1C, 0x0B,
0xF4, 0x5F,
];
let mut snow = Snow3gCt::new(&key, &iv);
assert_eq!(snow.next_word(), 0xABEE_9704);
assert_eq!(snow.next_word(), 0x7AC3_1373);
}
#[test]
fn keystream_test_set_2_ct() {
let key = [
0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66,
0xB1, 0xF3,
];
let iv = [
0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0xDE, 0x55, 0x19, 0x88, 0xCE, 0xB2,
0xF9, 0xB7,
];
let mut snow = Snow3gCt::new(&key, &iv);
assert_eq!(snow.next_word(), 0xEFF8_A342);
assert_eq!(snow.next_word(), 0xF751_480F);
}
#[test]
fn keystream_test_set_3_ct() {
let key = [
0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71,
0x40, 0x13,
];
let iv = [
0x62, 0xA5, 0x40, 0x98, 0x1B, 0xA6, 0xF9, 0xB7, 0x45, 0x92, 0xB0, 0xE7, 0x86, 0x90,
0xF7, 0x1B,
];
let mut snow = Snow3gCt::new(&key, &iv);
assert_eq!(snow.next_word(), 0xA8C8_74A9);
assert_eq!(snow.next_word(), 0x7AE7_C4F8);
}
#[test]
fn fill_xor_roundtrip() {
let key = [0x12u8; 16];
let iv = [0x34u8; 16];
let plaintext = b"Hello, SNOW 3G!!";
let mut buf = *plaintext;
Snow3g::new(&key, &iv).fill(&mut buf);
Snow3g::new(&key, &iv).fill(&mut buf);
assert_eq!(&buf, plaintext);
}
#[test]
fn fill_partial_word() {
let key = [0xABu8; 16];
let iv = [0xCDu8; 16];
let mut buf7 = [0u8; 7];
let mut buf8 = [0u8; 8];
Snow3g::new(&key, &iv).fill(&mut buf7);
Snow3g::new(&key, &iv).fill(&mut buf8);
assert_eq!(buf7[..], buf8[..7]);
}
#[test]
fn fill_xor_roundtrip_ct() {
let key = [0x12u8; 16];
let iv = [0x34u8; 16];
let plaintext = b"Hello, SNOW 3G!!";
let mut buf = *plaintext;
Snow3gCt::new(&key, &iv).fill(&mut buf);
Snow3gCt::new(&key, &iv).fill(&mut buf);
assert_eq!(&buf, plaintext);
}
#[test]
fn fill_partial_word_ct() {
let key = [0xABu8; 16];
let iv = [0xCDu8; 16];
let mut buf7 = [0u8; 7];
let mut buf8 = [0u8; 8];
Snow3gCt::new(&key, &iv).fill(&mut buf7);
Snow3gCt::new(&key, &iv).fill(&mut buf8);
assert_eq!(buf7[..], buf8[..7]);
}
#[test]
fn ct_sboxes_match_tables() {
for x in 0u16..=255 {
let b = u8::try_from(x).expect("table index fits in u8");
assert_eq!(sbox_eval(&SR_ANF, b), SR[x as usize], "SR {x:02x}");
assert_eq!(sbox_eval(&SQ_ANF, b), SQ[x as usize], "SQ {x:02x}");
assert_eq!(
alpha_word_ct(b, MUL_ALPHA_FACTORS),
MUL_ALPHA[x as usize],
"MUL {x:02x}"
);
assert_eq!(
alpha_word_ct(b, DIV_ALPHA_FACTORS),
DIV_ALPHA[x as usize],
"DIV {x:02x}"
);
}
}
#[test]
fn snow3g_and_ct_match() {
let key = [0x12u8; 16];
let iv = [0x34u8; 16];
let mut fast = Snow3g::new(&key, &iv);
let mut slow = Snow3gCt::new(&key, &iv);
for _ in 0..4 {
assert_eq!(fast.next_word(), slow.next_word());
}
}
#[test]
fn snow3g_and_ct_match_random_streams() {
let mut seed = 0xfeed_face_cafe_beefu64;
for _ in 0..128 {
let mut key = [0u8; 16];
let mut iv = [0u8; 16];
fill_bytes(&mut seed, &mut key);
fill_bytes(&mut seed, &mut iv);
let len = (xorshift64(&mut seed) as usize % 2048) + 1;
let mut fast_buf = vec![0u8; len];
let mut ct_buf = vec![0u8; len];
fill_bytes(&mut seed, &mut fast_buf);
ct_buf.copy_from_slice(&fast_buf);
Snow3g::new(&key, &iv).fill(&mut fast_buf);
Snow3gCt::new(&key, &iv).fill(&mut ct_buf);
assert_eq!(fast_buf, ct_buf);
}
}
#[test]
fn official_test_set_1_trace_fast_and_ct() {
let key = [
0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81,
0xFF, 0x48,
];
let iv = [
0xEA, 0x02, 0x47, 0x14, 0xAD, 0x5C, 0x4D, 0x84, 0xDF, 0x1F, 0x9B, 0x25, 0x1C, 0x0B,
0xF4, 0x5F,
];
let initial_lfsr = [
0xD429_BA60,
0x7D3A_4CFF,
0x6AD3_B6EF,
0xB77E_00B7,
0x2BD6_459F,
0x82C5_B300,
0x952C_4910,
0x4881_FF48,
0xD429_BA60,
0x6131_B8A0,
0xB5CC_2DCA,
0xB77E_00B7,
0x868A_081B,
0x82C5_B300,
0x952C_4910,
0xA283_B85C,
];
let init_rows = [
TraceRow {
s0: 0xD429_BA60,
s2: 0x6AD3_B6EF,
s5: 0x82C5_B300,
s11: 0xB77E_00B7,
s15: 0xA283_B85C,
r1: 0x0000_0000,
r2: 0x0000_0000,
r3: 0x0000_0000,
},
TraceRow {
s0: 0x7D3A_4CFF,
s2: 0xB77E_00B7,
s5: 0x952C_4910,
s11: 0x868A_081B,
s15: 0x97DF_2884,
r1: 0x82C5_B300,
r2: 0x6363_6363,
r3: 0x2525_2525,
},
TraceRow {
s0: 0x6AD3_B6EF,
s2: 0x2BD6_459F,
s5: 0x4881_FF48,
s11: 0x82C5_B300,
s15: 0x311B_A301,
r1: 0x136C_CF98,
r2: 0x486C_5BC4,
r3: 0x9393_9393,
},
TraceRow {
s0: 0xB77E_00B7,
s2: 0x82C5_B300,
s5: 0xD429_BA60,
s11: 0x952C_4910,
s15: 0xA69F_CBCB,
r1: 0x237E_C89F,
r2: 0xEAEB_C424,
r3: 0x4B78_15EA,
},
TraceRow {
s0: 0x2BD6_459F,
s2: 0x952C_4910,
s5: 0x6131_B8A0,
s11: 0xA283_B85C,
s15: 0xE76F_0ADA,
r1: 0x8A3D_73AE,
r2: 0x21A4_385B,
r3: 0xE662_EC27,
},
TraceRow {
s0: 0x82C5_B300,
s2: 0x4881_FF48,
s5: 0xB5CC_2DCA,
s11: 0x97DF_2884,
s15: 0xA52D_CD12,
r1: 0xA8F7_8CE2,
r2: 0x63A7_F600,
r3: 0xBC3F_3A8D,
},
TraceRow {
s0: 0x952C_4910,
s2: 0xD429_BA60,
s5: 0xB77E_00B7,
s11: 0x311B_A301,
s15: 0x1A34_9A62,
r1: 0x6D9B_0D47,
r2: 0x2071_2A2D,
r3: 0x391D_0883,
},
TraceRow {
s0: 0x4881_FF48,
s2: 0x6131_B8A0,
s5: 0x868A_081B,
s11: 0xA69F_CBCB,
s15: 0x2A2A_44DB,
r1: 0xAED4_3261,
r2: 0x401B_1511,
r3: 0x45A6_ED60,
},
];
let final_lfsr = [
0x8F12_15A6,
0xE003_A052,
0x9241_C929,
0x68D7_BF8C,
0x16BF_4C2A,
0x8DEF_9D70,
0x3238_1704,
0x11DD_346A,
0xE18B_81EA,
0x77EB_D4FE,
0x57ED_9505,
0x0C33_C0EF,
0x1A03_7B59,
0x9759_1E82,
0xA91C_CB44,
0x7B48_E04F,
];
let final_fsm = [0x61DA_9249, 0x427D_F38C, 0x0FB6_B101];
let keystream_rows = [
TraceRow {
s0: 0xE003_A052,
s2: 0x68D7_BF8C,
s5: 0x3238_1704,
s11: 0x1A03_7B59,
s15: 0x1646_644C,
r1: 0xC4D7_1FFD,
r2: 0x90F0_B31F,
r3: 0xCC61_2008,
},
TraceRow {
s0: 0x9241_C929,
s2: 0x16BF_4C2A,
s5: 0x11DD_346A,
s11: 0x9759_1E82,
s15: 0x52E4_3190,
r1: 0x8F49_EA2B,
r2: 0x0AAC_C1E1,
r3: 0x3367_438C,
},
TraceRow {
s0: 0x68D7_BF8C,
s2: 0x8DEF_9D70,
s5: 0xE18B_81EA,
s11: 0xA91C_CB44,
s15: 0xB737_110E,
r1: 0x2D67_39C7,
r2: 0x5295_DA23,
r3: 0x5293_E49E,
},
];
let outputs = [0xABEE_9704, 0x7AC3_1373];
let case = OfficialTraceCase {
key,
iv,
initial_lfsr,
init_rows,
final_lfsr,
final_fsm,
keystream_rows,
outputs,
};
assert_official_trace::<false>(&case);
assert_official_trace::<true>(&case);
}
#[test]
fn official_test_set_2_trace_fast_and_ct() {
let key = [
0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66,
0xB1, 0xF3,
];
let iv = [
0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0xDE, 0x55, 0x19, 0x88, 0xCE, 0xB2,
0xF9, 0xB7,
];
let initial_lfsr = [
0x731C_C1D3,
0x3C3F_4A03,
0xE0C2_1759,
0x2399_4E0C,
0x8CE3_3E2C,
0xC3C0_B5FC,
0x1F3D_E8A6,
0xDC66_B1F3,
0x731C_C1D3,
0xF28D_B3B4,
0x3E97_0ED1,
0x2399_4E0C,
0xBE9C_8F30,
0xC3C0_B5FC,
0x1F3D_E8A6,
0x0FA3_6461,
];
let init_rows = [
TraceRow {
s0: 0x731C_C1D3,
s2: 0xE0C2_1759,
s5: 0xC3C0_B5FC,
s11: 0x2399_4E0C,
s15: 0x0FA3_6461,
r1: 0x0000_0000,
r2: 0x0000_0000,
r3: 0x0000_0000,
},
TraceRow {
s0: 0x3C3F_4A03,
s2: 0x2399_4E0C,
s5: 0x1F3D_E8A6,
s11: 0xBE9C_8F30,
s15: 0xEF81_E474,
r1: 0xC3C0_B5FC,
r2: 0x6363_6363,
r3: 0x2525_2525,
},
TraceRow {
s0: 0xE0C2_1759,
s2: 0x8CE3_3E2C,
s5: 0xDC66_B1F3,
s11: 0xC3C0_B5FC,
s15: 0x7A55_4815,
r1: 0x9D7C_30E6,
r2: 0xF878_FA8B,
r3: 0x9393_9393,
},
TraceRow {
s0: 0x2399_4E0C,
s2: 0xC3C0_B5FC,
s5: 0x731C_C1D3,
s11: 0x1F3D_E8A6,
s15: 0x53E0_AE66,
r1: 0x486E_1CEB,
r2: 0x2148_E845,
r3: 0x098F_198B,
},
TraceRow {
s0: 0x8CE3_3E2C,
s2: 0x1F3D_E8A6,
s5: 0xF28D_B3B4,
s11: 0x0FA3_6461,
s15: 0x9A1E_E9B8,
r1: 0x9BDC_C09D,
r2: 0x87A6_22BB,
r3: 0xEFFA_4239,
},
TraceRow {
s0: 0xC3C0_B5FC,
s2: 0xDC66_B1F3,
s5: 0x3E97_0ED1,
s11: 0xEF81_E474,
s15: 0x2390_FE04,
r1: 0xA51E_1448,
r2: 0xF6CF_B4FB,
r3: 0x2087_DC1D,
},
TraceRow {
s0: 0x1F3D_E8A6,
s2: 0x731C_C1D3,
s5: 0x2399_4E0C,
s11: 0x7A55_4815,
s15: 0x6FB8_C36C,
r1: 0x14E0_87C7,
r2: 0x7246_2DC5,
r3: 0x0B8B_F471,
},
TraceRow {
s0: 0xDC66_B1F3,
s2: 0xF28D_B3B4,
s5: 0xBE9C_8F30,
s11: 0x53E0_AE66,
s15: 0xBA5D_B98F,
r1: 0x9A58_E842,
r2: 0x481D_2AB5,
r3: 0x5C8E_E565,
},
];
let final_lfsr = [
0x04D6_A929,
0x942E_1440,
0x82AB_D3FE,
0x5832_E9F4,
0x5F97_02A0,
0x0871_2C81,
0x644C_C9B9,
0xDBF6_DE13,
0xBAA5_B1D0,
0x92E9_DD53,
0xA2E2_FA6D,
0xCE69_65AA,
0x02C0_CD4E,
0x6E6D_984F,
0x114A_90E7,
0x5279_F8DA,
];
let final_fsm = [0x6513_0120, 0xA14C_7DBD, 0xB68B_551A];
let keystream_rows = [
TraceRow {
s0: 0x942E_1440,
s2: 0x5832_E9F4,
s5: 0x644C_C9B9,
s11: 0x02C0_CD4E,
s15: 0xC1E9_3B6B,
r1: 0x6046_F758,
r2: 0x59E6_85C1,
r3: 0x7DCB_C989,
},
TraceRow {
s0: 0x82AB_D3FE,
s2: 0x5F97_02A0,
s5: 0xDBF6_DE13,
s11: 0x6E6D_984F,
s15: 0xCEB9_9926,
r1: 0x736D_85F1,
r2: 0x37DD_84E6,
r3: 0xA9BE_CBB1,
},
TraceRow {
s0: 0x5832_E9F4,
s2: 0x0871_2C81,
s5: 0xBAA5_B1D0,
s11: 0x114A_90E7,
s15: 0xE34F_6919,
r1: 0xAA25_9A88,
r2: 0x56C4_5F48,
r3: 0xC354_6A61,
},
];
let outputs = [0xEFF8_A342, 0xF751_480F];
let case = OfficialTraceCase {
key,
iv,
initial_lfsr,
init_rows,
final_lfsr,
final_fsm,
keystream_rows,
outputs,
};
assert_official_trace::<false>(&case);
assert_official_trace::<true>(&case);
}
#[test]
fn official_test_set_3_trace_fast_and_ct() {
let key = [
0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71,
0x40, 0x13,
];
let iv = [
0x62, 0xA5, 0x40, 0x98, 0x1B, 0xA6, 0xF9, 0xB7, 0x45, 0x92, 0xB0, 0xE7, 0x86, 0x90,
0xF7, 0x1B,
];
let initial_lfsr = [
0xBFCA_3997,
0xF507_392E,
0x5700_7998,
0x4E8E_BFEC,
0x4035_C668,
0x0AF8_C6D1,
0xA8FF_8667,
0xB171_4013,
0xBFCA_3997,
0x7397_CE35,
0x1292_C97F,
0x4E8E_BFEC,
0x5B93_3FDF,
0x0AF8_C6D1,
0xA8FF_8667,
0xD3D4_008B,
];
let init_rows = [
TraceRow {
s0: 0xBFCA_3997,
s2: 0x5700_7998,
s5: 0x0AF8_C6D1,
s11: 0x4E8E_BFEC,
s15: 0xD3D4_008B,
r1: 0x0000_0000,
r2: 0x0000_0000,
r3: 0x0000_0000,
},
TraceRow {
s0: 0xF507_392E,
s2: 0x4E8E_BFEC,
s5: 0xA8FF_8667,
s11: 0x5B93_3FDF,
s15: 0xEE2C_ABF5,
r1: 0x0AF8_C6D1,
r2: 0x6363_6363,
r3: 0x2525_2525,
},
TraceRow {
s0: 0x5700_7998,
s2: 0x4035_C668,
s5: 0xB171_4013,
s11: 0x0AF8_C6D1,
s15: 0x6673_56A3,
r1: 0xF13E_06A5,
r2: 0x79A1_E99D,
r3: 0x9393_9393,
},
TraceRow {
s0: 0x4E8E_BFEC,
s2: 0x0AF8_C6D1,
s5: 0xBFCA_3997,
s11: 0xA8FF_8667,
s15: 0x6410_181D,
r1: 0x9C84_BD1D,
r2: 0x8EEE_B4AE,
r3: 0xE599_5CC4,
},
TraceRow {
s0: 0x4035_C668,
s2: 0xA8FF_8667,
s5: 0x7397_CE35,
s11: 0xD3D4_008B,
s15: 0x241A_7790,
r1: 0xE942_1A01,
r2: 0x7519_6F5C,
r3: 0xC83E_1776,
},
TraceRow {
s0: 0x0AF8_C6D1,
s2: 0xB171_4013,
s5: 0x1292_C97F,
s11: 0xEE2C_ABF5,
s15: 0xC485_B826,
r1: 0x30C3_489F,
r2: 0x36A4_4937,
r3: 0x0F31_7420,
},
TraceRow {
s0: 0xA8FF_8667,
s2: 0xBFCA_3997,
s5: 0x4E8E_BFEC,
s11: 0x6673_56A3,
s15: 0xA211_C1E9,
r1: 0x5448_0696,
r2: 0x02D9_0971,
r3: 0x3D98_2023,
},
TraceRow {
s0: 0xB171_4013,
s2: 0x7397_CE35,
s5: 0x5B93_3FDF,
s11: 0x6410_181D,
s15: 0x6E8A_E7E6,
r1: 0x75EF_A940,
r2: 0xD63B_98F8,
r3: 0x883F_13A7,
},
];
let final_lfsr = [
0xFEAF_BAD8,
0x1B11_050A,
0x2370_8014,
0xAC84_94DB,
0xED97_D431,
0xDBBB_59B3,
0x6CD3_0005,
0x7EC3_6405,
0xB20F_02AC,
0xEB40_7735,
0x50E4_1A0E,
0xFFA8_ABC1,
0xEB48_00A7,
0xD4E6_749D,
0xD1C4_52FE,
0xA92A_3153,
];
let final_fsm = [0x6599_AA50, 0x5EA9_188B, 0xF418_89FC];
let keystream_rows = [
TraceRow {
s0: 0x1B11_050A,
s2: 0xAC84_94DB,
s5: 0x6CD3_0005,
s11: 0xEB48_00A7,
s15: 0x0FE9_1C6F,
r1: 0x8E4C_E8DA,
r2: 0x2DEF_74EA,
r3: 0x42B4_B0A3,
},
TraceRow {
s0: 0x2370_8014,
s2: 0xED97_D431,
s5: 0x7EC3_6405,
s11: 0xD4E6_749D,
s15: 0xC3CB_3734,
r1: 0x5C57_2590,
r2: 0x79B5_1828,
r3: 0x2496_A1E1,
},
TraceRow {
s0: 0xAC84_94DB,
s2: 0xDBBB_59B3,
s5: 0xB20F_02AC,
s11: 0xD1C4_52FE,
s15: 0x739A_B29C,
r1: 0xD40A_DE0C,
r2: 0x5037_B990,
r3: 0x32D1_FAE0,
},
];
let outputs = [0xA8C8_74A9, 0x7AE7_C4F8];
let case = OfficialTraceCase {
key,
iv,
initial_lfsr,
init_rows,
final_lfsr,
final_fsm,
keystream_rows,
outputs,
};
assert_official_trace::<false>(&case);
assert_official_trace::<true>(&case);
}
#[test]
fn official_test_set_4_iterated_fast_and_ct() {
assert_iterated_test_set_4::<false>();
assert_iterated_test_set_4::<true>();
}
}