Code Drop Two (Update One)

This is the second code drop for the white-box api reference
implementation and tests. This corrects the errors in the license
white-box reference implementation and implements the remaining
test cases.

It should be noted that there is one test case missing, the test case
for handling ChromeOS's unique policy settings.

In order to make the tests easier to create and read, a license
builder class was created and golden content and keys were wrapped in
their own classes.

How key errors are communicated was changed in the API.
WB_RESULT_NO_SUCH_KEY and WB_RESULT_WRONG_KEY_TYPE were merged into
WB_RESULT_KEY_UNAVAILABLE.
This commit is contained in:
Aaron Vaage
2020-05-26 19:46:26 -07:00
parent 77f7ef98c0
commit ab70a5e358
18 changed files with 2908 additions and 665 deletions

View File

@@ -6,127 +6,186 @@
#include <string>
#include <vector>
#include "api/license_builder.h"
#include "api/license_whitebox_test_base.h"
#include "api/test_data.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/include/gtest/gtest.h"
namespace {
namespace widevine {
// TODO: This needs to be the real response.
const uint8_t kResponse[] = {
0x1e, 0x70, 0xbd, 0xeb, 0x24, 0xf2, 0x9d, 0x05, 0xc5, 0xb5,
0xf4, 0xca, 0xe6, 0x1d, 0x01, 0x97, 0x29, 0xf4, 0xe0, 0x7c,
0xfd, 0xcc, 0x97, 0x8d, 0xc2, 0xbb, 0x2d, 0x9b, 0x6b, 0x45,
0x06, 0xbd, 0x2c, 0x66, 0x10, 0x42, 0x73, 0x8d, 0x88, 0x9b,
0x18, 0xcc, 0xcb, 0x7e, 0x43, 0x23, 0x06, 0xe9, 0x8f, 0x8f,
};
const size_t kResponseSize = sizeof(kResponse);
// TODO: This needs to be the actual signature.
const uint8_t kSignature[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t kSignatureSize = sizeof(kSignature);
// These tests assume that WB_License_Create() and
// WB_License_ProcessLicenseResponse() work as all tests will require a valid
// white-box instance with a valid license already loaded.
class LicenseWhiteboxVerifyRenewalResponseTest : public ::testing::Test {
class LicenseWhiteboxVerifyRenewalResponseTest
: public LicenseWhiteboxTestBase {
protected:
void SetUp() override {
const std::vector<uint8_t> init_data_ = GetLicenseInitData();
ASSERT_EQ(
WB_License_Create(init_data_.data(), init_data_.size(), &whitebox_),
WB_RESULT_OK);
LicenseWhiteboxTestBase::SetUp();
garbage_renewal_signature_ = Sign(garbage_renewal_message_);
}
void TearDown() override { WB_License_Delete(whitebox_); }
void LoadLicense(const std::vector<uint8_t>& padding) {
const auto signing_key = LicenseBuilder::DefaultSigningKey();
void LoadLicense() {
// TODO: Load the license here. It would be nice if we could do it in
// SetUp(), but since we need to support the WB_RESULT_INVALID_STATE test
// case, we need a way to not load a license.
// We need a license so that we can always have a valid signature for our
// message(s), but don't load the license as some test will need no
// license loaded.
LicenseBuilder builder;
builder.AddSigningKey(signing_key, padding);
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
}
WB_License_Whitebox* whitebox_ = nullptr;
std::vector<uint8_t> Sign(const std::vector<uint8_t>& message) {
const auto key = LicenseBuilder::DefaultSigningKey();
// The server signing key is the first half of the signing key.
std::string server_key = std::string(
key.begin(), key.begin() + crypto_util::kSigningKeySizeBytes);
const auto signature = crypto_util::CreateSignatureHmacSha256(
server_key, std::string(message.begin(), message.end()));
return std::vector<uint8_t>(signature.begin(), signature.end());
}
// Allow this to be mutable so that a test can corrupt it. This data is random
// and has no meaning.
std::vector<uint8_t> garbage_renewal_message_ = {
0xf1, 0x5c, 0xf1, 0x92, 0x73, 0x0c, 0xf9, 0x5d, 0x2b, 0x1e, 0x3f, 0x51,
0xb2, 0x75, 0xa1, 0xb3, 0xd3, 0xa8, 0x16, 0x83, 0x08, 0xf1, 0xe2, 0x47,
0x7b, 0x80, 0x37, 0xed, 0xf8, 0x8b, 0x1d, 0x79, 0x7f, 0xb0, 0xa1, 0xde,
0xcd, 0xba, 0xd4, 0x8f, 0xb7, 0x3c, 0x1a, 0x3f, 0x3e, 0x3a, 0xb4, 0xea,
0xd8, 0xd7, 0xa4, 0x65, 0xa1, 0x40, 0x87, 0xf6, 0xaa, 0xf4, 0xb1, 0x24,
0x17, 0xed, 0xf4, 0xca, 0x18, 0x51, 0x4a, 0x54, 0x3c, 0x73, 0xca, 0x45,
0x3e, 0xef, 0x39, 0x49, 0x65, 0xdd, 0x62, 0x11, 0x99, 0x13, 0x40, 0x67,
0x7f, 0xfb, 0x07, 0x09, 0x1e, 0xfe, 0x0e, 0xdc, 0xda, 0x0a, 0x85, 0x91,
0x15, 0x40, 0xa8, 0x7a, 0x0e, 0x76, 0xf6, 0xbe, 0x94, 0x2c, 0x70, 0xe9,
0x07, 0xea, 0xf8, 0x7a, 0xc3, 0x48, 0xe1, 0xcf, 0xf4, 0x7b, 0xd6, 0x27,
0xd7, 0x30, 0x6f, 0x18, 0xb3, 0x2d, 0x6a, 0x23,
};
// Allow this to be mutable so that we can initialize it in SetUp() but also
// so a test can corrupt it.
std::vector<uint8_t> garbage_renewal_signature_;
};
// TODO: Implement the success test case.
// TODO: Implement a test that uses a real serialized response. Once we have a
// real serialized response, we should update all the tests - except the
// SuccessForGarbageMessage - to use the real serialized response.
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) {
LoadLicense(LicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
SuccessWithSigningKeyPKSC8Padding) {
LoadLicense(LicenseBuilder::PKSC8Padding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullWhitebox) {
LoadLicense();
LoadLicense(LicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(nullptr, kResponse, kResponseSize,
kSignature, kSignatureSize),
WB_RESULT_INVALID_PARAMETER);
ASSERT_EQ(
WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullMessage) {
LoadLicense();
LoadLicense(LicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr, kResponseSize,
kSignature, kSignatureSize),
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr,
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForZeroMessageSize) {
LoadLicense();
LoadLicense(LicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, kResponse, 0,
kSignature, kSignatureSize),
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(), 0,
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForNullSignature) {
LoadLicense();
LoadLicense(LicenseBuilder::NoPadding());
ASSERT_EQ(WB_License_VerifyRenewalResponse(
whitebox_, kResponse, kResponseSize, nullptr, kSignatureSize),
whitebox_, garbage_renewal_message_.data(),
garbage_renewal_message_.size(), nullptr,
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidParameterForInvalidSignatureSize) {
// TODO: This test needs to be skipped for now as the check for signature size
// is done after we compute the signature. This requires a valid license to
// have been loaded.
GTEST_SKIP();
LoadLicense(LicenseBuilder::NoPadding());
// We need an invalid signature size, the easiest way to do this is to take
// the expected signature size and trim a bit off the end.
const size_t invalid_signature_size = kSignatureSize - 1;
LoadLicense();
ASSERT_EQ(
WB_License_VerifyRenewalResponse(whitebox_, kResponse, kResponseSize,
kSignature, invalid_signature_size),
WB_RESULT_INVALID_PARAMETER);
ASSERT_EQ(WB_License_VerifyRenewalResponse(
whitebox_, garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(), 14),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidSignatureForInvalidSignature) {
// TODO: This test needs to be skipped for now as comparing signatures
// requires a key provided by a valid license.
GTEST_SKIP();
InvalidSignatureForModifiedMessage) {
LoadLicense(LicenseBuilder::NoPadding());
// In order to create an invalid signature, copy the valid signature and flip
// one byte. This will ensure that our invalid signature is always different
// from the valid signature.
std::vector<uint8_t> invalid_signature(kSignature,
kSignature + kSignatureSize);
invalid_signature[0] = ~invalid_signature[0];
Modify(&garbage_renewal_message_);
LoadLicense();
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_SIGNATURE);
}
ASSERT_EQ(WB_License_VerifyRenewalResponse(
whitebox_, kResponse, kResponseSize, invalid_signature.data(),
invalid_signature.size()),
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
InvalidSignatureForModifiedSignature) {
LoadLicense(LicenseBuilder::NoPadding());
Modify(&garbage_renewal_signature_);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_SIGNATURE);
}
@@ -134,9 +193,36 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoLicense) {
// Unlike the other tests, we do not call LoadLicense() as the criteria for
// WB_RESULT_INVALID_STATE is that no key can be found and keys are provided
// via a license.
ASSERT_EQ(
WB_License_VerifyRenewalResponse(whitebox_, kResponse, kResponseSize,
kSignature, kSignatureSize),
WB_RESULT_INVALID_STATE);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_STATE);
}
} // namespace
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoSigningKey) {
// Create a license with no signing key and one content key (every license
// must have a content key).
widevine::LicenseBuilder builder;
builder.AddStubbedContentKey();
License license;
builder.Build(*public_key_, &license);
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, license.message.data(), license.message.size(),
license.signature.data(), license.signature.size(),
license.session_key.data(), license.session_key.size(),
license.request.data(), license.request.size()),
WB_RESULT_OK);
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
garbage_renewal_message_.data(),
garbage_renewal_message_.size(),
garbage_renewal_signature_.data(),
garbage_renewal_signature_.size()),
WB_RESULT_INVALID_STATE);
}
} // namespace widevine