1use bip32::ChildNumber;
4use subtle::{Choice, ConstantTimeEq};
5use zip32::DiversifierIndex;
6
7#[cfg(feature = "transparent-inputs")]
8use {
9 crate::address::TransparentAddress,
10 alloc::string::ToString,
11 alloc::vec::Vec,
12 bip32::{ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, ExtendedPublicKey, Prefix},
13 secp256k1::PublicKey,
14 sha2::{Digest, Sha256},
15 zcash_protocol::consensus::{self, NetworkConstants},
16 zcash_spec::PrfExpand,
17 zip32::AccountId,
18};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct TransparentKeyScope(u32);
26
27impl TransparentKeyScope {
28 pub const fn custom(i: u32) -> Option<Self> {
34 if i < (1 << 31) {
35 Some(TransparentKeyScope(i))
36 } else {
37 None
38 }
39 }
40
41 pub const EXTERNAL: Self = TransparentKeyScope(0);
44
45 pub const INTERNAL: Self = TransparentKeyScope(1);
48
49 pub const EPHEMERAL: Self = TransparentKeyScope(2);
51}
52
53impl From<zip32::Scope> for TransparentKeyScope {
54 fn from(value: zip32::Scope) -> Self {
55 match value {
56 zip32::Scope::External => TransparentKeyScope::EXTERNAL,
57 zip32::Scope::Internal => TransparentKeyScope::INTERNAL,
58 }
59 }
60}
61
62impl From<TransparentKeyScope> for ChildNumber {
63 fn from(value: TransparentKeyScope) -> Self {
64 ChildNumber::new(value.0, false).expect("TransparentKeyScope is correct by construction")
65 }
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
72pub struct NonHardenedChildIndex(u32);
73
74impl ConstantTimeEq for NonHardenedChildIndex {
75 fn ct_eq(&self, other: &Self) -> Choice {
76 self.0.ct_eq(&other.0)
77 }
78}
79
80impl NonHardenedChildIndex {
81 pub const ZERO: NonHardenedChildIndex = NonHardenedChildIndex(0);
83
84 pub const MAX: NonHardenedChildIndex = NonHardenedChildIndex((1 << 31) - 1);
86
87 pub const fn from_index(i: u32) -> Option<Self> {
91 if i <= Self::MAX.0 {
92 Some(NonHardenedChildIndex(i))
93 } else {
94 None
95 }
96 }
97
98 pub const fn const_from_index(i: u32) -> Self {
102 assert!(i <= Self::MAX.0);
103 NonHardenedChildIndex(i)
104 }
105
106 pub const fn index(&self) -> u32 {
108 self.0
109 }
110
111 pub const fn next(&self) -> Option<Self> {
113 Self::from_index(self.0 + 1)
116 }
117
118 pub const fn saturating_sub(&self, delta: u32) -> Self {
120 NonHardenedChildIndex(self.0.saturating_sub(delta))
121 }
122
123 pub const fn saturating_add(&self, delta: u32) -> Self {
126 let idx = self.0.saturating_add(delta);
127 if idx > Self::MAX.0 {
128 Self::MAX
129 } else {
130 NonHardenedChildIndex(idx)
131 }
132 }
133}
134
135impl TryFrom<ChildNumber> for NonHardenedChildIndex {
136 type Error = ();
137
138 fn try_from(value: ChildNumber) -> Result<Self, Self::Error> {
139 if value.is_hardened() {
140 Err(())
141 } else {
142 NonHardenedChildIndex::from_index(value.index()).ok_or(())
143 }
144 }
145}
146
147impl From<NonHardenedChildIndex> for ChildNumber {
148 fn from(value: NonHardenedChildIndex) -> Self {
149 Self::new(value.index(), false).expect("NonHardenedChildIndex is correct by construction")
150 }
151}
152
153impl TryFrom<DiversifierIndex> for NonHardenedChildIndex {
154 type Error = ();
155
156 fn try_from(value: DiversifierIndex) -> Result<Self, Self::Error> {
157 let idx = u32::try_from(value).map_err(|_| ())?;
158 NonHardenedChildIndex::from_index(idx).ok_or(())
159 }
160}
161
162impl From<NonHardenedChildIndex> for DiversifierIndex {
163 fn from(value: NonHardenedChildIndex) -> Self {
164 DiversifierIndex::from(value.0)
165 }
166}
167
168pub struct NonHardenedChildIter {
170 next: Option<NonHardenedChildIndex>,
171 end: NonHardenedChildIndex,
172}
173
174impl Iterator for NonHardenedChildIter {
175 type Item = NonHardenedChildIndex;
176
177 fn next(&mut self) -> Option<Self::Item> {
178 let cur = self.next;
179 self.next = self
180 .next
181 .and_then(|i| i.next())
182 .filter(|succ| succ < &self.end);
183 cur
184 }
185}
186
187pub struct NonHardenedChildRange(core::ops::Range<NonHardenedChildIndex>);
189
190impl From<core::ops::Range<NonHardenedChildIndex>> for NonHardenedChildRange {
191 fn from(value: core::ops::Range<NonHardenedChildIndex>) -> Self {
192 Self(value)
193 }
194}
195
196impl IntoIterator for NonHardenedChildRange {
197 type Item = NonHardenedChildIndex;
198 type IntoIter = NonHardenedChildIter;
199
200 fn into_iter(self) -> Self::IntoIter {
201 NonHardenedChildIter {
202 next: Some(self.0.start),
203 end: self.0.end,
204 }
205 }
206}
207
208#[derive(Clone, Debug)]
212#[cfg(feature = "transparent-inputs")]
213pub struct AccountPrivKey(ExtendedPrivateKey<secp256k1::SecretKey>);
214
215#[cfg(feature = "transparent-inputs")]
216impl AccountPrivKey {
217 pub fn from_seed<P: consensus::Parameters>(
223 params: &P,
224 seed: &[u8],
225 account: AccountId,
226 ) -> Result<AccountPrivKey, bip32::Error> {
227 ExtendedPrivateKey::new(seed)?
228 .derive_child(ChildNumber::new(44, true)?)?
229 .derive_child(ChildNumber::new(params.coin_type(), true)?)?
230 .derive_child(ChildNumber::new(account.into(), true)?)
231 .map(AccountPrivKey)
232 }
233
234 pub fn from_extended_privkey(extprivkey: ExtendedPrivateKey<secp256k1::SecretKey>) -> Self {
235 AccountPrivKey(extprivkey)
236 }
237
238 pub fn to_account_pubkey(&self) -> AccountPubKey {
239 AccountPubKey(ExtendedPublicKey::from(&self.0))
240 }
241
242 pub fn derive_secret_key(
245 &self,
246 scope: TransparentKeyScope,
247 address_index: NonHardenedChildIndex,
248 ) -> Result<secp256k1::SecretKey, bip32::Error> {
249 self.0
250 .derive_child(scope.into())?
251 .derive_child(address_index.into())
252 .map(|k| *k.private_key())
253 }
254
255 pub fn derive_external_secret_key(
258 &self,
259 address_index: NonHardenedChildIndex,
260 ) -> Result<secp256k1::SecretKey, bip32::Error> {
261 self.derive_secret_key(zip32::Scope::External.into(), address_index)
262 }
263
264 pub fn derive_internal_secret_key(
267 &self,
268 address_index: NonHardenedChildIndex,
269 ) -> Result<secp256k1::SecretKey, bip32::Error> {
270 self.derive_secret_key(zip32::Scope::Internal.into(), address_index)
271 }
272
273 pub fn to_bytes(&self) -> Vec<u8> {
277 let xprv_encoded = self.0.to_extended_key(Prefix::XPRV).to_string();
279
280 bs58::decode(xprv_encoded)
282 .with_check(None)
283 .into_vec()
284 .expect("correct")
285 .split_off(Prefix::LENGTH)
286 }
287
288 pub fn from_bytes(b: &[u8]) -> Option<Self> {
292 let mut bytes = Prefix::XPRV.to_bytes().to_vec();
294 bytes.extend_from_slice(b);
295 let xprv_encoded = bs58::encode(bytes).with_check().into_string();
296
297 xprv_encoded
299 .parse::<ExtendedKey>()
300 .ok()
301 .and_then(|k| ExtendedPrivateKey::try_from(k).ok())
302 .map(AccountPrivKey::from_extended_privkey)
303 }
304}
305
306#[cfg(feature = "transparent-inputs")]
313#[derive(Clone, Debug)]
314pub struct AccountPubKey(ExtendedPublicKey<PublicKey>);
315
316#[cfg(feature = "transparent-inputs")]
317impl AccountPubKey {
318 pub fn derive_external_ivk(&self) -> Result<ExternalIvk, bip32::Error> {
321 self.0
322 .derive_child(ChildNumber::new(0, false)?)
323 .map(ExternalIvk)
324 }
325
326 pub fn derive_internal_ivk(&self) -> Result<InternalIvk, bip32::Error> {
329 self.0
330 .derive_child(ChildNumber::new(1, false)?)
331 .map(InternalIvk)
332 }
333
334 pub fn derive_ephemeral_ivk(&self) -> Result<EphemeralIvk, bip32::Error> {
337 self.0
338 .derive_child(ChildNumber::new(2, false)?)
339 .map(EphemeralIvk)
340 }
341
342 pub fn derive_address_pubkey(
345 &self,
346 scope: TransparentKeyScope,
347 address_index: NonHardenedChildIndex,
348 ) -> Result<secp256k1::PublicKey, bip32::Error> {
349 Ok(*self
350 .0
351 .derive_child(scope.into())?
352 .derive_child(address_index.into())?
353 .public_key())
354 }
355
356 pub fn derive_pubkey_at_bip32_path<P: consensus::Parameters>(
361 &self,
362 params: &P,
363 expected_account_index: AccountId,
364 path: &[ChildNumber],
365 ) -> Result<secp256k1::PublicKey, bip32::Error> {
366 if path.len() < 3 {
367 Err(bip32::Error::ChildNumber)
368 } else {
369 match path.split_at(3) {
370 ([purpose, coin_type, account_index], sub_path)
371 if purpose.is_hardened()
372 && purpose.index() == 44
373 && coin_type.is_hardened()
374 && coin_type.index() == params.network_type().coin_type()
375 && account_index.is_hardened()
376 && account_index.index() == expected_account_index.into() =>
377 {
378 sub_path
379 .iter()
380 .try_fold(self.0.clone(), |acc, child_index| {
381 acc.derive_child(*child_index)
382 })
383 .map(|k| *k.public_key())
384 }
385 _ => Err(bip32::Error::ChildNumber),
386 }
387 }
388 }
389
390 pub fn ovks_for_shielding(&self) -> (InternalOvk, ExternalOvk) {
395 let i_ovk = PrfExpand::TRANSPARENT_ZIP316_OVK
396 .with(&self.0.attrs().chain_code, &self.0.public_key().serialize());
397 let ovk_external = ExternalOvk(i_ovk[..32].try_into().unwrap());
398 let ovk_internal = InternalOvk(i_ovk[32..].try_into().unwrap());
399
400 (ovk_internal, ovk_external)
401 }
402
403 pub fn internal_ovk(&self) -> InternalOvk {
405 self.ovks_for_shielding().0
406 }
407
408 pub fn external_ovk(&self) -> ExternalOvk {
410 self.ovks_for_shielding().1
411 }
412
413 pub fn serialize(&self) -> Vec<u8> {
414 let mut buf = self.0.attrs().chain_code.to_vec();
415 buf.extend_from_slice(&self.0.public_key().serialize());
416 buf
417 }
418
419 pub fn deserialize(data: &[u8; 65]) -> Result<Self, bip32::Error> {
420 let chain_code = data[..32].try_into().expect("correct length");
421 let public_key = PublicKey::from_slice(&data[32..])?;
422 Ok(AccountPubKey(ExtendedPublicKey::new(
423 public_key,
424 ExtendedKeyAttrs {
425 depth: 3,
426 parent_fingerprint: [0xff, 0xff, 0xff, 0xff],
429 child_number: ChildNumber::new(0, true).expect("correct"),
430 chain_code,
431 },
432 )))
433 }
434}
435
436#[cfg(feature = "transparent-inputs")]
438#[deprecated(note = "This function will be removed from the public API in an upcoming refactor.")]
439pub fn pubkey_to_address(pubkey: &secp256k1::PublicKey) -> TransparentAddress {
440 TransparentAddress::PublicKeyHash(
441 *ripemd::Ripemd160::digest(Sha256::digest(pubkey.serialize())).as_ref(),
442 )
443}
444
445#[cfg(feature = "transparent-inputs")]
446pub(crate) mod private {
447 use super::TransparentKeyScope;
448 use bip32::ExtendedPublicKey;
449 use secp256k1::PublicKey;
450 pub trait SealedChangeLevelKey {
451 const SCOPE: TransparentKeyScope;
452 fn extended_pubkey(&self) -> &ExtendedPublicKey<PublicKey>;
453 fn from_extended_pubkey(key: ExtendedPublicKey<PublicKey>) -> Self;
454 }
455}
456
457#[cfg(feature = "transparent-inputs")]
473pub trait IncomingViewingKey: private::SealedChangeLevelKey + core::marker::Sized {
474 #[allow(deprecated)]
476 fn derive_address(
477 &self,
478 address_index: NonHardenedChildIndex,
479 ) -> Result<TransparentAddress, bip32::Error> {
480 let child_key = self.extended_pubkey().derive_child(address_index.into())?;
481 Ok(pubkey_to_address(child_key.public_key()))
482 }
483
484 fn default_address(&self) -> (TransparentAddress, NonHardenedChildIndex) {
488 let mut address_index = NonHardenedChildIndex::ZERO;
489 loop {
490 match self.derive_address(address_index) {
491 Ok(addr) => {
492 return (addr, address_index);
493 }
494 Err(_) => {
495 address_index = address_index.next().unwrap_or_else(|| {
496 panic!("Exhausted child index space attempting to find a default address.");
497 });
498 }
499 }
500 }
501 }
502
503 fn serialize(&self) -> Vec<u8> {
504 let extpubkey = self.extended_pubkey();
505 let mut buf = extpubkey.attrs().chain_code.to_vec();
506 buf.extend_from_slice(&extpubkey.public_key().serialize());
507 buf
508 }
509
510 fn deserialize(data: &[u8; 65]) -> Result<Self, bip32::Error> {
511 let chain_code = data[..32].try_into().expect("correct length");
512 let public_key = PublicKey::from_slice(&data[32..])?;
513 Ok(Self::from_extended_pubkey(ExtendedPublicKey::new(
514 public_key,
515 ExtendedKeyAttrs {
516 depth: 4,
517 parent_fingerprint: [0xff, 0xff, 0xff, 0xff],
521 child_number: Self::SCOPE.into(),
522 chain_code,
523 },
524 )))
525 }
526}
527
528#[cfg(feature = "transparent-inputs")]
535#[derive(Clone, Debug)]
536pub struct ExternalIvk(ExtendedPublicKey<PublicKey>);
537
538#[cfg(feature = "transparent-inputs")]
539impl private::SealedChangeLevelKey for ExternalIvk {
540 const SCOPE: TransparentKeyScope = TransparentKeyScope(0);
541
542 fn extended_pubkey(&self) -> &ExtendedPublicKey<PublicKey> {
543 &self.0
544 }
545
546 fn from_extended_pubkey(key: ExtendedPublicKey<PublicKey>) -> Self {
547 ExternalIvk(key)
548 }
549}
550
551#[cfg(feature = "transparent-inputs")]
552impl IncomingViewingKey for ExternalIvk {}
553
554#[cfg(feature = "transparent-inputs")]
562#[derive(Clone, Debug)]
563pub struct InternalIvk(ExtendedPublicKey<PublicKey>);
564
565#[cfg(feature = "transparent-inputs")]
566impl private::SealedChangeLevelKey for InternalIvk {
567 const SCOPE: TransparentKeyScope = TransparentKeyScope(1);
568
569 fn extended_pubkey(&self) -> &ExtendedPublicKey<PublicKey> {
570 &self.0
571 }
572
573 fn from_extended_pubkey(key: ExtendedPublicKey<PublicKey>) -> Self {
574 InternalIvk(key)
575 }
576}
577
578#[cfg(feature = "transparent-inputs")]
579impl IncomingViewingKey for InternalIvk {}
580
581#[cfg(feature = "transparent-inputs")]
586#[derive(Clone, Debug)]
587pub struct EphemeralIvk(ExtendedPublicKey<PublicKey>);
588
589#[cfg(feature = "transparent-inputs")]
590impl EphemeralIvk {
591 pub fn derive_ephemeral_address(
593 &self,
594 address_index: NonHardenedChildIndex,
595 ) -> Result<TransparentAddress, bip32::Error> {
596 let child_key = self.0.derive_child(address_index.into())?;
597 #[allow(deprecated)]
598 Ok(pubkey_to_address(child_key.public_key()))
599 }
600}
601
602pub struct InternalOvk([u8; 32]);
604
605impl InternalOvk {
606 pub fn as_bytes(&self) -> [u8; 32] {
607 self.0
608 }
609}
610
611pub struct ExternalOvk([u8; 32]);
614
615impl ExternalOvk {
616 pub fn as_bytes(&self) -> [u8; 32] {
617 self.0
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use bip32::ChildNumber;
624 use subtle::ConstantTimeEq;
625 use zcash_protocol::consensus::{NetworkConstants, MAIN_NETWORK};
626
627 use super::AccountPubKey;
628 use super::NonHardenedChildIndex;
629 #[allow(deprecated)]
630 use crate::keys::pubkey_to_address;
631 use crate::{
632 address::TransparentAddress,
633 keys::{AccountPrivKey, IncomingViewingKey, TransparentKeyScope},
634 test_vectors,
635 };
636
637 #[test]
638 #[allow(deprecated)]
639 fn address_derivation() {
640 let seed = [
641 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
642 24, 25, 26, 27, 28, 29, 30, 31,
643 ];
644
645 for account_index in 0..5 {
646 let account_index = zip32::AccountId::try_from(account_index).unwrap();
647 let account_sk =
648 AccountPrivKey::from_seed(&MAIN_NETWORK, &seed, account_index).unwrap();
649 let account_pubkey = account_sk.to_account_pubkey();
650
651 let external_ivk = account_pubkey.derive_external_ivk().unwrap();
652 let (address, address_index) = external_ivk.default_address();
653
654 let address_pubkey = account_pubkey
655 .derive_address_pubkey(TransparentKeyScope::EXTERNAL, address_index)
656 .unwrap();
657 assert_eq!(pubkey_to_address(&address_pubkey), address);
658
659 let expected_path = [
660 ChildNumber::new(44, true).unwrap(),
661 ChildNumber::new(MAIN_NETWORK.coin_type(), true).unwrap(),
662 ChildNumber::new(account_index.into(), true).unwrap(),
663 TransparentKeyScope::EXTERNAL.into(),
664 address_index.into(),
665 ];
666
667 for i in 0..3 {
669 assert_eq!(
670 account_pubkey.derive_pubkey_at_bip32_path(
671 &MAIN_NETWORK,
672 account_index,
673 &expected_path[..i]
674 ),
675 Err(bip32::Error::ChildNumber),
676 );
677 }
678
679 assert_eq!(
681 account_pubkey.derive_pubkey_at_bip32_path(
682 &MAIN_NETWORK,
683 account_index,
684 &expected_path[..4],
685 ),
686 Ok(*external_ivk.0.public_key()),
687 );
688
689 assert_eq!(
691 account_pubkey.derive_pubkey_at_bip32_path(
692 &MAIN_NETWORK,
693 account_index,
694 &expected_path,
695 ),
696 Ok(address_pubkey),
697 );
698 }
699 }
700
701 #[test]
702 #[allow(deprecated)]
703 fn bip_32_test_vectors() {
704 let seed = [
705 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
706 24, 25, 26, 27, 28, 29, 30, 31,
707 ];
708
709 for tv in test_vectors::bip_32() {
710 let account_sk = AccountPrivKey::from_seed(
711 &MAIN_NETWORK,
712 &seed,
713 zip32::AccountId::try_from(tv.account).unwrap(),
714 )
715 .unwrap();
716 let account_pubkey = account_sk.to_account_pubkey();
717
718 let mut key_bytes = [0u8; 65];
719 key_bytes[..32].copy_from_slice(&tv.c);
720 key_bytes[32..].copy_from_slice(&tv.pk);
721 assert_eq!(account_pubkey.serialize(), key_bytes);
722
723 let (internal_ovk, external_ovk) = account_pubkey.ovks_for_shielding();
724 assert_eq!(internal_ovk.as_bytes(), tv.internal_ovk);
725 assert_eq!(external_ovk.as_bytes(), tv.external_ovk);
726
727 let address = TransparentAddress::PublicKeyHash(tv.address);
730 assert_eq!(pubkey_to_address(account_pubkey.0.public_key()), address);
731 }
732 }
733
734 #[test]
735 fn check_ovk_test_vectors() {
736 for tv in test_vectors::transparent_ovk() {
737 let mut key_bytes = [0u8; 65];
738 key_bytes[..32].copy_from_slice(&tv.c);
739 key_bytes[32..].copy_from_slice(&tv.pk);
740 let account_key = AccountPubKey::deserialize(&key_bytes).unwrap();
741
742 let (internal, external) = account_key.ovks_for_shielding();
743
744 assert_eq!(tv.internal_ovk, internal.as_bytes());
745 assert_eq!(tv.external_ovk, external.as_bytes());
746 }
747 }
748
749 #[test]
750 fn nonhardened_indexes_accepted() {
751 assert_eq!(0, NonHardenedChildIndex::from_index(0).unwrap().index());
752 assert_eq!(
753 0x7fffffff,
754 NonHardenedChildIndex::from_index(0x7fffffff)
755 .unwrap()
756 .index()
757 );
758 }
759
760 #[test]
761 fn hardened_indexes_rejected() {
762 assert!(NonHardenedChildIndex::from_index(0x80000000).is_none());
763 assert!(NonHardenedChildIndex::from_index(0xffffffff).is_none());
764 }
765
766 #[test]
767 fn nonhardened_index_next() {
768 assert_eq!(1, NonHardenedChildIndex::ZERO.next().unwrap().index());
769 assert!(NonHardenedChildIndex::from_index(0x7fffffff)
770 .unwrap()
771 .next()
772 .is_none());
773 }
774
775 #[test]
776 fn nonhardened_index_ct_eq() {
777 assert!(check(
778 NonHardenedChildIndex::ZERO,
779 NonHardenedChildIndex::ZERO
780 ));
781 assert!(!check(
782 NonHardenedChildIndex::ZERO,
783 NonHardenedChildIndex::ZERO.next().unwrap()
784 ));
785
786 fn check<T: ConstantTimeEq>(v1: T, v2: T) -> bool {
787 v1.ct_eq(&v2).into()
788 }
789 }
790
791 #[test]
792 fn nonhardened_index_tryfrom_keyindex() {
793 let nh: NonHardenedChildIndex = ChildNumber::new(0, false).unwrap().try_into().unwrap();
794 assert_eq!(nh.index(), 0);
795
796 assert!(NonHardenedChildIndex::try_from(ChildNumber::new(0, true).unwrap()).is_err());
797 }
798}