1 /*
2   Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2.0,
6   as published by the Free Software Foundation.
7 
8   This program is also distributed with certain software (including
9   but not limited to OpenSSL) that is licensed under separate terms,
10   as designated in a particular file or component or in included license
11   documentation.  The authors of MySQL hereby grant you an additional
12   permission to link the program and your derivative works with the
13   separately licensed software that they have included with MySQL.
14 
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19 
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23 */
24 
25 #ifndef MYSQLROUTER_HTTP_BASE64_INCLUDED
26 #define MYSQLROUTER_HTTP_BASE64_INCLUDED
27 
28 #include "mysqlrouter/http_common_export.h"
29 
30 #include <algorithm>  // min
31 #include <array>
32 #include <stdexcept>
33 #include <string>
34 #include <utility>  // index_sequence
35 #include <vector>
36 
37 enum class Base64Endianess { LITTLE, BIG };
38 
39 /**
40  * Generic Base64 codec.
41  *
42  * Base64 comes in many flavours:
43  *
44  * - RFC4648 used by HTTP
45  * - crypt
46  * - bcrypt
47  * - pbkdf2 in MCF
48  * - UUencode
49  *
50  * they differ by
51  *
52  * - alphabet
53  * - endianess
54  * - padding
55  *
56  * Base64Impl provides generic encode and decode methods which are parametrized
57  * by Endianess, Padding.
58  *
59  * Parametrization with templates allows to provide:
60  *
61  * - one implementation for all combinations
62  * - without extra runtime overhead as dead code is removed by the compiler
63  *
64  * Endianess
65  * =========
66  *
67  * Little Endian
68  * -------------
69  *
70  * using Alphabet=Crypt
71  *
72  *     octet(hex):        55
73  *     uint32:      ........ ........  01010101 (LSB)
74  *     uint32:      ...... ...... ....01 010101 (LSB)
75  *     sextet(hex):                    1     15
76  *     Alphabet:                       /      J
77  *
78  *     Out: J/
79  *
80  *
81  * Big Endian
82  * ----------
83  *
84  * using Alphabet=Crypt
85  *
86  *     octet(hex):        55
87  *     uint32:      01010101 ........  ........ (LSB)
88  *     uint32:      010101 01.... ...... ...... (LSB)
89  *     sextet(hex):     15     10
90  *     Alphabet:         J      E
91  *
92  *     Out: JE
93  *
94  * Padding
95  * =======
96  *
97  * If padding is defined mandatory,
98  *
99  * - at encode() each group of 4 sextets is filled by with the padding
100  *   character.
101  * - at decode() input must have padding.
102  *
103  * If padding is not mandatory,
104  *
105  * - at encode() no padding is added.
106  * - at decode() padding is accepted, but not required.
107  */
108 
109 /*
110  * TODO: look into
111  *
112  * - http://0x80.pl/notesen/2015-12-27-base64-encoding.html
113  * - http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html
114  * - http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html
115  * - http://alfredklomp.com/programming/sse-base64/
116  * - https://github.com/lemire/fastbase64
117  */
118 class Base64Impl {
119  public:
120   /**
121    * type of all alphabet.
122    */
123   using alphabet_type = std::array<char, 64>;
124 
125   /**
126    * type of all inverse mappings of alphabets.
127    *
128    * - -1 invalid
129    * - 0-63 position into alphabet
130    */
131   using inverse_alphabet_type = std::array<int8_t, 256>;
132 
133   template <Base64Endianess endianess, bool PaddingMandatory, char PaddingChar>
decode(const std::string & encoded,const inverse_alphabet_type & inverse_alphabet)134   static std::vector<uint8_t> decode(
135       const std::string &encoded,
136       const inverse_alphabet_type &inverse_alphabet) {
137     std::vector<uint8_t> out((encoded.size() + 3) / 4 * 3);
138 
139     constexpr unsigned int shift_pos_0 =
140         endianess == Base64Endianess::BIG ? 16 : 0;
141     constexpr unsigned int shift_pos_1 =
142         endianess == Base64Endianess::BIG ? 8 : 8;
143     constexpr unsigned int shift_pos_2 =
144         endianess == Base64Endianess::BIG ? 0 : 16;
145 
146     auto out_it = out.begin();
147     auto data_it = encoded.cbegin();
148     const auto data_end_it = encoded.cend();
149     while (const size_t data_left = std::distance(data_it, data_end_it)) {
150       if (data_left < 2) {
151         throw std::runtime_error("invalid sequence");
152       }
153 
154       if (PaddingMandatory && (data_left < 4)) {
155         throw std::runtime_error("missing padding");
156       }
157 
158       uint32_t v = 0;
159       bool is_padding = false;
160       const size_t max_rounds = std::min(size_t{4}, data_left);
161       uint32_t sextets = 0;
162       for (size_t cnt = 0; cnt < max_rounds; ++cnt) {
163         const uint8_t b64 = *(data_it++);
164         if (is_padding && b64 != PaddingChar) {
165           throw std::runtime_error("invalid char, expected padding");
166         }
167         const int8_t c = (inverse_alphabet[b64]);
168 
169         if (c == -1) {
170           if (data_left <= 4 && cnt >= 2 && b64 == PaddingChar) {
171             // padding is ok at the end
172             is_padding = true;
173           } else {
174             throw std::runtime_error(std::string("invalid char"));
175           }
176         }
177 
178         // add new 6 bits
179         if (!is_padding) {
180           if (endianess == Base64Endianess::BIG) {
181             v |= c << (6 * (3 - cnt));
182           } else {
183             v |= c << (6 * cnt);
184           }
185           sextets++;
186         }
187       }
188 
189       // 3 * 6bit b64 = 18bits translates to 16bit (2 bits extra)
190       // 2 * 6bit b64 = 12bits translates to 8bit (4 bits extra)
191       //
192       // They must be 0b0 to ensure only one b64 value
193       // maps to one 8bit version and the other way around.
194       //
195       // Example
196       // ------
197       //
198       // WWU= -> Ye -> WWU=
199       //
200       //                   0x14
201       //     ...... ...... 010100
202       //     ........ ........ xx
203       //
204       // WWW= -> Ye -> WWU=
205       //
206       //                   0x16
207       //     ...... ...... 010110
208       //     ........ ........ xx
209       //
210       switch (sextets) {
211         case 2:
212           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_0);
213 
214           if (0 != static_cast<uint8_t>(v >> shift_pos_1)) {
215             throw std::runtime_error("unused bits");
216           }
217           break;
218         case 3:
219           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_0);
220           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_1);
221 
222           if (0 != static_cast<uint8_t>(v >> shift_pos_2)) {
223             throw std::runtime_error("unused bits");
224           }
225           break;
226         case 4:
227           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_0);
228           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_1);
229           *(out_it++) = static_cast<uint8_t>(v >> shift_pos_2);
230           break;
231       }
232     }
233 
234     out.resize(std::distance(out.begin(), out_it));
235 
236     return out;
237   }
238 
239   template <Base64Endianess endianess, bool PaddingMandatory, char PaddingChar>
encode(const std::vector<uint8_t> & data,const alphabet_type & alphabet)240   static std::string encode(const std::vector<uint8_t> &data,
241                             const alphabet_type &alphabet) {
242     std::string out;
243     // ensure we have enough space
244     out.resize((data.size() + 2) / 3 * 4);
245 
246     constexpr unsigned int shift_pos_0 =
247         endianess == Base64Endianess::BIG ? 16 : 0;
248     constexpr unsigned int shift_pos_1 =
249         endianess == Base64Endianess::BIG ? 8 : 8;
250     constexpr unsigned int shift_pos_2 =
251         endianess == Base64Endianess::BIG ? 0 : 16;
252 
253     auto out_it = out.begin();
254     auto data_it = data.begin();
255     const auto data_end_it = data.end();
256     while (const size_t data_left = std::distance(data_it, data_end_it)) {
257       // consume 3 bytes, if we have them
258       uint32_t v = 0;
259 
260       size_t padding_pos = 4;  // no padding
261       switch (data_left) {
262         case 1:
263           v |= (*(data_it++)) << shift_pos_0;
264           padding_pos = 2;  // out-byte 2 and 3 are padding
265           break;
266         case 2:
267           v |= (*(data_it++)) << shift_pos_0;
268           v |= (*(data_it++)) << shift_pos_1;
269           padding_pos = 3;  // out-byte 3 is padding
270           break;
271         default:
272           v |= (*(data_it++)) << shift_pos_0;
273           v |= (*(data_it++)) << shift_pos_1;
274           v |= (*(data_it++)) << shift_pos_2;
275           break;
276       }
277 
278       // and base64-encode them
279       size_t cnt;
280       for (cnt = 0; cnt < 4; ++cnt) {
281         if (cnt >= padding_pos) {
282           break;
283         }
284 
285         size_t pos;
286         if (endianess == Base64Endianess::BIG) {
287           // take the upper 6 bit and shift left each round
288           pos = (v & (0x3f << (3 * 6))) >> 3 * 6;
289           v <<= 6;
290         } else {
291           // take the lower 6 bit and shift right each round
292           pos = v & 0x3f;
293           v >>= 6;
294         }
295         *(out_it++) = alphabet.at(pos);
296       }
297 
298       if (PaddingMandatory) {
299         // apply padding if needed
300         for (; cnt < 4; ++cnt) {
301           *(out_it++) = PaddingChar;
302         }
303       }
304     }
305     out.resize(std::distance(out.begin(), out_it));
306 
307     return out;
308   }
309 };
310 
311 namespace Base64Alphabet {
312 
313 /**
314  * type of all alphabet.
315  */
316 using alphabet_type = Base64Impl::alphabet_type;
317 
318 /**
319  * type of all inverse mappings of alphabets.
320  *
321  * - -1 invalid
322  * - 0-63 position into alphabet
323  */
324 using inverse_alphabet_type = Base64Impl::inverse_alphabet_type;
325 
326 namespace detail {
327 /**
328  * find position of char in alphabet.
329  *
330  * @returns position in alphabet
331  * @retval -1 if not found
332  */
333 constexpr int8_t find_pos_of_char(const alphabet_type &v, uint8_t character,
334                                   size_t v_ndx = 0) {
335   // ensure that -1 and 255 don't overlap
336   static_assert(alphabet_type().size() <= 128,
337                 "alphabet MUST less <= 128 chars");
338   return (v_ndx >= v.size() ? -1
339                             : (v[v_ndx] == static_cast<char>(character))
340                                   ? static_cast<uint8_t>(v_ndx)
341                                   : find_pos_of_char(v, character, v_ndx + 1));
342 }
343 
344 // hand-roll std::make_index_sequence<256> as SunCC 12.6 runs into:
345 //
346 //     Error: Templates nested too deeply (recursively?).
347 //
348 // for std::make_index_sequence<n> with n > 255
349 //
350 // see: https://community.oracle.com/thread/4070120
351 //
352 // There exists a workaround:
353 //
354 // > The compiler has an undocumented "Q option" (or "Wizard option") that
355 // > lets you increase the allowed depth:
356 // >
357 // >     -Qoption ccfe -tmpldepth=N
358 // >     -W0,-tmpldepth=N
359 //
360 // but here we go the hand-rolled version of what std::make_index_sequence<256>
361 // would have generated instead.
362 
363 using ndx_256 = std::index_sequence<
364     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
365     21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
366     40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
367     59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
368     78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
369     97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
370     113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
371     128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
372     143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
373     158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
374     173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
375     188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
376     203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
377     218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
378     233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
379     248, 249, 250, 251, 252, 253, 254, 255>;
380 
381 /**
382  * build inverse-alphabet.
383  *
384  * calls find_pos_by_char() for each element of ndx_256
385  *
386  * based on C++11 parameter pack.
387  */
388 template <std::size_t... I>
make_inverse_array(const alphabet_type & v,std::index_sequence<I...>)389 constexpr inverse_alphabet_type make_inverse_array(const alphabet_type &v,
390                                                    std::index_sequence<I...>) {
391   return {{find_pos_of_char(v, I)...}};
392 }
393 
394 /**
395  * inverse
396  */
inverse(const alphabet_type & v)397 constexpr inverse_alphabet_type inverse(const alphabet_type &v) {
398   return make_inverse_array(v, ndx_256{});
399 }
400 }  // namespace detail
401 
402 class Base64 {
403  public:
404   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{
405       {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  // 0x00
406        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',  //
407        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',  // 0x10
408        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  //
409        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',  // 0x20
410        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',  //
411        'w', 'x', 'y', 'z', '0', '1', '2', '3',  // 0x30
412        '4', '5', '6', '7', '8', '9', '+', '/'}};
413 
414   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
415       detail::inverse(alphabet);
416 };
417 
418 class Base64Url {
419  public:
420   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{
421       {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  // 0x00
422        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',  //
423        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',  // 0x10
424        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  //
425        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',  // 0x20
426        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',  //
427        'w', 'x', 'y', 'z', '0', '1', '2', '3',  // 0x30
428        '4', '5', '6', '7', '8', '9', '-', '_'}};
429 
430   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
431       detail::inverse(alphabet);
432 };
433 
434 /**
435  * Base64 alphabet for MCF.
436  *
437  * same as Base64 from RFC4648, but different altchars to fit the needs of MCF
438  *
439  * altchars
440  * :  . and /
441  *
442  * paddingchar
443  * :  =
444  *
445  * padding mandatory
446  * :  no
447  */
448 class Mcf {
449  public:
450   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{
451       {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  // 0x00
452        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',  //
453        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',  // 0x10
454        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  //
455        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',  // 0x20
456        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',  //
457        'w', 'x', 'y', 'z', '0', '1', '2', '3',  // 0x30
458        '4', '5', '6', '7', '8', '9', '.', '/'}};
459 
460   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
461       detail::inverse(alphabet);
462 };
463 
464 class Crypt {
465  public:
466   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{
467       {'.', '/', '0', '1', '2', '3', '4', '5',  // 0x00
468        '6', '7', '8', '9', 'A', 'B', 'C', 'D',  //
469        'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',  // 0x10
470        'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',  //
471        'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',  // 0x20
472        'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',  //
473        'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',  // 0x30
474        's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}};
475 
476   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
477       detail::inverse(alphabet);
478 };
479 
480 class Bcrypt {
481  public:
482   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{
483       {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F',  // 0x00
484        'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',  //
485        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',  // 0x10
486        'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',  //
487        'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',  // 0x20
488        'm', 'n', 'o', 'p', 'q', 'r', 's', 't',  //
489        'u', 'v', 'w', 'x', 'y', 'z', '0', '1',  // 0x30
490        '2', '3', '4', '5', '6', '7', '8', '9'}};
491 
492   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
493       detail::inverse(alphabet);
494 };
495 
496 class Uuencode {
497  public:
498   static constexpr alphabet_type HTTP_COMMON_EXPORT alphabet{{
499       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,  // 0x00
500       0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
501       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,  // 0x10
502       0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
503       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,  // 0x20
504       0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
505       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,  // 0x30
506       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
507   }};
508 
509   static constexpr inverse_alphabet_type HTTP_COMMON_EXPORT inverse_alphabet =
510       detail::inverse(alphabet);
511 };
512 }  // namespace Base64Alphabet
513 
514 /**
515  * Base64 codec base class.
516  */
517 template <class Alphabet, Base64Endianess E, bool PaddingMandatory,
518           char PaddingChar>
519 class Base64Base {
520  public:
521   /**
522    * decode a base64 encoded string to binary.
523    *
524    * @pre encoded only contains alphabet
525    *
526    * @throws std::runtime_error if preconditions are not met
527    * @returns binary representation
528    */
decode(const std::string & encoded)529   static std::vector<uint8_t> decode(const std::string &encoded) {
530     return Base64Impl::decode<E, PaddingMandatory, PaddingChar>(
531         encoded, Alphabet::inverse_alphabet);
532   }
533 
534   /**
535    * encode binary to base64.
536    */
encode(const std::vector<uint8_t> & decoded)537   static std::string encode(const std::vector<uint8_t> &decoded) {
538     return Base64Impl::encode<E, PaddingMandatory, PaddingChar>(
539         decoded, Alphabet::alphabet);
540   }
541 };
542 
543 /**
544  * 'base64' alphabet from RFC4648.
545  *
546  * also used by:
547  *
548  * - uuencode-base64
549  * - data URI scheme (RFC2397)
550  *
551  * alphabet
552  * :  `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`
553  *
554  * padding mandatory
555  * :  yes, with =
556  */
557 using Base64 =
558     Base64Base<Base64Alphabet::Base64, Base64Endianess::BIG, true, '='>;
559 
560 /**
561  * 'base64url' URL and Filename-safe Base64 alphabet from RFC4648.
562  *
563  * '+' and '/' in 'base64' have special meaning in URLs and would need
564  * to be URL-encoded.
565  *
566  * alphabet
567  * :  `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`
568  *
569  * padding mandatory
570  * :  yes, with =
571  */
572 using Base64Url =
573     Base64Base<Base64Alphabet::Base64Url, Base64Endianess::BIG, true, '='>;
574 
575 /**
576  * Base64 alphabet for MCF's pbkdf2 methods.
577  *
578  * alphabet
579  * :  `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./`
580  *
581  * padding mandatory
582  * :  no
583  */
584 using Radix64Mcf =
585     Base64Base<Base64Alphabet::Mcf, Base64Endianess::BIG, false, ' '>;
586 
587 /**
588  * Radix64 for crypt (little-endian).
589  *
590  * alphabet
591  * :  `./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`
592  *
593  * padding mandatory
594  * :  no
595  */
596 using Radix64Crypt =
597     Base64Base<Base64Alphabet::Crypt, Base64Endianess::LITTLE, false, ' '>;
598 
599 /**
600  * Radix64 for crypt (big-endian).
601  *
602  * @see Radix64Crypt
603  */
604 using Radix64CryptBE =
605     Base64Base<Base64Alphabet::Crypt, Base64Endianess::BIG, false, ' '>;
606 
607 /**
608  * Radix64 for bcrypt.
609  *
610  * alphabet
611  * :  `./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`
612  *
613  * padding mandatory
614  * :  no
615  */
616 using Radix64Bcrypt =
617     Base64Base<Base64Alphabet::Bcrypt, Base64Endianess::LITTLE, false, ' '>;
618 
619 /**
620  * Radix64 for traditional Uuencode.
621  *
622  * alphabet
623  * :  0x32...0x95
624  *
625  * padding mandatory
626  * :  yes, with accent
627  */
628 using Radix64Uuencode =
629     Base64Base<Base64Alphabet::Uuencode, Base64Endianess::BIG, true, '`'>;
630 
631 #endif
632