From f00dc3e63708a6591da1dfc5ff595eeeebd2d0ce Mon Sep 17 00:00:00 2001 From: Guilleag01 Date: Wed, 24 Sep 2025 22:19:33 +0200 Subject: [PATCH] Very simple implementation of sha3_256 (doesn't work for very long inputs) --- .gitignore | 1 + Cargo.lock | 7 +++ Cargo.toml | 4 ++ src/lib.rs | 37 ++++++++++++ src/main.rs | 26 ++++++++ src/sha3.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 246 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/sha3.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c78e40a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "sha3" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b5ae530 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "sha3" +version = "0.1.0" +edition = "2024" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1199bac --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,37 @@ +pub mod sha3; + +#[cfg(test)] +mod tests { + // use super::*; + + use crate::sha3::Sha3_256; + + #[test] + fn it_works() { + let mut sha = Sha3_256::default(); + + let text = ""; + + let arr = text.as_bytes(); + + let mut data = [0_u8; 136]; + data.clone_from_slice(arr); + + // sha.absorb([0, 1, 2, 3, 4, 5, 6, 7]); + sha.absorb(&data); + let res = sha.squeeze(); + // a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a + let expected_res: [u8; 32] = [ + 0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61, + 0xd6, 0x62, 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b, + 0x80, 0xf8, 0x43, 0x4a, + ]; + + for i in 0..32 { + print!("{:#001x}", res[i]); + } + println!(); + + assert!(res == expected_res); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1764ff6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,26 @@ +use sha3::sha3::Sha3_256; + +fn main() { + let mut sha = Sha3_256::default(); + + // println!("{:?}", (0x01 as u64).to_ne_bytes()); + + let text = "hola"; + + sha.absorb(text.as_bytes()); + let res = sha.squeeze(); + + let expected_res: [u8; 32] = [ + 0x8a, 0xf1, 0x3d, 0x92, 0x44, 0x61, 0x8e, 0xee, 0x87, 0x6d, 0x04, 0x31, 0xf3, 0x44, 0x9a, + 0xa4, 0xff, 0x95, 0x27, 0x4c, 0xa3, 0xe7, 0xe5, 0xc6, 0x54, 0x19, 0x79, 0x49, 0x9f, 0x5b, + 0x85, 0xde, + ]; + + for i in 0..32 { + print!("{:#001x} ", res[i]); + } + println!(); + + assert!(res == expected_res); + println!("FUNCIONAAAAA"); +} diff --git a/src/sha3.rs b/src/sha3.rs new file mode 100644 index 0000000..df0c91f --- /dev/null +++ b/src/sha3.rs @@ -0,0 +1,171 @@ +// Rate: 1088 +// Capacity: 512 + +// use std::arch::x86_64::_mm256_xor_epi64; + +use std::array; + +const RATE_256: usize = 136; +const TOTAL_STATE_SIZE: usize = 200; +const ROUNDS: usize = 24; +const DELIMITER_SUFFIX: u8 = 0x06; // delimiter suffix for sha3 + +#[derive(Debug)] +pub struct Sha3_256 { + state: [u8; TOTAL_STATE_SIZE], +} + +impl Default for Sha3_256 { + fn default() -> Self { + Self { + state: [0; TOTAL_STATE_SIZE], + } + } +} + +impl Sha3_256 { + pub fn absorb(&mut self, input: &[u8]) { + // Xor input with rate + let mut remaining = input.len(); + let mut off = 0; + let mut in_len = 0; + while remaining > 0 { + in_len = remaining.min(RATE_256); + println!("{}", in_len); + for i in 0..in_len { + self.state[i] ^= input[i + off]; + } + off += in_len - 1; + remaining -= in_len; + + if in_len == RATE_256 { + keccak_permute(&mut self.state); + in_len = 0; + } + } + + self.state[in_len] ^= DELIMITER_SUFFIX; + + if (DELIMITER_SUFFIX & 0x80) != 0 && in_len == RATE_256 - 1 { + keccak_permute(&mut self.state); + } + + self.state[RATE_256 - 1] ^= 0x80; + } + + pub fn squeeze(&mut self) -> [u8; S] { + keccak_permute(&mut self.state); + let mut res = [0_u8; S]; + let mut out_len; + let mut remaining = S; + let mut off = 0; + while remaining > 0 { + out_len = remaining.min(RATE_256); + res[off..off + out_len].copy_from_slice(&self.state[0..out_len]); + off += out_len; + remaining -= out_len; + + if out_len > 0 { + keccak_permute(&mut self.state); + } + } + // *self.state.first_chunk().unwrap() + res + } +} + +fn keccak_permute(input: &mut [u8; TOTAL_STATE_SIZE]) { + let (lanes, _) = input.as_chunks_mut::<8>(); + let mut lfsr_state = 0x01_u8; + for _ in 0..ROUNDS { + // θ step + let c: [u64; 5] = array::from_fn(|x| { + get_lane(lanes, x, 0) + ^ get_lane(lanes, x, 1) + ^ get_lane(lanes, x, 2) + ^ get_lane(lanes, x, 3) + ^ get_lane(lanes, x, 4) + }); + + let mut d: u64; + for x in 0..5 { + d = c[(x + 4) % 5] ^ rol64(c[(x + 1) % 5], 1); + for y in 0..5 { + xor_lane(d, lanes, x, y); + } + } + + // ρ and π steps + let (mut x, mut y) = (1, 0); + let mut current = get_lane(lanes, x, y); + let mut temp: u64; + + for t in 0..24 { + let r = ((t + 1) * (t + 2) / 2) % 64; + let y2 = (2 * x + 3 * y) % 5; + x = y; + y = y2; + + temp = get_lane(lanes, x, y); + set_lane(rol64(current, r), x, y, lanes); + current = temp; + } + + // χ step + let mut temp2 = [0_u64; 5]; + for y in 0..5 { + for x in 0..5 { + temp2[x] = get_lane(lanes, x, y); + } + for x in 0..5 { + set_lane( + temp2[x] ^ ((!temp2[(x + 1) % 5]) & temp2[(x + 2) % 5]), + x, + y, + lanes, + ); + } + } + + // ι step + + for j in 0..7 { + let bit_pos: usize = (1 << j) - 1; + if lfsr86540(&mut lfsr_state) { + xor_lane((1 as u64) << bit_pos, lanes, 0, 0); + } + } + } +} + +#[inline] +fn get_lane(lanes: &[[u8; 8]], x: usize, y: usize) -> u64 { + u64::from_ne_bytes(lanes[x + 5 * y]) +} + +#[inline] +fn set_lane(lane: u64, x: usize, y: usize, lanes: &mut [[u8; 8]]) { + lanes[x + 5 * y] = lane.to_ne_bytes(); +} + +#[inline] +fn rol64(v: u64, off: usize) -> u64 { + ((v) << off) ^ ((v) >> (64 - off)) +} + +#[inline] +fn xor_lane(lane: u64, lanes: &mut [[u8; 8]], x: usize, y: usize) { + set_lane(get_lane(lanes, x, y) ^ lane, x, y, lanes); +} + +// Function that computes the linear feedback shift register (LFSR) +// I have absolutely no idea wtf is this shit. Copied from a github repo lol. +fn lfsr86540(lfsr: &mut u8) -> bool { + let res = (*lfsr & 0x01) != 0; + if (*lfsr & 0x80) != 0 { + *lfsr = (*lfsr << 1) ^ 0x71; + } else { + *lfsr <<= 1; + } + res +}