Merge changes from topic "b169740403_2" into sc-dev
* changes: Verify DRM certificate validity Extract creation and expiration times from DRM cert Allow two DRM certificates for each identifier
This commit is contained in:
@@ -54,9 +54,13 @@ class CertificateProvisioning {
|
||||
|
||||
// Extract serial number and system ID from a DRM Device certificate.
|
||||
// Either |serial_number| or |system_id| may be null, but not both.
|
||||
// Both |creation_time_seconds| and |expiration_time_seconds| may be null.
|
||||
// |creation_time_seconds| and |expiration_time_seconds| will be set to -1
|
||||
// if not present, 0 if unlimited and a valid time otherwise
|
||||
static bool ExtractDeviceInfo(const std::string& device_certificate,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
std::string* serial_number, uint32_t* system_id,
|
||||
int64_t* creation_time_seconds,
|
||||
int64_t* expiration_time_seconds);
|
||||
|
||||
// Removes json wrapping if applicable to extract the
|
||||
// SignedProvisioningMessage
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace wvcdm {
|
||||
|
||||
class FileSystem;
|
||||
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
|
||||
class DeviceFiles {
|
||||
public:
|
||||
typedef enum {
|
||||
@@ -31,6 +33,24 @@ class DeviceFiles {
|
||||
kLicenseStateUnknown,
|
||||
} LicenseState;
|
||||
|
||||
typedef enum {
|
||||
kCertificateValid,
|
||||
kCertificateExpired,
|
||||
kCertificateNotFound,
|
||||
kCertificateInvalid,
|
||||
kCannotHandle,
|
||||
} CertificateState;
|
||||
|
||||
// |kCertificateDefault| includes an expiration time set by the provisioning
|
||||
// service. This will replace any legacy certificates, if a forced
|
||||
// reprovisioning happens at the client or by the license service.
|
||||
// ATSC certificates are unaffected and have an unlimited lifetime.
|
||||
typedef enum {
|
||||
kCertificateDefault,
|
||||
kCertificateLegacy,
|
||||
kCertificateAtsc,
|
||||
} CertificateType;
|
||||
|
||||
// All error response codes start with 5000 to avoid overlap with other error
|
||||
// spaces.
|
||||
enum ResponseType {
|
||||
@@ -100,12 +120,19 @@ class DeviceFiles {
|
||||
// and used but not written or removed.
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
const CryptoWrappedKey& private_key);
|
||||
virtual bool RetrieveCertificate(bool atsc_mode_enabled,
|
||||
virtual CertificateState RetrieveCertificate(bool atsc_mode_enabled,
|
||||
std::string* certificate,
|
||||
CryptoWrappedKey* private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
virtual bool HasCertificate(bool atsc_mode_enabled);
|
||||
// Retrieves the legacy DRM certificate without performing expiry
|
||||
// related validation. Use this only when restoring/releasing
|
||||
// licenses/usage entries
|
||||
virtual bool RetrieveLegacyCertificate(std::string* certificate,
|
||||
CryptoWrappedKey* private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
virtual bool RemoveCertificate();
|
||||
|
||||
virtual bool StoreLicense(const CdmLicenseData& license_data,
|
||||
@@ -247,6 +274,21 @@ class DeviceFiles {
|
||||
virtual bool DeleteUsageTableInfo();
|
||||
|
||||
private:
|
||||
// This method will retrieve the certificate and perform expiry validation
|
||||
// appropriate for a given certificate type
|
||||
CertificateState RetrieveCertificate(CertificateType certificate_type,
|
||||
std::string* certificate,
|
||||
CryptoWrappedKey* private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
bool HasCertificate(CertificateType certificate_type);
|
||||
bool SetDeviceCertificate(const std::string& certificate,
|
||||
const CryptoWrappedKey& wrapped_private_key,
|
||||
DeviceCertificate* mutable_device_certificate);
|
||||
bool ExtractFromDeviceCertificate(const DeviceCertificate& device_certificate,
|
||||
std::string* certificate,
|
||||
CryptoWrappedKey* wrapped_private_key);
|
||||
|
||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||
// well as adding the device files base path to to the file name.
|
||||
ResponseType StoreFileWithHash(const std::string& name,
|
||||
@@ -260,7 +302,8 @@ class DeviceFiles {
|
||||
bool RemoveFile(const std::string& name);
|
||||
ssize_t GetFileSize(const std::string& name);
|
||||
|
||||
static std::string GetCertificateFileName(bool atsc_mode_enabled);
|
||||
static bool GetCertificateFileName(CertificateType certificate_type,
|
||||
std::string* certificate_file_name);
|
||||
static std::string GetHlsAttributesFileNameExtension();
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageTableFileName();
|
||||
@@ -268,18 +311,28 @@ class DeviceFiles {
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
|
||||
FRIEND_TEST(DeviceCertificateTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, ReadCertificateWithoutKeyType);
|
||||
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Delete);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Read);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Store);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, AppParametersBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, HasCertificateAtsc);
|
||||
FRIEND_TEST(DeviceFilesTest, HasCertificateDefault);
|
||||
FRIEND_TEST(DeviceFilesTest, HasCertificateLegacy);
|
||||
FRIEND_TEST(DeviceFilesTest, HasCertificateNone);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveAtscCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveAtscCertificateNotFound);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveCertificateWithoutKeyType);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveDefaultCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveDefaultCertificateNeverExpires);
|
||||
FRIEND_TEST(DeviceFilesTest,
|
||||
RetrieveLegacyCertificateWithClientExpirationTime);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLegacyCertificateWithoutExpirationTime);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreCertificateInvalidParams);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
|
||||
@@ -289,6 +342,9 @@ class DeviceFiles {
|
||||
FRIEND_TEST(DeviceFilesUsageTableTest, Read);
|
||||
FRIEND_TEST(DeviceFilesUsageTableTest, Store);
|
||||
FRIEND_TEST(DeviceFilesUsageTableTest, ReadWithoutLruData);
|
||||
FRIEND_TEST(RetrieveDefaultCertificateTest, ErrorScenarios);
|
||||
FRIEND_TEST(RetrieveLegacyCertificateTest, ErrorScenarios);
|
||||
FRIEND_TEST(StoreCertificateTest, DefaultAndLegacy);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest);
|
||||
|
||||
@@ -26,6 +26,7 @@ static const size_t CERTIFICATE_DATA_SIZE = 4 * 1024;
|
||||
// (NaN in JS translates to 0 in unix timestamp).
|
||||
static const int64_t NEVER_EXPIRES = 0;
|
||||
static const int64_t UNLIMITED_DURATION = 0;
|
||||
static const int64_t INVALID_TIME = -1;
|
||||
|
||||
// This is the lower limit. For OEMCrypto v16+ one can query and find how many
|
||||
// are supported
|
||||
|
||||
@@ -188,9 +188,9 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
bool atsc_mode_enabled = false;
|
||||
if (cdm_client_property_set != nullptr)
|
||||
atsc_mode_enabled = cdm_client_property_set->use_atsc_mode();
|
||||
if (!file_handle_->RetrieveCertificate(atsc_mode_enabled, &client_token,
|
||||
&private_key, &serial_number,
|
||||
nullptr)) {
|
||||
if (file_handle_->RetrieveCertificate(
|
||||
atsc_mode_enabled, &client_token, &private_key, &serial_number,
|
||||
nullptr) != DeviceFiles::kCertificateValid) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
CdmResponseType load_cert_sts;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
#include "clock.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kEmptyString;
|
||||
@@ -500,7 +502,8 @@ bool CertificateProvisioning::ExtractAndDecodeSignedMessageForTesting(
|
||||
|
||||
bool CertificateProvisioning::ExtractDeviceInfo(
|
||||
const std::string& device_certificate, std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
uint32_t* system_id, int64_t* creation_time_seconds,
|
||||
int64_t* expiration_time_seconds) {
|
||||
LOGV("Extracting device info");
|
||||
if (serial_number == nullptr && system_id == nullptr) {
|
||||
LOGE("Output parameters |serial_number| and |system_id| not provided");
|
||||
@@ -527,6 +530,28 @@ bool CertificateProvisioning::ExtractDeviceInfo(
|
||||
if (system_id != nullptr) {
|
||||
*system_id = drm_certificate.system_id();
|
||||
}
|
||||
if (creation_time_seconds != nullptr) {
|
||||
*creation_time_seconds = drm_certificate.has_creation_time_seconds()
|
||||
? drm_certificate.creation_time_seconds()
|
||||
: INVALID_TIME;
|
||||
}
|
||||
if (expiration_time_seconds != nullptr) {
|
||||
*expiration_time_seconds = drm_certificate.has_expiration_time_seconds()
|
||||
? drm_certificate.expiration_time_seconds()
|
||||
: INVALID_TIME;
|
||||
}
|
||||
/*
|
||||
Clock clock;
|
||||
//drm_certificate.set_expiration_time_seconds(clock.GetCurrentTime() + 10*365.25*24*60*60);
|
||||
drm_certificate.set_creation_time_seconds(-5);
|
||||
std::string serialized_drm_certificate;
|
||||
drm_certificate.SerializeToString(&serialized_drm_certificate);
|
||||
signed_drm_certificate.set_drm_certificate(serialized_drm_certificate);
|
||||
std::string serialized_signed_drm_certificate;
|
||||
signed_drm_certificate.SerializeToString(&serialized_signed_drm_certificate);
|
||||
LOGE("serialized_signed_drm_certificate: (%zu) %s", serialized_signed_drm_certificate.size(), b2a_hex(serialized_signed_drm_certificate).c_str());
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
#include "device_files.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "cdm_random.h"
|
||||
#include "certificate_provisioning.h"
|
||||
#include "clock.h"
|
||||
#include "file_store.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
@@ -43,6 +46,18 @@ using video_widevine_client::sdk::
|
||||
// Example: STRINGIFY(this_argument) -> "this_argument"
|
||||
#define STRINGIFY(PARAM...) #PARAM
|
||||
|
||||
#define RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(PARAM) \
|
||||
if ((PARAM) == nullptr) { \
|
||||
LOGE("Output parameter |" STRINGIFY(PARAM) "| not provided"); \
|
||||
return DeviceFiles::kCannotHandle; \
|
||||
}
|
||||
|
||||
#define RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_UNINITIALIZED() \
|
||||
if (!initialized_) { \
|
||||
LOGE("Device files is not initialized"); \
|
||||
return DeviceFiles::kCannotHandle; \
|
||||
}
|
||||
|
||||
#define RETURN_FALSE_IF_NULL(PARAM) \
|
||||
if ((PARAM) == nullptr) { \
|
||||
LOGE("Output parameter |" STRINGIFY(PARAM) "| not provided"); \
|
||||
@@ -71,8 +86,6 @@ using video_widevine_client::sdk::
|
||||
|
||||
namespace {
|
||||
|
||||
const char kAtscCertificateFileName[] = "atsccert.bin";
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kHlsAttributesFileNameExt[] = ".hal";
|
||||
const char kUsageInfoFileNamePrefix[] = "usage";
|
||||
const char kUsageInfoFileNameExt[] = ".bin";
|
||||
@@ -80,6 +93,7 @@ const char kLicenseFileNameExt[] = ".lic";
|
||||
const char kEmptyFileName[] = "";
|
||||
const char kUsageTableFileName[] = "usgtable.bin";
|
||||
const char kWildcard[] = "*";
|
||||
constexpr int64_t kFourMonthsInSeconds = (2 * 30 + 2 * 31) * 24 * 60 * 60;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -130,105 +144,259 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
|
||||
DeviceCertificate* device_certificate = file.mutable_device_certificate();
|
||||
device_certificate->set_certificate(certificate);
|
||||
device_certificate->set_wrapped_private_key(private_key.key());
|
||||
switch (private_key.type()) {
|
||||
case CryptoWrappedKey::kRsa:
|
||||
device_certificate->set_key_type(DeviceCertificate::RSA);
|
||||
break;
|
||||
case CryptoWrappedKey::kEcc:
|
||||
device_certificate->set_key_type(DeviceCertificate::ECC);
|
||||
break;
|
||||
case CryptoWrappedKey::kUninitialized: // Suppress compiler warnings.
|
||||
default:
|
||||
LOGE("Unexpected key type");
|
||||
|
||||
int64_t creation_time_seconds;
|
||||
int64_t expiration_time_seconds;
|
||||
uint32_t system_id;
|
||||
|
||||
if (!CertificateProvisioning::ExtractDeviceInfo(
|
||||
certificate, nullptr, &system_id, &creation_time_seconds,
|
||||
&expiration_time_seconds))
|
||||
return false;
|
||||
|
||||
if (creation_time_seconds <= 0) {
|
||||
LOGE("Invalid certificate creation time %" PRId64, creation_time_seconds);
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool default_certificate = expiration_time_seconds >= 0;
|
||||
|
||||
if (!SetDeviceCertificate(certificate, private_key, device_certificate))
|
||||
return false;
|
||||
|
||||
if (default_certificate) {
|
||||
Clock clock;
|
||||
device_certificate->set_acquisition_time_seconds(clock.GetCurrentTime());
|
||||
} else {
|
||||
// Since certificates of type kCertificateAtsc are not allowed to be
|
||||
// stored, this is a certificate of type kCertificateLegacy.
|
||||
// The only time when a legacy certificate is stored is when it does not
|
||||
// have an expiration time. Set expiration time to 6 months +- 2 months.
|
||||
Clock clock;
|
||||
const int64_t current_time = clock.GetCurrentTime();
|
||||
CdmRandomGenerator rng(current_time & 0xffffffff);
|
||||
|
||||
device_certificate->set_expiration_time_seconds(
|
||||
current_time + kFourMonthsInSeconds +
|
||||
rng.RandomInRange(kFourMonthsInSeconds));
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFileWithHash(GetCertificateFileName(false), serialized_file) ==
|
||||
kNoError;
|
||||
std::string certificate_file_name;
|
||||
const CertificateType certificate_type =
|
||||
default_certificate ? kCertificateDefault : kCertificateLegacy;
|
||||
if (!GetCertificateFileName(certificate_type, &certificate_file_name)) {
|
||||
LOGE("Unable to get certificate file name of type: %d", certificate_type);
|
||||
return false;
|
||||
}
|
||||
return StoreFileWithHash(certificate_file_name, serialized_file) == kNoError;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(bool atsc_mode_enabled,
|
||||
std::string* certificate,
|
||||
DeviceFiles::CertificateState DeviceFiles::RetrieveCertificate(
|
||||
bool atsc_mode_enabled, std::string* certificate,
|
||||
CryptoWrappedKey* private_key, std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_UNINITIALIZED();
|
||||
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(certificate);
|
||||
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(private_key);
|
||||
|
||||
if (!HasCertificate(atsc_mode_enabled)) {
|
||||
LOGW("Unable to find certificate, atsc mode: %s",
|
||||
atsc_mode_enabled ? "enabled" : "disabled");
|
||||
return kCertificateNotFound;
|
||||
}
|
||||
|
||||
if (atsc_mode_enabled)
|
||||
return RetrieveCertificate(kCertificateAtsc, certificate, private_key,
|
||||
serial_number, system_id);
|
||||
|
||||
if (HasCertificate(kCertificateDefault))
|
||||
return RetrieveCertificate(kCertificateDefault, certificate, private_key,
|
||||
serial_number, system_id);
|
||||
|
||||
return RetrieveCertificate(kCertificateLegacy, certificate, private_key,
|
||||
serial_number, system_id);
|
||||
}
|
||||
|
||||
DeviceFiles::CertificateState DeviceFiles::RetrieveCertificate(
|
||||
CertificateType certificate_type, std::string* certificate,
|
||||
CryptoWrappedKey* wrapped_private_key, std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(certificate);
|
||||
RETURN_CERTIFICATE_STATE_CANNOT_HANDLE_IF_NULL(wrapped_private_key);
|
||||
|
||||
std::string certificate_file_name;
|
||||
if (!GetCertificateFileName(certificate_type, &certificate_file_name)) {
|
||||
LOGW("Unable to find certificate file name for type: %d", certificate_type);
|
||||
return kCannotHandle;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (RetrieveHashedFile(certificate_file_name, &file) != kNoError) {
|
||||
LOGW("Unable to retrieve certificate file");
|
||||
return kCertificateNotFound;
|
||||
}
|
||||
|
||||
if (file.type() != video_widevine_client::sdk::File::DEVICE_CERTIFICATE) {
|
||||
LOGE("Certificate file is of incorrect file type: type = %d",
|
||||
static_cast<int>(file.type()));
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGE("Certificate file is of incorrect file version: version = %d",
|
||||
static_cast<int>(file.version()));
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
if (!file.has_device_certificate()) {
|
||||
LOGE("Certificate not present");
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
const DeviceCertificate& device_certificate = file.device_certificate();
|
||||
|
||||
if (!ExtractFromDeviceCertificate(device_certificate, certificate,
|
||||
wrapped_private_key)) {
|
||||
LOGE("Unable to extract from device certificate");
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
int64_t creation_time_seconds;
|
||||
int64_t expiration_time_seconds;
|
||||
|
||||
if (!CertificateProvisioning::ExtractDeviceInfo(
|
||||
device_certificate.certificate(), serial_number, system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds))
|
||||
return kCertificateInvalid;
|
||||
|
||||
Clock clock;
|
||||
const int64_t current_time = clock.GetCurrentTime();
|
||||
|
||||
switch (certificate_type) {
|
||||
case kCertificateDefault: {
|
||||
// Validation check for DRM certificate that includes an expiration
|
||||
// time set by the provisioning service. Since provisioning and
|
||||
// client clocks may not be in sync, verify by comparing time
|
||||
// elapsed since license was acquired with expiration period.
|
||||
// First verify that all the fields are set to valid values.
|
||||
// The service will validate certificate expiration so tampering of
|
||||
// time values at the client is not a concern.
|
||||
if (creation_time_seconds <= 0) {
|
||||
LOGE("Invalid creation time of default certificate: %" PRId64,
|
||||
creation_time_seconds);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
if (expiration_time_seconds < 0) {
|
||||
LOGE("Invalid expiration time of default certificate: %" PRId64,
|
||||
expiration_time_seconds);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
if (expiration_time_seconds == UNLIMITED_DURATION)
|
||||
return kCertificateValid;
|
||||
|
||||
if (!device_certificate.has_acquisition_time_seconds()) {
|
||||
LOGE("Acquisition time of default certificate not available");
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
const int64_t acquisition_time_seconds =
|
||||
device_certificate.acquisition_time_seconds();
|
||||
if (acquisition_time_seconds <= 0) {
|
||||
LOGE("Invalid acquisition time of default certificate: %" PRId64,
|
||||
acquisition_time_seconds);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
if (current_time < acquisition_time_seconds) {
|
||||
LOGE("Time not valid: current time: %" PRId64
|
||||
", acquisition time: %" PRId64,
|
||||
current_time, acquisition_time_seconds);
|
||||
return kCannotHandle;
|
||||
}
|
||||
|
||||
if (expiration_time_seconds < creation_time_seconds) {
|
||||
LOGE("Time not valid: expiration time: %" PRId64
|
||||
", creation time: %" PRId64,
|
||||
expiration_time_seconds, creation_time_seconds);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
if (current_time - acquisition_time_seconds >
|
||||
expiration_time_seconds - creation_time_seconds) {
|
||||
return kCertificateExpired;
|
||||
}
|
||||
return kCertificateValid;
|
||||
}
|
||||
|
||||
case kCertificateLegacy: {
|
||||
// Validation check for DRM certificate without an expiration
|
||||
// time set by the provisioning service. Add an expiry time
|
||||
// within the next 6 months +/- 2 months, if one has not been set.
|
||||
if (!device_certificate.has_expiration_time_seconds()) {
|
||||
StoreCertificate(*certificate, *wrapped_private_key);
|
||||
return kCertificateValid;
|
||||
}
|
||||
const int64_t expiration_time_seconds =
|
||||
device_certificate.expiration_time_seconds();
|
||||
if (expiration_time_seconds <= 0) {
|
||||
LOGE("Invalid expiration time of legacy certificate: %" PRId64,
|
||||
expiration_time_seconds);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
|
||||
if (current_time > expiration_time_seconds) return kCertificateExpired;
|
||||
|
||||
return kCertificateValid;
|
||||
}
|
||||
|
||||
case kCertificateAtsc:
|
||||
// No expiration enforced
|
||||
return kCertificateValid;
|
||||
|
||||
default:
|
||||
// Should never happen. This should be detected earlier when fetching
|
||||
// the file name
|
||||
LOGE("Invalid certificate type: %d", certificate_type);
|
||||
return kCertificateInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLegacyCertificate(std::string* certificate,
|
||||
CryptoWrappedKey* private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
RETURN_FALSE_IF_NULL(certificate);
|
||||
RETURN_FALSE_IF_NULL(private_key);
|
||||
if (!HasCertificate(kCertificateLegacy)) return false;
|
||||
|
||||
const CertificateState state = RetrieveCertificate(
|
||||
kCertificateLegacy, certificate, private_key, serial_number, system_id);
|
||||
if (state == kCertificateValid || state == kCertificateExpired) return true;
|
||||
|
||||
if (!HasCertificate(atsc_mode_enabled)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (RetrieveHashedFile(GetCertificateFileName(atsc_mode_enabled), &file) !=
|
||||
kNoError) {
|
||||
LOGW("Unable to retrieve certificate file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.type() != video_widevine_client::sdk::File::DEVICE_CERTIFICATE) {
|
||||
LOGE("Certificate file is of incorrect file type: type = %d",
|
||||
static_cast<int>(file.type()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGE("Certificate file is of incorrect file version: version = %d",
|
||||
static_cast<int>(file.version()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.has_device_certificate()) {
|
||||
LOGE("Certificate not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceCertificate device_certificate = file.device_certificate();
|
||||
*certificate = device_certificate.certificate();
|
||||
private_key->Clear();
|
||||
private_key->set_key(device_certificate.wrapped_private_key());
|
||||
if (device_certificate.has_key_type()) {
|
||||
const DeviceCertificate::PrivateKeyType key_type =
|
||||
device_certificate.key_type();
|
||||
switch (key_type) {
|
||||
case DeviceCertificate::RSA:
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
break;
|
||||
case DeviceCertificate::ECC:
|
||||
private_key->set_type(CryptoWrappedKey::kEcc);
|
||||
break;
|
||||
default:
|
||||
LOGW("Unknown DRM key type, defaulting to RSA: type = %d", key_type);
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Possible that device certificate is from V15, in this case, the
|
||||
// only supported key of at that time was RSA.
|
||||
LOGD("No key type info, assuming RSA");
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
}
|
||||
|
||||
return CertificateProvisioning::ExtractDeviceInfo(
|
||||
device_certificate.certificate(), serial_number, system_id);
|
||||
}
|
||||
|
||||
bool DeviceFiles::HasCertificate(bool atsc_mode_enabled) {
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
|
||||
return FileExists(GetCertificateFileName(atsc_mode_enabled));
|
||||
if (atsc_mode_enabled) return HasCertificate(kCertificateAtsc);
|
||||
|
||||
return HasCertificate(kCertificateDefault) ||
|
||||
HasCertificate(kCertificateLegacy);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveCertificate() {
|
||||
RETURN_FALSE_IF_UNINITIALIZED()
|
||||
|
||||
return RemoveFile(GetCertificateFileName(false));
|
||||
std::string certificate_file_name;
|
||||
if (GetCertificateFileName(kCertificateLegacy, &certificate_file_name))
|
||||
RemoveFile(certificate_file_name);
|
||||
if (GetCertificateFileName(kCertificateDefault, &certificate_file_name))
|
||||
return RemoveFile(certificate_file_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(const CdmLicenseData& license_data,
|
||||
@@ -1087,6 +1255,70 @@ bool DeviceFiles::DeleteUsageTableInfo() {
|
||||
return RemoveFile(GetUsageTableFileName());
|
||||
}
|
||||
|
||||
bool DeviceFiles::HasCertificate(CertificateType certificate_type) {
|
||||
RETURN_FALSE_IF_UNINITIALIZED();
|
||||
|
||||
std::string certificate_file_name;
|
||||
if (!GetCertificateFileName(certificate_type, &certificate_file_name))
|
||||
return false;
|
||||
|
||||
return FileExists(certificate_file_name);
|
||||
}
|
||||
|
||||
bool DeviceFiles::SetDeviceCertificate(
|
||||
const std::string& certificate, const CryptoWrappedKey& private_key,
|
||||
DeviceCertificate* mutable_device_certificate) {
|
||||
RETURN_FALSE_IF_NULL(mutable_device_certificate);
|
||||
|
||||
mutable_device_certificate->set_certificate(certificate);
|
||||
mutable_device_certificate->set_wrapped_private_key(private_key.key());
|
||||
switch (private_key.type()) {
|
||||
case CryptoWrappedKey::kRsa:
|
||||
mutable_device_certificate->set_key_type(DeviceCertificate::RSA);
|
||||
return true;
|
||||
case CryptoWrappedKey::kEcc:
|
||||
mutable_device_certificate->set_key_type(DeviceCertificate::ECC);
|
||||
return true;
|
||||
case CryptoWrappedKey::kUninitialized: // Suppress compiler warnings.
|
||||
default:
|
||||
LOGE("Unexpected key type: %d", private_key.type());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceFiles::ExtractFromDeviceCertificate(
|
||||
const DeviceCertificate& device_certificate, std::string* certificate,
|
||||
CryptoWrappedKey* private_key) {
|
||||
RETURN_FALSE_IF_NULL(certificate);
|
||||
RETURN_FALSE_IF_NULL(private_key);
|
||||
|
||||
*certificate = device_certificate.certificate();
|
||||
private_key->Clear();
|
||||
private_key->set_key(device_certificate.wrapped_private_key());
|
||||
if (device_certificate.has_key_type()) {
|
||||
const DeviceCertificate::PrivateKeyType key_type =
|
||||
device_certificate.key_type();
|
||||
switch (key_type) {
|
||||
case DeviceCertificate::RSA:
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
break;
|
||||
case DeviceCertificate::ECC:
|
||||
private_key->set_type(CryptoWrappedKey::kEcc);
|
||||
break;
|
||||
default:
|
||||
LOGW("Unknown DRM key type, defaulting to RSA: type = %d", key_type);
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Possible that device certificate is from V15, in this case, the
|
||||
// only supported key of at that time was RSA.
|
||||
LOGD("No key type info, assuming RSA");
|
||||
private_key->set_type(CryptoWrappedKey::kRsa);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceFiles::ResponseType DeviceFiles::StoreFileWithHash(
|
||||
const std::string& name, const std::string& serialized_file) {
|
||||
std::string hash = Sha256Hash(serialized_file);
|
||||
@@ -1269,8 +1501,22 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
||||
return file_system_->FileSize(path);
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetCertificateFileName(bool atsc_mode_enabled) {
|
||||
return atsc_mode_enabled ? kAtscCertificateFileName : kCertificateFileName;
|
||||
bool DeviceFiles::GetCertificateFileName(CertificateType certificate_type,
|
||||
std::string* file_name) {
|
||||
RETURN_FALSE_IF_NULL(file_name);
|
||||
switch (certificate_type) {
|
||||
case kCertificateDefault:
|
||||
*file_name = kCertificateFileName;
|
||||
return true;
|
||||
case kCertificateLegacy:
|
||||
*file_name = kLegacyCertificateFileName;
|
||||
return true;
|
||||
case kCertificateAtsc:
|
||||
*file_name = kAtscCertificateFileName;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetUsageTableFileName() { return kUsageTableFileName; }
|
||||
|
||||
@@ -116,8 +116,10 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
MockDeviceFiles() : DeviceFiles(nullptr) {}
|
||||
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD5(RetrieveCertificate, bool(bool, std::string*, CryptoWrappedKey*,
|
||||
std::string*, uint32_t*));
|
||||
MOCK_METHOD5(RetrieveCertificate,
|
||||
DeviceFiles::CertificateState(bool, std::string*,
|
||||
CryptoWrappedKey*, std::string*,
|
||||
uint32_t*));
|
||||
};
|
||||
|
||||
class MockUsageTableHeader : public UsageTableHeader {
|
||||
@@ -221,7 +223,7 @@ TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -249,7 +251,7 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -276,7 +278,7 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -310,7 +312,7 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(Return(false));
|
||||
.WillOnce(Return(DeviceFiles::kCertificateInvalid));
|
||||
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(nullptr));
|
||||
}
|
||||
@@ -331,7 +333,7 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) {
|
||||
EXPECT_CALL(*file_handle_,
|
||||
RetrieveCertificate(false, NotNull(), NotNull(), NotNull(), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
Return(DeviceFiles::kCertificateValid)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(kWrappedKey))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
|
||||
@@ -17,17 +17,37 @@
|
||||
namespace {
|
||||
|
||||
const std::string kSignedDeviceCertificate = wvcdm::a2bs_hex(
|
||||
"0A350802121B7769646576696E655F746573745F73657269616C5F6E756D62657228D2093A"
|
||||
"11746573742E7769646576696E652E636F6D12097369676E6174757265");
|
||||
"0A3D0802121B7769646576696E655F746573745F73657269616C5F6E756D62657218C09A0C"
|
||||
"28D2093A11746573742E7769646576696E652E636F6D6080B51812097369676E617475726"
|
||||
"5");
|
||||
const std::string kSignedDeviceCertificateCreationTimeUnlimited =
|
||||
wvcdm::a2bs_hex(
|
||||
"0A370802121B7769646576696E655F746573745F73657269616C5F6E756D6265721800"
|
||||
"28D2093A11746573742E7769646576696E652E636F6D12097369676E6174757265");
|
||||
const std::string kSignedDeviceCertificateExpirationTimeInvalid =
|
||||
wvcdm::a2bs_hex(
|
||||
"0A390802121B7769646576696E655F746573745F73657269616C5F6E756D62657218C0"
|
||||
"9A0C28D2093A11746573742E7769646576696E652E636F6D12097369676E617475726"
|
||||
"5");
|
||||
const std::string kSignedDeviceCertificateExpirationTimeUnlimited =
|
||||
wvcdm::a2bs_hex(
|
||||
"0A3B0802121B7769646576696E655F746573745F73657269616C5F6E756D62657218C0"
|
||||
"9A0C28D2093A11746573742E7769646576696E652E636F6D600012097369676E617475"
|
||||
"7265");
|
||||
const std::string kSignedDeviceCertificateInvalid = wvcdm::a2bs_hex(
|
||||
"76340802121B7769646576696E655F746573745F73657269616C5F6E756D62657228D2093A"
|
||||
"11746573742E7769646576696E652E636F6D12097369676E6174757265");
|
||||
const std::string kSignedDeviceCertificateNoDrmCertificate =
|
||||
wvcdm::a2bs_hex("12097369676E6174757265");
|
||||
const std::string kSignedDeviceCertificateInvalidCertificateType =
|
||||
wvcdm::a2bs_hex(
|
||||
"0A350801121B7769646576696E655F746573745F73657269616C5F6E756D62657228D2"
|
||||
"093A11746573742E7769646576696E652E636F6D12097369676E6174757265");
|
||||
const std::string kSignedDeviceCertificateNoDrmCertificate =
|
||||
wvcdm::a2bs_hex("12097369676E6174757265");
|
||||
const std::string kSignedDeviceCertificateTimesInvalid = wvcdm::a2bs_hex(
|
||||
"0A350802121B7769646576696E655F746573745F73657269616C5F6E756D62657228D2093A"
|
||||
"11746573742E7769646576696E652E636F6D12097369676E6174757265");
|
||||
const int64_t kCreationTime = 200000;
|
||||
const int64_t kExpirationTime = 400000;
|
||||
const std::string kSerialNumber = "widevine_test_serial_number";
|
||||
const uint32_t kSystemId = 1234;
|
||||
|
||||
@@ -91,17 +111,20 @@ TEST_F(CertificateProvisioningTest, ExtractDeviceInfo_InvalidInput) {
|
||||
uint32_t system_id;
|
||||
|
||||
EXPECT_FALSE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, nullptr, nullptr));
|
||||
kSignedDeviceCertificate, nullptr, nullptr, nullptr, nullptr));
|
||||
|
||||
int64_t creation_time_seconds, expiration_time_seconds;
|
||||
EXPECT_FALSE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateInvalid, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
|
||||
EXPECT_FALSE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateInvalid, &serial_number, &system_id));
|
||||
|
||||
EXPECT_FALSE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateNoDrmCertificate, &serial_number, &system_id));
|
||||
kSignedDeviceCertificateNoDrmCertificate, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
|
||||
EXPECT_FALSE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateInvalidCertificateType, &serial_number,
|
||||
&system_id));
|
||||
&system_id, &creation_time_seconds, &expiration_time_seconds));
|
||||
}
|
||||
|
||||
// Tests ExtractDeviceInfo success scenarios
|
||||
@@ -111,19 +134,72 @@ TEST_F(CertificateProvisioningTest, ExtractDeviceInfo_InvalidInput) {
|
||||
TEST_F(CertificateProvisioningTest, ExtractDeviceInfo) {
|
||||
std::string serial_number;
|
||||
uint32_t system_id;
|
||||
int64_t creation_time_seconds, expiration_time_seconds;
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, &serial_number, &system_id));
|
||||
kSignedDeviceCertificateTimesInvalid, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(INVALID_TIME, creation_time_seconds);
|
||||
EXPECT_EQ(INVALID_TIME, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateTimesInvalid, nullptr, &system_id, nullptr,
|
||||
nullptr));
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, nullptr, &system_id));
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
kSignedDeviceCertificateTimesInvalid, &serial_number, nullptr, nullptr,
|
||||
nullptr));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, &serial_number, nullptr));
|
||||
kSignedDeviceCertificateCreationTimeUnlimited, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(UNLIMITED_DURATION, creation_time_seconds);
|
||||
EXPECT_EQ(INVALID_TIME, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateExpirationTimeInvalid, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(kCreationTime, creation_time_seconds);
|
||||
EXPECT_EQ(INVALID_TIME, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateExpirationTimeUnlimited, &serial_number,
|
||||
&system_id, &creation_time_seconds, &expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(kCreationTime, creation_time_seconds);
|
||||
EXPECT_EQ(UNLIMITED_DURATION, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, &serial_number, &system_id,
|
||||
&creation_time_seconds, &expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(kCreationTime, creation_time_seconds);
|
||||
EXPECT_EQ(kExpirationTime, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificate, &serial_number, &system_id, nullptr,
|
||||
&expiration_time_seconds));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(kExpirationTime, expiration_time_seconds);
|
||||
|
||||
EXPECT_TRUE(certificate_provisioning_->ExtractDeviceInfo(
|
||||
kSignedDeviceCertificateExpirationTimeUnlimited, &serial_number,
|
||||
&system_id, &creation_time_seconds, nullptr));
|
||||
EXPECT_EQ(kSerialNumber, serial_number);
|
||||
EXPECT_EQ(kSystemId, system_id);
|
||||
EXPECT_EQ(kCreationTime, creation_time_seconds);
|
||||
EXPECT_EQ(kExpirationTime, expiration_time_seconds);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -366,8 +366,8 @@ void InitVectorConstants() {
|
||||
}
|
||||
}
|
||||
|
||||
void ToVector(std::vector<CdmUsageEntryInfo>& vec,
|
||||
const CdmUsageEntryInfo* arr, size_t total_size) {
|
||||
void ToVector(std::vector<CdmUsageEntryInfo>& vec, const CdmUsageEntryInfo* arr,
|
||||
size_t total_size) {
|
||||
size_t max = total_size / sizeof(CdmUsageEntryInfo);
|
||||
vec.clear();
|
||||
for (size_t i = 0; i < max; i++) {
|
||||
@@ -415,8 +415,7 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
const std::string&, const CdmUsageEntry&, uint32_t));
|
||||
MOCK_METHOD2(RetrieveUsageInfo,
|
||||
bool(const std::string&, std::vector<CdmUsageData>*));
|
||||
MOCK_METHOD1(ListLicenses,
|
||||
bool(std::vector<std::string>* key_set_ids));
|
||||
MOCK_METHOD1(ListLicenses, bool(std::vector<std::string>* key_set_ids));
|
||||
MOCK_METHOD1(ListUsageInfoFiles,
|
||||
bool(std::vector<std::string>* usage_info_files));
|
||||
|
||||
@@ -472,10 +471,9 @@ class MockUsageTableHeader : public UsageTableHeader {
|
||||
MockUsageTableHeader() : UsageTableHeader() {}
|
||||
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||
metrics::CryptoMetrics*));
|
||||
MOCK_METHOD6(AddEntry,
|
||||
CdmResponseType(CryptoSession*, bool, const CdmKeySetId&,
|
||||
const std::string&, const CdmKeyResponse&,
|
||||
uint32_t*));
|
||||
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
|
||||
const CdmKeySetId&, const std::string&,
|
||||
const CdmKeyResponse&, uint32_t*));
|
||||
|
||||
CdmResponseType SuperAddEntry(CryptoSession* crypto_session,
|
||||
bool persistent_license,
|
||||
@@ -493,9 +491,7 @@ class MockUsageTableHeader : public UsageTableHeader {
|
||||
|
||||
class UsageTableHeaderTest : public WvCdmTestBase {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
InitVectorConstants();
|
||||
}
|
||||
static void SetUpTestCase() { InitVectorConstants(); }
|
||||
|
||||
// Useful when UsageTableHeader is mocked
|
||||
void InvalidateEntry(uint32_t usage_entry_number, bool, DeviceFiles*,
|
||||
@@ -591,10 +587,7 @@ class UsageTableHeaderInitializationTest
|
||||
: public UsageTableHeaderTest,
|
||||
public ::testing::WithParamInterface<CdmSecurityLevel> {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
InitVectorConstants();
|
||||
}
|
||||
|
||||
static void SetUpTestCase() { InitVectorConstants(); }
|
||||
};
|
||||
|
||||
TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) {
|
||||
@@ -763,8 +756,8 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
|
||||
const SecurityLevel security_level =
|
||||
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
Open(security_level)).WillOnce(Return(NO_ERROR));
|
||||
EXPECT_CALL(*crypto_session_, Open(security_level))
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
LoadUsageTableHeader(security_level, kUsageTableHeader))
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
@@ -847,8 +840,8 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
const uint32_t expect_usage_entry_number =
|
||||
kOverFullUsageEntryInfoVector.size();
|
||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
|
||||
Return(NO_ERROR)));
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
|
||||
@@ -2151,7 +2151,8 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
}
|
||||
|
||||
if (!CertificateProvisioning::ExtractDeviceInfo(
|
||||
prov_response.device_certificate(), serial_number, nullptr)) {
|
||||
prov_response.device_certificate(), serial_number, nullptr, nullptr,
|
||||
nullptr)) {
|
||||
EXPECT_TRUE(false);
|
||||
return false;
|
||||
}
|
||||
@@ -2300,13 +2301,17 @@ TEST_F(WvCdmRequestLicenseTest, UnprovisionTest) {
|
||||
CryptoWrappedKey wrapped_private_key;
|
||||
std::string serial_number;
|
||||
uint32_t system_id;
|
||||
EXPECT_TRUE(handle.RetrieveCertificate(
|
||||
false, &certificate, &wrapped_private_key, &serial_number, &system_id));
|
||||
EXPECT_EQ(
|
||||
DeviceFiles::kCertificateValid,
|
||||
handle.RetrieveCertificate(false, &certificate, &wrapped_private_key,
|
||||
&serial_number, &system_id));
|
||||
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
decryptor_->Unprovision(security_level, kDefaultCdmIdentifier));
|
||||
EXPECT_FALSE(handle.RetrieveCertificate(
|
||||
false, &certificate, &wrapped_private_key, &serial_number, &system_id));
|
||||
EXPECT_NE(
|
||||
DeviceFiles::kCertificateValid,
|
||||
handle.RetrieveCertificate(false, &certificate, &wrapped_private_key,
|
||||
&serial_number, &system_id));
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningInterposedRetryTest) {
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
static const std::string kAtscCertificateFileName = "atsccert.bin";
|
||||
static const std::string kCertificateFileName = "cert1.bin";
|
||||
static const std::string kCertificateFileNameExt = ".bin";
|
||||
static const std::string kCertificateFileNamePrefix = "cert1_";
|
||||
static const std::string kLegacyCertificateFileName = "cert.bin";
|
||||
static const std::string kLegacyCertificateFileNamePrefix = "cert";
|
||||
|
||||
// File class. The implementation is platform dependent.
|
||||
class CORE_UTIL_EXPORT File {
|
||||
public:
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
const char kCertificateFileNamePrefix[] = "cert";
|
||||
const char kCertificateFileNameExt[] = ".bin";
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
|
||||
std::string GetFileNameSafeHash(const std::string& input) {
|
||||
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
|
||||
@@ -54,6 +51,10 @@ std::string GetFileNameForIdentifier(const std::string path,
|
||||
if (file_name == kCertificateFileName && !identifier.empty()) {
|
||||
const std::string hash = GetFileNameSafeHash(identifier);
|
||||
file_name = kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
|
||||
} else if (file_name == kLegacyCertificateFileName && !identifier.empty()) {
|
||||
const std::string hash = GetFileNameSafeHash(identifier);
|
||||
file_name =
|
||||
kLegacyCertificateFileNamePrefix + hash + kCertificateFileNameExt;
|
||||
}
|
||||
|
||||
if (dir_path.empty())
|
||||
|
||||
@@ -254,7 +254,7 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
std::string HexEncode(const uint8_t* in_buffer, unsigned int size) {
|
||||
static const char kHexChars[] = "0123456789ABCDEF";
|
||||
if (size == 0) return "";
|
||||
constexpr unsigned int kMaxSafeSize = 2048;
|
||||
constexpr unsigned int kMaxSafeSize = 3072;
|
||||
if (size > kMaxSafeSize) size = kMaxSafeSize;
|
||||
// Each input byte creates two output hex characters.
|
||||
std::string out_buffer(size * 2, '\0');
|
||||
|
||||
@@ -18,7 +18,10 @@ const std::string kTestFileName2 = "test2.txt";
|
||||
const std::string kTestFileName3 = "test3.other";
|
||||
const std::string kTestFileNameExt = ".txt";
|
||||
const std::string kTestFileNameExt3 = ".other";
|
||||
const std::string kTestIdentifier1 = "some_identifier";
|
||||
const std::string kTestIdentifier2 = "some_other_identifier";
|
||||
const std::string kWildcard = "*";
|
||||
const std::string kUnderscore = "_";
|
||||
} // namespace
|
||||
|
||||
class FileTest : public testing::Test {
|
||||
@@ -175,4 +178,182 @@ TEST_F(FileTest, ListFiles) {
|
||||
EXPECT_EQ(0u, names.size());
|
||||
}
|
||||
|
||||
TEST_F(FileTest, CreateGlobalCertificates) {
|
||||
// Clear directory
|
||||
std::vector<std::string> names;
|
||||
std::string path_dir = test_vectors::kTestDir;
|
||||
std::string wild_card_path = path_dir + kWildcard;
|
||||
file_system_.Remove(wild_card_path);
|
||||
if (file_system_.List(path_dir, &names)) {
|
||||
EXPECT_EQ(0u, names.size());
|
||||
}
|
||||
|
||||
// Create certificates and verify that they exist
|
||||
std::string certificate_path = test_vectors::kTestDir + kCertificateFileName;
|
||||
std::string legacy_certificate_path =
|
||||
test_vectors::kTestDir + kLegacyCertificateFileName;
|
||||
|
||||
std::unique_ptr<File> file =
|
||||
file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file_system_.IsGlobal());
|
||||
|
||||
EXPECT_TRUE(file_system_.Exists(certificate_path));
|
||||
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
|
||||
|
||||
EXPECT_TRUE(file_system_.List(path_dir, &names));
|
||||
|
||||
// Should find two files. Order not important.
|
||||
EXPECT_EQ(2u, names.size());
|
||||
EXPECT_THAT(names, ::testing::UnorderedElementsAre(
|
||||
kCertificateFileName, kLegacyCertificateFileName));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, CreateCertificates) {
|
||||
// Clear directory
|
||||
std::vector<std::string> names;
|
||||
std::string path_dir = test_vectors::kTestDir;
|
||||
std::string wild_card_path = path_dir + kWildcard;
|
||||
file_system_.Remove(wild_card_path);
|
||||
if (file_system_.List(path_dir, &names)) {
|
||||
EXPECT_EQ(0u, names.size());
|
||||
}
|
||||
|
||||
std::string certificate_path = test_vectors::kTestDir + kCertificateFileName;
|
||||
std::string legacy_certificate_path =
|
||||
test_vectors::kTestDir + kLegacyCertificateFileName;
|
||||
|
||||
// Create Global certificates
|
||||
std::unique_ptr<File> file =
|
||||
file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file_system_.IsGlobal());
|
||||
|
||||
// Create certificates with first identifier
|
||||
file_system_.set_identifier(kTestIdentifier1);
|
||||
file = file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(!file_system_.IsGlobal());
|
||||
|
||||
// Create certificates with second identifier
|
||||
file_system_.set_identifier(kTestIdentifier2);
|
||||
file = file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(!file_system_.IsGlobal());
|
||||
|
||||
EXPECT_TRUE(file_system_.Exists(certificate_path));
|
||||
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
|
||||
|
||||
EXPECT_TRUE(file_system_.List(path_dir, &names));
|
||||
|
||||
// Should find six files. Order not important.
|
||||
bool is_global_certificate_present = false;
|
||||
bool is_global_legacy_certificate_present = false;
|
||||
size_t certificate_count = 0;
|
||||
size_t legacy_certificate_count = 0;
|
||||
EXPECT_EQ(6u, names.size());
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
if (names[i].size() > kCertificateFileName.size()) {
|
||||
if (names[i].compare(0, kCertificateFileNamePrefix.size(),
|
||||
kCertificateFileNamePrefix) == 0)
|
||||
++certificate_count;
|
||||
else if (names[i].compare(0, kLegacyCertificateFileNamePrefix.size(),
|
||||
kLegacyCertificateFileNamePrefix) == 0)
|
||||
++legacy_certificate_count;
|
||||
} else if (names[i].compare(kCertificateFileName) == 0) {
|
||||
is_global_certificate_present = true;
|
||||
} else if (names[i].compare(kLegacyCertificateFileName) == 0) {
|
||||
is_global_legacy_certificate_present = true;
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(2, certificate_count);
|
||||
EXPECT_EQ(2, legacy_certificate_count);
|
||||
EXPECT_TRUE(is_global_certificate_present);
|
||||
EXPECT_TRUE(is_global_legacy_certificate_present);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, RemoveCertificates) {
|
||||
// Clear directory
|
||||
std::vector<std::string> names;
|
||||
std::string path_dir = test_vectors::kTestDir;
|
||||
std::string wild_card_path = path_dir + kWildcard;
|
||||
file_system_.Remove(wild_card_path);
|
||||
if (file_system_.List(path_dir, &names)) {
|
||||
EXPECT_EQ(0u, names.size());
|
||||
}
|
||||
|
||||
std::string certificate_path = test_vectors::kTestDir + kCertificateFileName;
|
||||
std::string legacy_certificate_path =
|
||||
test_vectors::kTestDir + kLegacyCertificateFileName;
|
||||
|
||||
// Create Global certificates
|
||||
std::unique_ptr<File> file =
|
||||
file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file_system_.IsGlobal());
|
||||
|
||||
// Create certificates with first identifier
|
||||
file_system_.set_identifier(kTestIdentifier1);
|
||||
file = file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(!file_system_.IsGlobal());
|
||||
|
||||
// Create certificates with second identifier
|
||||
file_system_.set_identifier(kTestIdentifier2);
|
||||
file = file_system_.Open(certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(!file_system_.IsGlobal());
|
||||
|
||||
EXPECT_TRUE(file_system_.Exists(certificate_path));
|
||||
EXPECT_TRUE(file_system_.Exists(legacy_certificate_path));
|
||||
|
||||
EXPECT_TRUE(file_system_.List(path_dir, &names));
|
||||
|
||||
EXPECT_EQ(6u, names.size());
|
||||
|
||||
// Remove all even number listed files
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
if (i % 2 == 0) {
|
||||
EXPECT_TRUE(file_system_.Remove(test_vectors::kTestDir + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that they have been removed
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
if (i % 2 == 1) {
|
||||
EXPECT_TRUE(file_system_.Exists(test_vectors::kTestDir + names[i]));
|
||||
} else {
|
||||
EXPECT_FALSE(file_system_.Exists(test_vectors::kTestDir + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all odd number listed files
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
if (i % 2 == 1) {
|
||||
EXPECT_TRUE(file_system_.Remove(test_vectors::kTestDir + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all have been removed
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
EXPECT_FALSE(file_system_.Exists(test_vectors::kTestDir + names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user