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}