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.
This commit is contained in:
志宇 2022-09-29 13:06:03 +08:00
parent af0b3698c6
commit fd34956c29
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
3 changed files with 32 additions and 24 deletions

View File

@ -41,12 +41,21 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
c c
} }
/// Computes the checksum bytes of a descriptor /// Computes the checksum bytes of a descriptor.
pub fn get_checksum_bytes(desc: &str) -> Result<[u8; 8], DescriptorError> { /// `exclude_hash = true` ignores all data after the first '#' (inclusive).
pub fn get_checksum_bytes(mut desc: &str, exclude_hash: bool) -> Result<[u8; 8], DescriptorError> {
let mut c = 1; let mut c = 1;
let mut cls = 0; let mut cls = 0;
let mut clscount = 0; let mut clscount = 0;
let mut original_checksum = None;
if exclude_hash {
if let Some(split) = desc.split_once('#') {
desc = split.0;
original_checksum = Some(split.1);
}
}
for ch in desc.as_bytes() { for ch in desc.as_bytes() {
let pos = INPUT_CHARSET let pos = INPUT_CHARSET
.iter() .iter()
@ -72,13 +81,20 @@ pub fn get_checksum_bytes(desc: &str) -> Result<[u8; 8], DescriptorError> {
checksum[j] = CHECKSUM_CHARSET[((c >> (5 * (7 - j))) & 31) as usize]; checksum[j] = CHECKSUM_CHARSET[((c >> (5 * (7 - j))) & 31) as usize];
} }
// if input data already had a checksum, check calculated checksum against original checksum
if let Some(original_checksum) = original_checksum {
if original_checksum.as_bytes() != &checksum {
return Err(DescriptorError::InvalidDescriptorChecksum);
}
}
Ok(checksum) Ok(checksum)
} }
/// Compute the checksum of a descriptor /// Compute the checksum of a descriptor
pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> { pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> {
// unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET` // unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET`
get_checksum_bytes(desc).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) }) get_checksum_bytes(desc, true).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
} }
#[cfg(test)] #[cfg(test)]

View File

@ -40,6 +40,7 @@ pub mod policy;
pub mod template; pub mod template;
pub use self::checksum::get_checksum; pub use self::checksum::get_checksum;
use self::checksum::get_checksum_bytes;
pub use self::derived::{AsDerived, DerivedDescriptorKey}; pub use self::derived::{AsDerived, DerivedDescriptorKey};
pub use self::error::Error as DescriptorError; pub use self::error::Error as DescriptorError;
pub use self::policy::Policy; pub use self::policy::Policy;
@ -84,19 +85,15 @@ impl IntoWalletDescriptor for &str {
secp: &SecpCtx, secp: &SecpCtx,
network: Network, network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
let descriptor = if self.contains('#') { let descriptor = match self.split_once('#') {
let parts: Vec<&str> = self.splitn(2, '#').collect(); Some((desc, original_checksum)) => {
if !get_checksum(parts[0]) let checksum = get_checksum_bytes(desc, false)?;
.ok() if original_checksum.as_bytes() != &checksum {
.map(|computed| computed == parts[1])
.unwrap_or(false)
{
return Err(DescriptorError::InvalidDescriptorChecksum); return Err(DescriptorError::InvalidDescriptorChecksum);
} }
desc
parts[0] }
} else { None => self,
self
}; };
ExtendedDescriptor::parse_descriptor(secp, descriptor)? ExtendedDescriptor::parse_descriptor(secp, descriptor)?

View File

@ -1943,15 +1943,10 @@ pub(crate) mod test {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let checksum = wallet.descriptor_checksum(KeychainKind::External); let checksum = wallet.descriptor_checksum(KeychainKind::External);
assert_eq!(checksum.len(), 8); assert_eq!(checksum.len(), 8);
assert_eq!(
let raw_descriptor = wallet get_checksum(&wallet.descriptor.to_string()).unwrap(),
.descriptor checksum
.to_string() );
.split_once('#')
.unwrap()
.0
.to_string();
assert_eq!(get_checksum(&raw_descriptor).unwrap(), checksum);
} }
#[test] #[test]