1 /***
2 * Copyright (C) Microsoft. All rights reserved.
3 * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4 *
5 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 *
7 * base64.cpp
8 *
9 * Tests for base64-related utility functions and classes.
10 *
11 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12 ****/
13
14 #include "stdafx.h"
15
16 using namespace utility;
17
18 namespace tests
19 {
20 namespace functional
21 {
22 namespace utils_tests
23 {
SUITE(base64)24 SUITE(base64)
25 {
26 // Note: base64 works by encoding any 3 bytes as a four-byte string. Each triple is encoded independently of
27 // previous and subsequent triples. If, for a given set of input bytes, the number is not an even multiple of 3,
28 // the remaining 1 or two bytes are encoded and padded using '=' characters at the end. The encoding format is
29 // defined by IETF RFC 4648. Such padding is only allowed at the end of a encoded string, which makes it impossible
30 // to generally concatenate encoded strings and wind up with a string that is a valid base64 encoding.
31 //
32 // Since each triple of bytes is independent of others, we don't have to test particularly large sets if input data,
33 // validating that the algorithm can process at least two triples should be sufficient.
34 //
35 TEST(rfc_4648_tests_encode)
36 {
37 // These tests are what base64 RFC 4648 proposes.
38 {
39 std::vector<unsigned char> str1;
40 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("")), utility::conversions::to_base64(str1));
41 }
42 {
43 std::vector<unsigned char> str1;
44 str1.push_back('f');
45 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zg==")), utility::conversions::to_base64(str1));
46 }
47 {
48 std::vector<unsigned char> str1;
49 str1.push_back('f');
50 str1.push_back('o');
51 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm8=")), utility::conversions::to_base64(str1));
52 }
53 {
54 std::vector<unsigned char> str1;
55 str1.push_back('f');
56 str1.push_back('o');
57 str1.push_back('o');
58 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9v")), utility::conversions::to_base64(str1));
59 }
60 {
61 std::vector<unsigned char> str1;
62 str1.push_back('f');
63 str1.push_back('o');
64 str1.push_back('o');
65 str1.push_back('b');
66 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYg==")), utility::conversions::to_base64(str1));
67 }
68 {
69 std::vector<unsigned char> str1;
70 str1.push_back('f');
71 str1.push_back('o');
72 str1.push_back('o');
73 str1.push_back('b');
74 str1.push_back('a');
75 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmE=")), utility::conversions::to_base64(str1));
76 }
77 {
78 std::vector<unsigned char> str1;
79 str1.push_back('f');
80 str1.push_back('o');
81 str1.push_back('o');
82 str1.push_back('b');
83 str1.push_back('a');
84 str1.push_back('r');
85 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm9vYmFy")), utility::conversions::to_base64(str1));
86 }
87 }
88
89 TEST(rfc_4648_tests_decode)
90 {
91 // These tests are what base64 RFC 4648 proposes.
92 {
93 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR(""));
94 VERIFY_ARE_EQUAL(0u, str1.size());
95 }
96 {
97 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zg=="));
98 VERIFY_ARE_EQUAL(1u, str1.size());
99 VERIFY_ARE_EQUAL('f', str1[0]);
100 }
101 {
102 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm8="));
103 VERIFY_ARE_EQUAL(2u, str1.size());
104 VERIFY_ARE_EQUAL('f', str1[0]);
105 VERIFY_ARE_EQUAL('o', str1[1]);
106 }
107 {
108 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9v"));
109 VERIFY_ARE_EQUAL(3u, str1.size());
110 VERIFY_ARE_EQUAL('f', str1[0]);
111 VERIFY_ARE_EQUAL('o', str1[1]);
112 VERIFY_ARE_EQUAL('o', str1[2]);
113 }
114 {
115 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYg=="));
116 VERIFY_ARE_EQUAL(4u, str1.size());
117 VERIFY_ARE_EQUAL('f', str1[0]);
118 VERIFY_ARE_EQUAL('o', str1[1]);
119 VERIFY_ARE_EQUAL('o', str1[2]);
120 VERIFY_ARE_EQUAL('b', str1[3]);
121 }
122 {
123 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmE="));
124 VERIFY_ARE_EQUAL(5u, str1.size());
125 VERIFY_ARE_EQUAL('f', str1[0]);
126 VERIFY_ARE_EQUAL('o', str1[1]);
127 VERIFY_ARE_EQUAL('o', str1[2]);
128 VERIFY_ARE_EQUAL('b', str1[3]);
129 VERIFY_ARE_EQUAL('a', str1[4]);
130 }
131 {
132 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm9vYmFy"));
133 VERIFY_ARE_EQUAL(6u, str1.size());
134 VERIFY_ARE_EQUAL('f', str1[0]);
135 VERIFY_ARE_EQUAL('o', str1[1]);
136 VERIFY_ARE_EQUAL('o', str1[2]);
137 VERIFY_ARE_EQUAL('b', str1[3]);
138 VERIFY_ARE_EQUAL('a', str1[4]);
139 VERIFY_ARE_EQUAL('r', str1[5]);
140 }
141 }
142
143 TEST(additional_encode)
144 {
145 {
146 // Check '/' encoding
147 std::vector<unsigned char> str1;
148 str1.push_back(254);
149 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("/g==")), utility::conversions::to_base64(str1));
150 }
151 {
152 // Check '+' encoding
153 std::vector<unsigned char> str1;
154 str1.push_back(250);
155 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("+g==")), utility::conversions::to_base64(str1));
156 }
157 {
158 std::vector<unsigned char> str1;
159 str1.push_back('f');
160 str1.push_back('o');
161 str1.push_back(239);
162 str1.push_back('b');
163 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Zm/vYg==")), utility::conversions::to_base64(str1));
164 }
165 {
166 std::vector<unsigned char> str1;
167 str1.push_back('g');
168 str1.push_back(239);
169 str1.push_back('o');
170 str1.push_back('b');
171 VERIFY_ARE_EQUAL(string_t(_XPLATSTR("Z+9vYg==")), utility::conversions::to_base64(str1));
172 }
173 }
174
175 TEST(additional_decode)
176 {
177 // Tests beyond what the RFC recommends.
178 {
179 // Check '/' decoding
180 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("/g=="));
181 VERIFY_ARE_EQUAL(1u, str1.size());
182 VERIFY_ARE_EQUAL(254u, str1[0]);
183 }
184 {
185 // Check '+' decoding
186 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("+g=="));
187 VERIFY_ARE_EQUAL(1u, str1.size());
188 VERIFY_ARE_EQUAL(250u, str1[0]);
189 }
190 {
191 // Check '/' decoding
192 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Zm/vYg=="));
193 VERIFY_ARE_EQUAL(4u, str1.size());
194 VERIFY_ARE_EQUAL('f', str1[0]);
195 VERIFY_ARE_EQUAL('o', str1[1]);
196 VERIFY_ARE_EQUAL(239, str1[2]);
197 VERIFY_ARE_EQUAL('b', str1[3]);
198 }
199 {
200 // Check '+' decoding
201 std::vector<unsigned char> str1 = utility::conversions::from_base64(_XPLATSTR("Z+9vYg=="));
202 VERIFY_ARE_EQUAL(4u, str1.size());
203 VERIFY_ARE_EQUAL('g', str1[0]);
204 VERIFY_ARE_EQUAL(239, str1[1]);
205 VERIFY_ARE_EQUAL('o', str1[2]);
206 VERIFY_ARE_EQUAL('b', str1[3]);
207 }
208 {
209 // Check the whole base64 alphabet
210 std::vector<unsigned char> str1 = utility::conversions::from_base64(
211 _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"));
212 VERIFY_ARE_EQUAL(48u, str1.size());
213 }
214 }
215
216 TEST(bad_decode)
217 {
218 // These tests are for input that should be disallowed by a very strict decoder, but
219 // the available APIs on Windows accept them, as does glib, which is used on Linux.
220
221 // Invalid character before padding, unused ones.
222 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q==")), std::runtime_error);
223 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Zm9vYmD=")), std::runtime_error);
224
225 // CRLF in the middle.
226 VERIFY_THROWS(utility::conversions::from_base64(
227 _XPLATSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\nabcdefghijklmnopqrstuvwxyz\r\n0123456789+/")),
228 std::runtime_error);
229
230 // Not the right length.
231 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q")), std::runtime_error);
232 // Characters not in the alphabet
233 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("$%#@")), std::runtime_error);
234 // Too much padding at the end.
235 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("/q=========")), std::runtime_error);
236 // Valid strings, concatenated
237 VERIFY_THROWS(utility::conversions::from_base64(_XPLATSTR("Z+9vYg==Z+9vYg==")), std::runtime_error);
238 }
239
240 TEST(large_string)
241 {
242 const size_t size = 64 * 1024;
243
244 std::vector<unsigned char> data(size);
245 for (auto i = 0u; i < size; ++i)
246 {
247 data[i] = (unsigned char)(rand() & 0xFF);
248 }
249
250 auto string = utility::conversions::to_base64(data);
251 auto data2 = utility::conversions::from_base64(string);
252
253 VERIFY_ARE_EQUAL(data, data2);
254 }
255
256 } // SUITE(base64)
257
258 } // namespace utils_tests
259 } // namespace functional
260 } // namespace tests
261