str/base32: new module.
[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 /* RFC 4648:
8  *
9  * (1) The final quantum of encoding input is an integral multiple of 40
10  *     bits; here, the final unit of encoded output will be an integral
11  *     multiple of 8 characters with no "=" padding.
12  *
13  * (2) The final quantum of encoding input is exactly 8 bits; here, the
14  *     final unit of encoded output will be two characters followed by
15  *     six "=" padding characters.
16  *
17  * (3) The final quantum of encoding input is exactly 16 bits; here, the
18  *     final unit of encoded output will be four characters followed by
19  *     four "=" padding characters.
20  *
21  * (4) The final quantum of encoding input is exactly 24 bits; here, the
22  *     final unit of encoded output will be five characters followed by
23  *     three "=" padding characters.
24  *
25  * (5) The final quantum of encoding input is exactly 32 bits; here, the
26  *     final unit of encoded output will be seven characters followed by
27  *     one "=" padding character.
28  */
29 static size_t padlen(size_t remainder)
30 {
31         switch (remainder) {
32         case 0:
33                 return 0;
34         case 1:
35                 return 6;
36         case 2:
37                 return 4;
38         case 3:
39                 return 3;
40         case 4:
41                 return 1;
42         default:
43                 abort();
44         }
45 }
46
47 size_t base32_str_size(size_t bytes)
48 {
49         return (bytes + 4) / 5 * 8 + 1;
50 }
51
52 size_t base32_data_size(const char *str, size_t strlen)
53 {
54         /* 8 chars == 5 bytes, round up to avoid overflow even though
55          * not required for well-formed strings. */
56         size_t max = (strlen + 7) / 8 * 5, padding = 0;
57
58         /* Count trailing padding bytes. */
59         while (strlen && str[strlen-1] == '=' && padding < 6) {
60                 strlen--;
61                 padding++;
62         }
63
64         return max - (padding * 5 + 7)  / 8;
65 }
66
67 static bool decode_8_chars(const char c[8], beint64_t *res, int *bytes)
68 {
69         uint64_t acc = 0;
70         size_t num_pad = 0;
71         for (int i = 0; i < 8; i++) {
72                 uint8_t val;
73                 acc <<= 5;
74                 if (c[i] >= 'a' && c[i] <= 'z')
75                         val = c[i] - 'a';
76                 else if (c[i] >= 'A' && c[i] <= 'Z')
77                         val = c[i] - 'A';
78                 else if (c[i] >= '2' && c[i] <= '7')
79                         val = c[i] - '2' + 26;
80                 else if (c[i] == '=') {
81                         num_pad++;
82                         continue;
83                 } else
84                         return false;
85                 /* Can't have padding then non-pad */
86                 if (num_pad)
87                         return false;
88                 acc |= val;
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         static const char enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
124
125         assert(bytes > 0 && bytes <= 5);
126         memcpy((char *)&val + 3, buf, bytes);
127         res = be64_to_cpu(val);
128
129         while (bits > 0) {
130                 *dest = enc[(res >> 35) & 0x1F];
131                 dest++;
132                 res <<= 5;
133                 bits -= 5;
134         }
135
136         if (bytes != 5)
137                 memset(dest, '=', padlen(bytes));
138 }
139
140 bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
141 {
142         while (bufsize) {
143                 int bytes = 5;
144
145                 if (bytes > bufsize)
146                         bytes = bufsize;
147
148                 if (destsize < 8)
149                         return false;
150                 encode_8_chars(dest, buf, bytes);
151                 buf = (const char *)buf + bytes;
152                 bufsize -= bytes;
153                 destsize -= 8;
154                 dest += 8;
155         }
156         if (destsize != 1)
157                 return false;
158         *dest = '\0';
159         return true;
160 }