Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for kupyna512 hash #597

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"groestl",
"jh",
"k12",
"kupyna512",
"md2",
"md4",
"md5",
Expand Down
6 changes: 6 additions & 0 deletions kupyna512/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "kupyna512"
version = "0.1.0"
edition = "2021"

[dependencies]
90 changes: 90 additions & 0 deletions kupyna512/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
mod sub_units;
#[cfg(test)]
mod tests;

const STATE_SIZE: usize = 1024;
const HASH_SIZE: usize = 512;

fn pad_message(message: &[u8], msg_len_bits: usize, state_size: usize) -> Vec<u8> {
let round_msg_len = message.len() * 8;
let d =
((-((round_msg_len + 97) as isize) % (state_size as isize)) + state_size as isize) as usize;

// Calculate the length of padding to be added
let pad_len = d / 8;

// We set the padded message size upfront to reduce allocations
let padded_len = (msg_len_bits + 7) / 8 + pad_len + 13;
let mut padded_message = vec![0x00; padded_len];

// Copy n bits from the input message
let full_bytes = msg_len_bits / 8;
let remaining_bits = msg_len_bits % 8;

padded_message[..full_bytes].copy_from_slice(&message[..full_bytes]);

if remaining_bits > 0 {
let last_byte = message[full_bytes];
padded_message[full_bytes] = last_byte & ((1 << remaining_bits) - 1);
}

// Set the n+1'th bit to high
padded_message[msg_len_bits / 8] |= 1 << (7 - (msg_len_bits % 8));

// Convert the length to a byte array and copy it into the padded message
let n_bytes = (msg_len_bits as u128).to_le_bytes(); // message length in little-endian
padded_message[padded_len - 12..].copy_from_slice(&n_bytes[0..12]);

padded_message
}

fn divide_into_blocks(padded_message: &[u8], state_size: usize) -> Vec<&[u8]> {
padded_message.chunks(state_size / 8).collect()
}

fn truncate(block: &[u8], n: usize) -> Vec<u8> {
let bytes_to_keep = n / 8;
let start_index = if block.len() > bytes_to_keep {
block.len() - bytes_to_keep
} else {
0
};
block[start_index..].to_vec()
}

pub fn hash(message: Vec<u8>, length: Option<usize>) -> Result<Vec<u8>, &'static str> {
let mut message = message;
let message_length: usize;
if let Some(len) = length {
if len > message.len() * 8 {
return Err("Message length is less than the provided length");
}

let mut trimmed_message = message[..(len / 8)].to_vec();

if len % 8 != 0 {
let extra_byte = message[len / 8];
let extra_bits = len % 8;
let mask = 0xFF << (8 - extra_bits);
trimmed_message.push(extra_byte & mask);
}

message = trimmed_message;
message_length = len;
} else {
message_length = message.len() * 8;
}

let padded_message = pad_message(&message, message_length, STATE_SIZE);

let blocks = divide_into_blocks(&padded_message, STATE_SIZE);

let mut init_vector: Vec<u8> = vec![0; STATE_SIZE / 8];
init_vector[0] = 0x80; // set the first bit of this init vector to high

let fin_vector = sub_units::plant(blocks, &init_vector);

let hash = truncate(&fin_vector, HASH_SIZE);

Ok(hash)
}
34 changes: 34 additions & 0 deletions kupyna512/src/sub_units/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
mod t_xor_plus;

use t_xor_plus::{t_plus_l, t_xor_l};

const ROUNDS: usize = 14;

fn xor_bytes(a: &[u8], b: &[u8]) -> Vec<u8> {
a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect()
}

fn silo(message_block: &[u8], prev_vector: &[u8]) -> Vec<u8> {
let m_xor_p = xor_bytes(message_block, prev_vector);

let t_xor_mp = t_xor_l(&m_xor_p, ROUNDS);

let t_plus_m = t_plus_l(message_block, ROUNDS);

xor_bytes(&(xor_bytes(&t_xor_mp, &t_plus_m)), prev_vector)
}

pub(crate) fn plant(message_blocks: Vec<&[u8]>, init_vector: &[u8]) -> Vec<u8> {
let mut last_vector = init_vector.to_vec();

for block in message_blocks {
last_vector = silo(block, &last_vector);
}

finalize(&last_vector)
}

fn finalize(ult_processed_block: &[u8]) -> Vec<u8> {
let t_xor_ult_processed_block = t_xor_l(ult_processed_block, ROUNDS);
xor_bytes(ult_processed_block, &t_xor_ult_processed_block)
}
161 changes: 161 additions & 0 deletions kupyna512/src/sub_units/t_xor_plus/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#[cfg(test)]
mod tests;

