schnorr_evm/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2pub use k256;
3use k256::elliptic_curve::group::prime::PrimeCurveAffine;
4use k256::elliptic_curve::point::AffineCoordinates;
5use k256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
6use k256::elliptic_curve::PrimeField;
7use k256::{AffinePoint, EncodedPoint, NonZeroScalar, ProjectivePoint, Scalar};
8use rand_core::{CryptoRng, RngCore};
9use sha3::Digest;
10
11pub mod proof_of_knowledge;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum Error {
15    InvalidSecretKey,
16    InvalidPublicKey,
17    InvalidSignature,
18}
19
20impl core::fmt::Display for Error {
21    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
22        match self {
23            Self::InvalidSecretKey => write!(f, "invalid secret key"),
24            Self::InvalidPublicKey => write!(f, "invalid public key"),
25            Self::InvalidSignature => write!(f, "invalid signature"),
26        }
27    }
28}
29
30#[cfg(feature = "std")]
31impl std::error::Error for Error {}
32
33pub type Result<T, E = Error> = core::result::Result<T, E>;
34
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct Signature {
37    pub e: Scalar,
38    pub z: Scalar,
39}
40
41impl Signature {
42    pub fn new(e: Scalar, z: Scalar) -> Self {
43        Self { e, z }
44    }
45
46    pub fn to_bytes(&self) -> [u8; 64] {
47        let mut bytes = [0; 64];
48        bytes[..32].copy_from_slice(&self.e.to_bytes());
49        bytes[32..].copy_from_slice(&self.z.to_bytes());
50        bytes
51    }
52
53    pub fn from_bytes(bytes: [u8; 64]) -> Result<Self> {
54        let e: &k256::FieldBytes = bytes[..32].try_into().unwrap();
55        let e = Option::from(Scalar::from_repr(*e)).ok_or(Error::InvalidSignature)?;
56        let z: &k256::FieldBytes = bytes[32..].try_into().unwrap();
57        let z = Option::from(Scalar::from_repr(*z)).ok_or(Error::InvalidSignature)?;
58        Ok(Self { e, z })
59    }
60}
61
62#[cfg(feature = "serde")]
63impl serde::Serialize for Signature {
64    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65    where
66        S: serde::Serializer,
67    {
68        serializer.serialize_bytes(self.to_bytes().as_ref())
69    }
70}
71
72#[cfg(feature = "serde")]
73impl<'de> serde::Deserialize<'de> for Signature {
74    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75    where
76        D: serde::Deserializer<'de>,
77    {
78        let bytes = Vec::<u8>::deserialize(deserializer)?;
79        let array = bytes
80            .try_into()
81            .map_err(|_| serde::de::Error::custom("invalid byte length"))?;
82        let signature =
83            Self::from_bytes(array).map_err(|err| serde::de::Error::custom(format!("{err}")))?;
84        Ok(signature)
85    }
86}
87
88#[derive(Clone, Copy)]
89pub struct SigningKey {
90    scalar: NonZeroScalar,
91}
92
93impl SigningKey {
94    pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
95        Self {
96            scalar: NonZeroScalar::random(rng),
97        }
98    }
99
100    #[cfg(feature = "std")]
101    pub fn random() -> Self {
102        Self::new(&mut rand_core::OsRng)
103    }
104
105    pub fn to_scalar(&self) -> NonZeroScalar {
106        self.scalar
107    }
108
109    pub fn to_bytes(&self) -> [u8; 32] {
110        self.scalar.to_bytes().into()
111    }
112
113    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self> {
114        let scalar =
115            Option::from(NonZeroScalar::from_repr(bytes.into())).ok_or(Error::InvalidSecretKey)?;
116        Ok(Self { scalar })
117    }
118
119    pub fn public(&self) -> VerifyingKey {
120        VerifyingKey::new(AffinePoint::GENERATOR * self.scalar.as_ref())
121    }
122
123    pub fn sign_prehashed_with_rng<R: RngCore + CryptoRng>(
124        &self,
125        rng: &mut R,
126        hash: [u8; 32],
127    ) -> Signature {
128        let k = NonZeroScalar::random(rng);
129        let r = AffinePoint::GENERATOR * k.as_ref();
130        let public = self.public();
131        let c = public.challenge(hash, r);
132        let z = k.as_ref() + c * self.scalar.as_ref();
133        Signature::new(c, z)
134    }
135
136    #[cfg(feature = "std")]
137    pub fn sign_prehashed(&self, hash: [u8; 32]) -> Signature {
138        self.sign_prehashed_with_rng(&mut rand_core::OsRng, hash)
139    }
140
141    #[cfg(feature = "std")]
142    pub fn sign(&self, msg: &[u8]) -> Signature {
143        let hash = VerifyingKey::message_hash(msg);
144        self.sign_prehashed(hash)
145    }
146}
147
148#[derive(Clone, Copy, Debug, PartialEq)]
149pub struct VerifyingKey {
150    element: ProjectivePoint,
151}
152
153impl VerifyingKey {
154    pub fn new(element: ProjectivePoint) -> Self {
155        Self { element }
156    }
157
158    pub fn from_bytes(bytes: [u8; 33]) -> Result<Self, Error> {
159        let point = EncodedPoint::from_bytes(bytes).map_err(|_| Error::InvalidPublicKey)?;
160        let point: AffinePoint =
161            Option::from(AffinePoint::from_encoded_point(&point)).ok_or(Error::InvalidPublicKey)?;
162        if point.is_identity().into() {
163            return Err(Error::InvalidPublicKey);
164        }
165        Ok(Self::new(ProjectivePoint::from(point)))
166    }
167
168    pub fn to_bytes(self) -> Result<[u8; 33], Error> {
169        let point = self.to_affine().to_encoded_point(true);
170        // can only happen if we encode the identity
171        point
172            .as_bytes()
173            .try_into()
174            .map_err(|_| Error::InvalidPublicKey)
175    }
176
177    pub fn to_element(self) -> ProjectivePoint {
178        self.element
179    }
180
181    fn to_affine(self) -> AffinePoint {
182        self.element.to_affine()
183    }
184
185    pub fn to_px_parity(self) -> ([u8; 32], u8) {
186        let affine = self.to_affine();
187        (affine.x().into(), affine.y_is_odd().unwrap_u8() + 27)
188    }
189
190    pub fn message_hash(message: &[u8]) -> [u8; 32] {
191        sha3::Keccak256::digest(message).into()
192    }
193
194    pub fn challenge(self, message_hash: [u8; 32], r: ProjectivePoint) -> Scalar {
195        let uncompressed = r.to_affine().to_encoded_point(false);
196        let digest = sha3::Keccak256::digest(&uncompressed.as_bytes()[1..]);
197        let address_r: [u8; 20] = digest[12..].try_into().unwrap();
198
199        let (pubkey_x, pubkey_y_parity) = self.to_px_parity();
200        let mut e_hasher = sha3::Keccak256::new();
201        e_hasher.update(address_r);
202        e_hasher.update([pubkey_y_parity]);
203        e_hasher.update(pubkey_x);
204        e_hasher.update(message_hash);
205        Scalar::from_repr(e_hasher.finalize()).unwrap()
206    }
207
208    pub fn verify_prehashed(
209        self,
210        message_hash: [u8; 32],
211        signature: &Signature,
212    ) -> Result<(), Error> {
213        let r = AffinePoint::GENERATOR * signature.z - self.element * signature.e;
214        let ep = self.challenge(message_hash, r);
215        if signature.e != ep {
216            return Err(Error::InvalidSignature);
217        }
218        Ok(())
219    }
220
221    pub fn verify(self, message: &[u8], signature: &Signature) -> Result<(), Error> {
222        self.verify_prehashed(Self::message_hash(message), signature)
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    #[test]
231    fn test_sign() {
232        let key = SigningKey::random();
233        let public = key.public();
234        let sig = key.sign(b"hello world");
235        public.verify(b"hello world", &sig).unwrap();
236    }
237}