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
76fn diversify_hash(d: [u8; 11]) -> Option<jubjub::ExtendedPoint> {
80 jubjub_group_hash(*b"Zcash_gd", &d)
81}
82
83#[derive(Copy, Clone, Eq, PartialEq)]
90#[cfg_attr(
91 any(test, feature = "proptest-impl"),
92 derive(proptest_derive::Arbitrary)
93)]
94pub struct Diversifier(pub(crate) [u8; 11]);
95
96impl fmt::Debug for Diversifier {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 f.debug_tuple("Diversifier")
99 .field(&hex::encode(self.0))
100 .finish()
101 }
102}
103
104impl From<[u8; 11]> for Diversifier {
105 fn from(bytes: [u8; 11]) -> Self {
106 Self(bytes)
107 }
108}
109
110impl From<Diversifier> for [u8; 11] {
111 fn from(d: Diversifier) -> [u8; 11] {
112 d.0
113 }
114}
115
116impl TryFrom<Diversifier> for jubjub::AffinePoint {
117 type Error = &'static str;
118
119 fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
122 if let Ok(extended_point) = jubjub::ExtendedPoint::try_from(d) {
123 Ok(extended_point.into())
124 } else {
125 Err("Invalid Diversifier -> jubjub::AffinePoint")
126 }
127 }
128}
129
130impl TryFrom<Diversifier> for jubjub::ExtendedPoint {
131 type Error = &'static str;
132
133 fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
134 let possible_point = diversify_hash(d.0);
135
136 if let Some(point) = possible_point {
137 Ok(point)
138 } else {
139 Err("Invalid Diversifier -> jubjub::ExtendedPoint")
140 }
141 }
142}
143
144impl PartialEq<[u8; 11]> for Diversifier {
145 fn eq(&self, other: &[u8; 11]) -> bool {
146 self.0 == *other
147 }
148}
149
150impl Diversifier {
151 pub fn new<T>(csprng: &mut T) -> Result<Self, AddressError>
158 where
159 T: RngCore + CryptoRng,
160 {
161 const DIVERSIFY_HASH_TRIES: u8 = 2;
163
164 for _ in 0..DIVERSIFY_HASH_TRIES {
165 let mut bytes = [0u8; 11];
166 csprng
167 .try_fill_bytes(&mut bytes)
168 .map_err(|_| AddressError::from(RandError::FillBytes))?;
169
170 if diversify_hash(bytes).is_some() {
171 return Ok(Self(bytes));
172 }
173 }
174 Err(AddressError::DiversifierGenerationFailure)
175 }
176}
177
178#[derive(Copy, Clone, PartialEq)]
197pub struct TransmissionKey(pub(crate) jubjub::AffinePoint);
198
199impl fmt::Debug for TransmissionKey {
200 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201 f.debug_struct("TransmissionKey")
202 .field("u", &hex::encode(self.0.get_u().to_bytes()))
203 .field("v", &hex::encode(self.0.get_v().to_bytes()))
204 .finish()
205 }
206}
207
208impl Eq for TransmissionKey {}
209
210impl TryFrom<[u8; 32]> for TransmissionKey {
211 type Error = &'static str;
212
213 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
219 let affine_point = jubjub::AffinePoint::from_bytes(bytes).unwrap();
220 if affine_point.is_torsion_free().into() {
222 Ok(Self(affine_point))
223 } else {
224 Err("Invalid jubjub::AffinePoint value for Sapling TransmissionKey")
225 }
226 }
227}
228
229impl From<TransmissionKey> for [u8; 32] {
230 fn from(pk_d: TransmissionKey) -> [u8; 32] {
231 pk_d.0.to_bytes()
232 }
233}
234
235impl PartialEq<[u8; 32]> for TransmissionKey {
236 fn eq(&self, other: &[u8; 32]) -> bool {
237 &self.0.to_bytes() == other
238 }
239}
240
241#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
252pub struct EphemeralPublicKey(
253 #[serde(with = "serde_helpers::AffinePoint")] pub(crate) jubjub::AffinePoint,
254);
255
256impl fmt::Debug for EphemeralPublicKey {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 f.debug_struct("EphemeralPublicKey")
259 .field("u", &hex::encode(self.0.get_u().to_bytes()))
260 .field("v", &hex::encode(self.0.get_v().to_bytes()))
261 .finish()
262 }
263}
264
265impl Eq for EphemeralPublicKey {}
266
267impl From<EphemeralPublicKey> for [u8; 32] {
268 fn from(nk: EphemeralPublicKey) -> [u8; 32] {
269 nk.0.to_bytes()
270 }
271}
272
273impl From<&EphemeralPublicKey> for [u8; 32] {
274 fn from(nk: &EphemeralPublicKey) -> [u8; 32] {
275 nk.0.to_bytes()
276 }
277}
278
279impl PartialEq<[u8; 32]> for EphemeralPublicKey {
280 fn eq(&self, other: &[u8; 32]) -> bool {
281 &self.0.to_bytes() == other
282 }
283}
284
285impl TryFrom<[u8; 32]> for EphemeralPublicKey {
286 type Error = &'static str;
287
288 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
299 let possible_point = jubjub::AffinePoint::from_bytes(bytes);
300
301 if possible_point.is_none().into() {
302 return Err("Invalid jubjub::AffinePoint value for Sapling EphemeralPublicKey");
303 }
304 if possible_point.unwrap().is_small_order().into() {
305 Err("jubjub::AffinePoint value for Sapling EphemeralPublicKey point is of small order")
306 } else {
307 Ok(Self(possible_point.unwrap()))
308 }
309 }
310}
311
312impl ZcashSerialize for EphemeralPublicKey {
313 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
314 writer.write_all(&<[u8; 32]>::from(self)[..])?;
315 Ok(())
316 }
317}
318
319impl ZcashDeserialize for EphemeralPublicKey {
320 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
321 Self::try_from(reader.read_32_bytes()?).map_err(SerializationError::Parse)
322 }
323}
324
325#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
334pub struct ValidatingKey(redjubjub::VerificationKey<SpendAuth>);
335
336impl From<ValidatingKey> for redjubjub::VerificationKey<SpendAuth> {
337 fn from(rk: ValidatingKey) -> Self {
338 rk.0
339 }
340}
341
342impl TryFrom<redjubjub::VerificationKey<SpendAuth>> for ValidatingKey {
343 type Error = &'static str;
344
345 fn try_from(key: redjubjub::VerificationKey<SpendAuth>) -> Result<Self, Self::Error> {
356 if bool::from(
357 jubjub::AffinePoint::from_bytes(key.into())
358 .unwrap()
359 .is_small_order(),
360 ) {
361 Err("jubjub::AffinePoint value for Sapling ValidatingKey is of small order")
362 } else {
363 Ok(Self(key))
364 }
365 }
366}
367
368impl TryFrom<[u8; 32]> for ValidatingKey {
369 type Error = &'static str;
370
371 fn try_from(value: [u8; 32]) -> Result<Self, Self::Error> {
372 let vk = redjubjub::VerificationKey::<SpendAuth>::try_from(value)
373 .map_err(|_| "Invalid redjubjub::ValidatingKey for Sapling ValidatingKey")?;
374 vk.try_into()
375 }
376}
377
378impl From<ValidatingKey> for [u8; 32] {
379 fn from(key: ValidatingKey) -> Self {
380 key.0.into()
381 }
382}
383
384impl From<ValidatingKey> for redjubjub::VerificationKeyBytes<SpendAuth> {
385 fn from(key: ValidatingKey) -> Self {
386 key.0.into()
387 }
388}