osom_lib_hashes/sha2/sha2_256/
portable.rs1#![allow(clippy::many_single_char_names)]
4
5use osom_lib_arrays::const_helpers::{from_be_const_u32, subslice_mut_const};
6use osom_lib_arrays::fixed_array::ConstBuffer;
7use osom_lib_reprc::macros::reprc;
8
9use super::sha2_256_shared::{INITIAL_STATE, K, calculate_final_blocks};
10use crate::traits::HashFunction;
11
12const CHUNK_SIZE_U32: usize = 16;
13
14#[reprc]
24#[must_use]
25pub struct SHA2_256_Portable {
26 pub(super) total_length: u64,
29
30 pub(super) state: [u32; 8],
32
33 pub(super) bufferer: ConstBuffer<64, u8>,
35}
36
37#[inline(always)]
38const fn ch(x: u32, y: u32, z: u32) -> u32 {
39 (x & y) ^ (!x & z)
40}
41
42#[inline(always)]
43const fn maj(x: u32, y: u32, z: u32) -> u32 {
44 (x & y) ^ (x & z) ^ (y & z)
45}
46
47#[inline(always)]
48const fn bsig0(x: u32) -> u32 {
49 x.rotate_right(2) ^ x.rotate_right(13) ^ x.rotate_right(22)
50}
51
52#[inline(always)]
53const fn bsig1(x: u32) -> u32 {
54 x.rotate_right(6) ^ x.rotate_right(11) ^ x.rotate_right(25)
55}
56
57#[inline(always)]
58const fn ssig0(x: u32) -> u32 {
59 x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
60}
61
62#[inline(always)]
63const fn ssig1(x: u32) -> u32 {
64 x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
65}
66
67const fn prepare_w_schedule(data: &[u32; 16]) -> [u32; 4 * CHUNK_SIZE_U32] {
68 let mut w = [0u32; 4 * CHUNK_SIZE_U32];
69 let mut index = 0;
70 while index < CHUNK_SIZE_U32 {
71 w[index] = data[index];
72 index += 1;
73 }
74
75 let mut index = CHUNK_SIZE_U32;
76 while index < 4 * CHUNK_SIZE_U32 {
77 w[index] = ssig1(w[index - 2])
78 .wrapping_add(w[index - 7])
79 .wrapping_add(ssig0(w[index - 15]))
80 .wrapping_add(w[index - 16]);
81 index += 1;
82 }
83
84 w
85}
86
87const fn update_state(state: &mut [u32; 8], data: &[u8; 64]) {
88 let data = {
89 let mut initial_data = [0u32; CHUNK_SIZE_U32];
90 let mut index = 0;
91 while index < CHUNK_SIZE_U32 {
92 initial_data[index] = unsafe { from_be_const_u32(data, index * 4) };
93 index += 1;
94 }
95 initial_data
96 };
97
98 let w = prepare_w_schedule(&data);
99
100 let mut a = state[0];
101 let mut b = state[1];
102 let mut c = state[2];
103 let mut d = state[3];
104 let mut e = state[4];
105 let mut f = state[5];
106 let mut g = state[6];
107 let mut h = state[7];
108
109 let mut index = 0;
110 while index < 64 {
111 let temp1 = h
112 .wrapping_add(bsig1(e))
113 .wrapping_add(ch(e, f, g))
114 .wrapping_add(K[index])
115 .wrapping_add(w[index]);
116 let temp2 = bsig0(a).wrapping_add(maj(a, b, c));
117 h = g;
118 g = f;
119 f = e;
120 e = d.wrapping_add(temp1);
121 d = c;
122 c = b;
123 b = a;
124 a = temp1.wrapping_add(temp2);
125 index += 1;
126 }
127
128 state[0] = state[0].wrapping_add(a);
129 state[1] = state[1].wrapping_add(b);
130 state[2] = state[2].wrapping_add(c);
131 state[3] = state[3].wrapping_add(d);
132 state[4] = state[4].wrapping_add(e);
133 state[5] = state[5].wrapping_add(f);
134 state[6] = state[6].wrapping_add(g);
135 state[7] = state[7].wrapping_add(h);
136}
137
138impl SHA2_256_Portable {
139 #[inline(always)]
141 pub const fn new() -> Self {
142 Self {
143 total_length: 0,
144 state: INITIAL_STATE,
145 bufferer: ConstBuffer::new(),
146 }
147 }
148
149 #[inline(always)]
150 pub(super) const fn from_pieces(total_length: u64, state: [u32; 8], bufferer: ConstBuffer<64, u8>) -> Self {
151 Self {
152 total_length,
153 state,
154 bufferer,
155 }
156 }
157
158 pub const fn update_const(&mut self, data: &[u8]) {
165 let len = data.len();
166 if len == 0 {
167 return;
168 }
169
170 assert!(
171 len <= u32::MAX as usize,
172 "The max size of a chunk for SHA2_256_Portable is u32::MAX."
173 );
174
175 let len: u64 = len as u64;
176
177 assert!(
178 self.total_length <= (u64::MAX / 8) - len,
179 "The total length of the data that SHA2-256 can process is u64::MAX / 8. This limit is reached."
180 );
181
182 self.total_length = unsafe { self.total_length.unchecked_add(len) };
184
185 let mut iterator = self.bufferer.buffer_const(data);
186 while let Some(block) = iterator.next() {
187 update_state(&mut self.state, block);
188 }
189 }
190
191 pub const fn write_result_const(&self, output: &mut [u8; 32]) {
198 let mut state = self.state;
200
201 let final_blocks = calculate_final_blocks(self.total_length, self.bufferer.current_state_const());
202 let mut tmp_buffer = ConstBuffer::<64, u8>::new();
203 let mut iterator = tmp_buffer.buffer_const(final_blocks.as_slice());
204 while let Some(block) = iterator.next() {
205 update_state(&mut state, block);
206 }
207
208 let mut index = 0;
210 while index < 8 {
211 let dst = unsafe {
212 let range = (4 * index)..(4 * (index + 1));
213 subslice_mut_const(output, range)
214 };
215 let src = &state[index].to_be_bytes();
216 dst.copy_from_slice(src);
217 index += 1;
218 }
219 }
220
221 #[inline(always)]
228 #[must_use]
229 pub const fn result_const(&self) -> [u8; 32] {
230 let mut array = core::mem::MaybeUninit::<[u8; 32]>::uninit();
231 let raw_ref = unsafe { &mut *array.as_mut_ptr() };
232 self.write_result_const(raw_ref);
233 unsafe { array.assume_init() }
234 }
235}
236
237impl Default for SHA2_256_Portable {
238 #[inline(always)]
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244impl HashFunction for SHA2_256_Portable {
245 type Output = [u8; 32];
246
247 #[inline(always)]
248 fn update(&mut self, data: impl AsRef<[u8]>) {
249 self.update_const(data.as_ref());
250 }
251
252 #[inline(always)]
253 fn write_result(&self, output: &mut Self::Output) {
254 self.write_result_const(output);
255 }
256}