Mirror of BoringSSL (grpc依赖)
https://boringssl.googlesource.com/boringssl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.4 KiB
307 lines
9.4 KiB
/* Copyright (c) 2014, Google Inc. |
|
* |
|
* Permission to use, copy, modify, and/or distribute this software for any |
|
* purpose with or without fee is hereby granted, provided that the above |
|
* copyright notice and this permission notice appear in all copies. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
|
|
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include <string> |
|
#include <vector> |
|
|
|
#include <gtest/gtest.h> |
|
|
|
#include <openssl/base64.h> |
|
#include <openssl/crypto.h> |
|
#include <openssl/err.h> |
|
|
|
#include "../internal.h" |
|
#include "../test/test_util.h" |
|
|
|
|
|
enum encoding_relation { |
|
// canonical indicates that the encoding is the expected encoding of the |
|
// input. |
|
canonical, |
|
// valid indicates that the encoding is /a/ valid encoding of the input, but |
|
// need not be the canonical one. |
|
valid, |
|
// invalid indicates that the encoded data is valid. |
|
invalid, |
|
}; |
|
|
|
struct Base64TestVector { |
|
enum encoding_relation relation; |
|
const char *decoded; |
|
const char *encoded; |
|
}; |
|
|
|
// Test vectors from RFC 4648. |
|
static const Base64TestVector kTestVectors[] = { |
|
{canonical, "", ""}, |
|
{canonical, "f", "Zg==\n"}, |
|
{canonical, "fo", "Zm8=\n"}, |
|
{canonical, "foo", "Zm9v\n"}, |
|
{canonical, "foob", "Zm9vYg==\n"}, |
|
{canonical, "fooba", "Zm9vYmE=\n"}, |
|
{canonical, "foobar", "Zm9vYmFy\n"}, |
|
{valid, "foobar", "Zm9vYmFy\n\n"}, |
|
{valid, "foobar", " Zm9vYmFy\n\n"}, |
|
{valid, "foobar", " Z m 9 v Y m F y\n\n"}, |
|
{invalid, "", "Zm9vYmFy=\n"}, |
|
{invalid, "", "Zm9vYmFy==\n"}, |
|
{invalid, "", "Zm9vYmFy===\n"}, |
|
{invalid, "", "Z"}, |
|
{invalid, "", "Z\n"}, |
|
{invalid, "", "ab!c"}, |
|
{invalid, "", "ab=c"}, |
|
{invalid, "", "abc"}, |
|
|
|
{canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"}, |
|
{valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"}, |
|
{valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"}, |
|
{invalid, "", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"}, |
|
{canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh" |
|
"4eHh4eHh4\n"}, |
|
{canonical, |
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh" |
|
"4eHh4eHh4eHh4eA==\n"}, |
|
{valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh" |
|
"4eHh4eHh4eHh4eA==\n"}, |
|
{valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e" |
|
"Hh4eHh4eHh4eA==\n"}, |
|
{invalid, "", |
|
"eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==" |
|
"\neHh4eHh4eHh4eHh4eHh4eHh4\n"}, |
|
|
|
// A '-' has traditionally been treated as the end of the data by OpenSSL |
|
// and anything following would be ignored. BoringSSL does not accept this |
|
// non-standard extension. |
|
{invalid, "", "Zm9vYmFy-anythinggoes"}, |
|
{invalid, "", "Zm9vYmFy\n-anythinggoes"}, |
|
|
|
// CVE-2015-0292 |
|
{invalid, "", |
|
"ZW5jb2RlIG1lCg===========================================================" |
|
"=======\n"}, |
|
}; |
|
|
|
class Base64Test : public testing::TestWithParam<Base64TestVector> {}; |
|
|
|
INSTANTIATE_TEST_SUITE_P(All, Base64Test, testing::ValuesIn(kTestVectors)); |
|
|
|
// RemoveNewlines returns a copy of |in| with all '\n' characters removed. |
|
static std::string RemoveNewlines(const char *in) { |
|
std::string ret; |
|
const size_t in_len = strlen(in); |
|
|
|
for (size_t i = 0; i < in_len; i++) { |
|
if (in[i] != '\n') { |
|
ret.push_back(in[i]); |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
TEST_P(Base64Test, EncodeBlock) { |
|
const Base64TestVector &t = GetParam(); |
|
if (t.relation != canonical) { |
|
return; |
|
} |
|
|
|
const size_t decoded_len = strlen(t.decoded); |
|
size_t max_encoded_len; |
|
ASSERT_TRUE(EVP_EncodedLength(&max_encoded_len, decoded_len)); |
|
|
|
std::vector<uint8_t> out_vec(max_encoded_len); |
|
uint8_t *out = out_vec.data(); |
|
size_t len = EVP_EncodeBlock(out, (const uint8_t *)t.decoded, decoded_len); |
|
|
|
std::string encoded(RemoveNewlines(t.encoded)); |
|
EXPECT_EQ(Bytes(encoded), Bytes(out, len)); |
|
} |
|
|
|
TEST_P(Base64Test, DecodeBase64) { |
|
const Base64TestVector &t = GetParam(); |
|
if (t.relation == valid) { |
|
// The non-canonical encodings will generally have odd whitespace etc |
|
// that |EVP_DecodeBase64| will reject. |
|
return; |
|
} |
|
|
|
const std::string encoded(RemoveNewlines(t.encoded)); |
|
std::vector<uint8_t> out_vec(encoded.size()); |
|
uint8_t *out = out_vec.data(); |
|
|
|
size_t len; |
|
int ok = EVP_DecodeBase64(out, &len, out_vec.size(), |
|
(const uint8_t *)encoded.data(), encoded.size()); |
|
|
|
if (t.relation == invalid) { |
|
EXPECT_FALSE(ok); |
|
} else if (t.relation == canonical) { |
|
ASSERT_TRUE(ok); |
|
EXPECT_EQ(Bytes(t.decoded), Bytes(out, len)); |
|
} |
|
} |
|
|
|
TEST_P(Base64Test, DecodeBlock) { |
|
const Base64TestVector &t = GetParam(); |
|
if (t.relation != canonical) { |
|
return; |
|
} |
|
|
|
std::string encoded(RemoveNewlines(t.encoded)); |
|
|
|
std::vector<uint8_t> out_vec(encoded.size()); |
|
uint8_t *out = out_vec.data(); |
|
|
|
// Test that the padding behavior of the deprecated API is preserved. |
|
int ret = |
|
EVP_DecodeBlock(out, (const uint8_t *)encoded.data(), encoded.size()); |
|
ASSERT_GE(ret, 0); |
|
// EVP_DecodeBlock should ignore padding. |
|
ASSERT_EQ(0, ret % 3); |
|
size_t expected_len = strlen(t.decoded); |
|
if (expected_len % 3 != 0) { |
|
ret -= 3 - (expected_len % 3); |
|
} |
|
EXPECT_EQ(Bytes(t.decoded), Bytes(out, static_cast<size_t>(ret))); |
|
} |
|
|
|
TEST_P(Base64Test, EncodeDecode) { |
|
const Base64TestVector &t = GetParam(); |
|
|
|
EVP_ENCODE_CTX ctx; |
|
const size_t decoded_len = strlen(t.decoded); |
|
|
|
if (t.relation == canonical) { |
|
size_t max_encoded_len; |
|
ASSERT_TRUE(EVP_EncodedLength(&max_encoded_len, decoded_len)); |
|
|
|
// EVP_EncodeUpdate will output new lines every 64 bytes of output so we |
|
// need slightly more than |EVP_EncodedLength| returns. */ |
|
max_encoded_len += (max_encoded_len + 63) >> 6; |
|
std::vector<uint8_t> out_vec(max_encoded_len); |
|
uint8_t *out = out_vec.data(); |
|
|
|
EVP_EncodeInit(&ctx); |
|
|
|
int out_len; |
|
EVP_EncodeUpdate(&ctx, out, &out_len, |
|
reinterpret_cast<const uint8_t *>(t.decoded), |
|
decoded_len); |
|
size_t total = out_len; |
|
|
|
EVP_EncodeFinal(&ctx, out + total, &out_len); |
|
total += out_len; |
|
|
|
EXPECT_EQ(Bytes(t.encoded), Bytes(out, total)); |
|
} |
|
|
|
std::vector<uint8_t> out_vec(strlen(t.encoded)); |
|
uint8_t *out = out_vec.data(); |
|
|
|
EVP_DecodeInit(&ctx); |
|
int out_len; |
|
size_t total = 0; |
|
int ret = EVP_DecodeUpdate(&ctx, out, &out_len, |
|
reinterpret_cast<const uint8_t *>(t.encoded), |
|
strlen(t.encoded)); |
|
if (ret != -1) { |
|
total = out_len; |
|
ret = EVP_DecodeFinal(&ctx, out + total, &out_len); |
|
total += out_len; |
|
} |
|
|
|
switch (t.relation) { |
|
case canonical: |
|
case valid: |
|
ASSERT_NE(-1, ret); |
|
EXPECT_EQ(Bytes(t.decoded), Bytes(out, total)); |
|
break; |
|
|
|
case invalid: |
|
EXPECT_EQ(-1, ret); |
|
break; |
|
} |
|
} |
|
|
|
TEST_P(Base64Test, DecodeUpdateStreaming) { |
|
const Base64TestVector &t = GetParam(); |
|
if (t.relation == invalid) { |
|
return; |
|
} |
|
|
|
const size_t encoded_len = strlen(t.encoded); |
|
|
|
std::vector<uint8_t> out(encoded_len); |
|
|
|
for (size_t chunk_size = 1; chunk_size <= encoded_len; chunk_size++) { |
|
SCOPED_TRACE(chunk_size); |
|
size_t out_len = 0; |
|
EVP_ENCODE_CTX ctx; |
|
EVP_DecodeInit(&ctx); |
|
|
|
for (size_t i = 0; i < encoded_len;) { |
|
size_t todo = encoded_len - i; |
|
if (todo > chunk_size) { |
|
todo = chunk_size; |
|
} |
|
|
|
int bytes_written; |
|
int ret = EVP_DecodeUpdate( |
|
&ctx, out.data() + out_len, &bytes_written, |
|
reinterpret_cast<const uint8_t *>(t.encoded + i), todo); |
|
i += todo; |
|
|
|
switch (ret) { |
|
case -1: |
|
FAIL() << "EVP_DecodeUpdate failed"; |
|
case 0: |
|
out_len += bytes_written; |
|
if (i == encoded_len || |
|
(i + 1 == encoded_len && t.encoded[i] == '\n') || |
|
// If there was an '-' in the input (which means “EOF”) then |
|
// this loop will continue to test that |EVP_DecodeUpdate| will |
|
// ignore the remainder of the input. |
|
strchr(t.encoded, '-') != nullptr) { |
|
break; |
|
} |
|
|
|
FAIL() |
|
<< "EVP_DecodeUpdate returned zero before end of encoded data."; |
|
case 1: |
|
out_len += bytes_written; |
|
break; |
|
default: |
|
FAIL() << "Invalid return value " << ret; |
|
} |
|
} |
|
|
|
int bytes_written; |
|
int ret = EVP_DecodeFinal(&ctx, out.data() + out_len, &bytes_written); |
|
ASSERT_NE(ret, -1); |
|
out_len += bytes_written; |
|
|
|
EXPECT_EQ(Bytes(t.decoded), Bytes(out.data(), out_len)); |
|
} |
|
}
|
|
|