Wrapped OKP info into several classes.

[ Merge of http://go/wvgerrit/133744 ]

This changes adds several small classes which contain and manage
system and engine information related to OTA keybox provisioning.
These classes closely map to the OKP device file messages.

Bug: 189232882
Test: Linux unit tests
Change-Id: Ia9334c38f9d7ea89b30d9ad05f0595570bb38658

Storing and loading OKP info.

[ Merge of http://go/wvgerrit/133763 and http://go/ag/15645333 ]

This change extends the DeviceFiles module to be able to store and
load OKP info.  Mild data validation is performed when storing and
loading the information.

Bug: 189232882
Test: Android unit tests
Change-Id: I077de3234157252f2255a4389bf82a8d5344a355

System OKP fallback policy.

[ Merge of http://go/wvgerrit/133783 and http://go/ag/15645334 ]

SystemFallbackPolicy provides a thread-safe interface for accessing
and modifying OKP info.

Bug: 189232882
Test: Android unit tests
Change-Id: I4e43e3bc047ed5fb6cb517b53e4094e812b70e1e

Engine OKP provisioner.

[ Merge of http://go/wvgerrit/133803 and http://go/ag/15645335 ]

The OtaKeyboxProvisioner provides a CdmEngine-specific context for
performing OTA keybox provisioning.  Utilizes the system-wide
SystemFallbackPolicy to relay provisioning status between engines.
The provisioner will handle message wrapping and unwrapping of the
raw OTA keybox request / response into the SignedProvisioningMessage
which is sent to/received from the provisioning server.

[ Partial merge of http://go/wvgerrit/125844 ]

Note: Includes partial CryptoSession changes from various CLs.
CryptoSession functionality has been stripped to reduce impact of
this CL.

Bug: 189232882
Test: Android unit tests
Change-Id: I282bf7d1887daefb2250af1bd595c4dc3dfcfb29

Integrated OKP into CDM Engine

[ Merge of http://go/wvgerrit/133804 and http://go/ag/15646376 ]

Extended the functionality of the CdmEngine to check if the device
requires OKP and to initialize OKP resources if required.  The
functionality of OpenSession() and GetProvisioningRequest() have been
the most affected.  If OKP is required, these methods will signal to
the app that provisioning is required and will return an OKP request.

Once a device is provisioned, the OKP data is cleared away and the
CdmEngine will resume normal operation.  Engines created after a
device is provisioned will immediately enter normal operations.
The exception is for CdmEngines which failed to perform OKP for some
reason and are still running.  Those apps will need to restart before
gaining access to L1 operations.

Bug: 187646550
Test: Android integration tests
Merged-In: Ia572a66a7b73479355758aa3d0c682691eaca0fc
Change-Id: Ia572a66a7b73479355758aa3d0c682691eaca0fc
This commit is contained in:
Rahul Frias
2021-09-15 05:00:19 -07:00
committed by Alex Dale
parent 52bd1d206e
commit 39558526f6
30 changed files with 2010 additions and 330 deletions

View File

@@ -15,6 +15,7 @@
#include "cdm_random.h"
#include "crypto_wrapped_key.h"
#include "file_store.h"
#include "okp_info.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -3785,6 +3786,7 @@ using ::testing::Expectation;
using ::testing::Gt;
using ::testing::HasSubstr;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::NotNull;
using ::testing::Return;
@@ -4882,6 +4884,110 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
}
}
// OKP info can only be stored on L1 device files.
TEST_F(DeviceFilesTest, OkpInfo_L1Only) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL3));
okp::SystemFallbackInfo info;
info.SetState(okp::SystemState::kNeedsProvisioning);
info.SetFirstCheckedTime(1234);
const std::string kErrorMessage = "OKP should not be available on L3";
EXPECT_FALSE(device_files.StoreOkpInfo(info)) << kErrorMessage;
EXPECT_FALSE(device_files.RetrieveOkpInfo(&info)) << kErrorMessage;
EXPECT_FALSE(device_files.DeleteOkpInfo()) << kErrorMessage;
}
// Uninitialized info cannot be stored.
TEST_F(DeviceFilesTest, OkpInfo_UninitializedInfo) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
okp::SystemFallbackInfo info; // Uninitialized.
EXPECT_FALSE(device_files.StoreOkpInfo(info));
}
TEST_F(DeviceFilesTest, OkpInfo_FileDoesNotExist) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
const std::string kOkpInfoPath =
device_base_path_ + DeviceFiles::GetOkpInfoFileName();
EXPECT_CALL(file_system, Exists(kOkpInfoPath)).WillOnce(Return(false));
okp::SystemFallbackInfo info;
EXPECT_FALSE(device_files.RetrieveOkpInfo(&info));
}
TEST_F(DeviceFilesTest, OkpInfo_RetrieveWithNull) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
EXPECT_FALSE(device_files.RetrieveOkpInfo(nullptr));
}
TEST_F(DeviceFilesTest, OkpInfo_DeleteFile) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
// L1 - Should succeed.
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
const std::string kOkpInfoPath =
device_base_path_ + DeviceFiles::GetOkpInfoFileName();
EXPECT_CALL(file_system, Remove(kOkpInfoPath)).WillOnce(Return(true));
EXPECT_TRUE(device_files.DeleteOkpInfo());
// L3 - Should fail.
EXPECT_TRUE(device_files.Init(kSecurityLevelL3));
EXPECT_FALSE(device_files.DeleteOkpInfo());
}
TEST_F(DeviceFilesTest, OkpInfo_StoreAndRetrieve) {
MockFileSystem file_system;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
// Prepare data.
okp::SystemFallbackInfo info;
info.SetState(okp::SystemState::kFallbackMode);
info.SetFirstCheckedTime(1234);
info.SetBackoffStartTime(2345);
info.SetBackoffDuration(1111);
// Set store expectations.
const std::string kOkpInfoPath =
device_base_path_ + DeviceFiles::GetOkpInfoFileName();
MockFile* file = new MockFile();
EXPECT_CALL(file_system, DoOpen(kOkpInfoPath, _)).WillOnce(Return(file));
std::string serialized;
EXPECT_CALL(*file, Write(NotNull(), _))
.WillOnce(DoAll(Invoke([&](const char* buf, size_t len) {
serialized.assign(buf, len);
}),
ReturnArg<1>()));
EXPECT_TRUE(device_files.StoreOkpInfo(info));
ASSERT_FALSE(serialized.empty()) << "OKP info was not serialized";
// Set retrieve expectations.
file = new MockFile();
EXPECT_CALL(file_system, Exists(kOkpInfoPath)).WillOnce(Return(true));
EXPECT_CALL(file_system, FileSize(kOkpInfoPath))
.WillOnce(Return(serialized.size()));
EXPECT_CALL(file_system, DoOpen(kOkpInfoPath, _)).WillOnce(Return(file));
EXPECT_CALL(*file, Read(NotNull(), _))
.WillOnce(DoAll(SetArrayArgument<0>(serialized.begin(), serialized.end()),
Return(serialized.size())));
okp::SystemFallbackInfo retrieved_info;
EXPECT_TRUE(device_files.RetrieveOkpInfo(&retrieved_info));
EXPECT_EQ(retrieved_info, info);
}
// From a usage info file containing 3 provider sessions, 2 will be
// deleted using the |key_set_id| associated with them.
// It is expected that once the provider sessions are deleted, the

