1mod sigma;
33
34use curve25519_dalek::constants;
35use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
36use curve25519_dalek::scalar::Scalar;
37use hmac::{Hmac, Mac};
38use rand_core::{CryptoRng, RngCore};
39use sha2::Sha512;
40use sigma::OrDLogProver;
41
42const CHALLENGE_CONTEXT: &[u8] = b"deevee challenge context 2022-06-17";
43
44fn challenge(
49 big_x0: &CompressedRistretto,
50 big_x1: &CompressedRistretto,
51 dh_result: &CompressedRistretto,
52 big_k0: &CompressedRistretto,
53 big_k1: &CompressedRistretto,
54 m: &[u8],
55) -> Scalar {
56 let mut hmac = Hmac::<Sha512>::new_from_slice(dh_result.as_bytes()).unwrap();
61
62 hmac.update(CHALLENGE_CONTEXT);
63 hmac.update(big_x0.as_bytes());
64 hmac.update(big_x1.as_bytes());
65 hmac.update(big_k0.as_bytes());
66 hmac.update(big_k1.as_bytes());
67 hmac.update(m);
68
69 Scalar::from_bytes_mod_order_wide(&hmac.finalize().into_bytes().into())
70}
71
72struct RawSignature {
73 e: Scalar,
74 e0: Scalar,
75 s0: Scalar,
76 s1: Scalar,
77}
78
79fn raw_sign<R: RngCore + CryptoRng>(
80 rng: &mut R,
81 x0: &Scalar,
82 big_x1: &RistrettoPoint,
83 m: &[u8],
84) -> RawSignature {
85 let big_x0 = x0 * &constants::RISTRETTO_BASEPOINT_TABLE;
86
87 let prover = OrDLogProver::create(rng, x0, big_x1);
88 let (big_k0, big_k1) = prover.commit();
89
90 let dh_result = x0 * big_x1;
91
92 let e = challenge(
93 &big_x0.compress(),
94 &big_x1.compress(),
95 &dh_result.compress(),
96 &big_k0.compress(),
97 &big_k1.compress(),
98 m,
99 );
100
101 let ((e0, _), (s0, s1)) = prover.respond(&e);
102
103 RawSignature { e, e0, s0, s1 }
104}
105
106fn raw_forge<R: RngCore + CryptoRng>(
107 rng: &mut R,
108 x1: &Scalar,
109 big_x0: &RistrettoPoint,
110 m: &[u8],
111) -> RawSignature {
112 let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
113
114 let prover = OrDLogProver::create(rng, x1, big_x0);
115 let (big_k1, big_k0) = prover.commit();
116
117 let dh_result = x1 * big_x0;
118
119 let e = challenge(
120 &big_x0.compress(),
121 &big_x1.compress(),
122 &dh_result.compress(),
123 &big_k0.compress(),
124 &big_k1.compress(),
125 m,
126 );
127
128 let ((_, e0), (s1, s0)) = prover.respond(&e);
129
130 RawSignature { e, e0, s0, s1 }
131}
132
133fn raw_verify(big_x0: &RistrettoPoint, x1: &Scalar, sig: &RawSignature, m: &[u8]) -> bool {
134 let big_x1 = x1 * &constants::RISTRETTO_BASEPOINT_TABLE;
135 let dh_result = x1 * big_x0;
136
137 let e1 = sig.e - sig.e0;
138 let (big_k0, big_k1) =
139 OrDLogProver::recompute((big_x0, &big_x1), (&sig.e0, &e1), (&sig.s0, &sig.s1));
140 let e = challenge(
141 &big_x0.compress(),
142 &big_x1.compress(),
143 &dh_result.compress(),
144 &big_k0.compress(),
145 &big_k1.compress(),
146 m,
147 );
148 e == sig.e
149}
150
151const SIGNATURE_LENGTH: usize = 128;
153
154#[derive(Clone, Copy, Debug)]
159pub struct Signature {
160 data: [u8; SIGNATURE_LENGTH],
161}
162
163impl Signature {
164 fn from_raw(raw: &RawSignature) -> Self {
165 let mut data = [0u8; SIGNATURE_LENGTH];
166 data[0..32].copy_from_slice(raw.e.as_bytes());
167 data[32..64].copy_from_slice(raw.e0.as_bytes());
168 data[64..96].copy_from_slice(raw.s0.as_bytes());
169 data[96..128].copy_from_slice(raw.s1.as_bytes());
170
171 Self::new(data)
172 }
173
174 fn as_raw(&self) -> Option<RawSignature> {
175 let e = Scalar::from_canonical_bytes(self.data[0..32].try_into().unwrap())?;
176 let e0 = Scalar::from_canonical_bytes(self.data[32..64].try_into().unwrap())?;
177 let s0 = Scalar::from_canonical_bytes(self.data[64..96].try_into().unwrap())?;
178 let s1 = Scalar::from_canonical_bytes(self.data[96..128].try_into().unwrap())?;
179
180 Some(RawSignature { e, e0, s0, s1 })
181 }
182
183 pub fn new(data: [u8; SIGNATURE_LENGTH]) -> Self {
185 Self { data }
186 }
187
188 pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
190 self.data
191 }
192}
193
194#[derive(Clone, Copy, Debug, PartialEq)]
199pub struct PublicKey(RistrettoPoint);
200
201impl PublicKey {
202 pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
206 Some(Self(CompressedRistretto(data).decompress()?))
207 }
208
209 pub fn to_bytes(&self) -> [u8; 32] {
211 self.0.compress().0
212 }
213}
214
215#[derive(Clone, Copy, PartialEq)]
222pub struct PrivateKey(Scalar);
223
224impl PrivateKey {
225 pub fn from_bytes(data: [u8; 32]) -> Option<Self> {
229 let scalar = Scalar::from_canonical_bytes(data)?;
230 Some(Self(scalar))
231 }
232
233 pub fn to_bytes(&self) -> [u8; 32] {
235 self.0.to_bytes()
236 }
237
238 pub fn sign<R: RngCore + CryptoRng>(
240 &self,
241 rng: &mut R,
242 designee: &PublicKey,
243 message: &[u8],
244 ) -> Signature {
245 let raw = raw_sign(rng, &self.0, &designee.0, message);
246 Signature::from_raw(&raw)
247 }
248
249 pub fn forge<R: RngCore + CryptoRng>(
251 &self,
252 rng: &mut R,
253 signer: &PublicKey,
254 message: &[u8],
255 ) -> Signature {
256 let raw = raw_forge(rng, &self.0, &signer.0, message);
257 Signature::from_raw(&raw)
258 }
259
260 pub fn verify(&self, signer: &PublicKey, message: &[u8], signature: &Signature) -> bool {
264 let raw_sig = match signature.as_raw() {
265 None => return false,
266 Some(s) => s,
267 };
268 raw_verify(&signer.0, &self.0, &raw_sig, message)
269 }
270}
271
272pub fn generate_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (PrivateKey, PublicKey) {
274 let scalar = Scalar::random(rng);
275 (
276 PrivateKey(scalar),
277 PublicKey(&scalar * &constants::RISTRETTO_BASEPOINT_TABLE),
278 )
279}