taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

base32.rs (8769B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024, 2025, 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::{borrow::Cow, fmt::Display, ops::Deref, str::FromStr};
     18 
     19 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
     20 
     21 pub const CROCKFORD_ALPHABET: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
     22 
     23 /** Encoded bytes len of Crockford's base32 */
     24 #[inline]
     25 const fn encoded_len(len: usize) -> usize {
     26     (len * 8).div_ceil(5)
     27 }
     28 
     29 /** Buffer bytes len of Crockford's base32 using a batch of 8 chars */
     30 #[inline]
     31 const fn encoded_buf_len(len: usize) -> usize {
     32     (len / 5 + 1) * 8
     33 }
     34 
     35 /** Encode bytes using Crockford's base32 */
     36 pub fn encode_static<'a, const N: usize>(bytes: &[u8; N], out: &'a mut [u8]) -> &'a str {
     37     // Batch encoded
     38     encode_batch(bytes, out);
     39 
     40     // Truncate incomplete ending chunk
     41     let truncated = &out[..encoded_len(bytes.len())];
     42 
     43     // SAFETY: only contains valid ASCII characters from CROCKFORD_ALPHABET
     44     unsafe { std::str::from_utf8_unchecked(truncated) }
     45 }
     46 
     47 /** Encode bytes using Crockford's base32 */
     48 pub fn encode(bytes: &[u8]) -> String {
     49     let mut buf = vec![0u8; encoded_buf_len(bytes.len())];
     50     // Batch encoded
     51     encode_batch(bytes, &mut buf);
     52 
     53     // Truncate incomplete ending chunk
     54     buf.truncate(encoded_len(bytes.len()));
     55 
     56     // SAFETY: only contains valid ASCII characters from CROCKFORD_ALPHABET
     57     unsafe { std::string::String::from_utf8_unchecked(buf) }
     58 }
     59 
     60 /** Batch encode bytes using Crockford's base32 */
     61 #[inline]
     62 fn encode_batch(bytes: &[u8], encoded: &mut [u8]) {
     63     // Check buffer len
     64     assert!(encoded.len() >= encoded_buf_len(bytes.len()));
     65 
     66     // Encode chunks of 5B for 8 chars
     67     for (chunk, encoded) in bytes.chunks(5).zip(encoded.chunks_exact_mut(8)) {
     68         let mut buf = [0u8; 5];
     69         for (i, &b) in chunk.iter().enumerate() {
     70             buf[i] = b;
     71         }
     72         encoded[0] = CROCKFORD_ALPHABET[((buf[0] & 0xF8) >> 3) as usize];
     73         encoded[1] = CROCKFORD_ALPHABET[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xC0) >> 6)) as usize];
     74         encoded[2] = CROCKFORD_ALPHABET[((buf[1] & 0x3E) >> 1) as usize];
     75         encoded[3] = CROCKFORD_ALPHABET[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xF0) >> 4)) as usize];
     76         encoded[4] = CROCKFORD_ALPHABET[(((buf[2] & 0x0F) << 1) | (buf[3] >> 7)) as usize];
     77         encoded[5] = CROCKFORD_ALPHABET[((buf[3] & 0x7C) >> 2) as usize];
     78         encoded[6] = CROCKFORD_ALPHABET[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xE0) >> 5)) as usize];
     79         encoded[7] = CROCKFORD_ALPHABET[(buf[4] & 0x1F) as usize];
     80     }
     81 }
     82 
     83 #[derive(Debug, thiserror::Error)]
     84 pub enum Base32Error<const N: usize> {
     85     #[error("invalid Crockford's base32 format")]
     86     Format,
     87     #[error("invalid length expected {N} bytes got {0}")]
     88     Length(usize),
     89 }
     90 
     91 /** Crockford's base32 inverse table, case insentitive and with substitution */
     92 const CROCKFORD_INV: [u8; 256] = {
     93     let mut table = [255; 256];
     94 
     95     // Fill the canonical alphabet
     96     let mut i = 0;
     97     while i < CROCKFORD_ALPHABET.len() {
     98         let b = CROCKFORD_ALPHABET[i];
     99         table[b as usize] = i as u8;
    100         i += 1;
    101     }
    102 
    103     // Add substitution
    104     table[b'O' as usize] = table[b'0' as usize];
    105     table[b'I' as usize] = table[b'1' as usize];
    106     table[b'L' as usize] = table[b'1' as usize];
    107     table[b'U' as usize] = table[b'V' as usize];
    108 
    109     // Make the table case insensitive
    110     let mut i = 0;
    111     while i < CROCKFORD_ALPHABET.len() {
    112         let b = CROCKFORD_ALPHABET[i];
    113         table[b.to_ascii_lowercase() as usize] = table[b as usize];
    114         i += 1;
    115     }
    116 
    117     table
    118 };
    119 
    120 /** Decoded bytes len of Crockford's base32 */
    121 #[inline]
    122 const fn decoded_len(len: usize) -> usize {
    123     len * 5 / 8
    124 }
    125 
    126 /** Buffer bytes len of Crockford's base32 using a batch of 5 bytes */
    127 #[inline]
    128 const fn decoded_buf_len(len: usize) -> usize {
    129     (len / 8 + 1) * 5
    130 }
    131 
    132 /** Decode N bytes from a Crockford's base32 string */
    133 pub fn decode_static<const N: usize>(encoded: &[u8]) -> Result<[u8; N], Base32Error<N>> {
    134     // Check decode length
    135     let output_length = decoded_len(encoded.len());
    136     if output_length != N {
    137         return Err(Base32Error::Length(output_length));
    138     }
    139 
    140     let mut decoded = vec![0u8; decoded_buf_len(encoded.len())]; // TODO use a stack allocated buffer when supported
    141 
    142     if !decode_batch(encoded, &mut decoded) {
    143         return Err(Base32Error::Format);
    144     }
    145     Ok(decoded[..N].try_into().unwrap())
    146 }
    147 
    148 /** Decode bytes from a Crockford's base32 string */
    149 pub fn decode(encoded: &[u8]) -> Result<Vec<u8>, Base32Error<0>> {
    150     let mut decoded = vec![0u8; decoded_buf_len(encoded.len())];
    151 
    152     if !decode_batch(encoded, &mut decoded) {
    153         return Err(Base32Error::Format);
    154     }
    155     Ok(decoded)
    156 }
    157 
    158 /** Batch decode bytes using Crockford's base32 */
    159 #[inline]
    160 fn decode_batch(encoded: &[u8], decoded: &mut [u8]) -> bool {
    161     let mut invalid = false;
    162 
    163     // Encode chunks of 8 chars for 5B
    164     for (chunk, decoded) in encoded.chunks(8).zip(decoded.chunks_exact_mut(5)) {
    165         let mut buf = [0; 8];
    166 
    167         // Lookup chunk
    168         for (i, &b) in chunk.iter().enumerate() {
    169             buf[i] = CROCKFORD_INV[b as usize];
    170         }
    171 
    172         // Check chunk validity
    173         invalid |= buf.contains(&255);
    174 
    175         // Decode chunk
    176         decoded[0] = (buf[0] << 3) | (buf[1] >> 2);
    177         decoded[1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4);
    178         decoded[2] = (buf[3] << 4) | (buf[4] >> 1);
    179         decoded[3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3);
    180         decoded[4] = (buf[6] << 5) | buf[7];
    181     }
    182 
    183     !invalid
    184 }
    185 
    186 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
    187 pub struct Base32<const L: usize>([u8; L]);
    188 
    189 impl<const L: usize> Base32<L> {
    190     pub fn rand() -> Self {
    191         Self(rand::random())
    192     }
    193 }
    194 
    195 impl<const L: usize> From<[u8; L]> for Base32<L> {
    196     fn from(array: [u8; L]) -> Self {
    197         Self(array)
    198     }
    199 }
    200 
    201 impl<const L: usize> Deref for Base32<L> {
    202     type Target = [u8; L];
    203 
    204     fn deref(&self) -> &Self::Target {
    205         &self.0
    206     }
    207 }
    208 
    209 impl<const L: usize> FromStr for Base32<L> {
    210     type Err = Base32Error<L>;
    211 
    212     fn from_str(s: &str) -> Result<Self, Self::Err> {
    213         Ok(Self(decode_static(s.as_bytes())?))
    214     }
    215 }
    216 
    217 impl<const L: usize> Display for Base32<L> {
    218     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    219         let mut buff = vec![0u8; encoded_buf_len(L)]; // TODO use a stack allocated buffer when supported
    220         f.write_str(encode_static(&self.0, &mut buff))
    221     }
    222 }
    223 
    224 impl<const L: usize> std::fmt::Debug for Base32<L> {
    225     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    226         Display::fmt(&self, f)
    227     }
    228 }
    229 
    230 impl<const L: usize> Serialize for Base32<L> {
    231     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    232     where
    233         S: Serializer,
    234     {
    235         serializer.serialize_str(&self.to_string())
    236     }
    237 }
    238 
    239 impl<'de, const L: usize> Deserialize<'de> for Base32<L> {
    240     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    241     where
    242         D: Deserializer<'de>,
    243     {
    244         let raw = Cow::<str>::deserialize(deserializer)?;
    245         Self::from_str(&raw).map_err(D::Error::custom)
    246     }
    247 }
    248 
    249 impl<'a, const L: usize> TryFrom<&'a [u8]> for Base32<L> {
    250     type Error = Base32Error<L>;
    251 
    252     fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
    253         Ok(Self(
    254             value
    255                 .try_into()
    256                 .map_err(|_| Base32Error::Length(value.len()))?,
    257         ))
    258     }
    259 }
    260 
    261 impl<const L: usize> sqlx::Type<sqlx::Postgres> for Base32<L> {
    262     fn type_info() -> sqlx::postgres::PgTypeInfo {
    263         <&[u8]>::type_info()
    264     }
    265 }
    266 
    267 impl<'q, const L: usize> sqlx::Encode<'q, sqlx::Postgres> for Base32<L> {
    268     fn encode_by_ref(
    269         &self,
    270         buf: &mut sqlx::postgres::PgArgumentBuffer,
    271     ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
    272         self.0.encode_by_ref(buf)
    273     }
    274 }
    275 
    276 impl<'r, const L: usize> sqlx::Decode<'r, sqlx::Postgres> for Base32<L> {
    277     fn decode(value: sqlx::postgres::PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
    278         let array = <[u8; L]>::decode(value)?;
    279         Ok(Self(array))
    280     }
    281 }