Skip to content

Commit b7d7580

Browse files
committed
(wip) add sha256
1 parent c2a0a3f commit b7d7580

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

cipher/src/algorithm/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub mod poly1305;
77
/// Module for the Blake3 message authentication code (MAC) algorithm.
88
pub mod blake3;
99

10+
pub mod sha256;
11+
1012
/// Re-exporting `ChaCha20` for direct use.
1113
pub use chacha20::ChaCha20;
1214

@@ -16,6 +18,9 @@ pub use poly1305::{Poly1305, SignedEnvelope};
1618
/// Re-exporting `Blake3Mac` for direct use.
1719
pub use blake3::Blake3Mac;
1820

21+
/// Re-exporting `Sha256` for direct use.
22+
pub use sha256::Sha256;
23+
1924
pub trait AlgorithmKeyIVInit {
2025
/// Initializes the algorithm with a key and an initialization vector (IV).
2126
///
@@ -67,6 +72,27 @@ pub trait AlgorithmProcessInPlace {
6772
fn process_in_place(&self, data: &mut [u8]);
6873
}
6974

75+
pub trait Hasher {
76+
/// Updates the hash with the provided data.
77+
///
78+
/// This method processes the input data and updates the internal state of the hash.
79+
/// It is typically used to feed data into the hash incrementally.
80+
///
81+
/// # Arguments
82+
/// * `data` - A byte slice of data to be hashed.
83+
fn update(&mut self, data: &[u8]);
84+
85+
/// Finalizes the hash computation and returns the resulting hash value.
86+
///
87+
/// This method completes the hash computation and returns the final hash value.
88+
/// It is typically called after all data has been fed into the hash.
89+
///
90+
/// # Returns
91+
///
92+
/// A byte vector representing the final hash value.
93+
fn finalize(&self) -> Vec<u8>;
94+
}
95+
7096
pub trait AlgorithmCore: AlgorithmKeyIVInit + AlgorithmProcess + AlgorithmProcessInPlace {}
7197

7298
pub trait EncryptionAlgorithm:
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
pub const STATE_WORDS: usize = 32;
2+
3+
pub const HASH_WORDS: usize = 8;
4+
5+
pub const SCHEDULE_WORDS: usize = 64;
6+
7+
pub const INITIAL_HASH: Sha256Hash = [
8+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
9+
];
10+
11+
pub const K: [u32; 64] = [
12+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
13+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
14+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
15+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
16+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
17+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
18+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
19+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
20+
];
21+
22+
pub type Sha256Hash = [u32; HASH_WORDS];
23+
24+
pub type MessageSchedule = [u32; SCHEDULE_WORDS];
25+
26+
pub type WorkingVariables = [u32; 8];
27+
28+
pub fn init_working_variables(hash: &Sha256Hash) -> WorkingVariables {
29+
[
30+
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
31+
]
32+
}
33+
34+
pub fn prepare_schedule(block: &[u32; 16]) -> MessageSchedule {
35+
let mut schedule = [0u32; SCHEDULE_WORDS];
36+
37+
// The first 16 words of the message schedule are the block itself.
38+
for i in 0..16 {
39+
schedule[i] = block[i];
40+
}
41+
// The remaining words are generated using the sigma functions.
42+
for i in 16..SCHEDULE_WORDS {
43+
schedule[i] = schedule[i - 16]
44+
.wrapping_add(sig0(schedule[i - 15]))
45+
.wrapping_add(schedule[i - 7])
46+
.wrapping_add(sig1(schedule[i - 2]));
47+
}
48+
49+
schedule
50+
}
51+
52+
pub fn compress_schedule(schedule: &MessageSchedule, v: &mut WorkingVariables) {
53+
for i in 0..SCHEDULE_WORDS {
54+
let t1 = v[7]
55+
.wrapping_add(big_sig1(v[4]))
56+
.wrapping_add(schedule[i])
57+
.wrapping_add(K[i]);
58+
59+
let t2 = big_sig0(v[0]).wrapping_add(v[0] & v[1] ^ v[0] & v[2] ^ v[1] & v[2]);
60+
61+
v[7] = v[6];
62+
v[6] = v[5];
63+
v[5] = v[4];
64+
v[4] = v[3].wrapping_add(t1);
65+
v[3] = v[2];
66+
v[2] = v[1];
67+
v[1] = v[0];
68+
v[0] = t1.wrapping_add(t2);
69+
}
70+
}
71+
72+
pub fn update_hash(hash: &mut Sha256Hash, v: &WorkingVariables) {
73+
for (h, w) in hash.iter_mut().zip(v.iter()) {
74+
*h = h.wrapping_add(*w);
75+
}
76+
}
77+
78+
pub fn sig0(x: u32) -> u32 {
79+
(x >> 7) ^ (x >> 18) ^ (x >> 3)
80+
}
81+
82+
pub fn sig1(x: u32) -> u32 {
83+
(x >> 17) ^ (x >> 19) ^ (x >> 10)
84+
}
85+
86+
pub fn big_sig0(x: u32) -> u32 {
87+
(x >> 2) ^ (x >> 13) ^ (x >> 22)
88+
}
89+
90+
pub fn big_sig1(x: u32) -> u32 {
91+
(x >> 6) ^ (x >> 11) ^ (x >> 25)
92+
}
93+
94+
pub fn pad(data: &[u8]) -> Vec<u8> {
95+
let mut padded = data.to_vec();
96+
let bit_length = (data.len() as u64) * 8;
97+
98+
// Append a single '1' bit (0x80 in hex)
99+
padded.push(0x80);
100+
101+
// Pad with '0' bits until the length is 56 bytes
102+
while padded.len() % 64 != 56 {
103+
padded.push(0x00);
104+
}
105+
106+
// Append the length of the original message as a 64-bit big-endian integer
107+
padded.extend_from_slice(&bit_length.to_be_bytes());
108+
109+
padded
110+
}

cipher/src/algorithm/sha256/mod.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
pub mod core;
2+
3+
use core::{
4+
compress_schedule, init_working_variables, pad, prepare_schedule, update_hash, Sha256Hash,
5+
INITIAL_HASH,
6+
};
7+
8+
use super::Hasher;
9+
10+
pub struct Sha256 {
11+
hash: Sha256Hash,
12+
}
13+
14+
impl Default for Sha256 {
15+
fn default() -> Self {
16+
Self { hash: INITIAL_HASH }
17+
}
18+
}
19+
20+
impl Sha256 {
21+
/// Resets the hash to its initial state.
22+
pub fn reset(&mut self) {
23+
self.hash = INITIAL_HASH;
24+
}
25+
26+
pub fn process_block(&mut self, block: &[u32; 16]) {
27+
let schedule = prepare_schedule(block);
28+
let mut v = init_working_variables(&self.hash);
29+
30+
// Perform the main hash computation loop.
31+
compress_schedule(&schedule, &mut v);
32+
33+
// Update the hash with the computed values.
34+
update_hash(&mut self.hash, &v);
35+
}
36+
}
37+
38+
impl Hasher for Sha256 {
39+
fn update(&mut self, data: &[u8]) {
40+
pad(data).chunks(64).for_each(|chunk| {
41+
let mut block = [0u32; 16];
42+
for (i, word) in chunk.chunks(4).enumerate() {
43+
let mut bytes = [0u8; 4];
44+
for (j, &byte) in word.iter().enumerate() {
45+
bytes[j] = byte;
46+
}
47+
block[i] = u32::from_be_bytes(bytes);
48+
}
49+
self.process_block(&block);
50+
});
51+
}
52+
53+
fn finalize(&self) -> Vec<u8> {
54+
let mut result = Vec::with_capacity(32);
55+
for &word in &self.hash {
56+
result.extend_from_slice(&word.to_be_bytes());
57+
}
58+
result
59+
}
60+
}
61+
62+
#[cfg(test)]
63+
mod test {
64+
use super::*;
65+
66+
#[test]
67+
fn it_should_process_sha256() {
68+
let mut hasher = Sha256::default();
69+
let data = b"abc";
70+
71+
hasher.update(data);
72+
73+
assert_eq!(
74+
hasher.finalize(),
75+
[
76+
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22,
77+
0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00,
78+
0x15, 0xad
79+
]
80+
);
81+
}
82+
}

0 commit comments

Comments
 (0)