ecdsa_fun/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![no_std]
4#![allow(non_snake_case)]
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9#[cfg(feature = "std")]
10#[macro_use]
11extern crate std;
12
13mod libsecp_compat;
14
15use fun::Tag;
16
17use fun::{derive_nonce, g, marker::*, nonce::NonceGen, s, Point, Scalar, G};
18pub use secp256kfun as fun;
19pub use secp256kfun::nonce;
20mod signature;
21pub use signature::Signature;
22#[cfg(feature = "adaptor")]
23#[cfg_attr(docsrs, doc(cfg(feature = "adaptor")))]
24pub mod adaptor;
25
26/// An instance of the ECDSA signature scheme.
27#[derive(Default, Clone, Debug)]
28pub struct ECDSA<NG> {
29    /// An instance of [`NonceGen`] to produce nonces.
30    ///
31    /// [`NonceGen`]: crate::nonce::NonceGen
32    pub nonce_gen: NG,
33    /// `enforce_low_s`: Whether the verify algorithm should enforce that the `s` component of the signature is low (see [BIP-146]).
34    ///
35    /// [BIP-146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#low_s
36    pub enforce_low_s: bool,
37}
38
39impl ECDSA<()> {
40    /// Creates an `ECDSA` instance that cannot be used to sign messages but can
41    /// verify signatures.
42    pub fn verify_only() -> Self {
43        ECDSA {
44            nonce_gen: (),
45            enforce_low_s: false,
46        }
47    }
48}
49
50impl<NG> ECDSA<NG> {
51    /// Creates a ECDSA instance.
52    ///
53    /// The caller chooses how nonces are generated by providing a [`NonceGen`].
54    ///
55    /// # Example
56    /// ```
57    /// use ecdsa_fun::{nonce, ECDSA};
58    /// use rand::rngs::ThreadRng;
59    /// use sha2::Sha256;
60    /// let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
61    /// let ecdsa = ECDSA::new(nonce_gen);
62    /// ```
63    ///
64    /// [`NonceGen`]: crate::nonce::NonceGen
65    pub fn new(nonce_gen: NG) -> Self
66    where
67        NG: Tag,
68    {
69        ECDSA {
70            nonce_gen: nonce_gen.tag(b"secp256kfun/ecdsa_fun"),
71            enforce_low_s: false,
72        }
73    }
74
75    /// Transforms the ECDSA instance into one which enforces the [BIP-146] low
76    /// s constraint **when verifying** (it is always low s when signing).
77    ///
78    /// *** DO NOT USE THIS IF VERIFYING BITCOIN TRANSACTIONS FROM THE CHAIN***:
79    /// [BIP-146] is only enforced for transaction relay so you can still have
80    /// valid high s signatures. This is especially true if you are using the
81    /// ECDSA adaptor signature scheme.
82    ///
83    /// [BIP-146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#low_s
84    pub fn enforce_low_s(self) -> Self {
85        ECDSA {
86            nonce_gen: self.nonce_gen,
87            enforce_low_s: true,
88        }
89    }
90}
91
92impl<NG> ECDSA<NG> {
93    /// Get the corresponding verification key for a secret key
94    ///
95    /// # Example
96    /// ```
97    /// use ecdsa_fun::{fun::Scalar, ECDSA};
98    /// let ecdsa = ECDSA::verify_only();
99    /// let secret_key = Scalar::random(&mut rand::thread_rng());
100    /// let verification_key = ecdsa.verification_key_for(&secret_key);
101    /// ```
102    pub fn verification_key_for(&self, secret_key: &Scalar) -> Point {
103        g!(secret_key * G).normalize()
104    }
105    /// Verify an ECDSA signature.
106    #[must_use]
107    pub fn verify(
108        &self,
109        verification_key: &Point<impl PointType, Public, NonZero>,
110        message: &[u8; 32],
111        signature: &Signature,
112    ) -> bool {
113        let (R_x, s) = signature.as_tuple();
114        // This ensures that there is only one valid s value per R_x for any given message.
115        if s.is_high() && self.enforce_low_s {
116            return false;
117        }
118
119        let m = Scalar::<Public, _>::from_bytes_mod_order(*message).public();
120        let s_inv = s.invert();
121
122        g!((s_inv * m) * G + (s_inv * R_x) * verification_key)
123            .non_zero()
124            .map_or(false, |implied_R| implied_R.x_eq_scalar(R_x))
125    }
126}
127
128impl<NG: NonceGen> ECDSA<NG> {
129    /// Deterministically produce a ECDSA signature on a message hash.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// use ecdsa_fun::{
135    ///     fun::{digest::Digest, prelude::*},
136    ///     nonce, ECDSA,
137    /// };
138    /// use rand::rngs::ThreadRng;
139    /// use sha2::Sha256;
140    /// let secret_key = Scalar::random(&mut rand::thread_rng());
141    /// let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
142    /// let ecdsa = ECDSA::new(nonce_gen);
143    /// let verification_key = ecdsa.verification_key_for(&secret_key);
144    /// let message_hash = {
145    ///     let message = b"Attack at dawn";
146    ///     let mut message_hash = [0u8; 32];
147    ///     let hash = Sha256::default().chain_update(message);
148    ///     message_hash.copy_from_slice(hash.finalize().as_ref());
149    ///     message_hash
150    /// };
151    /// let signature = ecdsa.sign(&secret_key, &message_hash);
152    /// assert!(ecdsa.verify(&verification_key, &message_hash, &signature));
153    /// ```
154    pub fn sign(&self, secret_key: &Scalar, message_hash: &[u8; 32]) -> Signature {
155        let x = secret_key;
156        let m = Scalar::<Public, _>::from_bytes_mod_order(*message_hash).public();
157        let r = derive_nonce!(
158            nonce_gen => self.nonce_gen,
159            secret => x,
160            public => [&message_hash[..]]
161        );
162        let R = g!(r * G).normalize(); // Must be normal so we can get x-coordinate
163
164        // This coverts R is its x-coordinate mod q. This acts as a kind of poor
165        // man's version of the Fiat-Shamir challenge in a Schnorr
166        // signature. The lack of any known algebraic relationship between r and
167        // R_x is what makes ECDSA signatures difficult to forge.
168        let R_x = Scalar::<Public, _>::from_bytes_mod_order(R.to_xonly_bytes())
169            // There *is* a single point that will be zero here but since we're
170            // choosing R pseudorandomly it won't occur.
171            .public()
172            .non_zero()
173            .expect("computationally unreachable");
174
175        let mut s = s!((m + R_x * x) / r)
176            // Given R_x is determined by x and m through a hash, reaching
177            // (m + R_x * x) = 0 is intractable.
178            .non_zero()
179            .expect("computationally unreachable")
180            .public();
181
182        // s values must be low (less than half group order), otherwise signatures
183        // would be malleable i.e. (R,s) and (R,-s) would both be valid signatures.
184        s.conditional_negate(s.is_high());
185
186        Signature { R_x, s }
187    }
188}
189
190#[macro_export]
191#[doc(hidden)]
192macro_rules! test_instance {
193    () => {
194        $crate::ECDSA::new($crate::nonce::Deterministic::<sha2::Sha256>::default())
195    };
196}
197
198#[cfg(test)]
199mod test {
200    use super::*;
201    use rand::RngCore;
202
203    #[test]
204    fn repeated_sign_and_verify() {
205        let ecdsa = test_instance!();
206        for _ in 0..20 {
207            let mut message = [0u8; 32];
208            rand::thread_rng().fill_bytes(&mut message);
209            let secret_key = Scalar::random(&mut rand::thread_rng());
210            let public_key = g!(secret_key * G).normalize();
211            let sig = ecdsa.sign(&secret_key, &message);
212            assert!(ecdsa.verify(&public_key, &message, &sig))
213        }
214    }
215
216    #[test]
217    fn low_s() {
218        let ecdsa_enforce_low_s = test_instance!().enforce_low_s();
219        let ecdsa = test_instance!();
220        // TODO: use proptest
221        for _ in 0..20 {
222            let mut message = [0u8; 32];
223            rand::thread_rng().fill_bytes(&mut message);
224            let secret_key = Scalar::random(&mut rand::thread_rng());
225            let public_key = ecdsa.verification_key_for(&secret_key);
226            let mut sig = ecdsa.sign(&secret_key, &message);
227            assert!(ecdsa.verify(&public_key, &message, &sig));
228            assert!(ecdsa_enforce_low_s.verify(&public_key, &message, &sig));
229            sig.s = -sig.s;
230            assert!(!ecdsa_enforce_low_s.verify(&public_key, &message, &sig));
231            assert!(ecdsa.verify(&public_key, &message, &sig));
232        }
233    }
234}