1use crate::error::Error;
5use crate::error::Result;
6
7#[allow(missing_docs)]
14#[derive(Debug, Clone, Copy)]
15pub enum Base {
16 Base2,
19 Base8,
21 Base10,
23 Base16Lower,
25 Base16Upper,
27 Base32Lower,
29 Base32Upper,
31 Base32PadLower,
33 Base32PadUpper,
35 Base32HexLower,
37 Base32HexUpper,
39 Base32HexPadLower,
41 Base32HexPadUpper,
43 Base32Z,
45 Base36Lower,
47 Base36Upper,
49 Base58Flickr,
51 Base58Btc,
53 Base64,
55 Base64Pad,
57 Base64Url,
59 Base64UrlPad,
61}
62
63impl From<Base> for multibase::Base {
65 fn from(base: Base) -> Self {
66 match base {
67 Base::Base2 => multibase::Base::Base2,
68 Base::Base8 => multibase::Base::Base8,
69 Base::Base10 => multibase::Base::Base10,
70 Base::Base16Lower => multibase::Base::Base16Lower,
71 Base::Base16Upper => multibase::Base::Base16Upper,
72 Base::Base32Lower => multibase::Base::Base32Lower,
73 Base::Base32Upper => multibase::Base::Base32Upper,
74 Base::Base32PadLower => multibase::Base::Base32PadLower,
75 Base::Base32PadUpper => multibase::Base::Base32PadUpper,
76 Base::Base32HexLower => multibase::Base::Base32HexLower,
77 Base::Base32HexUpper => multibase::Base::Base32HexUpper,
78 Base::Base32HexPadLower => multibase::Base::Base32HexPadLower,
79 Base::Base32HexPadUpper => multibase::Base::Base32HexPadUpper,
80 Base::Base32Z => multibase::Base::Base32Z,
81 Base::Base36Lower => multibase::Base::Base36Lower,
82 Base::Base36Upper => multibase::Base::Base36Upper,
83 Base::Base58Flickr => multibase::Base::Base58Flickr,
84 Base::Base58Btc => multibase::Base::Base58Btc,
85 Base::Base64 => multibase::Base::Base64,
86 Base::Base64Pad => multibase::Base::Base64Pad,
87 Base::Base64Url => multibase::Base::Base64Url,
88 Base::Base64UrlPad => multibase::Base::Base64UrlPad,
89 }
90 }
91}
92
93pub struct BaseEncoding;
95
96impl BaseEncoding {
97 pub fn encode<T>(data: &T, base: Base) -> String
99 where
100 T: AsRef<[u8]> + ?Sized,
101 {
102 multibase::Base::from(base).encode(data)
103 }
104
105 pub fn decode<T>(data: &T, base: Base) -> Result<Vec<u8>>
107 where
108 T: AsRef<str> + ?Sized,
109 {
110 multibase::Base::from(base)
111 .decode(data)
112 .map_err(|err| Error::DecodeBase(base, err))
113 }
114
115 pub fn encode_base58<T>(data: &T) -> String
119 where
120 T: AsRef<[u8]> + ?Sized,
121 {
122 Self::encode(data, Base::Base58Btc)
123 }
124
125 pub fn decode_base58<T>(data: &T) -> Result<Vec<u8>>
129 where
130 T: AsRef<str> + ?Sized,
131 {
132 Self::decode(data, Base::Base58Btc)
133 }
134
135 pub fn encode_multibase<T>(data: &T, base: Option<Base>) -> String
143 where
144 T: AsRef<[u8]> + ?Sized,
145 {
146 multibase::encode(multibase::Base::from(base.unwrap_or(Base::Base58Btc)), data)
147 }
148
149 pub fn decode_multibase<T>(data: &T) -> Result<Vec<u8>>
154 where
155 T: AsRef<str> + ?Sized,
156 {
157 if data.as_ref().is_empty() {
158 return Ok(Vec::new());
159 }
160 multibase::decode(data)
161 .map(|(_base, output)| output)
162 .map_err(Error::DecodeMultibase)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use quickcheck_macros::quickcheck;
169
170 use super::*;
171
172 #[test]
173 fn test_decode_base58_empty() {
174 assert_eq!(BaseEncoding::decode_base58("").unwrap(), Vec::<u8>::new());
175 }
176
177 #[quickcheck]
178 fn test_base58_random(data: Vec<u8>) {
179 assert_eq!(
180 BaseEncoding::decode_base58(&BaseEncoding::encode_base58(&data)).unwrap(),
181 data
182 );
183 }
184
185 #[quickcheck]
186 fn test_base64_random(data: Vec<u8>) {
187 assert_eq!(
188 BaseEncoding::decode(&BaseEncoding::encode(&data, Base::Base64Url), Base::Base64Url).unwrap(),
189 data
190 );
191 }
192
193 #[test]
196 fn test_b58() {
197 let test_vectors: [(&[u8], &str); 3] = [
198 (b"Hello World!", "2NEpo7TZRRrLZSi2U"),
199 (
200 b"The quick brown fox jumps over the lazy dog.",
201 "USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z",
202 ),
203 (&[0, 0, 0, 40, 127, 180, 205], "111233QC4"),
204 ];
205 for (test_decoded, test_encoded) in test_vectors {
206 let encoded: String = BaseEncoding::encode_base58(test_decoded);
207 assert_eq!(encoded, test_encoded, "encode failed on {test_decoded:?}");
208
209 let decoded: Vec<u8> = BaseEncoding::decode_base58(test_encoded).unwrap();
210 assert_eq!(decoded, test_decoded, "decode failed on {test_encoded}");
211 }
212 }
213
214 #[test]
215 fn test_decode_multibase_empty() {
216 assert_eq!(BaseEncoding::decode_multibase("").unwrap(), Vec::<u8>::new());
217 }
218
219 #[quickcheck]
220 fn test_multibase_random(data: Vec<u8>) {
221 assert_eq!(
222 BaseEncoding::decode_multibase(&BaseEncoding::encode_multibase(&data, None)).unwrap(),
223 data
224 );
225 }
226
227 #[quickcheck]
228 fn test_multibase_bases_random(data: Vec<u8>) {
229 let bases = [
230 Base::Base2,
231 Base::Base8,
232 Base::Base10,
233 Base::Base16Lower,
234 Base::Base16Upper,
235 Base::Base32Lower,
236 Base::Base32Upper,
237 Base::Base32PadLower,
238 Base::Base32PadUpper,
239 Base::Base32HexLower,
240 Base::Base32HexUpper,
241 Base::Base32HexPadLower,
242 Base::Base32HexPadUpper,
243 Base::Base32Z,
244 Base::Base36Lower,
245 Base::Base36Upper,
246 Base::Base58Flickr,
247 Base::Base58Btc,
248 Base::Base64,
249 Base::Base64Pad,
250 Base::Base64Url,
251 Base::Base64UrlPad,
252 ];
253 for base in bases {
254 assert_eq!(
255 BaseEncoding::decode_multibase(&BaseEncoding::encode_multibase(&data, Some(base))).unwrap(),
256 data
257 );
258 }
259 }
260
261 #[test]
264 fn test_multibase() {
265 let data: &str = r"Multibase is awesome! \o/";
267 for (base, expected) in [
268 (Base::Base16Upper, "F4D756C74696261736520697320617765736F6D6521205C6F2F"),
269 (Base::Base32Upper, "BJV2WY5DJMJQXGZJANFZSAYLXMVZW63LFEEQFY3ZP"),
270 (Base::Base58Btc, "zYAjKoNbau5KiqmHPmSxYCvn66dA1vLmwbt"),
271 (Base::Base64Pad, "MTXVsdGliYXNlIGlzIGF3ZXNvbWUhIFxvLw=="),
272 ] {
273 let encoded: String = BaseEncoding::encode_multibase(data, Some(base));
274 assert_eq!(encoded, expected);
275 }
276
277 let expected: Vec<u8> = data.as_bytes().to_vec();
279 for encoded in [
280 "F4D756C74696261736520697320617765736F6D6521205C6F2F",
281 "BJV2WY5DJMJQXGZJANFZSAYLXMVZW63LFEEQFY3ZP",
282 "zYAjKoNbau5KiqmHPmSxYCvn66dA1vLmwbt",
283 "MTXVsdGliYXNlIGlzIGF3ZXNvbWUhIFxvLw==",
284 ] {
285 let decoded: Vec<u8> = BaseEncoding::decode_multibase(encoded).unwrap();
286 assert_eq!(decoded, expected, "failed on {encoded}");
287 }
288 }
289}