Added an OTA keybox provisioner.
[ Merge of http://go/wvgerrit/133729 ] [ Cherry pick of http://ag/15836224 ] The OtaKeyboxProvisioner is a system-wide provisioner for sharing the provisioning workflow between CDM engines. Bug: 189232882 Test: GtsMediaTestCases Change-Id: I873af3087cc05e1831bdd1d2c14fb002b73e6902 Added keybox provisioning proto fields. [ Merge of http://go/wvgerrit/133730 and http://go/ag/15113032 ] This CL copies over the required license_protocol.proto changes that are required for OTA keybox provisioning. These fields are defined in the server-side certificate_provisioning.proto, defined in http://cl/377533774. Note, changes are slightly different from server proto due to the RVC version of license_protocol.proto being out of date with SC and newer changes. Bug: 189232882 Test: run_x86_64_tests Change-Id: I55fcf6a7ac2ba4b6026b9acc63e822ff33c431d9 Added OTA keybox provisioning device files. [ Merge of http://go/wvgerrit/133743 and http://go/ag/15421141 ] This change adds a new set of proto messages/fields the CDM's device files for recording device and engine information around OTA keybox provisioning (OKP). To make cleanup and thread protection possible, there is a single file which will contain all the information for the device as a whole and each CDM engine tied to an app/origin. Bug: 189232882 Test: Linux unit tests Change-Id: Iaf80cd6342f32657e04416750d9b278d935821a5 Client ID for OKP requests. [ Merge of http://go/wvgerrit/133744 and http://go/ag/15645331 ] Extended the CDM ClientIdentification class to support a subset of client info used for OKP requests. Bug: 189232882 Test: Android unit tests Change-Id: I6aafb4f2164efe69bc733ece0a912f0e91893b91
This commit is contained in:
@@ -54,6 +54,7 @@ cc_library_static {
|
|||||||
CORE_SRC_DIR + "/license.cpp",
|
CORE_SRC_DIR + "/license.cpp",
|
||||||
CORE_SRC_DIR + "/license_key_status.cpp",
|
CORE_SRC_DIR + "/license_key_status.cpp",
|
||||||
CORE_SRC_DIR + "/oemcrypto_adapter_dynamic.cpp",
|
CORE_SRC_DIR + "/oemcrypto_adapter_dynamic.cpp",
|
||||||
|
CORE_SRC_DIR + "/ota_keybox_provisioner.cpp",
|
||||||
CORE_SRC_DIR + "/policy_engine.cpp",
|
CORE_SRC_DIR + "/policy_engine.cpp",
|
||||||
CORE_SRC_DIR + "/policy_timers.cpp",
|
CORE_SRC_DIR + "/policy_timers.cpp",
|
||||||
CORE_SRC_DIR + "/policy_timers_v15.cpp",
|
CORE_SRC_DIR + "/policy_timers_v15.cpp",
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ class CertificateProvisioning {
|
|||||||
static bool ExtractAndDecodeSignedMessageForTesting(
|
static bool ExtractAndDecodeSignedMessageForTesting(
|
||||||
const std::string& provisioning_response, std::string* result);
|
const std::string& provisioning_response, std::string* result);
|
||||||
|
|
||||||
|
// Retrieve the provisioning server URL used for certificate
|
||||||
|
// provisioning. This will be the same value as returned in
|
||||||
|
// |default_url| by GetProvisioningRequest().
|
||||||
|
static void GetProvisioningServerUrl(std::string* default_url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CdmResponseType GetProvisioningRequestInternal(
|
CdmResponseType GetProvisioningRequestInternal(
|
||||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||||
|
|||||||
@@ -5,33 +5,35 @@
|
|||||||
#ifndef WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
#ifndef WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
||||||
#define WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
#define WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// ClientIdentification fills in the ClientIdentification portion
|
// ClientIdentification fills in the ClientIdentification portion
|
||||||
// of the License or Provisioning request messages.
|
// of the License or Provisioning request messages.
|
||||||
|
|
||||||
#include "disallow_copy_and_assign.h"
|
#include "disallow_copy_and_assign.h"
|
||||||
#include "license_protocol.pb.h"
|
#include "license_protocol.pb.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
|
|
||||||
class CryptoSession;
|
class CryptoSession;
|
||||||
|
|
||||||
class ClientIdentification {
|
class ClientIdentification {
|
||||||
public:
|
public:
|
||||||
ClientIdentification() : is_license_request_(true) {}
|
ClientIdentification() {}
|
||||||
virtual ~ClientIdentification() {}
|
virtual ~ClientIdentification() {}
|
||||||
|
|
||||||
// Call this method when used with provisioning requests
|
// Call this method when used with provisioning requests
|
||||||
CdmResponseType Init(CryptoSession* crypto_session);
|
CdmResponseType InitForProvisioning(CryptoSession* crypto_session);
|
||||||
|
|
||||||
// Use in conjunction with license requests
|
// Use in conjunction with license requests
|
||||||
// |client_token| must be provided
|
// |client_token| must be provided
|
||||||
// |crypto_session| input parameter, mandatory
|
// |crypto_session| input parameter, mandatory
|
||||||
CdmResponseType Init(const std::string& client_token,
|
CdmResponseType InitForLicenseRequest(const std::string& client_token,
|
||||||
CryptoSession* crypto_session);
|
CryptoSession* crypto_session);
|
||||||
|
|
||||||
// Fill the ClientIdentification portion of the license or provisioning
|
CdmResponseType InitForOtaKeyboxProvisioning(CryptoSession* crypto_session);
|
||||||
// request
|
|
||||||
|
// Fill the ClientIdentification portion of the license, DRM cert
|
||||||
|
// provisioning or OTA keybox provisioning request.
|
||||||
// |app_parameters| parameters provided by client/app to be included in
|
// |app_parameters| parameters provided by client/app to be included in
|
||||||
// provisioning/license request. optional, only used
|
// provisioning/license request. optional, only used
|
||||||
// if |is_license_request| is true
|
// if |is_license_request| is true
|
||||||
@@ -49,13 +51,13 @@ class ClientIdentification {
|
|||||||
bool GetProvisioningTokenType(
|
bool GetProvisioningTokenType(
|
||||||
video_widevine::ClientIdentification::TokenType* token_type);
|
video_widevine::ClientIdentification::TokenType* token_type);
|
||||||
|
|
||||||
bool is_license_request_;
|
bool is_license_request_ = false;
|
||||||
|
bool is_okp_request_ = false;
|
||||||
std::string client_token_;
|
std::string client_token_;
|
||||||
CryptoSession* crypto_session_;
|
std::string device_id_;
|
||||||
|
CryptoSession* crypto_session_ = nullptr;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(ClientIdentification);
|
CORE_DISALLOW_COPY_AND_ASSIGN(ClientIdentification);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace wvcdm
|
} // namespace wvcdm
|
||||||
|
|
||||||
#endif // WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
#endif // WVCDM_CORE_CLIENT_IDENTIFICATION_H_
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace wvcdm {
|
|||||||
|
|
||||||
class CryptoKey;
|
class CryptoKey;
|
||||||
class CryptoSessionFactory;
|
class CryptoSessionFactory;
|
||||||
|
class OtaKeyboxProvisioner;
|
||||||
class UsageTableHeader;
|
class UsageTableHeader;
|
||||||
|
|
||||||
using CryptoKeyMap = std::map<std::string, CryptoKey*>;
|
using CryptoKeyMap = std::map<std::string, CryptoKey*>;
|
||||||
@@ -41,6 +42,7 @@ OEMCrypto_Substring GetSubstring(const std::string& message = "",
|
|||||||
bool set_zero = false);
|
bool set_zero = false);
|
||||||
OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode);
|
OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode);
|
||||||
|
|
||||||
|
|
||||||
class CryptoSession {
|
class CryptoSession {
|
||||||
public:
|
public:
|
||||||
using HdcpCapability = OEMCrypto_HDCP_Capability;
|
using HdcpCapability = OEMCrypto_HDCP_Capability;
|
||||||
@@ -288,7 +290,14 @@ class CryptoSession {
|
|||||||
SecurityLevel requested_security_level, CdmClientTokenType* token_type);
|
SecurityLevel requested_security_level, CdmClientTokenType* token_type);
|
||||||
|
|
||||||
// OTA Provisioning
|
// OTA Provisioning
|
||||||
// TODO(sigquit): include rest of http://go/wvgerrit/126004
|
|
||||||
|
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();
|
||||||
|
|
||||||
// Generates an OTA provisioning request.
|
// Generates an OTA provisioning request.
|
||||||
// This should only be called by an instance of OtaKeyboxProvisioner.
|
// This should only be called by an instance of OtaKeyboxProvisioner.
|
||||||
@@ -504,6 +513,11 @@ class CryptoSession {
|
|||||||
static std::mutex factory_mutex_;
|
static std::mutex factory_mutex_;
|
||||||
static std::unique_ptr<CryptoSessionFactory> factory_;
|
static std::unique_ptr<CryptoSessionFactory> factory_;
|
||||||
|
|
||||||
|
// A singleton instance of OtaKeyboxProvisioner. 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_;
|
||||||
|
|
||||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
||||||
}; // class CryptoSession
|
}; // class CryptoSession
|
||||||
|
|
||||||
|
|||||||
68
libwvdrmengine/cdm/core/include/ota_keybox_provisioner.h
Normal file
68
libwvdrmengine/cdm/core/include/ota_keybox_provisioner.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// 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_OTA_KEYBOX_PROVISIONER_H_
|
||||||
|
#define WVCDM_CORE_OTA_KEYBOX_PROVISIONER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "disallow_copy_and_assign.h"
|
||||||
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
class CryptoSession;
|
||||||
|
|
||||||
|
// Wrapper around an OEMCrypto system-wide OTA keybox provisioning
|
||||||
|
// workflow.
|
||||||
|
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();
|
||||||
|
|
||||||
|
~OtaKeyboxProvisioner();
|
||||||
|
|
||||||
|
// === 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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OtaKeyboxProvisioner();
|
||||||
|
|
||||||
|
bool is_provisioned_ = false;
|
||||||
|
|
||||||
|
// These counters are for debugging purposes.
|
||||||
|
// Number of requests generated.
|
||||||
|
uint32_t request_count_ = 0;
|
||||||
|
// Number of responses provided.
|
||||||
|
uint32_t response_count_ = 0;
|
||||||
|
|
||||||
|
// It is expected that multiple CDM engines may interact with the
|
||||||
|
// OtaKeyboxProvisioner instance simultaneously.
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
|
||||||
|
CORE_DISALLOW_COPY_AND_ASSIGN(OtaKeyboxProvisioner);
|
||||||
|
}; // class OtaKeyboxProvisioner
|
||||||
|
} // namespace wvcdm
|
||||||
|
#endif // WVCDM_CORE_OTA_KEYBOX_PROVISIONER_H_
|
||||||
@@ -118,6 +118,16 @@ using video_widevine::SignedProvisioningMessage;
|
|||||||
using video_widevine::
|
using video_widevine::
|
||||||
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1;
|
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1;
|
||||||
|
|
||||||
|
// static
|
||||||
|
void CertificateProvisioning::GetProvisioningServerUrl(
|
||||||
|
std::string* default_url) {
|
||||||
|
if (default_url == nullptr) {
|
||||||
|
LOGE("Output |default_url| is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default_url->assign(kProvisioningServerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
CdmResponseType CertificateProvisioning::Init(
|
CdmResponseType CertificateProvisioning::Init(
|
||||||
const std::string& service_certificate) {
|
const std::string& service_certificate) {
|
||||||
std::string certificate = service_certificate.empty()
|
std::string certificate = service_certificate.empty()
|
||||||
@@ -221,7 +231,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
|
|||||||
ProvisioningRequest provisioning_request;
|
ProvisioningRequest provisioning_request;
|
||||||
|
|
||||||
wvcdm::ClientIdentification id;
|
wvcdm::ClientIdentification id;
|
||||||
status = id.Init(crypto_session_.get());
|
status = id.InitForProvisioning(crypto_session_.get());
|
||||||
if (status != NO_ERROR) return status;
|
if (status != NO_ERROR) return status;
|
||||||
|
|
||||||
video_widevine::ClientIdentification client_id;
|
video_widevine::ClientIdentification client_id;
|
||||||
|
|||||||
@@ -66,35 +66,44 @@ using video_widevine::ProvisioningRequest;
|
|||||||
using video_widevine::ProvisioningResponse;
|
using video_widevine::ProvisioningResponse;
|
||||||
using video_widevine::SignedProvisioningMessage;
|
using video_widevine::SignedProvisioningMessage;
|
||||||
|
|
||||||
CdmResponseType ClientIdentification::Init(CryptoSession* crypto_session) {
|
CdmResponseType ClientIdentification::InitForProvisioning(
|
||||||
|
CryptoSession* crypto_session) {
|
||||||
if (crypto_session == nullptr) {
|
if (crypto_session == nullptr) {
|
||||||
LOGE("Crypto session not provided");
|
LOGE("Crypto session not provided");
|
||||||
return PARAMETER_NULL;
|
return PARAMETER_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_license_request_ = false;
|
is_license_request_ = false;
|
||||||
crypto_session_ = crypto_session;
|
crypto_session_ = crypto_session;
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType ClientIdentification::Init(const std::string& client_token,
|
CdmResponseType ClientIdentification::InitForLicenseRequest(
|
||||||
CryptoSession* crypto_session) {
|
const std::string& client_token, CryptoSession* crypto_session) {
|
||||||
if (crypto_session == nullptr) {
|
if (crypto_session == nullptr) {
|
||||||
LOGE("Crypto session not provided");
|
LOGE("Crypto session not provided");
|
||||||
return PARAMETER_NULL;
|
return PARAMETER_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_token.empty()) {
|
if (client_token.empty()) {
|
||||||
LOGE("Client token is empty");
|
LOGE("Client token is empty");
|
||||||
return PARAMETER_NULL;
|
return PARAMETER_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_license_request_ = true;
|
is_license_request_ = true;
|
||||||
client_token_ = client_token;
|
client_token_ = client_token;
|
||||||
crypto_session_ = crypto_session;
|
crypto_session_ = crypto_session;
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning(
|
||||||
|
CryptoSession* crypto_session) {
|
||||||
|
if (crypto_session == nullptr) {
|
||||||
|
LOGE("Crypto session not provided");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
is_okp_request_ = true;
|
||||||
|
crypto_session_ = crypto_session;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the ClientIdentification message token type for provisioning request.
|
* Return the ClientIdentification message token type for provisioning request.
|
||||||
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
||||||
@@ -107,7 +116,7 @@ CdmResponseType ClientIdentification::Prepare(
|
|||||||
client_id->set_type(
|
client_id->set_type(
|
||||||
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
||||||
client_id->set_token(client_token_);
|
client_id->set_token(client_token_);
|
||||||
} else {
|
} else if (!is_okp_request_) {
|
||||||
video_widevine::ClientIdentification::TokenType token_type;
|
video_widevine::ClientIdentification::TokenType token_type;
|
||||||
if (!GetProvisioningTokenType(&token_type)) {
|
if (!GetProvisioningTokenType(&token_type)) {
|
||||||
LOGE("Failed to get provisioning token type");
|
LOGE("Failed to get provisioning token type");
|
||||||
@@ -189,6 +198,11 @@ CdmResponseType ClientIdentification::Prepare(
|
|||||||
client_id->set_provider_client_token(provider_client_token);
|
client_id->set_provider_client_token(provider_client_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_okp_request_) {
|
||||||
|
// Capabilities is not important for OTA keybox provisionining.
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
ClientCapabilities* client_capabilities =
|
ClientCapabilities* client_capabilities =
|
||||||
client_id->mutable_client_capabilities();
|
client_id->mutable_client_capabilities();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "crypto_key.h"
|
#include "crypto_key.h"
|
||||||
#include "entitlement_key_session.h"
|
#include "entitlement_key_session.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "ota_keybox_provisioner.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "privacy_crypto.h"
|
#include "privacy_crypto.h"
|
||||||
#include "properties.h"
|
#include "properties.h"
|
||||||
@@ -178,6 +179,7 @@ std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l1_;
|
|||||||
std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l3_;
|
std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l3_;
|
||||||
std::recursive_mutex CryptoSession::usage_table_mutex_;
|
std::recursive_mutex CryptoSession::usage_table_mutex_;
|
||||||
std::atomic<uint64_t> CryptoSession::request_id_index_source_(0);
|
std::atomic<uint64_t> CryptoSession::request_id_index_source_(0);
|
||||||
|
std::unique_ptr<OtaKeyboxProvisioner> CryptoSession::ota_keybox_provisioner_l1_;
|
||||||
|
|
||||||
size_t GetOffset(std::string message, std::string field) {
|
size_t GetOffset(std::string message, std::string field) {
|
||||||
size_t pos = message.find(field);
|
size_t pos = message.find(field);
|
||||||
@@ -360,8 +362,15 @@ void CryptoSession::Init() {
|
|||||||
LOGD("OEMCrypto version (L3 security level): %s.%s", api_version.c_str(),
|
LOGD("OEMCrypto version (L3 security level): %s.%s", api_version.c_str(),
|
||||||
api_minor_version.c_str());
|
api_minor_version.c_str());
|
||||||
if (needs_keybox_provisioning_) {
|
if (needs_keybox_provisioning_) {
|
||||||
LOGE("OEMCrypto needs provisioning");
|
WithStaticFieldWriteLock("OtaKeyboxProvisioner", [&] {
|
||||||
// TODO(fredgc,sigquit,rfrias): handle provisioning.
|
if (!ota_keybox_provisioner_l1_) {
|
||||||
|
LOGD("OEMCrypto needs keybox provisioning");
|
||||||
|
// Only create once. Possible that OEMCrypto is initialized
|
||||||
|
// and terminated many times over the life cycle of the OTA
|
||||||
|
// keybox provisioning process.
|
||||||
|
ota_keybox_provisioner_l1_ = OtaKeyboxProvisioner::Create();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3011,11 +3020,20 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
|
|||||||
remaining_input_data -= chunk_size;
|
remaining_input_data -= chunk_size;
|
||||||
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, chunk_size);
|
AdvanceDestBuffer(&fake_sample.buffers.output_descriptor, chunk_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(sigquit): include rest of http://go/wvgerrit/126004
|
OtaKeyboxProvisioner* CryptoSession::GetOtaKeyboxProvisioner() {
|
||||||
|
const auto getter = [&]() -> OtaKeyboxProvisioner* {
|
||||||
|
// If not set, then OTA keybox provisioning is not supported or
|
||||||
|
// not needed.
|
||||||
|
if (!ota_keybox_provisioner_l1_) return nullptr;
|
||||||
|
// May have already been initialized.
|
||||||
|
if (ota_keybox_provisioner_l1_->IsProvisioned()) return nullptr;
|
||||||
|
return ota_keybox_provisioner_l1_.get();
|
||||||
|
};
|
||||||
|
return WithStaticFieldReadLock("GetOtaKeyboxProvisioner", getter);
|
||||||
|
}
|
||||||
|
|
||||||
CdmResponseType CryptoSession::PrepareOtaProvisioningRequest(
|
CdmResponseType CryptoSession::PrepareOtaProvisioningRequest(
|
||||||
bool use_test_key, std::string* request) {
|
bool use_test_key, std::string* request) {
|
||||||
@@ -3026,17 +3044,25 @@ CdmResponseType CryptoSession::PrepareOtaProvisioningRequest(
|
|||||||
if (status != OEMCrypto_ERROR_SHORT_BUFFER)
|
if (status != OEMCrypto_ERROR_SHORT_BUFFER)
|
||||||
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
|
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
|
||||||
"PrepareOtaProvisioningRequest");
|
"PrepareOtaProvisioningRequest");
|
||||||
std::string temp_buffer(buffer_length, '\0');
|
if (buffer_length == 0) {
|
||||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
LOGE("OTA request size is zero");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
request->resize(buffer_length);
|
||||||
|
uint8_t* buf = reinterpret_cast<uint8_t*>(&request->front());
|
||||||
status = OEMCrypto_GenerateOTARequest(buf, &buffer_length, use_test_key);
|
status = OEMCrypto_GenerateOTARequest(buf, &buffer_length, use_test_key);
|
||||||
if (OEMCrypto_SUCCESS == status) request->assign(temp_buffer);
|
if (OEMCrypto_SUCCESS != status) {
|
||||||
|
request->clear();
|
||||||
|
} else if (buffer_length != request->size()) {
|
||||||
|
request->resize(buffer_length);
|
||||||
|
}
|
||||||
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
|
return MapOEMCryptoResult(status, UNKNOWN_ERROR,
|
||||||
"PrepareOtaProvisioningRequest");
|
"PrepareOtaProvisioningRequest");
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType CryptoSession::LoadOtaProvisioning(
|
CdmResponseType CryptoSession::LoadOtaProvisioning(
|
||||||
bool use_test_key, const std::string& response) {
|
bool use_test_key, const std::string& response) {
|
||||||
OEMCryptoResult status = OEMCrypto_ProcessOTAKeybox(
|
const OEMCryptoResult status = OEMCrypto_ProcessOTAKeybox(
|
||||||
reinterpret_cast<const uint8_t*>(response.data()), response.size(),
|
reinterpret_cast<const uint8_t*>(response.data()), response.size(),
|
||||||
use_test_key);
|
use_test_key);
|
||||||
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "LoadOtaProvisioning");
|
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "LoadOtaProvisioning");
|
||||||
|
|||||||
@@ -125,6 +125,71 @@ message UsageTableInfo {
|
|||||||
optional bool use_lru = 3 [default = false];
|
optional bool use_lru = 3 [default = false];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stores information related to a device's experience with OTA Keybox
|
||||||
|
// Provisioning (OKP). Only devices which both support OKP and require
|
||||||
|
// OKP should create this file. Otherwise, this information is not
|
||||||
|
// needed.
|
||||||
|
message OtaKeyboxProvisioningInfo {
|
||||||
|
// Engine-specific information about OKP.
|
||||||
|
message OkpEngineInfo {
|
||||||
|
// Engine identifier.
|
||||||
|
optional bytes app_id = 1;
|
||||||
|
optional bytes origin = 2;
|
||||||
|
reserved 3 to 5; // Reserved for future engine composite keys.
|
||||||
|
// Counters for engine-specific OKP events.
|
||||||
|
// These counters are reset after a certain amount of time
|
||||||
|
// (OKP period) since the last event.
|
||||||
|
// Number of calls to openSession() where it is recommended
|
||||||
|
// to the app to try keybox provisioning.
|
||||||
|
optional uint32 try_okp_counter = 6;
|
||||||
|
// Number of calls to getProvisionRequest().
|
||||||
|
optional uint32 generate_request_counter = 7;
|
||||||
|
// Number of failed calls to provideProvisionRequest().
|
||||||
|
optional uint32 failed_response_counter = 8;
|
||||||
|
|
||||||
|
// The value of |last_event_time| and |backoff_start_time| are set
|
||||||
|
// using the system's wall-clock in epoch seconds. A value of
|
||||||
|
// zero indicates it's not set.
|
||||||
|
|
||||||
|
// Time of the last engine OKP event (change of the above counters;
|
||||||
|
// the beginning of the current OKP period).
|
||||||
|
// Zero indicates no event has yet occurred.
|
||||||
|
optional int64 last_event_time = 9;
|
||||||
|
// Beginning of an app/origin backoff period.
|
||||||
|
// Zero indicates that engine is not in a backoff state.
|
||||||
|
optional int64 backoff_start_time = 10;
|
||||||
|
// Intended length of “backoff period”. This will be assigned a
|
||||||
|
// random duration initially, then double each time an engine
|
||||||
|
// enters a backoff state. This is base on Google's recommended
|
||||||
|
// exponential backoff rules.
|
||||||
|
// Value of 0 indicates that backoff has not yet occurred.
|
||||||
|
optional int64 backoff_duration = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OkpDeviceState {
|
||||||
|
// Not yet checked for provisioning state. This should be a
|
||||||
|
// transitory state only. Device which do not need OTA Keybox
|
||||||
|
// Provisioning should simply not store this file.
|
||||||
|
OKP_UNKNOWN = 0;
|
||||||
|
// OEMCrypto has reported that keybox provisioning is required and
|
||||||
|
// that the device supports OKP. Device may or may not be in the
|
||||||
|
// process of performing provisioning.
|
||||||
|
OKP_NEEDS_PROVISIONING = 1;
|
||||||
|
// The device has successfully provisioned its keybox.
|
||||||
|
OKP_PROVISIONED = 2;
|
||||||
|
}
|
||||||
|
// Device-wide OKP state.
|
||||||
|
optional OkpDeviceState state = 1;
|
||||||
|
// Time when the CDM service first discovers that it needs to
|
||||||
|
// provision the L1 keybox.
|
||||||
|
optional int64 first_checked_time = 2;
|
||||||
|
// System time of when a successful provisioning request has been
|
||||||
|
// received. Only relevant if |state| is OKP_PROVISIONED.
|
||||||
|
optional int64 provisioning_time = 3;
|
||||||
|
// A list of all records for each identifiable engine.
|
||||||
|
repeated OkpEngineInfo engine_infos = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message File {
|
message File {
|
||||||
enum FileType {
|
enum FileType {
|
||||||
DEVICE_CERTIFICATE = 1;
|
DEVICE_CERTIFICATE = 1;
|
||||||
@@ -132,6 +197,7 @@ message File {
|
|||||||
USAGE_INFO = 3;
|
USAGE_INFO = 3;
|
||||||
HLS_ATTRIBUTES = 4;
|
HLS_ATTRIBUTES = 4;
|
||||||
USAGE_TABLE_INFO = 5;
|
USAGE_TABLE_INFO = 5;
|
||||||
|
OKP_INFO = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FileVersion { VERSION_1 = 1; }
|
enum FileVersion { VERSION_1 = 1; }
|
||||||
@@ -143,6 +209,7 @@ message File {
|
|||||||
optional UsageInfo usage_info = 5;
|
optional UsageInfo usage_info = 5;
|
||||||
optional HlsAttributes hls_attributes = 6;
|
optional HlsAttributes hls_attributes = 6;
|
||||||
optional UsageTableInfo usage_table_info = 7;
|
optional UsageTableInfo usage_table_info = 7;
|
||||||
|
optional OtaKeyboxProvisioningInfo okp_info = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message HashedFile {
|
message HashedFile {
|
||||||
|
|||||||
@@ -1046,7 +1046,8 @@ CdmResponseType CdmLicense::PrepareClientId(
|
|||||||
return CLIENT_TOKEN_NOT_SET;
|
return CLIENT_TOKEN_NOT_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmResponseType status = id.Init(client_token_, crypto_session_);
|
CdmResponseType status =
|
||||||
|
id.InitForLicenseRequest(client_token_, crypto_session_);
|
||||||
if (status != NO_ERROR) return status;
|
if (status != NO_ERROR) return status;
|
||||||
|
|
||||||
video_widevine::ClientIdentification* client_id =
|
video_widevine::ClientIdentification* client_id =
|
||||||
|
|||||||
@@ -532,6 +532,14 @@ message ProvisioningRequest {
|
|||||||
// Serialized, encrypted session keys. Required.
|
// Serialized, encrypted session keys. Required.
|
||||||
optional bytes encrypted_session_keys = 2;
|
optional bytes encrypted_session_keys = 2;
|
||||||
}
|
}
|
||||||
|
// This message contains the custom serialized message for OTA provisioning
|
||||||
|
// using Android Attestation and a device id as authentication.
|
||||||
|
message AndroidAttestationOtaKeyboxRequest {
|
||||||
|
// The request contains custom serialized and signed data for the
|
||||||
|
// Android Attestation OTA request.
|
||||||
|
optional bytes ota_request = 1;
|
||||||
|
}
|
||||||
|
|
||||||
oneof clear_or_encrypted_client_id {
|
oneof clear_or_encrypted_client_id {
|
||||||
// Device root of trust and other client identification. Required.
|
// Device root of trust and other client identification. Required.
|
||||||
ClientIdentification client_id = 1;
|
ClientIdentification client_id = 1;
|
||||||
@@ -555,6 +563,8 @@ message ProvisioningRequest {
|
|||||||
// SessionKeys encrypted using a service cert public key.
|
// SessionKeys encrypted using a service cert public key.
|
||||||
// Required for keybox provisioning.
|
// Required for keybox provisioning.
|
||||||
optional EncryptedSessionKeys encrypted_session_keys = 8;
|
optional EncryptedSessionKeys encrypted_session_keys = 8;
|
||||||
|
// The custom request for Android Attestation OTA.
|
||||||
|
optional AndroidAttestationOtaKeyboxRequest android_ota_keybox_request = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provisioning response sent by the provisioning server to client devices.
|
// Provisioning response sent by the provisioning server to client devices.
|
||||||
@@ -579,6 +589,14 @@ message ProvisioningResponse {
|
|||||||
// Devices in this series have been revoked. Provisioning is not possible.
|
// Devices in this series have been revoked. Provisioning is not possible.
|
||||||
REVOKED_DEVICE_SERIES = 2;
|
REVOKED_DEVICE_SERIES = 2;
|
||||||
}
|
}
|
||||||
|
// This message contains the custom response for Android Attestation OTA
|
||||||
|
// provisioning which uses the Android Attestation keybox and a device id
|
||||||
|
// from the chip set.
|
||||||
|
message AndroidAttestationOtaKeyboxResponse {
|
||||||
|
// The response contains custom serialized and signed data for the
|
||||||
|
// Android Attestation OTA keybox provisioning.
|
||||||
|
optional bytes ota_response = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||||
// Required. For X.509 certificates, the private RSA key may also include
|
// Required. For X.509 certificates, the private RSA key may also include
|
||||||
@@ -603,6 +621,9 @@ message ProvisioningResponse {
|
|||||||
// than |status| may be empty and should be ignored if the |status|
|
// than |status| may be empty and should be ignored if the |status|
|
||||||
// is present and not NO_ERROR
|
// is present and not NO_ERROR
|
||||||
optional ProvisioningStatus status = 7;
|
optional ProvisioningStatus status = 7;
|
||||||
|
// The Android Attestation OTA response. Only populated if the request
|
||||||
|
// was an Android Attestation OTA request.
|
||||||
|
optional AndroidAttestationOtaKeyboxResponse android_ota_keybox_response = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocol-specific context data used to hold the state of the server in
|
// Protocol-specific context data used to hold the state of the server in
|
||||||
@@ -654,6 +675,8 @@ message SignedProvisioningMessage {
|
|||||||
PROVISIONING_20 = 2; // Keybox factory-provisioned devices.
|
PROVISIONING_20 = 2; // Keybox factory-provisioned devices.
|
||||||
PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices.
|
PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices.
|
||||||
ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices.
|
ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices.
|
||||||
|
// Android-Attestation-based OTA keyboxes.
|
||||||
|
ANDROID_ATTESTATION_KEYBOX_OTA = 6;
|
||||||
INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol.
|
INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
83
libwvdrmengine/cdm/core/src/ota_keybox_provisioner.cpp
Normal file
83
libwvdrmengine/cdm/core/src/ota_keybox_provisioner.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// 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 "ota_keybox_provisioner.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "crypto_session.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
using UniqueLock = std::unique_lock<std::mutex>;
|
||||||
|
namespace {
|
||||||
|
// Indicates not to use the test keybox for OTA provisioning.
|
||||||
|
constexpr bool kProductionKeybox = false;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::unique_ptr<OtaKeyboxProvisioner> OtaKeyboxProvisioner::Create() {
|
||||||
|
return std::unique_ptr<OtaKeyboxProvisioner>(new OtaKeyboxProvisioner());
|
||||||
|
}
|
||||||
|
|
||||||
|
OtaKeyboxProvisioner::OtaKeyboxProvisioner() : mutex_() {}
|
||||||
|
|
||||||
|
OtaKeyboxProvisioner::~OtaKeyboxProvisioner() {}
|
||||||
|
|
||||||
|
CdmResponseType OtaKeyboxProvisioner::GenerateProvisioningRequest(
|
||||||
|
CryptoSession* crypto_session, std::string* request) {
|
||||||
|
if (crypto_session == nullptr) {
|
||||||
|
LOGE("Input |crypto_session| is null");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
if (request == nullptr) {
|
||||||
|
LOGE("Output |request| is null");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
UniqueLock lock(mutex_);
|
||||||
|
// Do not generate new requests if already provisioned.
|
||||||
|
if (IsProvisioned()) {
|
||||||
|
LOGW("Already provisioned");
|
||||||
|
// TODO(sigquit): Use a response code that will indicate to the
|
||||||
|
// caller that the system is already provisioned.
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
const CdmResponseType result =
|
||||||
|
crypto_session->PrepareOtaProvisioningRequest(kProductionKeybox, request);
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
LOGV("OTA request generated: request = %s", b2a_hex(*request).c_str());
|
||||||
|
request_count_++;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType OtaKeyboxProvisioner::HandleProvisioningResponse(
|
||||||
|
CryptoSession* crypto_session, const std::string& response) {
|
||||||
|
if (crypto_session == nullptr) {
|
||||||
|
LOGE("Input |crypto_session| is null");
|
||||||
|
return PARAMETER_NULL;
|
||||||
|
}
|
||||||
|
if (response.empty()) {
|
||||||
|
LOGE("OTA provisioning response is empty");
|
||||||
|
return EMPTY_PROVISIONING_RESPONSE;
|
||||||
|
}
|
||||||
|
UniqueLock lock(mutex_);
|
||||||
|
if (IsProvisioned()) {
|
||||||
|
// Response already received, silently dropping.
|
||||||
|
response_count_++;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
const CdmResponseType result =
|
||||||
|
crypto_session->LoadOtaProvisioning(kProductionKeybox, response);
|
||||||
|
if (result != NO_ERROR) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
LOGD("OTA response successfully processed: response = %s",
|
||||||
|
b2a_hex(response).c_str());
|
||||||
|
is_provisioned_ = true;
|
||||||
|
response_count_ = 1;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
} // namespace wvcdm
|
||||||
242
libwvdrmengine/cdm/core/test/ota_keybox_provisioner_test.cpp
Normal file
242
libwvdrmengine/cdm/core/test/ota_keybox_provisioner_test.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
// 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 "ota_keybox_provisioner.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "crypto_session.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
using ::testing::DoAll;
|
||||||
|
using ::testing::NotNull;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::SetArgPointee;
|
||||||
|
namespace {
|
||||||
|
const std::string kEmptyString;
|
||||||
|
const std::string kFakeOtaProvisioningRequest =
|
||||||
|
"Totally real device ID, not fake" // Device ID : 32 bytes
|
||||||
|
"Totally real model ID, also real" // Model ID : 32 bytes
|
||||||
|
"Super secure key" // Encryped session key : 16 bytes
|
||||||
|
"Undoubtedly authentic signature!"; // Signature : 32 bytes
|
||||||
|
|
||||||
|
const std::string kFakeOtaProvisioningResponse =
|
||||||
|
"Definitely an IV" // IV : 16 bytes
|
||||||
|
// Keybox : 128 bytes
|
||||||
|
"I'm a keybox, look at my keys and box-like appearance []. You might "
|
||||||
|
"be thinking 'you are not a real keybox', but you'd be wrong"
|
||||||
|
"Scribble scribble dot slash dot "; // Signature : 32 bytes
|
||||||
|
|
||||||
|
const std::string kAnotherFakeOtaProvisioningResponse =
|
||||||
|
"Looks like an IV" // IV : 16 bytes
|
||||||
|
// Keybox : 128 bytes
|
||||||
|
"I am also a keybox. It's so safe to assume I'm a real keybox that "
|
||||||
|
"attempting to verify that will look very embarrassing for you"
|
||||||
|
"A drawing of boat with dolphins "; // Signature : 32 bytes
|
||||||
|
|
||||||
|
class MockCryptoSession : public CryptoSession {
|
||||||
|
public:
|
||||||
|
MockCryptoSession() : CryptoSession(&crypto_metrics_) {}
|
||||||
|
~MockCryptoSession() {}
|
||||||
|
|
||||||
|
bool IsOpen() override { return is_open_; }
|
||||||
|
void SetIsOpen(bool is_open) { is_open_ = is_open; }
|
||||||
|
|
||||||
|
MOCK_METHOD2(PrepareOtaProvisioningRequest,
|
||||||
|
CdmResponseType(bool, std::string*));
|
||||||
|
MOCK_METHOD2(LoadOtaProvisioning, CdmResponseType(bool, const std::string&));
|
||||||
|
|
||||||
|
void ExpectRequest(const std::string& request, CdmResponseType result) {
|
||||||
|
if (result == NO_ERROR) {
|
||||||
|
EXPECT_CALL(*this, PrepareOtaProvisioningRequest(false, NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<1>(request), Return(NO_ERROR)));
|
||||||
|
} else {
|
||||||
|
EXPECT_CALL(*this, PrepareOtaProvisioningRequest(false, NotNull()))
|
||||||
|
.WillOnce(Return(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectResponse(const std::string& response, CdmResponseType result) {
|
||||||
|
EXPECT_CALL(*this, LoadOtaProvisioning(false, response))
|
||||||
|
.WillOnce(Return(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_open_ = false;
|
||||||
|
|
||||||
|
static metrics::CryptoMetrics crypto_metrics_;
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics::CryptoMetrics MockCryptoSession::crypto_metrics_;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class OtaKeyboxProvisionerTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
crypto_session_.reset(new MockCryptoSession());
|
||||||
|
provisioner_ = OtaKeyboxProvisioner::Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
crypto_session_.reset();
|
||||||
|
provisioner_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MockCryptoSession> crypto_session_;
|
||||||
|
std::unique_ptr<OtaKeyboxProvisioner> provisioner_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(OtaKeyboxProvisionerTest, SingleRequestSingleResponse) {
|
||||||
|
// Pre-request conditions.
|
||||||
|
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(0u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(0u, provisioner_->response_count());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Post-request, pre-response conditions.
|
||||||
|
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(1u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(0u, provisioner_->response_count());
|
||||||
|
|
||||||
|
// Load response.
|
||||||
|
const std::string response = kFakeOtaProvisioningResponse;
|
||||||
|
crypto_session_->ExpectResponse(response, NO_ERROR);
|
||||||
|
EXPECT_EQ(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||||
|
crypto_session_.get(), response));
|
||||||
|
|
||||||
|
// Post-response conditions.
|
||||||
|
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(1u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(1u, provisioner_->response_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OtaKeyboxProvisionerTest, MultipleRequestsMultipleResponse) {
|
||||||
|
// Generate first request.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(2u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(2u, provisioner_->response_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Generate request.
|
||||||
|
crypto_session_->ExpectRequest(kFakeOtaProvisioningRequest, NO_ERROR);
|
||||||
|
std::string request;
|
||||||
|
EXPECT_EQ(NO_ERROR, provisioner_->GenerateProvisioningRequest(
|
||||||
|
crypto_session_.get(), &request));
|
||||||
|
|
||||||
|
// Attempt to load empty response. No call to CryptoSession.
|
||||||
|
const std::string response = "";
|
||||||
|
EXPECT_NE(NO_ERROR, provisioner_->HandleProvisioningResponse(
|
||||||
|
crypto_session_.get(), response));
|
||||||
|
|
||||||
|
EXPECT_FALSE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(1u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(0u, provisioner_->response_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(OtaKeyboxProvisionerTest, GenerateRequestAfterProvisioning) {
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
EXPECT_TRUE(provisioner_->IsProvisioned());
|
||||||
|
EXPECT_EQ(1u, provisioner_->request_count());
|
||||||
|
EXPECT_EQ(1u, provisioner_->response_count());
|
||||||
|
}
|
||||||
|
} // namespace wvcdm
|
||||||
Reference in New Issue
Block a user