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