Expose Bech32Encode trait for fuzzing
authorJeffrey Czyz <jkczyz@gmail.com>
Thu, 9 Feb 2023 16:59:11 +0000 (10:59 -0600)
committerJeffrey Czyz <jkczyz@gmail.com>
Fri, 24 Feb 2023 00:25:49 +0000 (18:25 -0600)
In order to fuzz test Bech32Encode parsing independent of the underlying
message deserialization, the trait needs to be exposed. Conditionally
expose it only for fuzzing.

lightning/src/offers/parse.rs

index 35c1425acc13b86316858065afb1ed15a7a6402e..6afd4d68fef385bb5b6b77b8375891e53878f6e7 100644 (file)
 //! Parsing and formatting for bech32 message encoding.
 
 use bitcoin::bech32;
-use bitcoin::bech32::{FromBase32, ToBase32};
 use bitcoin::secp256k1;
 use core::convert::TryFrom;
-use core::fmt;
 use crate::io;
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::SeekReadable;
 
 use crate::prelude::*;
 
-/// Indicates a message can be encoded using bech32.
-pub(super) trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
-       /// Human readable part of the message's bech32 encoding.
-       const BECH32_HRP: &'static str;
-
-       /// Parses a bech32-encoded message into a TLV stream.
-       fn from_bech32_str(s: &str) -> Result<Self, ParseError> {
-               // Offer encoding may be split by '+' followed by optional whitespace.
-               let encoded = match s.split('+').skip(1).next() {
-                       Some(_) => {
-                               for chunk in s.split('+') {
-                                       let chunk = chunk.trim_start();
-                                       if chunk.is_empty() || chunk.contains(char::is_whitespace) {
-                                               return Err(ParseError::InvalidContinuation);
+#[cfg(not(fuzzing))]
+pub(super) use sealed::Bech32Encode;
+
+#[cfg(fuzzing)]
+pub use sealed::Bech32Encode;
+
+mod sealed {
+       use bitcoin::bech32;
+       use bitcoin::bech32::{FromBase32, ToBase32};
+       use core::convert::TryFrom;
+       use core::fmt;
+       use super::ParseError;
+
+       use crate::prelude::*;
+
+       /// Indicates a message can be encoded using bech32.
+       pub trait Bech32Encode: AsRef<[u8]> + TryFrom<Vec<u8>, Error=ParseError> {
+               /// Human readable part of the message's bech32 encoding.
+               const BECH32_HRP: &'static str;
+
+               /// Parses a bech32-encoded message into a TLV stream.
+               fn from_bech32_str(s: &str) -> Result<Self, ParseError> {
+                       // Offer encoding may be split by '+' followed by optional whitespace.
+                       let encoded = match s.split('+').skip(1).next() {
+                               Some(_) => {
+                                       for chunk in s.split('+') {
+                                               let chunk = chunk.trim_start();
+                                               if chunk.is_empty() || chunk.contains(char::is_whitespace) {
+                                                       return Err(ParseError::InvalidContinuation);
+                                               }
                                        }
-                               }
 
-                               let s = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect::<String>();
-                               Bech32String::Owned(s)
-                       },
-                       None => Bech32String::Borrowed(s),
-               };
+                                       let s: String = s.chars().filter(|c| *c != '+' && !c.is_whitespace()).collect();
+                                       Bech32String::Owned(s)
+                               },
+                               None => Bech32String::Borrowed(s),
+                       };
 
-               let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
+                       let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
 
-               if hrp != Self::BECH32_HRP {
-                       return Err(ParseError::InvalidBech32Hrp);
-               }
+                       if hrp != Self::BECH32_HRP {
+                               return Err(ParseError::InvalidBech32Hrp);
+                       }
 
-               let data = Vec::<u8>::from_base32(&data)?;
-               Self::try_from(data)
-       }
+                       let data = Vec::<u8>::from_base32(&data)?;
+                       Self::try_from(data)
+               }
 
-       /// Formats the message using bech32-encoding.
-       fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-               bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32())
-                       .expect("HRP is invalid").unwrap();
+               /// Formats the message using bech32-encoding.
+               fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+                       bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32())
+                               .expect("HRP is invalid").unwrap();
 
-               Ok(())
+                       Ok(())
+               }
        }
-}
 
-// Used to avoid copying a bech32 string not containing the continuation character (+).
-enum Bech32String<'a> {
-       Borrowed(&'a str),
-       Owned(String),
-}
+       // Used to avoid copying a bech32 string not containing the continuation character (+).
+       enum Bech32String<'a> {
+               Borrowed(&'a str),
+               Owned(String),
+       }
 
-impl<'a> AsRef<str> for Bech32String<'a> {
-       fn as_ref(&self) -> &str {
-               match self {
-                       Bech32String::Borrowed(s) => s,
-                       Bech32String::Owned(s) => s,
+       impl<'a> AsRef<str> for Bech32String<'a> {
+               fn as_ref(&self) -> &str {
+                       match self {
+                               Bech32String::Borrowed(s) => s,
+                               Bech32String::Owned(s) => s,
+                       }
                }
        }
 }