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.
285 lines
12 KiB
C++
285 lines
12 KiB
C++
// Copyright 2020 Google LLC. All Rights Reserved.
|
|
|
|
#include "api/license_whitebox.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "api/golden_data.h"
|
|
#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 widevine {
|
|
|
|
class LicenseWhiteboxProcessLicenseResponseTest
|
|
: public LicenseWhiteboxTestBase {
|
|
protected:
|
|
void UseLicenseWithoutSigningKey() {
|
|
LicenseBuilder builder;
|
|
builder.AddStubbedContentKey();
|
|
builder.Build(*public_key_, &license_);
|
|
}
|
|
|
|
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding) {
|
|
LicenseBuilder builder;
|
|
builder.AddSigningKey(LicenseBuilder::DefaultSigningKey(), padding);
|
|
builder.AddStubbedContentKey();
|
|
builder.Build(*public_key_, &license_);
|
|
}
|
|
|
|
License license_;
|
|
};
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) {
|
|
UseLicenseWithoutSigningKey();
|
|
|
|
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);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
|
SuccessWithSigningKeyNoPadding) {
|
|
UseLicenseWithSigningKey(LicenseBuilder::NoPadding());
|
|
|
|
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);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
|
SuccessWithSigningKeyPKSC8Padding) {
|
|
UseLicenseWithSigningKey(LicenseBuilder::PKSC8Padding());
|
|
|
|
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);
|
|
}
|
|
|
|
class LicenseWhiteboxProcessLicenseResponseErrorTest
|
|
: public LicenseWhiteboxProcessLicenseResponseTest {
|
|
protected:
|
|
void SetUp() override {
|
|
LicenseWhiteboxProcessLicenseResponseTest::SetUp();
|
|
|
|
// For these tests, we don't care what license we use, it just needs to be
|
|
// a valid license.
|
|
UseLicenseWithoutSigningKey();
|
|
}
|
|
};
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidSignatureForModifedMessage) {
|
|
Modify(&license_.message);
|
|
|
|
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_INVALID_SIGNATURE);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidSignatureForModifedSignature) {
|
|
Modify(&license_.signature);
|
|
|
|
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_INVALID_SIGNATURE);
|
|
}
|
|
|
|
// The license request is used to derive the signing key. If the request was
|
|
// modified, then the wrong signing key should be generated.
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidSignatureForModifedLicenseRequest) {
|
|
Modify(&license_.request);
|
|
|
|
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_INVALID_SIGNATURE);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForNullWhitebox) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
nullptr, 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_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForNullMessage) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, nullptr, 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_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForZeroMessageSize) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), 0,
|
|
license_.signature.data(), license_.signature.size(),
|
|
license_.session_key.data(), license_.session_key.size(),
|
|
license_.request.data(), license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForNullSignature) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), license_.message.size(),
|
|
nullptr, license_.signature.size(), license_.session_key.data(),
|
|
license_.session_key.size(), license_.request.data(),
|
|
license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForInvalidSignatureSize) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), license_.message.size(),
|
|
license_.signature.data(), 5, license_.session_key.data(),
|
|
license_.session_key.size(), license_.request.data(),
|
|
license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForNullSessionKey) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), license_.message.size(),
|
|
license_.signature.data(), license_.signature.size(), nullptr,
|
|
license_.session_key.size(), license_.request.data(),
|
|
license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForInvalidSessionKeySize) {
|
|
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), license_.message.size(),
|
|
license_.signature.data(), license_.signature.size(),
|
|
license_.session_key.data(), 5, license_.request.data(),
|
|
license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
// If the session key is modified, unwrapping it will fail. Therefore, we will
|
|
// know that the parameter is invalid (compared to a modified license request).
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForModifedSessionKey) {
|
|
Modify(&license_.session_key);
|
|
|
|
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_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForNullLicenseRequest) {
|
|
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(),
|
|
nullptr, license_.request.size()),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
|
|
InvalidParameterForZeroLienseRequestSize) {
|
|
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(), 0),
|
|
WB_RESULT_INVALID_PARAMETER);
|
|
}
|
|
|
|
class LicenseWhiteboxMultiLicenseTest
|
|
: public LicenseWhiteboxProcessLicenseResponseTest {
|
|
protected:
|
|
void SetUp() override {
|
|
LicenseWhiteboxProcessLicenseResponseTest::SetUp();
|
|
|
|
// For these tests, we don't care what license we use, it just needs to be
|
|
// a valid license.
|
|
UseLicenseWithoutSigningKey();
|
|
}
|
|
};
|
|
|
|
// A whitebox can only process a license once. If it has loaded a license
|
|
// (successfully) it should reject later calls with WB_RESULT_INVALID_STATE.
|
|
TEST_F(LicenseWhiteboxMultiLicenseTest, InvalidState) {
|
|
// Load the first license. This one is expected to succeed as the whitebox has
|
|
// not loaded a license yet.
|
|
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);
|
|
|
|
// Attempt to load the same license again. This should fail as it already has
|
|
// a license (even though it is the same 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_INVALID_STATE);
|
|
}
|
|
|
|
// Even though a whitebox can only load a license once, if it fails to load a
|
|
// license, it should still be able to try again.
|
|
TEST_F(LicenseWhiteboxMultiLicenseTest, SuccessAfterFailure) {
|
|
// Force this one to fail my changing the request, this will cause an error
|
|
// in key derivation which is a later step of license parsing.
|
|
std::vector<uint8_t> bad_request = license_.request;
|
|
Modify(&bad_request);
|
|
ASSERT_NE(WB_License_ProcessLicenseResponse(
|
|
whitebox_, license_.message.data(), license_.message.size(),
|
|
license_.signature.data(), license_.signature.size(),
|
|
license_.session_key.data(), license_.session_key.size(),
|
|
bad_request.data(), bad_request.size()),
|
|
WB_RESULT_OK);
|
|
|
|
// Attempt to load the license again, but use the correct (unmodified)
|
|
// request.
|
|
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);
|
|
}
|
|
|
|
} // namespace widevine
|