Skip to main content

ic_ec/nist/
ecdsa.rs

1//! FIPS 186-5 ECDSA over the NIST prime curves, with RFC 6979 nonces.
2//!
3//! # Deterministic nonces
4//!
5//! ECDSA's notorious failure mode is the signing nonce `k`: reuse it across two
6//! signatures, or let an attacker predict a few bits, and the private key falls
7//! out by simple algebra. This has broken real systems repeatedly (the PS3, and
8//! several Bitcoin wallets).
9//!
10//! Signing here derives `k` deterministically from the private key and the
11//! message, per [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979). There is no
12//! RNG in the signing path, so there is no entropy failure that can produce a
13//! repeated nonce — the same class of protection Ed25519 gets by construction.
14//! It also makes signatures reproducible, which is why the RFC's published
15//! vectors serve as known-answer tests for the whole stack beneath.
16//!
17//! # Pairing a curve with a hash
18//!
19//! Each instantiation fixes the hash at the curve's security level: SHA-256
20//! with P-256, SHA-384 with P-384. For both, the hash output and the group
21//! order are the same width, so RFC 6979's bit-length juggling collapses to a
22//! straight reduction.
23
24use super::arith::Field;
25use super::point::{AffinePoint, Curve, Point};
26use ic_core::traits::{Digest, Mac};
27use ic_core::{ensure, Result, Zeroize};
28
29/// A curve paired with the hash and MAC its signatures use.
30pub trait EcdsaCurve: Curve + crate::nist::gentable::HasGeneratorTable {
31    /// The message digest, at the curve's security level.
32    type Digest: Digest;
33    /// HMAC over the same digest, for RFC 6979.
34    type Hmac: Mac;
35}
36
37/// Widest scalar this module handles, for stack buffers.
38const MAX_SCALAR: usize = 66;
39
40/// Widest `T` accumulator RFC 6979 can need, in bytes.
41///
42/// `T` grows by one HMAC output per round until it covers `qlen` bits. The
43/// worst case here is P-521: HMAC-SHA-512 gives 512 bits a round and the order
44/// is 521, so it takes two rounds and 128 bytes.
45const MAX_T: usize = 128;
46
47/// RFC 6979 `bits2int`: keep the leftmost `order_bits` bits of `t`.
48///
49/// When `t` is at least as wide as the order, that means taking the first
50/// `ceil(order_bits / 8)` bytes and shifting right by however many bits the
51/// byte boundary overshot. For P-256 and P-384 the overshoot is zero and this
52/// is a straight copy; for P-521 it is seven bits, and skipping the shift would
53/// produce nonces that are wrong by a factor of 128 — which still verifies
54/// against itself and against nothing else.
55fn bits2int(t: &[u8], order_bits: usize, out: &mut [u8]) {
56    let m = order_bits.div_ceil(8);
57    let shift = m * 8 - order_bits;
58    for i in 0..m {
59        let hi = if i == 0 { 0 } else { t[i - 1] };
60        let carry = if shift == 0 { 0 } else { hi << (8 - shift) };
61        out[i] = (t[i] >> shift) | carry;
62    }
63}
64
65/// Derive the RFC 6979 nonce for a given attempt.
66///
67/// The `K`/`V` state machine is run from scratch and advanced `attempt` times,
68/// so a rejected candidate (`r == 0` or `s == 0`) moves to the next one exactly
69/// as the RFC specifies. Those rejections have negligible probability, so the
70/// loop exists for correctness rather than speed.
71fn rfc6979_nonce<C: EcdsaCurve>(
72    private_key: &[u8],
73    h1: &[u8],
74    attempt: usize,
75) -> Result<C::Scalar> {
76    let n = C::SCALAR_BYTES;
77    ensure!(n <= MAX_SCALAR, InvalidParameter, "scalar too wide");
78
79    // bits2octets(h1): reduce the hash modulo n, then re-encode.
80    let e = C::scalar_reduce_slice(h1);
81    let e_octets = e.to_bytes();
82    let e_octets = e_octets.as_ref();
83
84    let tag_len = <C::Hmac as Mac>::TAG_LEN;
85    let mut v_buf = [0x01u8; MAX_SCALAR];
86    let mut k_buf = [0x00u8; MAX_SCALAR];
87    let v = &mut v_buf[..tag_len];
88    let k = &mut k_buf[..tag_len];
89
90    // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1))
91    let mut mac = C::Hmac::new(k)?;
92    mac.update(v);
93    mac.update(&[0x00]);
94    mac.update(private_key);
95    mac.update(e_octets);
96    k.copy_from_slice(mac.finalize().as_ref());
97    let t = C::Hmac::mac(k, v)?;
98    v.copy_from_slice(t.as_ref());
99
100    // K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1))
101    let mut mac = C::Hmac::new(k)?;
102    mac.update(v);
103    mac.update(&[0x01]);
104    mac.update(private_key);
105    mac.update(e_octets);
106    k.copy_from_slice(mac.finalize().as_ref());
107    let t = C::Hmac::mac(k, v)?;
108    v.copy_from_slice(t.as_ref());
109
110    let mut found = 0usize;
111    let mut t_buf = [0u8; MAX_T];
112    let mut candidate_buf = [0u8; MAX_SCALAR];
113
114    // Bounded so a pathological key cannot spin forever.
115    for _ in 0..(attempt + 1) * 8 + 16 {
116        // RFC 6979 3.2 step h: T = T || HMAC_K(V) until T covers qlen bits,
117        // then k = bits2int(T). One round is enough for P-256 and P-384, where
118        // the HMAC output is exactly the order width; P-521 needs two, because
119        // SHA-512 gives 512 bits against a 521-bit order.
120        let mut tlen = 0usize;
121        while tlen * 8 < C::ORDER_BITS {
122            let block = C::Hmac::mac(k, v)?;
123            v.copy_from_slice(block.as_ref());
124            let take = core::cmp::min(tag_len, MAX_T - tlen);
125            t_buf[tlen..tlen + take].copy_from_slice(&v[..take]);
126            tlen += take;
127        }
128        bits2int(&t_buf[..tlen], C::ORDER_BITS, &mut candidate_buf);
129
130        // Accept only a canonical, non-zero scalar.
131        if let Some(candidate) = C::scalar_from_slice(&candidate_buf[..n]) {
132            if !bool::from(candidate.is_zero()) {
133                if found == attempt {
134                    k_buf.zeroize();
135                    v_buf.zeroize();
136                    t_buf.zeroize();
137                    candidate_buf.zeroize();
138                    return Ok(candidate);
139                }
140                found += 1;
141            }
142        }
143
144        // K = HMAC_K(V || 0x00); V = HMAC_K(V)
145        let mut mac = C::Hmac::new(k)?;
146        mac.update(v);
147        mac.update(&[0x00]);
148        k.copy_from_slice(mac.finalize().as_ref());
149        let t = C::Hmac::mac(k, v)?;
150        v.copy_from_slice(t.as_ref());
151    }
152
153    k_buf.zeroize();
154    v_buf.zeroize();
155    t_buf.zeroize();
156    candidate_buf.zeroize();
157    Err(ic_core::err!(
158        Internal,
159        "rfc6979 nonce generation did not converge"
160    ))
161}
162
163/// Load a private key, rejecting zero and anything at or above `n`.
164fn load_private_key<C: Curve>(bytes: &[u8]) -> Result<C::Scalar> {
165    ensure!(
166        bytes.len() == C::SCALAR_BYTES,
167        InvalidLength,
168        "ecdsa private key"
169    );
170    let d = C::scalar_from_slice(bytes).ok_or(ic_core::err!(
171        InvalidParameter,
172        "ecdsa private key is not less than n"
173    ))?;
174    ensure!(
175        !bool::from(d.is_zero()),
176        InvalidParameter,
177        "ecdsa private key must not be zero"
178    );
179    Ok(d)
180}
181
182/// Compute the public key, SEC1 uncompressed.
183pub fn public_key<C: EcdsaCurve>(private_key: &[u8], out: &mut [u8]) -> Result<()> {
184    ic_core::module::operational()?;
185    ensure!(
186        out.len() == 1 + 2 * C::FIELD_BYTES,
187        InvalidLength,
188        "ecdsa public key buffer"
189    );
190    let d = load_private_key::<C>(private_key)?;
191    let q = Point::<C>::mul_generator(&d)
192        .to_affine()
193        .ok_or(ic_core::err!(Internal, "public key is the identity"))?;
194    // See the note in `ecdh::public_key`: the length check above and the
195    // encoder must agree, and this is what says so if they stop.
196    ensure!(
197        q.write_uncompressed(out),
198        Internal,
199        "public key buffer length disagrees with the encoder"
200    );
201    Ok(())
202}
203
204/// Compute the public key, SEC1 compressed.
205pub fn public_key_compressed<C: EcdsaCurve>(private_key: &[u8], out: &mut [u8]) -> Result<()> {
206    ic_core::module::operational()?;
207    ensure!(
208        out.len() == 1 + C::FIELD_BYTES,
209        InvalidLength,
210        "ecdsa compressed key buffer"
211    );
212    let d = load_private_key::<C>(private_key)?;
213    let q = Point::<C>::mul_generator(&d)
214        .to_affine()
215        .ok_or(ic_core::err!(Internal, "public key is the identity"))?;
216    ensure!(
217        q.write_compressed(out),
218        Internal,
219        "compressed key buffer length disagrees with the encoder"
220    );
221    Ok(())
222}
223
224/// Sign `message`, writing fixed-width `r || s`.
225pub fn sign<C: EcdsaCurve>(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
226    ic_core::module::operational()?;
227    let n = C::SCALAR_BYTES;
228    ensure!(
229        signature.len() == 2 * n,
230        InvalidLength,
231        "ecdsa signature buffer"
232    );
233    let d = load_private_key::<C>(private_key)?;
234
235    let digest = C::Digest::digest(message);
236    let h1 = digest.as_ref();
237    let e = C::scalar_reduce_slice(h1);
238
239    // Both rejections below have negligible probability; the loop is here so
240    // that "negligible" never becomes "silently wrong".
241    for attempt in 0..8 {
242        let k = rfc6979_nonce::<C>(private_key, h1, attempt)?;
243
244        let point = Point::<C>::mul_generator(&k)
245            .to_affine()
246            .ok_or(ic_core::err!(Internal, "kG is the identity"))?;
247        let r = C::scalar_reduce_slice(point.x.to_bytes().as_ref());
248        if bool::from(r.is_zero()) {
249            continue;
250        }
251
252        // s = k^-1 (e + r*d)
253        let s = k.invert().mul(&e.add(&r.mul(&d)));
254        if bool::from(s.is_zero()) {
255            continue;
256        }
257
258        signature[..n].copy_from_slice(r.to_bytes().as_ref());
259        signature[n..].copy_from_slice(s.to_bytes().as_ref());
260        return Ok(());
261    }
262
263    Err(ic_core::err!(Internal, "ecdsa signing did not converge"))
264}
265
266/// Verify a fixed-width `r || s` signature.
267pub fn verify<C: EcdsaCurve>(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
268    ic_core::module::operational()?;
269    let digest = C::Digest::digest(message);
270    verify_digest::<C>(public_key, digest.as_ref(), signature)
271}
272
273/// Digest widths `verify_prehash` accepts, on every curve (for instance
274/// [`crate::p256::EcdsaP256Sha256::verify_prehash`]): those of SHA-224, SHA-256,
275/// SHA-384 and SHA-512, and of SHA-3 and SHA-512/t at the same widths.
276///
277/// SHA-1's 20 bytes are not among them. This library implements no SHA-1 and
278/// registers it as excluded, and accepting its width here would make this the
279/// one way to verify a signature over it.
280pub const PREHASH_LENS: [usize; 4] = [28, 32, 48, 64];
281
282/// Verify a fixed-width `r || s` signature over a digest the caller computed.
283///
284/// For signatures made with a hash other than the curve's own, which X.509 and
285/// other protocols allow: P-384 with SHA-512, P-256 with SHA-384. The digest is
286/// turned into the scalar `e` as FIPS 186-5 section 6.4.2 specifies, by taking
287/// its leftmost bits up to the width of the group order; for every width in
288/// [`PREHASH_LENS`] that is the leftmost bytes, which `scalar_reduce_slice`
289/// takes.
290///
291/// The digest must also be at least as wide as the curve's strength demands:
292/// 32 bytes on P-256, 48 on P-384, 64 on P-521. A narrower one would make the
293/// hash the weakest part of the signature -- SHA-224 under P-521 is 112-bit
294/// collision resistance under a 256-bit key -- and SP 800-57 asks for a hash at
295/// least as strong as the key. That rule is enforced here rather than left to
296/// the caller, because the digest's width is the one thing about it this
297/// function can see.
298///
299/// Which hash produced the digest is still the caller's to establish: a
300/// 64-byte BLAKE2b digest is as wide as SHA-512's, and nothing here can tell
301/// them apart.
302pub fn verify_prehash<C: EcdsaCurve>(
303    public_key: &[u8],
304    digest: &[u8],
305    signature: &[u8],
306) -> Result<()> {
307    ic_core::module::operational()?;
308    ensure!(
309        PREHASH_LENS.contains(&digest.len()),
310        InvalidLength,
311        "ecdsa digest must be 28, 32, 48 or 64 bytes"
312    );
313    ensure!(
314        digest.len() >= min_prehash_len::<C>(),
315        InvalidLength,
316        "ecdsa digest narrower than the curve's strength"
317    );
318    verify_digest::<C>(public_key, digest, signature)
319}
320
321/// The narrowest digest `verify_prehash` accepts on `C`: as wide as the
322/// order, or SHA-512's 64 bytes where the order is wider (P-521).
323pub fn min_prehash_len<C: EcdsaCurve>() -> usize {
324    core::cmp::min(C::SCALAR_BYTES, 64)
325}
326
327/// The verification both entry points share, from the digest onwards.
328fn verify_digest<C: EcdsaCurve>(public_key: &[u8], digest: &[u8], signature: &[u8]) -> Result<()> {
329    let n = C::SCALAR_BYTES;
330    ensure!(signature.len() == 2 * n, InvalidLength, "ecdsa signature");
331
332    let q = AffinePoint::<C>::from_sec1(public_key).ok_or(ic_core::err!(
333        MalformedEncoding,
334        "ecdsa public key is not a curve point"
335    ))?;
336
337    // r and s must both be canonical and non-zero. A non-canonical encoding is
338    // rejected rather than reduced, so a signature has exactly one valid byte
339    // representation.
340    let r = C::scalar_from_slice(&signature[..n]).ok_or(ic_core::err!(
341        MalformedEncoding,
342        "ecdsa r is not less than n"
343    ))?;
344    let s = C::scalar_from_slice(&signature[n..]).ok_or(ic_core::err!(
345        MalformedEncoding,
346        "ecdsa s is not less than n"
347    ))?;
348    ensure!(
349        !bool::from(r.is_zero()) && !bool::from(s.is_zero()),
350        MalformedEncoding,
351        "ecdsa r and s must be non-zero"
352    );
353
354    let e = C::scalar_reduce_slice(digest);
355
356    let w = s.invert();
357    let u1 = e.mul(&w);
358    let u2 = r.mul(&w);
359
360    let point = Point::<C>::mul_double(&u1, &Point::<C>::from_affine(&q), &u2);
361    let affine = point
362        .to_affine()
363        .ok_or(ic_core::err!(AuthenticationFailed, "ecdsa"))?;
364
365    let v = C::scalar_reduce_slice(affine.x.to_bytes().as_ref());
366    if bool::from(v.ct_eq(&r)) {
367        Ok(())
368    } else {
369        Err(ic_core::err!(AuthenticationFailed, "ecdsa"))
370    }
371}
372
373/// Whether `s` is above `n/2`.
374fn is_high_s<C: Curve>(s: &C::Scalar) -> bool {
375    let bytes = s.to_bytes();
376    let bytes = bytes.as_ref();
377
378    // n/2, by shifting the modulus encoding right one bit.
379    let n_bytes = C::Scalar::ZERO.sub(&C::Scalar::ONE).to_bytes();
380    let n_bytes = n_bytes.as_ref();
381    // `n - 1` encoded; adding one back gives n, but the top bit of n/2 is what
382    // matters and n is odd, so (n-1)/2 == n/2 rounded down.
383    let mut half = [0u8; MAX_SCALAR];
384    let len = n_bytes.len();
385    let mut carry = 0u8;
386    for i in 0..len {
387        let v = n_bytes[i];
388        half[i] = (v >> 1) | (carry << 7);
389        carry = v & 1;
390    }
391
392    // Compare big-endian, most significant byte first. Both operands are public
393    // wherever this is called.
394    for i in 0..len {
395        if bytes[i] != half[i] {
396            return bytes[i] > half[i];
397        }
398    }
399    false
400}
401
402/// Rewrite a signature to its low-`s` form, if it is not already.
403///
404/// ECDSA is malleable: `(r, s)` and `(r, n - s)` are both valid for the same
405/// message, so a signature is not a unique identifier unless one form is
406/// chosen. FIPS 186-5 and RFC 6979 accept both, and this library signs and
407/// verifies per the standard, so normalization is offered rather than imposed —
408/// apply it when a signature doubles as a database key or a transaction id.
409pub fn normalize_s<C: EcdsaCurve>(signature: &mut [u8]) -> Result<()> {
410    let n = C::SCALAR_BYTES;
411    ensure!(signature.len() == 2 * n, InvalidLength, "ecdsa signature");
412    let s = C::scalar_from_slice(&signature[n..]).ok_or(ic_core::err!(
413        MalformedEncoding,
414        "ecdsa s is not less than n"
415    ))?;
416    if is_high_s::<C>(&s) {
417        let flipped = C::Scalar::ZERO.sub(&s);
418        signature[n..].copy_from_slice(flipped.to_bytes().as_ref());
419    }
420    Ok(())
421}
422
423/// Whether a signature is already in low-`s` form.
424pub fn has_low_s<C: EcdsaCurve>(signature: &[u8]) -> Result<bool> {
425    let n = C::SCALAR_BYTES;
426    ensure!(signature.len() == 2 * n, InvalidLength, "ecdsa signature");
427    let s = C::scalar_from_slice(&signature[n..]).ok_or(ic_core::err!(
428        MalformedEncoding,
429        "ecdsa s is not less than n"
430    ))?;
431    Ok(!is_high_s::<C>(&s))
432}