#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::{
Error,
Result,
};
pub struct SaturninBs32Core {
round_constants: Vec<u32>,
num_super_rounds: usize,
}
impl SaturninBs32Core {
pub fn new(num_super_rounds: usize, domain: u8) -> Result<Self> {
if num_super_rounds > 31 {
return Err(Error::InvalidAlgorithm {
algorithm: "Number of super-rounds must be <= 31",
});
}
if domain > 15 {
return Err(Error::InvalidAlgorithm {
algorithm: "Domain must be <= 15",
});
}
let round_constants = Self::get_round_constants(num_super_rounds, domain);
Ok(Self {
round_constants,
num_super_rounds,
})
}
fn get_round_constants(num_super_rounds: usize, domain: u8) -> Vec<u32> {
if num_super_rounds == 16 {
match domain {
7 => {
#[cfg(feature = "alloc")]
{
use alloc::vec;
vec![
0x3FBA180C, 0x563AB9AB, 0x125EA5EF, 0x859DA26C, 0xB8CF779B, 0x7D4DE793,
0x07EFB49F, 0x8D525306, 0x1E08E6AB, 0x41729F87, 0x8C4AEF0A, 0x4AA0C9A7,
0xD93A95EF, 0xBB00D2AF, 0xB62C5BF0, 0x386D94D8,
]
}
#[cfg(not(feature = "alloc"))]
{
Self::generate_round_constants_lfsr(num_super_rounds, domain)
}
}
8 => {
#[cfg(feature = "alloc")]
{
use alloc::vec;
vec![
0x3C9B19A7, 0xA9098694, 0x23F878DA, 0xA7B647D3, 0x74FC9D78, 0xEACAAE11,
0x2F31A677, 0x4CC8C054, 0x2F51CA05, 0x5268F195, 0x4F5B8A2B, 0xF614B4AC,
0xF1D95401, 0x764D2568, 0x6A493611, 0x8EEF9C3E,
]
}
#[cfg(not(feature = "alloc"))]
{
Self::generate_round_constants_lfsr(num_super_rounds, domain)
}
}
_ => Self::generate_round_constants_lfsr(num_super_rounds, domain),
}
} else {
Self::generate_round_constants_lfsr(num_super_rounds, domain)
}
}
fn generate_round_constants_lfsr(num_super_rounds: usize, domain: u8) -> Vec<u32> {
let mut constants = Vec::with_capacity(num_super_rounds);
let mut x0 = (domain as u32)
.wrapping_add((num_super_rounds as u32) << 4)
.wrapping_add(0xFE00);
let mut x1 = x0;
for _ in 0..num_super_rounds {
for _ in 0..16 {
x0 = (x0 << 1) ^ (0x2D & (!(x0 >> 15).wrapping_add(1)));
x1 = (x1 << 1) ^ (0x53 & (!(x1 >> 15).wrapping_add(1)));
}
constants.push((x1 << 16) | x0);
}
constants
}
pub fn encrypt_block(&self, key: &[u8], block: &mut [u8]) -> Result<()> {
if key.len() != 32 {
return Err(Error::InvalidKeySize {
expected: 32,
actual: key.len(),
});
}
if block.len() != 32 {
return Err(Error::InvalidMessageSize {
max: 32,
actual: block.len(),
});
}
let mut keybuf = [0u32; 16];
for i in 0..8 {
let w = (key[i << 1] as u32) |
((key[(i << 1) + 1] as u32) << 8) |
((key[(i << 1) + 16] as u32) << 16) |
((key[(i << 1) + 17] as u32) << 24);
keybuf[i] = w;
keybuf[i + 8] = ((w & 0x001F001F) << 11) | ((w >> 5) & 0x07FF07FF);
}
let mut q = [0u32; 8];
self.decode_block(block, &mut q);
for i in 0..8 {
q[i] ^= keybuf[i];
}
for i in (0..self.num_super_rounds).step_by(2) {
self.apply_sbox(&mut q);
self.apply_mds(&mut q);
self.apply_sbox(&mut q);
self.apply_shift_rows_slice(&mut q);
self.apply_mds(&mut q);
self.apply_shift_rows_slice_inv(&mut q);
q[0] ^= self.round_constants[i];
for j in 0..8 {
q[j] ^= keybuf[j + 8];
}
if i + 1 < self.num_super_rounds {
self.apply_sbox(&mut q);
self.apply_mds(&mut q);
self.apply_sbox(&mut q);
self.apply_shift_rows_sheet(&mut q);
self.apply_mds(&mut q);
self.apply_shift_rows_sheet_inv(&mut q);
q[0] ^= self.round_constants[i + 1];
for j in 0..8 {
q[j] ^= keybuf[j];
}
}
}
self.encode_block(&q, block);
Ok(())
}
pub fn decrypt_block(&self, key: &[u8], block: &mut [u8]) -> Result<()> {
if key.len() != 32 {
return Err(Error::InvalidKeySize {
expected: 32,
actual: key.len(),
});
}
if block.len() != 32 {
return Err(Error::InvalidMessageSize {
max: 32,
actual: block.len(),
});
}
let mut keybuf = [0u32; 16];
for i in 0..8 {
let w = (key[i << 1] as u32) |
((key[(i << 1) + 1] as u32) << 8) |
((key[(i << 1) + 16] as u32) << 16) |
((key[(i << 1) + 17] as u32) << 24);
keybuf[i] = w;
keybuf[i + 8] = ((w & 0x001F001F) << 11) | ((w >> 5) & 0x07FF07FF);
}
let mut q = [0u32; 8];
self.decode_block(block, &mut q);
for i in 0..8 {
q[i] ^= keybuf[i];
}
for i in (0..self.num_super_rounds).rev().step_by(2) {
if i + 1 < self.num_super_rounds {
for j in 0..8 {
q[j] ^= keybuf[j + 8];
}
q[0] ^= self.round_constants[i + 1];
q[4] ^= self.round_constants[i + 1];
self.apply_shift_rows_sheet_inv(&mut q);
self.apply_mds_inv(&mut q);
self.apply_shift_rows_sheet_inv(&mut q);
self.apply_sbox_inv(&mut q);
}
if i < self.num_super_rounds {
for j in 0..8 {
q[j] ^= keybuf[j];
}
q[0] ^= self.round_constants[i];
q[4] ^= self.round_constants[i];
self.apply_shift_rows_slice_inv(&mut q);
self.apply_mds_inv(&mut q);
self.apply_shift_rows_slice_inv(&mut q);
self.apply_sbox_inv(&mut q);
}
}
for i in 0..8 {
q[i] ^= keybuf[i];
}
self.encode_block(&q, block);
Ok(())
}
fn decode_block(&self, src: &[u8], q: &mut [u32; 8]) {
q[0] = (src[0] as u32) |
((src[1] as u32) << 8) |
((src[16] as u32) << 16) |
((src[17] as u32) << 24);
q[1] = (src[2] as u32) |
((src[3] as u32) << 8) |
((src[18] as u32) << 16) |
((src[19] as u32) << 24);
q[2] = (src[4] as u32) |
((src[5] as u32) << 8) |
((src[20] as u32) << 16) |
((src[21] as u32) << 24);
q[3] = (src[6] as u32) |
((src[7] as u32) << 8) |
((src[22] as u32) << 16) |
((src[23] as u32) << 24);
q[4] = (src[8] as u32) |
((src[9] as u32) << 8) |
((src[24] as u32) << 16) |
((src[25] as u32) << 24);
q[5] = (src[10] as u32) |
((src[11] as u32) << 8) |
((src[26] as u32) << 16) |
((src[27] as u32) << 24);
q[6] = (src[12] as u32) |
((src[13] as u32) << 8) |
((src[28] as u32) << 16) |
((src[29] as u32) << 24);
q[7] = (src[14] as u32) |
((src[15] as u32) << 8) |
((src[30] as u32) << 16) |
((src[31] as u32) << 24);
}
fn encode_block(&self, q: &[u32; 8], dst: &mut [u8]) {
dst[0] = q[0] as u8;
dst[1] = (q[0] >> 8) as u8;
dst[16] = (q[0] >> 16) as u8;
dst[17] = (q[0] >> 24) as u8;
dst[2] = q[1] as u8;
dst[3] = (q[1] >> 8) as u8;
dst[18] = (q[1] >> 16) as u8;
dst[19] = (q[1] >> 24) as u8;
dst[4] = q[2] as u8;
dst[5] = (q[2] >> 8) as u8;
dst[20] = (q[2] >> 16) as u8;
dst[21] = (q[2] >> 24) as u8;
dst[6] = q[3] as u8;
dst[7] = (q[3] >> 8) as u8;
dst[22] = (q[3] >> 16) as u8;
dst[23] = (q[3] >> 24) as u8;
dst[8] = q[4] as u8;
dst[9] = (q[4] >> 8) as u8;
dst[24] = (q[4] >> 16) as u8;
dst[25] = (q[4] >> 24) as u8;
dst[10] = q[5] as u8;
dst[11] = (q[5] >> 8) as u8;
dst[26] = (q[5] >> 16) as u8;
dst[27] = (q[5] >> 24) as u8;
dst[12] = q[6] as u8;
dst[13] = (q[6] >> 8) as u8;
dst[28] = (q[6] >> 16) as u8;
dst[29] = (q[6] >> 24) as u8;
dst[14] = q[7] as u8;
dst[15] = (q[7] >> 8) as u8;
dst[30] = (q[7] >> 16) as u8;
dst[31] = (q[7] >> 24) as u8;
}
fn apply_sbox(&self, q: &mut [u32; 8]) {
let mut a = q[0];
let mut b = q[1];
let mut c = q[2];
let mut d = q[3];
a ^= b & c;
b ^= a | d;
d ^= b | c;
c ^= b & d;
b ^= a | c;
a ^= b | d;
q[0] = b;
q[1] = c;
q[2] = d;
q[3] = a;
a = q[4];
b = q[5];
c = q[6];
d = q[7];
a ^= b & c;
b ^= a | d;
d ^= b | c;
c ^= b & d;
b ^= a | c;
a ^= b | d;
q[4] = d;
q[5] = b;
q[6] = a;
q[7] = c;
}
fn apply_mds(&self, q: &mut [u32; 8]) {
q[0] ^= q[4];
q[1] ^= q[5];
q[2] ^= q[6];
q[3] ^= q[7];
self.mul_column_4_7(q);
q[4] ^= self.swap_words(q[0]);
q[5] ^= self.swap_words(q[1]);
q[6] ^= self.swap_words(q[2]);
q[7] ^= self.swap_words(q[3]);
self.mul_column_0_3(q);
self.mul_column_0_3(q);
q[0] ^= q[4];
q[1] ^= q[5];
q[2] ^= q[6];
q[3] ^= q[7];
q[4] ^= self.swap_words(q[0]);
q[5] ^= self.swap_words(q[1]);
q[6] ^= self.swap_words(q[2]);
q[7] ^= self.swap_words(q[3]);
}
fn mul_column_4_7(&self, q: &mut [u32; 8]) {
let tmp = q[4];
q[4] = q[5];
q[5] = q[6];
q[6] = q[7];
q[7] = tmp ^ q[4];
}
fn mul_column_0_3(&self, q: &mut [u32; 8]) {
let tmp = q[0];
q[0] = q[1];
q[1] = q[2];
q[2] = q[3];
q[3] = tmp ^ q[0];
}
fn swap_words(&self, x: u32) -> u32 {
x.rotate_left(16)
}
fn apply_shift_rows_slice(&self, q: &mut [u32; 8]) {
q[0] = (q[0] & 0xFFFF) | ((q[0] & 0x33330000) << 2) | ((q[0] >> 2) & 0x33330000);
q[1] = (q[1] & 0xFFFF) | ((q[1] & 0x33330000) << 2) | ((q[1] >> 2) & 0x33330000);
q[2] = (q[2] & 0xFFFF) | ((q[2] & 0x33330000) << 2) | ((q[2] >> 2) & 0x33330000);
q[3] = (q[3] & 0xFFFF) | ((q[3] & 0x33330000) << 2) | ((q[3] >> 2) & 0x33330000);
q[4] = ((q[4] & 0x00007777) << 1) |
((q[4] >> 3) & 0x00001111) |
((q[4] & 0x11110000) << 3) |
((q[4] >> 1) & 0x77770000);
q[5] = ((q[5] & 0x00007777) << 1) |
((q[5] >> 3) & 0x00001111) |
((q[5] & 0x11110000) << 3) |
((q[5] >> 1) & 0x77770000);
q[6] = ((q[6] & 0x00007777) << 1) |
((q[6] >> 3) & 0x00001111) |
((q[6] & 0x11110000) << 3) |
((q[6] >> 1) & 0x77770000);
q[7] = ((q[7] & 0x00007777) << 1) |
((q[7] >> 3) & 0x00001111) |
((q[7] & 0x11110000) << 3) |
((q[7] >> 1) & 0x77770000);
}
fn apply_shift_rows_slice_inv(&self, q: &mut [u32; 8]) {
q[0] = (q[0] & 0xFFFF) | ((q[0] & 0x33330000) << 2) | ((q[0] >> 2) & 0x33330000);
q[1] = (q[1] & 0xFFFF) | ((q[1] & 0x33330000) << 2) | ((q[1] >> 2) & 0x33330000);
q[2] = (q[2] & 0xFFFF) | ((q[2] & 0x33330000) << 2) | ((q[2] >> 2) & 0x33330000);
q[3] = (q[3] & 0xFFFF) | ((q[3] & 0x33330000) << 2) | ((q[3] >> 2) & 0x33330000);
q[4] = ((q[4] & 0x00001111) << 3) |
((q[4] >> 1) & 0x00007777) |
((q[4] & 0x77770000) << 1) |
((q[4] >> 3) & 0x11110000);
q[5] = ((q[5] & 0x00001111) << 3) |
((q[5] >> 1) & 0x00007777) |
((q[5] & 0x77770000) << 1) |
((q[5] >> 3) & 0x11110000);
q[6] = ((q[6] & 0x00001111) << 3) |
((q[6] >> 1) & 0x00007777) |
((q[6] & 0x77770000) << 1) |
((q[6] >> 3) & 0x11110000);
q[7] = ((q[7] & 0x00001111) << 3) |
((q[7] >> 1) & 0x00007777) |
((q[7] & 0x77770000) << 1) |
((q[7] >> 3) & 0x11110000);
}
fn apply_shift_rows_sheet(&self, q: &mut [u32; 8]) {
q[0] = (q[0] & 0xFFFF) | ((q[0] & 0x00FF0000) << 8) | ((q[0] >> 8) & 0x00FF0000);
q[1] = (q[1] & 0xFFFF) | ((q[1] & 0x00FF0000) << 8) | ((q[1] >> 8) & 0x00FF0000);
q[2] = (q[2] & 0xFFFF) | ((q[2] & 0x00FF0000) << 8) | ((q[2] >> 8) & 0x00FF0000);
q[3] = (q[3] & 0xFFFF) | ((q[3] & 0x00FF0000) << 8) | ((q[3] >> 8) & 0x00FF0000);
q[4] = ((q[4] & 0x00000FFF) << 4) |
((q[4] >> 12) & 0x0000000F) |
((q[4] & 0x000F0000) << 12) |
((q[4] >> 4) & 0x0FFF0000);
q[5] = ((q[5] & 0x00000FFF) << 4) |
((q[5] >> 12) & 0x0000000F) |
((q[5] & 0x000F0000) << 12) |
((q[5] >> 4) & 0x0FFF0000);
q[6] = ((q[6] & 0x00000FFF) << 4) |
((q[6] >> 12) & 0x0000000F) |
((q[6] & 0x000F0000) << 12) |
((q[6] >> 4) & 0x0FFF0000);
q[7] = ((q[7] & 0x00000FFF) << 4) |
((q[7] >> 12) & 0x0000000F) |
((q[7] & 0x000F0000) << 12) |
((q[7] >> 4) & 0x0FFF0000);
}
fn apply_shift_rows_sheet_inv(&self, q: &mut [u32; 8]) {
q[0] = (q[0] & 0xFFFF) | ((q[0] & 0x00FF0000) << 8) | ((q[0] >> 8) & 0x00FF0000);
q[1] = (q[1] & 0xFFFF) | ((q[1] & 0x00FF0000) << 8) | ((q[1] >> 8) & 0x00FF0000);
q[2] = (q[2] & 0xFFFF) | ((q[2] & 0x00FF0000) << 8) | ((q[2] >> 8) & 0x00FF0000);
q[3] = (q[3] & 0xFFFF) | ((q[3] & 0x00FF0000) << 8) | ((q[3] >> 8) & 0x00FF0000);
q[4] = ((q[4] & 0x0000000F) << 12) |
((q[4] >> 4) & 0x00000FFF) |
((q[4] & 0x0FFF0000) << 4) |
((q[4] >> 12) & 0x000F0000);
q[5] = ((q[5] & 0x0000000F) << 12) |
((q[5] >> 4) & 0x00000FFF) |
((q[5] & 0x0FFF0000) << 4) |
((q[5] >> 12) & 0x000F0000);
q[6] = ((q[6] & 0x0000000F) << 12) |
((q[6] >> 4) & 0x00000FFF) |
((q[6] & 0x0FFF0000) << 4) |
((q[6] >> 12) & 0x000F0000);
q[7] = ((q[7] & 0x0000000F) << 12) |
((q[7] >> 4) & 0x00000FFF) |
((q[7] & 0x0FFF0000) << 4) |
((q[7] >> 12) & 0x000F0000);
}
fn apply_sbox_inv(&self, q: &mut [u32; 8]) {
self.apply_sbox(q);
}
fn apply_mds_inv(&self, q: &mut [u32; 8]) {
self.apply_mds(q);
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use std::eprintln;
use super::*;
#[test]
fn test_bs32_core_creation() {
let core = SaturninBs32Core::new(16, 8).unwrap();
assert_eq!(core.num_super_rounds, 16);
assert_eq!(core.round_constants.len(), 16);
}
#[test]
fn test_bs32_round_trip() {
let core = SaturninBs32Core::new(16, 8).unwrap();
let key = [0u8; 32];
let mut block = [0u8; 32];
let original = block;
core.encrypt_block(&key, &mut block).unwrap();
assert_ne!(block, original);
}
#[test]
fn test_bs32_hash_case() {
let core = SaturninBs32Core::new(16, 8).unwrap();
let mut block = [
0x80u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let key = [0u8; 32];
#[cfg(feature = "std")]
eprintln!("Input block: {:02X?}", &block[0..8]);
core.encrypt_block(&key, &mut block).unwrap();
#[cfg(feature = "std")]
{
eprintln!("Encrypted: {:02X?}", &block[0..8]);
eprintln!("Expected from KAT: [83, B1, 56, 41, B0, 95, 69, B0]");
}
}
#[test]
fn test_bs32_round_constants() {
let core = SaturninBs32Core::new(16, 8).unwrap();
let expected = [
0x3C9B19A7, 0xA9098694, 0x23F878DA, 0xA7B647D3, 0x74FC9D78, 0xEACAAE11, 0x2F31A677,
0x4CC8C054, 0x2F51CA05, 0x5268F195, 0x4F5B8A2B, 0xF614B4AC, 0xF1D95401, 0x764D2568,
0x6A493611, 0x8EEF9C3E,
];
#[cfg(feature = "std")]
{
eprintln!("Round constants for domain 8:");
for (i, &constant) in core.round_constants.iter().enumerate() {
eprintln!(" RC[{}] = 0x{:08X}", i, constant);
}
eprintln!("\nExpected round constants:");
for (i, &constant) in expected.iter().enumerate() {
eprintln!(" RC[{}] = 0x{:08X}", i, constant);
}
}
assert_eq!(core.round_constants, expected);
}
#[test]
fn test_bs32_data_encoding() {
let core = SaturninBs32Core::new(16, 8).unwrap();
let original = [
0x80u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let mut q = [0u32; 8];
core.decode_block(&original, &mut q);
let mut decoded = [0u8; 32];
core.encode_block(&q, &mut decoded);
#[cfg(feature = "std")]
{
eprintln!("Original: {:02X?}", &original[0..8]);
eprintln!("Decoded: {:02X?}", &decoded[0..8]);
eprintln!("q[0] = 0x{:08X}", q[0]);
eprintln!("q[1] = 0x{:08X}", q[1]);
}
assert_eq!(original, decoded);
}
}