zip32/
lib.rs

1//! Common types for implementing [ZIP 32] for hierarchical deterministic key management.
2//!
3//! [ZIP 32]: https://zips.z.cash/zip-0032
4
5#![no_std]
6#![deny(missing_docs)]
7#![deny(unsafe_code)]
8#![deny(rustdoc::broken_intra_doc_links)]
9
10#[cfg(test)]
11extern crate alloc;
12
13#[cfg(feature = "std")]
14extern crate std;
15
16use core::mem;
17
18use memuse::{self, DynamicUsage};
19use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
20
21pub mod arbitrary;
22pub mod fingerprint;
23pub mod hardened_only;
24pub mod registered;
25
26/// A type-safe wrapper for account identifiers.
27///
28/// Accounts are 31-bit unsigned integers, and are always treated as hardened in
29/// derivation paths.
30#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct AccountId(u32);
32
33memuse::impl_no_dynamic_usage!(AccountId);
34
35impl TryFrom<u32> for AccountId {
36    type Error = TryFromIntError;
37
38    fn try_from(id: u32) -> Result<Self, Self::Error> {
39        // Account IDs are always hardened in derivation paths, so they are effectively at
40        // most 31 bits.
41        if id < (1 << 31) {
42            Ok(Self(id))
43        } else {
44            Err(TryFromIntError(()))
45        }
46    }
47}
48
49impl From<AccountId> for u32 {
50    fn from(id: AccountId) -> Self {
51        id.0
52    }
53}
54
55impl From<AccountId> for ChildIndex {
56    fn from(id: AccountId) -> Self {
57        // Account IDs are always hardened in derivation paths.
58        ChildIndex::hardened(id.0)
59    }
60}
61
62impl ConditionallySelectable for AccountId {
63    fn conditional_select(a0: &Self, a1: &Self, c: Choice) -> Self {
64        AccountId(u32::conditional_select(&a0.0, &a1.0, c))
65    }
66}
67
68impl AccountId {
69    /// The ID for account zero (the first account).
70    pub const ZERO: Self = Self(0);
71
72    /// Returns the next account ID in sequence, or `None` on overflow.
73    pub fn next(&self) -> Option<Self> {
74        Self::try_from(self.0 + 1).ok()
75    }
76
77    /// Constant function to construct an account ID from a u32.
78    ///
79    /// # Panics
80    /// Panics if the provided value is >= 2^31
81    pub const fn const_from_u32(value: u32) -> Self {
82        if value < (1 << 31) {
83            Self(value)
84        } else {
85            panic!("Account IDs must be in the range 0..2^31");
86        }
87    }
88}
89
90/// The error type returned when a checked integral type conversion fails.
91#[derive(Clone, Copy, Debug)]
92pub struct TryFromIntError(());
93
94impl core::fmt::Display for TryFromIntError {
95    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        write!(f, "out of range integral type conversion attempted")
97    }
98}
99
100#[cfg(feature = "std")]
101impl std::error::Error for TryFromIntError {}
102
103// Helper function for arbitrary and registered master key generation.
104
105pub(crate) fn with_ikm<F, T>(context_string: &[u8], seed: &[u8], f: F) -> T
106where
107    F: FnOnce(&[&[u8]]) -> T,
108{
109    let context_len =
110        u8::try_from(context_string.len()).expect("context string should be at most 252 bytes");
111    assert!((1..=252).contains(&context_len));
112
113    let seed_len = u8::try_from(seed.len()).expect("seed should be at most 252 bytes");
114    assert!((32..=252).contains(&seed_len));
115
116    let ikm = &[&[context_len], context_string, &[seed_len], seed];
117
118    f(ikm)
119}
120
121// ZIP 32 structures
122
123/// A child index for a derived key.
124///
125/// Only hardened derivation is supported.
126#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub struct ChildIndex(u32);
128
129impl ConstantTimeEq for ChildIndex {
130    fn ct_eq(&self, other: &Self) -> Choice {
131        self.0.ct_eq(&other.0)
132    }
133}
134
135impl ChildIndex {
136    /// Parses the given ZIP 32 child index.
137    ///
138    /// Returns `None` if the hardened bit is not set.
139    pub fn from_index(i: u32) -> Option<Self> {
140        if i >= (1 << 31) {
141            Some(ChildIndex(i))
142        } else {
143            None
144        }
145    }
146
147    /// Constructs a hardened `ChildIndex` from the given value.
148    ///
149    /// # Panics
150    ///
151    /// Panics if `value >= (1 << 31)`.
152    pub const fn hardened(value: u32) -> Self {
153        assert!(value < (1 << 31));
154        Self(value + (1 << 31))
155    }
156
157    /// Returns the index as a 32-bit integer, including the hardened bit.
158    pub fn index(&self) -> u32 {
159        self.0
160    }
161
162    /// A `ChildIndex` sometimes employed for private-use subtrees.
163    pub const PRIVATE_USE: Self = Self::hardened(0x7fff_ffff);
164}
165
166/// A value that is needed, in addition to a spending key, in order to derive descendant
167/// keys and addresses of that key.
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169pub struct ChainCode([u8; 32]);
170
171impl ConstantTimeEq for ChainCode {
172    fn ct_eq(&self, other: &Self) -> Choice {
173        self.0.ct_eq(&other.0)
174    }
175}
176
177impl ChainCode {
178    /// Constructs a `ChainCode` from the given array.
179    pub fn new(c: [u8; 32]) -> Self {
180        Self(c)
181    }
182
183    /// Returns the byte representation of the chain code, as required for
184    /// [ZIP 32](https://zips.z.cash/zip-0032) encoding.
185    pub fn as_bytes(&self) -> &[u8; 32] {
186        &self.0
187    }
188}
189
190/// The index for a particular diversifier.
191#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
192pub struct DiversifierIndex([u8; 11]);
193
194impl Default for DiversifierIndex {
195    fn default() -> Self {
196        DiversifierIndex::new()
197    }
198}
199
200macro_rules! di_from {
201    ($n:ident) => {
202        impl From<$n> for DiversifierIndex {
203            fn from(j: $n) -> Self {
204                let mut j_bytes = [0; 11];
205                j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes());
206                DiversifierIndex(j_bytes)
207            }
208        }
209    };
210}
211di_from!(u32);
212di_from!(u64);
213di_from!(usize);
214
215impl From<[u8; 11]> for DiversifierIndex {
216    fn from(j_bytes: [u8; 11]) -> Self {
217        DiversifierIndex(j_bytes)
218    }
219}
220
221impl TryFrom<u128> for DiversifierIndex {
222    type Error = TryFromIntError;
223
224    fn try_from(value: u128) -> Result<Self, Self::Error> {
225        if (value >> 88) == 0 {
226            Ok(Self(value.to_le_bytes()[..11].try_into().unwrap()))
227        } else {
228            Err(TryFromIntError(()))
229        }
230    }
231}
232
233macro_rules! di_try_into {
234    ($n:ident) => {
235        impl TryFrom<DiversifierIndex> for $n {
236            type Error = core::num::TryFromIntError;
237
238            fn try_from(di: DiversifierIndex) -> Result<Self, Self::Error> {
239                u128::from(di).try_into()
240            }
241        }
242    };
243}
244di_try_into!(u32);
245di_try_into!(u64);
246di_try_into!(usize);
247
248impl From<DiversifierIndex> for u128 {
249    fn from(di: DiversifierIndex) -> Self {
250        let mut u128_bytes = [0u8; 16];
251        u128_bytes[0..11].copy_from_slice(&di.0[..]);
252        u128::from_le_bytes(u128_bytes)
253    }
254}
255
256impl PartialOrd for DiversifierIndex {
257    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
258        Some(self.cmp(other))
259    }
260}
261
262impl Ord for DiversifierIndex {
263    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
264        self.0
265            .iter()
266            .rev()
267            .zip(other.0.iter().rev())
268            .find_map(|(a, b)| match a.cmp(b) {
269                core::cmp::Ordering::Equal => None,
270                ineq => Some(ineq),
271            })
272            .unwrap_or(core::cmp::Ordering::Equal)
273    }
274}
275
276impl DiversifierIndex {
277    /// Constructs the zero index.
278    pub fn new() -> Self {
279        DiversifierIndex([0; 11])
280    }
281
282    /// Returns the raw bytes of the diversifier index.
283    pub fn as_bytes(&self) -> &[u8; 11] {
284        &self.0
285    }
286
287    /// Increments this index, failing on overflow.
288    pub fn increment(&mut self) -> Result<(), DiversifierIndexOverflowError> {
289        for k in 0..11 {
290            self.0[k] = self.0[k].wrapping_add(1);
291            if self.0[k] != 0 {
292                // No overflow
293                return Ok(());
294            }
295        }
296        // Overflow
297        Err(DiversifierIndexOverflowError)
298    }
299}
300
301/// The error type returned when a [`DiversifierIndex`] increment fails.
302#[derive(Clone, Copy, Debug)]
303pub struct DiversifierIndexOverflowError;
304
305impl core::fmt::Display for DiversifierIndexOverflowError {
306    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307        write!(f, "DiversifierIndex increment overflowed")
308    }
309}
310
311#[cfg(feature = "std")]
312impl std::error::Error for DiversifierIndexOverflowError {}
313
314/// The scope of a viewing key or address.
315///
316/// A "scope" narrows the visibility or usage to a level below "full".
317///
318/// Consistent usage of `Scope` enables the user to provide consistent views over a wallet
319/// to other people. For example, a user can give an external incoming viewing key to a
320/// merchant terminal, enabling it to only detect "real" transactions from customers and
321/// not internal transactions from the wallet.
322#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
323pub enum Scope {
324    /// A scope used for wallet-external operations, namely deriving addresses to give to
325    /// other users in order to receive funds.
326    External,
327    /// A scope used for wallet-internal operations, such as creating change notes,
328    /// auto-shielding, and note management.
329    Internal,
330}
331
332memuse::impl_no_dynamic_usage!(Scope);
333
334#[cfg(test)]
335mod tests {
336    use super::{AccountId, DiversifierIndex};
337
338    use assert_matches::assert_matches;
339
340    #[test]
341    fn account_id_next() {
342        let zero = AccountId::ZERO;
343        assert_eq!(zero.next(), AccountId::try_from(1).ok());
344
345        let max_id = AccountId::try_from((1 << 31) - 1).unwrap();
346        assert_eq!(max_id.next(), None);
347    }
348
349    #[test]
350    fn diversifier_index_to_u32() {
351        let two = DiversifierIndex([
352            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353        ]);
354        assert_eq!(u32::try_from(two), Ok(2));
355        assert_eq!(DiversifierIndex::from(2_u32), two);
356
357        let max_u32 = DiversifierIndex([
358            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359        ]);
360        assert_eq!(u32::try_from(max_u32), Ok(u32::MAX));
361        assert_eq!(DiversifierIndex::from(u32::MAX), max_u32);
362
363        let too_big = DiversifierIndex([
364            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365        ]);
366        assert_matches!(u32::try_from(too_big), Err(_));
367    }
368
369    #[test]
370    fn diversifier_index_to_u64() {
371        let two = DiversifierIndex([
372            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373        ]);
374        assert_eq!(u64::try_from(two), Ok(2));
375        assert_eq!(DiversifierIndex::from(2_u64), two);
376
377        let max_u64 = DiversifierIndex([
378            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
379        ]);
380        assert_eq!(u64::try_from(max_u64), Ok(u64::MAX));
381        assert_eq!(DiversifierIndex::from(u64::MAX), max_u64);
382
383        let too_big = DiversifierIndex([
384            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
385        ]);
386        assert_matches!(u64::try_from(too_big), Err(_));
387    }
388
389    #[test]
390    fn diversifier_index_to_u128() {
391        let two = DiversifierIndex([
392            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393        ]);
394        assert_eq!(u128::from(two), 2);
395        assert_eq!(DiversifierIndex::try_from(2_u128).unwrap(), two);
396
397        let max_di = DiversifierIndex([
398            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
399        ]);
400        assert_eq!(u128::from(max_di), 0x00ff_ffff_ffff_ffff_ffff_ffff);
401        assert_eq!(
402            DiversifierIndex::try_from(0x00ff_ffff_ffff_ffff_ffff_ffff_u128).unwrap(),
403            max_di,
404        );
405
406        assert!(DiversifierIndex::try_from(0x0100_0000_0000_0000_0000_0000_u128).is_err());
407    }
408
409    #[test]
410    fn diversifier_index_increment() {
411        let mut di = DiversifierIndex::new();
412        assert_eq!(di, 0_u32.into());
413
414        assert_matches!(di.increment(), Ok(_));
415        assert_eq!(di, 1_u32.into());
416
417        assert_matches!(di.increment(), Ok(_));
418        assert_eq!(di, 2_u32.into());
419
420        let mut di = DiversifierIndex::from(0xff_u32);
421        assert_eq!(di, 0x00ff_u32.into());
422
423        assert_matches!(di.increment(), Ok(_));
424        assert_eq!(di, 0x0100_u32.into());
425
426        assert_matches!(di.increment(), Ok(_));
427        assert_eq!(di, 0x0101_u32.into());
428
429        let mut di = DiversifierIndex::try_from(0x00ff_ffff_ffff_ffff_ffff_fffe_u128).unwrap();
430        assert_eq!(u128::from(di), 0x00ff_ffff_ffff_ffff_ffff_fffe_u128);
431
432        assert_matches!(di.increment(), Ok(_));
433        assert_eq!(u128::from(di), 0x00ff_ffff_ffff_ffff_ffff_ffff_u128);
434
435        assert_matches!(di.increment(), Err(_));
436    }
437
438    #[test]
439    fn diversifier_index_ord() {
440        assert!(DiversifierIndex::from(1u64) < DiversifierIndex::from(2u64));
441        assert!(DiversifierIndex::from(u64::MAX - 1) < DiversifierIndex::from(u64::MAX));
442        assert!(DiversifierIndex::from(3u64) == DiversifierIndex::from(3u64));
443        assert!(DiversifierIndex::from(u64::MAX) == DiversifierIndex::from(u64::MAX));
444    }
445}