Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
base64.cpp
Go to the documentation of this file.
1/*
2 base64.cpp and base64.h
3
4 base64 encoding and decoding with C++.
5 More information at
6 https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
7
8 Version: 2.rc.08 (release candidate)
9
10 Copyright (C) 2004-2017, 2020, 2021 René Nyffenegger
11
12 This source code is provided 'as-is', without any express or implied
13 warranty. In no event will the author be held liable for any damages
14 arising from the use of this software.
15
16 Permission is granted to anyone to use this software for any purpose,
17 including commercial applications, and to alter it and redistribute it
18 freely, subject to the following restrictions:
19
20 1. The origin of this source code must not be misrepresented; you must not
21 claim that you wrote the original source code. If you use this source code
22 in a product, an acknowledgment in the product documentation would be
23 appreciated but is not required.
24
25 2. Altered source versions must be plainly marked as such, and must not be
26 misrepresented as being the original source code.
27
28 3. This notice may not be removed or altered from any source distribution.
29
30 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
31
32*/
33
34#include "base64.hpp"
35#include "throw_or_abort.hpp"
36
37#include <algorithm>
38
39//
40// Depending on the url parameter in base64_chars, one of
41// two sets of base64 characters needs to be chosen.
42// They differ in their last two characters.
43//
44static const char* base64_chars[2] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
45 "abcdefghijklmnopqrstuvwxyz"
46 "0123456789"
47 "+/",
48
49 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
50 "abcdefghijklmnopqrstuvwxyz"
51 "0123456789"
52 "-_" };
53
54static unsigned int pos_of_char(const unsigned char chr)
55{
56 //
57 // Return the position of chr within base64_encode()
58 //
59
60 if (chr >= 'A' && chr <= 'Z')
61 return chr - 'A';
62 else if (chr >= 'a' && chr <= 'z')
63 return chr - 'a' + ('Z' - 'A') + 1;
64 else if (chr >= '0' && chr <= '9')
65 return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
66 else if (chr == '+' || chr == '-')
67 return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
68 else if (chr == '/' || chr == '_')
69 return 63; // Ditto for '/' and '_'
70 else
71 //
72 // 2020-10-23: Throw std::exception rather than const char*
73 //(Pablo Martin-Gomez, https://github.com/Bouska)
74 //
75 throw_or_abort("Input is not valid base64-encoded data.");
76}
77
78static std::string insert_linebreaks(std::string str, size_t distance)
79{
80 //
81 // Provided by https://github.com/JomaCorpFX, adapted by me.
82 //
83 if (!str.length()) {
84 return "";
85 }
86
87 size_t pos = distance;
88
89 while (pos < str.size()) {
90 str.insert(pos, "\n");
91 pos += distance + 1;
92 }
93
94 return str;
95}
96
97template <typename String, unsigned int line_length> static std::string encode_with_line_breaks(String s)
98{
99 return insert_linebreaks(base64_encode(s, false), line_length);
100}
101
102template <typename String> static std::string encode_pem(String s)
103{
104 return encode_with_line_breaks<String, 64>(s);
105}
106
107template <typename String> static std::string encode_mime(String s)
108{
109 return encode_with_line_breaks<String, 76>(s);
110}
111
112template <typename String> static std::string encode(String s, bool url)
113{
114 return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url);
115}
116
117std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url)
118{
119
120 size_t len_encoded = (in_len + 2) / 3 * 4;
121
122 unsigned char trailing_char = url ? '.' : '=';
123
124 //
125 // Choose set of base64 characters. They differ
126 // for the last two positions, depending on the url
127 // parameter.
128 // A bool (as is the parameter url) is guaranteed
129 // to evaluate to either 0 or 1 in C++ therefore,
130 // the correct character set is chosen by subscripting
131 // base64_chars with url.
132 //
133 const char* base64_chars_ = base64_chars[url];
134
135 std::string ret;
136 ret.reserve(len_encoded);
137
138 unsigned int pos = 0;
139
140 while (pos < in_len) {
141 ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
142
143 if (pos + 1 < in_len) {
144 ret.push_back(
145 base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
146
147 if (pos + 2 < in_len) {
148 ret.push_back(
149 base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
150 ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & 0x3f]);
151 } else {
152 ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
153 ret.push_back(static_cast<char>(trailing_char));
154 }
155 } else {
156
157 ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
158 ret.push_back(static_cast<char>(trailing_char));
159 ret.push_back(static_cast<char>(trailing_char));
160 }
161
162 pos += 3;
163 }
164
165 return ret;
166}
167
168template <typename String> static std::string decode(String encoded_string, bool remove_linebreaks)
169{
170 //
171 // decode(…) is templated so that it can be used with String = const std::string&
172 // or std::string_view (requires at least C++17)
173 //
174
175 if (encoded_string.empty())
176 return std::string();
177
178 if (remove_linebreaks) {
179
180 std::string copy(encoded_string);
181
182 copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
183
184 return base64_decode(copy, false);
185 }
186
187 size_t length_of_string = encoded_string.length();
188 size_t pos = 0;
189
190 //
191 // The approximate length (bytes) of the decoded string might be one or
192 // two bytes smaller, depending on the amount of trailing equal signs
193 // in the encoded string. This approximation is needed to reserve
194 // enough space in the string to be returned.
195 //
196 size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
197 std::string ret;
198 ret.reserve(approx_length_of_decoded_string);
199
200 while (pos < length_of_string) {
201 //
202 // Iterate over encoded input string in chunks. The size of all
203 // chunks except the last one is 4 bytes.
204 //
205 // The last chunk might be padded with equal signs or dots
206 // in order to make it 4 bytes in size as well, but this
207 // is not required as per RFC 2045.
208 //
209 // All chunks except the last one produce three output bytes.
210 //
211 // The last chunk produces at least one and up to three bytes.
212 //
213
214 size_t pos_of_char_1 = pos_of_char(static_cast<unsigned char>(encoded_string[pos + 1]));
215
216 //
217 // Emit the first output byte that is produced in each chunk:
218 //
219 ret.push_back(static_cast<std::string::value_type>(
220 ((pos_of_char(static_cast<unsigned char>(encoded_string[pos + 0])) << 2) + ((pos_of_char_1 & 0x30) >> 4))));
221
222 if ((pos + 2 < length_of_string) && // Check for data that is not padded with equal signs (which is
223 // allowed by RFC 2045)
224 encoded_string[pos + 2] != '=' &&
225 encoded_string[pos + 2] != '.' // accept URL-safe base 64 strings, too, so check for '.' also.
226 ) {
227 //
228 // Emit a chunk's second byte (which might not be produced in the last chunk).
229 //
230 unsigned int pos_of_char_2 = pos_of_char(static_cast<unsigned char>(encoded_string[pos + 2]));
231 ret.push_back(
232 static_cast<std::string::value_type>(((pos_of_char_1 & 0x0f) << 4) + ((pos_of_char_2 & 0x3c) >> 2)));
233
234 if ((pos + 3 < length_of_string) && encoded_string[pos + 3] != '=' && encoded_string[pos + 3] != '.') {
235 //
236 // Emit a chunk's third byte (which might not be produced in the last chunk).
237 //
238 ret.push_back(static_cast<std::string::value_type>(
239 ((pos_of_char_2 & 0x03) << 6) + pos_of_char(static_cast<unsigned char>(encoded_string[pos + 3]))));
240 }
241 }
242
243 pos += 4;
244 }
245
246 return ret;
247}
248
249std::string base64_decode(std::string const& s, bool remove_linebreaks)
250{
251 return decode(s, remove_linebreaks);
252}
253
254std::string base64_encode(std::string const& s, bool url)
255{
256 return encode(s, url);
257}
258
259std::string base64_encode_pem(std::string const& s)
260{
261 return encode_pem(s);
262}
263
264std::string base64_encode_mime(std::string const& s)
265{
266 return encode_mime(s);
267}
268
269#if __cplusplus >= 201703L
270//
271// Interface with std::string_view rather than const std::string&
272// Requires C++17
273// Provided by Yannic Bonenberger (https://github.com/Yannic)
274//
275
276std::string base64_encode(std::string_view s, bool url)
277{
278 return encode(s, url);
279}
280
281std::string base64_encode_pem(std::string_view s)
282{
283 return encode_pem(s);
284}
285
286std::string base64_encode_mime(std::string_view s)
287{
288 return encode_mime(s);
289}
290
291std::string base64_decode(std::string_view s, bool remove_linebreaks)
292{
293 return decode(s, remove_linebreaks);
294}
295
296#endif // __cplusplus >= 201703L
std::string base64_encode_mime(std::string const &s)
Definition base64.cpp:264
std::string base64_decode(std::string const &s, bool remove_linebreaks)
Definition base64.cpp:249
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len, bool url)
Definition base64.cpp:117
std::string base64_encode_pem(std::string const &s)
Definition base64.cpp:259
void throw_or_abort(std::string const &err)