View File

@@ -0,0 +1,372 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "okp_fallback_policy.h"
#include <memory>
#include <gtest/gtest.h>
#include "mock_clock.h"
#include "test_printers.h"
namespace wvcdm {
namespace okp {
namespace {
// Thu 29 Jul 2021 10:43:21 PM UTC
constexpr int64_t kInitialTime = 1627598601;
void SetSystemInfoAsProvisioned(SystemFallbackInfo* info,
int64_t provisioning_time) {
info->SetState(SystemState::kProvisioned);
if (!info->HasFirstCheckedTime()) {
info->SetFirstCheckedTime(kInitialTime);
}
info->ClearBackoffStartTime();
info->SetProvisioningTime(provisioning_time);
}
void SetSystemInfoAsFallback(SystemFallbackInfo* info,
int64_t backoff_start_time,
int64_t backoff_duration) {
info->SetState(SystemState::kFallbackMode);
if (!info->HasFirstCheckedTime()) {
info->SetFirstCheckedTime(kInitialTime);
}
info->SetBackoffStartTime(backoff_start_time);
info->SetBackoffDuration(backoff_duration);
info->ClearProvisioningTime();
}
class OkpFallbackPolicyTest : public ::testing::Test {
protected:
void SetUp() override {
clock_.SetTime(kInitialTime);
system_policy_ = SystemFallbackPolicy::CreateForTesting(&clock_);
ASSERT_TRUE(system_policy_);
system_policy_->MarkNeedsProvisioning();
}
void SetUpWithInfo(const SystemFallbackInfo& info) {
TearDown();
system_policy_ = SystemFallbackPolicy::CreateForTesting(info, &clock_);
}
void TearDown() override { system_policy_.reset(); }
FrozenClock clock_;
std::unique_ptr<SystemFallbackPolicy> system_policy_;
}; // class OkpFallbackPolicyTest
} // namespace
// Test ensures that test fixture is initialized correctly.
TEST_F(OkpFallbackPolicyTest, Initialization) {
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kInitialTime, system_policy_->info().first_checked_time());
EXPECT_EQ(SystemState::kNeedsProvisioning, system_policy_->state());
}
// Setup:
// 1) Device needs OKP
// 2) Some CDM engine successfully provisions, and marks the fallback
// policy as provisioned.
// Expectation:
// Policy is marked as provisioned and timestamp is updated.
TEST_F(OkpFallbackPolicyTest, SuccessfulProvisioning) {
// App calls provideProvisionResponse() and succeeds.
constexpr int64_t kProvisioningTime = kInitialTime + 10;
clock_.SetTime(kProvisioningTime);
system_policy_->MarkProvisioned();
// Final checks.
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kProvisioningTime, system_policy_->info().provisioning_time());
}
// Setup:
// 1) Device state is restored and is already provisioned
// Expectation:
// Fallback policy should still be marked as provisioned.
TEST_F(OkpFallbackPolicyTest, Restore_DeviceProvisioned) {
SystemFallbackInfo info;
SetSystemInfoAsProvisioned(&info, kInitialTime);
// Reinitialize.
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
// Checks.
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
}
// Setup:
// 1) Device needs OKP
// 2) App attempts OKP, but response fails and triggers fallback.
// Expectation:
// Policy should indicate fallback, and update info.
TEST_F(OkpFallbackPolicyTest, TriggerFallback) {
// App calls provideProvisionResponse() and fails.
constexpr int64_t kFallbackTime = kInitialTime + 10;
clock_.SetTime(kFallbackTime);
system_policy_->TriggerFallback();
// Checks.
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_TRUE(system_policy_->IsInFallbackMode());
EXPECT_EQ(system_policy_->info().backoff_start_time(), kFallbackTime);
EXPECT_GE(system_policy_->info().backoff_duration(),
kMinInitialBackoffDuration);
}
// Setup:
// 1) Device needs OKP
// 2) Two apps are performing provisioning simultaneously
// 3) The first app successfully provisions device
// 4) The second app succeeds (see notes) shortly after
// Expectation:
// The time of the first provisioning should be recorded, the
// second should not.
// Note:
// The CDM should silently drop responses which arrive after
// the first successful response. The fallback policy is agnostic
// to the exact mechanism to enforce this behavor.
TEST_F(OkpFallbackPolicyTest, TwoSuccessfulProvisionings) {
// App calls provideProvisionResponse() and succeeds.
constexpr int64_t kFirstProvisioningTime = kInitialTime + 10;
clock_.SetTime(kFirstProvisioningTime);
system_policy_->MarkProvisioned();
// App calls provideProvisionResponse() and fails.
constexpr int64_t kSecondProvisioningTime = kFirstProvisioningTime + 10;
clock_.SetTime(kSecondProvisioningTime);
system_policy_->TriggerFallback();
// Checks.
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kFirstProvisioningTime, system_policy_->info().provisioning_time());
}
// Setup:
// 1) Device needs OKP
// 2) Two apps are performing provisioning simultaneously
// 3) The first app successfully provisions device
// 4) The second app fails, and triggers fallback
// Expectation:
// Once provisioned, fallback policy should remain provisioned.
// Note:
// In this case, the second engine should check if provisioned
// before triggering its own L3 fallback.
TEST_F(OkpFallbackPolicyTest, TriggerFallbackAfterProvisioning) {
// App calls provideProvisionResponse() and succeeds.
constexpr int64_t kProvisioningTime = kInitialTime + 10;
clock_.SetTime(kProvisioningTime);
system_policy_->MarkProvisioned();
// App calls provideProvisionResponse() and fails.
constexpr int64_t kFallbackTime = kProvisioningTime + 10;
clock_.SetTime(kFallbackTime);
system_policy_->TriggerFallback();
// Checks.
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kProvisioningTime, system_policy_->info().provisioning_time());
EXPECT_FALSE(system_policy_->info().HasBackoffStartTime());
EXPECT_FALSE(system_policy_->info().HasBackoffDuration());
}
// Setup:
// 1) Device needs OKP
// 2) Two apps are performing provisioning simultaneously
// 3) The first app fails, and triggers fallback
// 4) The seoncd app successfully provisions device
// Expectation:
// Fallback policy should indicate that the device is provisioned;
// overriding the fallback.
// Note:
// Depending on the exact timing, the first app may or may not fallback
// to L3 for the remainder of the apps life cycle. If the app did
// fallback to L3, it will be able to resume use of L1 when it restarts.
TEST_F(OkpFallbackPolicyTest, ProvisionAfterFallback) {
// App calls provideProvisionResponse() and fails.
constexpr int64_t kFallbackTime = kInitialTime + 10;
clock_.SetTime(kFallbackTime);
system_policy_->TriggerFallback();
// App calls provideProvisionResponse() and succeeds.
constexpr int64_t kProvisioningTime = kFallbackTime + 10;
clock_.SetTime(kProvisioningTime);
system_policy_->MarkProvisioned();
// Checks.
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kProvisioningTime, system_policy_->info().provisioning_time());
EXPECT_FALSE(system_policy_->info().HasBackoffStartTime());
}
// Setup:
// 1) Device needs OKP
// 2) Fallback policy is restored, not in fallback mode
// 3) Provisioning is attempted
// Expectation:
// Policy should resume, allowing provisioning.
TEST_F(OkpFallbackPolicyTest, Restore_NeedsProvisioning) {
SystemFallbackInfo info;
info.SetState(SystemState::kNeedsProvisioning);
info.SetFirstCheckedTime(kInitialTime);
constexpr int64_t kRestoreTime = kInitialTime + 10;
clock_.SetTime(kRestoreTime);
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
// Checks
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(system_policy_->info().first_checked_time(), kInitialTime);
// Provision
constexpr int64_t kProvisioningTime = kRestoreTime + 10;
clock_.SetTime(kProvisioningTime);
system_policy_->MarkProvisioned();
// Checks
EXPECT_TRUE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(kProvisioningTime, system_policy_->info().provisioning_time());
}
// Setup:
// 1) Device needs OKP
// 2) Fallback policy is restored, but had previously entered fallback mode
// 3) Device is still in "backoff period"
// Expectation:
// Fallback policy should continue to be in fallback mode after restore.
TEST_F(OkpFallbackPolicyTest, Restore_InBackoffPeriod) {
SystemFallbackInfo info;
constexpr int64_t kBackoffDuration = 100;
constexpr int64_t kFallbackTime = kInitialTime + 10;
SetSystemInfoAsFallback(&info, kFallbackTime, kBackoffDuration);
constexpr int64_t kRestoreTime = kFallbackTime + (kBackoffDuration / 2);
clock_.SetTime(kRestoreTime); // Within backoff period.
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
// Checks
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_TRUE(system_policy_->IsInFallbackMode());
EXPECT_EQ(system_policy_->info().backoff_start_time(), kFallbackTime);
EXPECT_EQ(system_policy_->info().backoff_duration(), kBackoffDuration);
}
// Setup:
// 1) Device needs OKP
// 2) Fallback policy is restored, but had previously entered fallback mode
// 3) Backoff period is over
// Expectation:
// Fallback policy should no longer be in fallback mode.
TEST_F(OkpFallbackPolicyTest, Restore_AfterBackoffPeriod) {
SystemFallbackInfo info;
constexpr int64_t kBackoffDuration = 100;
constexpr int64_t kFallbackTime = kInitialTime + 10;
SetSystemInfoAsFallback(&info, kFallbackTime, kBackoffDuration);
constexpr int64_t kRestoreTime = kFallbackTime + (kBackoffDuration * 2);
clock_.SetTime(kRestoreTime); // After backoff period.
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
// Checks
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
// Backoff duration should still be recorded as it will be required
// for exponential backoff.
EXPECT_EQ(system_policy_->info().backoff_duration(), kBackoffDuration);
}
// Setup:
// 1) Device needs OKP
// 2) Fallback policy is restored, but had previously entered fallback mode
// 3) Device system clock has been rewound to a time before backoff started
// Expectation:
// Restore should be successful, fallback may be canceled, provisioning
// should still be needed.
// Note:
// This is not an expected circumstance, but may occur if a user changes
// their devices system time.
TEST_F(OkpFallbackPolicyTest, Restore_InBackoffPeriod_SystemTimeRollback) {
SystemFallbackInfo info;
constexpr int64_t kBackoffDuration = 100;
constexpr int64_t kFallbackTime = kInitialTime + 10;
SetSystemInfoAsFallback(&info, kFallbackTime, kBackoffDuration);
constexpr int64_t kRestoreTime = kFallbackTime - 250;
clock_.SetTime(kRestoreTime); // Before backoff period.
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
// Checks
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
}
// Setup:
// 1) Device needs OKP
// 2) Fallback policy is restored, but had previously entered fallback mode
// 3) Backoff period is over
// 4) OKP fails again, tiggering fallback again
// Expectation:
// Fallback policy should re-enter fallback mode, with a backoff period
// double that of before.
TEST_F(OkpFallbackPolicyTest, Restore_AfterBackoff_FallbackAgain) {
SystemFallbackInfo info;
constexpr int64_t kBackoffDuration = 100;
constexpr int64_t kFallbackTime = kInitialTime + 10;
SetSystemInfoAsFallback(&info, kFallbackTime, kBackoffDuration);
constexpr int64_t kRestoreTime = kFallbackTime + (kBackoffDuration * 2);
clock_.SetTime(kRestoreTime); // After backoff period.
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
ASSERT_FALSE(system_policy_->IsInFallbackMode());
// Trigger second fallback.
constexpr int64_t kSecondFallbackTime = kRestoreTime + 10;
clock_.SetTime(kSecondFallbackTime);
system_policy_->TriggerFallback();
constexpr int64_t kExpectedBackoffDuration = kBackoffDuration * 2;
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_TRUE(system_policy_->IsInFallbackMode());
EXPECT_EQ(system_policy_->info().backoff_start_time(), kSecondFallbackTime);
EXPECT_EQ(system_policy_->info().backoff_duration(),
kExpectedBackoffDuration);
}
// Setup:
// 1) Device needs OKP again (see notes)
// 2) Fallback policy is restored, previously being marked as provisioned
// 3) Fallback policy is marked as needing provisioning
// Expectation:
// Once marked as needing re-provisioning, the fallback policy should
// clear is self and indicate that provisioning is needed.
// Note:
// Ideally this should never happen. Once a device is provisioned, is
// should never require OKP again. However, the device should have never
// required OKP in the first place. If a future bug arises, and a device
// requires OKP again, the fallback policy should be able to handle
// this situation without bricking L1.
TEST_F(OkpFallbackPolicyTest, Restore_NeedsProvisioningAgain) {
SystemFallbackInfo info;
constexpr int64_t kInitialProvisioningTime = kInitialTime;
SetSystemInfoAsProvisioned(&info, kInitialProvisioningTime);
constexpr int64_t kRestoreTime = kInitialProvisioningTime + 100;
clock_.SetTime(kRestoreTime);
// Restore
SetUpWithInfo(info);
ASSERT_TRUE(system_policy_);
ASSERT_TRUE(system_policy_->IsProvisioned());
system_policy_->MarkNeedsProvisioning();
// Checks
EXPECT_FALSE(system_policy_->IsProvisioned());
EXPECT_FALSE(system_policy_->IsInFallbackMode());
EXPECT_EQ(system_policy_->info().first_checked_time(), kRestoreTime);
}
} // namespace okp
} // namespace wvcdm

