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