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::string& fill() {
42 static const std::string fill{"="};
43 return fill;
44 }
45 };
55 struct base64url {
56 static const std::array<char, 64>& data() {
57 static constexpr std::array<char, 64> data{
58 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
59 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
60 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
61 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
62 return data;
63 }
64 static const std::string& fill() {
65 static const std::string fill{"%3d"};
66 return fill;
67 }
68 };
69 namespace helper {
77 static const std::array<char, 64>& data() {
78 static constexpr std::array<char, 64> data{
79 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
80 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
81 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
82 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
83 return data;
84 }
85 static const std::vector<std::string>& fill() {
86 static const std::vector<std::string> fill{"%3D", "%3d"};
87 return fill;
88 }
89 };
90 } // namespace helper
91
92 inline uint32_t index(const std::array<char, 64>& alphabet, char symbol) {
93 auto itr = std::find_if(alphabet.cbegin(), alphabet.cend(), [symbol](char c) { return c == symbol; });
94 if (itr == alphabet.cend()) { throw std::runtime_error("Invalid input: not within alphabet"); }
95
96 return static_cast<uint32_t>(std::distance(alphabet.cbegin(), itr));
97 }
98 } // namespace alphabet
99
103 namespace base {
104 namespace details {
105 struct padding {
106 size_t count = 0;
107 size_t length = 0;
108
109 padding() = default;
110 padding(size_t count, size_t length) : count(count), length(length) {}
111
112 padding operator+(const padding& p) { return padding(count + p.count, length + p.length); }
113
114 friend bool operator==(const padding& lhs, const padding& rhs) {
115 return lhs.count == rhs.count && lhs.length == rhs.length;
116 }
117 };
118
119 inline padding count_padding(const std::string& base, const std::vector<std::string>& fills) {
120 for (const auto& fill : fills) {
121 if (base.size() < fill.size()) continue;
122 // Does the end of the input exactly match the fill pattern?
123 if (base.substr(base.size() - fill.size()) == fill) {
124 return padding{1, fill.length()} +
125 count_padding(base.substr(0, base.size() - fill.size()), fills);
126 }
127 }
128
129 return {};
130 }
131
132 inline std::string encode(const std::string& bin, const std::array<char, 64>& alphabet,
133 const std::string& fill) {
134 size_t size = bin.size();
135 std::string res;
136
137 // clear incomplete bytes
138 size_t fast_size = size - size % 3;
139 for (size_t i = 0; i < fast_size;) {
140 uint32_t octet_a = static_cast<unsigned char>(bin[i++]);
141 uint32_t octet_b = static_cast<unsigned char>(bin[i++]);
142 uint32_t octet_c = static_cast<unsigned char>(bin[i++]);
143
144 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
145
146 res += alphabet[(triple >> 3 * 6) & 0x3F];
147 res += alphabet[(triple >> 2 * 6) & 0x3F];
148 res += alphabet[(triple >> 1 * 6) & 0x3F];
149 res += alphabet[(triple >> 0 * 6) & 0x3F];
150 }
151
152 if (fast_size == size) return res;
153
154 size_t mod = size % 3;
155
156 uint32_t octet_a = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
157 uint32_t octet_b = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
158 uint32_t octet_c = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
159
160 uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
161
162 switch (mod) {
163 case 1:
164 res += alphabet[(triple >> 3 * 6) & 0x3F];
165 res += alphabet[(triple >> 2 * 6) & 0x3F];
166 res += fill;
167 res += fill;
168 break;
169 case 2:
170 res += alphabet[(triple >> 3 * 6) & 0x3F];
171 res += alphabet[(triple >> 2 * 6) & 0x3F];
172 res += alphabet[(triple >> 1 * 6) & 0x3F];
173 res += fill;
174 break;
175 default: break;
176 }
177
178 return res;
179 }
180
181 inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
182 const std::vector<std::string>& fill) {
183 const auto pad = count_padding(base, fill);
184 if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill");
185
186 const size_t size = base.size() - pad.length;
187 if ((size + pad.count) % 4 != 0) throw std::runtime_error("Invalid input: incorrect total size");
188
189 size_t out_size = size / 4 * 3;
190 std::string res;
191 res.reserve(out_size);
192
193 auto get_sextet = [&](size_t offset) { return alphabet::index(alphabet, base[offset]); };
194
195 size_t fast_size = size - size % 4;
196 for (size_t i = 0; i < fast_size;) {
197 uint32_t sextet_a = get_sextet(i++);
198 uint32_t sextet_b = get_sextet(i++);
199 uint32_t sextet_c = get_sextet(i++);
200 uint32_t sextet_d = get_sextet(i++);
201
202 uint32_t triple =
203 (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);
204
205 res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
206 res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
207 res += static_cast<char>((triple >> 0 * 8) & 0xFFU);
208 }
209
210 if (pad.count == 0) return res;
211
212 uint32_t triple = (get_sextet(fast_size) << 3 * 6) + (get_sextet(fast_size + 1) << 2 * 6);
213
214 switch (pad.count) {
215 case 1:
216 triple |= (get_sextet(fast_size + 2) << 1 * 6);
217 res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
218 res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
219 break;
220 case 2: res += static_cast<char>((triple >> 2 * 8) & 0xFFU); break;
221 default: break;
222 }
223
224 return res;
225 }
226
227 inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
228 const std::string& fill) {
229 return decode(base, alphabet, std::vector<std::string>{fill});
230 }
231
232 inline std::string pad(const std::string& base, const std::string& fill) {
233 std::string padding;
234 switch (base.size() % 4) {
235 case 1: padding += fill; JWT_FALLTHROUGH;
236 case 2: padding += fill; JWT_FALLTHROUGH;
237 case 3: padding += fill; JWT_FALLTHROUGH;
238 default: break;
239 }
240
241 return base + padding;
242 }
243
244 inline std::string trim(const std::string& base, const std::string& fill) {
245 auto pos = base.find(fill);
246 return base.substr(0, pos);
247 }
248 } // namespace details
249
260 template<typename T>
261 std::string encode(const std::string& bin) {
262 return details::encode(bin, T::data(), T::fill());
263 }
274 template<typename T>
275 std::string decode(const std::string& base) {
276 return details::decode(base, T::data(), T::fill());
277 }
288 template<typename T>
289 std::string pad(const std::string& base) {
290 return details::pad(base, T::fill());
291 }
302 template<typename T>
303 std::string trim(const std::string& base) {
304 return details::trim(base, T::fill());
305 }
306 } // namespace base
307} // namespace jwt
308
309#endif
std::string trim(const std::string &base)
Generic base64 trimming.
Definition base.h:303
std::string decode(const std::string &base)
Generic base64 decoding.
Definition base.h:275
std::string pad(const std::string &base)
Generic base64 padding.
Definition base.h:289
std::string encode(const std::string &bin)
Generic base64 encoding.
Definition base.h:261
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:55
A General purpose base64url alphabet respecting the URI Case Normalization
Definition base.h:76