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