View File

@@ -7,13 +7,22 @@
#include <gtest/gtest.h>
#include "crypto_session.h"
#include "mock_clock.h"
#include "okp_fallback_policy.h"
#include "properties.h"
#include "string_conversions.h"
namespace wvcdm {
using ::testing::DoAll;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::video_widevine::ProvisioningResponse;
using ::video_widevine::SignedProvisioningMessage;
using ProvisioningType = SignedProvisioningMessage::ProvisioningType;
namespace {
constexpr int64_t kInitialTime = 1629321960; // Wed 18 Aug 2021 09:26:00 PM
const std::string kEmptyString;
const std::string kFakeOtaProvisioningRequest =
"Totally real device ID, not fake" // Device ID : 32 bytes
@@ -47,6 +56,14 @@ class MockCryptoSession : public CryptoSession {
CdmResponseType(bool, std::string*));
MOCK_METHOD2(LoadOtaProvisioning, CdmResponseType(bool, const std::string&));
// Client ID
uint8_t GetSecurityPatchLevel() override { return 0x00; }
bool GetBuildInformation(std::string* info) override {
if (info == nullptr) return false;
info->assign("OtaKeyboxProvisionerTest");
return true;
}
void ExpectRequest(const std::string& request, CdmResponseType result) {
if (result == NO_ERROR) {
EXPECT_CALL(*this, PrepareOtaProvisioningRequest(false, NotNull()))
@@ -69,174 +86,292 @@ class MockCryptoSession : public CryptoSession {
};
metrics::CryptoMetrics MockCryptoSession::crypto_metrics_;
std::vector<uint8_t> ToVector(const std::string& s) {
return std::vector<uint8_t>(s.begin(), s.end());
}
std::string FromVector(const std::vector<uint8_t>& v) {
return std::string(v.begin(), v.end());
}
} // namespace
class OtaKeyboxProvisionerTest : public ::testing::Test {
protected:
void SetUp() override {
crypto_session_.reset(new MockCryptoSession());
provisioner_ = OtaKeyboxProvisioner::Create();
clock_.SetTime(kInitialTime);
fallback_policy_ = okp::SystemFallbackPolicy::CreateForTesting(&clock_);
ASSERT_TRUE(fallback_policy_);
fallback_policy_->MarkNeedsProvisioning();
crypto_session_ = new MockCryptoSession();
crypto_session_->SetIsOpen(true);
provisioner_ = OtaKeyboxProvisioner::CreateForTesting(
std::unique_ptr<CryptoSession>(crypto_session_),
fallback_policy_.get());
ASSERT_TRUE(provisioner_);
}
void SetUpWithInfo(const okp::SystemFallbackInfo& info) {
TearDown();
fallback_policy_ =
okp::SystemFallbackPolicy::CreateForTesting(info, &clock_);
ASSERT_TRUE(fallback_policy_);
fallback_policy_->MarkNeedsProvisioning();
crypto_session_ = new MockCryptoSession();
crypto_session_->SetIsOpen(true);
provisioner_ = OtaKeyboxProvisioner::CreateForTesting(
std::unique_ptr<CryptoSession>(crypto_session_),
fallback_policy_.get());
}
void TearDown() override {
crypto_session_.reset();
provisioner_.reset();
crypto_session_ = nullptr;
fallback_policy_.reset();
}
std::unique_ptr<MockCryptoSession> crypto_session_;
FrozenClock clock_;
std::unique_ptr<okp::SystemFallbackPolicy> fallback_policy_;
MockCryptoSession* crypto_session_ = nullptr;
std::unique_ptr<OtaKeyboxProvisioner> provisioner_;
bool restore_messages_are_json_ = false;
};
TEST_F(OtaKeyboxProvisionerTest, SingleRequestSingleResponse) {
// Used to make a variety of SignedProvisioningMessage, including invalid
// responses.
// |provisioning_type| - ProvisioningType of outer message.
// Valid response is ANDROID_ATTESTATION_KEYBOX_OTA
// |include_response| - Include the actual ProvisioningResponse message
// bytes. Valid response is true.
// |include_ota_response| - Includes the |android_ota_keybox_response|
// field of the ProvisioningResponse message.
// |ota_response| - Raw bytes of the ota_response field of the
// AndroidAttestationOtaKeyboxResponse.
// Valid responses contain the response to be passed
// into OEMCrypto.
void MakeSignedOtaProvisioningResponseEx(ProvisioningType provisioning_type,
bool include_response,
bool include_ota_response,
const std::string& ota_response,
std::string* response) {
ProvisioningResponse prov_response;
if (include_ota_response) {
prov_response.mutable_android_ota_keybox_response()->set_ota_response(
ota_response);
}
std::string message;
prov_response.SerializeToString(&message);
SignedProvisioningMessage signed_response;
signed_response.set_protocol_version(SignedProvisioningMessage::VERSION_1);
signed_response.set_provisioning_type(provisioning_type);
if (include_response) {
signed_response.set_message(message);
}
std::string proto_response;
signed_response.SerializeToString(&proto_response);
if (Properties::provisioning_messages_are_binary()) {
*response = std::move(proto_response);
return;
}
response->assign("{\n\"signedResponse\": \"");
response->append(Base64SafeEncodeNoPad(ToVector(proto_response)));
response->append("\"\n}\n");
}
// Make a valid SignedProvisioningMessage containing an OTA keybox response.
void MakeSignedOtaProvisioningResponse(const std::string& ota_response,
std::string* response) {
MakeSignedOtaProvisioningResponseEx(
SignedProvisioningMessage::ANDROID_ATTESTATION_KEYBOX_OTA, true, true,
ota_response, response);
}
bool RequestContains(const std::string& request, const std::string& part) {
if (Properties::provisioning_messages_are_binary()) {
const auto request_pos = request.find(part);
return request_pos != std::string::npos;
}
const std::string decoded_request = FromVector(Base64SafeDecode(request));
if (decoded_request.empty()) return false;
const auto request_pos = decoded_request.find(part);
return request_pos != std::string::npos;
}
TEST_F(OtaKeyboxProvisionerTest, FullProvisioning) {
// Pre-request conditions.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(0u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
EXPECT_FALSE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
// Generate request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &request));
EXPECT_EQ(request, kFakeOtaProvisioningRequest);
std::string signed_prov_message, default_url;
EXPECT_EQ(NO_ERROR, provisioner_->GetProvisioningRequest(&signed_prov_message,
&default_url));
EXPECT_TRUE(
RequestContains(signed_prov_message, kFakeOtaProvisioningRequest));
EXPECT_FALSE(default_url.empty());
// Post-request, pre-response conditions.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(1u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
EXPECT_TRUE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
// Load response.
const std::string response = kFakeOtaProvisioningResponse;
crypto_session_->ExpectResponse(response, NO_ERROR);
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), response));
constexpr int64_t kProvisioningTime = kInitialTime + 10;
clock_.SetTime(kProvisioningTime);
std::string response;
MakeSignedOtaProvisioningResponse(kFakeOtaProvisioningResponse, &response);
crypto_session_->ExpectResponse(kFakeOtaProvisioningResponse, NO_ERROR);
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(response));
// Post-response conditions.
EXPECT_TRUE(provisioner_->IsProvisioned());
EXPECT_EQ(1u, provisioner_->request_count());
EXPECT_EQ(1u, provisioner_->response_count());
EXPECT_TRUE(provisioner_->request_generated());
EXPECT_TRUE(provisioner_->response_received());
EXPECT_EQ(kProvisioningTime, fallback_policy_->info().provisioning_time());
}
TEST_F(OtaKeyboxProvisionerTest, MultipleRequestsMultipleResponse) {
// Generate first request.
// The OTA keybox provisioning is a system-wide provisioning processes.
// After a particular CDM engine (A) begins the request, it may be
// completed by a different CDM engine (B) before A receives a response.
// Provisioning from A perspective should complete without issues.
TEST_F(OtaKeyboxProvisionerTest, CompletedInDifferentEngine) {
// Generate request (engine A).
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string first_request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &first_request));
EXPECT_EQ(first_request, kFakeOtaProvisioningRequest);
std::string signed_prov_message, default_url;
EXPECT_EQ(NO_ERROR, provisioner_->GetProvisioningRequest(&signed_prov_message,
&default_url));
// Generate second request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string second_request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &second_request));
EXPECT_EQ(second_request, kFakeOtaProvisioningRequest);
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(2u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
// Load first response.
const std::string first_response = kFakeOtaProvisioningResponse;
crypto_session_->ExpectResponse(first_response, NO_ERROR);
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), first_response));
// Loading second response should be silently dropped.
// No call to CryptoSession.
const std::string second_response = kAnotherFakeOtaProvisioningResponse;
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), second_response));
// Post-response conditions.
// Complete provisioning in different context (outside of engine A's scope).
constexpr int64_t kProvisioningTime = kInitialTime + 10;
clock_.SetTime(kProvisioningTime);
fallback_policy_->MarkProvisioned();
// Engine provisioner should still indicate that provisioning is complete.
EXPECT_TRUE(provisioner_->IsProvisioned());
EXPECT_EQ(2u, provisioner_->request_count());
EXPECT_EQ(2u, provisioner_->response_count());
EXPECT_FALSE(provisioner_->response_received());
// Load engine A's response.
constexpr int64_t kResponseTime = kProvisioningTime + 10;
clock_.SetTime(kResponseTime);
std::string response;
MakeSignedOtaProvisioningResponse(kAnotherFakeOtaProvisioningResponse,
&response);
// OtaKeyboxProvisioner::HandleProvisioningResponse will be called, but
// not OEMCrypto.
// Expect success.
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(response));
// Post-engine-response.;
EXPECT_TRUE(provisioner_->IsProvisioned());
EXPECT_TRUE(provisioner_->response_received());
EXPECT_EQ(kProvisioningTime, fallback_policy_->info().provisioning_time());
}
TEST_F(OtaKeyboxProvisionerTest, NullRequestParameters) {
// Null CryptoSession
std::string request;
EXPECT_NE(NO_ERROR,
provisioner_->GenerateProvisioningRequest(nullptr, &request));
// Null request, no call to CryptoSession.
EXPECT_NE(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), nullptr));
// Counter should not increase.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(0u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
}
TEST_F(OtaKeyboxProvisionerTest, EmptyRequest) {
// Test to ensure there are no critical failures when receiving a
// malformed SignedProvisioningMessage.
TEST_F(OtaKeyboxProvisionerTest, MalformedResponseMessage) {
// Generate request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &request));
std::string signed_prov_message, default_url;
EXPECT_EQ(NO_ERROR, provisioner_->GetProvisioningRequest(&signed_prov_message,
&default_url));
// Attempt to load empty response. No call to CryptoSession.
const std::string response = "";
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), response));
// Invalid response 1: Cannot SignedProvisioningMessage.
std::string response = "Not a SignedProvisioningMessage";
EXPECT_EQ(PARSE_OKP_RESPONSE_ERROR,
provisioner_->HandleProvisioningResponse(response));
// Invalid response 2: Wrong SignedProvisioningMessage protocol.
MakeSignedOtaProvisioningResponseEx(
SignedProvisioningMessage::PROVISIONING_30, true, true,
kFakeOtaProvisioningResponse, &response);
EXPECT_EQ(PARSE_OKP_RESPONSE_ERROR,
provisioner_->HandleProvisioningResponse(response));
// Invalid response 3: Missing serialized ProvisioningResponse message.
MakeSignedOtaProvisioningResponseEx(
SignedProvisioningMessage::ANDROID_ATTESTATION_KEYBOX_OTA, false, true,
kFakeOtaProvisioningResponse, &response);
EXPECT_EQ(PARSE_OKP_RESPONSE_ERROR,
provisioner_->HandleProvisioningResponse(response));
// Invalid response 4: Missing AndroidAttestationOtaKeyboxResponse message,
MakeSignedOtaProvisioningResponseEx(
SignedProvisioningMessage::ANDROID_ATTESTATION_KEYBOX_OTA, true, false,
kFakeOtaProvisioningResponse, &response);
EXPECT_EQ(PARSE_OKP_RESPONSE_ERROR,
provisioner_->HandleProvisioningResponse(response));
// Invalid response 5: Missing raw OTA keybox response.
MakeSignedOtaProvisioningResponse(kEmptyString, &response);
EXPECT_EQ(PARSE_OKP_RESPONSE_ERROR,
provisioner_->HandleProvisioningResponse(response));
// Post-response failure conditions.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(1u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
EXPECT_TRUE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
}
// Test case where OEMCrypto rejects the provided OTA keybox response.
TEST_F(OtaKeyboxProvisionerTest, RejectedResponse) {
// Generate request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string request, default_url;
EXPECT_EQ(NO_ERROR,
provisioner_->GetProvisioningRequest(&request, &default_url));
// Load response. OEMCrypto returns error.
std::string response;
MakeSignedOtaProvisioningResponse(kFakeOtaProvisioningResponse, &response);
crypto_session_->ExpectResponse(kFakeOtaProvisioningResponse, UNKNOWN_ERROR);
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(response));
// Post-response failure conditions.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_TRUE(provisioner_->IsInFallbackMode());
EXPECT_TRUE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
}
TEST_F(OtaKeyboxProvisionerTest, OtaProvisioningNotImplemented) {
// Generate request.
crypto_session_->ExpectRequest(kEmptyString, NOT_IMPLEMENTED_ERROR);
std::string request;
EXPECT_EQ(NOT_IMPLEMENTED_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &request));
// Counter should not increase.
std::string request, default_url;
EXPECT_EQ(NOT_IMPLEMENTED_ERROR,
provisioner_->GetProvisioningRequest(&request, &default_url));
// Post-response failure conditions.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(0u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
}
TEST_F(OtaKeyboxProvisionerTest, ResponseRejected) {
// Generate request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &request));
// Attempt to load response. OEMCrypto rejects response.
const std::string response = kFakeOtaProvisioningResponse;
crypto_session_->ExpectResponse(response, UNKNOWN_ERROR);
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), response));
// Should not be provisioned.
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_EQ(1u, provisioner_->request_count());
EXPECT_EQ(0u, provisioner_->response_count());
EXPECT_TRUE(provisioner_->IsInFallbackMode());
EXPECT_FALSE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
}
TEST_F(OtaKeyboxProvisionerTest, GenerateRequestAfterProvisioning) {
fallback_policy_->MarkProvisioned();
// Generate request.
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
std::string first_request;
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &first_request));
// Load response.
const std::string response = kFakeOtaProvisioningResponse;
crypto_session_->ExpectResponse(response, NO_ERROR);
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
crypto_session_.get(), response));
// Attempt to generate second request. Should fail.
std::string second_request;
EXPECT_NE(NO_ERROR, provisioner_->GenerateProvisioningRequest(
crypto_session_.get(), &second_request));
std::string request, default_url;
EXPECT_EQ(OKP_ALREADY_PROVISIONED,
provisioner_->GetProvisioningRequest(&request, &default_url));
EXPECT_TRUE(provisioner_->IsProvisioned());
EXPECT_EQ(1u, provisioner_->request_count());
EXPECT_EQ(1u, provisioner_->response_count());
EXPECT_FALSE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received());
}
TEST_F(OtaKeyboxProvisionerTest, ResponseWithoutRequest) {
std::string response;
MakeSignedOtaProvisioningResponse(kFakeOtaProvisioningResponse, &response);
// Does not trigger system-wide fallback. This is a bad app.
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(response));
EXPECT_FALSE(provisioner_->IsProvisioned());
EXPECT_FALSE(provisioner_->IsInFallbackMode());
EXPECT_FALSE(provisioner_->request_generated());
EXPECT_FALSE(provisioner_->response_received()); // Not actually received.
}
} // namespace wvcdm

