Goby3  3.1.4
2024.02.22
base.h
Go to the documentation of this file.
1 #ifndef JWT_CPP_BASE_H
2 #define JWT_CPP_BASE_H
3 
4 #include <array>
5 #include <stdexcept>
6 #include <string>
7 
8 #ifdef __has_cpp_attribute
9 #if __has_cpp_attribute(fallthrough)
10 #define JWT_FALLTHROUGH [[fallthrough]]
11 #endif
12 #endif
13 
14 #ifndef JWT_FALLTHROUGH
15 #define JWT_FALLTHROUGH
16 #endif
17 
18 namespace jwt
19 {
23 namespace alphabet
24 {
28 struct base64
29 {
30  static const std::array<char, 64>& data()
31  {
32  static constexpr std::array<char, 64> data{
33  {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
34  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
35  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
36  'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}};
37  return data;
38  }
39  static const std::string& fill()
40  {
41  static std::string fill{"="};
42  return fill;
43  }
44 };
48 struct base64url
49 {
50  static const std::array<char, 64>& data()
51  {
52  static constexpr std::array<char, 64> data{
53  {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
54  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
55  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
56  'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
57  return data;
58  }
59  static const std::string& fill()
60  {
61  static std::string fill{"%3d"};
62  return fill;
63  }
64 };
65 } // namespace alphabet
66 
70 class base
71 {
72  public:
73  template <typename T> static std::string encode(const std::string& bin)
74  {
75  return encode(bin, T::data(), T::fill());
76  }
77  template <typename T> static std::string decode(const std::string& base)
78  {
79  return decode(base, T::data(), T::fill());
80  }
81  template <typename T> static std::string pad(const std::string& base)
82  {
83  return pad(base, T::fill());
84  }
85  template <typename T> static std::string trim(const std::string& base)
86  {
87  return trim(base, T::fill());
88  }
89 
90  private:
91  static std::string encode(const std::string& bin, const std::array<char, 64>& alphabet,
92  const std::string& fill)
93  {
94  size_t size = bin.size();
95  std::string res;
96 
97  // clear incomplete bytes
98  size_t fast_size = size - size % 3;
99  for (size_t i = 0; i < fast_size;)
100  {
101  uint32_t octet_a = static_cast<unsigned char>(bin[i++]);
102  uint32_t octet_b = static_cast<unsigned char>(bin[i++]);
103  uint32_t octet_c = static_cast<unsigned char>(bin[i++]);
104 
105  uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
106 
107  res += alphabet[(triple >> 3 * 6) & 0x3F];
108  res += alphabet[(triple >> 2 * 6) & 0x3F];
109  res += alphabet[(triple >> 1 * 6) & 0x3F];
110  res += alphabet[(triple >> 0 * 6) & 0x3F];
111  }
112 
113  if (fast_size == size)
114  return res;
115 
116  size_t mod = size % 3;
117 
118  uint32_t octet_a = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
119  uint32_t octet_b = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
120  uint32_t octet_c = fast_size < size ? static_cast<unsigned char>(bin[fast_size++]) : 0;
121 
122  uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
123 
124  switch (mod)
125  {
126  case 1:
127  res += alphabet[(triple >> 3 * 6) & 0x3F];
128  res += alphabet[(triple >> 2 * 6) & 0x3F];
129  res += fill;
130  res += fill;
131  break;
132  case 2:
133  res += alphabet[(triple >> 3 * 6) & 0x3F];
134  res += alphabet[(triple >> 2 * 6) & 0x3F];
135  res += alphabet[(triple >> 1 * 6) & 0x3F];
136  res += fill;
137  break;
138  default: break;
139  }
140 
141  return res;
142  }
143 
144  static std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
145  const std::string& fill)
146  {
147  size_t size = base.size();
148 
149  size_t fill_cnt = 0;
150  while (size > fill.size())
151  {
152  if (base.substr(size - fill.size(), fill.size()) == fill)
153  {
154  fill_cnt++;
155  size -= fill.size();
156  if (fill_cnt > 2)
157  throw std::runtime_error("Invalid input: too much fill");
158  }
159  else
160  break;
161  }
162 
163  if ((size + fill_cnt) % 4 != 0)
164  throw std::runtime_error("Invalid input: incorrect total size");
165 
166  size_t out_size = size / 4 * 3;
167  std::string res;
168  res.reserve(out_size);
169 
170  auto get_sextet = [&](size_t offset)
171  {
172  for (size_t i = 0; i < alphabet.size(); i++)
173  {
174  if (alphabet[i] == base[offset])
175  return static_cast<uint32_t>(i);
176  }
177  throw std::runtime_error("Invalid input: not within alphabet");
178  };
179 
180  size_t fast_size = size - size % 4;
181  for (size_t i = 0; i < fast_size;)
182  {
183  uint32_t sextet_a = get_sextet(i++);
184  uint32_t sextet_b = get_sextet(i++);
185  uint32_t sextet_c = get_sextet(i++);
186  uint32_t sextet_d = get_sextet(i++);
187 
188  uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) +
189  (sextet_d << 0 * 6);
190 
191  res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
192  res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
193  res += static_cast<char>((triple >> 0 * 8) & 0xFFU);
194  }
195 
196  if (fill_cnt == 0)
197  return res;
198 
199  uint32_t triple = (get_sextet(fast_size) << 3 * 6) + (get_sextet(fast_size + 1) << 2 * 6);
200 
201  switch (fill_cnt)
202  {
203  case 1:
204  triple |= (get_sextet(fast_size + 2) << 1 * 6);
205  res += static_cast<char>((triple >> 2 * 8) & 0xFFU);
206  res += static_cast<char>((triple >> 1 * 8) & 0xFFU);
207  break;
208  case 2: res += static_cast<char>((triple >> 2 * 8) & 0xFFU); break;
209  default: break;
210  }
211 
212  return res;
213  }
214 
215  static std::string pad(const std::string& base, const std::string& fill)
216  {
217  std::string padding;
218  switch (base.size() % 4)
219  {
220  case 1: padding += fill; JWT_FALLTHROUGH;
221  case 2: padding += fill; JWT_FALLTHROUGH;
222  case 3: padding += fill; JWT_FALLTHROUGH;
223  default: break;
224  }
225 
226  return base + padding;
227  }
228 
229  static std::string trim(const std::string& base, const std::string& fill)
230  {
231  auto pos = base.find(fill);
232  return base.substr(0, pos);
233  }
234 };
235 } // namespace jwt
236 
237 #endif
jwt::alphabet::base64url::data
static const std::array< char, 64 > & data()
Definition: base.h:50
jwt::base::encode
static std::string encode(const std::string &bin)
Definition: base.h:73
jwt::base
Alphabet generic methods for working with encoding/decoding the base64 family.
Definition: base.h:70
jwt::base::decode
static std::string decode(const std::string &base)
Definition: base.h:77
jwt::base::pad
static std::string pad(const std::string &base)
Definition: base.h:81
jwt::alphabet::base64url::fill
static const std::string & fill()
Definition: base.h:59
jwt
JSON Web Token.
Definition: base.h:18
jwt::alphabet::base64url
valid list of characted when working with Base64URL
Definition: base.h:48
jwt::alphabet::base64::fill
static const std::string & fill()
Definition: base.h:39
jwt::alphabet::base64
valid list of characted when working with Base64
Definition: base.h:28
jwt::base::trim
static std::string trim(const std::string &base)
Definition: base.h:85
JWT_FALLTHROUGH
#define JWT_FALLTHROUGH
Definition: base.h:15
jwt::alphabet::base64::data
static const std::array< char, 64 > & data()
Definition: base.h:30