Skip to main content

bech32_no_std/
lib.rs

1// Copyright (c) 2017 Clark Moody
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Encoding and decoding of the Bech32 format
22//!
23//! Bech32 is an encoding scheme that is easy to use for humans and efficient to encode in QR codes.
24//!
25//! A Bech32 string consists of a human-readable part (HRP), a separator (the character `'1'`), and a data part.
26//! A checksum at the end of the string provides error detection to prevent mistakes when the string is written off or read out loud.
27//!
28//! The original description in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) has more details.
29//!
30//! # Examples
31//!
32//! ```
33//! use bech32::{self, FromBase32, ToBase32};
34//!
35//! let encoded = bech32::encode("bech32", vec![0x00, 0x01, 0x02].to_base32()).unwrap();
36//! assert_eq!(encoded, "bech321qqqsyrhqy2a".to_string());
37//!
38//! let (hrp, data) = bech32::decode(&encoded).unwrap();
39//! assert_eq!(hrp, "bech32");
40//! assert_eq!(Vec::<u8>::from_base32(&data).unwrap(), vec![0x00, 0x01, 0x02]);
41//! ```
42//!
43
44// Allow trait objects without dyn on nightly and make 1.22 ignore the unknown lint
45#![allow(unknown_lints)]
46#![allow(bare_trait_objects)]
47#![deny(missing_docs)]
48#![deny(non_upper_case_globals)]
49#![deny(non_camel_case_types)]
50#![deny(non_snake_case)]
51#![deny(unused_mut)]
52#![cfg_attr(feature = "strict", deny(warnings))]
53#![cfg_attr(not(feature = "std"), no_std)]
54
55use core::fmt;
56
57#[cfg(feature = "std")]
58use std::borrow::Cow;
59
60#[cfg(not(feature = "std"))]
61extern crate alloc;
62#[cfg(not(feature = "std"))]
63use alloc::{borrow::Cow, string::String, vec::Vec};
64
65// AsciiExt is needed for Rust 1.14 but not for newer versions
66#[allow(unused_imports, deprecated)]
67#[cfg(feature = "std")]
68use std::ascii::AsciiExt;
69
70/// Integer in the range `0..32`
71#[derive(PartialEq, Eq, Debug, Copy, Clone, Default, PartialOrd, Ord, Hash)]
72#[allow(non_camel_case_types)]
73pub struct u5(u8);
74
75impl u5 {
76    /// Convert a `u8` to `u5` if in range, return `Error` otherwise
77    pub fn try_from_u8(value: u8) -> Result<u5, Error> {
78        if value > 31 {
79            Err(Error::InvalidData(value))
80        } else {
81            Ok(u5(value))
82        }
83    }
84
85    /// Returns a copy of the underlying `u8` value
86    pub fn to_u8(self) -> u8 {
87        self.0
88    }
89
90    /// Get char representing this 5 bit value as defined in BIP173
91    pub fn to_char(self) -> char {
92        CHARSET[self.to_u8() as usize]
93    }
94}
95
96impl Into<u8> for u5 {
97    fn into(self) -> u8 {
98        self.0
99    }
100}
101
102impl AsRef<u8> for u5 {
103    fn as_ref(&self) -> &u8 {
104        &self.0
105    }
106}
107
108/// Interface to write `u5`s into a sink
109pub trait WriteBase32 {
110    /// Write error
111    type Err: fmt::Debug;
112
113    /// Write a `u5` slice
114    fn write(&mut self, data: &[u5]) -> Result<(), Self::Err> {
115        for b in data {
116            self.write_u5(*b)?;
117        }
118        Ok(())
119    }
120
121    /// Write a single `u5`
122    fn write_u5(&mut self, data: u5) -> Result<(), Self::Err>;
123}
124
125/// Allocationless Bech32 writer that accumulates the checksum data internally and writes them out
126/// in the end.
127pub struct Bech32Writer<'a> {
128    formatter: &'a mut fmt::Write,
129    chk: u32,
130}
131
132impl<'a> Bech32Writer<'a> {
133    /// Creates a new writer that can write a bech32 string without allocating itself.
134    ///
135    /// This is a rather low-level API and doesn't check the HRP or data length for standard
136    /// compliance.
137    pub fn new(hrp: &str, fmt: &'a mut fmt::Write) -> Result<Bech32Writer<'a>, fmt::Error> {
138        let mut writer = Bech32Writer {
139            formatter: fmt,
140            chk: 1,
141        };
142
143        writer.formatter.write_str(hrp)?;
144        writer.formatter.write_char(SEP)?;
145
146        // expand HRP
147        for b in hrp.bytes() {
148            writer.polymod_step(u5(b >> 5));
149        }
150        writer.polymod_step(u5(0));
151        for b in hrp.bytes() {
152            writer.polymod_step(u5(b & 0x1f));
153        }
154
155        Ok(writer)
156    }
157
158    fn polymod_step(&mut self, v: u5) {
159        let b = (self.chk >> 25) as u8;
160        self.chk = (self.chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref()));
161
162        for (i, item) in GEN.iter().enumerate() {
163            if (b >> i) & 1 == 1 {
164                self.chk ^= item;
165            }
166        }
167    }
168
169    /// Write out the checksum at the end. If this method isn't called this will happen on drop.
170    pub fn finalize(mut self) -> fmt::Result {
171        self.inner_finalize()?;
172        #[cfg(feature = "std")]
173        std::mem::forget(self);
174        Ok(())
175    }
176
177    fn inner_finalize(&mut self) -> fmt::Result {
178        // Pad with 6 zeros
179        for _ in 0..6 {
180            self.polymod_step(u5(0))
181        }
182
183        let plm: u32 = self.chk ^ 1;
184
185        for p in 0..6 {
186            self.formatter
187                .write_char(u5(((plm >> (5 * (5 - p))) & 0x1f) as u8).to_char())?;
188        }
189
190        Ok(())
191    }
192}
193impl<'a> WriteBase32 for Bech32Writer<'a> {
194    type Err = fmt::Error;
195
196    /// Writes a single 5 bit value of the data part
197    fn write_u5(&mut self, data: u5) -> fmt::Result {
198        self.polymod_step(data);
199        self.formatter.write_char(data.to_char())
200    }
201}
202
203impl<'a> Drop for Bech32Writer<'a> {
204    fn drop(&mut self) {
205        self.inner_finalize()
206            .expect("Unhandled error writing the checksum on drop.")
207    }
208}
209
210/// Parse/convert base32 slice to `Self`. It is the reciprocal of
211/// `ToBase32`.
212pub trait FromBase32: Sized {
213    /// The associated error which can be returned from parsing (e.g. because of bad padding).
214    type Err;
215
216    /// Convert a base32 slice to `Self`.
217    fn from_base32(b32: &[u5]) -> Result<Self, Self::Err>;
218}
219
220impl WriteBase32 for Vec<u5> {
221    type Err = ();
222
223    fn write(&mut self, data: &[u5]) -> Result<(), Self::Err> {
224        self.extend_from_slice(data);
225        Ok(())
226    }
227
228    fn write_u5(&mut self, data: u5) -> Result<(), Self::Err> {
229        self.push(data);
230        Ok(())
231    }
232}
233
234impl FromBase32 for Vec<u8> {
235    type Err = Error;
236
237    /// Convert base32 to base256, removes null-padding if present, returns
238    /// `Err(Error::InvalidPadding)` if padding bits are unequal `0`
239    fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
240        convert_bits(b32, 5, 8, false)
241    }
242}
243
244/// A trait for converting a value to a type `T` that represents a `u5` slice.
245pub trait ToBase32 {
246    /// Convert `Self` to base32 vector
247    fn to_base32(&self) -> Vec<u5> {
248        let mut vec = Vec::new();
249        self.write_base32(&mut vec).unwrap();
250        vec
251    }
252
253    /// Encode as base32 and write it to the supplied writer
254    /// Implementations shouldn't allocate.
255    fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err>;
256}
257
258/// Interface to calculate the length of the base32 representation before actually serializing
259pub trait Base32Len: ToBase32 {
260    /// Calculate the base32 serialized length
261    fn base32_len(&self) -> usize;
262}
263
264impl<T: AsRef<[u8]>> ToBase32 for T {
265    fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
266        // Amount of bits left over from last round, stored in buffer.
267        let mut buffer_bits = 0u32;
268        // Holds all unwritten bits left over from last round. The bits are stored beginning from
269        // the most significant bit. E.g. if buffer_bits=3, then the byte with bits a, b and c will
270        // look as follows: [a, b, c, 0, 0, 0, 0, 0]
271        let mut buffer: u8 = 0;
272
273        for &b in self.as_ref() {
274            // Write first u5 if we have to write two u5s this round. That only happens if the
275            // buffer holds too many bits, so we don't have to combine buffer bits with new bits
276            // from this rounds byte.
277            if buffer_bits >= 5 {
278                writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?;
279                buffer <<= 5;
280                buffer_bits -= 5;
281            }
282
283            // Combine all bits from buffer with enough bits from this rounds byte so that they fill
284            // a u5. Save reamining bits from byte to buffer.
285            let from_buffer = buffer >> 3;
286            let from_byte = b >> (3 + buffer_bits); // buffer_bits <= 4
287
288            writer.write_u5(u5(from_buffer | from_byte))?;
289            buffer = b << (5 - buffer_bits);
290            buffer_bits += 3;
291        }
292
293        // There can be at most two u5s left in the buffer after processing all bytes, write them.
294        if buffer_bits >= 5 {
295            writer.write_u5(u5((buffer & 0b1111_1000) >> 3))?;
296            buffer <<= 5;
297            buffer_bits -= 5;
298        }
299
300        if buffer_bits != 0 {
301            writer.write_u5(u5(buffer >> 3))?;
302        }
303
304        Ok(())
305    }
306}
307
308impl<T: AsRef<[u8]>> Base32Len for T {
309    fn base32_len(&self) -> usize {
310        let bits = self.as_ref().len() * 8;
311        if bits % 5 == 0 {
312            bits / 5
313        } else {
314            bits / 5 + 1
315        }
316    }
317}
318
319/// A trait to convert between u8 arrays and u5 arrays without changing the content of the elements,
320/// but checking that they are in range.
321pub trait CheckBase32<T: AsRef<[u5]>> {
322    /// Error type if conversion fails
323    type Err;
324
325    /// Check if all values are in range and return array-like struct of `u5` values
326    fn check_base32(self) -> Result<T, Self::Err>;
327}
328
329impl<'f, T: AsRef<[u8]>> CheckBase32<Vec<u5>> for T {
330    type Err = Error;
331
332    fn check_base32(self) -> Result<Vec<u5>, Self::Err> {
333        self.as_ref()
334            .iter()
335            .map(|x| u5::try_from_u8(*x))
336            .collect::<Result<Vec<u5>, Error>>()
337    }
338}
339
340#[derive(Clone, Copy, PartialEq, Eq)]
341enum Case {
342    Upper,
343    Lower,
344    None,
345}
346
347/// Check if the HRP is valid. Returns the case of the HRP, if any.
348///
349/// # Errors
350/// * **MixedCase**: If the HRP contains both uppercase and lowercase characters.
351/// * **InvalidChar**: If the HRP contains any non-ASCII characters (outside 33..=126).
352/// * **InvalidLength**: If the HRP is outside 1..83 characters long.
353fn check_hrp(hrp: &str) -> Result<Case, Error> {
354    if hrp.is_empty() || hrp.len() > 83 {
355        return Err(Error::InvalidLength);
356    }
357
358    let mut has_lower: bool = false;
359    let mut has_upper: bool = false;
360    for b in hrp.bytes() {
361        // Valid subset of ASCII
362        if b < 33 || b > 126 {
363            return Err(Error::InvalidChar(b as char));
364        }
365
366        if b >= b'a' && b <= b'z' {
367            has_lower = true;
368        } else if b >= b'A' && b <= b'Z' {
369            has_upper = true;
370        };
371
372        if has_lower && has_upper {
373            return Err(Error::MixedCase);
374        }
375    }
376
377    Ok(match (has_upper, has_lower) {
378        (true, false) => Case::Upper,
379        (false, true) => Case::Lower,
380        (false, false) => Case::None,
381        (true, true) => unreachable!(),
382    })
383}
384
385/// Encode a bech32 payload to an [fmt::Write].
386/// This method is intended for implementing traits from [std::fmt].
387///
388/// # Errors
389/// * If [check_hrp] returns an error for the given HRP.
390/// # Deviations from standard
391/// * No length limits are enforced for the data part
392pub fn encode_to_fmt<T: AsRef<[u5]>>(
393    fmt: &mut fmt::Write,
394    hrp: &str,
395    data: T,
396) -> Result<fmt::Result, Error> {
397    let hrp_lower = match check_hrp(&hrp)? {
398        Case::Upper => Cow::Owned(hrp.to_lowercase()),
399        Case::Lower | Case::None => Cow::Borrowed(hrp),
400    };
401
402    match Bech32Writer::new(&hrp_lower, fmt) {
403        Ok(mut writer) => {
404            Ok(writer.write(data.as_ref()).and_then(|_| {
405                // Finalize manually to avoid panic on drop if write fails
406                writer.finalize()
407            }))
408        }
409        Err(e) => Ok(Err(e)),
410    }
411}
412
413/// Encode a bech32 payload to string.
414///
415/// # Errors
416/// * If [check_hrp] returns an error for the given HRP.
417/// # Deviations from standard
418/// * No length limits are enforced for the data part
419pub fn encode<T: AsRef<[u5]>>(hrp: &str, data: T) -> Result<String, Error> {
420    let mut buf = String::new();
421    encode_to_fmt(&mut buf, hrp, data)?.unwrap();
422    Ok(buf)
423}
424
425/// Decode a bech32 string into the raw HRP and the data bytes.
426///
427/// Returns the HRP in lowercase..
428pub fn decode(s: &str) -> Result<(String, Vec<u5>), Error> {
429    // Ensure overall length is within bounds
430    if s.len() < 8 {
431        return Err(Error::InvalidLength);
432    }
433
434    // Split at separator and check for two pieces
435    let (raw_hrp, raw_data) = match s.rfind(SEP) {
436        None => return Err(Error::MissingSeparator),
437        Some(sep) => {
438            let (hrp, data) = s.split_at(sep);
439            (hrp, &data[1..])
440        }
441    };
442    if raw_data.len() < 6 {
443        return Err(Error::InvalidLength);
444    }
445
446    let mut case = check_hrp(&raw_hrp)?;
447    let hrp_lower = match case {
448        Case::Upper => raw_hrp.to_lowercase(),
449        // already lowercase
450        Case::Lower | Case::None => String::from(raw_hrp),
451    };
452
453    // Check data payload
454    let mut data = raw_data
455        .chars()
456        .map(|c| {
457            // Only check if c is in the ASCII range, all invalid ASCII
458            // characters have the value -1 in CHARSET_REV (which covers
459            // the whole ASCII range) and will be filtered out later.
460            if !c.is_ascii() {
461                return Err(Error::InvalidChar(c));
462            }
463
464            if c.is_lowercase() {
465                match case {
466                    Case::Upper => return Err(Error::MixedCase),
467                    Case::None => case = Case::Lower,
468                    Case::Lower => {}
469                }
470            } else if c.is_uppercase() {
471                match case {
472                    Case::Lower => return Err(Error::MixedCase),
473                    Case::None => case = Case::Upper,
474                    Case::Upper => {}
475                }
476            }
477
478            // c should be <128 since it is in the ASCII range, CHARSET_REV.len() == 128
479            let num_value = CHARSET_REV[c as usize];
480
481            if num_value > 31 || num_value < 0 {
482                return Err(Error::InvalidChar(c));
483            }
484
485            Ok(u5::try_from_u8(num_value as u8).expect("range checked above, num_value <= 31"))
486        })
487        .collect::<Result<Vec<u5>, Error>>()?;
488
489    // Ensure checksum
490    if !verify_checksum(&hrp_lower.as_bytes(), &data) {
491        return Err(Error::InvalidChecksum);
492    }
493
494    // Remove checksum from data payload
495    let dbl: usize = data.len();
496    data.truncate(dbl - 6);
497
498    Ok((hrp_lower, data))
499}
500
501fn verify_checksum(hrp: &[u8], data: &[u5]) -> bool {
502    let mut exp = hrp_expand(hrp);
503    exp.extend_from_slice(data);
504    polymod(&exp) == 1u32
505}
506
507fn hrp_expand(hrp: &[u8]) -> Vec<u5> {
508    let mut v: Vec<u5> = Vec::new();
509    for b in hrp {
510        v.push(u5::try_from_u8(*b >> 5).expect("can't be out of range, max. 7"));
511    }
512    v.push(u5::try_from_u8(0).unwrap());
513    for b in hrp {
514        v.push(u5::try_from_u8(*b & 0x1f).expect("can't be out of range, max. 31"));
515    }
516    v
517}
518
519fn polymod(values: &[u5]) -> u32 {
520    let mut chk: u32 = 1;
521    let mut b: u8;
522    for v in values {
523        b = (chk >> 25) as u8;
524        chk = (chk & 0x01ff_ffff) << 5 ^ (u32::from(*v.as_ref()));
525
526        for (i, item) in GEN.iter().enumerate() {
527            if (b >> i) & 1 == 1 {
528                chk ^= item;
529            }
530        }
531    }
532    chk
533}
534
535/// Human-readable part and data part separator
536const SEP: char = '1';
537
538/// Encoding character set. Maps data value -> char
539const CHARSET: [char; 32] = [
540    'q', 'p', 'z', 'r', 'y', '9', 'x', '8', //  +0
541    'g', 'f', '2', 't', 'v', 'd', 'w', '0', //  +8
542    's', '3', 'j', 'n', '5', '4', 'k', 'h', // +16
543    'c', 'e', '6', 'm', 'u', 'a', '7', 'l', // +24
544];
545
546/// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
547const CHARSET_REV: [i8; 128] = [
548    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
549    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
550    15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23,
551    -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29,
552    -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1,
553    -1, -1, -1, -1,
554];
555
556/// Generator coefficients
557const GEN: [u32; 5] = [
558    0x3b6a_57b2,
559    0x2650_8e6d,
560    0x1ea1_19fa,
561    0x3d42_33dd,
562    0x2a14_62b3,
563];
564
565/// Error types for Bech32 encoding / decoding
566#[derive(Copy, Clone, PartialEq, Debug)]
567pub enum Error {
568    /// String does not contain the separator character
569    MissingSeparator,
570    /// The checksum does not match the rest of the data
571    InvalidChecksum,
572    /// The data or human-readable part is too long or too short
573    InvalidLength,
574    /// Some part of the string contains an invalid character
575    InvalidChar(char),
576    /// Some part of the data has an invalid value
577    InvalidData(u8),
578    /// The bit conversion failed due to a padding issue
579    InvalidPadding,
580    /// The whole string must be of one case
581    MixedCase,
582}
583
584impl fmt::Display for Error {
585    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586        match *self {
587            Error::MissingSeparator => write!(f, "missing human-readable separator, \"{}\"", SEP),
588            Error::InvalidChecksum => write!(f, "invalid checksum"),
589            Error::InvalidLength => write!(f, "invalid length"),
590            Error::InvalidChar(n) => write!(f, "invalid character (code={})", n),
591            Error::InvalidData(n) => write!(f, "invalid data point ({})", n),
592            Error::InvalidPadding => write!(f, "invalid padding"),
593            Error::MixedCase => write!(f, "mixed-case strings not allowed"),
594        }
595    }
596}
597
598#[cfg(feature = "std")]
599impl std::error::Error for Error {
600    fn description(&self) -> &str {
601        match *self {
602            Error::MissingSeparator => "missing human-readable separator",
603            Error::InvalidChecksum => "invalid checksum",
604            Error::InvalidLength => "invalid length",
605            Error::InvalidChar(_) => "invalid character",
606            Error::InvalidData(_) => "invalid data point",
607            Error::InvalidPadding => "invalid padding",
608            Error::MixedCase => "mixed-case strings not allowed",
609        }
610    }
611}
612
613/// Convert between bit sizes
614///
615/// # Errors
616/// * `Error::InvalidData` if any element of `data` is out of range
617/// * `Error::InvalidPadding` if `pad == false` and the padding bits are not `0`
618///
619/// # Panics
620/// Function will panic if attempting to convert `from` or `to` a bit size that
621/// is 0 or larger than 8 bits.
622///
623/// # Examples
624///
625/// ```rust
626/// use bech32::convert_bits;
627/// let base5 = convert_bits(&[0xff], 8, 5, true);
628/// assert_eq!(base5.unwrap(), vec![0x1f, 0x1c]);
629/// ```
630pub fn convert_bits<T>(data: &[T], from: u32, to: u32, pad: bool) -> Result<Vec<u8>, Error>
631where
632    T: Into<u8> + Copy,
633{
634    if from > 8 || to > 8 || from == 0 || to == 0 {
635        panic!("convert_bits `from` and `to` parameters 0 or greater than 8");
636    }
637    let mut acc: u32 = 0;
638    let mut bits: u32 = 0;
639    let mut ret: Vec<u8> = Vec::new();
640    let maxv: u32 = (1 << to) - 1;
641    for value in data {
642        let v: u32 = u32::from(Into::<u8>::into(*value));
643        if (v >> from) != 0 {
644            // Input value exceeds `from` bit size
645            return Err(Error::InvalidData(v as u8));
646        }
647        acc = (acc << from) | v;
648        bits += from;
649        while bits >= to {
650            bits -= to;
651            ret.push(((acc >> bits) & maxv) as u8);
652        }
653    }
654    if pad {
655        if bits > 0 {
656            ret.push(((acc << (to - bits)) & maxv) as u8);
657        }
658    } else if bits >= from || ((acc << (to - bits)) & maxv) != 0 {
659        return Err(Error::InvalidPadding);
660    }
661    Ok(ret)
662}
663
664#[cfg(test)]
665mod tests {
666    use super::*;
667
668    #[test]
669    fn getters() {
670        let decoded = decode("BC1SW50QA3JX3S").unwrap();
671        let data = [16, 14, 20, 15, 0].check_base32().unwrap();
672        assert_eq!(&decoded.0, "bc");
673        assert_eq!(decoded.1, data.as_slice());
674    }
675
676    #[test]
677    fn valid_checksum() {
678        let strings: Vec<&str> = vec!(
679            "A12UEL5L",
680            "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
681            "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
682            "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
683            "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
684        );
685        for s in strings {
686            let decode_result = decode(s);
687            if !decode_result.is_ok() {
688                panic!(
689                    "Did not decode: {:?} Reason: {:?}",
690                    s,
691                    decode_result.unwrap_err()
692                );
693            }
694            assert!(decode_result.is_ok());
695            let decoded = decode_result.unwrap();
696            let encode_result = encode(&decoded.0, decoded.1).unwrap();
697            assert_eq!(s.to_lowercase(), encode_result.to_lowercase());
698        }
699    }
700
701    #[test]
702    fn invalid_strings() {
703        let pairs: Vec<(&str, Error)> = vec!(
704            (" 1nwldj5",
705                Error::InvalidChar(' ')),
706            ("abc1\u{2192}axkwrx",
707                Error::InvalidChar('\u{2192}')),
708            ("an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
709                Error::InvalidLength),
710            ("pzry9x0s0muk",
711                Error::MissingSeparator),
712            ("1pzry9x0s0muk",
713                Error::InvalidLength),
714            ("x1b4n0q5v",
715                Error::InvalidChar('b')),
716            ("ABC1DEFGOH",
717                Error::InvalidChar('O')),
718            ("li1dgmt3",
719                Error::InvalidLength),
720            ("de1lg7wt\u{ff}",
721                Error::InvalidChar('\u{ff}')),
722        );
723        for p in pairs {
724            let (s, expected_error) = p;
725            let dec_result = decode(s);
726            if dec_result.is_ok() {
727                println!("{:?}", dec_result.unwrap());
728                panic!("Should be invalid: {:?}", s);
729            }
730            assert_eq!(
731                dec_result.unwrap_err(),
732                expected_error,
733                "testing input '{}'",
734                s
735            );
736        }
737    }
738
739    #[test]
740    fn valid_conversion() {
741        // Set of [data, from_bits, to_bits, pad, result]
742        let tests: Vec<(Vec<u8>, u32, u32, bool, Vec<u8>)> = vec![
743            (vec![0x01], 1, 1, true, vec![0x01]),
744            (vec![0x01, 0x01], 1, 1, true, vec![0x01, 0x01]),
745            (vec![0x01], 8, 8, true, vec![0x01]),
746            (vec![0x01], 8, 4, true, vec![0x00, 0x01]),
747            (vec![0x01], 8, 2, true, vec![0x00, 0x00, 0x00, 0x01]),
748            (
749                vec![0x01],
750                8,
751                1,
752                true,
753                vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
754            ),
755            (vec![0xff], 8, 5, true, vec![0x1f, 0x1c]),
756            (vec![0x1f, 0x1c], 5, 8, false, vec![0xff]),
757        ];
758        for t in tests {
759            let (data, from_bits, to_bits, pad, expected_result) = t;
760            let result = convert_bits(&data, from_bits, to_bits, pad);
761            assert!(result.is_ok());
762            assert_eq!(result.unwrap(), expected_result);
763        }
764    }
765
766    #[test]
767    fn invalid_conversion() {
768        // Set of [data, from_bits, to_bits, pad, expected error]
769        let tests: Vec<(Vec<u8>, u32, u32, bool, Error)> = vec![
770            (vec![0xff], 8, 5, false, Error::InvalidPadding),
771            (vec![0x02], 1, 1, true, Error::InvalidData(0x02)),
772        ];
773        for t in tests {
774            let (data, from_bits, to_bits, pad, expected_error) = t;
775            let result = convert_bits(&data, from_bits, to_bits, pad);
776            assert!(result.is_err());
777            assert_eq!(result.unwrap_err(), expected_error);
778        }
779    }
780
781    #[test]
782    fn convert_bits_invalid_bit_size() {
783        use std::panic::{catch_unwind, set_hook, take_hook};
784
785        let invalid = &[(0, 8), (5, 0), (9, 5), (8, 10), (0, 16)];
786
787        for &(from, to) in invalid {
788            set_hook(Box::new(|_| {}));
789            let result = catch_unwind(|| {
790                let _ = convert_bits(&[0], from, to, true);
791            });
792            let _ = take_hook();
793            assert!(result.is_err());
794        }
795    }
796
797    #[test]
798    fn check_base32() {
799        assert!([0u8, 1, 2, 30, 31].check_base32().is_ok());
800        assert!([0u8, 1, 2, 30, 31, 32].check_base32().is_err());
801        assert!([0u8, 1, 2, 30, 31, 255].check_base32().is_err());
802
803        assert!([1u8, 2, 3, 4].check_base32().is_ok());
804        assert_eq!(
805            [30u8, 31, 35, 20].check_base32(),
806            Err(Error::InvalidData(35))
807        );
808    }
809
810    #[test]
811    fn test_encode() {
812        assert_eq!(
813            encode("", vec![1u8, 2, 3, 4].check_base32().unwrap()),
814            Err(Error::InvalidLength)
815        );
816    }
817
818    #[test]
819    fn from_base32() {
820        use FromBase32;
821        assert_eq!(
822            Vec::from_base32(&[0x1f, 0x1c].check_base32().unwrap()),
823            Ok(vec![0xff])
824        );
825        assert_eq!(
826            Vec::from_base32(&[0x1f, 0x1f].check_base32().unwrap()),
827            Err(Error::InvalidPadding)
828        );
829    }
830
831    #[test]
832    fn to_base32() {
833        use ToBase32;
834        assert_eq!([0xffu8].to_base32(), [0x1f, 0x1c].check_base32().unwrap());
835    }
836
837    #[test]
838    fn reverse_charset() {
839        use CHARSET_REV;
840
841        fn get_char_value(c: char) -> i8 {
842            let charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
843            match charset.find(c.to_ascii_lowercase()) {
844                Some(x) => x as i8,
845                None => -1,
846            }
847        }
848
849        let expected_rev_charset = (0u8..128)
850            .map(|i| get_char_value(i as char))
851            .collect::<Vec<_>>();
852
853        assert_eq!(&(CHARSET_REV[..]), expected_rev_charset.as_slice());
854    }
855
856    #[test]
857    fn writer() {
858        let hrp = "lnbc";
859        let data = "Hello World!".as_bytes().to_base32();
860
861        let mut written_str = String::new();
862        {
863            let mut writer = Bech32Writer::new(hrp, &mut written_str).unwrap();
864            writer.write(&data).unwrap();
865            writer.finalize().unwrap();
866        }
867
868        let encoded_str = encode(hrp, data).unwrap();
869
870        assert_eq!(encoded_str, written_str);
871    }
872
873    #[test]
874    fn write_on_drop() {
875        let hrp = "lntb";
876        let data = "Hello World!".as_bytes().to_base32();
877
878        let mut written_str = String::new();
879        {
880            let mut writer = Bech32Writer::new(hrp, &mut written_str).unwrap();
881            writer.write(&data).unwrap();
882        }
883
884        let encoded_str = encode(hrp, data).unwrap();
885
886        assert_eq!(encoded_str, written_str);
887    }
888
889    #[test]
890    fn test_hrp_case() {
891        // Tests for issue with HRP case checking being ignored for encoding
892        use ToBase32;
893        let encoded_str = encode("HRP", [0x00, 0x00].to_base32()).unwrap();
894
895        assert_eq!(encoded_str, "hrp1qqqq40atq3");
896    }
897}