Skip to content

Commit

Permalink
fixup! SFT-UNKN: Add foundation-bip32 crate.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeandudey committed Nov 10, 2023
1 parent 69287cb commit 04b51b0
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 57 deletions.
46 changes: 31 additions & 15 deletions bip32/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ pub struct Fingerprint(pub [u8; 4]);
///
/// This contains the key derivation information about a public key.
#[derive(Debug, Clone)]
pub struct ExtendedPublicKey<'a> {
pub struct ExtendedPublicKey {
/// The version of the extended public key.
pub version: [u8; 4],
/// The depth of the extended public key.
pub depth: u8,
/// The fingerprint of the extended public key parent.
pub parent_fingerprint: &'a [u8],
pub parent_fingerprint: [u8; 4],
/// The child number of the extended public key.
pub child_number: u32,
/// The chain code of the extended public key.
pub chain_code: &'a [u8],
pub chain_code: [u8; 32],
/// The public key.
pub public_key: PublicKey,
}
Expand All @@ -46,38 +46,54 @@ pub struct ExtendedPublicKey<'a> {
///
/// The buffer contained in the type should be always valid.
#[derive(Debug, Clone)]
pub struct DerivationPathLe<'a> {
buf: &'a [u8],
pub struct DerivationPathLe<Input> {
buf: Input,
len: usize,
}

