Skip to content

Commit f39cfaf

Browse files
committed
get_checksum_bytes now checks input data for checksum
If `exclude_hash` is set, we split the input data, and if a checksum already existed within the original data, we check the calculated checksum against the original checksum. Additionally, the implementation of `IntoWalletDescriptor` for `&str` has been refactored for clarity.
1 parent 74366f7 commit f39cfaf

File tree

3 files changed

+32
-24
lines changed

3 files changed

+32
-24
lines changed

src/descriptor/checksum.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,21 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
4141
c
4242
}
4343

44-
/// Computes the checksum bytes of a descriptor
45-
pub fn get_checksum_bytes(desc: &str) -> Result<[u8; 8], DescriptorError> {
44+
/// Computes the checksum bytes of a descriptor.
45+
/// `exclude_hash = true` ignores all data after the first '#' (inclusive).
46+
pub fn get_checksum_bytes(mut desc: &str, exclude_hash: bool) -> Result<[u8; 8], DescriptorError> {
4647
let mut c = 1;
4748
let mut cls = 0;
4849
let mut clscount = 0;
4950

51+
let mut original_checksum = None;
52+
if exclude_hash {
53+
if let Some(split) = desc.split_once('#') {
54+
desc = split.0;
55+
original_checksum = Some(split.1);
56+
}
57+
}
58+
5059
for ch in desc.as_bytes() {
5160
let pos = INPUT_CHARSET
5261
.iter()
@@ -72,13 +81,20 @@ pub fn get_checksum_bytes(desc: &str) -> Result<[u8; 8], DescriptorError> {
7281
checksum[j] = CHECKSUM_CHARSET[((c >> (5 * (7 - j))) & 31) as usize];
7382
}
7483

84+
// if input data already had a checksum, check calculated checksum against original checksum
85+
if let Some(original_checksum) = original_checksum {
86+
if original_checksum.as_bytes() != &checksum {
87+
return Err(DescriptorError::InvalidDescriptorChecksum);
88+
}
89+
}
90+
7591
Ok(checksum)
7692
}
7793

7894
/// Compute the checksum of a descriptor
7995
pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> {
8096
// unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET`
81-
get_checksum_bytes(desc).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
97+
get_checksum_bytes(desc, true).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
8298
}
8399

84100
#[cfg(test)]

src/descriptor/mod.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod policy;
4040
pub mod template;
4141

4242
pub use self::checksum::get_checksum;
43+
use self::checksum::get_checksum_bytes;
4344
pub use self::derived::{AsDerived, DerivedDescriptorKey};
4445
pub use self::error::Error as DescriptorError;
4546
pub use self::policy::Policy;
@@ -84,19 +85,15 @@ impl IntoWalletDescriptor for &str {
8485
secp: &SecpCtx,
8586
network: Network,
8687
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
87-
let descriptor = if self.contains('#') {
88-
let parts: Vec<&str> = self.splitn(2, '#').collect();
89-
if !get_checksum(parts[0])
90-
.ok()
91-
.map(|computed| computed == parts[1])
92-
.unwrap_or(false)
93-
{
94-
return Err(DescriptorError::InvalidDescriptorChecksum);
88+
let descriptor = match self.split_once('#') {
89+
Some((desc, original_checksum)) => {
90+
let checksum = get_checksum_bytes(desc, false)?;
91+
if original_checksum.as_bytes() != &checksum {
92+
return Err(DescriptorError::InvalidDescriptorChecksum);
93+
}
94+
desc
9595
}
96-
97-
parts[0]
98-
} else {
99-
self
96+
None => self,
10097
};
10198

10299
ExtendedDescriptor::parse_descriptor(secp, descriptor)?

src/wallet/mod.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,15 +1949,10 @@ pub(crate) mod test {
19491949
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
19501950
let checksum = wallet.descriptor_checksum(KeychainKind::External);
19511951
assert_eq!(checksum.len(), 8);
1952-
1953-
let raw_descriptor = wallet
1954-
.descriptor
1955-
.to_string()
1956-
.split_once('#')
1957-
.unwrap()
1958-
.0
1959-
.to_string();
1960-
assert_eq!(get_checksum(&raw_descriptor).unwrap(), checksum);
1952+
assert_eq!(
1953+
get_checksum(&wallet.descriptor.to_string()).unwrap(),
1954+
checksum
1955+
);
19611956
}
19621957

19631958
#[test]

0 commit comments

Comments
 (0)