1use alloc::vec::Vec;
4use corez::io::{self, Read, Write};
5
6use ::zip32::{AccountId, ChildIndex};
7use aes::Aes256;
8use blake2b_simd::{Hash as Blake2bHash, Params};
9use fpe::ff1::{BinaryNumeralString, FF1};
10use group::{
11 ff::{Field, PrimeField},
12 prime::PrimeCurveAffine,
13 Curve, GroupEncoding,
14};
15use pasta_curves::pallas;
16use rand::RngCore;
17use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
18use zcash_note_encryption::EphemeralKeyBytes;
19
20use crate::{
21 address::Address,
22 primitives::redpallas::{self, SpendAuth},
23 spec::{
24 commit_ivk, diversify_hash, extract_p, ka_orchard, ka_orchard_prepared, prf_nf, to_base,
25 to_scalar, NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar,
26 PreparedNonIdentityBase, PreparedNonZeroScalar, PrfExpand,
27 },
28 zip32::{self, ExtendedSpendingKey},
29};
30
31pub use ::zip32::{DiversifierIndex, Scope};
32
33const KDF_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_OrchardKDF";
34const ZIP32_PURPOSE: u32 = 32;
35
36#[derive(Debug, Copy, Clone)]
42pub struct SpendingKey([u8; 32]);
43
44impl ConstantTimeEq for SpendingKey {
45 fn ct_eq(&self, other: &Self) -> Choice {
46 self.to_bytes().ct_eq(other.to_bytes())
47 }
48}
49
50impl SpendingKey {
51 pub fn random(rng: &mut impl RngCore) -> Self {
58 loop {
59 let mut bytes = [0; 32];
60 rng.fill_bytes(&mut bytes);
61 let sk = SpendingKey::from_bytes(bytes);
62 if sk.is_some().into() {
63 break sk.unwrap();
64 }
65 }
66 }
67
68 pub fn from_bytes(sk: [u8; 32]) -> CtOption<Self> {
72 let sk = SpendingKey(sk);
73 let ask = SpendAuthorizingKey::derive_inner(&sk);
78 let fvk = (&sk).into();
80 let external_ivk = KeyAgreementPrivateKey::derive_inner(&fvk);
81 let internal_ivk = KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal());
82 CtOption::new(
83 sk,
84 !(ask.is_zero() | external_ivk.is_none() | internal_ivk.is_none()),
85 )
86 }
87
88 pub fn to_bytes(&self) -> &[u8; 32] {
90 &self.0
91 }
92
93 pub fn from_zip32_seed(
95 seed: &[u8],
96 coin_type: u32,
97 account: AccountId,
98 ) -> Result<Self, zip32::Error> {
99 if coin_type >= (1 << 31) {
100 return Err(zip32::Error::InvalidChildIndex(coin_type));
101 }
102
103 let path = &[
105 ChildIndex::hardened(ZIP32_PURPOSE),
106 ChildIndex::hardened(coin_type),
107 ChildIndex::hardened(account.into()),
108 ];
109 ExtendedSpendingKey::from_path(seed, path).map(|esk| esk.sk())
110 }
111}
112
113#[derive(Clone, Debug)]
120pub struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
121
122impl SpendAuthorizingKey {
123 pub fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
125 to_scalar(PrfExpand::ORCHARD_ASK.with(&sk.0))
126 }
127
128 pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::SigningKey<SpendAuth> {
132 self.0.randomize(randomizer)
133 }
134}
135
136impl From<&SpendingKey> for SpendAuthorizingKey {
137 fn from(sk: &SpendingKey) -> Self {
138 let ask = Self::derive_inner(sk);
139 assert!(!bool::from(ask.is_zero()));
141 let ret = SpendAuthorizingKey(ask.to_repr().try_into().unwrap());
143 if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
145 SpendAuthorizingKey((-ask).to_repr().try_into().unwrap())
146 } else {
147 ret
148 }
149 }
150}
151
152#[derive(Debug, Clone, PartialOrd, Ord)]
160pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
161
162impl From<&SpendAuthorizingKey> for SpendValidatingKey {
163 fn from(ask: &SpendAuthorizingKey) -> Self {
164 SpendValidatingKey((&ask.0).into())
165 }
166}
167
168impl From<&SpendValidatingKey> for pallas::Point {
169 fn from(spend_validating_key: &SpendValidatingKey) -> pallas::Point {
170 pallas::Point::from_bytes(&(&spend_validating_key.0).into()).unwrap()
171 }
172}
173
174impl PartialEq for SpendValidatingKey {
175 fn eq(&self, other: &Self) -> bool {
176 <[u8; 32]>::from(&self.0).eq(&<[u8; 32]>::from(&other.0))
177 }
178}
179
180impl Eq for SpendValidatingKey {}
181
182impl SpendValidatingKey {
183 pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::VerificationKey<SpendAuth> {
185 self.0.randomize(randomizer)
186 }
187
188 #[cfg_attr(feature = "unstable-frost", visibility::make(pub))]
191 pub(crate) fn to_bytes(&self) -> [u8; 32] {
192 let b = <[u8; 32]>::from(&self.0);
195 assert!(b[31] & 0x80 == 0);
196 b
197 }
198
199 #[cfg_attr(feature = "unstable-frost", visibility::make(pub))]
203 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
204 <[u8; 32]>::try_from(bytes)
205 .ok()
206 .and_then(|b| {
207 if b != [0; 32] && b[31] & 0x80 == 0 {
212 <redpallas::VerificationKey<SpendAuth>>::try_from(b).ok()
213 } else {
214 None
215 }
216 })
217 .map(SpendValidatingKey)
218 }
219}
220
221#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
229pub struct NullifierDerivingKey(pallas::Base);
230
231impl NullifierDerivingKey {
232 pub fn inner(&self) -> pallas::Base {
234 self.0
235 }
236}
237
238impl From<&SpendingKey> for NullifierDerivingKey {
239 fn from(sk: &SpendingKey) -> Self {
240 NullifierDerivingKey(to_base(PrfExpand::ORCHARD_NK.with(&sk.0)))
241 }
242}
243
244impl NullifierDerivingKey {
245 pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
247 prf_nf(self.0, rho)
248 }
249
250 pub(crate) fn to_bytes(self) -> [u8; 32] {
252 <[u8; 32]>::from(self.0)
253 }
254
255 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
257 let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
258 let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey);
259 if nk.is_some().into() {
260 Some(nk.unwrap())
261 } else {
262 None
263 }
264 }
265}
266
267#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
273pub struct CommitIvkRandomness(pallas::Scalar);
274
275impl From<&SpendingKey> for CommitIvkRandomness {
276 fn from(sk: &SpendingKey) -> Self {
277 CommitIvkRandomness(to_scalar(PrfExpand::ORCHARD_RIVK.with(&sk.0)))
278 }
279}
280
281impl CommitIvkRandomness {
282 pub fn inner(&self) -> pallas::Scalar {
284 self.0
285 }
286
287 pub(crate) fn to_bytes(self) -> [u8; 32] {
289 <[u8; 32]>::from(self.0)
290 }
291
292 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
294 let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
295 let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness);
296 if rivk.is_some().into() {
297 Some(rivk.unwrap())
298 } else {
299 None
300 }
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
313pub struct FullViewingKey {
314 ak: SpendValidatingKey,
315 nk: NullifierDerivingKey,
316 rivk: CommitIvkRandomness,
317}
318
319impl From<&SpendingKey> for FullViewingKey {
320 fn from(sk: &SpendingKey) -> Self {
321 FullViewingKey {
322 ak: (&SpendAuthorizingKey::from(sk)).into(),
323 nk: sk.into(),
324 rivk: sk.into(),
325 }
326 }
327}
328
329impl From<&ExtendedSpendingKey> for FullViewingKey {
330 fn from(extsk: &ExtendedSpendingKey) -> Self {
331 (&extsk.sk()).into()
332 }
333}
334
335impl From<FullViewingKey> for SpendValidatingKey {
336 fn from(fvk: FullViewingKey) -> Self {
337 fvk.ak
338 }
339}
340
341impl FullViewingKey {
342 pub fn nk(&self) -> &NullifierDerivingKey {
344 &self.nk
345 }
346
347 pub fn rivk(&self, scope: Scope) -> CommitIvkRandomness {
349 match scope {
350 Scope::External => self.rivk,
351 Scope::Internal => {
352 let k = self.rivk.0.to_repr();
353 let ak = self.ak.to_bytes();
354 let nk = self.nk.to_bytes();
355 CommitIvkRandomness(to_scalar(
356 PrfExpand::ORCHARD_RIVK_INTERNAL.with(&k, &ak, &nk),
357 ))
358 }
359 }
360 }
361
362 fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
366 let k = self.rivk.0.to_repr();
367 let b = [(&self.ak.0).into(), self.nk.0.to_repr()];
368 let r = PrfExpand::ORCHARD_DK_OVK.with(&k, &b[0], &b[1]);
369 (
370 DiversifierKey(r[..32].try_into().unwrap()),
371 OutgoingViewingKey(r[32..].try_into().unwrap()),
372 )
373 }
374
375 pub fn address_at(&self, j: impl Into<DiversifierIndex>, scope: Scope) -> Address {
377 self.to_ivk(scope).address_at(j)
378 }
379
380 pub fn address(&self, d: Diversifier, scope: Scope) -> Address {
382 match scope {
384 Scope::External => KeyAgreementPrivateKey::from_fvk(self),
385 Scope::Internal => KeyAgreementPrivateKey::from_fvk(&self.derive_internal()),
386 }
387 .address(d)
388 }
389
390 pub fn scope_for_address(&self, address: &Address) -> Option<Scope> {
393 [Scope::External, Scope::Internal]
394 .into_iter()
395 .find(|scope| self.to_ivk(*scope).diversifier_index(address).is_some())
396 }
397
398 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
402 writer.write_all(&self.to_bytes())
403 }
404
405 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
409 let mut data = [0u8; 96];
410 reader.read_exact(&mut data)?;
411
412 Self::from_bytes(&data).ok_or_else(|| {
413 io::Error::new(
414 io::ErrorKind::InvalidInput,
415 "Unable to deserialize a valid Orchard FullViewingKey from bytes",
416 )
417 })
418 }
419
420 pub fn to_bytes(&self) -> [u8; 96] {
424 let mut result = [0u8; 96];
425 result[0..32].copy_from_slice(&<[u8; 32]>::from(self.ak.0.clone()));
426 result[32..64].copy_from_slice(&self.nk.0.to_repr());
427 result[64..96].copy_from_slice(&self.rivk.0.to_repr());
428 result
429 }
430
431 pub fn from_bytes(bytes: &[u8; 96]) -> Option<Self> {
435 let ak = SpendValidatingKey::from_bytes(&bytes[..32])?;
436 let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
437 let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
438
439 let fvk = FullViewingKey { ak, nk, rivk };
440
441 let _: NonZeroPallasBase = Option::from(KeyAgreementPrivateKey::derive_inner(&fvk))?;
443 let _: NonZeroPallasBase =
444 Option::from(KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal()))?;
445
446 Some(fvk)
447 }
448
449 fn derive_internal(&self) -> Self {
454 FullViewingKey {
455 ak: self.ak.clone(),
456 nk: self.nk,
457 rivk: self.rivk(Scope::Internal),
458 }
459 }
460
461 pub fn to_ivk(&self, scope: Scope) -> IncomingViewingKey {
463 match scope {
464 Scope::External => IncomingViewingKey::from_fvk(self),
465 Scope::Internal => IncomingViewingKey::from_fvk(&self.derive_internal()),
466 }
467 }
468
469 pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
471 match scope {
472 Scope::External => OutgoingViewingKey::from_fvk(self),
473 Scope::Internal => OutgoingViewingKey::from_fvk(&self.derive_internal()),
474 }
475 }
476}
477
478#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
484pub(crate) struct DiversifierKey([u8; 32]);
485
486impl DiversifierKey {
487 pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
489 let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
490 let enc = ff
491 .encrypt(
492 &[],
493 &BinaryNumeralString::from_bytes_le(j.into().as_bytes()),
494 )
495 .unwrap();
496 Diversifier(enc.to_bytes_le().try_into().unwrap())
497 }
498
499 pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
501 let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
502 let dec = ff
503 .decrypt(&[], &BinaryNumeralString::from_bytes_le(d.as_array()))
504 .unwrap();
505 DiversifierIndex::from(<[u8; 11]>::try_from(dec.to_bytes_le()).unwrap())
506 }
507
508 pub fn to_bytes(&self) -> &[u8; 32] {
510 &self.0
511 }
512
513 pub fn from_bytes(bytes: [u8; 32]) -> Self {
515 DiversifierKey(bytes)
516 }
517}
518
519#[derive(Clone, Copy, Debug, PartialEq, Eq)]
526pub struct Diversifier([u8; 11]);
527
528impl Diversifier {
529 pub fn from_bytes(d: [u8; 11]) -> Self {
531 Diversifier(d)
532 }
533
534 pub fn as_array(&self) -> &[u8; 11] {
536 &self.0
537 }
538}
539
540#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
559struct KeyAgreementPrivateKey(NonZeroPallasScalar);
560
561impl KeyAgreementPrivateKey {
562 fn from_fvk(fvk: &FullViewingKey) -> Self {
568 let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
570 KeyAgreementPrivateKey(ivk.into())
571 }
572}
573
574impl KeyAgreementPrivateKey {
575 fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
581 let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
582 commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
583 .and_then(NonZeroPallasBase::from_base)
600 }
601
602 fn address(&self, d: Diversifier) -> Address {
604 let prepared_ivk = PreparedIncomingViewingKey::new_inner(self);
605 let pk_d = DiversifiedTransmissionKey::derive(&prepared_ivk, &d);
606 Address::from_parts(d, pk_d)
607 }
608}
609
610#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
623pub struct IncomingViewingKey {
624 dk: DiversifierKey,
625 ivk: KeyAgreementPrivateKey,
626}
627
628impl IncomingViewingKey {
629 fn from_fvk(fvk: &FullViewingKey) -> Self {
631 IncomingViewingKey {
632 dk: fvk.derive_dk_ovk().0,
633 ivk: KeyAgreementPrivateKey::from_fvk(fvk),
634 }
635 }
636}
637
638impl IncomingViewingKey {
639 pub fn to_bytes(&self) -> [u8; 64] {
643 let mut result = [0u8; 64];
644 result[..32].copy_from_slice(self.dk.to_bytes());
645 result[32..].copy_from_slice(&self.ivk.0.to_repr());
646 result
647 }
648
649 pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
651 NonZeroPallasBase::from_bytes(bytes[32..].try_into().unwrap()).map(|ivk| {
652 IncomingViewingKey {
653 dk: DiversifierKey(bytes[..32].try_into().unwrap()),
654 ivk: KeyAgreementPrivateKey(ivk.into()),
655 }
656 })
657 }
658
659 pub fn diversifier_index(&self, addr: &Address) -> Option<DiversifierIndex> {
663 let j = self.dk.diversifier_index(&addr.diversifier());
664 if &self.address_at(j) == addr {
665 Some(j)
666 } else {
667 None
668 }
669 }
670
671 pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
673 self.address(self.dk.get(j))
674 }
675
676 pub fn address(&self, d: Diversifier) -> Address {
678 self.ivk.address(d)
679 }
680
681 pub fn prepare(&self) -> PreparedIncomingViewingKey {
683 PreparedIncomingViewingKey::new(self)
684 }
685}
686
687#[derive(Clone, Debug)]
689pub struct PreparedIncomingViewingKey(PreparedNonZeroScalar);
690
691#[cfg(feature = "std")]
692impl memuse::DynamicUsage for PreparedIncomingViewingKey {
693 fn dynamic_usage(&self) -> usize {
694 self.0.dynamic_usage()
695 }
696
697 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
698 self.0.dynamic_usage_bounds()
699 }
700}
701
702impl PreparedIncomingViewingKey {
703 pub fn new(ivk: &IncomingViewingKey) -> Self {
706 Self::new_inner(&ivk.ivk)
707 }
708
709 fn new_inner(ivk: &KeyAgreementPrivateKey) -> Self {
710 Self(PreparedNonZeroScalar::new(&ivk.0))
711 }
712}
713
714#[derive(Debug, Clone)]
724pub struct OutgoingViewingKey([u8; 32]);
725
726impl OutgoingViewingKey {
727 fn from_fvk(fvk: &FullViewingKey) -> Self {
729 fvk.derive_dk_ovk().1
730 }
731}
732
733impl From<[u8; 32]> for OutgoingViewingKey {
734 fn from(ovk: [u8; 32]) -> Self {
735 OutgoingViewingKey(ovk)
736 }
737}
738
739impl AsRef<[u8; 32]> for OutgoingViewingKey {
740 fn as_ref(&self) -> &[u8; 32] {
741 &self.0
742 }
743}
744
745#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
751pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
752
753impl DiversifiedTransmissionKey {
754 pub fn inner(&self) -> NonIdentityPallasPoint {
756 self.0
757 }
758}
759
760impl DiversifiedTransmissionKey {
761 pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self {
765 let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array()));
766 DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d))
767 }
768
769 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
771 NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
772 }
773
774 pub fn to_bytes(self) -> [u8; 32] {
776 self.0.to_bytes()
777 }
778}
779
780impl ConditionallySelectable for DiversifiedTransmissionKey {
781 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
782 DiversifiedTransmissionKey(NonIdentityPallasPoint::conditional_select(
783 &a.0, &b.0, choice,
784 ))
785 }
786}
787
788#[derive(Debug)]
799pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar);
800
801impl ConstantTimeEq for EphemeralSecretKey {
802 fn ct_eq(&self, other: &Self) -> subtle::Choice {
803 self.0.ct_eq(&other.0)
804 }
805}
806
807impl EphemeralSecretKey {
808 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
809 NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey)
810 }
811
812 pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey {
813 EphemeralPublicKey(ka_orchard(&self.0, &g_d))
814 }
815
816 pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
817 SharedSecret(ka_orchard(&self.0, &pk_d.0))
818 }
819}
820
821#[derive(Debug)]
832pub struct EphemeralPublicKey(NonIdentityPallasPoint);
833
834impl EphemeralPublicKey {
835 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
836 NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey)
837 }
838
839 pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
840 EphemeralKeyBytes(self.0.to_bytes())
841 }
842
843 pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret {
844 SharedSecret(ka_orchard(&ivk.ivk.0, &self.0))
845 }
846}
847
848#[derive(Clone, Debug)]
850pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase);
851
852impl PreparedEphemeralPublicKey {
853 pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
854 PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0))
855 }
856
857 pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
858 SharedSecret(ka_orchard_prepared(&ivk.0, &self.0))
859 }
860}
861
862#[derive(Debug)]
868pub struct SharedSecret(NonIdentityPallasPoint);
869
870impl SharedSecret {
871 #[cfg(test)]
873 pub(crate) fn to_bytes(&self) -> [u8; 32] {
874 self.0.to_bytes()
875 }
876
877 pub(crate) fn batch_to_affine(
879 shared_secrets: Vec<Option<Self>>,
880 ) -> impl Iterator<Item = Option<pallas::Affine>> {
881 let secrets: Vec<_> = shared_secrets
883 .iter()
884 .filter_map(|s| s.as_ref().map(|s| *(s.0)))
885 .collect();
886
887 let mut secrets_affine = vec![pallas::Affine::identity(); secrets.len()];
889 group::Curve::batch_normalize(&secrets, &mut secrets_affine);
890
891 let mut secrets_affine = secrets_affine.into_iter();
893 shared_secrets
894 .into_iter()
895 .map(move |s| s.and_then(|_| secrets_affine.next()))
896 }
897
898 pub(crate) fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
902 Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key)
903 }
904
905 pub(crate) fn kdf_orchard_inner(
907 secret: pallas::Affine,
908 ephemeral_key: &EphemeralKeyBytes,
909 ) -> Blake2bHash {
910 Params::new()
911 .hash_length(32)
912 .personal(KDF_ORCHARD_PERSONALIZATION)
913 .to_state()
914 .update(&secret.to_bytes())
915 .update(&ephemeral_key.0)
916 .finalize()
917 }
918}
919
920#[cfg(any(test, feature = "test-dependencies"))]
922#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
923pub mod testing {
924 use proptest::prelude::*;
925
926 use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey};
927
928 prop_compose! {
929 pub fn arb_spending_key()(
931 key in prop::array::uniform32(prop::num::u8::ANY)
932 .prop_map(SpendingKey::from_bytes)
933 .prop_filter(
934 "Values must correspond to valid Orchard spending keys.",
935 |opt| bool::from(opt.is_some())
936 )
937 ) -> SpendingKey {
938 key.unwrap()
939 }
940 }
941
942 prop_compose! {
943 pub fn arb_esk()(
945 esk in prop::array::uniform32(prop::num::u8::ANY)
946 .prop_map(|b| EphemeralSecretKey::from_bytes(&b))
947 .prop_filter(
948 "Values must correspond to valid Orchard ephemeral secret keys.",
949 |opt| bool::from(opt.is_some())
950 )
951 ) -> EphemeralSecretKey {
952 esk.unwrap()
953 }
954 }
955
956 prop_compose! {
957 pub(crate) fn arb_diversifier_key()(
959 dk_bytes in prop::array::uniform32(prop::num::u8::ANY)
960 ) -> DiversifierKey {
961 DiversifierKey::from_bytes(dk_bytes)
962 }
963 }
964
965 prop_compose! {
966 pub fn arb_diversifier_index()(
968 d_bytes in prop::array::uniform11(prop::num::u8::ANY)
969 ) -> DiversifierIndex {
970 DiversifierIndex::from(d_bytes)
971 }
972 }
973}
974
975#[cfg(test)]
976mod tests {
977 use ff::PrimeField;
978 use proptest::prelude::*;
979
980 use super::{
981 testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key},
982 *,
983 };
984 use crate::{
985 note::{ExtractedNoteCommitment, RandomSeed, Rho},
986 value::NoteValue,
987 Note,
988 };
989
990 #[test]
991 fn spend_validating_key_from_bytes() {
992 assert!(SpendValidatingKey::from_bytes(&[0; 32]).is_none());
994 }
995
996 #[test]
997 fn parsers_reject_invalid() {
998 assert!(bool::from(
999 EphemeralSecretKey::from_bytes(&[0xff; 32]).is_none()
1000 ));
1001 assert!(bool::from(
1002 EphemeralPublicKey::from_bytes(&[0xff; 32]).is_none()
1003 ));
1004 }
1005
1006 proptest! {
1007 #[test]
1008 fn key_agreement(
1009 sk in arb_spending_key(),
1010 esk in arb_esk(),
1011 j in arb_diversifier_index(),
1012 ) {
1013 let ivk = IncomingViewingKey::from_fvk(&(&sk).into());
1014 let addr = ivk.address_at(j);
1015
1016 let epk = esk.derive_public(addr.g_d());
1017
1018 assert!(bool::from(
1019 esk.agree(addr.pk_d()).0.ct_eq(&epk.agree(&ivk).0)
1020 ));
1021 }
1022 }
1023
1024 proptest! {
1025 #[test]
1026 fn diversifier_index(
1027 dk in arb_diversifier_key(),
1028 j in arb_diversifier_index(),
1029 ) {
1030 let d = dk.get(j);
1031 assert_eq!(j, dk.diversifier_index(&d));
1032 }
1033 }
1034
1035 #[test]
1036 fn test_vectors() {
1037 for tv in crate::test_vectors::keys::test_vectors() {
1038 let sk = SpendingKey::from_bytes(tv.sk).unwrap();
1039
1040 let ask: SpendAuthorizingKey = (&sk).into();
1041 assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask);
1042
1043 let ak: SpendValidatingKey = (&ask).into();
1044 assert_eq!(<[u8; 32]>::from(ak.0), tv.ak);
1045
1046 let nk: NullifierDerivingKey = (&sk).into();
1047 assert_eq!(nk.0.to_repr(), tv.nk);
1048
1049 let rivk: CommitIvkRandomness = (&sk).into();
1050 assert_eq!(rivk.0.to_repr(), tv.rivk);
1051
1052 let fvk: FullViewingKey = (&sk).into();
1053 assert_eq!(<[u8; 32]>::from(&fvk.ak.0), tv.ak);
1054 assert_eq!(fvk.nk().0.to_repr(), tv.nk);
1055 assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
1056
1057 let external_ivk = fvk.to_ivk(Scope::External);
1058 assert_eq!(external_ivk.ivk.0.to_repr(), tv.ivk);
1059
1060 let diversifier = Diversifier(tv.default_d);
1061
1062 let addr = fvk.address(diversifier, Scope::External);
1063 assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
1064
1065 let rho = Rho::from_bytes(&tv.note_rho).unwrap();
1066 let note = Note::from_parts(
1067 addr,
1068 NoteValue::from_raw(tv.note_v),
1069 rho,
1070 RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
1071 )
1072 .unwrap();
1073
1074 let cmx: ExtractedNoteCommitment = note.commitment().into();
1075 assert_eq!(cmx.to_bytes(), tv.note_cmx);
1076
1077 assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
1078
1079 let internal_rivk = fvk.rivk(Scope::Internal);
1080 assert_eq!(internal_rivk.0.to_repr(), tv.internal_rivk);
1081
1082 let internal_ivk = fvk.to_ivk(Scope::Internal);
1083 assert_eq!(internal_ivk.ivk.0.to_repr(), tv.internal_ivk);
1084 assert_eq!(internal_ivk.dk.0, tv.internal_dk);
1085
1086 let internal_ovk = fvk.to_ovk(Scope::Internal);
1087 assert_eq!(internal_ovk.0, tv.internal_ovk);
1088 }
1089 }
1090}