1extern crate alloc;
8use alloc::{boxed::Box, string::String};
9
10use crate::{BaseField, CurvePoint, Hashable, Keypair, PubKey, ScalarField, Signature, Signer};
11use ark_ec::{
12 AffineRepr, CurveGroup,
14};
15use ark_ff::{
16 BigInteger, Field, PrimeField, Zero,
20};
21use blake2::{
22 digest::{Update, VariableOutput},
23 Blake2bVar,
24};
25use core::ops::{Add, Neg};
26use mina_hasher::{self, DomainParameter, Hasher, ROInput};
27
28pub struct Schnorr<H: Hashable> {
32 hasher: Box<dyn Hasher<Message<H>>>,
33 domain_param: H::D,
34}
35
36#[derive(Clone)]
37struct Message<H: Hashable> {
38 input: H,
39 pub_key_x: BaseField,
40 pub_key_y: BaseField,
41 rx: BaseField,
42}
43
44impl<H: Hashable> Hashable for Message<H> {
45 type D = H::D;
46
47 fn to_roinput(&self) -> ROInput {
48 self.input
49 .to_roinput()
50 .append_field(self.pub_key_x)
51 .append_field(self.pub_key_y)
52 .append_field(self.rx)
53 }
54
55 fn domain_string(domain_param: Self::D) -> Option<String> {
56 H::domain_string(domain_param)
57 }
58}
59
60impl<H: 'static + Hashable> Signer<H> for Schnorr<H> {
61 fn sign(&mut self, kp: &Keypair, input: &H) -> Signature {
62 let k: ScalarField = self.derive_nonce(kp, input);
63 let r: CurvePoint = CurvePoint::generator()
64 .mul_bigint(k.into_bigint())
65 .into_affine();
66 let k: ScalarField = if r.y.into_bigint().is_even() { k } else { -k };
67
68 let e: ScalarField = self.message_hash(&kp.public, r.x, input);
69 let s: ScalarField = k + e * kp.secret.scalar();
70
71 Signature::new(r.x, s)
72 }
73
74 fn verify(&mut self, sig: &Signature, public: &PubKey, input: &H) -> bool {
75 let ev: ScalarField = self.message_hash(public, sig.rx, input);
76
77 let sv = CurvePoint::generator()
78 .mul_bigint(sig.s.into_bigint())
79 .into_affine();
80 let rv = public.point().mul_bigint(ev.into_bigint()).neg().add(sv);
82
83 if rv.is_zero() {
84 return false;
85 }
86
87 let rv = rv.into_affine();
88
89 rv.y.into_bigint().is_even() && rv.x == sig.rx
90 }
91}
92
93pub(crate) fn create_legacy<H: 'static + Hashable>(domain_param: H::D) -> impl Signer<H> {
94 Schnorr::<H> {
95 hasher: Box::new(mina_hasher::create_legacy::<Message<H>>(
96 domain_param.clone(),
97 )),
98 domain_param,
99 }
100}
101
102pub(crate) fn create_kimchi<H: 'static + Hashable>(domain_param: H::D) -> impl Signer<H> {
103 Schnorr::<H> {
104 hasher: Box::new(mina_hasher::create_kimchi::<Message<H>>(
105 domain_param.clone(),
106 )),
107 domain_param,
108 }
109}
110
111impl<H: 'static + Hashable> Schnorr<H> {
112 fn derive_nonce(&self, kp: &Keypair, input: &H) -> ScalarField {
116 let mut blake_hasher = Blake2bVar::new(32).unwrap();
117
118 let roi = input
119 .to_roinput()
120 .append_field(kp.public.point().x)
121 .append_field(kp.public.point().y)
122 .append_scalar(*kp.secret.scalar())
123 .append_bytes(&self.domain_param.clone().into_bytes());
124
125 blake_hasher.update(&roi.to_bytes());
126
127 let mut bytes = [0; 32];
128 blake_hasher
129 .finalize_variable(&mut bytes)
130 .expect("incorrect output size");
131 bytes[bytes.len() - 1] &= 0b0011_1111;
136
137 ScalarField::from_random_bytes(&bytes[..]).expect("failed to create scalar from bytes")
138 }
139
140 fn message_hash(&mut self, pub_key: &PubKey, rx: BaseField, input: &H) -> ScalarField {
146 let schnorr_input = Message::<H> {
147 input: input.clone(),
148 pub_key_x: pub_key.point().x,
149 pub_key_y: pub_key.point().y,
150 rx,
151 };
152
153 ScalarField::from(self.hasher.hash(&schnorr_input).into_bigint())
157 }
158}