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
433 lines
18 KiB
C++
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
|