zebra_chain/sapling/
keys.rs1use std::{fmt, io};
14
15use rand_core::{CryptoRng, RngCore};
16
17use crate::{
18 error::{AddressError, RandError},
19 primitives::redjubjub::SpendAuth,
20 serialization::{
21 serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
22 },
23};
24
25#[cfg(test)]
26mod test_vectors;
27
28pub(super) const RANDOMNESS_BEACON_URS: &[u8; 64] =
39 b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
40
41fn jubjub_group_hash(d: [u8; 8], m: &[u8]) -> Option<jubjub::ExtendedPoint> {
53 let hash = blake2s_simd::Params::new()
54 .hash_length(32)
55 .personal(&d)
56 .to_state()
57 .update(RANDOMNESS_BEACON_URS)
58 .update(m)
59 .finalize();
60
61 let ct_option = jubjub::AffinePoint::from_bytes(*hash.as_array());
62
63 if ct_option.is_some().unwrap_u8() == 1 {
64 let extended_point = ct_option.unwrap().mul_by_cofactor();
65
66 if extended_point != jubjub::ExtendedPoint::identity() {
67 Some(extended_point)
68 } else {
69 None
70 }
71 } else {
72 None
73 }
74}
75
76pub(super) fn find_group_hash(d: [u8; 8], m: &[u8]) -> jubjub::ExtendedPoint {
86 let mut tag = m.to_vec();
87 let i = tag.len();
88 tag.push(0u8);
89
90 loop {
91 let gh = jubjub_group_hash(d, &tag[..]);
92
93 assert!(tag[i] != u8::MAX);
95 tag[i] += 1;
96
97 if let Some(gh) = gh {
98 break gh;
99 }
100 }
101}
102
103fn diversify_hash(d: [u8; 11]) -> Option<jubjub::ExtendedPoint> {
107 jubjub_group_hash(*b"Zcash_gd", &d)
108}
109
110#[derive(Copy, Clone, Eq, PartialEq)]
117#[cfg_attr(
118 any(test, feature = "proptest-impl"),
119 derive(proptest_derive::Arbitrary)
120)]
121pub struct Diversifier(pub(crate) [u8; 11]);
122
123impl fmt::Debug for Diversifier {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 f.debug_tuple("Diversifier")
126 .field(&hex::encode(self.0))
127 .finish()
128 }
129}
130
131impl From<[u8; 11]> for Diversifier {
132 fn from(bytes: [u8; 11]) -> Self {
133 Self(bytes)
134 }
135}
136
137impl From<Diversifier> for [u8; 11] {
138 fn from(d: Diversifier) -> [u8; 11] {
139 d.0
140 }
141}
142
143impl TryFrom<Diversifier> for jubjub::AffinePoint {
144 type Error = &'static str;
145
146 fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
149 if let Ok(extended_point) = jubjub::ExtendedPoint::try_from(d) {
150 Ok(extended_point.into())
151 } else {
152 Err("Invalid Diversifier -> jubjub::AffinePoint")
153 }
154 }
155}
156
157impl TryFrom<Diversifier> for jubjub::ExtendedPoint {
158 type Error = &'static str;
159
160 fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
161 let possible_point = diversify_hash(d.0);
162
163 if let Some(point) = possible_point {
164 Ok(point)
165 } else {
166 Err("Invalid Diversifier -> jubjub::ExtendedPoint")
167 }
168 }
169}
170
171impl PartialEq<[u8; 11]> for Diversifier {
172 fn eq(&self, other: &[u8; 11]) -> bool {
173 self.0 == *other
174 }
175}
176
177impl Diversifier {
178 pub fn new<T>(csprng: &mut T) -> Result<Self, AddressError>
185 where
186 T: RngCore + CryptoRng,
187 {
188 const DIVERSIFY_HASH_TRIES: u8 = 2;
190
191 for _ in 0..DIVERSIFY_HASH_TRIES {
192 let mut bytes = [0u8; 11];
193 csprng
194 .try_fill_bytes(&mut bytes)
195 .map_err(|_| AddressError::from(RandError::FillBytes))?;
196
197 if diversify_hash(bytes).is_some() {
198 return Ok(Self(bytes));
199 }
200 }
201 Err(AddressError::DiversifierGenerationFailure)
202 }
203}
204
205#[derive(Copy, Clone, PartialEq)]
224pub struct TransmissionKey(pub(crate) jubjub::AffinePoint);
225
226impl fmt::Debug for TransmissionKey {
227 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228 f.debug_struct("TransmissionKey")
229 .field("u", &hex::encode(self.0.get_u().to_bytes()))
230 .field("v", &hex::encode(self.0.get_v().to_bytes()))
231 .finish()
232 }
233}
234
235impl Eq for TransmissionKey {}
236
237impl TryFrom<[u8; 32]> for TransmissionKey {
238 type Error = &'static str;
239
240 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
246 let affine_point = jubjub::AffinePoint::from_bytes(bytes).unwrap();
247 if affine_point.is_torsion_free().into() {
249 Ok(Self(affine_point))
250 } else {
251 Err("Invalid jubjub::AffinePoint value for Sapling TransmissionKey")
252 }
253 }
254}
255
256impl From<TransmissionKey> for [u8; 32] {
257 fn from(pk_d: TransmissionKey) -> [u8; 32] {
258 pk_d.0.to_bytes()
259 }
260}
261
262impl PartialEq<[u8; 32]> for TransmissionKey {
263 fn eq(&self, other: &[u8; 32]) -> bool {
264 &self.0.to_bytes() == other
265 }
266}
267
268#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
279pub struct EphemeralPublicKey(
280 #[serde(with = "serde_helpers::AffinePoint")] pub(crate) jubjub::AffinePoint,
281);
282
283impl fmt::Debug for EphemeralPublicKey {
284 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285 f.debug_struct("EphemeralPublicKey")
286 .field("u", &hex::encode(self.0.get_u().to_bytes()))
287 .field("v", &hex::encode(self.0.get_v().to_bytes()))
288 .finish()
289 }
290}
291
292impl Eq for EphemeralPublicKey {}
293
294impl From<EphemeralPublicKey> for [u8; 32] {
295 fn from(nk: EphemeralPublicKey) -> [u8; 32] {
296 nk.0.to_bytes()
297 }
298}
299
300impl From<&EphemeralPublicKey> for [u8; 32] {
301 fn from(nk: &EphemeralPublicKey) -> [u8; 32] {
302 nk.0.to_bytes()
303 }
304}
305
306impl PartialEq<[u8; 32]> for EphemeralPublicKey {
307 fn eq(&self, other: &[u8; 32]) -> bool {
308 &self.0.to_bytes() == other
309 }
310}
311
312impl TryFrom<[u8; 32]> for EphemeralPublicKey {
313 type Error = &'static str;
314
315 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
326 let possible_point = jubjub::AffinePoint::from_bytes(bytes);
327
328 if possible_point.is_none().into() {
329 return Err("Invalid jubjub::AffinePoint value for Sapling EphemeralPublicKey");
330 }
331 if possible_point.unwrap().is_small_order().into() {
332 Err("jubjub::AffinePoint value for Sapling EphemeralPublicKey point is of small order")
333 } else {
334 Ok(Self(possible_point.unwrap()))
335 }
336 }
337}
338
339impl ZcashSerialize for EphemeralPublicKey {
340 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
341 writer.write_all(&<[u8; 32]>::from(self)[..])?;
342 Ok(())
343 }
344}
345
346impl ZcashDeserialize for EphemeralPublicKey {
347 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
348 Self::try_from(reader.read_32_bytes()?).map_err(SerializationError::Parse)
349 }
350}
351
352#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
361pub struct ValidatingKey(redjubjub::VerificationKey<SpendAuth>);
362
363impl TryFrom<redjubjub::VerificationKey<SpendAuth>> for ValidatingKey {
364 type Error = &'static str;
365
366 fn try_from(key: redjubjub::VerificationKey<SpendAuth>) -> Result<Self, Self::Error> {
377 if bool::from(
378 jubjub::AffinePoint::from_bytes(key.into())
379 .unwrap()
380 .is_small_order(),
381 ) {
382 Err("jubjub::AffinePoint value for Sapling ValidatingKey is of small order")
383 } else {
384 Ok(Self(key))
385 }
386 }
387}
388
389impl TryFrom<[u8; 32]> for ValidatingKey {
390 type Error = &'static str;
391
392 fn try_from(value: [u8; 32]) -> Result<Self, Self::Error> {
393 let vk = redjubjub::VerificationKey::<SpendAuth>::try_from(value)
394 .map_err(|_| "Invalid redjubjub::ValidatingKey for Sapling ValidatingKey")?;
395 vk.try_into()
396 }
397}
398
399impl From<ValidatingKey> for [u8; 32] {
400 fn from(key: ValidatingKey) -> Self {
401 key.0.into()
402 }
403}
404
405impl From<ValidatingKey> for redjubjub::VerificationKeyBytes<SpendAuth> {
406 fn from(key: ValidatingKey) -> Self {
407 key.0.into()
408 }
409}