1use alloc::vec::Vec;
4use core2::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(crate) 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 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(crate) struct NullifierDerivingKey(pallas::Base);
230
231impl NullifierDerivingKey {
232 pub(crate) fn inner(&self) -> pallas::Base {
233 self.0
234 }
235}
236
237impl From<&SpendingKey> for NullifierDerivingKey {
238 fn from(sk: &SpendingKey) -> Self {
239 NullifierDerivingKey(to_base(PrfExpand::ORCHARD_NK.with(&sk.0)))
240 }
241}
242
243impl NullifierDerivingKey {
244 pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
245 prf_nf(self.0, rho)
246 }
247
248 pub(crate) fn to_bytes(self) -> [u8; 32] {
250 <[u8; 32]>::from(self.0)
251 }
252
253 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
254 let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
255 let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey);
256 if nk.is_some().into() {
257 Some(nk.unwrap())
258 } else {
259 None
260 }
261 }
262}
263
264#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
270pub(crate) struct CommitIvkRandomness(pallas::Scalar);
271
272impl From<&SpendingKey> for CommitIvkRandomness {
273 fn from(sk: &SpendingKey) -> Self {
274 CommitIvkRandomness(to_scalar(PrfExpand::ORCHARD_RIVK.with(&sk.0)))
275 }
276}
277
278impl CommitIvkRandomness {
279 pub(crate) fn inner(&self) -> pallas::Scalar {
280 self.0
281 }
282
283 pub(crate) fn to_bytes(self) -> [u8; 32] {
285 <[u8; 32]>::from(self.0)
286 }
287
288 pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
289 let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
290 let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness);
291 if rivk.is_some().into() {
292 Some(rivk.unwrap())
293 } else {
294 None
295 }
296 }
297}
298
299#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
308pub struct FullViewingKey {
309 ak: SpendValidatingKey,
310 nk: NullifierDerivingKey,
311 rivk: CommitIvkRandomness,
312}
313
314impl From<&SpendingKey> for FullViewingKey {
315 fn from(sk: &SpendingKey) -> Self {
316 FullViewingKey {
317 ak: (&SpendAuthorizingKey::from(sk)).into(),
318 nk: sk.into(),
319 rivk: sk.into(),
320 }
321 }
322}
323
324impl From<&ExtendedSpendingKey> for FullViewingKey {
325 fn from(extsk: &ExtendedSpendingKey) -> Self {
326 (&extsk.sk()).into()
327 }
328}
329
330impl From<FullViewingKey> for SpendValidatingKey {
331 fn from(fvk: FullViewingKey) -> Self {
332 fvk.ak
333 }
334}
335
336impl FullViewingKey {
337 pub(crate) fn nk(&self) -> &NullifierDerivingKey {
338 &self.nk
339 }
340
341 pub(crate) fn rivk(&self, scope: Scope) -> CommitIvkRandomness {
343 match scope {
344 Scope::External => self.rivk,
345 Scope::Internal => {
346 let k = self.rivk.0.to_repr();
347 let ak = self.ak.to_bytes();
348 let nk = self.nk.to_bytes();
349 CommitIvkRandomness(to_scalar(
350 PrfExpand::ORCHARD_RIVK_INTERNAL.with(&k, &ak, &nk),
351 ))
352 }
353 }
354 }
355
356 fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
360 let k = self.rivk.0.to_repr();
361 let b = [(&self.ak.0).into(), self.nk.0.to_repr()];
362 let r = PrfExpand::ORCHARD_DK_OVK.with(&k, &b[0], &b[1]);
363 (
364 DiversifierKey(r[..32].try_into().unwrap()),
365 OutgoingViewingKey(r[32..].try_into().unwrap()),
366 )
367 }
368
369 pub fn address_at(&self, j: impl Into<DiversifierIndex>, scope: Scope) -> Address {
371 self.to_ivk(scope).address_at(j)
372 }
373
374 pub fn address(&self, d: Diversifier, scope: Scope) -> Address {
376 match scope {
378 Scope::External => KeyAgreementPrivateKey::from_fvk(self),
379 Scope::Internal => KeyAgreementPrivateKey::from_fvk(&self.derive_internal()),
380 }
381 .address(d)
382 }
383
384 pub fn scope_for_address(&self, address: &Address) -> Option<Scope> {
387 [Scope::External, Scope::Internal]
388 .into_iter()
389 .find(|scope| self.to_ivk(*scope).diversifier_index(address).is_some())
390 }
391
392 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
396 writer.write_all(&self.to_bytes())
397 }
398
399 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
403 let mut data = [0u8; 96];
404 reader.read_exact(&mut data)?;
405
406 Self::from_bytes(&data).ok_or_else(|| {
407 io::Error::new(
408 io::ErrorKind::InvalidInput,
409 "Unable to deserialize a valid Orchard FullViewingKey from bytes",
410 )
411 })
412 }
413
414 pub fn to_bytes(&self) -> [u8; 96] {
418 let mut result = [0u8; 96];
419 result[0..32].copy_from_slice(&<[u8; 32]>::from(self.ak.0.clone()));
420 result[32..64].copy_from_slice(&self.nk.0.to_repr());
421 result[64..96].copy_from_slice(&self.rivk.0.to_repr());
422 result
423 }
424
425 pub fn from_bytes(bytes: &[u8; 96]) -> Option<Self> {
429 let ak = SpendValidatingKey::from_bytes(&bytes[..32])?;
430 let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
431 let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
432
433 let fvk = FullViewingKey { ak, nk, rivk };
434
435 let _: NonZeroPallasBase = Option::from(KeyAgreementPrivateKey::derive_inner(&fvk))?;
437 let _: NonZeroPallasBase =
438 Option::from(KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal()))?;
439
440 Some(fvk)
441 }
442
443 fn derive_internal(&self) -> Self {
448 FullViewingKey {
449 ak: self.ak.clone(),
450 nk: self.nk,
451 rivk: self.rivk(Scope::Internal),
452 }
453 }
454
455 pub fn to_ivk(&self, scope: Scope) -> IncomingViewingKey {
457 match scope {
458 Scope::External => IncomingViewingKey::from_fvk(self),
459 Scope::Internal => IncomingViewingKey::from_fvk(&self.derive_internal()),
460 }
461 }
462
463 pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
465 match scope {
466 Scope::External => OutgoingViewingKey::from_fvk(self),
467 Scope::Internal => OutgoingViewingKey::from_fvk(&self.derive_internal()),
468 }
469 }
470}
471
472#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
478pub(crate) struct DiversifierKey([u8; 32]);
479
480impl DiversifierKey {
481 pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
483 let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
484 let enc = ff
485 .encrypt(
486 &[],
487 &BinaryNumeralString::from_bytes_le(j.into().as_bytes()),
488 )
489 .unwrap();
490 Diversifier(enc.to_bytes_le().try_into().unwrap())
491 }
492
493 pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
495 let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
496 let dec = ff
497 .decrypt(&[], &BinaryNumeralString::from_bytes_le(d.as_array()))
498 .unwrap();
499 DiversifierIndex::from(<[u8; 11]>::try_from(dec.to_bytes_le()).unwrap())
500 }
501
502 pub fn to_bytes(&self) -> &[u8; 32] {
504 &self.0
505 }
506
507 pub fn from_bytes(bytes: [u8; 32]) -> Self {
509 DiversifierKey(bytes)
510 }
511}
512
513#[derive(Clone, Copy, Debug, PartialEq, Eq)]
520pub struct Diversifier([u8; 11]);
521
522impl Diversifier {
523 pub fn from_bytes(d: [u8; 11]) -> Self {
525 Diversifier(d)
526 }
527
528 pub fn as_array(&self) -> &[u8; 11] {
530 &self.0
531 }
532}
533
534#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
553struct KeyAgreementPrivateKey(NonZeroPallasScalar);
554
555impl KeyAgreementPrivateKey {
556 fn from_fvk(fvk: &FullViewingKey) -> Self {
562 let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
564 KeyAgreementPrivateKey(ivk.into())
565 }
566}
567
568impl KeyAgreementPrivateKey {
569 fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
575 let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
576 commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
577 .and_then(NonZeroPallasBase::from_base)
594 }
595
596 fn address(&self, d: Diversifier) -> Address {
598 let prepared_ivk = PreparedIncomingViewingKey::new_inner(self);
599 let pk_d = DiversifiedTransmissionKey::derive(&prepared_ivk, &d);
600 Address::from_parts(d, pk_d)
601 }
602}
603
604#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
617pub struct IncomingViewingKey {
618 dk: DiversifierKey,
619 ivk: KeyAgreementPrivateKey,
620}
621
622impl IncomingViewingKey {
623 fn from_fvk(fvk: &FullViewingKey) -> Self {
625 IncomingViewingKey {
626 dk: fvk.derive_dk_ovk().0,
627 ivk: KeyAgreementPrivateKey::from_fvk(fvk),
628 }
629 }
630}
631
632impl IncomingViewingKey {
633 pub fn to_bytes(&self) -> [u8; 64] {
637 let mut result = [0u8; 64];
638 result[..32].copy_from_slice(self.dk.to_bytes());
639 result[32..].copy_from_slice(&self.ivk.0.to_repr());
640 result
641 }
642
643 pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
645 NonZeroPallasBase::from_bytes(bytes[32..].try_into().unwrap()).map(|ivk| {
646 IncomingViewingKey {
647 dk: DiversifierKey(bytes[..32].try_into().unwrap()),
648 ivk: KeyAgreementPrivateKey(ivk.into()),
649 }
650 })
651 }
652
653 pub fn diversifier_index(&self, addr: &Address) -> Option<DiversifierIndex> {
657 let j = self.dk.diversifier_index(&addr.diversifier());
658 if &self.address_at(j) == addr {
659 Some(j)
660 } else {
661 None
662 }
663 }
664
665 pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
667 self.address(self.dk.get(j))
668 }
669
670 pub fn address(&self, d: Diversifier) -> Address {
672 self.ivk.address(d)
673 }
674
675 pub fn prepare(&self) -> PreparedIncomingViewingKey {
677 PreparedIncomingViewingKey::new(self)
678 }
679}
680
681#[derive(Clone, Debug)]
683pub struct PreparedIncomingViewingKey(PreparedNonZeroScalar);
684
685#[cfg(feature = "std")]
686impl memuse::DynamicUsage for PreparedIncomingViewingKey {
687 fn dynamic_usage(&self) -> usize {
688 self.0.dynamic_usage()
689 }
690
691 fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
692 self.0.dynamic_usage_bounds()
693 }
694}
695
696impl PreparedIncomingViewingKey {
697 pub fn new(ivk: &IncomingViewingKey) -> Self {
700 Self::new_inner(&ivk.ivk)
701 }
702
703 fn new_inner(ivk: &KeyAgreementPrivateKey) -> Self {
704 Self(PreparedNonZeroScalar::new(&ivk.0))
705 }
706}
707
708#[derive(Debug, Clone)]
718pub struct OutgoingViewingKey([u8; 32]);
719
720impl OutgoingViewingKey {
721 fn from_fvk(fvk: &FullViewingKey) -> Self {
723 fvk.derive_dk_ovk().1
724 }
725}
726
727impl From<[u8; 32]> for OutgoingViewingKey {
728 fn from(ovk: [u8; 32]) -> Self {
729 OutgoingViewingKey(ovk)
730 }
731}
732
733impl AsRef<[u8; 32]> for OutgoingViewingKey {
734 fn as_ref(&self) -> &[u8; 32] {
735 &self.0
736 }
737}
738
739#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
745pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
746
747impl DiversifiedTransmissionKey {
748 pub(crate) fn inner(&self) -> NonIdentityPallasPoint {
749 self.0
750 }
751}
752
753impl DiversifiedTransmissionKey {
754 pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self {
758 let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array()));
759 DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d))
760 }
761
762 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
764 NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
765 }
766
767 pub(crate) fn to_bytes(self) -> [u8; 32] {
769 self.0.to_bytes()
770 }
771}
772
773impl ConditionallySelectable for DiversifiedTransmissionKey {
774 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
775 DiversifiedTransmissionKey(NonIdentityPallasPoint::conditional_select(
776 &a.0, &b.0, choice,
777 ))
778 }
779}
780
781#[derive(Debug)]
792pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar);
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 NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey)
803 }
804
805 pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey {
806 EphemeralPublicKey(ka_orchard(&self.0, &g_d))
807 }
808
809 pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
810 SharedSecret(ka_orchard(&self.0, &pk_d.0))
811 }
812}
813
814#[derive(Debug)]
825pub struct EphemeralPublicKey(NonIdentityPallasPoint);
826
827impl EphemeralPublicKey {
828 pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
829 NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey)
830 }
831
832 pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
833 EphemeralKeyBytes(self.0.to_bytes())
834 }
835
836 pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret {
837 SharedSecret(ka_orchard(&ivk.ivk.0, &self.0))
838 }
839}
840
841#[derive(Clone, Debug)]
843pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase);
844
845impl PreparedEphemeralPublicKey {
846 pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
847 PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0))
848 }
849
850 pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
851 SharedSecret(ka_orchard_prepared(&ivk.0, &self.0))
852 }
853}
854
855#[derive(Debug)]
861pub struct SharedSecret(NonIdentityPallasPoint);
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<pallas::Affine>> {
874 let secrets: Vec<_> = shared_secrets
876 .iter()
877 .filter_map(|s| s.as_ref().map(|s| *(s.0)))
878 .collect();
879
880 let mut secrets_affine = vec![pallas::Affine::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_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
895 Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key)
896 }
897
898 pub(crate) fn kdf_orchard_inner(
900 secret: pallas::Affine,
901 ephemeral_key: &EphemeralKeyBytes,
902 ) -> Blake2bHash {
903 Params::new()
904 .hash_length(32)
905 .personal(KDF_ORCHARD_PERSONALIZATION)
906 .to_state()
907 .update(&secret.to_bytes())
908 .update(&ephemeral_key.0)
909 .finalize()
910 }
911}
912
913#[cfg(any(test, feature = "test-dependencies"))]
915#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
916pub mod testing {
917 use proptest::prelude::*;
918
919 use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey};
920
921 prop_compose! {
922 pub fn arb_spending_key()(
924 key in prop::array::uniform32(prop::num::u8::ANY)
925 .prop_map(SpendingKey::from_bytes)
926 .prop_filter(
927 "Values must correspond to valid Orchard spending keys.",
928 |opt| bool::from(opt.is_some())
929 )
930 ) -> SpendingKey {
931 key.unwrap()
932 }
933 }
934
935 prop_compose! {
936 pub fn arb_esk()(
938 esk in prop::array::uniform32(prop::num::u8::ANY)
939 .prop_map(|b| EphemeralSecretKey::from_bytes(&b))
940 .prop_filter(
941 "Values must correspond to valid Orchard ephemeral secret keys.",
942 |opt| bool::from(opt.is_some())
943 )
944 ) -> EphemeralSecretKey {
945 esk.unwrap()
946 }
947 }
948
949 prop_compose! {
950 pub(crate) fn arb_diversifier_key()(
952 dk_bytes in prop::array::uniform32(prop::num::u8::ANY)
953 ) -> DiversifierKey {
954 DiversifierKey::from_bytes(dk_bytes)
955 }
956 }
957
958 prop_compose! {
959 pub fn arb_diversifier_index()(
961 d_bytes in prop::array::uniform11(prop::num::u8::ANY)
962 ) -> DiversifierIndex {
963 DiversifierIndex::from(d_bytes)
964 }
965 }
966}
967
968#[cfg(test)]
969mod tests {
970 use ff::PrimeField;
971 use proptest::prelude::*;
972
973 use super::{
974 testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key},
975 *,
976 };
977 use crate::{
978 note::{ExtractedNoteCommitment, RandomSeed, Rho},
979 value::NoteValue,
980 Note,
981 };
982
983 #[test]
984 fn spend_validating_key_from_bytes() {
985 assert!(SpendValidatingKey::from_bytes(&[0; 32]).is_none());
987 }
988
989 #[test]
990 fn parsers_reject_invalid() {
991 assert!(bool::from(
992 EphemeralSecretKey::from_bytes(&[0xff; 32]).is_none()
993 ));
994 assert!(bool::from(
995 EphemeralPublicKey::from_bytes(&[0xff; 32]).is_none()
996 ));
997 }
998
999 proptest! {
1000 #[test]
1001 fn key_agreement(
1002 sk in arb_spending_key(),
1003 esk in arb_esk(),
1004 j in arb_diversifier_index(),
1005 ) {
1006 let ivk = IncomingViewingKey::from_fvk(&(&sk).into());
1007 let addr = ivk.address_at(j);
1008
1009 let epk = esk.derive_public(addr.g_d());
1010
1011 assert!(bool::from(
1012 esk.agree(addr.pk_d()).0.ct_eq(&epk.agree(&ivk).0)
1013 ));
1014 }
1015 }
1016
1017 proptest! {
1018 #[test]
1019 fn diversifier_index(
1020 dk in arb_diversifier_key(),
1021 j in arb_diversifier_index(),
1022 ) {
1023 let d = dk.get(j);
1024 assert_eq!(j, dk.diversifier_index(&d));
1025 }
1026 }
1027
1028 #[test]
1029 fn test_vectors() {
1030 for tv in crate::test_vectors::keys::test_vectors() {
1031 let sk = SpendingKey::from_bytes(tv.sk).unwrap();
1032
1033 let ask: SpendAuthorizingKey = (&sk).into();
1034 assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask);
1035
1036 let ak: SpendValidatingKey = (&ask).into();
1037 assert_eq!(<[u8; 32]>::from(ak.0), tv.ak);
1038
1039 let nk: NullifierDerivingKey = (&sk).into();
1040 assert_eq!(nk.0.to_repr(), tv.nk);
1041
1042 let rivk: CommitIvkRandomness = (&sk).into();
1043 assert_eq!(rivk.0.to_repr(), tv.rivk);
1044
1045 let fvk: FullViewingKey = (&sk).into();
1046 assert_eq!(<[u8; 32]>::from(&fvk.ak.0), tv.ak);
1047 assert_eq!(fvk.nk().0.to_repr(), tv.nk);
1048 assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
1049
1050 let external_ivk = fvk.to_ivk(Scope::External);
1051 assert_eq!(external_ivk.ivk.0.to_repr(), tv.ivk);
1052
1053 let diversifier = Diversifier(tv.default_d);
1054
1055 let addr = fvk.address(diversifier, Scope::External);
1056 assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
1057
1058 let rho = Rho::from_bytes(&tv.note_rho).unwrap();
1059 let note = Note::from_parts(
1060 addr,
1061 NoteValue::from_raw(tv.note_v),
1062 rho,
1063 RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
1064 )
1065 .unwrap();
1066
1067 let cmx: ExtractedNoteCommitment = note.commitment().into();
1068 assert_eq!(cmx.to_bytes(), tv.note_cmx);
1069
1070 assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
1071
1072 let internal_rivk = fvk.rivk(Scope::Internal);
1073 assert_eq!(internal_rivk.0.to_repr(), tv.internal_rivk);
1074
1075 let internal_ivk = fvk.to_ivk(Scope::Internal);
1076 assert_eq!(internal_ivk.ivk.0.to_repr(), tv.internal_ivk);
1077 assert_eq!(internal_ivk.dk.0, tv.internal_dk);
1078
1079 let internal_ovk = fvk.to_ovk(Scope::Internal);
1080 assert_eq!(internal_ovk.0, tv.internal_ovk);
1081 }
1082 }
1083}