zip32/src/lib.rs

191 lines
5.0 KiB
Rust

//! Implementation of [ZIP 32] for hierarchical deterministic key management.
//!
//! [ZIP 32]: https://zips.z.cash/zip-0032
use memuse::{self, DynamicUsage};
use subtle::{Choice, ConditionallySelectable};
pub mod fingerprint;
/// A type-safe wrapper for account identifiers.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AccountId(u32);
memuse::impl_no_dynamic_usage!(AccountId);
impl From<u32> for AccountId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<AccountId> for u32 {
fn from(id: AccountId) -> Self {
id.0
}
}
impl From<AccountId> for ChildIndex {
fn from(id: AccountId) -> Self {
// Account IDs are always hardened in derivation paths.
ChildIndex::hardened(id.0)
}
}
impl ConditionallySelectable for AccountId {
fn conditional_select(a0: &Self, a1: &Self, c: Choice) -> Self {
AccountId(u32::conditional_select(&a0.0, &a1.0, c))
}
}
// ZIP 32 structures
/// A child index for a derived key.
///
/// Only hardened derivation is supported.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChildIndex(u32);
impl ChildIndex {
/// Parses the given ZIP 32 child index.
///
/// Returns `None` if the hardened bit is not set.
pub fn from_index(i: u32) -> Option<Self> {
if i >= (1 << 31) {
Some(ChildIndex(i))
} else {
None
}
}
/// Constructs a hardened `ChildIndex` from the given value.
///
/// # Panics
///
/// Panics if `value >= (1 << 31)`.
pub const fn hardened(value: u32) -> Self {
assert!(value < (1 << 31));
Self(value + (1 << 31))
}
/// Returns the index as a 32-bit integer, including the hardened bit.
pub fn index(&self) -> u32 {
self.0
}
}
/// A value that is needed, in addition to a spending key, in order to derive descendant
/// keys and addresses of that key.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChainCode([u8; 32]);
impl ChainCode {
/// Constructs a `ChainCode` from the given array.
pub fn new(c: [u8; 32]) -> Self {
Self(c)
}
/// Returns the byte representation of the chain code, as required for
/// [ZIP 32](https://zips.z.cash/zip-0032) encoding.
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DiversifierIndex(pub [u8; 11]);
impl Default for DiversifierIndex {
fn default() -> Self {
DiversifierIndex::new()
}
}
impl From<u32> for DiversifierIndex {
fn from(i: u32) -> Self {
u64::from(i).into()
}
}
impl From<u64> for DiversifierIndex {
fn from(i: u64) -> Self {
let mut result = DiversifierIndex([0; 11]);
result.0[..8].copy_from_slice(&i.to_le_bytes());
result
}
}
impl TryFrom<DiversifierIndex> for u32 {
type Error = std::num::TryFromIntError;
fn try_from(di: DiversifierIndex) -> Result<u32, Self::Error> {
let mut u128_bytes = [0u8; 16];
u128_bytes[0..11].copy_from_slice(&di.0[..]);
u128::from_le_bytes(u128_bytes).try_into()
}
}
impl DiversifierIndex {
pub fn new() -> Self {
DiversifierIndex([0; 11])
}
pub fn increment(&mut self) -> Result<(), ()> {
for k in 0..11 {
self.0[k] = self.0[k].wrapping_add(1);
if self.0[k] != 0 {
// No overflow
return Ok(());
}
}
// Overflow
Err(())
}
}
/// The scope of a viewing key or address.
///
/// A "scope" narrows the visibility or usage to a level below "full".
///
/// Consistent usage of `Scope` enables the user to provide consistent views over a wallet
/// to other people. For example, a user can give an external [SaplingIvk] to a merchant
/// terminal, enabling it to only detect "real" transactions from customers and not
/// internal transactions from the wallet.
///
/// [SaplingIvk]: crate::sapling::SaplingIvk
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Scope {
/// A scope used for wallet-external operations, namely deriving addresses to give to
/// other users in order to receive funds.
External,
/// A scope used for wallet-internal operations, such as creating change notes,
/// auto-shielding, and note management.
Internal,
}
memuse::impl_no_dynamic_usage!(Scope);
#[cfg(test)]
mod tests {
use super::DiversifierIndex;
use assert_matches::assert_matches;
#[test]
fn diversifier_index_to_u32() {
let two = DiversifierIndex([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
assert_eq!(u32::try_from(two), Ok(2));
let max_u32 = DiversifierIndex([
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
assert_eq!(u32::try_from(max_u32), Ok(u32::MAX));
let too_big = DiversifierIndex([
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
assert_matches!(u32::try_from(too_big), Err(_));
}
}