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