Skip to main content

foundry_cheatcodes/
crypto.rs

1//! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes.
2
3use crate::{Cheatcode, Cheatcodes, Result, Vm::*};
4use alloy_primitives::{Address, B256, U256, keccak256};
5use alloy_signer::{Signer, SignerSync};
6use alloy_signer_local::{
7    LocalSigner, MnemonicBuilder, PrivateKeySigner,
8    coins_bip39::{
9        ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean,
10        Portuguese, Spanish, Wordlist,
11    },
12};
13use alloy_sol_types::SolValue;
14use foundry_common::wallet::{derive_private_key, derive_private_key_with_language};
15use foundry_evm_core::evm::FoundryEvmNetwork;
16use k256::{
17    FieldBytes, Scalar,
18    ecdsa::{SigningKey, hazmat},
19    elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint},
20};
21
22use p256::ecdsa::{
23    Signature as P256Signature, SigningKey as P256SigningKey, signature::hazmat::PrehashSigner,
24};
25
26use ed25519_consensus::{
27    Signature as Ed25519Signature, SigningKey as Ed25519SigningKey,
28    VerificationKey as Ed25519VerificationKey,
29};
30
31/// The BIP32 default derivation path prefix.
32const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/";
33
34impl Cheatcode for createWallet_0Call {
35    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
36        let Self { walletLabel } = self;
37        create_wallet(&U256::from_be_bytes(keccak256(walletLabel).0), Some(walletLabel), state)
38    }
39}
40
41impl Cheatcode for createWallet_1Call {
42    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
43        let Self { privateKey } = self;
44        create_wallet(privateKey, None, state)
45    }
46}
47
48impl Cheatcode for createWallet_2Call {
49    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
50        let Self { privateKey, walletLabel } = self;
51        create_wallet(privateKey, Some(walletLabel), state)
52    }
53}
54
55impl Cheatcode for sign_0Call {
56    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
57        let Self { wallet, digest } = self;
58        let sig = sign(&wallet.privateKey, digest)?;
59        Ok(encode_full_sig(sig))
60    }
61}
62
63impl Cheatcode for signWithNonceUnsafeCall {
64    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
65        let pk: U256 = self.privateKey;
66        let digest: B256 = self.digest;
67        let nonce: U256 = self.nonce;
68        let sig: alloy_primitives::Signature = sign_with_nonce(&pk, &digest, &nonce)?;
69        Ok(encode_full_sig(sig))
70    }
71}
72
73impl Cheatcode for signCompact_0Call {
74    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
75        let Self { wallet, digest } = self;
76        let sig = sign(&wallet.privateKey, digest)?;
77        Ok(encode_compact_sig(sig))
78    }
79}
80
81impl Cheatcode for deriveKey_0Call {
82    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
83        let Self { mnemonic, index } = self;
84        derive_key::<English>(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index)
85    }
86}
87
88impl Cheatcode for deriveKey_1Call {
89    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
90        let Self { mnemonic, derivationPath, index } = self;
91        derive_key::<English>(mnemonic, derivationPath, *index)
92    }
93}
94
95impl Cheatcode for deriveKey_2Call {
96    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
97        let Self { mnemonic, index, language } = self;
98        derive_key_str(mnemonic, DEFAULT_DERIVATION_PATH_PREFIX, *index, language)
99    }
100}
101
102impl Cheatcode for deriveKey_3Call {
103    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
104        let Self { mnemonic, derivationPath, index, language } = self;
105        derive_key_str(mnemonic, derivationPath, *index, language)
106    }
107}
108
109impl Cheatcode for rememberKeyCall {
110    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
111        let Self { privateKey } = self;
112        let wallet = parse_wallet(privateKey)?;
113        let address = inject_wallet(state, wallet);
114        Ok(address.abi_encode())
115    }
116}
117
118impl Cheatcode for rememberKeys_0Call {
119    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
120        let Self { mnemonic, derivationPath, count } = self;
121        let wallets = derive_wallets::<English>(mnemonic, derivationPath, *count)?;
122        let mut addresses = Vec::<Address>::with_capacity(wallets.len());
123        for wallet in wallets {
124            let addr = inject_wallet(state, wallet);
125            addresses.push(addr);
126        }
127
128        Ok(addresses.abi_encode())
129    }
130}
131
132impl Cheatcode for rememberKeys_1Call {
133    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
134        let Self { mnemonic, derivationPath, language, count } = self;
135        let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?;
136        let mut addresses = Vec::<Address>::with_capacity(wallets.len());
137        for wallet in wallets {
138            let addr = inject_wallet(state, wallet);
139            addresses.push(addr);
140        }
141
142        Ok(addresses.abi_encode())
143    }
144}
145
146fn inject_wallet<FEN: FoundryEvmNetwork>(
147    state: &mut Cheatcodes<FEN>,
148    wallet: LocalSigner<SigningKey>,
149) -> Address {
150    let address = wallet.address();
151    state.wallets().add_local_signer(wallet);
152    address
153}
154
155impl Cheatcode for sign_1Call {
156    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
157        let Self { privateKey, digest } = self;
158        let sig = sign(privateKey, digest)?;
159        Ok(encode_full_sig(sig))
160    }
161}
162
163impl Cheatcode for signCompact_1Call {
164    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
165        let Self { privateKey, digest } = self;
166        let sig = sign(privateKey, digest)?;
167        Ok(encode_compact_sig(sig))
168    }
169}
170
171impl Cheatcode for sign_2Call {
172    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
173        let Self { digest } = self;
174        let sig = sign_with_wallet(state, None, digest)?;
175        Ok(encode_full_sig(sig))
176    }
177}
178
179impl Cheatcode for signCompact_2Call {
180    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
181        let Self { digest } = self;
182        let sig = sign_with_wallet(state, None, digest)?;
183        Ok(encode_compact_sig(sig))
184    }
185}
186
187impl Cheatcode for sign_3Call {
188    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
189        let Self { signer, digest } = self;
190        let sig = sign_with_wallet(state, Some(*signer), digest)?;
191        Ok(encode_full_sig(sig))
192    }
193}
194
195impl Cheatcode for signCompact_3Call {
196    fn apply<FEN: FoundryEvmNetwork>(&self, state: &mut Cheatcodes<FEN>) -> Result {
197        let Self { signer, digest } = self;
198        let sig = sign_with_wallet(state, Some(*signer), digest)?;
199        Ok(encode_compact_sig(sig))
200    }
201}
202
203impl Cheatcode for signP256Call {
204    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
205        let Self { privateKey, digest } = self;
206        sign_p256(privateKey, digest)
207    }
208}
209
210impl Cheatcode for publicKeyP256Call {
211    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
212        let Self { privateKey } = self;
213        let pub_key =
214            parse_private_key_p256(privateKey)?.verifying_key().as_affine().to_encoded_point(false);
215        let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
216        let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
217
218        Ok((pub_key_x, pub_key_y).abi_encode())
219    }
220}
221
222impl Cheatcode for createEd25519KeyCall {
223    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
224        let Self { salt } = self;
225        create_ed25519_key(salt)
226    }
227}
228
229impl Cheatcode for publicKeyEd25519Call {
230    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
231        let Self { privateKey } = self;
232        public_key_ed25519(privateKey)
233    }
234}
235
236impl Cheatcode for signEd25519Call {
237    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
238        let Self { namespace, message, privateKey } = self;
239        sign_ed25519(namespace, message, privateKey)
240    }
241}
242
243impl Cheatcode for verifyEd25519Call {
244    fn apply<FEN: FoundryEvmNetwork>(&self, _state: &mut Cheatcodes<FEN>) -> Result {
245        let Self { signature, namespace, message, publicKey } = self;
246        verify_ed25519(signature, namespace, message, publicKey)
247    }
248}
249
250/// Using a given private key, return its public ETH address, its public key affine x and y
251/// coordinates, and its private key (see the 'Wallet' struct)
252///
253/// If 'label' is set to 'Some()', assign that label to the associated ETH address in state
254fn create_wallet<FEN: FoundryEvmNetwork>(
255    private_key: &U256,
256    label: Option<&str>,
257    state: &mut Cheatcodes<FEN>,
258) -> Result {
259    let key = parse_private_key(private_key)?;
260    let addr = alloy_signer::utils::secret_key_to_address(&key);
261
262    let pub_key = key.verifying_key().as_affine().to_encoded_point(false);
263    let pub_key_x = U256::from_be_bytes((*pub_key.x().unwrap()).into());
264    let pub_key_y = U256::from_be_bytes((*pub_key.y().unwrap()).into());
265
266    if let Some(label) = label {
267        state.labels.insert(addr, label.into());
268    }
269
270    Ok(Wallet { addr, publicKeyX: pub_key_x, publicKeyY: pub_key_y, privateKey: *private_key }
271        .abi_encode())
272}
273
274fn encode_full_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
275    // Retrieve v, r and s from signature.
276    let v = U256::from(sig.v() as u64 + 27);
277    let r = B256::from(sig.r());
278    let s = B256::from(sig.s());
279    (v, r, s).abi_encode()
280}
281
282fn encode_compact_sig(sig: alloy_primitives::Signature) -> Vec<u8> {
283    // Implement EIP-2098 compact signature.
284    let r = B256::from(sig.r());
285    let mut vs = sig.s();
286    vs.set_bit(255, sig.v());
287    (r, vs).abi_encode()
288}
289
290fn sign(private_key: &U256, digest: &B256) -> Result<alloy_primitives::Signature> {
291    // The `ecrecover` precompile does not use EIP-155. No chain ID is needed.
292    let wallet = parse_wallet(private_key)?;
293    let sig = wallet.sign_hash_sync(digest)?;
294    debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address());
295    Ok(sig)
296}
297
298/// Signs `digest` on secp256k1 using a user-supplied ephemeral nonce `k` (no RFC6979).
299/// - `private_key` and `nonce` must be in (0, n)
300/// - `digest` is a 32-byte prehash.
301///
302/// # Warning
303///
304/// Use [`sign_with_nonce`] with extreme caution!
305/// Reusing the same nonce (`k`) with the same private key in ECDSA will leak the private key.
306/// Always generate `nonce` with a cryptographically secure RNG, and never reuse it across
307/// signatures.
308fn sign_with_nonce(
309    private_key: &U256,
310    digest: &B256,
311    nonce: &U256,
312) -> Result<alloy_primitives::Signature> {
313    let d_scalar: Scalar =
314        <Scalar as k256::elliptic_curve::PrimeField>::from_repr(private_key.to_be_bytes().into())
315            .into_option()
316            .ok_or_else(|| fmt_err!("invalid private key scalar"))?;
317    if bool::from(d_scalar.is_zero()) {
318        return Err(fmt_err!("private key cannot be 0"));
319    }
320
321    let k_scalar: Scalar =
322        <Scalar as k256::elliptic_curve::PrimeField>::from_repr(nonce.to_be_bytes().into())
323            .into_option()
324            .ok_or_else(|| fmt_err!("invalid nonce scalar"))?;
325    if bool::from(k_scalar.is_zero()) {
326        return Err(fmt_err!("nonce cannot be 0"));
327    }
328
329    let mut z = [0u8; 32];
330    z.copy_from_slice(digest.as_slice());
331    let z_fb: FieldBytes = FieldBytes::from(z);
332
333    // Hazmat signing using the scalar `d` (SignPrimitive is implemented for `Scalar`)
334    // Note: returns (Signature, Option<RecoveryId>)
335    let (sig_raw, recid_opt) =
336        <Scalar as hazmat::SignPrimitive<k256::Secp256k1>>::try_sign_prehashed(
337            &d_scalar, k_scalar, &z_fb,
338        )
339        .map_err(|e| fmt_err!("sign_prehashed failed: {e}"))?;
340
341    // Enforce low-s; if mirrored, parity flips (we’ll account for it below if we use recid)
342    let (sig_low, flipped) =
343        if let Some(norm) = sig_raw.normalize_s() { (norm, true) } else { (sig_raw, false) };
344
345    let r_u256 = U256::from_be_bytes(sig_low.r().to_bytes().into());
346    let s_u256 = U256::from_be_bytes(sig_low.s().to_bytes().into());
347
348    // Determine v parity in {0,1}
349    let v_parity = if let Some(id) = recid_opt {
350        let mut v = id.to_byte() & 1;
351        if flipped {
352            v ^= 1;
353        }
354        v
355    } else {
356        // Fallback: choose parity by recovery to expected address
357        let expected_addr = {
358            let sk: SigningKey = parse_private_key(private_key)?;
359            alloy_signer::utils::secret_key_to_address(&sk)
360        };
361        // Try v = 0
362        let cand0 = alloy_primitives::Signature::new(r_u256, s_u256, false);
363        if cand0.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
364            return Ok(cand0);
365        }
366        // Try v = 1
367        let cand1 = alloy_primitives::Signature::new(r_u256, s_u256, true);
368        if cand1.recover_address_from_prehash(digest).ok() == Some(expected_addr) {
369            return Ok(cand1);
370        }
371        return Err(fmt_err!("failed to determine recovery id for signature"));
372    };
373
374    let y_parity = v_parity != 0;
375    Ok(alloy_primitives::Signature::new(r_u256, s_u256, y_parity))
376}
377
378fn sign_with_wallet<FEN: FoundryEvmNetwork>(
379    state: &mut Cheatcodes<FEN>,
380    signer: Option<Address>,
381    digest: &B256,
382) -> Result<alloy_primitives::Signature> {
383    if state.wallets().is_empty() {
384        bail!("no wallets available");
385    }
386
387    let mut wallets = state.wallets().inner.lock();
388    let maybe_provided_sender = wallets.provided_sender;
389    let signers = wallets.multi_wallet.signers()?;
390
391    let signer = if let Some(signer) = signer {
392        signer
393    } else if let Some(provided_sender) = maybe_provided_sender {
394        provided_sender
395    } else if signers.len() == 1 {
396        *signers.keys().next().unwrap()
397    } else {
398        bail!(
399            "could not determine signer, there are multiple signers available use vm.sign(signer, digest) to specify one"
400        );
401    };
402
403    let wallet = signers
404        .get(&signer)
405        .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?;
406
407    let sig = foundry_common::block_on(wallet.sign_hash(digest))?;
408    debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer);
409    Ok(sig)
410}
411
412fn sign_p256(private_key: &U256, digest: &B256) -> Result {
413    let signing_key = parse_private_key_p256(private_key)?;
414    let signature: P256Signature = signing_key.sign_prehash(digest.as_slice())?;
415    let signature = signature.normalize_s().unwrap_or(signature);
416    let r_bytes: [u8; 32] = signature.r().to_bytes().into();
417    let s_bytes: [u8; 32] = signature.s().to_bytes().into();
418
419    Ok((r_bytes, s_bytes).abi_encode())
420}
421
422fn validate_private_key<C: ecdsa::PrimeCurve>(private_key: &U256) -> Result<()> {
423    ensure!(*private_key != U256::ZERO, "private key cannot be 0");
424    let order = U256::from_be_slice(&C::ORDER.to_be_byte_array());
425    ensure!(
426        *private_key < order,
427        "private key must be less than the {curve:?} curve order ({order})",
428        curve = C::default(),
429    );
430
431    Ok(())
432}
433
434fn parse_private_key(private_key: &U256) -> Result<SigningKey> {
435    validate_private_key::<k256::Secp256k1>(private_key)?;
436    Ok(SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
437}
438
439fn parse_private_key_p256(private_key: &U256) -> Result<P256SigningKey> {
440    validate_private_key::<p256::NistP256>(private_key)?;
441    Ok(P256SigningKey::from_bytes((&private_key.to_be_bytes()).into())?)
442}
443
444fn parse_signing_key_ed25519(private_key: &B256) -> Result<Ed25519SigningKey> {
445    Ed25519SigningKey::try_from(private_key.as_slice())
446        .map_err(|e| fmt_err!("invalid Ed25519 private key: {e}"))
447}
448
449fn create_ed25519_key(salt: &B256) -> Result {
450    let signing_key = parse_signing_key_ed25519(salt)?;
451    let public_key = B256::from_slice(signing_key.verification_key().as_ref());
452    Ok((public_key, *salt).abi_encode())
453}
454
455fn public_key_ed25519(private_key: &B256) -> Result {
456    let signing_key = parse_signing_key_ed25519(private_key)?;
457    Ok(B256::from_slice(signing_key.verification_key().as_ref()).abi_encode())
458}
459
460fn sign_ed25519(namespace: &[u8], message: &[u8], private_key: &B256) -> Result {
461    let signing_key = parse_signing_key_ed25519(private_key)?;
462    let combined = [namespace, message].concat();
463    let signature: [u8; 64] = signing_key.sign(&combined).into();
464    Ok(signature.to_vec().abi_encode())
465}
466
467fn verify_ed25519(signature: &[u8], namespace: &[u8], message: &[u8], public_key: &B256) -> Result {
468    if signature.len() != 64 {
469        return Ok(false.abi_encode());
470    }
471
472    let Ok(verification_key) = Ed25519VerificationKey::try_from(public_key.as_slice()) else {
473        return Ok(false.abi_encode());
474    };
475
476    let Ok(sig_bytes): Result<[u8; 64], _> = signature.try_into() else {
477        return Ok(false.abi_encode());
478    };
479
480    let combined = [namespace, message].concat();
481    let valid = verification_key.verify(&Ed25519Signature::from(sig_bytes), &combined).is_ok();
482    Ok(valid.abi_encode())
483}
484
485pub(super) fn parse_wallet(private_key: &U256) -> Result<PrivateKeySigner> {
486    parse_private_key(private_key).map(PrivateKeySigner::from)
487}
488
489fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result {
490    let private_key = derive_private_key_with_language(mnemonic, path, index, language)
491        .map_err(|e| fmt_err!("{e}"))?;
492    Ok(private_key.abi_encode())
493}
494
495fn derive_key<W: Wordlist>(mnemonic: &str, path: &str, index: u32) -> Result {
496    let private_key =
497        derive_private_key::<W>(mnemonic, path, index).map_err(|e| fmt_err!("{e}"))?;
498    Ok(private_key.abi_encode())
499}
500
501fn derive_wallets_str(
502    mnemonic: &str,
503    path: &str,
504    language: &str,
505    count: u32,
506) -> Result<Vec<LocalSigner<SigningKey>>> {
507    match language {
508        "chinese_simplified" => derive_wallets::<ChineseSimplified>(mnemonic, path, count),
509        "chinese_traditional" => derive_wallets::<ChineseTraditional>(mnemonic, path, count),
510        "czech" => derive_wallets::<Czech>(mnemonic, path, count),
511        "english" => derive_wallets::<English>(mnemonic, path, count),
512        "french" => derive_wallets::<French>(mnemonic, path, count),
513        "italian" => derive_wallets::<Italian>(mnemonic, path, count),
514        "japanese" => derive_wallets::<Japanese>(mnemonic, path, count),
515        "korean" => derive_wallets::<Korean>(mnemonic, path, count),
516        "portuguese" => derive_wallets::<Portuguese>(mnemonic, path, count),
517        "spanish" => derive_wallets::<Spanish>(mnemonic, path, count),
518        _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")),
519    }
520}
521
522fn derive_wallets<W: Wordlist>(
523    mnemonic: &str,
524    path: &str,
525    count: u32,
526) -> Result<Vec<LocalSigner<SigningKey>>> {
527    let mut out = path.to_string();
528
529    if !out.ends_with('/') {
530        out.push('/');
531    }
532
533    let mut wallets = Vec::with_capacity(count as usize);
534    for idx in 0..count {
535        let wallet = MnemonicBuilder::<W>::default()
536            .phrase(mnemonic)
537            .derivation_path(format!("{out}{idx}"))?
538            .build()?;
539        wallets.push(wallet);
540    }
541
542    Ok(wallets)
543}
544
545#[cfg(test)]
546mod tests {
547    use super::*;
548    use alloy_primitives::{FixedBytes, hex::FromHex};
549    use k256::elliptic_curve::Curve;
550    use p256::ecdsa::signature::hazmat::PrehashVerifier;
551
552    #[test]
553    fn test_sign_p256() {
554        use p256::ecdsa::VerifyingKey;
555
556        let pk_u256: U256 = "1".parse().unwrap();
557        let signing_key = P256SigningKey::from_bytes(&pk_u256.to_be_bytes().into()).unwrap();
558        let digest = FixedBytes::from_hex(
559            "0x44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56",
560        )
561        .unwrap();
562
563        let result = sign_p256(&pk_u256, &digest).unwrap();
564        let result_bytes: [u8; 64] = result.try_into().unwrap();
565        let signature = P256Signature::from_bytes(&result_bytes.into()).unwrap();
566        let verifying_key = VerifyingKey::from(&signing_key);
567        assert!(verifying_key.verify_prehash(digest.as_slice(), &signature).is_ok());
568    }
569
570    #[test]
571    fn test_sign_p256_pk_too_large() {
572        // max n from https://neuromancer.sk/std/secg/secp256r1
573        let pk =
574            "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551".parse().unwrap();
575        let digest = FixedBytes::from_hex(
576            "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
577        )
578        .unwrap();
579        let result = sign_p256(&pk, &digest);
580        assert_eq!(
581            result.err().unwrap().to_string(),
582            "private key must be less than the NistP256 curve order (115792089210356248762697446949407573529996955224135760342422259061068512044369)"
583        );
584    }
585
586    #[test]
587    fn test_sign_p256_pk_0() {
588        let digest = FixedBytes::from_hex(
589            "0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad",
590        )
591        .unwrap();
592        let result = sign_p256(&U256::ZERO, &digest);
593        assert_eq!(result.err().unwrap().to_string(), "private key cannot be 0");
594    }
595
596    #[test]
597    fn test_sign_with_nonce_varies_and_recovers() {
598        // Given a fixed private key and digest
599        let pk_u256: U256 = U256::from(1u64);
600        let digest = FixedBytes::from_hex(
601            "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
602        )
603        .unwrap();
604
605        // Two distinct nonces
606        let n1: U256 = U256::from(123u64);
607        let n2: U256 = U256::from(456u64);
608
609        // Sign with both nonces
610        let sig1 = sign_with_nonce(&pk_u256, &digest, &n1).expect("sig1");
611        let sig2 = sign_with_nonce(&pk_u256, &digest, &n2).expect("sig2");
612
613        // (r,s) must differ when nonce differs
614        assert!(
615            sig1.r() != sig2.r() || sig1.s() != sig2.s(),
616            "signatures should differ with different nonces"
617        );
618
619        // ecrecover must yield the address for both signatures
620        let sk = parse_private_key(&pk_u256).unwrap();
621        let expected = alloy_signer::utils::secret_key_to_address(&sk);
622
623        assert_eq!(sig1.recover_address_from_prehash(&digest).unwrap(), expected);
624        assert_eq!(sig2.recover_address_from_prehash(&digest).unwrap(), expected);
625    }
626
627    #[test]
628    fn test_sign_with_nonce_zero_nonce_errors() {
629        // nonce = 0 should be rejected
630        let pk_u256: U256 = U256::from(1u64);
631        let digest = FixedBytes::from_hex(
632            "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
633        )
634        .unwrap();
635        let n0: U256 = U256::ZERO;
636
637        let err = sign_with_nonce(&pk_u256, &digest, &n0).unwrap_err();
638        let msg = err.to_string();
639        assert!(msg.contains("nonce cannot be 0"), "unexpected error: {msg}");
640    }
641
642    #[test]
643    fn test_sign_with_nonce_nonce_ge_order_errors() {
644        // nonce >= n should be rejected
645        use k256::Secp256k1;
646        // Curve order n as U256
647        let n_u256 = U256::from_be_slice(&Secp256k1::ORDER.to_be_byte_array());
648
649        let pk_u256: U256 = U256::from(1u64);
650        let digest = FixedBytes::from_hex(
651            "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
652        )
653        .unwrap();
654
655        // Try exactly n (>= n invalid)
656        let err = sign_with_nonce(&pk_u256, &digest, &n_u256).unwrap_err();
657        let msg = err.to_string();
658        assert!(msg.contains("invalid nonce scalar"), "unexpected error: {msg}");
659    }
660
661    #[test]
662    fn test_create_ed25519_key_determinism() {
663        let salt = B256::from([1u8; 32]);
664        let result1 = create_ed25519_key(&salt).unwrap();
665        let result2 = create_ed25519_key(&salt).unwrap();
666        assert_eq!(result1, result2, "same salt should produce same keys");
667    }
668
669    #[test]
670    fn test_create_ed25519_key_different_salts() {
671        let salt1 = B256::from([1u8; 32]);
672        let salt2 = B256::from([2u8; 32]);
673        let result1 = create_ed25519_key(&salt1).unwrap();
674        let result2 = create_ed25519_key(&salt2).unwrap();
675        assert_ne!(result1, result2, "different salts should produce different keys");
676    }
677
678    #[test]
679    fn test_public_key_ed25519_consistency() {
680        let salt = B256::from([42u8; 32]);
681        let create_result = create_ed25519_key(&salt).unwrap();
682        let (expected_public, private): (B256, B256) =
683            <(B256, B256)>::abi_decode(&create_result).unwrap();
684
685        let derived_public_result = public_key_ed25519(&private).unwrap();
686        let derived_public = B256::abi_decode(&derived_public_result).unwrap();
687
688        assert_eq!(expected_public, derived_public, "derived public key should match");
689    }
690
691    #[test]
692    fn test_sign_and_verify_ed25519_valid() {
693        let salt = B256::from([123u8; 32]);
694        let create_result = create_ed25519_key(&salt).unwrap();
695        let (public_key, private_key): (B256, B256) =
696            <(B256, B256)>::abi_decode(&create_result).unwrap();
697
698        let namespace = b"test.namespace";
699        let message = b"hello world";
700        let sig_result = sign_ed25519(namespace, message, &private_key).unwrap();
701        let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
702
703        let verify_result = verify_ed25519(&sig_bytes, namespace, message, &public_key).unwrap();
704        let valid = bool::abi_decode(&verify_result).unwrap();
705
706        assert!(valid, "signature should be valid");
707    }
708
709    #[test]
710    fn test_verify_ed25519_invalid_signature() {
711        let salt = B256::from([123u8; 32]);
712        let create_result = create_ed25519_key(&salt).unwrap();
713        let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
714
715        let invalid_sig = [0u8; 64];
716        let namespace = b"test.namespace";
717        let message = b"hello world";
718
719        let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
720        let valid = bool::abi_decode(&verify_result).unwrap();
721
722        assert!(!valid, "invalid signature should not verify");
723    }
724
725    #[test]
726    fn test_verify_ed25519_namespace_separation() {
727        let salt = B256::from([123u8; 32]);
728        let create_result = create_ed25519_key(&salt).unwrap();
729        let (public_key, private_key): (B256, B256) =
730            <(B256, B256)>::abi_decode(&create_result).unwrap();
731
732        let namespace_a = b"namespace.a";
733        let message = b"message";
734        let sig_result = sign_ed25519(namespace_a, message, &private_key).unwrap();
735        let sig_bytes: Vec<u8> = Vec::abi_decode(&sig_result).unwrap();
736
737        let namespace_b = b"namespace.b";
738        let verify_result = verify_ed25519(&sig_bytes, namespace_b, message, &public_key).unwrap();
739        let valid = bool::abi_decode(&verify_result).unwrap();
740        assert!(!valid, "signature with namespace A should not verify with namespace B");
741
742        let verify_result = verify_ed25519(&sig_bytes, namespace_a, message, &public_key).unwrap();
743        let valid = bool::abi_decode(&verify_result).unwrap();
744        assert!(valid, "signature should verify with correct namespace");
745    }
746
747    #[test]
748    fn test_verify_ed25519_invalid_signature_length() {
749        let salt = B256::from([123u8; 32]);
750        let create_result = create_ed25519_key(&salt).unwrap();
751        let (public_key, _): (B256, B256) = <(B256, B256)>::abi_decode(&create_result).unwrap();
752
753        let invalid_sig = [0u8; 32];
754        let namespace = b"test";
755        let message = b"message";
756
757        let verify_result = verify_ed25519(&invalid_sig, namespace, message, &public_key).unwrap();
758        let valid = bool::abi_decode(&verify_result).unwrap();
759        assert!(!valid, "signature with wrong length should not verify");
760    }
761}