Nova/src/pasta.rs
src/pasta.rsRaw
1//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
2use crate::traits::{ChallengeTrait, CompressedGroup, Group};
3use ff::Field;
4use merlin::Transcript;
5use pasta_curves::{
6 self,
7 arithmetic::{CurveAffine, CurveExt, Group as Grp},
8 group::{Curve, GroupEncoding},
9 pallas, vesta, Ep, Eq,
10};
11use rand::SeedableRng;
12use rand_chacha::ChaCha20Rng;
13use rug::Integer;
14use std::{borrow::Borrow, ops::Mul};
15
16//////////////////////////////////////Pallas///////////////////////////////////////////////
17
18/// A wrapper for compressed group elements that come from the pallas curve
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub struct PallasCompressedElementWrapper {
21 repr: [u8; 32],
22}
23
24impl PallasCompressedElementWrapper {
25 /// Wraps repr into the wrapper
26 pub fn new(repr: [u8; 32]) -> Self {
27 Self { repr }
28 }
29}
30
31unsafe impl Send for PallasCompressedElementWrapper {}
32unsafe impl Sync for PallasCompressedElementWrapper {}
33
34impl Group for pallas::Point {
35 type Base = pallas::Base;
36 type Scalar = pallas::Scalar;
37 type CompressedGroupElement = PallasCompressedElementWrapper;
38
39 fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self
40 where
41 I: IntoIterator,
42 I::Item: Borrow<Self::Scalar>,
43 J: IntoIterator,
44 J::Item: Borrow<Self>,
45 Self: Clone,
46 {
47 // Unoptimized.
48 scalars
49 .into_iter()
50 .zip(points)
51 .map(|(scalar, point)| (*point.borrow()).mul(*scalar.borrow()))
52 .fold(Ep::group_zero(), |acc, x| acc + x)
53 }
54
55 fn compress(&self) -> Self::CompressedGroupElement {
56 PallasCompressedElementWrapper::new(self.to_bytes())
57 }
58
59 fn from_uniform_bytes(bytes: &[u8]) -> Option<Self> {
60 if bytes.len() != 64 {
61 None
62 } else {
63 let mut arr = [0; 32];
64 arr.copy_from_slice(&bytes[0..32]);
65
66 let hash = Ep::hash_to_curve("from_uniform_bytes");
67 Some(hash(&arr))
68 }
69 }
70
71 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
72 let coordinates = self.to_affine().coordinates();
73 if coordinates.is_some().unwrap_u8() == 1 {
74 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
75 } else {
76 (Self::Base::zero(), Self::Base::zero(), true)
77 }
78 }
79
80 fn get_order() -> Integer {
81 Integer::from_str_radix(
82 "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
83 16,
84 )
85 .unwrap()
86 }
87}
88
89impl ChallengeTrait for pallas::Scalar {
90 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
91 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
92 transcript.challenge_bytes(label, &mut key);
93 let mut rng = ChaCha20Rng::from_seed(key);
94 pallas::Scalar::random(&mut rng)
95 }
96}
97
98impl CompressedGroup for PallasCompressedElementWrapper {
99 type GroupElement = pallas::Point;
100
101 fn decompress(&self) -> Option<pallas::Point> {
102 Some(Ep::from_bytes(&self.repr).unwrap())
103 }
104 fn as_bytes(&self) -> &[u8] {
105 &self.repr
106 }
107}
108
109//////////////////////////////////////Vesta////////////////////////////////////////////////
110
111/// A wrapper for compressed group elements that come from the vesta curve
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub struct VestaCompressedElementWrapper {
114 repr: [u8; 32],
115}
116
117impl VestaCompressedElementWrapper {
118 /// Wraps repr into the wrapper
119 pub fn new(repr: [u8; 32]) -> Self {
120 Self { repr }
121 }
122}
123
124unsafe impl Send for VestaCompressedElementWrapper {}
125unsafe impl Sync for VestaCompressedElementWrapper {}
126
127impl Group for vesta::Point {
128 type Base = vesta::Base;
129 type Scalar = vesta::Scalar;
130 type CompressedGroupElement = VestaCompressedElementWrapper;
131
132 fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self
133 where
134 I: IntoIterator,
135 I::Item: Borrow<Self::Scalar>,
136 J: IntoIterator,
137 J::Item: Borrow<Self>,
138 Self: Clone,
139 {
140 // Unoptimized.
141 scalars
142 .into_iter()
143 .zip(points)
144 .map(|(scalar, point)| (*point.borrow()).mul(*scalar.borrow()))
145 .fold(Eq::group_zero(), |acc, x| acc + x)
146 }
147
148 fn compress(&self) -> Self::CompressedGroupElement {
149 VestaCompressedElementWrapper::new(self.to_bytes())
150 }
151
152 fn from_uniform_bytes(bytes: &[u8]) -> Option<Self> {
153 if bytes.len() != 64 {
154 None
155 } else {
156 let mut arr = [0; 32];
157 arr.copy_from_slice(&bytes[0..32]);
158
159 let hash = Eq::hash_to_curve("from_uniform_bytes");
160 Some(hash(&arr))
161 }
162 }
163
164 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
165 let coordinates = self.to_affine().coordinates();
166 if coordinates.is_some().unwrap_u8() == 1 {
167 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
168 } else {
169 (Self::Base::zero(), Self::Base::zero(), true)
170 }
171 }
172
173 fn get_order() -> Integer {
174 Integer::from_str_radix(
175 "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
176 16,
177 )
178 .unwrap()
179 }
180}
181
182impl ChallengeTrait for vesta::Scalar {
183 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
184 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
185 transcript.challenge_bytes(label, &mut key);
186 let mut rng = ChaCha20Rng::from_seed(key);
187 vesta::Scalar::random(&mut rng)
188 }
189}
190
191impl CompressedGroup for VestaCompressedElementWrapper {
192 type GroupElement = vesta::Point;
193
194 fn decompress(&self) -> Option<vesta::Point> {
195 Some(Eq::from_bytes(&self.repr).unwrap())
196 }
197 fn as_bytes(&self) -> &[u8] {
198 &self.repr
199 }
200}