• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2014 The BoringSSL Authors
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <string>
19 #include <vector>
20 
21 #include <gtest/gtest.h>
22 
23 #include <openssl/base64.h>
24 #include <openssl/crypto.h>
25 #include <openssl/err.h>
26 
27 #include "../internal.h"
28 #include "../test/test_util.h"
29 
30 
31 enum encoding_relation {
32   // canonical indicates that the encoding is the expected encoding of the
33   // input.
34   canonical,
35   // valid indicates that the encoding is /a/ valid encoding of the input, but
36   // need not be the canonical one.
37   valid,
38   // invalid indicates that the encoded data is valid.
39   invalid,
40 };
41 
42 struct Base64TestVector {
43   enum encoding_relation relation;
44   const char *decoded;
45   const char *encoded;
46 };
47 
48 static const Base64TestVector kTestVectors[] = {
49     // Test vectors from RFC 4648, section 10.
50     {canonical, "", ""},
51     {canonical, "f", "Zg==\n"},
52     {canonical, "fo", "Zm8=\n"},
53     {canonical, "foo", "Zm9v\n"},
54     {canonical, "foob", "Zm9vYg==\n"},
55     {canonical, "fooba", "Zm9vYmE=\n"},
56     {canonical, "foobar", "Zm9vYmFy\n"},
57 
58     {invalid, "", "Zm9vYmFy=\n"},
59     {invalid, "", "Zm9vYmFy==\n"},
60     {invalid, "", "Zm9vYmFy===\n"},
61 
62     // valid non-canonical encodings due to arbitrary whitespace
63     {valid, "foobar", "Zm9vYmFy\n\n"},
64     {valid, "foobar", " Zm9vYmFy\n\n"},
65     {valid, "foobar", " Z m 9 v Y m F y\n\n"},
66     {valid, "foobar", "Zm9vYmFy\r\n"},
67 
68     // The following "valid" encodings are arguably invalid, but they are
69     // commonly accepted by parsers, in particular by OpenSSL.
70     {valid, "v", "dv==\n"},
71     {canonical, "w", "dw==\n"},
72     {valid, "w", "dx==\n"},
73     {valid, "w", "d+==\n"},
74     {valid, "w", "d/==\n"},
75     {invalid, "", "d===\n"},
76     {canonical, "w`", "d2A=\n"},
77     {valid, "w`", "d2B=\n"},
78     {valid, "w`", "d2C=\n"},
79     {valid, "w`", "d2D=\n"},
80     {canonical, "wa", "d2E=\n"},
81 
82     {invalid, "", "Z"},
83     {invalid, "", "Z\n"},
84     {invalid, "", "ab!c"},
85     {invalid, "", "ab=c"},
86     {invalid, "", "abc"},
87 
88     {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
89      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"},
90     {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
91      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"},
92     {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
93      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"},
94     {invalid, "",
95      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"},
96     {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
97      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
98      "4eHh4eHh4\n"},
99     {canonical,
100      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
101      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
102      "4eHh4eHh4eHh4eA==\n"},
103     {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
104      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh"
105      "4eHh4eHh4eHh4eA==\n"},
106     {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
107      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e"
108      "Hh4eHh4eHh4eA==\n"},
109     {invalid, "",
110      "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=="
111      "\neHh4eHh4eHh4eHh4eHh4eHh4\n"},
112 
113     // A '-' has traditionally been treated as the end of the data by OpenSSL
114     // and anything following would be ignored. BoringSSL does not accept this
115     // non-standard extension.
116     {invalid, "", "Zm9vYmFy-anythinggoes"},
117     {invalid, "", "Zm9vYmFy\n-anythinggoes"},
118 
119     // CVE-2015-0292
120     {invalid, "",
121      "ZW5jb2RlIG1lCg==========================================================="
122      "=======\n"},
123 };
124 
125 class Base64Test : public testing::TestWithParam<Base64TestVector> {};
126 
127 INSTANTIATE_TEST_SUITE_P(All, Base64Test, testing::ValuesIn(kTestVectors));
128 
129 // RemoveNewlines returns a copy of |in| with all '\n' characters removed.
RemoveNewlines(const char * in)130 static std::string RemoveNewlines(const char *in) {
131   std::string ret;
132   const size_t in_len = strlen(in);
133 
134   for (size_t i = 0; i < in_len; i++) {
135     if (in[i] != '\n') {
136       ret.push_back(in[i]);
137     }
138   }
139 
140   return ret;
141 }
142 
TEST_P(Base64Test,EncodeBlock)143 TEST_P(Base64Test, EncodeBlock) {
144   const Base64TestVector &t = GetParam();
145   if (t.relation != canonical) {
146     return;
147   }
148 
149   const size_t decoded_len = strlen(t.decoded);
150   size_t max_encoded_len;
151   ASSERT_TRUE(EVP_EncodedLength(&max_encoded_len, decoded_len));
152 
153   std::vector<uint8_t> out_vec(max_encoded_len);
154   uint8_t *out = out_vec.data();
155   size_t len = EVP_EncodeBlock(out, (const uint8_t *)t.decoded, decoded_len);
156 
157   std::string encoded(RemoveNewlines(t.encoded));
158   EXPECT_EQ(Bytes(encoded), Bytes(out, len));
159 }
160 
TEST_P(Base64Test,DecodeBase64)161 TEST_P(Base64Test, DecodeBase64) {
162   const Base64TestVector &t = GetParam();
163   if (t.relation == valid) {
164     // The non-canonical encodings will generally have odd whitespace etc
165     // that |EVP_DecodeBase64| will reject.
166     return;
167   }
168 
169   const std::string encoded(RemoveNewlines(t.encoded));
170   std::vector<uint8_t> out_vec(encoded.size());
171   uint8_t *out = out_vec.data();
172 
173   size_t len;
174   int ok = EVP_DecodeBase64(out, &len, out_vec.size(),
175                             (const uint8_t *)encoded.data(), encoded.size());
176 
177   if (t.relation == invalid) {
178     EXPECT_FALSE(ok);
179   } else if (t.relation == canonical) {
180     ASSERT_TRUE(ok);
181     EXPECT_EQ(Bytes(t.decoded), Bytes(out, len));
182   }
183 }
184 
TEST_P(Base64Test,DecodeBlock)185 TEST_P(Base64Test, DecodeBlock) {
186   const Base64TestVector &t = GetParam();
187   if (t.relation != canonical) {
188     return;
189   }
190 
191   std::string encoded(RemoveNewlines(t.encoded));
192 
193   std::vector<uint8_t> out_vec(encoded.size());
194   uint8_t *out = out_vec.data();
195 
196   // Test that the padding behavior of the deprecated API is preserved.
197   int ret =
198       EVP_DecodeBlock(out, (const uint8_t *)encoded.data(), encoded.size());
199   ASSERT_GE(ret, 0);
200   // EVP_DecodeBlock should ignore padding.
201   ASSERT_EQ(0, ret % 3);
202   size_t expected_len = strlen(t.decoded);
203   if (expected_len % 3 != 0) {
204     ret -= 3 - (expected_len % 3);
205   }
206   EXPECT_EQ(Bytes(t.decoded), Bytes(out, static_cast<size_t>(ret)));
207 }
208 
TEST_P(Base64Test,EncodeDecode)209 TEST_P(Base64Test, EncodeDecode) {
210   const Base64TestVector &t = GetParam();
211 
212   EVP_ENCODE_CTX ctx;
213   const size_t decoded_len = strlen(t.decoded);
214 
215   if (t.relation == canonical) {
216     size_t max_encoded_len;
217     ASSERT_TRUE(EVP_EncodedLength(&max_encoded_len, decoded_len));
218 
219     // EVP_EncodeUpdate will output new lines every 64 bytes of output so we
220     // need slightly more than |EVP_EncodedLength| returns. */
221     max_encoded_len += (max_encoded_len + 63) >> 6;
222     std::vector<uint8_t> out_vec(max_encoded_len);
223     uint8_t *out = out_vec.data();
224 
225     EVP_EncodeInit(&ctx);
226 
227     int out_len;
228     EVP_EncodeUpdate(&ctx, out, &out_len,
229                      reinterpret_cast<const uint8_t *>(t.decoded),
230                      decoded_len);
231     size_t total = out_len;
232 
233     EVP_EncodeFinal(&ctx, out + total, &out_len);
234     total += out_len;
235 
236     EXPECT_EQ(Bytes(t.encoded), Bytes(out, total));
237   }
238 
239   std::vector<uint8_t> out_vec(strlen(t.encoded));
240   uint8_t *out = out_vec.data();
241 
242   EVP_DecodeInit(&ctx);
243   int out_len;
244   size_t total = 0;
245   int ret = EVP_DecodeUpdate(&ctx, out, &out_len,
246                              reinterpret_cast<const uint8_t *>(t.encoded),
247                              strlen(t.encoded));
248   if (ret != -1) {
249     total = out_len;
250     ret = EVP_DecodeFinal(&ctx, out + total, &out_len);
251     total += out_len;
252   }
253 
254   switch (t.relation) {
255     case canonical:
256     case valid:
257       ASSERT_NE(-1, ret);
258       EXPECT_EQ(Bytes(t.decoded), Bytes(out, total));
259       break;
260 
261     case invalid:
262       EXPECT_EQ(-1, ret);
263       break;
264   }
265 }
266 
TEST_P(Base64Test,DecodeUpdateStreaming)267 TEST_P(Base64Test, DecodeUpdateStreaming) {
268   const Base64TestVector &t = GetParam();
269   if (t.relation == invalid) {
270     return;
271   }
272 
273   const size_t encoded_len = strlen(t.encoded);
274 
275   std::vector<uint8_t> out(encoded_len);
276 
277   for (size_t chunk_size = 1; chunk_size <= encoded_len; chunk_size++) {
278     SCOPED_TRACE(chunk_size);
279     size_t out_len = 0;
280     EVP_ENCODE_CTX ctx;
281     EVP_DecodeInit(&ctx);
282 
283     for (size_t i = 0; i < encoded_len;) {
284       size_t todo = encoded_len - i;
285       if (todo > chunk_size) {
286         todo = chunk_size;
287       }
288 
289       int bytes_written;
290       int ret = EVP_DecodeUpdate(
291           &ctx, out.data() + out_len, &bytes_written,
292           reinterpret_cast<const uint8_t *>(t.encoded + i), todo);
293       i += todo;
294 
295       switch (ret) {
296         case -1:
297           FAIL() << "EVP_DecodeUpdate failed";
298         case 0:
299           out_len += bytes_written;
300           if (i == encoded_len ||
301               (i + 1 == encoded_len && t.encoded[i] == '\n') ||
302               // If there was an '-' in the input (which means “EOF”) then
303               // this loop will continue to test that |EVP_DecodeUpdate| will
304               // ignore the remainder of the input.
305               strchr(t.encoded, '-') != nullptr) {
306             break;
307           }
308 
309           FAIL()
310               << "EVP_DecodeUpdate returned zero before end of encoded data.";
311         case 1:
312           out_len += bytes_written;
313           break;
314         default:
315           FAIL() << "Invalid return value " << ret;
316       }
317     }
318 
319     int bytes_written;
320     int ret = EVP_DecodeFinal(&ctx, out.data() + out_len, &bytes_written);
321     ASSERT_NE(ret, -1);
322     out_len += bytes_written;
323 
324     EXPECT_EQ(Bytes(t.decoded), Bytes(out.data(), out_len));
325   }
326 }
327