Skip to content

Commit bffefac

Browse files
authored
Added compute_sha256_u32_array_safe that does not fail on conversions. (#9386)
1 parent 96ad982 commit bffefac

File tree

3 files changed

+7265
-7156
lines changed

3 files changed

+7265
-7156
lines changed

corelib/src/sha256.cairo

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#[feature("byte-span")]
1717
use core::byte_array::ToByteSpanTrait;
1818
#[feature("bounded-int-utils")]
19-
use core::internal::bounded_int::upcast;
19+
use core::internal::bounded_int::{BoundedInt, downcast, upcast};
2020
use starknet::SyscallResultTrait;
2121

2222
/// A handle to the state of a SHA-256 hash.
@@ -36,17 +36,29 @@ const SHA256_INITIAL_STATE: [u32; 8] = [
3636
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
3737
];
3838

39-
/// Computes the SHA-256 hash of an array of 32-bit words.
39+
/// Computes the SHA-256 hash of an input provided as 32-bit words, with optional trailing bytes.
40+
///
41+
/// # Note
42+
///
43+
/// For better type safety, consider using `compute_sha256_u32_array_safe` when
44+
/// `last_input_num_bytes` is guaranteed to be in the range 0..=3.
4045
///
4146
/// # Arguments
4247
///
43-
/// * `input` - An array of `u32` values to hash
44-
/// * `last_input_word` - The final word when input is not word-aligned
45-
/// * `last_input_num_bytes` - Number of bytes in the last input word (must be less than 4)
48+
/// * `input` - The main input, expressed as an array of `u32` words.
49+
/// * `last_input_word` - A partial final word containing any remaining bytes when the input is not
50+
/// word-aligned.
51+
/// * `last_input_num_bytes` - The number of valid bytes in `last_input_word`. Must be in the range
52+
/// 0..=3.
53+
///
54+
/// # Panics
55+
///
56+
/// * If `last_input_num_bytes` is greater than 3.
4657
///
4758
/// # Returns
4859
///
49-
/// * The SHA-256 hash of the `input array` + `last_input_word` as big endian
60+
/// * The SHA-256 hash of `input` followed by the `last_input_num_bytes` most significant bytes of
61+
/// `last_input_word`, interpreted in big-endian order.
5062
///
5163
/// # Examples
5264
///
@@ -59,6 +71,39 @@ const SHA256_INITIAL_STATE: [u32; 8] = [
5971
/// ```
6072
pub fn compute_sha256_u32_array(
6173
mut input: Array<u32>, last_input_word: u32, last_input_num_bytes: u32,
74+
) -> [u32; 8] {
75+
let last_input_num_bytes = downcast(last_input_num_bytes).expect('`last_input_num_bytes` > 3');
76+
compute_sha256_u32_array_safe(input, last_input_word, last_input_num_bytes)
77+
}
78+
79+
/// A type representing a bounded integer in the range `0..=3`.
80+
pub type u2 = BoundedInt<0, 3>;
81+
82+
/// Computes the SHA-256 hash of an input provided as 32-bit words, with optional trailing bytes.
83+
///
84+
/// # Arguments
85+
///
86+
/// * `input` - The main input, expressed as an array of `u32` words.
87+
/// * `last_input_word` - A partial final word containing any remaining bytes when the input is not
88+
/// word-aligned.
89+
/// * `last_input_num_bytes` - The number of valid bytes in `last_input_word`.
90+
///
91+
/// # Returns
92+
///
93+
/// * The SHA-256 hash of `input` followed by the `last_input_num_bytes` most significant bytes of
94+
/// `last_input_word`, interpreted in big-endian order.
95+
///
96+
/// # Examples
97+
///
98+
/// ```
99+
/// use core::sha256::compute_sha256_u32_array_safe;
100+
///
101+
/// let hash = compute_sha256_u32_array_safe(array![0x68656c6c], 0x6f, 1);
102+
/// assert!(hash == [0x2cf24dba, 0x5fb0a30e, 0x26e83b2a, 0xc5b9e29e, 0x1b161e5c, 0x1fa7425e,
103+
/// 0x73043362, 0x938b9824]);
104+
/// ```
105+
pub fn compute_sha256_u32_array_safe(
106+
mut input: Array<u32>, last_input_word: u32, last_input_num_bytes: u2,
62107
) -> [u32; 8] {
63108
add_sha256_padding(ref input, last_input_word, last_input_num_bytes);
64109

@@ -107,7 +152,7 @@ pub fn compute_sha256_byte_array(arr: @ByteArray) -> [u32; 8] {
107152
word_arr.append(upcast(conversions::shift_append_byte(b0_b1_b2, b3)));
108153
};
109154

110-
compute_sha256_u32_array(word_arr, last_word, last_word_len)
155+
compute_sha256_u32_array_safe(word_arr, last_word, last_word_len)
111156
}
112157

113158
/// Adds padding to the input array according to the SHA-256 specification.
@@ -121,9 +166,10 @@ pub fn compute_sha256_byte_array(arr: @ByteArray) -> [u32; 8] {
121166
///
122167
/// # Arguments
123168
/// * `arr` - Array to pad (modified in place)
124-
/// * `last_input_word` - Final word for non-word-aligned inputs
125-
/// * `last_input_num_bytes` - Number of valid bytes in last_input_word
126-
fn add_sha256_padding(ref arr: Array<u32>, last_input_word: u32, last_input_num_bytes: u32) {
169+
/// * `last_input_word` - A partial final word containing any remaining bytes when the input is not
170+
/// word-aligned.
171+
/// * `last_input_num_bytes` - The number of valid bytes in `last_input_word`.
172+
fn add_sha256_padding(ref arr: Array<u32>, last_input_word: u32, last_input_num_bytes: u2) {
127173
let bitlen = conversions::bitlen(arr.len(), last_input_num_bytes);
128174
arr.append(conversions::to_last_word(last_input_word, last_input_num_bytes));
129175
// Now writing the length in bits, at the end of a block.
@@ -173,6 +219,7 @@ mod conversions {
173219
use core::internal::bounded_int::{
174220
self, AddHelper, BoundedInt, DivRemHelper, MulHelper, UnitInt,
175221
};
222+
use super::u2;
176223

177224
impl U8Shift of MulHelper<u8, UnitInt<0x100>> {
178225
type Result = BoundedInt<0, 0xFF00>;
@@ -234,7 +281,7 @@ mod conversions {
234281
}
235282

236283
/// Returns the last word to append to the input `u32` array given the last word input.
237-
pub fn to_last_word(word: u32, len: u32) -> u32 {
284+
pub fn to_last_word(word: u32, len: u2) -> u32 {
238285
let shift: BoundedInt<0, 0x800000> = match len {
239286
0 => { return 0x80000000; },
240287
1 => 0x800000,
@@ -253,17 +300,17 @@ mod conversions {
253300
type Result = BoundedInt<0, 0x1FFFFFFFE0>;
254301
}
255302

256-
impl WordBitLen of MulHelper<u32, UnitInt<8>> {
257-
type Result = BoundedInt<0, 0x7FFFFFFF8>;
303+
impl WordBitLen of MulHelper<u2, UnitInt<8>> {
304+
type Result = BoundedInt<0, 0x18>;
258305
}
259306

260-
impl FullBitLen of AddHelper<BoundedInt<0, 0x1FFFFFFFE0>, BoundedInt<0, 0x7FFFFFFF8>> {
261-
type Result = BoundedInt<0, { 0x1FFFFFFFE0 + 0x7FFFFFFF8 }>;
307+
impl FullBitLen of AddHelper<BoundedInt<0, 0x1FFFFFFFE0>, BoundedInt<0, 0x18>> {
308+
type Result = BoundedInt<0, { 0x1FFFFFFFE0 + 0x18 }>;
262309
}
263310

264311
/// Returns the bit length of the input message, given the length of the representation u32
265312
/// array and last word bytes.
266-
pub fn bitlen(arr_len: u32, last_word_bytes: u32) -> u32 {
313+
pub fn bitlen(arr_len: u32, last_word_bytes: u2) -> u32 {
267314
let arr_bits = bounded_int::mul::<_, _, ArrBitLen>(arr_len, 32);
268315
let last_word_bits = bounded_int::mul::<_, _, WordBitLen>(last_word_bytes, 8);
269316
bounded_int::downcast(bounded_int::add::<_, _, FullBitLen>(arr_bits, last_word_bits))

0 commit comments

Comments
 (0)