blob: dcbc1e5989d91485c7a83d041f8c5a3fd67ef48a [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/randomized_encoder.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr size_t kBitsPerByte = 8;
constexpr size_t kMaxLengthInBytes = 64;
constexpr size_t kMaxLengthInBits = kMaxLengthInBytes * kBitsPerByte;
// Get the |i|-th bit of |s| where |i| counts up from the 0-bit of the first
// character in |s|. It is expected that the caller guarantees that |i| is a
// valid bit-offset into |s|
bool GetBit(base::StringPiece s, size_t i) {
DCHECK_LT(i / kBitsPerByte, s.length());
return static_cast<bool>((s[i / kBitsPerByte]) & (1 << (i % kBitsPerByte)));
}
// This is a reference encoder implementation. This implementation performs the
// all bits encoding one full byte at a time and then packs the selected bits
// into a final output buffer.
std::string ReferenceEncodeImpl(base::StringPiece coins,
base::StringPiece noise,
base::StringPiece value,
size_t bit_offset,
size_t bit_stride) {
// Encode all of the bits.
std::string all_bits = noise.as_string();
size_t value_length = std::min(value.length(), kMaxLengthInBytes);
for (size_t i = 0; i < value_length; ++i) {
all_bits[i] = (value[i] & coins[i]) | (all_bits[i] & ~coins[i]);
}
// Select the only the ones matching bit_offset and bit_stride.
std::string output(kMaxLengthInBytes / bit_stride, 0);
size_t src_offset = bit_offset;
size_t dst_offset = 0;
while (src_offset < kMaxLengthInBits) {
bool bit_value = GetBit(all_bits, src_offset);
output[dst_offset / kBitsPerByte] |=
(bit_value << (dst_offset % kBitsPerByte));
src_offset += bit_stride;
dst_offset += 1;
}
return output;
}
// A test version of the RandomizedEncoder class. Exposes "ForTest" methods.
class TestRandomizedEncoder : public autofill::RandomizedEncoder {
public:
using RandomizedEncoder::RandomizedEncoder;
using RandomizedEncoder::GetCoins;
using RandomizedEncoder::GetNoise;
};
// Data structure used to drive the encoding test cases.
struct EncodeParams {
// The type of encoding to perform with the RandomizedEncoder.
autofill::AutofillRandomizedValue_EncodingType encoding_type;
// The bit offset to start from with the reference encoder.
size_t bit_offset;
// The bit stride to select the next bit to encode with the reference encoder.
size_t bit_stride;
};
// A table to test cases, mapping encoding scheme to the reference encoder.
const EncodeParams kEncodeParams[] = {
// One bit per byte. These all require 8 bytes to encode and have 8-bit
// strides, starting from a different initial bit offset.
{autofill::AutofillRandomizedValue_EncodingType_BIT_0, 0, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_1, 1, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_2, 2, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_3, 3, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_4, 4, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_5, 5, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_6, 6, 8},
{autofill::AutofillRandomizedValue_EncodingType_BIT_7, 7, 8},
// Four bits per byte. These require 32 bytes to encode and have 2-bit
// strides/
{autofill::AutofillRandomizedValue_EncodingType_EVEN_BITS, 0, 2},
{autofill::AutofillRandomizedValue_EncodingType_ODD_BITS, 1, 2},
// All bits per byte. This require 64 bytes to encode and has a 1-bit
// stride.
{autofill::AutofillRandomizedValue_EncodingType_ALL_BITS, 0u, 1},
};
using RandomizedEncoderTest = ::testing::TestWithParam<EncodeParams>;
} // namespace
TEST_P(RandomizedEncoderTest, Encode) {
const autofill::FormSignature form_signature = 0x1234567812345678;
const autofill::FieldSignature field_signature = 0xCAFEBABE;
const std::string data_type = "css_class";
const EncodeParams& params = GetParam();
const std::string value("This is some text for testing purposes.");
EXPECT_LT(value.length(), kMaxLengthInBytes);
TestRandomizedEncoder encoder("this is a secret", params.encoding_type);
// Encode the output string.
std::string actual_result =
encoder.Encode(form_signature, field_signature, data_type, value);
// Capture the coin and noise bits used for the form, field and metadata type.
std::string coins =
encoder.GetCoins(form_signature, field_signature, data_type);
std::string noise =
encoder.GetNoise(form_signature, field_signature, data_type);
// Use the reference encoder implementation to get the expected output.
std::string expected_result = ReferenceEncodeImpl(
coins, noise, value, params.bit_offset, params.bit_stride);
// The results should be the same.
EXPECT_EQ(kMaxLengthInBytes / params.bit_stride, actual_result.length());
EXPECT_EQ(expected_result, actual_result);
}
TEST_P(RandomizedEncoderTest, EncodeLarge) {
const autofill::FormSignature form_signature = 0x8765432187654321;
const autofill::FieldSignature field_signature = 0xDEADBEEF;
const std::string data_type = "html_name";
const EncodeParams& params = GetParam();
const std::string value(
"This is some text for testing purposes. It exceeds the maximum encoding "
"size. This serves to validate that truncation is performed. Lots and "
" or text. Yay!");
EXPECT_GT(value.length(), kMaxLengthInBytes);
TestRandomizedEncoder encoder("this is a secret", params.encoding_type);
// Encode the output string.
std::string actual_result =
encoder.Encode(form_signature, field_signature, data_type, value);
// Capture the coin and noise bits used for the form, field and metadata type.
std::string coins =
encoder.GetCoins(form_signature, field_signature, data_type);
std::string noise =
encoder.GetNoise(form_signature, field_signature, data_type);
// Use the reference encoder implementation to get the expected output.
std::string expected_result = ReferenceEncodeImpl(
coins, noise, value, params.bit_offset, params.bit_stride);
// The results should be the same.
EXPECT_EQ(kMaxLengthInBytes / params.bit_stride, actual_result.length());
EXPECT_EQ(expected_result, actual_result);
}
INSTANTIATE_TEST_CASE_P(All,
RandomizedEncoderTest,
::testing::ValuesIn(kEncodeParams));