Add rollback-prevention time methods to ref

Merge from master branch of Widevine repo of http://go/wvgerrit/66077
Merge from oemcrypto-v15 branch of Widevine repo of http://go/wvgerrit/64562

Bug: b/78357351

b/62058202 addressed issues with the Level 3 OEMCrypto in guarding
against rollback. This change does something similar for the ref, so
that OEMCrypto vendors have rollback-prevention code they can refer to.

Test: linux/ce cdm unit tests
Test: tested as part of http://go/ag/5501993

Change-Id: I76128c5def2615ecbdbe94e3af1fec4a025be8c1
This commit is contained in:
Srujan Gaddam
2018-11-12 14:19:56 -08:00
committed by Fred Gylys-Colwell
parent b7e4b56934
commit 0ee5214b92
7 changed files with 135 additions and 14 deletions

View File

@@ -14,12 +14,14 @@
#include <utility>
#include <vector>
#include <openssl/aes.h>
#include <openssl/err.h>
#include "keys.h"
#include "log.h"
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "string_conversions.h"
namespace wvoec_ref {
@@ -85,6 +87,105 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
return NULL;
}
time_t CryptoEngine::OnlineTime() {
// Use the monotonic clock for times that don't have to be stable across
// device boots.
timespec current_time;
int gettime_result = clock_gettime(CLOCK_MONOTONIC, &current_time);
if (gettime_result == 0) {
return current_time.tv_sec;
} else {
// Can't use monotonic clock, use roll back time.
return RollbackCorrectedOfflineTime();
}
}
time_t CryptoEngine::RollbackCorrectedOfflineTime() {
struct TimeInfo {
// The max time recorded through this function call.
time_t previous_time;
// If the wall time is rollbacked to before the previous_time, this member
// is updated to reflect the offset.
time_t rollback_offset;
// Pad the struct so that TimeInfo is a multiple of 16.
uint8_t padding[16 - (2 * sizeof(time_t)) % 16];
};
std::vector<uint8_t> encrypted_buffer(sizeof(TimeInfo));
std::vector<uint8_t> clear_buffer(sizeof(TimeInfo));
TimeInfo time_info;
memset(&time_info, 0, sizeof(time_info));
// Use the device key for encrypt/decrypt.
const std::vector<uint8_t>& key = DeviceRootKey();
wvcdm::File* file;
std::string path;
// Note: this path is OK for a real implementation, but using security level 1
// would be better.
// TODO(fredgc, jfore): Address how this property is presented to the ref.
// For now, the path is empty.
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGE("RollbackCorrectedOfflineTime: Unable to get base path");
}*/
std::string filename = path + "StoredUsageTime.dat";
wvcdm::FileSystem* file_system = file_system_.get();
if (file_system->Exists(filename)) {
// Load time info from previous call to this function.
file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
}
file->Read(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
file->Close();
// Decrypt the encrypted TimeInfo buffer.
AES_KEY aes_key;
AES_set_decrypt_key(&key[0], 128, &aes_key);
std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0);
AES_cbc_encrypt(&encrypted_buffer[0], &clear_buffer[0], sizeof(TimeInfo),
&aes_key, iv.data(), AES_DECRYPT);
memcpy(&time_info, &clear_buffer[0], sizeof(TimeInfo));
}
time_t current_time;
// Add any time offsets in the past to the current time.
current_time = time(NULL) + time_info.rollback_offset;
if (time_info.previous_time > current_time) {
// Time has been rolled back.
// Update the rollback offset.
time_info.rollback_offset += time_info.previous_time - current_time;
// Keep current time at previous recorded time.
current_time = time_info.previous_time;
}
// The new previous_time will either stay the same or move forward.
time_info.previous_time = current_time;
// Copy updated data and encrypt the buffer.
memcpy(&clear_buffer[0], &time_info, sizeof(TimeInfo));
AES_KEY aes_key;
AES_set_encrypt_key(&key[0], 128, &aes_key);
std::vector<uint8_t> iv(wvoec::KEY_IV_SIZE, 0);
AES_cbc_encrypt(&clear_buffer[0], &encrypted_buffer[0], sizeof(TimeInfo),
&aes_key, iv.data(), AES_ENCRYPT);
// Write the encrypted buffer to disk.
file = file_system->Open(
filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
filename.c_str());
return time(NULL);
}
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
file->Close();
// Return time with offset.
return current_time;
}
OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() {
return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1;
}

View File

@@ -87,6 +87,10 @@ class CryptoEngine {
return kMaxSupportedOEMCryptoSessions;
}
time_t OnlineTime();
time_t RollbackCorrectedOfflineTime();
// Returns the HDCP version currently in use.
virtual OEMCrypto_HDCP_Capability config_current_hdcp_capability();

View File

