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 6dc73c8 commit 3e6ecff
Showing 1 changed file with 57 additions and 7 deletions.
64 changes: 57 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ pub mod segwit;
use alloc::{string::String, vec::Vec};
use core::fmt;

#[cfg(feature = "alloc")]
use crate::error::write_err;
#[cfg(doc)]
use crate::primitives::decode::CheckedHrpstring;
Expand Down Expand Up @@ -220,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 @@ -230,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 @@ -242,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 @@ -257,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 @@ -270,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 @@ -288,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 @@ -456,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 3e6ecff

Please sign in to comment.