1use alloc::vec::Vec;
8use core::fmt;
9use corez::io::{self, Read, Write};
10
11use super::{
12 address::PaymentAddress,
13 constants,
14 note_encryption::KDF_SAPLING_PERSONALIZATION,
15 spec::{
16 crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared,
17 ka_sapling_derive_public, ka_sapling_derive_public_subgroup_prepared, PreparedBase,
18 PreparedBaseSubgroup, PreparedScalar,
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#[cfg(feature = "zeroize")]
30use zeroize::{Zeroize, ZeroizeOnDrop};
31
32use crate::zeroize_secret;
33
34#[cfg(all(feature = "circuit", test))]
35use rand_core::Rng;
36
37#[derive(Debug)]
39#[non_exhaustive]
40pub enum DecodingError {
41 LengthInvalid { expected: usize, actual: usize },
43 InvalidAsk,
45 InvalidNsk,
47 InvalidIvk,
49 UnsupportedChildIndex,
52}
53
54impl fmt::Display for DecodingError {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match self {
57 DecodingError::LengthInvalid { expected, actual } => {
58 write!(f, "invalid slice length (expected {expected}, got {actual}")
59 }
60 DecodingError::InvalidAsk => write!(f, "invalid `ask`"),
61 DecodingError::InvalidNsk => write!(f, "invalid `nsk`"),
62 DecodingError::InvalidIvk => write!(f, "derived `ivk` is zero"),
63 DecodingError::UnsupportedChildIndex => write!(
64 f,
65 "unsupported child index (either non-hardened, or non-zero at depth 0)"
66 ),
67 }
68 }
69}
70
71#[cfg(feature = "std")]
72impl std::error::Error for DecodingError {}
73
74#[derive(Clone, Debug)]
82pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
83
84impl PartialEq for SpendAuthorizingKey {
85 fn eq(&self, other: &Self) -> bool {
86 self.0.to_bytes().ct_eq(&other.0.to_bytes()).into()
87 }
88}
89
90#[cfg(feature = "zeroize")]
91impl Zeroize for SpendAuthorizingKey {
92 fn zeroize(&mut self) {
93 self.0.zeroize();
94 }
95}
96
97#[cfg(feature = "zeroize")]
99impl ZeroizeOnDrop for SpendAuthorizingKey {}
100
101impl Eq for SpendAuthorizingKey {}
102
103impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
104 fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
105 jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
106 }
107}
108
109impl SpendAuthorizingKey {
110 fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
112 let mut prf = PrfExpand::SAPLING_ASK.with(sk);
113 let ask = jubjub::Scalar::from_bytes_wide(&prf);
114 zeroize_secret(&mut prf);
115 ask
116 }
117
118 pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
123 if ask.is_zero().into() {
124 None
125 } else {
126 let mut repr = ask.to_bytes();
127 let key = SpendAuthorizingKey(
128 redjubjub::SigningKey::from_bytes(&repr)
129 .expect("canonical scalar encodings are valid RedJubjub signing keys"),
130 );
131 zeroize_secret(&mut repr);
132 Some(key)
133 }
134 }
135
136 fn from_spending_key(sk: &[u8]) -> Option<Self> {
138 Self::from_scalar(Self::derive_inner(sk))
139 }
140
141 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
143 <[u8; 32]>::try_from(bytes)
144 .ok()
145 .and_then(|b| {
146 jubjub::Scalar::from_repr(b)
150 .and_then(|s| {
151 CtOption::new(
152 redjubjub::SigningKey::from_bytes(&b)
153 .expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
154 !s.is_zero(),
155 )
156 })
157 .into()
158 })
159 .map(SpendAuthorizingKey)
160 }
161
162 pub fn to_bytes(&self) -> [u8; 32] {
167 self.0.to_bytes()
168 }
169
170 pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
174 let mut repr = self.0.to_bytes();
175 let ask = jubjub::Scalar::from_repr(repr).unwrap();
176 zeroize_secret(&mut repr);
177 ask
178 }
179
180 pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
184 self.0.randomize(randomizer)
185 }
186}
187
188#[derive(Clone, Debug)]
194pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
195
196impl From<&SpendAuthorizingKey> for SpendValidatingKey {
197 fn from(ask: &SpendAuthorizingKey) -> Self {
198 SpendValidatingKey((&ask.0).into())
199 }
200}
201
202impl PartialEq for SpendValidatingKey {
203 fn eq(&self, other: &Self) -> bool {
204 <[u8; 32]>::from(self.0)
205 .ct_eq(&<[u8; 32]>::from(other.0))
206 .into()
207 }
208}
209
210impl Eq for SpendValidatingKey {}
211
212impl SpendValidatingKey {
213 #[cfg(all(feature = "circuit", test))]
215 pub(crate) fn fake_random<R: Rng>(mut rng: R) -> Self {
216 loop {
217 if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
218 break k;
219 }
220 }
221 }
222
223 #[cfg(feature = "temporary-zcashd")]
225 pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
226 Self::from_bytes(bytes)
227 }
228
229 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
231 <[u8; 32]>::try_from(bytes)
232 .ok()
233 .and_then(|b| {
234 jubjub::SubgroupPoint::from_bytes(&b)
240 .and_then(|p| {
241 CtOption::new(
242 redjubjub::VerificationKey::try_from(b)
243 .expect("RedJubjub permits the set of valid SpendValidatingKeys"),
244 !p.is_identity(),
245 )
246 })
247 .into()
248 })
249 .map(SpendValidatingKey)
250 }
251
252 pub fn to_bytes(&self) -> [u8; 32] {
255 <[u8; 32]>::from(self.0)
256 }
257
258 pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
260 self.0.randomize(randomizer)
261 }
262}
263
264#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub struct OutgoingViewingKey(pub [u8; 32]);
267
268#[cfg(feature = "zeroize")]
271impl Zeroize for OutgoingViewingKey {
272 fn zeroize(&mut self) {
273 self.0.zeroize();
274 }
275}
276
277#[derive(Clone)]
283pub struct ExpandedSpendingKey {
284 ask: SpendAuthorizingKey,
285 nsk: jubjub::Fr,
286 ovk: OutgoingViewingKey,
287}
288
289#[cfg(feature = "zeroize")]
290impl Zeroize for ExpandedSpendingKey {
291 fn zeroize(&mut self) {
292 self.ask.zeroize();
293 self.nsk.zeroize();
294 self.ovk.zeroize();
295 }
296}
297
298#[cfg(feature = "zeroize")]
299impl ZeroizeOnDrop for ExpandedSpendingKey {}
300
301#[cfg(feature = "zeroize")]
302impl Drop for ExpandedSpendingKey {
303 fn drop(&mut self) {
304 self.zeroize();
305 }
306}
307
308impl fmt::Debug for ExpandedSpendingKey {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 f.debug_struct("ExpandedSpendingKey")
311 .finish_non_exhaustive()
312 }
313}
314
315impl ExpandedSpendingKey {
316 pub fn from_spending_key(sk: &[u8]) -> Option<Self> {
322 let ask = SpendAuthorizingKey::from_spending_key(sk)?;
323 let mut nsk_prf = PrfExpand::SAPLING_NSK.with(sk);
324 let mut nsk = jubjub::Fr::from_bytes_wide(&nsk_prf);
325 zeroize_secret(&mut nsk_prf);
326 let mut ovk_prf = PrfExpand::SAPLING_OVK.with(sk);
327 let mut ovk = OutgoingViewingKey([0u8; 32]);
328 ovk.0.copy_from_slice(&ovk_prf[..32]);
329 zeroize_secret(&mut ovk_prf);
330 let expsk = Self::from_parts(ask, nsk, ovk);
331 zeroize_secret(&mut nsk);
332 zeroize_secret(&mut ovk.0);
333 expsk
334 }
335
336 pub fn from_parts(
340 ask: SpendAuthorizingKey,
341 nsk: jubjub::Fr,
342 ovk: OutgoingViewingKey,
343 ) -> Option<Self> {
344 ProofGenerationKey::from_parts((&ask).into(), nsk).map(|_| ExpandedSpendingKey {
345 ask,
346 nsk,
347 ovk,
348 })
349 }
350
351 pub fn ask(&self) -> &SpendAuthorizingKey {
353 &self.ask
354 }
355
356 pub fn nsk(&self) -> &jubjub::Fr {
358 &self.nsk
359 }
360
361 pub fn ovk(&self) -> &OutgoingViewingKey {
363 &self.ovk
364 }
365
366 pub fn proof_generation_key(&self) -> ProofGenerationKey {
367 ProofGenerationKey {
369 ak: (&self.ask).into(),
370 nsk: self.nsk,
371 }
372 }
373
374 pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
380 if b.len() != 96 {
381 return Err(DecodingError::LengthInvalid {
382 expected: 96,
383 actual: b.len(),
384 });
385 }
386
387 let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
388 let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
389 .ok_or(DecodingError::InvalidNsk)?;
390 let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
391
392 Self::from_parts(ask, nsk, ovk).ok_or(DecodingError::InvalidIvk)
393 }
394
395 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
396 let mut repr = [0u8; 96];
397 reader.read_exact(repr.as_mut())?;
398 let result = Self::from_bytes(&repr);
399 zeroize_secret(&mut repr);
400 result.map_err(|e| match e {
401 DecodingError::InvalidAsk => {
402 io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
403 }
404 DecodingError::InvalidNsk => {
405 io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
406 }
407 DecodingError::InvalidIvk => io::Error::new(io::ErrorKind::InvalidData, "ivk is zero"),
408 DecodingError::LengthInvalid { .. } | DecodingError::UnsupportedChildIndex => {
409 unreachable!()
410 }
411 })
412 }
413
414 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
415 writer.write_all(&self.to_bytes())
416 }
417
418 pub fn to_bytes(&self) -> [u8; 96] {
425 let mut result = [0u8; 96];
426 result[0..32].copy_from_slice(&self.ask.to_bytes());
427 result[32..64].copy_from_slice(&self.nsk.to_repr());
428 result[64..96].copy_from_slice(&self.ovk.0);
429 result
430 }
431}
432
433#[derive(Clone)]
439pub struct ProofGenerationKey {
440 ak: SpendValidatingKey,
441 nsk: jubjub::Fr,
442}
443
444#[cfg(feature = "zeroize")]
445impl Zeroize for ProofGenerationKey {
446 fn zeroize(&mut self) {
447 self.nsk.zeroize();
449 }
450}
451
452#[cfg(feature = "zeroize")]
453impl ZeroizeOnDrop for ProofGenerationKey {}
454
455#[cfg(feature = "zeroize")]
456impl Drop for ProofGenerationKey {
457 fn drop(&mut self) {
458 self.zeroize();
459 }
460}
461
462impl fmt::Debug for ProofGenerationKey {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 f.debug_struct("ProofGenerationKey")
465 .field("ak", &self.ak)
466 .finish_non_exhaustive()
467 }
468}
469
470impl ProofGenerationKey {
471 pub fn from_parts(ak: SpendValidatingKey, nsk: jubjub::Fr) -> Option<Self> {
476 let nk = NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * nsk);
477 ViewingKey::from_parts(ak.clone(), nk).map(|_| ProofGenerationKey { ak, nsk })
478 }
479
480 pub fn ak(&self) -> &SpendValidatingKey {
482 &self.ak
483 }
484
485 pub fn nsk(&self) -> &jubjub::Fr {
487 &self.nsk
488 }
489
490 pub fn to_viewing_key(&self) -> ViewingKey {
492 ViewingKey {
494 ak: self.ak.clone(),
495 nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
496 }
497 }
498}
499
500#[derive(Debug, Copy, Clone, PartialEq, Eq)]
502pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
503
504#[derive(Debug, Clone, PartialEq, Eq)]
509pub struct ViewingKey {
510 ak: SpendValidatingKey,
511 nk: NullifierDerivingKey,
512}
513
514impl ViewingKey {
515 pub fn from_parts(ak: SpendValidatingKey, nk: NullifierDerivingKey) -> Option<Self> {
520 let ivk = crh_ivk(ak.to_bytes(), nk.0.to_bytes());
521 SaplingIvk::from_scalar(ivk)
522 .into_option()
523 .map(|_| ViewingKey { ak, nk })
524 }
525
526 pub fn ak(&self) -> &SpendValidatingKey {
528 &self.ak
529 }
530
531 pub fn nk(&self) -> &NullifierDerivingKey {
533 &self.nk
534 }
535
536 pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
537 self.ak.randomize(&ar)
538 }
539
540 pub fn ivk(&self) -> SaplingIvk {
542 SaplingIvk::from_scalar(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
543 .expect("ViewingKey constructors reject a zero ivk")
544 }
545
546 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
547 self.ivk().to_payment_address(diversifier)
548 }
549}
550
551#[derive(Debug, PartialEq, Eq)]
553pub struct FullViewingKey {
554 pub vk: ViewingKey,
555 pub ovk: OutgoingViewingKey,
556}
557
558impl Clone for FullViewingKey {
559 fn clone(&self) -> Self {
560 FullViewingKey {
561 vk: self.vk.clone(),
562 ovk: self.ovk,
563 }
564 }
565}
566
567impl FullViewingKey {
568 pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
570 FullViewingKey {
571 vk: expsk.proof_generation_key().to_viewing_key(),
572 ovk: expsk.ovk,
573 }
574 }
575
576 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
581 let ak = {
582 let mut buf = [0u8; 32];
583 reader.read_exact(&mut buf)?;
584 SpendValidatingKey::from_bytes(&buf)
585 };
586 let nk = {
587 let mut buf = [0u8; 32];
588 reader.read_exact(&mut buf)?;
589 jubjub::SubgroupPoint::from_bytes(&buf)
590 };
591 if ak.is_none() {
592 return Err(io::Error::new(
593 io::ErrorKind::InvalidInput,
594 "ak not of prime order",
595 ));
596 }
597 if nk.is_none().into() {
598 return Err(io::Error::new(
599 io::ErrorKind::InvalidInput,
600 "nk not in prime-order subgroup",
601 ));
602 }
603 let ak = ak.unwrap();
604 let nk = NullifierDerivingKey(nk.unwrap());
605 let vk = ViewingKey::from_parts(ak, nk)
606 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "ivk is zero"))?;
607
608 let mut ovk = [0u8; 32];
609 reader.read_exact(&mut ovk)?;
610
611 Ok(FullViewingKey {
612 vk,
613 ovk: OutgoingViewingKey(ovk),
614 })
615 }
616
617 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
618 writer.write_all(&self.vk.ak().to_bytes())?;
619 writer.write_all(&self.vk.nk().0.to_bytes())?;
620 writer.write_all(&self.ovk.0)?;
621
622 Ok(())
623 }
624
625 pub fn to_bytes(&self) -> [u8; 96] {
626 let mut result = [0u8; 96];
627 self.write(&mut result[..])
628 .expect("should be able to serialize a FullViewingKey");
629 result
630 }
631}
632
633#[derive(Debug, Clone, PartialEq, Eq)]
641pub struct SaplingIvk(jubjub::Fr);
642
643impl SaplingIvk {
644 pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
649 Self::parse_with(bytes, |ivk| ivk)
650 }
651
652 pub(crate) fn parse_with<T>(bytes: &[u8; 32], f: impl FnOnce(Self) -> T) -> CtOption<T> {
658 let in_range = (bytes[31] & 0b1111_1000).ct_eq(&0);
660 jubjub::Fr::from_repr(*bytes)
661 .and_then(|ivk| CtOption::new(f(SaplingIvk(ivk)), in_range & !ivk.is_zero()))
662 }
663
664 pub(crate) fn random<R: rand_core::Rng + ?Sized>(rng: &mut R) -> Self {
666 loop {
667 let mut bytes = [0u8; 32];
668 rng.fill_bytes(&mut bytes);
669 bytes[31] &= 0b0000_0111;
670 if let Some(ivk) = Self::from_bytes(&bytes).into_option() {
671 break ivk;
672 }
673 }
674 }
675
676 pub(crate) fn from_scalar(ivk: jubjub::Fr) -> CtOption<Self> {
680 Self::from_bytes(&ivk.to_repr())
681 }
682
683 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
684 let prepared_ivk = PreparedIncomingViewingKey::new(self);
685 DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
686 .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
687 }
688
689 pub fn to_repr(&self) -> [u8; 32] {
690 self.0.to_repr()
691 }
692}
693
694#[derive(Clone, Debug)]
696pub struct PreparedIncomingViewingKey(PreparedScalar);
697
698#[cfg(feature = "std")]
699impl memuse::DynamicUsage for PreparedIncomingViewingKey {
700 fn dynamic_usage(&self) -> usize {
701 self.0.dynamic_usage()
702 }
703
704 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
705 self.0.dynamic_usage_bounds()
706 }
707}
708
709impl PreparedIncomingViewingKey {
710 pub fn new(ivk: &SaplingIvk) -> Self {
712 Self(PreparedScalar::new(&ivk.0))
713 }
714}
715
716#[derive(Copy, Clone, Debug, PartialEq, Eq)]
717pub struct Diversifier(pub [u8; 11]);
718
719impl Diversifier {
720 pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
721 diversify_hash(&self.0)
722 }
723}
724
725#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
734pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
735
736impl DiversifiedTransmissionKey {
737 pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
743 d.g_d()
744 .map(PreparedBaseSubgroup::new)
745 .map(|g_d| ka_sapling_derive_public_subgroup_prepared(&ivk.0, &g_d))
746 .map(DiversifiedTransmissionKey)
747 }
748
749 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
751 jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
752 }
753
754 pub(crate) fn to_bytes(self) -> [u8; 32] {
756 self.0.to_bytes()
757 }
758
759 pub(crate) fn is_identity(&self) -> bool {
761 self.0.is_identity().into()
762 }
763
764 pub fn inner(&self) -> jubjub::SubgroupPoint {
769 self.0
770 }
771}
772
773impl ConditionallySelectable for DiversifiedTransmissionKey {
774 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
775 DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
776 &a.0, &b.0, choice,
777 ))
778 }
779}
780
781#[derive(Debug)]
792pub struct EphemeralSecretKey(pub(crate) jubjub::Scalar);
793
794impl ConstantTimeEq for EphemeralSecretKey {
795 fn ct_eq(&self, other: &Self) -> subtle::Choice {
796 self.0.ct_eq(&other.0)
797 }
798}
799
800impl EphemeralSecretKey {
801 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
802 jubjub::Scalar::from_bytes(bytes).map(EphemeralSecretKey)
803 }
804
805 pub(crate) fn derive_public(&self, g_d: jubjub::ExtendedPoint) -> EphemeralPublicKey {
806 EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
807 }
808
809 pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
810 SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
811 }
812}
813
814#[derive(Debug)]
825pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
826
827impl EphemeralPublicKey {
828 pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
829 EphemeralPublicKey(epk.into())
830 }
831
832 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
833 jubjub::ExtendedPoint::from_bytes(bytes).map(EphemeralPublicKey)
834 }
835
836 pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
837 EphemeralKeyBytes(self.0.to_bytes())
838 }
839}
840
841#[derive(Clone, Debug)]
843pub struct PreparedEphemeralPublicKey(PreparedBase);
844
845impl PreparedEphemeralPublicKey {
846 pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
847 PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
848 }
849
850 pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
851 SharedSecret(ka_sapling_agree_prepared(&ivk.0, &self.0))
852 }
853}
854
855#[derive(Debug)]
861pub struct SharedSecret(jubjub::SubgroupPoint);
862
863impl SharedSecret {
864 #[cfg(test)]
866 pub(crate) fn to_bytes(&self) -> [u8; 32] {
867 self.0.to_bytes()
868 }
869
870 pub(crate) fn batch_to_affine(
872 shared_secrets: Vec<Option<Self>>,
873 ) -> impl Iterator<Item = Option<jubjub::AffinePoint>> {
874 let secrets: Vec<_> = shared_secrets
876 .iter()
877 .filter_map(|s| s.as_ref().map(|s| jubjub::ExtendedPoint::from(s.0)))
878 .collect();
879
880 let mut secrets_affine = vec![jubjub::AffinePoint::identity(); secrets.len()];
882 group::Curve::batch_normalize(&secrets, &mut secrets_affine);
883
884 let mut secrets_affine = secrets_affine.into_iter();
886 shared_secrets
887 .into_iter()
888 .map(move |s| s.and_then(|_| secrets_affine.next()))
889 }
890
891 pub(crate) fn kdf_sapling(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
895 Self::kdf_sapling_inner(
896 jubjub::ExtendedPoint::from(self.0).to_affine(),
897 ephemeral_key,
898 )
899 }
900
901 pub(crate) fn kdf_sapling_inner(
903 secret: jubjub::AffinePoint,
904 ephemeral_key: &EphemeralKeyBytes,
905 ) -> Blake2bHash {
906 Blake2bParams::new()
907 .hash_length(32)
908 .personal(KDF_SAPLING_PERSONALIZATION)
909 .to_state()
910 .update(&secret.to_bytes())
911 .update(ephemeral_key.as_ref())
912 .finalize()
913 }
914}
915
916#[cfg(any(test, feature = "test-dependencies"))]
917#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
918pub mod testing {
919 use proptest::collection::vec;
920 use proptest::prelude::*;
921
922 use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
923
924 prop_compose! {
925 pub fn arb_expanded_spending_key()(
926 expsk in vec(any::<u8>(), 32..252).prop_filter_map(
927 "spending key must be valid",
928 |sk| ExpandedSpendingKey::from_spending_key(&sk),
929 )
930 ) -> ExpandedSpendingKey {
931 expsk
932 }
933 }
934
935 prop_compose! {
936 pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
937 FullViewingKey::from_expanded_spending_key(&sk)
938 }
939 }
940
941 prop_compose! {
942 pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
943 fvk.vk.ivk()
944 }
945 }
946}
947
948#[cfg(test)]
949mod tests {
950 use alloc::string::ToString;
951 use group::{Group, GroupEncoding};
952
953 use ff::PrimeField;
954 use proptest::prelude::*;
955
956 use super::{
957 testing::arb_incoming_viewing_key, FullViewingKey, SaplingIvk, SpendAuthorizingKey,
958 SpendValidatingKey,
959 };
960 use crate::{constants::SPENDING_KEY_GENERATOR, test_vectors};
961
962 #[test]
963 fn ak_must_be_prime_order() {
964 let mut buf = [0; 96];
965 let identity = jubjub::SubgroupPoint::identity();
966
967 buf[0..32].copy_from_slice(&identity.to_bytes());
969 buf[32..64].copy_from_slice(&identity.to_bytes());
970
971 assert_eq!(
973 FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
974 "ak not of prime order"
975 );
976
977 let basepoint = SPENDING_KEY_GENERATOR;
979 buf[0..32].copy_from_slice(&basepoint.to_bytes());
980
981 assert!(FullViewingKey::read(&buf[..]).is_ok());
983 }
984
985 #[test]
986 fn spend_auth_sig_test_vectors() {
987 for tv in test_vectors::signatures::make_test_vectors() {
988 let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
989 let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
990 let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
991 let sig = redjubjub::Signature::from(tv.sig);
992 let rsig = redjubjub::Signature::from(tv.rsig);
993
994 let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
995
996 assert_eq!(sk.randomize(&alpha).to_bytes(), tv.rsk);
997 assert_eq!(vk.randomize(&alpha), rvk);
998
999 assert_eq!(
1002 vk.0.verify(&tv.m, &rsig),
1003 Err(redjubjub::Error::InvalidSignature),
1004 );
1005 assert_eq!(
1006 rvk.verify(&tv.m, &sig),
1007 Err(redjubjub::Error::InvalidSignature),
1008 );
1009 }
1010 }
1011
1012 #[test]
1013 fn ivk_encoding_must_be_in_range() {
1014 let parses = |bytes: [u8; 32]| bool::from(SaplingIvk::from_bytes(&bytes).is_some());
1015
1016 assert!(!parses([0; 32]));
1018
1019 let mut one = [0; 32];
1020 one[0] = 1;
1021 assert!(parses(one));
1022
1023 let mut max = [0xff; 32];
1024 max[31] = 0b0000_0111;
1025 assert!(parses(max));
1026
1027 let mut two_pow_251 = [0; 32];
1029 two_pow_251[31] = 0b0000_1000;
1030 assert!(!parses(two_pow_251));
1031 assert!(!parses((-jubjub::Fr::one()).to_repr()));
1032 }
1033
1034 proptest! {
1035 #[test]
1036 fn ivk_encoding_round_trip(ivk in arb_incoming_viewing_key()) {
1037 prop_assert_eq!(SaplingIvk::from_bytes(&ivk.to_repr()).into_option(), Some(ivk));
1038 }
1039 }
1040}
1041
1042#[cfg(all(test, feature = "zeroize"))]
1043mod zeroize_tests {
1044 use zeroize::Zeroize;
1045
1046 use super::ExpandedSpendingKey;
1047
1048 #[test]
1049 fn expanded_spending_key_zeroizes() {
1050 let mut expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1051 assert_ne!(expsk.to_bytes(), [0; 96]);
1052
1053 expsk.zeroize();
1054 assert_eq!(expsk.to_bytes(), [0; 96]);
1055 }
1056
1057 #[test]
1058 fn proof_generation_key_zeroizes() {
1059 let expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1060 let mut pgk = expsk.proof_generation_key();
1061 assert_ne!(pgk.nsk, <jubjub::Fr as ff::Field>::ZERO);
1062
1063 pgk.zeroize();
1064 assert_eq!(pgk.nsk, <jubjub::Fr as ff::Field>::ZERO);
1065 }
1066
1067 #[test]
1068 fn spend_authorizing_key_zeroizes() {
1069 let expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1070 let mut ask = expsk.ask.clone();
1071 assert_ne!(ask.to_bytes(), [0; 32]);
1072
1073 ask.zeroize();
1074 assert_eq!(ask.to_bytes(), [0; 32]);
1075 }
1076}