@@ -31,16 +31,21 @@
namespace wvoec_ref {
OldUsageTableEntry::OldUsageTableEntry(const std::vector<uint8_t> &pst_hash)
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash)
: pst_hash_(pst_hash),
time_of_license_received_(time(NULL)),
old_usage_table_(old_usage_table),
time_of_license_received_(
old_usage_table_->ce_->RollbackCorrectedOfflineTime()),
time_of_first_decrypt_(0),
time_of_last_decrypt_(0),
status_(kUnused) {}
OldUsageTableEntry::~OldUsageTableEntry() {}
OldUsageTableEntry::OldUsageTableEntry(const OldStoredUsageEntry *buffer) {
OldUsageTableEntry::OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer)
: old_usage_table_(old_usage_table) {
pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH);
time_of_license_received_ = buffer->time_of_license_received;
time_of_first_decrypt_ = buffer->time_of_first_decrypt;
@@ -157,7 +162,7 @@ OldUsageTable::OldUsageTable(CryptoEngine *ce) {
// entries.
for (uint64_t i = 0; i < stored_table->count; i++) {
OldUsageTableEntry *entry =
new OldUsageTableEntry(&stored_table->entries[i].entry);
new OldUsageTableEntry(this, &stored_table->entries[i].entry);
table_[entry->pst_hash()] = entry;
}
}
@@ -188,7 +193,7 @@ OldUsageTableEntry *OldUsageTable::CreateEntry(
LOGE("OldUsageTable: Could not compute hash of pst.");
return NULL;
}
OldUsageTableEntry *entry = new OldUsageTableEntry(pst_hash);
OldUsageTableEntry *entry = new OldUsageTableEntry(this, pst_hash);
wvcdm::AutoLock lock(lock_);
table_[pst_hash] = entry;
return entry;

View File

@@ -23,6 +23,7 @@
namespace wvoec_ref {
class CryptoEngine;
class OldUsageTable;
class UsagetTableEntry;
struct OldStoredUsageEntry {
@@ -51,13 +52,16 @@ struct OldStoredUsageTable {
class OldUsageTableEntry {
public:
OldUsageTableEntry(const std::vector<uint8_t> &pst_hash);
OldUsageTableEntry(const OldStoredUsageEntry *buffer);
OldUsageTableEntry(OldUsageTable *old_usage_table,
const std::vector<uint8_t> &pst_hash);
OldUsageTableEntry(OldUsageTable *old_usage_table,
const OldStoredUsageEntry *buffer);
~OldUsageTableEntry();
const std::vector<uint8_t> &pst_hash() const { return pst_hash_; }
private:
std::vector<uint8_t> pst_hash_;
const OldUsageTable *old_usage_table_;
int64_t time_of_license_received_;
int64_t time_of_first_decrypt_;
int64_t time_of_last_decrypt_;
@@ -88,6 +92,8 @@ class OldUsageTable {
wvcdm::Lock lock_;
int64_t generation_;
CryptoEngine *ce_;
friend class OldUsageTableEntry;
};
} // namespace wvoec_ref

View File

@@ -483,10 +483,10 @@ OEMCryptoResult SessionContext::CheckNonceOrEntry(
return OEMCrypto_SUCCESS;
}
void SessionContext::StartTimer() { timer_start_ = time(NULL); }
void SessionContext::StartTimer() { timer_start_ = ce_->OnlineTime(); }
uint32_t SessionContext::CurrentTimer() {
time_t now = time(NULL);
time_t now = ce_->OnlineTime();
return now - timer_start_;
}

View File

@@ -71,7 +71,8 @@ OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) {
data_.pst_length = pst_length;
if (!pst || !pst_length) return OEMCrypto_ERROR_INVALID_CONTEXT;
memcpy(data_.pst, pst, pst_length);
data_.time_of_license_received = time(NULL);
data_.time_of_license_received =
usage_table_->ce_->RollbackCorrectedOfflineTime();
return OEMCrypto_SUCCESS;
}
@@ -105,7 +106,8 @@ bool UsageTableEntry::CheckForUse() {
recent_decrypt_ = true;
if (data_.status == kUnused) {
data_.status = kActive;
data_.time_of_first_decrypt = time(NULL);
data_.time_of_first_decrypt =
usage_table_->ce_->RollbackCorrectedOfflineTime();
data_.generation_number++;
usage_table_->IncrementGeneration();
}
@@ -149,7 +151,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
wvcdm::Unpacked_PST_Report pst_report(buffer);
int64_t now = time(NULL);
int64_t now = usage_table_->ce_->RollbackCorrectedOfflineTime();
pst_report.set_seconds_since_license_received(now -
data_.time_of_license_received);
pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt);
@@ -170,7 +172,8 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
void UsageTableEntry::UpdateAndIncrement() {
if (recent_decrypt_) {
data_.time_of_last_decrypt = time(NULL);
data_.time_of_last_decrypt =
usage_table_->ce_->RollbackCorrectedOfflineTime();
recent_decrypt_ = false;
}
data_.generation_number++;
@@ -757,7 +760,7 @@ OEMCryptoResult UsageTable::CreateOldUsageEntry(
std::vector<uint8_t> pstv(pst, pst + pst_length);
OldUsageTableEntry* old_entry = old_table_->CreateEntry(pstv);
int64_t now = time(NULL);
int64_t now = ce_->RollbackCorrectedOfflineTime();
old_entry->time_of_license_received_ = now - time_since_license_received;
old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt;
old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt;

View File

@@ -129,6 +129,8 @@ class UsageTable {
std::vector<int64_t> generation_numbers_;
std::vector<SessionContext*> sessions_;
OldUsageTable* old_table_;
friend class UsageTableEntry;
};
} // namespace wvoec_ref