More OEMCrypto Usage Table Unit Tests

Merge from widevine repo of http://go/wvgerrit/23421

This CL adds some more unit tests for big usage tables, and corrects a
problem found in the reference code.

Change-Id: Iae9a4406d79a13362223c2b4da7365b845d92382
This commit is contained in:
Fred Gylys-Colwell
2017-01-27 15:23:56 -08:00
parent 49a61f6270
commit e4231fea3b
5 changed files with 46 additions and 17 deletions

View File

@@ -948,7 +948,6 @@ extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed."); LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
// TODO(fredgc): Don't use the keybox to encrypt the wrapped RSA key.
const std::vector<uint8_t> context( const std::vector<uint8_t> context(
wrapped->context, wrapped->context + sizeof(wrapped->context)); wrapped->context, wrapped->context + sizeof(wrapped->context));
// Generate mac and encryption keys for encrypting the signature. // Generate mac and encryption keys for encrypting the signature.
@@ -1157,7 +1156,6 @@ extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); dump_hex("iv", wrapped->iv, sizeof(wrapped->iv));
} }
} }
// TODO(fredgc): Don't use the keybox to encrypt the wrapped RSA key.
if (!crypto_engine->ValidRootOfTrust()) { if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID; return OEMCrypto_ERROR_KEYBOX_INVALID;

View File

@@ -116,7 +116,7 @@ void UsageTableEntry::Deactivate(const std::vector<uint8_t>& pst) {
} else if (data_.status == kActive) { } else if (data_.status == kActive) {
data_.status = kInactiveUsed; data_.status = kInactiveUsed;
} }
forbid_report_ = false; forbid_report_ = true;
data_.generation_number++; data_.generation_number++;
usage_table_->IncrementGeneration(); usage_table_->IncrementGeneration();
} }
@@ -130,13 +130,13 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
pst.size() != data_.pst_length) { pst.size() != data_.pst_length) {
LOGE("ReportUsage: bad pst length = %d, should be %d.", LOGE("ReportUsage: bad pst length = %d, should be %d.",
pst.size(), data_.pst_length); pst.size(), data_.pst_length);
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_WRONG_PST;
} }
if (memcmp(&pst[0], data_.pst, data_.pst_length)) { if (memcmp(&pst[0], data_.pst, data_.pst_length)) {
LOGE("ReportUsage: wrong pst %s, should be %s.", LOGE("ReportUsage: wrong pst %s, should be %s.",
wvcdm::b2a_hex(pst).c_str(), wvcdm::b2a_hex(pst).c_str(),
wvcdm::HexEncode(data_.pst, data_.pst_length).c_str()); wvcdm::HexEncode(data_.pst, data_.pst_length).c_str());
return OEMCrypto_ERROR_INVALID_CONTEXT; return OEMCrypto_ERROR_WRONG_PST;
} }
size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size()); size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size());
if (*buffer_length < length_needed) { if (*buffer_length < length_needed) {
@@ -658,7 +658,6 @@ bool UsageTable::LoadGenerationNumber(bool or_make_new_one) {
if (or_make_new_one) { if (or_make_new_one) {
RAND_bytes(reinterpret_cast<uint8_t*>(&master_generation_number_), RAND_bytes(reinterpret_cast<uint8_t*>(&master_generation_number_),
sizeof(int64_t)); sizeof(int64_t));
master_generation_number_ = 0; // TODO(fredgc): remove after debugging.
return true; return true;
} }
LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str());

View File

@@ -872,7 +872,8 @@ void Session::MoveUsageEntry(uint32_t new_index,
ASSERT_NO_FATAL_FAILURE(close()); ASSERT_NO_FATAL_FAILURE(close());
} }
void Session::GenerateReport(const std::string& pst, bool expect_success, void Session::GenerateReport(const std::string& pst,
OEMCryptoResult expected_result,
Session* other) { Session* other) {
ASSERT_TRUE(open_); ASSERT_TRUE(open_);
if (other) { // If other is specified, copy mac keys. if (other) { // If other is specified, copy mac keys.
@@ -883,7 +884,7 @@ void Session::GenerateReport(const std::string& pst, bool expect_success,
OEMCryptoResult sts = OEMCrypto_ReportUsage( OEMCryptoResult sts = OEMCrypto_ReportUsage(
session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()), pst.length(), session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()), pst.length(),
&pst_report_buffer_[0], &length); &pst_report_buffer_[0], &length);
if (expect_success) { if (expected_result == OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
} }
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
@@ -893,12 +894,11 @@ void Session::GenerateReport(const std::string& pst, bool expect_success,
sts = OEMCrypto_ReportUsage(session_id(), sts = OEMCrypto_ReportUsage(session_id(),
reinterpret_cast<const uint8_t*>(pst.c_str()), reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length(), &pst_report_buffer_[0], &length); pst.length(), &pst_report_buffer_[0], &length);
if (!expect_success) { ASSERT_EQ(expected_result, sts);
ASSERT_NE(OEMCrypto_SUCCESS, sts); if (expected_result != OEMCrypto_SUCCESS) {
return; return;
} }
ASSERT_EQ(pst_report_buffer_.size(), length); ASSERT_EQ(pst_report_buffer_.size(), length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH); vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH);
unsigned int sig_len = SHA_DIGEST_LENGTH; unsigned int sig_len = SHA_DIGEST_LENGTH;
HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(),

View File

@@ -286,11 +286,12 @@ class Session {
const vector<uint8_t>& encrypted_usage_entry() const { const vector<uint8_t>& encrypted_usage_entry() const {
return encrypted_usage_entry_; return encrypted_usage_entry_;
} }
// Generates a usage report for the specified pst. If expect_success is true, // Generates a usage report for the specified pst. If there is success,
// the report's signature is verified, and several fields are given sanity // the report's signature is verified, and several fields are given sanity
// checks. If other is not null, then the mac keys are copied from other in // checks. If other is not null, then the mac keys are copied from other in
// order to verify signatures. // order to verify signatures.
void GenerateReport(const std::string& pst, bool expect_success = true, void GenerateReport(const std::string& pst,
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
Session* other = 0); Session* other = 0);
// Move this usage entry to a new index. // Move this usage entry to a new index.
void MoveUsageEntry(uint32_t new_index, std::vector<uint8_t>* header_buffer, void MoveUsageEntry(uint32_t new_index, std::vector<uint8_t>* header_buffer,

View File

@@ -4386,6 +4386,36 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) {
s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
} }
TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) {
std::string pst = "my_pst";
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
s.get_nonce(), pst));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused));
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR());
// Cannot generate a report without first updating the file.
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// Now it's OK.
ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst));
// Cannot generate a report without first updating the file.
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
// Decrypt should fail.
ASSERT_NO_FATAL_FAILURE(
s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) {
std::string pst = "my_pst"; std::string pst = "my_pst";
Session s; Session s;
@@ -4802,7 +4832,7 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) {
ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused));
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst, kActive)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive));
ASSERT_NO_FATAL_FAILURE(s.close()); ASSERT_NO_FATAL_FAILURE(s.close());
} }
@@ -4934,7 +4964,7 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) {
ASSERT_NO_FATAL_FAILURE(s3.open()); ASSERT_NO_FATAL_FAILURE(s3.open());
ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s));
ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, true, &s)); ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s));
EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); EXPECT_EQ(kInactiveUsed, s3.pst_report().status());
} }
@@ -5265,7 +5295,8 @@ TEST_F(UsageTableTest, GenerateReportWrongPST) {
ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_));
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst", false)); ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst",
OEMCrypto_ERROR_WRONG_PST));
} }
TEST_F(UsageTableTest, TimingTest) { TEST_F(UsageTableTest, TimingTest) {
@@ -5548,7 +5579,7 @@ TEST_F(UsageTableTest, PSTLargeBuffer) {
ASSERT_NO_FATAL_FAILURE(s3.open()); ASSERT_NO_FATAL_FAILURE(s3.open());
ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s));
ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, true, &s)); ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s));
EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); EXPECT_EQ(kInactiveUsed, s3.pst_report().status());
} }