blob: 8adb9923bf989143683fb1bc24c26cf77a9fbad9 [file]
// Copyright 2018 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/md5.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "../test/abi_test.h"
#include "../test/test_util.h"
#include "internal.h"
BSSL_NAMESPACE_BEGIN
namespace {
#if defined(MD5_ASM) && defined(SUPPORTS_ABI_TEST)
TEST(MD5Test, ABI) {
MD5_CTX ctx;
MD5_Init(&ctx);
static const uint8_t kBuf[MD5_CBLOCK * 8] = {0};
CHECK_ABI(md5_block_asm_data_order, ctx.h, kBuf, 1);
CHECK_ABI(md5_block_asm_data_order, ctx.h, kBuf, 2);
CHECK_ABI(md5_block_asm_data_order, ctx.h, kBuf, 4);
CHECK_ABI(md5_block_asm_data_order, ctx.h, kBuf, 8);
}
#endif // MD5_ASM && SUPPORTS_ABI_TEST
// Validate that computing MD5 at once or in 2 steps with a context
// serialization in-between provide the same result.
TEST(MD5Test, MarshalAndParseStateRoundTrip) {
// Prepare an input, pseudo-random content doesn't matter much.
std::vector<uint8_t> input(200);
for (size_t i = 0; i < input.size(); i++) {
input[i] = static_cast<uint8_t>(i * 37 + 13);
}
// Compute MD5 on whole input at once.
uint8_t expected_digest[MD5_DIGEST_LENGTH];
MD5(input.data(), input.size(), expected_digest);
// Test split position at every possible place between 0 and input size.
// The input size of 200 guarantees covering at least 2*MD5_CBLOCK (==2*64)
// plus some margin.
for (size_t split = 0; split <= input.size(); split++) {
SCOPED_TRACE(split);
// Compute MD5 on first part of the split.
MD5_CTX ctx;
ASSERT_EQ(MD5_Init(&ctx), 1);
ASSERT_EQ(MD5_Update(&ctx, input.data(), split), 1);
// Serialize the state.
uint8_t state[MD5_STATE_MAX_BYTES];
size_t state_len = 0;
ASSERT_EQ(MD5_marshal_state(&ctx, state, &state_len), 1);
EXPECT_EQ(state_len, 24 + (split % MD5_CBLOCK));
// Restore the state and add the second part of the split.
MD5_CTX restored;
ASSERT_EQ(MD5_parse_state(&restored, state, state_len), 1);
ASSERT_EQ(MD5_Update(&restored, input.data() + split, input.size() - split),
1);
// Verify that both MD5 computed match.
uint8_t actual_digest[MD5_DIGEST_LENGTH];
ASSERT_EQ(MD5_Final(actual_digest, &restored), 1);
EXPECT_EQ(Bytes(actual_digest, MD5_DIGEST_LENGTH),
Bytes(expected_digest, MD5_DIGEST_LENGTH));
}
}
// Verify that the serialization doesn't change.
// Because serialization is used to compute MD5 across job updates, it's
// important that it remains backward and forward compatible.
TEST(MD5Test, MarshalAndParseStateWireFormat) {
struct TestVector {
const char *name;
std::string input;
size_t input_repeat;
const char *expected_state_hex;
const char *expected_digest_hex;
std::string continuation;
const char *expected_continuation_digest_hex;
};
const TestVector kTestVectors[] = {
{
"EmptyInput",
"",
1,
"0123456789abcdeffedcba98765432100000000000000000",
"d41d8cd98f00b204e9800998ecf8427e",
"Hello, World!",
"65a8e27d8879283831b664bd8b7f0ad4",
},
{
"ShortInput_abc",
"abc",
1,
"0123456789abcdeffedcba98765432101800000000000000616263",
"900150983cd24fb0d6963f7d28e17f72",
"xyz",
"70fb874a43097a25234382390c0baeb3",
},
{
// 55 bytes (440 bits) + 1 byte pad (0x80) + 8 bytes length = 64B.
"Exact_1block_55bytes",
"1234567890123456789012345678901234567890123456789012345",
1,
"0123456789abcdeffedcba9876543210b801000000000000"
"31323334353637383930313233343536373839303132333435"
"36373839303132333435363738393031323334353637383930"
"3132333435",
"c9ccf168914a1bcfc3229f1948e67da0",
"xyz",
"fb463c0c8c6dd9330623d22e764b0c71",
},
{
// 56 bytes (448 bits) + 1 byte pad = 456 bits > 448; forces 2nd block
// during finalization.
"PaddingSpill_56bytes",
"12345678901234567890123456789012345678901234567890123456",
1,
"0123456789abcdeffedcba9876543210c001000000000000"
"31323334353637383930313233343536373839303132333435"
"36373839303132333435363738393031323334353637383930"
"313233343536",
"49f193adce178490e34d1b3a4ec0064c",
"xyz",
"f2d4136679b29ddb872955d2aa8e8484",
},
{
"OneByteBeforeBlockBoundary_63bytes",
std::string(63, 'a'),
1,
"0123456789abcdeffedcba9876543210f801000000000000"
"6161616161616161616161616161616161616161616161616161616161616161"
"61616161616161616161616161616161616161616161616161616161616161",
"b06521f39153d618550606be297466d5",
"xyz",
"d6ac91844477aed9d6a74c4451ddb443",
},
{
// State transformed, count=512 bits (0x200), buffer empty (0 bytes).
"ExactBlockBoundary_64bytes",
std::string(64, 'a'),
1,
"56ffd48962d95c1233deca6925e333000002000000000000",
"014842d480b571495a4a0363793f7367",
"xyz",
"d953efb81c869c70ee05f9c2e1bf0123",
},
{
"OneBytePastBlockBoundary_65bytes",
std::string(65, 'a'),
1,
"56ffd48962d95c1233deca6925e33300080200000000000061",
"c743a45e0d2e6a95cb859adae0248435",
"xyz",
"6f722413acec8914bdb1816a4b73f6c7",
},
{
"ExactTwoBlocksBoundary_128bytes",
std::string(128, 'a'),
1,
"d7991be0c609e6a1ea1dd2fe4f6b6bbe0004000000000000",
"e510683b3f5ffe4093d021808bc6ff70",
"xyz",
"514e7a94db9f189e947ec22967f9b6d2",
},
{
// 40 bytes followed by 24 bytes flushes on the 64-byte block
// boundary.
"MultiStepUpdateFlushingOnBlockBoundary",
std::string(40, 'a'),
1,
"0123456789abcdeffedcba98765432104001000000000000"
"6161616161616161616161616161616161616161616161616161616161616161"
"6161616161616161",
"4697843037d962f62a5a429e611e0f5f",
std::string(24, 'a'),
"014842d480b571495a4a0363793f7367",
},
{
// An input bigger than 512 MiB (536 MiB = 8576 * 64 KiB) requires
// more than 32 bits for the bit counter, ensuring the full 64-bit
// counter (Nh and Nl) is marshaled and parsed.
"MoreThan32bits",
std::string(64 * 1024, 'a'),
536 * 16,
"918ddea3ed0fb037455bbece751ff1ca0000000c01000000",
"f156cbc99685a811424ff1d18255fd23",
"xyz",
"2004333d9e2dc522d21d32ecf4893fc1",
},
};
for (const auto &tv : kTestVectors) {
SCOPED_TRACE(tv.name);
// Compute MD5 on `input` (repeated `input_repeat` times).
MD5_CTX ctx;
ASSERT_EQ(MD5_Init(&ctx), 1);
for (size_t i = 0; i < tv.input_repeat; i++) {
ASSERT_EQ(MD5_Update(&ctx, tv.input.data(), tv.input.size()), 1);
}
uint8_t state[MD5_STATE_MAX_BYTES];
size_t state_len = 0;
ASSERT_EQ(MD5_marshal_state(&ctx, state, &state_len), 1);
// Verify that serialized context is as expected (`expected_state_hex`).
std::vector<uint8_t> expected_state;
ASSERT_TRUE(DecodeHex(&expected_state, tv.expected_state_hex));
EXPECT_EQ(Bytes(state, state_len), Bytes(expected_state));
// Verify that finalizing `ctx` produces `expected_digest_hex`.
uint8_t digest[MD5_DIGEST_LENGTH];
ASSERT_EQ(MD5_Final(digest, &ctx), 1);
std::vector<uint8_t> expected_digest;
ASSERT_TRUE(DecodeHex(&expected_digest, tv.expected_digest_hex));
EXPECT_EQ(Bytes(digest, MD5_DIGEST_LENGTH), Bytes(expected_digest));
// Verify that parsing `expected_state` and finalizing without further
// updates also produces `expected_digest_hex` and re-marshals identically.
MD5_CTX restored;
ASSERT_EQ(MD5_parse_state(&restored, expected_state.data(),
expected_state.size()),
1);
uint8_t remarshaled_state[MD5_STATE_MAX_BYTES];
size_t remarshaled_len = 0;
ASSERT_EQ(MD5_marshal_state(&restored, remarshaled_state, &remarshaled_len),
1);
EXPECT_EQ(Bytes(remarshaled_state, remarshaled_len), Bytes(expected_state));
ASSERT_EQ(MD5_Final(digest, &restored), 1);
EXPECT_EQ(Bytes(digest, MD5_DIGEST_LENGTH), Bytes(expected_digest));
// Restore context again and extend computed MD5 with `continuation`.
ASSERT_EQ(MD5_parse_state(&restored, expected_state.data(),
expected_state.size()),
1);
ASSERT_EQ(
MD5_Update(&restored, tv.continuation.data(), tv.continuation.size()),
1);
// Verify that the resulting digest is `expected_continuation_digest_hex`.
ASSERT_EQ(MD5_Final(digest, &restored), 1);
std::vector<uint8_t> expected_continuation_digest;
ASSERT_TRUE(DecodeHex(&expected_continuation_digest,
tv.expected_continuation_digest_hex));
EXPECT_EQ(Bytes(digest, MD5_DIGEST_LENGTH),
Bytes(expected_continuation_digest));
}
}
TEST(MD5Test, ParseStateInvalid) {
MD5_CTX ctx;
ASSERT_EQ(MD5_Init(&ctx), 1);
ASSERT_EQ(MD5_Update(&ctx, "abc", 3), 1);
uint8_t initial_state[MD5_STATE_MAX_BYTES];
size_t initial_len = 0;
ASSERT_EQ(MD5_marshal_state(&ctx, initial_state, &initial_len), 1);
auto is_unmodified = [&]() {
uint8_t cur_state[MD5_STATE_MAX_BYTES];
size_t cur_len = 0;
return MD5_marshal_state(&ctx, cur_state, &cur_len) == 1 &&
Bytes(cur_state, cur_len) == Bytes(initial_state, initial_len);
};
MD5_CTX valid_ctx;
uint8_t state[MD5_STATE_MAX_BYTES + 1] = {0};
// Too short (< 24 bytes).
EXPECT_EQ(MD5_parse_state(&ctx, state, 0), 0);
EXPECT_TRUE(is_unmodified());
EXPECT_EQ(MD5_parse_state(&ctx, state, 23), 0);
EXPECT_TRUE(is_unmodified());
// Too long (> MD5_STATE_MAX_BYTES).
EXPECT_EQ(MD5_parse_state(&ctx, state, MD5_STATE_MAX_BYTES + 1), 0);
EXPECT_TRUE(is_unmodified());
// Valid 24-byte state with Nl = 0, Nh = 0.
EXPECT_EQ(MD5_parse_state(&valid_ctx, state, 24), 1);
// Extra trailing bytes when Nl = 0 (expects 24 bytes, given 25).
EXPECT_EQ(MD5_parse_state(&ctx, state, 25), 0);
EXPECT_TRUE(is_unmodified());
// Nl is not a multiple of 8 bits (low 3 bits non-zero).
state[16] = 1;
EXPECT_EQ(MD5_parse_state(&ctx, state, 24), 0);
EXPECT_TRUE(is_unmodified());
// Nl = 32 bits (4 buffered bytes -> expects 28 bytes total).
state[16] = 32;
EXPECT_EQ(MD5_parse_state(&ctx, state, 24), 0);
EXPECT_TRUE(is_unmodified());
EXPECT_EQ(MD5_parse_state(&ctx, state, 27), 0);
EXPECT_TRUE(is_unmodified());
EXPECT_EQ(MD5_parse_state(&valid_ctx, state, 28), 1);
EXPECT_EQ(MD5_parse_state(&ctx, state, 29), 0);
EXPECT_TRUE(is_unmodified());
}
} // namespace
BSSL_NAMESPACE_END