Skip to content

Commit 3e6ecff

Browse files
committed
Check encoded string is not too long
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.
1 parent 6dc73c8 commit 3e6ecff

File tree

1 file changed

+57
-7
lines changed

1 file changed

+57
-7
lines changed

src/lib.rs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ pub mod segwit;
142142
use alloc::{string::String, vec::Vec};
143143
use core::fmt;
144144

145-
#[cfg(feature = "alloc")]
146145
use crate::error::write_err;
147146
#[cfg(doc)]
148147
use crate::primitives::decode::CheckedHrpstring;
@@ -220,7 +219,7 @@ pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
220219
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
221220
#[cfg(feature = "alloc")]
222221
#[inline]
223-
pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
222+
pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
224223
encode_lower::<Ck>(hrp, data)
225224
}
226225

@@ -230,7 +229,9 @@ pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error>
230229
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
231230
#[cfg(feature = "alloc")]
232231
#[inline]
233-
pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
232+
pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
233+
let _ = encoded_length::<Ck>(&hrp, data)?;
234+
234235
let mut buf = String::new();
235236
encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
236237
Ok(buf)
@@ -242,7 +243,9 @@ pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::
242243
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
243244
#[cfg(feature = "alloc")]
244245
#[inline]
245-
pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error> {
246+
pub fn encode_upper<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, EncodeError> {
247+
let _ = encoded_length::<Ck>(&hrp, data)?;
248+
246249
let mut buf = String::new();
247250
encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
248251
Ok(buf)
@@ -257,7 +260,7 @@ pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
257260
fmt: &mut W,
258261
hrp: Hrp,
259262
data: &[u8],
260-
) -> Result<(), fmt::Error> {
263+
) -> Result<(), EncodeError> {
261264
encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
262265
}
263266

@@ -270,7 +273,9 @@ pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
270273
fmt: &mut W,
271274
hrp: Hrp,
272275
data: &[u8],
273-
) -> Result<(), fmt::Error> {
276+
) -> Result<(), EncodeError> {
277+
let _ = encoded_length::<Ck>(&hrp, data)?;
278+
274279
let iter = data.iter().copied().bytes_to_fes();
275280
let chars = iter.with_checksum::<Ck>(&hrp).chars();
276281
for c in chars {
@@ -288,7 +293,9 @@ pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
288293
fmt: &mut W,
289294
hrp: Hrp,
290295
data: &[u8],
291-
) -> Result<(), fmt::Error> {
296+
) -> Result<(), EncodeError> {
297+
let _ = encoded_length::<Ck>(&hrp, data)?;
298+
292299
let iter = data.iter().copied().bytes_to_fes();
293300
let chars = iter.with_checksum::<Ck>(&hrp).chars();
294301
for c in chars {
@@ -456,6 +463,49 @@ impl From<DecodeError> for DecodeFromReaderError {
456463
fn from(e: DecodeError) -> Self { Self::Decode(e) }
457464
}
458465

466+
/// An error while encoding an address.
467+
#[derive(Debug, Clone, PartialEq, Eq)]
468+
#[non_exhaustive]
469+
pub enum EncodeError {
470+
/// Encoding HRP and data into a bech32 string exceeds the spec limit of 90 characters.
471+
TooLong(EncodedLengthError),
472+
/// Error writing to the formatter.
473+
Fmt(fmt::Error),
474+
}
475+
476+
impl fmt::Display for EncodeError {
477+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478+
use EncodeError::*;
479+
480+
match *self {
481+
TooLong(ref e) => write_err!(f, "encoded string too long"; e),
482+
Fmt(ref e) => write_err!(f, "write to formatter failed"; e),
483+
}
484+
}
485+
}
486+
487+
#[cfg(feature = "std")]
488+
impl std::error::Error for EncodeError {
489+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
490+
use EncodeError::*;
491+
492+
match *self {
493+
TooLong(ref e) => Some(e),
494+
Fmt(ref e) => Some(e),
495+
}
496+
}
497+
}
498+
499+
impl From<EncodedLengthError> for EncodeError {
500+
#[inline]
501+
fn from(e: EncodedLengthError) -> Self { Self::TooLong(e) }
502+
}
503+
504+
impl From<fmt::Error> for EncodeError {
505+
#[inline]
506+
fn from(e: fmt::Error) -> Self { Self::Fmt(e) }
507+
}
508+
459509
/// Encoding bech32 string exceeds the spec limit of 90 characters.
460510
#[derive(Debug, Clone, PartialEq, Eq)]
461511
#[non_exhaustive]

0 commit comments

Comments
 (0)