1use crate::stdlib::{boxed::Box, collections::HashMap, prelude::*};
2
3use crate::Felt252;
4use crate::{
5 hint_processor::{
6 builtin_hint_processor::hint_utils::{
7 get_integer_from_var_name, get_ptr_from_var_name, insert_value_from_var_name,
8 },
9 hint_processor_utils::felt_to_u32,
10 },
11 serde::deserialize_program::ApTracking,
12 types::relocatable::MaybeRelocatable,
13 vm::errors::{hint_errors::HintError, vm_errors::VirtualMachineError},
14 vm::vm_core::VirtualMachine,
15};
16use generic_array::GenericArray;
17use num_traits::ToPrimitive;
18use sha2::compress256;
19
20use crate::hint_processor::hint_processor_definition::HintReference;
21
22use super::hint_utils::get_constant_from_var_name;
23
24const SHA256_STATE_SIZE_FELTS: usize = 8;
25const BLOCK_SIZE: usize = 7;
26const IV: [u32; SHA256_STATE_SIZE_FELTS] = [
27 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
28];
29
30pub fn sha256_input(
31 vm: &mut VirtualMachine,
32 ids_data: &HashMap<String, HintReference>,
33 ap_tracking: &ApTracking,
34) -> Result<(), HintError> {
35 let n_bytes = get_integer_from_var_name("n_bytes", vm, ids_data, ap_tracking)?;
36 let n_bytes = n_bytes.as_ref();
37
38 insert_value_from_var_name(
39 "full_word",
40 if n_bytes >= &Felt252::from(4_i32) {
41 Felt252::ONE
42 } else {
43 Felt252::ZERO
44 },
45 vm,
46 ids_data,
47 ap_tracking,
48 )
49}
50
51fn sha256_main(
53 vm: &mut VirtualMachine,
54 ids_data: &HashMap<String, HintReference>,
55 ap_tracking: &ApTracking,
56 constants: &HashMap<String, Felt252>,
57 iv: &mut [u32; 8],
58) -> Result<(), HintError> {
59 let input_ptr = get_ptr_from_var_name("sha256_start", vm, ids_data, ap_tracking)?;
60
61 let input_chunk_size_felts =
64 get_constant_from_var_name("SHA256_INPUT_CHUNK_SIZE_FELTS", constants)?
65 .to_usize()
66 .unwrap_or(100); if input_chunk_size_felts >= 100 {
69 return Err(HintError::AssertionFailed(
70 "assert 0 <= _sha256_input_chunk_size_felts < 100"
71 .to_string()
72 .into_boxed_str(),
73 ));
74 }
75
76 let mut message: Vec<u8> = Vec::with_capacity(4 * input_chunk_size_felts);
77
78 for i in 0..input_chunk_size_felts {
79 let input_element = vm.get_integer((input_ptr + i)?)?;
80 let bytes = felt_to_u32(input_element.as_ref())?.to_be_bytes();
81 message.extend(bytes);
82 }
83
84 let new_message = GenericArray::clone_from_slice(&message);
85 compress256(iv, &[new_message]);
86
87 let mut output: Vec<MaybeRelocatable> = Vec::with_capacity(iv.len());
88
89 for new_state in iv {
90 output.push(Felt252::from(*new_state).into());
91 }
92
93 let output_base = get_ptr_from_var_name("output", vm, ids_data, ap_tracking)?;
94
95 vm.write_arg(output_base, &output)
96 .map_err(VirtualMachineError::Memory)?;
97 Ok(())
98}
99
100pub fn sha256_main_constant_input_length(
113 vm: &mut VirtualMachine,
114 ids_data: &HashMap<String, HintReference>,
115 ap_tracking: &ApTracking,
116 constants: &HashMap<String, Felt252>,
117) -> Result<(), HintError> {
118 let mut iv = IV;
119 sha256_main(vm, ids_data, ap_tracking, constants, &mut iv)
120}
121
122pub fn sha256_main_arbitrary_input_length(
136 vm: &mut VirtualMachine,
137 ids_data: &HashMap<String, HintReference>,
138 ap_tracking: &ApTracking,
139 constants: &HashMap<String, Felt252>,
140) -> Result<(), HintError> {
141 let iv_ptr = get_ptr_from_var_name("state", vm, ids_data, ap_tracking)?;
142
143 let state_size_felt = get_constant_from_var_name("SHA256_STATE_SIZE_FELTS", constants)?;
144
145 let state_size = match state_size_felt.to_usize() {
146 Some(size) if size == SHA256_STATE_SIZE_FELTS => size,
147 Some(size) if size < 100 => {
150 return Err(HintError::InvalidValue(Box::new((
151 "SHA256_STATE_SIZE_FELTS",
152 *state_size_felt,
153 Felt252::from(SHA256_STATE_SIZE_FELTS),
154 ))))
155 }
156 _ => {
158 return Err(HintError::AssertionFailed(
159 "assert 0 <= _sha256_state_size_felts < 100"
160 .to_string()
161 .into_boxed_str(),
162 ))
163 }
164 };
165
166 let mut iv = vm
167 .get_integer_range(iv_ptr, state_size)?
168 .into_iter()
169 .map(|x| felt_to_u32(x.as_ref()))
170 .collect::<Result<Vec<u32>, _>>()?
171 .try_into()
172 .expect("size is constant");
173
174 sha256_main(vm, ids_data, ap_tracking, constants, &mut iv)
175}
176
177pub fn sha256_finalize(
178 vm: &mut VirtualMachine,
179 ids_data: &HashMap<String, HintReference>,
180 ap_tracking: &ApTracking,
181) -> Result<(), HintError> {
182 let message: Vec<u8> = vec![0; 64];
183
184 let mut iv = IV;
185
186 let iv_static: Vec<MaybeRelocatable> = iv.iter().map(|n| Felt252::from(*n).into()).collect();
187
188 let new_message = GenericArray::clone_from_slice(&message);
189 compress256(&mut iv, &[new_message]);
190
191 let mut output: Vec<MaybeRelocatable> = Vec::with_capacity(SHA256_STATE_SIZE_FELTS);
192
193 for new_state in iv {
194 output.push(Felt252::from(new_state).into());
195 }
196
197 let sha256_ptr_end = get_ptr_from_var_name("sha256_ptr_end", vm, ids_data, ap_tracking)?;
198
199 let mut padding: Vec<MaybeRelocatable> = Vec::new();
200 let zero_vector_message: Vec<MaybeRelocatable> = vec![Felt252::ZERO.into(); 16];
201
202 for _ in 0..BLOCK_SIZE - 1 {
203 padding.extend_from_slice(zero_vector_message.as_slice());
204 padding.extend_from_slice(iv_static.as_slice());
205 padding.extend_from_slice(output.as_slice());
206 }
207
208 vm.write_arg(sha256_ptr_end, &padding)
209 .map_err(VirtualMachineError::Memory)?;
210 Ok(())
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 use crate::{
218 any_box,
219 hint_processor::{
220 builtin_hint_processor::{
221 builtin_hint_processor_definition::{BuiltinHintProcessor, HintProcessorData},
222 hint_code,
223 },
224 hint_processor_definition::{HintProcessorLogic, HintReference},
225 },
226 utils::test_utils::*,
227 vm::vm_core::VirtualMachine,
228 };
229 use assert_matches::assert_matches;
230
231 use rstest::rstest;
232 #[cfg(target_arch = "wasm32")]
233 use wasm_bindgen_test::*;
234
235 const SHA256_INPUT_CHUNK_SIZE_FELTS: usize = 16;
236
237 #[test]
238 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
239 fn sha256_input_one() {
240 let mut vm = vm_with_range_check!();
241 vm.segments = segments![((1, 1), 7)];
242 vm.run_context.fp = 2;
243 let ids_data = ids_data!["full_word", "n_bytes"];
244 assert_matches!(sha256_input(&mut vm, &ids_data, &ApTracking::new()), Ok(()));
245
246 check_memory![vm.segments.memory, ((1, 0), 1)];
247 }
248
249 #[test]
250 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
251 fn sha256_input_zero() {
252 let mut vm = vm_with_range_check!();
253 vm.segments = segments![((1, 1), 3)];
254 vm.run_context.fp = 2;
255 let ids_data = ids_data!["full_word", "n_bytes"];
256 assert_matches!(sha256_input(&mut vm, &ids_data, &ApTracking::new()), Ok(()));
257
258 check_memory![vm.segments.memory, ((1, 0), 0)];
259 }
260
261 #[test]
262 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
263 fn sha256_constant_input_length_ok() {
264 let hint_code = hint_code::SHA256_MAIN_CONSTANT_INPUT_LENGTH;
265 let mut vm = vm_with_range_check!();
266
267 vm.segments = segments![
268 ((1, 0), (2, 0)),
269 ((1, 1), (3, 0)),
270 ((2, 0), 22),
271 ((2, 1), 22),
272 ((2, 2), 22),
273 ((2, 3), 22),
274 ((2, 4), 22),
275 ((2, 5), 22),
276 ((2, 6), 22),
277 ((2, 7), 22),
278 ((2, 8), 22),
279 ((2, 9), 22),
280 ((2, 10), 22),
281 ((2, 11), 22),
282 ((2, 12), 22),
283 ((2, 13), 22),
284 ((2, 14), 22),
285 ((2, 15), 22),
286 ((3, 9), 0)
287 ];
288 vm.run_context.fp = 2;
289 let ids_data = ids_data!["sha256_start", "output"];
290 let constants = HashMap::from([(
291 "SHA256_INPUT_CHUNK_SIZE_FELTS".to_string(),
292 Felt252::from(SHA256_INPUT_CHUNK_SIZE_FELTS),
293 )]);
294 assert_matches!(
295 run_hint!(&mut vm, ids_data, hint_code, exec_scopes_ref!(), &constants),
296 Ok(())
297 );
298
299 check_memory![
300 vm.segments.memory,
301 ((3, 0), 3704205499_u32),
302 ((3, 1), 2308112482_u32),
303 ((3, 2), 3022351583_u32),
304 ((3, 3), 174314172_u32),
305 ((3, 4), 1762869695_u32),
306 ((3, 5), 1649521060_u32),
307 ((3, 6), 2811202336_u32),
308 ((3, 7), 4231099170_u32)
309 ];
310 }
311
312 #[test]
313 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
314 fn sha256_arbitrary_input_length_ok() {
315 let hint_code = hint_code::SHA256_MAIN_ARBITRARY_INPUT_LENGTH;
316 let mut vm = vm_with_range_check!();
317
318 vm.segments = segments![
319 ((1, 0), (2, 0)),
320 ((1, 1), (3, 0)),
321 ((1, 2), (4, 0)),
322 ((2, 0), 22),
323 ((2, 1), 22),
324 ((2, 2), 22),
325 ((2, 3), 22),
326 ((2, 4), 22),
327 ((2, 5), 22),
328 ((2, 6), 22),
329 ((2, 7), 22),
330 ((2, 8), 22),
331 ((2, 9), 22),
332 ((2, 10), 22),
333 ((2, 11), 22),
334 ((2, 12), 22),
335 ((2, 13), 22),
336 ((2, 14), 22),
337 ((2, 15), 22),
338 ((3, 9), 0),
339 ((4, 0), 0x6A09E667),
340 ((4, 1), 0xBB67AE85),
341 ((4, 2), 0x3C6EF372),
342 ((4, 3), 0xA54FF53A),
343 ((4, 4), 0x510E527F),
344 ((4, 5), 0x9B05688C),
345 ((4, 6), 0x1F83D9AB),
346 ((4, 7), 0x5BE0CD18),
347 ];
348 vm.run_context.fp = 3;
349 let ids_data = ids_data!["sha256_start", "output", "state"];
350 let constants = HashMap::from([
351 (
352 "SHA256_INPUT_CHUNK_SIZE_FELTS".to_string(),
353 Felt252::from(SHA256_INPUT_CHUNK_SIZE_FELTS),
354 ),
355 (
356 "SHA256_STATE_SIZE_FELTS".to_string(),
357 Felt252::from(SHA256_STATE_SIZE_FELTS),
358 ),
359 ]);
360 assert_matches!(
361 run_hint!(&mut vm, ids_data, hint_code, exec_scopes_ref!(), &constants),
362 Ok(())
363 );
364 check_memory![
365 vm.segments.memory,
366 ((3, 0), 1676947577_u32),
367 ((3, 1), 1555161467_u32),
368 ((3, 2), 2679819371_u32),
369 ((3, 3), 2084775296_u32),
370 ((3, 4), 3059346845_u32),
371 ((3, 5), 785647811_u32),
372 ((3, 6), 2729325562_u32),
373 ((3, 7), 2503090120_u32)
374 ];
375 }
376
377 #[rstest]
378 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
379 #[case(hint_code::SHA256_MAIN_CONSTANT_INPUT_LENGTH)]
380 #[case(hint_code::SHA256_MAIN_ARBITRARY_INPUT_LENGTH)]
381 fn sha256_invalid_chunk_size(#[case] hint_code: &str) {
382 let mut vm = vm_with_range_check!();
383
384 vm.segments = segments![
385 ((1, 0), (2, 0)),
386 ((1, 1), (3, 0)),
387 ((1, 2), (4, 0)),
388 ((4, 0), 0x6A09E667),
389 ((4, 1), 0xBB67AE85),
390 ((4, 2), 0x3C6EF372),
391 ((4, 3), 0xA54FF53A),
392 ((4, 4), 0x510E527F),
393 ((4, 5), 0x9B05688C),
394 ((4, 6), 0x1F83D9AB),
395 ((4, 7), 0x5BE0CD18),
396 ];
397 vm.run_context.fp = 3;
398 let ids_data = ids_data!["sha256_start", "output", "state"];
399 let constants = HashMap::from([
400 (
401 "SHA256_INPUT_CHUNK_SIZE_FELTS".to_string(),
402 Felt252::from(100),
403 ),
404 (
405 "SHA256_STATE_SIZE_FELTS".to_string(),
406 Felt252::from(SHA256_STATE_SIZE_FELTS),
407 ),
408 ]);
409 assert_matches!(
410 run_hint!(&mut vm, ids_data, hint_code, exec_scopes_ref!(), &constants),
411 Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert 0 <= _sha256_input_chunk_size_felts < 100"
412 );
413 }
414
415 #[test]
416 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
417 fn sha256_invalid_state_size() {
418 let hint_code = hint_code::SHA256_MAIN_ARBITRARY_INPUT_LENGTH;
419 let mut vm = vm_with_range_check!();
420
421 vm.segments = segments![
422 ((1, 0), (2, 0)),
423 ((1, 1), (3, 0)),
424 ((1, 2), (4, 0)),
425 ((4, 0), 0x6A09E667),
426 ((4, 1), 0xBB67AE85),
427 ((4, 2), 0x3C6EF372),
428 ((4, 3), 0xA54FF53A),
429 ((4, 4), 0x510E527F),
430 ((4, 5), 0x9B05688C),
431 ((4, 6), 0x1F83D9AB),
432 ((4, 7), 0x5BE0CD18),
433 ];
434 vm.run_context.fp = 3;
435 let ids_data = ids_data!["sha256_start", "output", "state"];
436 let constants = HashMap::from([
437 (
438 "SHA256_INPUT_CHUNK_SIZE_FELTS".to_string(),
439 Felt252::from(SHA256_INPUT_CHUNK_SIZE_FELTS),
440 ),
441 ("SHA256_STATE_SIZE_FELTS".to_string(), Felt252::from(100)),
442 ]);
443 assert_matches!(
444 run_hint!(&mut vm, ids_data, hint_code, exec_scopes_ref!(), &constants),
445 Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert 0 <= _sha256_state_size_felts < 100"
446 );
447 }
448
449 #[test]
450 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
451 fn sha256_unexpected_state_size() {
452 let hint_code = hint_code::SHA256_MAIN_ARBITRARY_INPUT_LENGTH;
453 let state_size = Felt252::from(9);
454 let mut vm = vm_with_range_check!();
455
456 vm.segments = segments![
457 ((1, 0), (2, 0)),
458 ((1, 1), (3, 0)),
459 ((1, 2), (4, 0)),
460 ((4, 0), 0x6A09E667),
461 ((4, 1), 0xBB67AE85),
462 ((4, 2), 0x3C6EF372),
463 ((4, 3), 0xA54FF53A),
464 ((4, 4), 0x510E527F),
465 ((4, 5), 0x9B05688C),
466 ((4, 6), 0x1F83D9AB),
467 ((4, 7), 0x5BE0CD18),
468 ];
469 vm.run_context.fp = 3;
470 let ids_data = ids_data!["sha256_start", "output", "state"];
471 let constants = HashMap::from([
472 (
473 "SHA256_INPUT_CHUNK_SIZE_FELTS".to_string(),
474 Felt252::from(SHA256_INPUT_CHUNK_SIZE_FELTS),
475 ),
476 ("SHA256_STATE_SIZE_FELTS".to_string(), state_size),
477 ]);
478 let expected_size = Felt252::from(SHA256_STATE_SIZE_FELTS);
479 assert_matches!(
480 run_hint!(&mut vm, ids_data, hint_code, exec_scopes_ref!(), &constants),
481 Err(HintError::InvalidValue(bx))
482 if *bx == ("SHA256_STATE_SIZE_FELTS", state_size, expected_size)
483 );
484 }
485}