Skip to content

Commit

Permalink
Check encoded string is not too long
Browse files Browse the repository at this point in the history
Currently it is possible to encode a string that is more than 90
characters long, this is in violation of BIP-173. We recently added a
function `crate::encoded_length` that does the length check,
call it when encoding in all the encode and encode to fmt functions.
  • Loading branch information
tcharding committed Oct 17, 2023
1 parent 2b60efb commit 1822c15
Showing 1 changed file with 57 additions and 6 deletions.
63 changes: 57 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
#[cfg(feature = "alloc")]
#[inline]
pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
encode_lower::<Ck>(hrp, data)
}

Expand All @@ -229,7 +229,9 @@ pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error>
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
#[cfg(feature = "alloc")]
#[inline]
pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
let _ = encoded_length::<Ck>(&hrp, data)?;

let mut buf = String::new();
encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
Ok(buf)
Expand All @@ -241,7 +243,9 @@ pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
#[cfg(feature = "alloc")]
#[inline]
pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
let _ = encoded_length::<Ck>(&hrp, data)?;

let mut buf = String::new();
encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
Ok(buf)
Expand All @@ -256,7 +260,7 @@ pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
) -> Result<(), EncodeError> {
encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
}

Expand All @@ -269,7 +273,9 @@ pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
) -> Result<(), EncodeError> {
let _ = encoded_length::<Ck>(&hrp, data)?;

let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
Expand All @@ -287,7 +293,9 @@ pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
fmt: &mut W,
hrp: Hrp,
data: &[u8],
) -> Result<(), fmt::Error> {
) -> Result<(), EncodeError> {
let _ = encoded_length::<Ck>(&hrp, data)?;

let iter = data.iter().copied().bytes_to_fes();
let chars = iter.with_checksum::<Ck>(&hrp).chars();
for c in chars {
Expand Down Expand Up @@ -455,6 +463,49 @@ impl From<DecodeError> for DecodeFromReaderError {
fn from(e: DecodeError) -> Self { Self::Decode(e) }
}

/// An error while encoding an address.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EncodeError {
/// Encoding HRP and data into a bech32 string exceeds the spec limit of 90 characters.
TooLong(EncodedLengthError),
/// Error writing to the formatter.
Fmt(fmt::Error),
}

impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use EncodeError::*;

match *self {
TooLong(ref e) => write_err!(f, "encoded string too long"; e),
Fmt(ref e) => write_err!(f, "write to formatter failed"; e),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for EncodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use EncodeError::*;

match *self {
TooLong(ref e) => Some(e),
Fmt(ref e) => Some(e),
}
}
}

impl From<EncodedLengthError> for EncodeError {
#[inline]
fn from(e: EncodedLengthError) -> Self { Self::TooLong(e) }
}

impl From<fmt::Error> for EncodeError {
#[inline]
fn from(e: fmt::Error) -> Self { Self::Fmt(e) }
}

/// Encoding bech32 string exceeds the spec limit of 90 characters.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
Expand Down

0 comments on commit 1822c15

Please sign in to comment.