Wrapped OKP info into several classes.

[ Cherry pick of http://ag/15836995 ]
[ 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
Change-Id: Ia572a66a7b73479355758aa3d0c682691eaca0fc
This commit is contained in:
Rahul Frias
2021-09-15 05:00:19 -07:00
committed by Alex Dale
parent 3acc64a478
commit 6afcbab5cf
30 changed files with 2010 additions and 330 deletions

View File

@@ -24,11 +24,11 @@
#include "wv_cdm_types.h"
namespace wvcdm {
class CdmClientPropertySet;
class CdmEngineFactory;
class CdmSession;
class CryptoEngine;
class OtaKeyboxProvisioner;
class UsagePropertySet;
class WvCdmEventListener;
@@ -379,6 +379,13 @@ class CdmEngine {
void CloseExpiredReleaseSessions();
// Returns "true" if |okp_provisioner_| should be checked.
bool OkpCheck();
// Returns "true" if CdmEngine should always fallback to L3.
bool OkpIsInFallbackMode();
void OkpTriggerFallback();
void OkpCleanUp();
// instance variables
/*
@@ -423,6 +430,23 @@ class CdmEngine {
// occur that may subsequently call back into CdmEngine.
std::recursive_mutex session_map_lock_;
// OTA Keybox Provisioning (OKP)
// Engine should check for the OKP status of the device before opening
// sessions or generating DRM cert provisioning requests.
bool okp_initialized_ = false;
// If OKP is required, then the engine should create an instance
// of |okp_provisioner_|. If the instance exists, it should be used
// for GetProvisionRequest, ProvideProvisionRequest, and
// OpenSession when requested with default security level.
std::unique_ptr<OtaKeyboxProvisioner> okp_provisioner_;
// Should the engine need to fallback, this flag should be set to
// true and |okp_provisioner_| should be cleared. All follow-up
// requests from the app with security level default should use L3.
bool okp_fallback_ = false;
// To prevent race conditions around the engine's OKP state, this mutex
// should be locked before the use of any of the |okp_*| variables.
std::mutex okp_mutex_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
};

View File

@@ -60,9 +60,13 @@ class CdmSession {
//
// |event_listener| is caller owned, may be null, but must be in scope as long
// as the session is in scope.
//
// |forced_level3|_is used to specify whether the "default" security level
// should always use L3 even if L1 is available.
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener);
WvCdmEventListener* event_listener,
bool forced_level3);
// Restores an offline session identified by the |key_set_id| and
// |license_type|. The |error_detail| will be filled with an internal error
@@ -280,6 +284,9 @@ class CdmSession {
bool is_temporary_;
CdmSecurityLevel security_level_;
SecurityLevel requested_security_level_;
// If |forced_level3_|, |security_level_| and |requested_security_level_|
// MUST be set to kSecurityLevelL3 and kLevel3, respectively.
bool forced_level3_ = false;
CdmAppParameterMap app_parameters_;
bool atsc_mode_enabled_ = false;
std::string drm_certificate_;

View File

@@ -64,7 +64,7 @@ class CertificateProvisioning {
// Removes json wrapping if applicable to extract the
// SignedProvisioningMessage
static bool ExtractAndDecodeSignedMessageForTesting(
static bool ExtractAndDecodeSignedMessage(
const std::string& provisioning_response, std::string* result);
// Retrieve the provisioning server URL used for certificate

View File

@@ -23,12 +23,15 @@
#include "wv_cdm_types.h"
namespace wvcdm {
class CryptoKey;
class CryptoSessionFactory;
class OtaKeyboxProvisioner;
class UsageTableHeader;
namespace okp {
class SystemFallbackPolicy;
} // namespace okp
using CryptoKeyMap = std::map<std::string, CryptoKey*>;
// Crypto session utility functions used by KeySession implementations.
@@ -42,7 +45,6 @@ OEMCrypto_Substring GetSubstring(const std::string& message = "",
bool set_zero = false);
OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode);
class CryptoSession {
public:
using HdcpCapability = OEMCrypto_HDCP_Capability;
@@ -293,11 +295,11 @@ class CryptoSession {
bool needs_keybox_provisioning() const { return needs_keybox_provisioning_; }
// Returns a system-wide singleton instance of OtaKeyboxProvisioner
// to be used for OTA provisioning requests/responses across apps.
// Returns a null pointer if OTA provisioning is NOT supported, or
// if the device has already been provisioned.
virtual OtaKeyboxProvisioner* GetOtaKeyboxProvisioner();
// Returns a system-wide singleton instance of SystemFallbackPolicy
// to be used for communicating OTA keybox provisioning state between
// apps. Returns a null pointer if OTA provisioning is not supported,
// or if the device has already been provisioned.
static okp::SystemFallbackPolicy* GetOkpFallbackPolicy();
// Generates an OTA provisioning request.
// This should only be called by an instance of OtaKeyboxProvisioner.
@@ -513,10 +515,10 @@ class CryptoSession {
static std::mutex factory_mutex_;
static std::unique_ptr<CryptoSessionFactory> factory_;
// A singleton instance of OtaKeyboxProvisioner. Only one will
// A singleton instance of SystemFallbackPolicy. Only one will
// be created for the system if OTA keybox provisioning is both
// required and supported by L1.
static std::unique_ptr<OtaKeyboxProvisioner> ota_keybox_provisioner_l1_;
static std::unique_ptr<okp::SystemFallbackPolicy> okp_fallback_policy_l1_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
}; // class CryptoSession

View File

@@ -12,6 +12,7 @@
#include "crypto_wrapped_key.h"
#include "device_files.pb.h"
#include "disallow_copy_and_assign.h"
#include "okp_info.h"
#include "platform.h"
#include "wv_cdm_types.h"
@@ -271,6 +272,11 @@ class DeviceFiles {
virtual bool DeleteUsageTableInfo();
// OTA Keybox Provisioning (OKP) information.
virtual bool StoreOkpInfo(const okp::SystemFallbackInfo& info);
virtual bool RetrieveOkpInfo(okp::SystemFallbackInfo* info);
virtual bool DeleteOkpInfo();
private:
// This method will retrieve the certificate and perform expiry validation
// appropriate for a given certificate type
@@ -299,6 +305,7 @@ class DeviceFiles {
static std::string GetHlsAttributesFileNameExtension();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageTableFileName();
static std::string GetOkpInfoFileName();
static std::string GetFileNameSafeHash(const std::string& input);
#if defined(UNIT_TEST)
@@ -327,6 +334,9 @@ class DeviceFiles {
FRIEND_TEST(DeviceFilesTest, StoreCertificateInvalidParams);
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
FRIEND_TEST(DeviceFilesTest, OkpInfo_FileDoesNotExist);
FRIEND_TEST(DeviceFilesTest, OkpInfo_DeleteFile);
FRIEND_TEST(DeviceFilesTest, OkpInfo_StoreAndRetrieve);
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
FRIEND_TEST(DeviceFilesUsageInfoTest, DeleteAll);
FRIEND_TEST(DeviceFilesUsageInfoTest, Read);

View File

@@ -0,0 +1,113 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CORE_OKP_FALLBACK_POLICY_H_
#define WVCDM_CORE_OKP_FALLBACK_POLICY_H_
#include <inttypes.h>
#include <memory>
#include <mutex>
#include "clock.h"
#include "disallow_copy_and_assign.h"
#include "okp_info.h"
namespace wvcdm {
class DeviceFiles;
class FileSystem;
// OTA Keybox Provisioning (OKP)
namespace okp {
static constexpr int64_t kSecondsPerHour = 60 * 60;
static constexpr int64_t kSecondsPerDay = kSecondsPerHour * 24;
// Initial backoff duration. Subsequent backoff durations for the
// same engine will double its previous duration.
static constexpr int64_t kAverageInitialBackoffDuration = kSecondsPerDay;
static constexpr int64_t kInitalBackoffDurationDelta = kSecondsPerHour * 12;
// Minimum backoff duration which an device will be required to
// backoff the first time.
static constexpr int64_t kMinInitialBackoffDuration =
kAverageInitialBackoffDuration - kInitalBackoffDurationDelta;
static constexpr int64_t kMaxInitialBackoffDuration =
kAverageInitialBackoffDuration + kInitalBackoffDurationDelta;
// SystemFallbackPolicy is a centralized OKP state manager which allows
// multiple CDM engines to communicate between each other. In a production
// build, there should only be at most one SystemFallbackPolicy instance.
class SystemFallbackPolicy {
public:
// Creates a new instance of SystemFallbackPolicy. If there exists
// OKP information for the device in storage, it will be loaded and
// the system policy will resume from its previous state. If no
// OKP information exists, then the policy begins new.
// Caller should immediately mark the fallback policy as requiring
// provisioning.
static std::unique_ptr<SystemFallbackPolicy> Create();
// Creates a new instance of SystemFallbackPolicy for testing.
// The testing instance of SystemFallbackPolicy behaves similar to a
// production instance, except that it will not use device storage.
// Optionally, a fake clock may be used for timestamp operations
// and/or fake data may be used to initialize the policy.
// Params:
// - |info| (optional)
// Fake device OKP info to use as a resume point. If not
// specified, then policy begins the same as if no OKP
// device info exists.
// - |clock| (optional)
// Fake/mock clock to be used instead of the CDM's default
// Clock.
static std::unique_ptr<SystemFallbackPolicy> CreateForTesting(
Clock* clock = nullptr);
static std::unique_ptr<SystemFallbackPolicy> CreateForTesting(
const SystemFallbackInfo& info, Clock* clock = nullptr);
// == System Info ==
const SystemFallbackInfo& info() const { return info_; }
SystemState state() const { return info_.state(); }
void MarkNeedsProvisioning();
void TriggerFallback();
void MarkProvisioned();
bool IsProvisioned();
bool IsInFallbackMode();
~SystemFallbackPolicy();
private:
SystemFallbackPolicy();
// Checks the device's file system for OKP info and loads it.
// If the info does not exist, policy begins fresh.
void TryRestore();
void StoreInfo();
int64_t GetSecondsSinceBackoffStart() const;
void EndBackoffPeriod();
void SetClockForTesting(Clock* clock) {
clock_ref_ = (clock == nullptr) ? &clock_ : clock;
}
int64_t GetCurrentTime() const { return clock_ref_->GetCurrentTime(); }
bool IsTestMode() const;
SystemFallbackInfo info_;
// Handle for the DeviceFiles instance used to store the OKP
// information.
// Not set for test instances.
std::unique_ptr<FileSystem> fs_;
std::unique_ptr<DeviceFiles> device_files_;
Clock clock_; // System clock
Clock* clock_ref_ = nullptr; // Pointer to clock to be used.
// All public methods must lock to protect from simultaneous
// engine access.
mutable std::mutex mutex_;
CORE_DISALLOW_COPY_AND_ASSIGN(SystemFallbackPolicy);
}; // class SystemFallbackPolicy
} // namespace okp
} // namespace wvcdm
#endif // WVCDM_CORE_OKP_FALLBACK_POLICY_H_

View File

@@ -0,0 +1,78 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CORE_OKP_SYSTEM_INFO_H_
#define WVCDM_CORE_OKP_SYSTEM_INFO_H_
#include <inttypes.h>
namespace wvcdm {
// OTA Keybox Provisioning (OKP)
namespace okp {
enum class SystemState {
kUnknown = 0,
kNeedsProvisioning = 1,
kFallbackMode = 2, // Fallback indicates provisioning is needed.
kProvisioned = 3
// Note: "Not needed" is represented by an absence of info.
};
// Converts a SystemState value to a human readable string. Intended
// to be used for debug logging.
const char* SystemStateToString(SystemState state);
// Container for all the device information related to OKP.
class SystemFallbackInfo {
public:
SystemState state() const { return state_; }
void SetState(SystemState state) { state_ = state; }
bool HasFirstCheckedTime() const { return first_checked_time_ != 0; }
int64_t first_checked_time() const { return first_checked_time_; }
void SetFirstCheckedTime(int64_t time) {
first_checked_time_ = (time > 0 ? time : 0);
}
bool HasBackoffStartTime() const { return backoff_start_time_ > 0; }
int64_t backoff_start_time() const { return backoff_start_time_; }
void SetBackoffStartTime(int64_t time) {
backoff_start_time_ = (time > 0 ? time : 0);
}
void ClearBackoffStartTime() { backoff_start_time_ = 0; }
bool HasBackoffDuration() const { return backoff_duration_ > 0; }
int64_t backoff_duration() const { return backoff_duration_; }
void SetBackoffDuration(int64_t duration) {
backoff_duration_ = (duration > 0 ? duration : 0);
}
void DoubleBackoffDuration() { backoff_duration_ *= 2; }
bool HasProvisioningTime() const { return provisioning_time_ != 0; }
int64_t provisioning_time() const { return provisioning_time_; }
void SetProvisioningTime(int64_t time) {
provisioning_time_ = (time > 0 ? time : 0);
}
void ClearProvisioningTime() { provisioning_time_ = 0; }
void Clear() {
state_ = SystemState::kUnknown;
first_checked_time_ = 0;
backoff_start_time_ = 0;
backoff_duration_ = 0;
provisioning_time_ = 0;
}
bool operator==(const SystemFallbackInfo& other) const;
bool operator!=(const SystemFallbackInfo& other) const {
return !(*this == other);
}
private:
SystemState state_ = SystemState::kUnknown;
int64_t first_checked_time_ = 0;
int64_t backoff_start_time_ = 0;
int64_t backoff_duration_ = 0;
int64_t provisioning_time_ = 0;
}; // class SystemFallbackInfo
} // namespace okp
} // namespace wvcdm
#endif // WVCDM_CORE_OKP_SYSTEM_INFO_H_

View File

@@ -5,62 +5,79 @@
#define WVCDM_CORE_OTA_KEYBOX_PROVISIONER_H_
#include <memory>
#include <mutex>
#include <string>
#include "client_identification.h"
#include "disallow_copy_and_assign.h"
#include "metrics_collections.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class CryptoSession;
namespace okp {
class SystemFallbackPolicy;
} // namespace okp
// Wrapper around an OEMCrypto system-wide OTA keybox provisioning
// workflow.
// A CdmEngine-specific OTA keybox provisioning context.
class OtaKeyboxProvisioner {
public:
// Creates a new OTA keybox provisioner. This should only be
// created once and object ownership belongs to the CryptoSession
// module.
static std::unique_ptr<OtaKeyboxProvisioner> Create();
// Creates a new OtaKeyboxProvisioner.
// Checks for the system fallback policy and if the device
// requires provisioning.
// |crypto_metrics| - CryptoMetrics instance that is used in the
// the calling EngineMetrics.
static std::unique_ptr<OtaKeyboxProvisioner> Create(
metrics::CryptoMetrics* crypto_metrics);
static std::unique_ptr<OtaKeyboxProvisioner> CreateForTesting(
std::unique_ptr<CryptoSession>&& crypto_session,
okp::SystemFallbackPolicy* fallback_policy);
~OtaKeyboxProvisioner();
// Returns true if the underlying SystemFallbackPolicy is
// provisioned.
// Note: This may change without a call to HandleProvisioningResponse()
// on this instance as provisioning is a system-wide responsibility.
bool IsProvisioned() const;
bool IsInFallbackMode() const;
// Indicates that a request has been successfully generated.
uint32_t request_generated() const { return request_generated_; }
// Indicates that a response has been successfully received by
// this provisioner.
bool response_received() const { return response_received_; }
// === Request/response API ===
// Returns true if a provisioning response has been provided
// and accepted by OEMCrytpo.
bool IsProvisioned() const { return is_provisioned_; }
uint32_t request_count() const { return request_count_; }
uint32_t response_count() const { return response_count_; }
// Generates an OTA provisioning request.
// Generating a request will succeed so long as OTA provisioning
// is supported and no valid response has been provided.
CdmResponseType GenerateProvisioningRequest(CryptoSession* crypto_session,
std::string* request);
// Accepts a provisioning response from the OTA provisioning
// server. The first response which is successfully loaded is
// is used. Any subsequent response after the first successful
// response is silently discarded.
CdmResponseType HandleProvisioningResponse(CryptoSession* crypto_session,
const std::string& response);
// Generates and prepares a OTA Keybox Provisioning request, packing
// it into a SignedProvisioningMessage.
// |default_url| will be populated with the URL of the provisioning
// server used for OTA keybox provisioning.
CdmResponseType GetProvisioningRequest(std::string* request,
std::string* default_url);
// Receives, unwraps and loads the OTA Keybox Provisioning response.
// |response| must be a SignedProvisioningMessage containing an
// OTA keybox provisioning response.
CdmResponseType HandleProvisioningResponse(const std::string& response);
private:
OtaKeyboxProvisioner();
OtaKeyboxProvisioner(std::unique_ptr<CryptoSession>&& crypto_session,
okp::SystemFallbackPolicy* fallback_policy);
bool is_provisioned_ = false;
bool Init();
// These counters are for debugging purposes.
// Number of requests generated.
uint32_t request_count_ = 0;
// Number of responses provided.
uint32_t response_count_ = 0;
void CleanUp();
// It is expected that multiple CDM engines may interact with the
// OtaKeyboxProvisioner instance simultaneously.
mutable std::mutex mutex_;
std::unique_ptr<CryptoSession> crypto_session_;
ClientIdentification client_id_;
// Pointer to the system-wide okp::SystemFallbackPolicy. This class
// does not take ownership of this pointer.
okp::SystemFallbackPolicy* fallback_policy_ = nullptr;
// These flags are for debugging purposes.
bool request_generated_ = false;
bool response_received_ = false;
CORE_DISALLOW_COPY_AND_ASSIGN(OtaKeyboxProvisioner);
}; // class OtaKeyboxProvisioner

View File

@@ -421,6 +421,8 @@ enum CdmResponseType : int32_t {
CERT_PROVISIONING_RESPONSE_ERROR_10 = 366,
CLIENT_TOKEN_NOT_SET = 367,
USAGE_ENTRY_ALREADY_LOADED = 368,
PARSE_OKP_RESPONSE_ERROR = 369,
OKP_ALREADY_PROVISIONED = 370,
// Don't forget to add new values to
// * core/test/test_printers.cpp.
// * android/include/mapErrors-inl.h