Skip to main content

airbender_crypto/secp256k1/
mod.rs

1#![allow(long_running_const_eval)]
2#![allow(clippy::precedence)]
3
4mod context;
5pub mod field;
6pub mod hooks;
7mod points;
8mod recover;
9pub mod scalars;
10
11#[cfg(test)]
12mod test_vectors;
13
14use core::fmt::Debug;
15use core::fmt::Display;
16
17pub use context::ECMultContext;
18pub use recover::recover_with_context;
19pub use recover::recover_with_context_and_hooks;
20
21#[cfg(feature = "secp256k1-static-context")]
22pub use recover::{recover, recover_with_hooks};
23
24#[derive(Debug, PartialEq)]
25pub enum Secp256k1Err {
26    OperationOverflow,
27    InvalidParams,
28    RecoveredInfinity,
29}
30
31/// The order of the secp256k1 curve, divided by two. Signatures that should be checked according
32/// to EIP-2 should have an S value less than or equal to this.
33///
34/// `57896044618658097711785492504343953926418782139537452191302581570759080747168`
35pub const SECP256K1N_HALF: [u8; 32] = [
36    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
37    0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
38];
39
40pub const SECP256K1N_HALF_U256: ruint::aliases::U256 =
41    ruint::aliases::U256::from_be_bytes(SECP256K1N_HALF);
42
43impl Display for Secp256k1Err {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        match self {
46            Self::OperationOverflow => write!(f, "secp256k1: restoring x-coordinate overflowed"),
47            Self::InvalidParams => write!(
48                f,
49                "secp256k1: could not decompress signature to curve point"
50            ),
51            Self::RecoveredInfinity => write!(f, "secp256k1: recovered the point at infinity"),
52        }
53    }
54}
55
56#[cfg(feature = "secp256k1-static-context")]
57pub fn ecrecover_test() {
58    use crate::k256::{
59        ecdsa::{hazmat::bits2field, SigningKey},
60        elliptic_curve::{group::GroupEncoding, ops::Reduce},
61        Scalar,
62    };
63    use crate::sha3::Keccak256;
64    use crate::MiniDigest;
65    let message = "In the beginning the Universe was created.
66    This had made many people very angry and has been widely regarded as a bad move";
67    let private_key = SigningKey::from_bytes(
68        &[
69            136, 84, 181, 46, 13, 86, 203, 113, 63, 17, 137, 177, 95, 211, 104, 70, 112, 232, 200,
70            156, 225, 27, 123, 207, 243, 114, 4, 216, 148, 242, 81, 154,
71        ]
72        .into(),
73    )
74    .unwrap();
75    let digest = {
76        let mut hasher = Keccak256::new();
77        hasher.update(message);
78        let res = hasher.finalize();
79        let mut hash_bytes = [0u8; 32];
80        hash_bytes.copy_from_slice(&res);
81        hash_bytes
82    };
83
84    let public_key = private_key.verifying_key().as_affine();
85
86    let (signature, recovery_id) = private_key.sign_prehash_recoverable(&digest).unwrap();
87    let msg = <Scalar as Reduce<crate::k256::U256>>::reduce_bytes(
88        &bits2field::<crate::k256::Secp256k1>(&digest)
89            .map_err(|_| ())
90            .unwrap(),
91    );
92
93    let recovered_key = recover(&msg, &signature, &recovery_id).unwrap();
94
95    assert_eq!(recovered_key.to_bytes(), public_key.to_bytes());
96}
97
98#[cfg(test)]
99mod test {
100    use super::*;
101
102    #[test]
103    fn run_outside() {
104        ecrecover_test();
105    }
106}