Files
whitebox/whitebox/api/license_whitebox_process_license_response_test.cc
John Rummell adb98d80d3 Update to latest version including support for Partner Keys
Includes change to WB_License_Create() so that it includes a separate
parameter that specifies the init data used for Provider Keys.

This updates the repo to match the internal repo at commit:
8c1c4338906a32eed83eb63702690d1f02ff7cd0
2021-12-13 16:40:24 -08:00

433 lines
18 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_whitebox_test_base.h"
#include "api/test_license_builder.h"
#include "crypto_utils/crypto_util.h"
#include "crypto_utils/rsa_key.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace widevine {
class LicenseWhiteboxProcessLicenseResponseTest
: public LicenseWhiteboxTestBase {
protected:
void SetUp() {
LicenseWhiteboxTestBase::SetUp();
server_ = TestServer::CreateDualKey();
}
// No content keys. No signing keys.
void UseLicenseWithNoKeys() {
TestLicenseBuilder builder;
builder.Build(*server_, &license_);
}
void UseLicenseWithoutSigningKey() {
TestLicenseBuilder builder;
builder.AddContentKey(golden_data_.CBCContent().software_crypto_key);
builder.Build(*server_, &license_);
}
void UseLicenseWithSigningKey(TestLicenseBuilder::Padding padding) {
TestLicenseBuilder builder;
builder.GetSettings().padding = padding;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
builder.AddContentKey(golden_data_.CBCContent().software_crypto_key);
builder.Build(*server_, &license_);
}
std::unique_ptr<TestServer> server_;
License license_;
};
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) {
UseLicenseWithoutSigningKey();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
SuccessWithSigningKeyNoPadding) {
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
SuccessWithSigningKeyPKSC8Padding) {
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
// If there were multiple signing keys (this can only happen if a license server
// was manipulating the license and we are using protobuf parsing), the
// implementation is free to pick either key.
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
SuccessWithMultipleSigningKeys) {
TestLicenseBuilder builder;
builder.GetSettings().padding = TestLicenseBuilder::Padding::kPKSC8;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
builder.AddContentKey(golden_data_.CBCContent().software_crypto_key);
builder.Build(*server_, &license_);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, InvalidParameterWithNoKeys) {
UseLicenseWithNoKeys();
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithProviderKey) {
const size_t kProviderKeyId = 1;
TestLicenseBuilder builder;
builder.GetSettings().padding = TestLicenseBuilder::Padding::kPKSC8;
builder.GetSettings().provider_key_id = kProviderKeyId;
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
builder.AddContentKey(golden_data_.CBCContent().software_crypto_key);
builder.Build(*server_, &license_);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kProviderKeyId, license_.request.data(),
license_.request.size()),
WB_RESULT_OK);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseTest, InvalidProviderKey) {
// Other tests pass in kNoProviderKeyId (0). However, invalid keys should also
// be treated as if no key is provided. It is expected to only have a small
// number of provider keys (starting at index 1), so pick a large number.
const size_t kInvalidProviderKey = 0xfff;
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kInvalidProviderKey,
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);
WB_Result result = WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId, license_.request.data(),
license_.request.size());
// Depending on where/how the message is verified, an implementation can
// return invalid parameter or invalid signature. Preferably it would be
// "invalid signature", but not required.
ASSERT_THAT(result, testing::AnyOf(WB_RESULT_INVALID_PARAMETER,
WB_RESULT_INVALID_SIGNATURE));
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedSignature) {
Modify(&license_.signature);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
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_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
// 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).
// However, it was found that allowing the error to propagate was a better
// implementation solution, so we allow "invalid signature" to be returned.
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidSignatureForModifedSessionKey) {
Modify(&license_.session_key);
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_SIGNATURE);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullWhitebox) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
nullptr, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullMessage) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), nullptr, license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForZeroMessageSize) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(), 0,
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullSignature) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), nullptr, license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForInvalidSignatureSize) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(), 5,
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullSessionKey) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), nullptr, license_.session_key.size(),
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForInvalidSessionKeySize) {
ASSERT_EQ(
WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(), 5,
kNoProviderKeyId, license_.request.data(), license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForNullLicenseRequest) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY,
license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, nullptr, license_.request.size()),
WB_RESULT_INVALID_PARAMETER);
}
TEST_F(LicenseWhiteboxProcessLicenseResponseErrorTest,
InvalidParameterForZeroLienseRequestSize) {
ASSERT_EQ(WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY,
license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, 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_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
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_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
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_, WB_LICENSE_KEY_MODE_DUAL_KEY,
license_.core_message.data(), license_.core_message.size(),
license_.message.data(), license_.message.size(),
license_.signature.data(), license_.signature.size(),
license_.session_key.data(), license_.session_key.size(),
kNoProviderKeyId, 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_, WB_LICENSE_KEY_MODE_DUAL_KEY, license_.core_message.data(),
license_.core_message.size(), license_.message.data(),
license_.message.size(), license_.signature.data(),
license_.signature.size(), license_.session_key.data(),
license_.session_key.size(), kNoProviderKeyId,
license_.request.data(), license_.request.size()),
WB_RESULT_OK);
}
} // namespace widevine