schnorr_signatures/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5use core::ops::Deref;
6#[cfg(not(feature = "std"))]
7#[macro_use]
8extern crate alloc;
9use std_shims::{
10  vec::Vec,
11  io::{self, Read, Write},
12};
13
14use rand_core::{RngCore, CryptoRng};
15
16use zeroize::{Zeroize, Zeroizing};
17
18use ciphersuite::{
19  group::{
20    ff::{Field, PrimeField},
21    Group, GroupEncoding,
22  },
23  Ciphersuite,
24};
25use multiexp::{multiexp_vartime, BatchVerifier};
26
27/// Half-aggregation from <https://eprint.iacr.org/2021/350>.
28#[cfg(feature = "aggregate")]
29pub mod aggregate;
30
31#[cfg(test)]
32mod tests;
33
34/// A Schnorr signature of the form (R, s) where s = r + cx.
35///
36/// These are intended to be strict. It is generic over Ciphersuite which is for PrimeGroups,
37/// and mandates canonical encodings in its read function.
38///
39/// RFC 8032 has an alternative verification formula, 8R = 8s - 8cX, which is intended to handle
40/// torsioned nonces/public keys. Due to this library's strict requirements, such signatures will
41/// not be verifiable with this library.
42#[allow(non_snake_case)]
43#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
44pub struct SchnorrSignature<C: Ciphersuite> {
45  pub R: C::G,
46  pub s: C::F,
47}
48
49impl<C: Ciphersuite> SchnorrSignature<C> {
50  /// Read a SchnorrSignature from something implementing Read.
51  pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
52    Ok(SchnorrSignature { R: C::read_G(reader)?, s: C::read_F(reader)? })
53  }
54
55  /// Write a SchnorrSignature to something implementing Read.
56  pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
57    writer.write_all(self.R.to_bytes().as_ref())?;
58    writer.write_all(self.s.to_repr().as_ref())
59  }
60
61  /// Serialize a SchnorrSignature, returning a `Vec<u8>`.
62  pub fn serialize(&self) -> Vec<u8> {
63    let mut buf = vec![];
64    self.write(&mut buf).unwrap();
65    buf
66  }
67
68  /// Sign a Schnorr signature with the given nonce for the specified challenge.
69  ///
70  /// This challenge must be properly crafted, which means being binding to the public key, nonce,
71  /// and any message. Failure to do so will let a malicious adversary to forge signatures for
72  /// different keys/messages.
73  #[allow(clippy::needless_pass_by_value)] // Prevents further-use of this single-use value
74  pub fn sign(
75    private_key: &Zeroizing<C::F>,
76    nonce: Zeroizing<C::F>,
77    challenge: C::F,
78  ) -> SchnorrSignature<C> {
79    SchnorrSignature {
80      // Uses deref instead of * as * returns C::F yet deref returns &C::F, preventing a copy
81      R: C::generator() * nonce.deref(),
82      s: (challenge * private_key.deref()) + nonce.deref(),
83    }
84  }
85
86  /// Return the series of pairs whose products sum to zero for a valid signature.
87  /// This is intended to be used with a multiexp.
88  pub fn batch_statements(&self, public_key: C::G, challenge: C::F) -> [(C::F, C::G); 3] {
89    // s = r + ca
90    // sG == R + cA
91    // R + cA - sG == 0
92    [
93      // R
94      (C::F::ONE, self.R),
95      // cA
96      (challenge, public_key),
97      // -sG
98      (-self.s, C::generator()),
99    ]
100  }
101
102  /// Verify a Schnorr signature for the given key with the specified challenge.
103  ///
104  /// This challenge must be properly crafted, which means being binding to the public key, nonce,
105  /// and any message. Failure to do so will let a malicious adversary to forge signatures for
106  /// different keys/messages.
107  #[must_use]
108  pub fn verify(&self, public_key: C::G, challenge: C::F) -> bool {
109    multiexp_vartime(&self.batch_statements(public_key, challenge)).is_identity().into()
110  }
111
112  /// Queue a signature for batch verification.
113  ///
114  /// This challenge must be properly crafted, which means being binding to the public key, nonce,
115  /// and any message. Failure to do so will let a malicious adversary to forge signatures for
116  /// different keys/messages.
117  pub fn batch_verify<R: RngCore + CryptoRng, I: Copy + Zeroize>(
118    &self,
119    rng: &mut R,
120    batch: &mut BatchVerifier<I, C::G>,
121    id: I,
122    public_key: C::G,
123    challenge: C::F,
124  ) {
125    batch.queue(rng, id, self.batch_statements(public_key, challenge));
126  }
127}