1use starknet_types_core::felt::Felt;
2
3use starknet_rust_crypto::{
4 blake2s_hash, blake2s_hash_many, blake2s_hash_single, poseidon_hash, poseidon_hash_many,
5 poseidon_hash_single, rfc6979_generate_k, sign, verify, Blake2Hasher, PoseidonHasher,
6 SignError, VerifyError,
7};
8pub use starknet_rust_crypto::{pedersen_hash, ExtendedSignature, Signature};
9
10mod errors {
11 use core::fmt::{Display, Formatter, Result};
12
13 #[derive(Debug)]
15 pub enum EcdsaSignError {
16 MessageHashOutOfRange,
18 }
19
20 #[derive(Debug)]
21 pub enum EcdsaVerifyError {
23 MessageHashOutOfRange,
25 InvalidPublicKey,
27 SignatureROutOfRange,
29 SignatureSOutOfRange,
31 }
32
33 #[cfg(feature = "std")]
34 impl std::error::Error for EcdsaSignError {}
35
36 impl Display for EcdsaSignError {
37 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
38 match self {
39 Self::MessageHashOutOfRange => write!(f, "message hash out of range"),
40 }
41 }
42 }
43
44 #[cfg(feature = "std")]
45 impl std::error::Error for EcdsaVerifyError {}
46
47 impl Display for EcdsaVerifyError {
48 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
49 match self {
50 Self::MessageHashOutOfRange => write!(f, "message hash out of range"),
51 Self::InvalidPublicKey => write!(f, "invalid public key"),
52 Self::SignatureROutOfRange => write!(f, "signature r value out of range"),
53 Self::SignatureSOutOfRange => write!(f, "signature s value out of range"),
54 }
55 }
56 }
57}
58pub use errors::{EcdsaSignError, EcdsaVerifyError};
59
60pub fn compute_hash_on_elements<'a, ESI, II>(data: II) -> Felt
71where
72 ESI: ExactSizeIterator<Item = &'a Felt>,
73 II: IntoIterator<IntoIter = ESI>,
74{
75 let mut current_hash = Felt::ZERO;
76 let data_iter = data.into_iter();
77 let data_len = Felt::from(data_iter.len());
78
79 for elem in data_iter {
80 current_hash = pedersen_hash(¤t_hash, elem);
81 }
82
83 pedersen_hash(¤t_hash, &data_len)
84}
85
86pub fn ecdsa_sign(
89 private_key: &Felt,
90 message_hash: &Felt,
91) -> Result<ExtendedSignature, EcdsaSignError> {
92 let mut seed = None;
94 loop {
95 let k = rfc6979_generate_k(message_hash, private_key, seed.as_ref());
96
97 match sign(private_key, message_hash, &k) {
98 Ok(sig) => {
99 return Ok(sig);
100 }
101 Err(SignError::InvalidMessageHash) => {
102 return Err(EcdsaSignError::MessageHashOutOfRange)
103 }
104 Err(SignError::InvalidK) => {
105 seed = match seed {
107 Some(prev_seed) => Some(prev_seed + Felt::ONE),
108 None => Some(Felt::ONE),
109 };
110 }
111 };
112 }
113}
114
115pub fn ecdsa_verify(
117 public_key: &Felt,
118 message_hash: &Felt,
119 signature: &Signature,
120) -> Result<bool, EcdsaVerifyError> {
121 match verify(public_key, message_hash, &signature.r, &signature.s) {
122 Ok(result) => Ok(result),
123 Err(VerifyError::InvalidMessageHash) => Err(EcdsaVerifyError::MessageHashOutOfRange),
124 Err(VerifyError::InvalidPublicKey) => Err(EcdsaVerifyError::InvalidPublicKey),
125 Err(VerifyError::InvalidR) => Err(EcdsaVerifyError::SignatureROutOfRange),
126 Err(VerifyError::InvalidS) => Err(EcdsaVerifyError::SignatureSOutOfRange),
127 }
128}
129
130#[derive(Clone, Debug, Copy, Eq, PartialEq)]
132pub struct HashFunction {
133 inner: HashFunctionInner,
134}
135
136#[derive(Clone, Debug, Copy, Eq, PartialEq)]
137enum HashFunctionInner {
138 Poseidon,
139 Blake2s,
140}
141
142#[derive(Debug, Clone)]
144pub struct StatefulHasher {
145 inner: StatefulHasherInner,
146}
147
148#[derive(Debug, Clone)]
149enum StatefulHasherInner {
150 Poseidon(PoseidonHasher),
151 Blake2s(Blake2Hasher),
152}
153
154impl HashFunction {
155 pub fn stateful(&self) -> StatefulHasher {
157 let hasher = match &self.inner {
158 HashFunctionInner::Poseidon => StatefulHasherInner::Poseidon(PoseidonHasher::new()),
159 HashFunctionInner::Blake2s => StatefulHasherInner::Blake2s(Blake2Hasher::new()),
160 };
161 StatefulHasher { inner: hasher }
162 }
163
164 pub fn poseidon() -> Self {
166 Self {
167 inner: HashFunctionInner::Poseidon,
168 }
169 }
170
171 pub fn blake2s() -> Self {
173 Self {
174 inner: HashFunctionInner::Blake2s,
175 }
176 }
177
178 pub fn hash(&self, x: Felt, y: Felt) -> Felt {
180 match &self.inner {
181 HashFunctionInner::Poseidon => poseidon_hash(x, y),
182 HashFunctionInner::Blake2s => blake2s_hash(x, y),
183 }
184 }
185
186 pub fn hash_single(&self, input: Felt) -> Felt {
188 match &self.inner {
189 HashFunctionInner::Poseidon => poseidon_hash_single(input),
190 HashFunctionInner::Blake2s => blake2s_hash_single(input),
191 }
192 }
193
194 pub fn hash_many(&self, inputs: &[Felt]) -> Felt {
196 match &self.inner {
197 HashFunctionInner::Poseidon => poseidon_hash_many(inputs),
198 HashFunctionInner::Blake2s => blake2s_hash_many(inputs),
199 }
200 }
201}
202
203impl StatefulHasher {
204 pub fn update(&mut self, msg: Felt) {
206 match &mut self.inner {
207 StatefulHasherInner::Poseidon(hasher) => hasher.update(msg),
208 StatefulHasherInner::Blake2s(hasher) => hasher.update(msg),
209 }
210 }
211
212 pub fn finalize(self) -> Felt {
214 match self.inner {
215 StatefulHasherInner::Poseidon(hasher) => hasher.finalize(),
216 StatefulHasherInner::Blake2s(hasher) => hasher.finalize(),
217 }
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
227 fn test_compute_hash_on_elements() {
228 let hash = compute_hash_on_elements(&[
230 Felt::from_hex("0xaa").unwrap(),
231 Felt::from_hex("0xbb").unwrap(),
232 Felt::from_hex("0xcc").unwrap(),
233 Felt::from_hex("0xdd").unwrap(),
234 ]);
235 let expected_hash =
236 Felt::from_hex("025cde77210b1c223b2c6e69db6e9021aa1599177ab177474d5326cd2a62cb69")
237 .unwrap();
238
239 assert_eq!(expected_hash, hash);
240 }
241
242 #[test]
243 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
244 fn test_compute_hash_on_elements_empty_data() {
245 let hash = compute_hash_on_elements(&[]);
247 let expected_hash =
248 Felt::from_hex("049ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804")
249 .unwrap();
250
251 assert_eq!(expected_hash, hash);
252 }
253
254 #[test]
255 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
256 fn test_ecdsa_sign() {
257 let signature = ecdsa_sign(
259 &Felt::from_hex("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79")
260 .unwrap(),
261 &Felt::from_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76")
262 .unwrap(),
263 )
264 .unwrap();
265 let expected_r =
266 Felt::from_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f")
267 .unwrap();
268 let expected_s =
269 Felt::from_hex("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a")
270 .unwrap();
271
272 assert_eq!(signature.r, expected_r);
273 assert_eq!(signature.s, expected_s);
274 }
275
276 #[test]
277 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
278 fn test_ecdsa_sign_message_hash_out_of_range() {
279 match ecdsa_sign(
280 &Felt::from_hex("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79")
281 .unwrap(),
282 &Felt::from_hex("0800000000000000000000000000000000000000000000000000000000000000")
283 .unwrap(),
284 ) {
285 Err(EcdsaSignError::MessageHashOutOfRange) => {}
286 _ => panic!("Should throw error on out of range message hash"),
287 };
288 }
289
290 #[test]
291 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
292 fn test_ecdsa_verify_valid_signature() {
293 let public_key =
295 Felt::from_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159")
296 .unwrap();
297 let message_hash =
298 Felt::from_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76")
299 .unwrap();
300 let r = Felt::from_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f")
301 .unwrap();
302 let s = Felt::from_hex("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a")
303 .unwrap();
304
305 assert!(ecdsa_verify(&public_key, &message_hash, &Signature { r, s }).unwrap());
306 }
307
308 #[test]
309 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
310 fn test_ecdsa_verify_invalid_signature() {
311 let public_key =
313 Felt::from_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159")
314 .unwrap();
315 let message_hash =
316 Felt::from_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76")
317 .unwrap();
318 let r = Felt::from_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f")
319 .unwrap();
320 let s = Felt::from_hex("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b")
321 .unwrap();
322
323 assert!(!ecdsa_verify(&public_key, &message_hash, &Signature { r, s }).unwrap());
324 }
325
326 #[test]
327 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
328 fn test_hash_function_hash() {
329 let x = Felt::from_hex("0x1").unwrap();
330 let y = Felt::from_hex("0x2").unwrap();
331
332 assert_eq!(HashFunction::poseidon().hash(x, y), poseidon_hash(x, y));
333 assert_eq!(HashFunction::blake2s().hash(x, y), blake2s_hash(x, y));
334 }
335
336 #[test]
337 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
338 fn test_hash_function_hash_single() {
339 let x = Felt::from_hex("0x1").unwrap();
340
341 assert_eq!(
342 HashFunction::poseidon().hash_single(x),
343 poseidon_hash_single(x)
344 );
345 assert_eq!(
346 HashFunction::blake2s().hash_single(x),
347 blake2s_hash_single(x)
348 );
349 }
350
351 #[test]
352 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
353 fn test_hash_function_hash_many() {
354 let many = vec![
355 Felt::from_hex("0x3").unwrap(),
356 Felt::from_hex("0x4").unwrap(),
357 ];
358
359 assert_eq!(
360 HashFunction::poseidon().hash_many(&many),
361 poseidon_hash_many(&many)
362 );
363
364 assert_eq!(
365 HashFunction::blake2s().hash_many(&many),
366 blake2s_hash_many(&many)
367 );
368 }
369
370 #[test]
371 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
372 fn test_hash_function_stateful() {
373 let x = Felt::from_hex("0x1").unwrap();
374 let y = Felt::from_hex("0x2").unwrap();
375
376 let mut poseidon_hasher = HashFunction::poseidon().stateful();
377 poseidon_hasher.update(x);
378 poseidon_hasher.update(y);
379 assert_eq!(poseidon_hasher.finalize(), poseidon_hash_many(&[x, y]));
380
381 let mut blake2s_hasher = HashFunction::blake2s().stateful();
382 blake2s_hasher.update(x);
383 blake2s_hasher.update(y);
384 assert_eq!(blake2s_hasher.finalize(), blake2s_hash_many(&[x, y]));
385 }
386}