Skip to content

Commit 1822c15

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 2b60efb commit 1822c15

File tree

1 file changed

+57
-6
lines changed

1 file changed

+57
-6
lines changed

src/lib.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ pub fn decode(s: &str) -> Result<(Hrp, Vec<u8>), DecodeError> {
219219
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
220220
#[cfg(feature = "alloc")]
221221
#[inline]
222-
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> {
223223
encode_lower::<Ck>(hrp, data)
224224
}
225225

@@ -229,7 +229,9 @@ pub fn encode<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::Error>
229229
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
230230
#[cfg(feature = "alloc")]
231231
#[inline]
232-
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+
233235
let mut buf = String::new();
234236
encode_lower_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
235237
Ok(buf)
@@ -241,7 +243,9 @@ pub fn encode_lower<Ck: Checksum>(hrp: Hrp, data: &[u8]) -> Result<String, fmt::
241243
/// `Ck` algorithm (`NoChecksum` to exclude checksum all together).
242244
#[cfg(feature = "alloc")]
243245
#[inline]
244-
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+
245249
let mut buf = String::new();
246250
encode_upper_to_fmt::<Ck, String>(&mut buf, hrp, data)?;
247251
Ok(buf)
@@ -256,7 +260,7 @@ pub fn encode_to_fmt<Ck: Checksum, W: fmt::Write>(
256260
fmt: &mut W,
257261
hrp: Hrp,
258262
data: &[u8],
259-
) -> Result<(), fmt::Error> {
263+
) -> Result<(), EncodeError> {
260264
encode_lower_to_fmt::<Ck, W>(fmt, hrp, data)
261265
}
262266

@@ -269,7 +273,9 @@ pub fn encode_lower_to_fmt<Ck: Checksum, W: fmt::Write>(
269273
fmt: &mut W,
270274
hrp: Hrp,
271275
data: &[u8],
272-
) -> Result<(), fmt::Error> {
276+
) -> Result<(), EncodeError> {
277+
let _ = encoded_length::<Ck>(&hrp, data)?;
278+
273279
let iter = data.iter().copied().bytes_to_fes();
274280
let chars = iter.with_checksum::<Ck>(&hrp).chars();
275281
for c in chars {
@@ -287,7 +293,9 @@ pub fn encode_upper_to_fmt<Ck: Checksum, W: fmt::Write>(
287293
fmt: &mut W,
288294
hrp: Hrp,
289295
data: &[u8],
290-
) -> Result<(), fmt::Error> {
296+
) -> Result<(), EncodeError> {
297+
let _ = encoded_length::<Ck>(&hrp, data)?;
298+
291299
let iter = data.iter().copied().bytes_to_fes();
292300
let chars = iter.with_checksum::<Ck>(&hrp).chars();
293301
for c in chars {
@@ -455,6 +463,49 @@ impl From<DecodeError> for DecodeFromReaderError {
455463
fn from(e: DecodeError) -> Self { Self::Decode(e) }
456464
}
457465

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+
458509
/// Encoding bech32 string exceeds the spec limit of 90 characters.
459510
#[derive(Debug, Clone, PartialEq, Eq)]
460511
#[non_exhaustive]

0 commit comments

Comments
 (0)