View File

@@ -7,7 +7,6 @@
#include "test_printers.h"
namespace wvcdm {
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
switch (value) {
case NO_ERROR:
@@ -623,6 +622,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case NO_USAGE_ENTRIES:
*os << "NO_USAGE_ENTRIES";
break;
case OKP_ALREADY_PROVISIONED:
*os << "OKP_ALREADY_PROVISIONED";
break;
case OPEN_CRYPTO_SESSION_ERROR:
*os << "OPEN_CRYPTO_SESSION_ERROR";
break;
@@ -632,6 +634,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case PARAMETER_NULL:
*os << "PARAMETER_NULL";
break;
case PARSE_OKP_RESPONSE_ERROR:
*os << "PARSE_OKP_RESPONSE_ERROR";
break;
case PARSE_REQUEST_ERROR_1:
*os << "PARSE_REQUEST_ERROR_1";
break;
@@ -1210,5 +1215,9 @@ void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os) {
break;
}
}
namespace okp {
void PrintTo(const SystemState& state, std::ostream* os) {
*os << SystemStateToString(state);
}
} // namespace okp
} // namespace wvcdm

View File

@@ -8,7 +8,9 @@
#define CDM_TEST_PRINTERS_H_
#include <iostream>
#include "OEMCryptoCENC.h"
#include "okp_info.h"
#include "wv_cdm_types.h"
namespace wvcdm {
@@ -17,6 +19,8 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os);
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os);
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os);
void PrintTo(const enum OEMCryptoResult& value, ::std::ostream* os);
namespace okp {
void PrintTo(const SystemState& state, std::ostream* os);
} // namespace okp
} // namespace wvcdm
#endif // CDM_TEST_PRINTERS_H_