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