mod tables;

const ROWS: usize = 16;
const COLS: usize = 8; // For 512-bit state, adjust if needed

const BITS_IN_BYTE: u8 = 8;
const REDUCTION_POLYNOMIAL: u16 = 0x011d;

type Matrix = [[u8; COLS]; ROWS];

use tables::{MDS_MATRIX, SBOXES};

pub(crate) fn block_to_matrix(block: &[u8]) -> Matrix {
let mut matrix = [[0u8; COLS]; ROWS];
for i in 0..ROWS {
for j in 0..COLS {
matrix[i][j] = block[i * COLS + j];
}
}
matrix
}

fn matrix_to_block(matrix: Matrix) -> Vec<u8> {
let mut block = vec![0u8; ROWS * COLS];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heap allocation is not necessary here. You can use byte array instead.

for i in 0..ROWS {
for j in 0..COLS {
block[i * COLS + j] = matrix[i][j];
}
}
block
}

pub(crate) fn add_constant_xor(mut state: Matrix, round: usize) -> Matrix {
for (j, row) in state.iter_mut().enumerate().take(ROWS) {
let constant = ((j * 0x10) ^ round) as u8;
row[0] ^= constant;
}
state
}

pub(crate) fn add_constant_plus(mut state: Matrix, round: usize) -> Matrix {
let state_ptr = state.as_mut_ptr() as *mut u64;

for j in 0..ROWS {
// dbg!("{}",j);
unsafe {
*state_ptr.add(j) = state_ptr.add(j).read().wrapping_add(
0x00F0F0F0F0F0F0F3u64 ^ (((((ROWS - j - 1) * 0x10) ^ round) as u64) << 56),
);
}
}

state
}

pub(crate) fn s_box_layer(mut state: Matrix) -> Matrix {
for i in 0..COLS {
for row in state.iter_mut() {
row[i] = SBOXES[i % 4][row[i] as usize];
}
}
state
}

pub(crate) fn rotate_rows(mut state: Matrix) -> Matrix {
let mut temp = [0u8; ROWS];
let mut shift: i32 = -1;
for i in 0..COLS {
if i == COLS - 1 {
shift = 11;
} else {
shift += 1;
}
for col in 0..ROWS {
temp[(col + shift as usize) % ROWS] = state[col][i];
}
for col in 0..ROWS {
state[col][i] = temp[col];
}
}
state
}

fn multiply_gf(mut x: u8, mut y: u8) -> u8 {
let mut r = 0u8;

for _ in 0..BITS_IN_BYTE {
if y & 1 == 1 {
r ^= x;
}
let hbit = (x & 0x80) >> 7;
x <<= 1;
if hbit == 1 {
x ^= REDUCTION_POLYNOMIAL as u8;
}
y >>= 1;
}

r
}

pub(crate) fn mix_columns(state: Matrix) -> Matrix {
let mut result = [[0u8; COLS]; ROWS];

for col in 0..ROWS {
for row in (0..COLS).rev() {
let mut product = 0u8;
for b in (0..COLS).rev() {
product ^= multiply_gf(state[col][b], MDS_MATRIX[row][b]);
}
result[col][row] = product;
}
}

result
}

/// Placeholder for the T⊕l transformation.
///
/// # Arguments
///
/// * `block` - A byte slice representing the block to be transformed.
/// * `_rounds` - The number of rounds to perform.
///
/// # Returns
///
/// * A `Vec<u8>` containing the transformed block.
pub fn t_xor_l(block: &[u8], rounds: usize) -> Vec<u8> {
let mut state = block_to_matrix(block);
for nu in 0..rounds {
state = add_constant_xor(state, nu);
state = s_box_layer(state);
state = rotate_rows(state);
state = mix_columns(state);
}
matrix_to_block(state)
}

/// Placeholder for the T+l transformation.
///
/// # Arguments
///
/// * `block` - A byte slice representing the block to be transformed.
/// * `_rounds` - The number of rounds to perform.
///
/// # Returns
///
/// * A `Vec<u8>` containing the transformed block.
pub fn t_plus_l(block: &[u8], rounds: usize) -> Vec<u8> {
let mut state = block_to_matrix(block);
for nu in 0..rounds {
state = add_constant_plus(state, nu);
state = s_box_layer(state);
state = rotate_rows(state);
state = mix_columns(state);
}
matrix_to_block(state)
}
Loading
Loading