impl<'a> DerivationPathLe<'a> {
impl<Input> DerivationPathLe<Input> {
/// Returns the number of elements in the derivation path.
#[inline]
pub fn len(&self) -> usize {
self.len
}

/// Returns an iterator over the elements of the derivation path.
pub fn iter(&self) -> DerivationPathLeIter {
DerivationPathLeIter { buf: self.buf }
pub fn iter(&self) -> DerivationPathLeIter<Input>
where
Input: Clone,
{
DerivationPathLeIter {
count: 0,
len: self.len,
buf: self.buf.clone(),
}
}
}

/// Iterator over the derivation path elements.
pub struct DerivationPathLeIter<'a> {
buf: &'a [u8],
pub struct DerivationPathLeIter<Input> {
count: usize,
len: usize,
buf: Input,
}

impl<'a> Iterator for DerivationPathLeIter<'a> {
impl<Input> Iterator for DerivationPathLeIter<Input>
where
Input: Clone
+ core::fmt::Debug
+ nom::InputLength
+ nom::InputIter<Item = u8>
+ nom::Slice<core::ops::RangeFrom<usize>>,
{
type Item = u32;

fn next(&mut self) -> Option<Self::Item> {
if self.buf.is_empty() {
if self.count >= self.len {
return None;
}

let (buf, item) = le_u32::<_, nom::error::Error<_>>(self.buf)
let (buf, item) = le_u32::<_, nom::error::Error<_>>(self.buf.clone())
.expect("element should be valid at this point");
self.buf = buf;

Expand All @@ -87,11 +103,11 @@ impl<'a> Iterator for DerivationPathLeIter<'a> {

/// Information about an extended public key was derived.
#[derive(Debug)]
pub struct KeySource<'a> {
pub struct KeySource<Input> {
/// Fingerprint of the master key.
pub fingerprint: Fingerprint,
/// Derivation path of the key.
pub path: DerivationPathLe<'a>,
pub path: DerivationPathLe<Input>,
}

#[cfg(test)]
Expand Down
124 changes: 82 additions & 42 deletions bip32/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,101 @@

//! Parser combinators for BIP-32 serialization formats.

use nom::multi::fill;
use core::ops::RangeFrom;

use nom::{
branch::alt,
bytes::complete::{tag, take},
combinator::{map, map_res, verify},
error::{FromExternalError, ParseError},
multi::many0_count,
multi::{fill, many0_count},
number::complete::{be_u32, le_u32, u8},
sequence::tuple,
IResult,
Compare, IResult, InputIter, InputLength, InputTake, Slice,
};
use secp256k1::PublicKey;

use crate::{DerivationPathLe, ExtendedPublicKey, Fingerprint, KeySource};

fn bitcoin_mainnet_xpub<'a, Error: ParseError<&'a [u8]>>(
input: &'a [u8],
) -> IResult<&[u8], &[u8], Error> {
fn to_fixed_bytes<Input, const N: usize>(i: Input) -> [u8; N]
where
Input: InputIter<Item = u8>,
{
let mut slice = [0; N];
for (i, byte) in i.iter_indices() {
slice[i] = byte;
}
slice
}

fn bitcoin_mainnet_xpub<Input, Error>(i: Input) -> IResult<Input, Input, Error>
where
Input: for<'a> Compare<&'a [u8]> + InputTake + Clone,
Error: ParseError<Input>,
{
let tag = tag::<_, Input, Error>;

let xpub = tag(&[0x04, 0x88, 0xb2, 0x1e]);
let ypub = tag(&[0x04, 0x9d, 0x7c, 0xb2]);
let zpub = tag(&[0x04, 0xb2, 0x47, 0x46]);
let multisig_ypub = tag(&[0x02, 0x95, 0xb4, 0x3f]); // Ypub
let multisig_zpub = tag(&[0x02, 0xaa, 0x7e, 0xd3]); // Zpub

let mut parser = alt((xpub, ypub, zpub, multisig_ypub, multisig_zpub));
parser(input)
parser(i)
}

fn bitcoin_testnet_xpub<'a, Error: ParseError<&'a [u8]>>(
input: &'a [u8],
) -> IResult<&[u8], &[u8], Error> {
fn bitcoin_testnet_xpub<Input, Error>(i: Input) -> IResult<Input, Input, Error>
where
Input: for<'a> Compare<&'a [u8]> + InputTake + Clone,
Error: ParseError<Input>,
{
let tag = tag::<_, Input, Error>;

let tpub = tag(&[0x04, 0x35, 0x87, 0xcf]);
let upub = tag(&[0x04, 0x4a, 0x52, 0x62]);
let vpub = tag(&[0x04, 0x5f, 0x1c, 0xf6]);
let multisig_upub = tag(&[0x02, 0x42, 0x89, 0xef]); // Upub
let multisig_vpub = tag(&[0x02, 0x57, 0x54, 0x83]); // Vpub

let mut parser = alt((tpub, upub, vpub, multisig_upub, multisig_vpub));
parser(input)
parser(i)
}

fn xpub_version<'a, Error: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&[u8], [u8; 4], Error> {
let bitcoin_xpub = alt((bitcoin_mainnet_xpub, bitcoin_testnet_xpub));

let mut version = map(bitcoin_xpub, |version| {
let mut tmp = [0; 4];
tmp.copy_from_slice(&version);
tmp
});
fn xpub_version<Input, Error>(i: Input) -> IResult<Input, [u8; 4], Error>
where
Input: for<'a> Compare<&'a [u8]> + InputTake + Clone + InputIter<Item = u8>,
Error: ParseError<Input>,
{
let bitcoin_xpub = alt((
bitcoin_mainnet_xpub::<Input, Error>,
bitcoin_testnet_xpub::<Input, Error>,
));
let mut version = map(bitcoin_xpub, to_fixed_bytes::<Input, 4>);

version(i)
}

/// Parse an extended public key.
pub fn extended_public_key<'a, Error>(i: &'a [u8]) -> IResult<&[u8], ExtendedPublicKey, Error>
pub fn extended_public_key<Input, Error>(i: Input) -> IResult<Input, ExtendedPublicKey, Error>
where
Error: ParseError<&'a [u8]>,
Error: FromExternalError<&'a [u8], secp256k1::Error>,
Input: for<'a> Compare<&'a [u8]>
+ InputTake
+ Clone
+ InputIter<Item = u8>
+ InputLength
+ Slice<RangeFrom<usize>>,
Error: ParseError<Input>,
Error: FromExternalError<Input, secp256k1::Error>,
{
let depth = u8;
let parent_fingerprint = take(4usize);
let child_number = be_u32;
let chain_code = take(32usize);
let public_key = map_res(take(33usize), PublicKey::from_slice);
let depth = u8::<Input, Error>;
let parent_fingerprint = map(take::<_, Input, Error>(4usize), to_fixed_bytes::<_, 4>);
let child_number = be_u32::<Input, Error>;
let chain_code = map(take::<_, Input, Error>(32usize), to_fixed_bytes::<_, 32>);
let public_key = map_res(
map(take::<_, Input, Error>(33usize), to_fixed_bytes::<_, 33>),
|pk| PublicKey::from_slice(&pk),
);

let parser = tuple((
xpub_version,
Expand Down Expand Up @@ -94,7 +125,7 @@ where
let mut parser = verify(parser, |key| {
// When the depth is 0 (master key) the parent fingerprint should be 0 and
// child number should be 0 as well as it doesn't apply here.
if key.depth == 0 && (key.parent_fingerprint != &[0; 4] || key.child_number != 0) {
if key.depth == 0 && (&key.parent_fingerprint != &[0; 4] || key.child_number != 0) {
false
} else {
true
Expand All @@ -108,26 +139,34 @@ where
/// integers.
///
/// `<derivation_path_le> := <le_u32>*`
pub fn derivation_path_le<'a, Error: ParseError<&'a [u8]>>(
input: &'a [u8],
) -> IResult<&[u8], DerivationPathLe, Error> {
let (next_input, len) = many0_count(le_u32)(input)?;
pub fn derivation_path_le<Input, Error>(
input: Input,
) -> IResult<Input, DerivationPathLe<Input>, Error>
where
Input: Clone + Slice<RangeFrom<usize>> + InputIter<Item = u8> + InputLength,
Error: ParseError<Input>,
{
let (next_input, len) = many0_count(le_u32)(input.clone())?;
Ok((next_input, DerivationPathLe { buf: input, len }))
}

/// Parses a BIP-32 fingerprint.
pub fn fingerprint<'a, Error: ParseError<&'a [u8]>>(
i: &'a [u8],
) -> IResult<&'a [u8], Fingerprint, Error> {
pub fn fingerprint<Input, Error>(i: Input) -> IResult<Input, Fingerprint, Error>
where
Input: PartialEq + Clone + Slice<RangeFrom<usize>> + InputIter<Item = u8> + InputLength,
Error: ParseError<Input>,
{
let mut buf = [0; 4];
let (i, _) = fill(u8, &mut buf)(i)?;
Ok((i, Fingerprint(buf)))
}

/// Parses a [`KeySource`].
pub fn key_source<'a, Error: ParseError<&'a [u8]>>(
i: &'a [u8],
) -> IResult<&'a [u8], KeySource, Error> {
pub fn key_source<Input, Error>(i: Input) -> IResult<Input, KeySource<Input>, Error>
where
Input: PartialEq + Clone + Slice<RangeFrom<usize>> + InputIter<Item = u8> + InputLength,
Error: ParseError<Input>,
{
let fields = tuple((fingerprint, derivation_path_le));
let mut parser = map(fields, |(fingerprint, path)| KeySource {
fingerprint,
Expand All @@ -148,7 +187,8 @@ mod tests {
for test_vector in vectors.valid {
println!("Test vector: {}", test_vector.name);
for chain in &test_vector.chains {
extended_public_key::<nom::error::Error<_>>(&chain.extended_public_key.0).unwrap();
let buf = chain.extended_public_key.as_slice();
extended_public_key::<_, nom::error::Error<_>>(buf).unwrap();
}
}
}
Expand All @@ -162,7 +202,7 @@ mod tests {
println!("Test vector: {}", test_vector.name);
for (i, key) in test_vector.extended_keys.iter().enumerate() {
println!("Index: {}", i);
extended_public_key::<nom::error::Error<_>>(&key.0).unwrap_err();
extended_public_key::<_, nom::error::Error<_>>(key.as_slice()).unwrap_err();
}
}
}
Expand All @@ -171,7 +211,7 @@ mod tests {
fn parse_derivation_path_le() {
const INPUT: &[u8] = &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80];

let (i, path) = derivation_path_le::<nom::error::Error<_>>(INPUT).unwrap();
let (i, path) = derivation_path_le::<_, nom::error::Error<_>>(INPUT).unwrap();
assert!(i.is_empty());
assert_eq!(path.buf, INPUT);
}
Expand All @@ -180,7 +220,7 @@ mod tests {
fn parse_key_source() {
const INPUT: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xEE, 0xFF, 0xFF];

let (i, source) = key_source::<nom::error::Error<_>>(INPUT).unwrap();
let (i, source) = key_source::<_, nom::error::Error<_>>(INPUT).unwrap();
assert!(i.is_empty());
assert_eq!(source.fingerprint, Fingerprint([0xAA, 0xBB, 0xCC, 0xDD]));
assert_eq!(source.path.iter().next().unwrap(), 0xFFFF_EEEE);
Expand Down

0 comments on commit 04b51b0

Please sign in to comment.