]> git.ozlabs.org Git - ccan/blob - ccan/str/base32/base32.c
base32: add ability to substitute character set.
[ccan] / ccan / str / base32 / base32.c
1 /* CC0 license (public domain) - see LICENSE file for details */
2 #include "base32.h"
3 #include <assert.h>
4 #include <ccan/endian/endian.h>
5 #include <string.h> /* for memcpy, memset */
6
7 const char *base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=";
8
9 /* RFC 4648:
10  *
11  * (1) The final quantum of encoding input is an integral multiple of 40
12  *     bits; here, the final unit of encoded output will be an integral
13  *     multiple of 8 characters with no "=" padding.
14  *
15  * (2) The final quantum of encoding input is exactly 8 bits; here, the
16  *     final unit of encoded output will be two characters followed by
17  *     six "=" padding characters.
18  *
19  * (3) The final quantum of encoding input is exactly 16 bits; here, the
20  *     final unit of encoded output will be four characters followed by
21  *     four "=" padding characters.
22  *
23  * (4) The final quantum of encoding input is exactly 24 bits; here, the
24  *     final unit of encoded output will be five characters followed by
25  *     three "=" padding characters.
26  *
27  * (5) The final quantum of encoding input is exactly 32 bits; here, the
28  *     final unit of encoded output will be seven characters followed by
29  *     one "=" padding character.
30  */
31 static size_t padlen(size_t remainder)
32 {
33         switch (remainder) {
34         case 0:
35                 return 0;
36         case 1:
37                 return 6;
38         case 2:
39                 return 4;
40         case 3:
41                 return 3;
42         case 4:
43                 return 1;
44         default:
45                 abort();
46         }
47 }
48
49 size_t base32_str_size(size_t bytes)
50 {
51         return (bytes + 4) / 5 * 8 + 1;
52 }
53
54 size_t base32_data_size(const char *str, size_t strlen)
55 {
56         /* 8 chars == 5 bytes, round up to avoid overflow even though
57          * not required for well-formed strings. */
58         size_t max = (strlen + 7) / 8 * 5, padding = 0;
59
60         /* Count trailing padding bytes. */
61         while (strlen && str[strlen-1] == base32_chars[32] && padding < 6) {
62                 strlen--;
63                 padding++;
64         }
65
66         return max - (padding * 5 + 7)  / 8;
67 }
68
69 static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes)
70 {
71         uint64_t acc = 0;
72         size_t num_pad = 0;
73         for (int i = 0; i < 8; i++) {
74                 const char *p;
75
76                 acc <<= 5;
77                 p = memchr(base32_chars, c[i], 32);
78                 if (!p) {
79                         if (c[i] == base32_chars[32]) {
80                                 num_pad++;
81                                 continue;
82                         }
83                         return false;
84                 }
85                 /* Can't have padding then non-pad */
86                 if (num_pad)
87                         return false;
88                 acc |= (p - base32_chars);
89         }
90         *res = cpu_to_be64(acc);
91
92         /* Can't have 2 or 5 padding bytes */
93         if (num_pad == 5 || num_pad == 2)
94                 return false;
95         *bytes = (40 - num_pad * 5) / 8;
96         return true;
97 }
98
99 bool base32_decode(const char *str, size_t slen, void *buf, size_t bufsize)
100 {
101         while (slen >= 8) {
102                 beint64_t val;
103                 int bytes;
104                 if (!decode_8_chars(str, &val, &bytes))
105                         return false;
106                 str += 8;
107                 slen -= 8;
108                 /* Copy bytes into dst. */
109                 if (bufsize < bytes)
110                         return false;
111                 memcpy(buf, (char *)&val + 3, bytes);
112                 buf = (char *)buf + bytes;
113                 bufsize -= bytes;
114         }
115         return slen == 0 && bufsize == 0;
116 }
117
118 static void encode_8_chars(char *dest, const uint8_t *buf, int bytes)
119 {
120         beint64_t val = 0;
121         uint64_t res;
122         int bits = bytes * 8;
123
124         assert(bytes > 0 && bytes <= 5);
125         memcpy((char *)&val + 3, buf, bytes);
126         res = be64_to_cpu(val);
127
128         while (bits > 0) {
129                 *dest = base32_chars[(res >> 35) & 0x1F];
130                 dest++;
131                 res <<= 5;
132                 bits -= 5;
133         }
134
135         if (bytes != 5)
136                 memset(dest, base32_chars[32], padlen(bytes));
137 }
138
139 bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
140 {
141         while (bufsize) {
142                 int bytes = 5;
143
144                 if (bytes > bufsize)
145                         bytes = bufsize;
146
147                 if (destsize < 8)
148                         return false;
149                 encode_8_chars(dest, buf, bytes);
150                 buf = (const char *)buf + bytes;
151                 bufsize -= bytes;
152                 destsize -= 8;
153                 dest += 8;
154         }
155         if (destsize != 1)
156                 return false;
157         *dest = '\0';
158         return true;
159 }