#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::{num::NonZeroU32, ops::Range};
use minicbor::{data::Type, decode::Error, encode::Write, Decode, Decoder, Encode, Encoder};
#[doc(alias("crypto-keypath"))]
#[derive(Debug, Clone, PartialEq)]
pub struct KeypathRef<'a> {
pub components: PathComponents<'a>,
pub source_fingerprint: Option<NonZeroU32>,
pub depth: Option<u8>,
}
impl<'a> KeypathRef<'a> {
pub fn new_master(source_fingerprint: NonZeroU32) -> Self {
Self {
components: PathComponents {
storage: PathStorage::RawDerivationPath(&[]),
},
source_fingerprint: Some(source_fingerprint),
depth: Some(0),
}
}
}
impl<'b, C> Decode<'b, C> for KeypathRef<'b> {
fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
let mut components = None;
let mut source_fingerprint = None;
let mut depth = None;
let mut len = d.map()?;
loop {
match len {
Some(0) => break,
Some(n) => len = Some(n - 1),
None => {
if d.datatype()? == Type::Break {
break;
}
}
}
match d.u32()? {
1 => components = Some(PathComponents::decode(d, ctx)?),
2 => {
source_fingerprint = Some(
NonZeroU32::new(d.u32()?)
.ok_or_else(|| Error::message("source-fingerprint is zero"))?,
)
}
3 => depth = Some(d.u8()?),
_ => return Err(Error::message("unknown map entry")),
}
}
Ok(Self {
components: components.ok_or_else(|| Error::message("components is missing"))?,
source_fingerprint,
depth,
})
}
}
impl<'a, C> Encode<C> for KeypathRef<'a> {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
let len =
1 + u64::from(self.source_fingerprint.is_some()) + u64::from(self.depth.is_some());
e.map(len)?;
e.u8(1)?;
self.components.encode(e, ctx)?;
if let Some(source_fingerprint) = self.source_fingerprint {
e.u8(2)?.u32(source_fingerprint.get())?;
}
if let Some(depth) = self.depth {
e.u8(3)?.u8(depth)?;
}
Ok(())
}
}
#[cfg(feature = "bitcoin")]
impl<'a> From<&'a bitcoin::bip32::DerivationPath> for KeypathRef<'a> {
fn from(derivation_path: &'a bitcoin::bip32::DerivationPath) -> Self {
Self {
components: PathComponents {
storage: PathStorage::DerivationPath(derivation_path.as_ref()),
},
source_fingerprint: None,
depth: None,
}
}
}
#[doc(alias("crypto-keypath"))]
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq)]
pub struct Keypath {
pub components: Vec<PathComponent>,
pub source_fingerprint: Option<NonZeroU32>,
pub depth: Option<u8>,
}
#[cfg(feature = "alloc")]
impl<'a> From<KeypathRef<'a>> for Keypath {
fn from(keypath: KeypathRef<'a>) -> Self {
Self {
components: keypath.components.iter().collect(),
source_fingerprint: keypath.source_fingerprint,
depth: keypath.depth,
}
}
}
#[derive(Debug, Clone)]
pub struct PathComponents<'a> {
storage: PathStorage<'a>,
}
#[derive(Debug, Clone)]
enum PathStorage<'a> {
Cbor {
d: Decoder<'a>,
len: usize,
},
RawDerivationPath(&'a [u32]),
#[cfg(feature = "bitcoin")]
DerivationPath(&'a [bitcoin::bip32::ChildNumber]),
}
impl<'a> PathStorage<'a> {
fn len(&self) -> usize {
match self {
PathStorage::Cbor { len, .. } => *len,
PathStorage::RawDerivationPath(path) => path.len(),
#[cfg(feature = "bitcoin")]
PathStorage::DerivationPath(path) => path.len(),
}
}
}
impl<'a> PathComponents<'a> {
pub fn len(&self) -> usize {
self.storage.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> PathComponentsIter<'a> {
PathComponentsIter {
storage: self.storage.clone(),
index: 0,
}
}
}
impl<'b, C> Decode<'b, C> for PathComponents<'b> {
fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
let mut array_len = d.array()?.map(|len| len / 2);
let path_decoder = d.clone();
let mut len: usize = 0;
loop {
match array_len {
Some(0) => break,
Some(n) => array_len = Some(n.saturating_sub(1)),
None => {
if d.datatype()? == Type::Break {
break;
}
}
}
PathComponent::decode(d, ctx)?;
match len.overflowing_add(1) {
(new_len, false) => len = new_len,
(_, true) => return Err(Error::message("too many elements")),
}
}
Ok(Self {
storage: PathStorage::Cbor {
d: path_decoder,
len,
},
})
}
}
impl<'a, C> Encode<C> for PathComponents<'a> {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(self.len() as u64 * 2)?;
for elt in self.iter() {
elt.encode(e, ctx)?;
}
Ok(())
}
}
impl<'a> PartialEq for PathComponents<'a> {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for (lhs, rhs) in self.iter().zip(other.iter()) {
if lhs != rhs {
return false;
}
}
true
}
}
impl<'a> From<&'a [u32]> for PathComponents<'a> {
fn from(path: &'a [u32]) -> Self {
Self {
storage: PathStorage::RawDerivationPath(path),
}
}
}
impl<'a, const N: usize> From<&'a [u32; N]> for PathComponents<'a> {
fn from(path: &'a [u32; N]) -> Self {
Self {
storage: PathStorage::RawDerivationPath(path),
}
}
}
pub struct PathComponentsIter<'a> {
storage: PathStorage<'a>,
index: usize,
}
impl<'a> Iterator for PathComponentsIter<'a> {
type Item = PathComponent;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.storage.len() {
return None;
}
let component = match self.storage {
PathStorage::Cbor { ref mut d, .. } => {
PathComponent::decode(d, &mut ()).expect("path component should be valid")
}
PathStorage::RawDerivationPath(path) => {
let (number, is_hardened) = if path[self.index] & (1 << 31) != 0 {
(path[self.index] ^ (1 << 31), true)
} else {
(path[self.index], false)
};
PathComponent {
number: ChildNumber::Number(number),
is_hardened,
}
}
#[cfg(feature = "bitcoin")]
PathStorage::DerivationPath(path) => PathComponent::from(path[self.index]),
};
self.index += 1;
Some(component)
}
}
impl<'a> ExactSizeIterator for PathComponentsIter<'a> {
fn len(&self) -> usize {
self.storage.len()
}
}
#[doc(alias("path-component"))]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PathComponent {
pub number: ChildNumber,
pub is_hardened: bool,
}
impl<'b, C> Decode<'b, C> for PathComponent {
fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, Error> {
let number = match d.datatype()? {
Type::U8 | Type::U16 | Type::U32 => ChildNumber::Number(d.u32()?),
Type::Array => {
let mut array = d.array_iter::<u32>()?;
let low = array
.next()
.ok_or_else(|| Error::message("low child-index not present"))??;
let high = array
.next()
.ok_or_else(|| Error::message("high child-index not present"))??;
if array.next().is_some() {
return Err(Error::message("invalid child-index-range size"));
}
ChildNumber::Range(low..high)
}
_ => return Err(Error::message("unknown child number")),
};
Ok(Self {
number,
is_hardened: d.bool()?,
})
}
}
impl<C> Encode<C> for PathComponent {
fn encode<W: Write>(
&self,
e: &mut Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self.number {
ChildNumber::Number(n) => e.u32(n)?,
ChildNumber::Range(ref range) => e.array(2)?.u32(range.start)?.u32(range.end)?,
};
e.bool(self.is_hardened)?;
Ok(())
}
}
#[cfg(feature = "bitcoin")]
impl From<bitcoin::bip32::ChildNumber> for PathComponent {
fn from(number: bitcoin::bip32::ChildNumber) -> Self {
match number {
bitcoin::bip32::ChildNumber::Normal { index } => PathComponent {
number: ChildNumber::Number(index),
is_hardened: false,
},
bitcoin::bip32::ChildNumber::Hardened { index } => PathComponent {
number: ChildNumber::Number(index),
is_hardened: true,
},
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ChildNumber {
Number(u32),
Range(Range<u32>),
}