base32.rs (7041B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-2025 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::{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 /** Batch encode bytes using Crockford's base32 */ 48 #[inline] 49 fn encode_batch(bytes: &[u8], encoded: &mut [u8]) { 50 // Check buffer len 51 assert!(encoded.len() >= encoded_buf_len(bytes.len())); 52 53 // Encode chunks of 5B for 8 chars 54 for (chunk, encoded) in bytes.chunks(5).zip(encoded.chunks_exact_mut(8)) { 55 let mut buf = [0u8; 5]; 56 for (i, &b) in chunk.iter().enumerate() { 57 buf[i] = b; 58 } 59 encoded[0] = CROCKFORD_ALPHABET[((buf[0] & 0xF8) >> 3) as usize]; 60 encoded[1] = CROCKFORD_ALPHABET[(((buf[0] & 0x07) << 2) | ((buf[1] & 0xC0) >> 6)) as usize]; 61 encoded[2] = CROCKFORD_ALPHABET[((buf[1] & 0x3E) >> 1) as usize]; 62 encoded[3] = CROCKFORD_ALPHABET[(((buf[1] & 0x01) << 4) | ((buf[2] & 0xF0) >> 4)) as usize]; 63 encoded[4] = CROCKFORD_ALPHABET[(((buf[2] & 0x0F) << 1) | (buf[3] >> 7)) as usize]; 64 encoded[5] = CROCKFORD_ALPHABET[((buf[3] & 0x7C) >> 2) as usize]; 65 encoded[6] = CROCKFORD_ALPHABET[(((buf[3] & 0x03) << 3) | ((buf[4] & 0xE0) >> 5)) as usize]; 66 encoded[7] = CROCKFORD_ALPHABET[(buf[4] & 0x1F) as usize]; 67 } 68 } 69 70 #[derive(Debug, thiserror::Error)] 71 pub enum Base32Error<const N: usize> { 72 #[error("invalid Crockford's base32 format")] 73 Format, 74 #[error("invalid length expected {N} bytes got {0}")] 75 Length(usize), 76 } 77 78 /** Crockford's base32 inverse table, case insentitive and with substitution */ 79 const CROCKFORD_INV: [u8; 256] = { 80 let mut table = [255; 256]; 81 82 // Fill the canonical alphabet 83 let mut i = 0; 84 while i < CROCKFORD_ALPHABET.len() { 85 let b = CROCKFORD_ALPHABET[i]; 86 table[b as usize] = i as u8; 87 i += 1; 88 } 89 90 // Add substitution 91 table[b'O' as usize] = table[b'0' as usize]; 92 table[b'I' as usize] = table[b'1' as usize]; 93 table[b'L' as usize] = table[b'1' as usize]; 94 table[b'U' as usize] = table[b'V' as usize]; 95 96 // Make the table case insensitive 97 let mut i = 0; 98 while i < CROCKFORD_ALPHABET.len() { 99 let b = CROCKFORD_ALPHABET[i]; 100 table[b.to_ascii_lowercase() as usize] = table[b as usize]; 101 i += 1; 102 } 103 104 table 105 }; 106 107 /** Decoded bytes len of Crockford's base32 */ 108 #[inline] 109 const fn decoded_len(len: usize) -> usize { 110 len * 5 / 8 111 } 112 113 /** Buffer bytes len of Crockford's base32 using a batch of 5 bytes */ 114 #[inline] 115 const fn decoded_buf_len(len: usize) -> usize { 116 (len / 8 + 1) * 5 117 } 118 119 /** Decode N bytes from a Crockford's base32 string */ 120 pub fn decode<const N: usize>(encoded: &[u8]) -> Result<[u8; N], Base32Error<N>> { 121 // Check decode length 122 let output_length = decoded_len(encoded.len()); 123 if output_length != N { 124 return Err(Base32Error::Length(output_length)); 125 } 126 127 let mut decoded = vec![0u8; decoded_buf_len(encoded.len())]; // TODO use a stack allocated buffer when supported 128 let mut invalid = false; 129 130 // Encode chunks of 8 chars for 5B 131 for (chunk, decoded) in encoded.chunks(8).zip(decoded.chunks_exact_mut(5)) { 132 let mut buf = [0; 8]; 133 134 // Lookup chunk 135 for (i, &b) in chunk.iter().enumerate() { 136 buf[i] = CROCKFORD_INV[b as usize]; 137 } 138 139 // Check chunk validity 140 invalid |= buf.contains(&255); 141 142 // Decode chunk 143 decoded[0] = (buf[0] << 3) | (buf[1] >> 2); 144 decoded[1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4); 145 decoded[2] = (buf[3] << 4) | (buf[4] >> 1); 146 decoded[3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3); 147 decoded[4] = (buf[6] << 5) | buf[7]; 148 } 149 150 if invalid { 151 return Err(Base32Error::Format); 152 } 153 Ok(decoded[..N].try_into().unwrap()) 154 } 155 156 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 157 pub struct Base32<const L: usize>([u8; L]); 158 159 impl<const L: usize> Base32<L> { 160 pub fn rand() -> Self { 161 let mut bytes = [0; L]; 162 fastrand::fill(&mut bytes); 163 Self(bytes) 164 } 165 } 166 167 impl<const L: usize> From<[u8; L]> for Base32<L> { 168 fn from(array: [u8; L]) -> Self { 169 Self(array) 170 } 171 } 172 173 impl<const L: usize> Deref for Base32<L> { 174 type Target = [u8; L]; 175 176 fn deref(&self) -> &Self::Target { 177 &self.0 178 } 179 } 180 181 impl<const L: usize> FromStr for Base32<L> { 182 type Err = Base32Error<L>; 183 184 fn from_str(s: &str) -> Result<Self, Self::Err> { 185 Ok(Self(decode(s.as_bytes())?)) 186 } 187 } 188 189 impl<const L: usize> Display for Base32<L> { 190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 191 let mut buff = vec![0u8; encoded_buf_len(L)]; // TODO use a stack allocated buffer when supported 192 f.write_str(encode_static(&self.0, &mut buff)) 193 } 194 } 195 196 impl<const L: usize> Serialize for Base32<L> { 197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 198 where 199 S: Serializer, 200 { 201 serializer.serialize_str(&self.to_string()) 202 } 203 } 204 205 impl<'de, const L: usize> Deserialize<'de> for Base32<L> { 206 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 207 where 208 D: Deserializer<'de>, 209 { 210 Base32::from_str(&String::deserialize(deserializer)?).map_err(D::Error::custom) 211 } 212 } 213 214 impl<'a, const L: usize> TryFrom<&'a [u8]> for Base32<L> { 215 type Error = Base32Error<L>; 216 217 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { 218 Ok(Self( 219 value 220 .try_into() 221 .map_err(|_| Base32Error::Length(value.len()))?, 222 )) 223 } 224 }