JWT-CPP v0.7.0
A header only library for creating and validating JSON Web Tokens (JWT) in C++
Loading...
Searching...
No Matches
base.h
1#ifndef JWT_CPP_BASE_H
2#define JWT_CPP_BASE_H
3
4#include <algorithm>
5#include <array>
6#include <cstdint>
7#include <stdexcept>
8#include <string>
9#include <vector>
10
11#ifdef __has_cpp_attribute
12#if __has_cpp_attribute(fallthrough)
13#define JWT_FALLTHROUGH [[fallthrough]]
14#endif
15#endif
16
17#ifndef JWT_FALLTHROUGH
18#define JWT_FALLTHROUGH
19#endif
20
21namespace jwt {
25 namespace alphabet {
32 struct base64 {
33 static const std::array<char, 64>& data() {
34 static constexpr std::array<char, 64> data{
35 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
36 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
37 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
38 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}};
39 return data;
40 }
41 static const std::array<int8_t, 256>& rdata() {
42 static constexpr std::array<int8_t, 256> rdata{{
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
45 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
46 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
47 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
48 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
50 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54 }};
55 return rdata;
56 }
57 static const std::string& fill() {
58 static const std::string fill{"="};
59 return fill;
60 }
61 };
71 struct base64url {
72 static const std::array<char, 64>& data() {
73 static constexpr std::array<char, 64> data{
74 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
75 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
76 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
77 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
78 return data;
79 }
80 static const std::array<int8_t, 256>& rdata() {
81 static constexpr std::array<int8_t, 256> rdata{{
82 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
83 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
84 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
85 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
86 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
87 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
88 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
89 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
90 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
91 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
92 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
93 }};
94 return rdata;
95 }
96 static const std::string& fill() {
97 static const std::string fill{"%3d"};
98 return fill;
99 }
100 };
101 namespace helper {
109 static const std::array<char, 64>& data() {
110 static constexpr std::array<char, 64> data{
111 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
112 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
113 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
114 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
115 return data;
116 }
117 static const std::array<int8_t, 256>& rdata() {
118 static constexpr std::array<int8_t, 256> rdata{{
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
121 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
122 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
123 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
124 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
126 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
130 }};
131 return rdata;
132 }
133 static const std::vector<std::string>& fill() {
134 static const std::vector<std::string> fill{"%3D", "%3d"};
135 return fill;
136 }
137 };
138 } // namespace helper
139
140 inline uint32_t index(const std::array<int8_t, 256>& rdata, char symbol) {
141 auto index = rdata[static_cast<unsigned char>(symbol)];
142 if (index <= -1) { throw std::runtime_error("Invalid input: not within alphabet"); }
143 return static_cast<uint32_t>(index);
144 }
145 } // namespace alphabet
146
150 namespace base {
151 namespace details {
152 struct padding {
153 size_t count = 0;
154 size_t length = 0;
155
156 padding() = default;
157 padding(size_t count, size_t length) : count(count), length(length) {}
158
159 padding operator+(const padding& p) { return padding(count + p.count, length + p.length); }
160
161 friend bool operator==(const padding& lhs, const padding& rhs) {
162 return lhs.count == rhs.count && lhs.length == rhs.length;
163 }
164 };
165
166 inline padding count_padding(const std::string& base, const std::vector<std::string>& fills) {
167 for (const auto& fill : fills) {
168 if (base.size() < fill.size()) continue;
169 // Does the end of the input exactly match the fill pattern?
170 if (base.substr(base.size() - fill.size()) == fill) {
171 return padding{1, fill.length()} +
172 count_padding(base.substr(0, base.size() - fill.size()), fills);
173 }
174 }
175
176 return {};
177 }
178
179 inline std::string encode(const std::string& bin, const std::array<char, 64>& alphabet,
180 const std::string& fill) {
181 size_t size = bin.size();
182 std::string res;
183
184 // clear incomplete bytes
185 size_t fast_size = size - size % 3;
186 for (size_t i = 0; i < fast_size;) {
187 uint32_t octet_a = static_cast<unsigned char>(bin[i++]);
188 uint32_t octet_b = static_cast<unsigned char>(bin[i++]);
189 uint32_t octet_c = static_cast<unsigned char>(bin[i++]);
190
191 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
192
193 res += alphabet[(triple >> 3 * 6) & 0x3F];
194 res += alphabet[(triple >> 2 * 6) & 0x3F];
195 res += alphabet[(triple >> 1 * 6) & 0x3F];
196 res += alphabet[(triple >> 0 * 6) & 0x3F];
197 }
198
199 if (fast_size == size) return res;
200
201 size_t mod = size % 3;
202
203 uint32_t octet_a = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
204 uint32_t octet_b = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
205 uint32_t octet_c = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
206
207 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
208
209 switch (mod) {
210 case 1:
211 res += alphabet[(triple >> 3 * 6) & 0x3F];
212 res += alphabet[(triple >> 2 * 6) & 0x3F];
213 res += fill;
214 res += fill;
215 break;
216 case 2:
217 res += alphabet[(triple >> 3 * 6) & 0x3F];
218 res += alphabet[(triple >> 2 * 6) & 0x3F];
219 res += alphabet[(triple >> 1 * 6) & 0x3F];
220 res += fill;
221 break;
222 default: break;
223 }
224
225 return res;
226 }
227
228 inline std::string decode(const std::string& base, const std::array<int8_t, 256>& rdata,
229 const std::vector<std::string>& fill) {
230 const auto pad = count_padding(base, fill);
231 if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill");
232
233 const size_t size = base.size() - pad.length;
234 if ((size + pad.count) % 4 != 0) throw std::runtime_error("Invalid input: incorrect total size");
235
236 size_t out_size = size / 4 * 3;
237 std::string res;
238 res.reserve(out_size);
239
240 auto get_sextet = [&](size_t offset) { return alphabet::index(rdata, base[offset]); };
241
242 size_t fast_size = size - size % 4;
243 for (size_t i = 0; i < fast_size;) {
244 uint32_t sextet_a = get_sextet(i++);
245 uint32_t sextet_b = get_sextet(i++);
246 uint32_t sextet_c = get_sextet(i++);
247 uint32_t sextet_d = get_sextet(i++);
248
249 uint32_t triple =
250 (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
251
252 res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
253 res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
254 res += static_cast<char>((triple >> 0 * 8) & 0xFFU);
255 }
256
257 if (pad.count == 0) return res;
258
259 uint32_t triple = (get_sextet(fast_size) << 3 * 6) + (get_sextet(fast_size + 1) << 2 * 6);
260
261 switch (pad.count) {
262 case 1:
263 triple |= (get_sextet(fast_size + 2) << 1 * 6);
264 res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
265 res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
266 break;
267 case 2: res += static_cast<char>((triple >> 2 * 8) & 0xFFU); break;
268 default: break;
269 }
270
271 return res;
272 }
273
274 inline std::string decode(const std::string& base, const std::array<int8_t, 256>& rdata,
275 const std::string& fill) {
276 return decode(base, rdata, std::vector<std::string>{fill});
277 }
278
279 inline std::string pad(const std::string& base, const std::string& fill) {
280 std::string padding;
281 switch (base.size() % 4) {
282 case 1: padding += fill; JWT_FALLTHROUGH;
283 case 2: padding += fill; JWT_FALLTHROUGH;
284 case 3: padding += fill; JWT_FALLTHROUGH;
285 default: break;
286 }
287
288 return base + padding;
289 }
290
291 inline std::string trim(const std::string& base, const std::string& fill) {
292 auto pos = base.find(fill);
293 return base.substr(0, pos);
294 }
295 } // namespace details
296
307 template<typename T>
308 std::string encode(const std::string& bin) {
309 return details::encode(bin, T::data(), T::fill());
310 }
321 template<typename T>
322 std::string decode(const std::string& base) {
323 return details::decode(base, T::rdata(), T::fill());
324 }
335 template<typename T>
336 std::string pad(const std::string& base) {
337 return details::pad(base, T::fill());
338 }
349 template<typename T>
350 std::string trim(const std::string& base) {
351 return details::trim(base, T::fill());
352 }
353 } // namespace base
354} // namespace jwt
355
356#endif
std::string trim(const std::string &base)
Generic base64 trimming.
Definition base.h:350
std::string decode(const std::string &base)
Generic base64 decoding.
Definition base.h:322
std::string pad(const std::string &base)
Generic base64 padding.
Definition base.h:336
std::string encode(const std::string &bin)
Generic base64 encoding.
Definition base.h:308
JSON Web Token.
Definition base.h:21
valid list of character when working with Base64
Definition base.h:32
valid list of character when working with Base64URL
Definition base.h:71
A General purpose base64url alphabet respecting the URI Case Normalization
Definition base.h:108