517
|
1 /* |
|
2 * base64.hpp -- base64 encoding and decoding |
|
3 * |
|
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> |
|
5 * |
|
6 * Permission to use, copy, modify, and/or distribute this software for any |
|
7 * purpose with or without fee is hereby granted, provided that the above |
|
8 * copyright notice and this permission notice appear in all copies. |
|
9 * |
|
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
17 */ |
|
18 |
|
19 #ifndef BASE64_HPP |
|
20 #define BASE64_HPP |
|
21 |
|
22 /** |
|
23 * \file base64.hpp |
|
24 * \brief Base64 encoding and decoding. |
|
25 * \author David Demelier <markand@malikania.fr> |
|
26 */ |
|
27 |
|
28 /** |
|
29 * \page Base64 Base64 |
|
30 * \brief Base64 encoding and decoding. |
|
31 * |
|
32 * The %base64 library let you encode and decode %base64 data from std::string and iterators. |
|
33 * |
|
34 * ## Encoding |
|
35 * |
|
36 * You can encode like this: |
|
37 * |
|
38 * ````cpp |
|
39 * std::string b64 = base64::encode("Hello world!"); |
|
40 * ```` |
|
41 * |
|
42 * ## Decoding |
|
43 * |
|
44 * And you can decode like this: |
|
45 * |
|
46 * ```` |
|
47 * try { |
|
48 * std::string text = base64::decode(msg); |
|
49 * } catch (const std::exception &ex) { |
|
50 * std::cerr << ex.what() << std::endl; |
|
51 * } |
|
52 * ```` |
|
53 */ |
|
54 |
|
55 #include <cassert> |
|
56 #include <cctype> |
|
57 #include <stdexcept> |
|
58 #include <string> |
|
59 |
|
60 /** |
|
61 * \brief main %base64 namespace. |
|
62 */ |
|
63 namespace base64 { |
|
64 |
|
65 /** |
|
66 * Check if the character is a %base64 character, A-Za-z0-9 and +/. |
|
67 * |
|
68 * \param ch the character to test |
|
69 * \return true if valid |
|
70 */ |
|
71 inline bool isBase64(char ch) noexcept |
|
72 { |
|
73 return std::isalnum(ch) != 0 || ch == '+' || ch == '/'; |
|
74 } |
|
75 |
|
76 /** |
|
77 * Check if the given character is a valid character in %base64 string, including '='. |
|
78 * |
|
79 * \param ch the character |
|
80 * \return true if the character is valid |
|
81 */ |
|
82 inline bool isValid(char ch) noexcept |
|
83 { |
|
84 return isBase64(ch) || ch == '='; |
|
85 } |
|
86 |
|
87 /** |
|
88 * Get the %base64 character from the 6-bits value. |
|
89 * |
|
90 * \pre value must be valid (< 64) |
|
91 * \param value the value |
|
92 * \return the %base64 character for value |
|
93 */ |
|
94 inline char lookup(unsigned char value) noexcept |
|
95 { |
|
96 static const char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
97 |
|
98 assert(value < 64); |
|
99 |
|
100 return table[value]; |
|
101 } |
|
102 |
|
103 /** |
|
104 * Get the integer value from the %base64 character. |
|
105 * |
|
106 * \pre isBase64(ch) |
|
107 * \param ch the %base64 character |
|
108 * \return the integer value for the %base64 character ch |
|
109 * ```` |
|
110 * auto b64 = base64::rlookup('D') // 3 |
|
111 * ```` |
|
112 */ |
|
113 inline unsigned char rlookup(char ch) noexcept |
|
114 { |
|
115 assert(isBase64(ch)); |
|
116 |
|
117 if (ch >= '0' && ch <= '9') |
|
118 return static_cast<unsigned char>(ch + 4); |
|
119 if (ch >= 'A' && ch <= 'Z') |
|
120 return static_cast<unsigned char>(ch - 65); |
|
121 if (ch >= 'a' && ch <= 'z') |
|
122 return static_cast<unsigned char>(ch - 71); |
|
123 |
|
124 return (ch == '+') ? 62U : 63U; |
|
125 } |
|
126 |
|
127 /** |
|
128 * Encode the input to the output. |
|
129 * |
|
130 * Requirements: |
|
131 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) |
|
132 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) |
|
133 * |
|
134 * \param input the beginning |
|
135 * \param end the end of the data |
|
136 * \param output the output destination |
|
137 * \return output |
|
138 */ |
|
139 template <typename InputIt, typename OutputIt> |
|
140 OutputIt encode(InputIt input, InputIt end, OutputIt output) |
|
141 { |
|
142 while (input != end) { |
|
143 char inputbuf[3] = { 0, 0, 0 }; |
|
144 int count; |
|
145 |
|
146 for (count = 0; count < 3 && input != end; ++count) |
|
147 inputbuf[count] = *input++; |
|
148 |
|
149 *output++ = lookup(inputbuf[0] >> 2 & 0x3f); |
|
150 *output++ = lookup((inputbuf[0] << 4 & 0x3f) | (inputbuf[1] >> 4 & 0x0f)); |
|
151 *output++ = (count < 2) ? '=' : lookup((inputbuf[1] << 2 & 0x3c) | (inputbuf[2] >> 6 & 0x03)); |
|
152 *output++ = (count < 3) ? '=' : lookup(inputbuf[2] & 0x3f); |
|
153 } |
|
154 |
|
155 return output; |
|
156 } |
|
157 |
|
158 /** |
|
159 * Decode the input to the output. |
|
160 * |
|
161 * Requirements: |
|
162 * - **InputIt** must be [InputIterator](http://en.cppreference.com/w/cpp/concept/InputIterator) |
|
163 * - **OutputIt** must be [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator) |
|
164 * |
|
165 * \param input the beginning |
|
166 * \param end the end of the data |
|
167 * \param output the output destination |
|
168 * \return output |
|
169 * \throw std::invalid_argument on bad %base64 string |
|
170 */ |
|
171 template <typename InputIt, typename OutputIt> |
|
172 OutputIt decode(InputIt input, InputIt end, OutputIt output) |
|
173 { |
|
174 while (input != end) { |
|
175 char inputbuf[4] = { -1, -1, -1, -1 }; |
|
176 int count; |
|
177 |
|
178 for (count = 0; count < 4 && input != end; ++count) { |
|
179 // Check if the character is valid and get its value. |
|
180 if ((*input == '=' && count <= 1) || !isValid(*input)) |
|
181 throw std::invalid_argument("invalid base64 string"); |
|
182 if (isBase64(*input)) |
|
183 inputbuf[count] = static_cast<char>(rlookup(*input)); |
|
184 |
|
185 input++; |
|
186 } |
|
187 |
|
188 if (count != 4) |
|
189 throw std::invalid_argument("truncated string"); |
|
190 |
|
191 *output++ = static_cast<char>(((inputbuf[0] << 2) & 0xfc) | ((inputbuf[1] >> 4) & 0x03)); |
|
192 |
|
193 if (inputbuf[2] != -1) |
|
194 *output++ = static_cast<char>(((inputbuf[1] << 4) & 0xf0) | ((inputbuf[2] >> 2) & 0x0f)); |
|
195 if (inputbuf[3] != -1) { |
|
196 // "XY=Z" is not allowed. |
|
197 if (inputbuf[2] == -1) |
|
198 throw std::invalid_argument("invalid base64 string"); |
|
199 |
|
200 *output++ = static_cast<char>(((inputbuf[2] << 6) & 0xc0) | (inputbuf[3] & 0x3f)); |
|
201 } |
|
202 } |
|
203 |
|
204 return output; |
|
205 } |
|
206 |
|
207 /** |
|
208 * Encode a string. |
|
209 * |
|
210 * \param input the input string |
|
211 * \return the %base64 formatted string |
|
212 */ |
|
213 inline std::string encode(const std::string &input) |
|
214 { |
|
215 std::string result; |
|
216 |
|
217 encode(input.begin(), input.end(), std::back_inserter(result)); |
|
218 |
|
219 return result; |
|
220 } |
|
221 |
|
222 /** |
|
223 * Decode a string. |
|
224 * |
|
225 * \param input the %base64 formatted string |
|
226 * \return the original string |
|
227 * \throw std::invalid_argument on bad %base64 string |
|
228 */ |
|
229 inline std::string decode(const std::string &input) |
|
230 { |
|
231 std::string result; |
|
232 |
|
233 decode(input.begin(), input.end(), std::back_inserter(result)); |
|
234 |
|
235 return result; |
|
236 } |
|
237 |
|
238 } // !base64 |
|
239 |
|
240 #endif // !BASE64_HPP |