1use alloc::vec::Vec;
8use core::fmt;
9use corez::io::{self, Read, Write};
10
11use super::{
12 address::PaymentAddress,
13 constants::{self, PROOF_GENERATION_KEY_GENERATOR},
14 note_encryption::KDF_SAPLING_PERSONALIZATION,
15 spec::{
16 PreparedBase, PreparedBaseSubgroup, PreparedScalar, crh_ivk, diversify_hash,
17 ka_sapling_agree, ka_sapling_agree_prepared, ka_sapling_derive_public,
18 ka_sapling_derive_public_subgroup_prepared,
19 },
20};
21
22use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
23use ff::{Field, PrimeField};
24use group::{Curve, Group, GroupEncoding};
25use redjubjub::SpendAuth;
26use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
27use zcash_note_encryption::EphemeralKeyBytes;
28use zcash_spec::PrfExpand;
29
30#[cfg(all(feature = "circuit", test))]
31use rand_core::Rng;
32
33pub(crate) const SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
35
36pub(crate) fn master_key_expansion(seed: &[u8]) -> Blake2bHash {
38 Blake2bParams::new()
39 .hash_length(64)
40 .personal(SAPLING_MASTER_PERSONALIZATION)
41 .hash(seed)
42}
43
44#[derive(Debug)]
46#[non_exhaustive]
47pub enum DecodingError {
48 LengthInvalid { expected: usize, actual: usize },
50 InvalidAsk,
52 InvalidNsk,
54 UnsupportedChildIndex,
57}
58
59impl fmt::Display for DecodingError {
60 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61 match self {
62 DecodingError::LengthInvalid { expected, actual } => {
63 write!(f, "invalid slice length (expected {expected}, got {actual}")
64 }
65 DecodingError::InvalidAsk => write!(f, "invalid `ask`"),
66 DecodingError::InvalidNsk => write!(f, "invalid `nsk`"),
67 DecodingError::UnsupportedChildIndex => write!(
68 f,
69 "unsupported child index (either non-hardened, or non-zero at depth 0)"
70 ),
71 }
72 }
73}
74
75#[cfg(feature = "std")]
76impl std::error::Error for DecodingError {}
77
78#[derive(Clone, Debug)]
84pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
85
86impl PartialEq for SpendAuthorizingKey {
87 fn eq(&self, other: &Self) -> bool {
88 <[u8; 32]>::from(self.0)
89 .ct_eq(&<[u8; 32]>::from(other.0))
90 .into()
91 }
92}
93
94impl Eq for SpendAuthorizingKey {}
95
96impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
97 fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
98 jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
99 }
100}
101
102impl SpendAuthorizingKey {
103 fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
105 jubjub::Scalar::from_bytes_wide(&PrfExpand::SAPLING_ASK.with(sk))
106 }
107
108 pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
110 if ask.is_zero().into() {
111 None
112 } else {
113 Some(SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()))
114 }
115 }
116
117 fn from_spending_key(sk: &[u8]) -> Option<Self> {
119 Self::from_scalar(Self::derive_inner(sk))
120 }
121
122 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
124 <[u8; 32]>::try_from(bytes)
125 .ok()
126 .and_then(|b| {
127 jubjub::Scalar::from_repr(b)
131 .and_then(|s| {
132 CtOption::new(
133 redjubjub::SigningKey::try_from(b)
134 .expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
135 !s.is_zero(),
136 )
137 })
138 .into()
139 })
140 .map(SpendAuthorizingKey)
141 }
142
143 pub fn to_bytes(&self) -> [u8; 32] {
145 <[u8; 32]>::from(self.0)
146 }
147
148 #[cfg(feature = "wallet")]
152 pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
153 jubjub::Scalar::from_repr(self.0.into()).unwrap()
154 }
155
156 pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
160 self.0.randomize(randomizer)
161 }
162}
163
164#[derive(Clone, Debug)]
170pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
171
172impl From<&SpendAuthorizingKey> for SpendValidatingKey {
173 fn from(ask: &SpendAuthorizingKey) -> Self {
174 SpendValidatingKey((&ask.0).into())
175 }
176}
177
178impl PartialEq for SpendValidatingKey {
179 fn eq(&self, other: &Self) -> bool {
180 <[u8; 32]>::from(self.0)
181 .ct_eq(&<[u8; 32]>::from(other.0))
182 .into()
183 }
184}
185
186impl Eq for SpendValidatingKey {}
187
188impl SpendValidatingKey {
189 #[cfg(all(feature = "circuit", test))]
191 pub(crate) fn fake_random<R: Rng>(mut rng: R) -> Self {
192 loop {
193 if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
194 break k;
195 }
196 }
197 }
198
199 #[cfg(feature = "temporary-zcashd")]
201 pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
202 Self::from_bytes(bytes)
203 }
204
205 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
207 <[u8; 32]>::try_from(bytes)
208 .ok()
209 .and_then(|b| {
210 jubjub::SubgroupPoint::from_bytes(&b)
216 .and_then(|p| {
217 CtOption::new(
218 redjubjub::VerificationKey::try_from(b)
219 .expect("RedJubjub permits the set of valid SpendValidatingKeys"),
220 !p.is_identity(),
221 )
222 })
223 .into()
224 })
225 .map(SpendValidatingKey)
226 }
227
228 pub fn to_bytes(&self) -> [u8; 32] {
231 <[u8; 32]>::from(self.0)
232 }
233
234 pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
236 self.0.randomize(randomizer)
237 }
238}
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub struct OutgoingViewingKey(pub [u8; 32]);
243
244#[derive(Clone)]
246pub struct ExpandedSpendingKey {
247 pub ask: SpendAuthorizingKey,
248 pub nsk: jubjub::Fr,
249 pub ovk: OutgoingViewingKey,
250}
251
252impl fmt::Debug for ExpandedSpendingKey {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.debug_struct("ExpandedSpendingKey")
255 .finish_non_exhaustive()
256 }
257}
258
259impl ExpandedSpendingKey {
260 pub fn from_spending_key(sk: &[u8]) -> Self {
267 let ask =
268 SpendAuthorizingKey::from_spending_key(sk).expect("negligible chance of ask == 0");
269 let nsk = jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_NSK.with(sk));
270 let mut ovk = OutgoingViewingKey([0u8; 32]);
271 ovk.0
272 .copy_from_slice(&PrfExpand::SAPLING_OVK.with(sk)[..32]);
273 ExpandedSpendingKey { ask, nsk, ovk }
274 }
275
276 pub fn proof_generation_key(&self) -> ProofGenerationKey {
277 ProofGenerationKey {
278 ak: (&self.ask).into(),
279 nsk: self.nsk,
280 }
281 }
282
283 pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
287 if b.len() != 96 {
288 return Err(DecodingError::LengthInvalid {
289 expected: 96,
290 actual: b.len(),
291 });
292 }
293
294 let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
295 let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
296 .ok_or(DecodingError::InvalidNsk)?;
297 let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
298
299 Ok(ExpandedSpendingKey { ask, nsk, ovk })
300 }
301
302 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
303 let mut repr = [0u8; 96];
304 reader.read_exact(repr.as_mut())?;
305 Self::from_bytes(&repr).map_err(|e| match e {
306 DecodingError::InvalidAsk => {
307 io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
308 }
309 DecodingError::InvalidNsk => {
310 io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
311 }
312 DecodingError::LengthInvalid { .. } | DecodingError::UnsupportedChildIndex => {
313 unreachable!()
314 }
315 })
316 }
317
318 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
319 writer.write_all(&self.to_bytes())
320 }
321
322 pub fn to_bytes(&self) -> [u8; 96] {
326 let mut result = [0u8; 96];
327 result[0..32].copy_from_slice(&self.ask.to_bytes());
328 result[32..64].copy_from_slice(&self.nsk.to_repr());
329 result[64..96].copy_from_slice(&self.ovk.0);
330 result
331 }
332}
333
334#[derive(Clone)]
335pub struct ProofGenerationKey {
336 pub ak: SpendValidatingKey,
337 pub nsk: jubjub::Fr,
338}
339
340impl fmt::Debug for ProofGenerationKey {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 f.debug_struct("ProofGenerationKey")
343 .field("ak", &self.ak)
344 .finish_non_exhaustive()
345 }
346}
347
348impl ProofGenerationKey {
349 pub fn to_viewing_key(&self) -> ViewingKey {
350 ViewingKey {
351 ak: self.ak.clone(),
352 nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
353 }
354 }
355}
356
357#[derive(Debug, Copy, Clone, PartialEq, Eq)]
359pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
360
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct ViewingKey {
363 pub ak: SpendValidatingKey,
364 pub nk: NullifierDerivingKey,
365}
366
367impl ViewingKey {
368 pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
369 self.ak.randomize(&ar)
370 }
371
372 pub fn ivk(&self) -> SaplingIvk {
373 SaplingIvk(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
374 }
375
376 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
377 self.ivk().to_payment_address(diversifier)
378 }
379}
380
381#[derive(Debug, PartialEq, Eq)]
383pub struct FullViewingKey {
384 pub vk: ViewingKey,
385 pub ovk: OutgoingViewingKey,
386}
387
388impl Clone for FullViewingKey {
389 fn clone(&self) -> Self {
390 FullViewingKey {
391 vk: ViewingKey {
392 ak: self.vk.ak.clone(),
393 nk: self.vk.nk,
394 },
395 ovk: self.ovk,
396 }
397 }
398}
399
400impl FullViewingKey {
401 pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
402 FullViewingKey {
403 vk: ViewingKey {
404 ak: (&expsk.ask).into(),
405 nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
406 },
407 ovk: expsk.ovk,
408 }
409 }
410
411 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
412 let ak = {
413 let mut buf = [0u8; 32];
414 reader.read_exact(&mut buf)?;
415 SpendValidatingKey::from_bytes(&buf)
416 };
417 let nk = {
418 let mut buf = [0u8; 32];
419 reader.read_exact(&mut buf)?;
420 jubjub::SubgroupPoint::from_bytes(&buf)
421 };
422 if ak.is_none() {
423 return Err(io::Error::new(
424 io::ErrorKind::InvalidInput,
425 "ak not of prime order",
426 ));
427 }
428 if nk.is_none().into() {
429 return Err(io::Error::new(
430 io::ErrorKind::InvalidInput,
431 "nk not in prime-order subgroup",
432 ));
433 }
434 let ak = ak.unwrap();
435 let nk = NullifierDerivingKey(nk.unwrap());
436
437 let mut ovk = [0u8; 32];
438 reader.read_exact(&mut ovk)?;
439
440 Ok(FullViewingKey {
441 vk: ViewingKey { ak, nk },
442 ovk: OutgoingViewingKey(ovk),
443 })
444 }
445
446 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
447 writer.write_all(&self.vk.ak.to_bytes())?;
448 writer.write_all(&self.vk.nk.0.to_bytes())?;
449 writer.write_all(&self.ovk.0)?;
450
451 Ok(())
452 }
453
454 pub fn to_bytes(&self) -> [u8; 96] {
455 let mut result = [0u8; 96];
456 self.write(&mut result[..])
457 .expect("should be able to serialize a FullViewingKey");
458 result
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq)]
463pub struct SaplingIvk(pub jubjub::Fr);
464
465impl SaplingIvk {
466 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
467 let prepared_ivk = PreparedIncomingViewingKey::new(self);
468 DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
469 .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
470 }
471
472 pub fn to_repr(&self) -> [u8; 32] {
473 self.0.to_repr()
474 }
475}
476
477#[derive(Clone, Debug)]
479pub struct PreparedIncomingViewingKey(PreparedScalar);
480
481impl PreparedIncomingViewingKey {
482 pub fn new(ivk: &SaplingIvk) -> Self {
484 Self(PreparedScalar::new(&ivk.0))
485 }
486}
487
488#[derive(Copy, Clone, Debug, PartialEq, Eq)]
489pub struct Diversifier(pub [u8; 11]);
490
491impl Diversifier {
492 pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
493 diversify_hash(&self.0)
494 }
495}
496
497#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
506pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
507
508impl DiversifiedTransmissionKey {
509 pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
515 d.g_d()
516 .map(PreparedBaseSubgroup::new)
517 .map(|g_d| ka_sapling_derive_public_subgroup_prepared(&ivk.0, &g_d))
518 .map(DiversifiedTransmissionKey)
519 }
520
521 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
523 jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
524 }
525
526 pub(crate) fn to_bytes(self) -> [u8; 32] {
528 self.0.to_bytes()
529 }
530
531 pub(crate) fn is_identity(&self) -> bool {
533 self.0.is_identity().into()
534 }
535
536 pub fn inner(&self) -> jubjub::SubgroupPoint {
541 self.0
542 }
543}
544
545impl ConditionallySelectable for DiversifiedTransmissionKey {
546 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
547 DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
548 &a.0, &b.0, choice,
549 ))
550 }
551}
552
553#[derive(Debug)]
564pub struct EphemeralSecretKey(pub(crate) jubjub::Scalar);
565
566impl ConstantTimeEq for EphemeralSecretKey {
567 fn ct_eq(&self, other: &Self) -> subtle::Choice {
568 self.0.ct_eq(&other.0)
569 }
570}
571
572impl EphemeralSecretKey {
573 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
574 jubjub::Scalar::from_bytes(bytes).map(EphemeralSecretKey)
575 }
576
577 pub(crate) fn derive_public(&self, g_d: jubjub::ExtendedPoint) -> EphemeralPublicKey {
578 EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
579 }
580
581 pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
582 SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
583 }
584}
585
586#[derive(Debug)]
597pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
598
599impl EphemeralPublicKey {
600 pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
601 EphemeralPublicKey(epk.into())
602 }
603
604 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
605 jubjub::ExtendedPoint::from_bytes(bytes).map(EphemeralPublicKey)
606 }
607
608 pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
609 EphemeralKeyBytes(self.0.to_bytes())
610 }
611}
612
613#[derive(Clone, Debug)]
615pub struct PreparedEphemeralPublicKey(PreparedBase);
616
617impl PreparedEphemeralPublicKey {
618 pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
619 PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
620 }
621
622 pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
623 SharedSecret(ka_sapling_agree_prepared(&ivk.0, &self.0))
624 }
625}
626
627#[derive(Debug)]
633pub struct SharedSecret(jubjub::SubgroupPoint);
634
635impl SharedSecret {
636 #[cfg(test)]
638 pub(crate) fn to_bytes(&self) -> [u8; 32] {
639 self.0.to_bytes()
640 }
641
642 pub(crate) fn batch_to_affine(
644 shared_secrets: Vec<Option<Self>>,
645 ) -> impl Iterator<Item = Option<jubjub::AffinePoint>> {
646 let secrets: Vec<_> = shared_secrets
648 .iter()
649 .filter_map(|s| s.as_ref().map(|s| jubjub::ExtendedPoint::from(s.0)))
650 .collect();
651
652 let mut secrets_affine = vec![jubjub::AffinePoint::identity(); secrets.len()];
654 group::Curve::batch_normalize(&secrets, &mut secrets_affine);
655
656 let mut secrets_affine = secrets_affine.into_iter();
658 shared_secrets
659 .into_iter()
660 .map(move |s| s.and_then(|_| secrets_affine.next()))
661 }
662
663 pub(crate) fn kdf_sapling(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
667 Self::kdf_sapling_inner(
668 jubjub::ExtendedPoint::from(self.0).to_affine(),
669 ephemeral_key,
670 )
671 }
672
673 pub(crate) fn kdf_sapling_inner(
675 secret: jubjub::AffinePoint,
676 ephemeral_key: &EphemeralKeyBytes,
677 ) -> Blake2bHash {
678 Blake2bParams::new()
679 .hash_length(32)
680 .personal(KDF_SAPLING_PERSONALIZATION)
681 .to_state()
682 .update(&secret.to_bytes())
683 .update(ephemeral_key.as_ref())
684 .finalize()
685 }
686}
687
688#[cfg(any(test, feature = "test-dependencies"))]
689#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
690pub mod testing {
691 use proptest::collection::vec;
692 use proptest::prelude::*;
693
694 use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
695
696 prop_compose! {
697 pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey {
698 ExpandedSpendingKey::from_spending_key(&v)
699 }
700 }
701
702 prop_compose! {
703 pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
704 FullViewingKey::from_expanded_spending_key(&sk)
705 }
706 }
707
708 prop_compose! {
709 pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
710 fvk.vk.ivk()
711 }
712 }
713}
714
715#[cfg(test)]
716mod tests {
717 use alloc::string::ToString;
718 use group::{Group, GroupEncoding};
719
720 use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey};
721 use crate::{constants::SPENDING_KEY_GENERATOR, test_vectors};
722
723 #[test]
724 fn ak_must_be_prime_order() {
725 let mut buf = [0; 96];
726 let identity = jubjub::SubgroupPoint::identity();
727
728 buf[0..32].copy_from_slice(&identity.to_bytes());
730 buf[32..64].copy_from_slice(&identity.to_bytes());
731
732 assert_eq!(
734 FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
735 "ak not of prime order"
736 );
737
738 let basepoint = SPENDING_KEY_GENERATOR;
740 buf[0..32].copy_from_slice(&basepoint.to_bytes());
741
742 assert!(FullViewingKey::read(&buf[..]).is_ok());
744 }
745
746 #[test]
747 fn spend_auth_sig_test_vectors() {
748 for tv in test_vectors::signatures::make_test_vectors() {
749 let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
750 let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
751 let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
752 let sig = redjubjub::Signature::from(tv.sig);
753 let rsig = redjubjub::Signature::from(tv.rsig);
754
755 let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
756
757 assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk);
758 assert_eq!(vk.randomize(&alpha), rvk);
759
760 assert_eq!(
763 vk.0.verify(&tv.m, &rsig),
764 Err(redjubjub::Error::InvalidSignature),
765 );
766 assert_eq!(
767 rvk.verify(&tv.m, &sig),
768 Err(redjubjub::Error::InvalidSignature),
769 );
770 }
771 }
772}