1#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5#[cfg(feature = "alloc")]
6use core::borrow::Borrow;
7
8use group::GroupEncoding;
9#[cfg(feature = "alloc")]
10use group::{ff::PrimeField, Group};
11use pasta_curves::pallas;
12
13use crate::{private, SigType};
14
15#[cfg(feature = "alloc")]
16use crate::scalar_mul::{LookupTable5, NonAdjacentForm, VartimeMultiscalarMul};
17
18#[cfg(test)]
19mod tests;
20
21const ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [
26 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124,
27 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183,
28];
29
30const ORCHARD_BINDINGSIG_BASEPOINT_BYTES: [u8; 32] = [
33 145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, 91,
34 35, 102, 79, 187, 9, 164, 15, 85, 68, 244, 7,
35];
36
37#[derive(Copy, Clone, PartialEq, Eq, Debug)]
39pub enum SpendAuth {}
40impl Default for SpendAuth {
42 fn default() -> Self {
43 unimplemented!()
44 }
45}
46impl SigType for SpendAuth {}
47impl super::SpendAuth for SpendAuth {}
48
49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
51pub enum Binding {}
52impl Default for Binding {
54 fn default() -> Self {
55 unimplemented!()
56 }
57}
58impl SigType for Binding {}
59impl super::Binding for Binding {}
60
61impl private::SealedScalar for pallas::Scalar {
62 fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
63 <pallas::Scalar as group::ff::FromUniformBytes<64>>::from_uniform_bytes(bytes)
64 }
65 fn from_raw(val: [u64; 4]) -> Self {
66 pallas::Scalar::from_raw(val)
67 }
68}
69impl private::Sealed<SpendAuth> for SpendAuth {
70 const H_STAR_PERSONALIZATION: &'static [u8; 16] = b"Zcash_RedPallasH";
71 type Point = pallas::Point;
72 type Scalar = pallas::Scalar;
73
74 fn basepoint() -> pallas::Point {
75 pallas::Point::from_bytes(&ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES).unwrap()
76 }
77}
78impl private::Sealed<Binding> for Binding {
79 const H_STAR_PERSONALIZATION: &'static [u8; 16] = b"Zcash_RedPallasH";
80 type Point = pallas::Point;
81 type Scalar = pallas::Scalar;
82
83 fn basepoint() -> pallas::Point {
84 pallas::Point::from_bytes(&ORCHARD_BINDINGSIG_BASEPOINT_BYTES).unwrap()
85 }
86}
87
88#[cfg(feature = "alloc")]
89impl NonAdjacentForm for pallas::Scalar {
90 fn inner_to_bytes(&self) -> [u8; 32] {
91 self.to_repr()
92 }
93
94 fn naf_length() -> usize {
97 255
98 }
99}
100
101#[cfg(feature = "alloc")]
102impl<'a> From<&'a pallas::Point> for LookupTable5<pallas::Point> {
103 #[allow(non_snake_case)]
104 fn from(A: &'a pallas::Point) -> Self {
105 let mut Ai = [*A; 8];
106 let A2 = A.double();
107 for i in 0..7 {
108 Ai[i + 1] = A2 + Ai[i];
109 }
110 LookupTable5(Ai)
112 }
113}
114
115#[cfg(feature = "alloc")]
116impl VartimeMultiscalarMul for pallas::Point {
117 type Scalar = pallas::Scalar;
118 type Point = pallas::Point;
119
120 #[allow(non_snake_case)]
121 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<pallas::Point>
122 where
123 I: IntoIterator,
124 I::Item: Borrow<Self::Scalar>,
125 J: IntoIterator<Item = Option<pallas::Point>>,
126 {
127 let nafs: Vec<_> = scalars
128 .into_iter()
129 .map(|c| c.borrow().non_adjacent_form(5))
130 .collect();
131
132 let lookup_tables = points
133 .into_iter()
134 .map(|P_opt| P_opt.map(|P| LookupTable5::<pallas::Point>::from(&P)))
135 .collect::<Option<Vec<_>>>()?;
136
137 let mut r = pallas::Point::identity();
138 let naf_size = Self::Scalar::naf_length();
139
140 for i in (0..naf_size).rev() {
141 let mut t = r.double();
142
143 for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
144 #[allow(clippy::comparison_chain)]
145 if naf[i] > 0 {
146 t += lookup_table.select(naf[i] as usize);
147 } else if naf[i] < 0 {
148 t -= lookup_table.select(-naf[i] as usize);
149 }
150 }
151
152 r = t;
153 }
154
155 Some(r)
156 }
157}