Catch null pst in OEMCrypto
This is a copy of https://widevine-internal-review.googlesource.com/#/c/11030 It is an error for the key control block to have a nonzero replay control flag and a null pst. This CL adds unit tests to oemcrypto_test to verify that oemcrypto checkes this. A unit test is also added for verifying that an offline license has a valid nonce the first time it is loaded. It also updates the reference implementation (mock) to check that the pst is not empty when the replay control flag is nonzero. It also updates the level 3 implementation to check that the pst is not empty when the replay control flag is nonzero. This change is compiled into the arm library, but because of compilation errors, is not included in x86 or mips. Current Library Version: arm: Level3 Library Aug 27 2014 18:42:40 bug: 16525204 OEMCrypto unit test for reloading offline license bug: 16844305 Mock OEMCrypto does not catch null pst Change-Id: Icdb090e80fc92522c187b26f30e5ba082f26363b
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -344,6 +344,13 @@ bool SessionContext::CheckNonceOrEntry(const KeyControlBlock& key_control_block,
|
||||
switch (key_control_block.control_bits() & kControlReplayMask) {
|
||||
case kControlNonceRequired: // Online license. Nonce always required.
|
||||
if (!CheckNonce(key_control_block.nonce())) return false;
|
||||
if (pst.size() == 0) {
|
||||
LOGE("KCB: PST null for kControlNonceRequired.");
|
||||
return false;
|
||||
}
|
||||
if (!(key_control_block.control_bits() & kControlNonceEnabled)) {
|
||||
LOGE("KCB: Server provided Nonce_Required but Nonce_Enabled = 0.");
|
||||
}
|
||||
if (!usage_entry_) {
|
||||
if (ce_->usage_table()->FindEntry(pst)) {
|
||||
LOGE("KCB: Cannot create duplicate entries in usage table.");
|
||||
@@ -353,6 +360,13 @@ bool SessionContext::CheckNonceOrEntry(const KeyControlBlock& key_control_block,
|
||||
}
|
||||
break; // Offline license. Nonce required on first use.
|
||||
case kControlNonceOrEntry:
|
||||
if (key_control_block.control_bits() & kControlNonceEnabled) {
|
||||
LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1.");
|
||||
}
|
||||
if (pst.size() == 0) {
|
||||
LOGE("KCB: PST null for kControlNonceOrEntry.");
|
||||
return false;
|
||||
}
|
||||
if (!usage_entry_) {
|
||||
usage_entry_ = ce_->usage_table()->FindEntry(pst);
|
||||
if (usage_entry_) {
|
||||
@@ -405,7 +419,8 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
std::vector<uint8_t> key_data_iv;
|
||||
std::vector<uint8_t> key_control;
|
||||
std::vector<uint8_t> key_control_iv;
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
std::vector<uint8_t> pstv;
|
||||
if (pst_length > 0) pstv.assign(pst, pst + pst_length);
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
key_id.assign(key_array[i].key_id,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
|
||||
@@ -318,14 +318,21 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// Later on, we use pst_length to verify the the pst is valid. This makes
|
||||
// sure that we aren't given a null string but told it has postiive length.
|
||||
if ((pst == NULL && pst_length > 0) || (pst != NULL && pst_length == 0)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_ONCTEXT - null pst.]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// Range check
|
||||
if (!RangeCheck(message, message_length, enc_mac_keys,
|
||||
2 * wvcdm::MAC_KEY_SIZE, true) ||
|
||||
!RangeCheck(message, message_length, enc_mac_key_iv, wvcdm::KEY_IV_SIZE,
|
||||
true) ||
|
||||
!RangeCheck(message, message_length, pst, pst_length, true)) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range check.]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
|
||||
@@ -4525,9 +4525,7 @@ class DISABLED_UsageTableTest : public DISABLED_GenericDRMTest,
|
||||
void LoadOfflineLicense(Session& s, const std::string& pst) {
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceOrEntry,
|
||||
s.get_nonce(), pst);
|
||||
s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst);
|
||||
s.EncryptAndSign();
|
||||
s.LoadTestKeys(pst, new_mac_keys_);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
@@ -4613,6 +4611,27 @@ TEST_P(DISABLED_UsageTableTest, RepeatOnlineLicense) {
|
||||
}
|
||||
}
|
||||
|
||||
// A license with non-zero replay control bits needs a valid pst..
|
||||
TEST_P(DISABLED_UsageTableTest, OnlineEmptyPST) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
||||
s.get_nonce());
|
||||
s.EncryptAndSign();
|
||||
OEMCryptoResult sts = OEMCrypto_LoadKeys(
|
||||
s.session_id(), s.message_ptr(), sizeof(MessageData), &s.signature()[0],
|
||||
s.signature().size(), s.encrypted_license().mac_key_iv,
|
||||
s.encrypted_license().mac_keys, kNumKeys, s.key_array(),
|
||||
NULL, 0);
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, EmptyTable) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
@@ -5178,9 +5197,8 @@ TEST_P(DISABLED_UsageTableTest, BadReloadOfflineLicense) {
|
||||
Session s2;
|
||||
s2.open();
|
||||
s2.GenerateDerivedKeys();
|
||||
s2.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceOrEntry,
|
||||
s2.get_nonce(), pst);
|
||||
s2.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry,
|
||||
s2.get_nonce(), pst);
|
||||
s2.EncryptAndSign();
|
||||
uint8_t* pst_ptr = s2.encrypted_license().pst;
|
||||
ASSERT_NE(OEMCrypto_SUCCESS,
|
||||
@@ -5201,6 +5219,50 @@ TEST_P(DISABLED_UsageTableTest, BadReloadOfflineLicense) {
|
||||
}
|
||||
}
|
||||
|
||||
// An offline license should not load on the first call if the nonce is bad.
|
||||
TEST_P(DISABLED_UsageTableTest, OfflineBadNonce) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
std::string pst = "my_pst";
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceOrEntry,
|
||||
42, pst);
|
||||
s.EncryptAndSign();
|
||||
uint8_t* pst_ptr = s.encrypted_license().pst;
|
||||
OEMCryptoResult sts = OEMCrypto_LoadKeys(
|
||||
s.session_id(), s.message_ptr(), sizeof(MessageData), &s.signature()[0],
|
||||
s.signature().size(), s.encrypted_license().mac_key_iv,
|
||||
s.encrypted_license().mac_keys, kNumKeys, s.key_array(),
|
||||
pst_ptr, pst.length());
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
// An offline license needs a valid pst.
|
||||
TEST_P(DISABLED_UsageTableTest, OfflineEmptyPST) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceOrEntry,
|
||||
s.get_nonce());
|
||||
s.EncryptAndSign();
|
||||
OEMCryptoResult sts = OEMCrypto_LoadKeys(
|
||||
s.session_id(), s.message_ptr(), sizeof(MessageData), &s.signature()[0],
|
||||
s.signature().size(), s.encrypted_license().mac_key_iv,
|
||||
s.encrypted_license().mac_keys, kNumKeys, s.key_array(),
|
||||
NULL, 0);
|
||||
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, DeactivateOfflineLicense) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
std::string pst = "my_pst";
|
||||
@@ -5248,9 +5310,8 @@ TEST_P(DISABLED_UsageTableTest, BadRange) {
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeys();
|
||||
s.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceOrEntry,
|
||||
s.get_nonce(), pst);
|
||||
s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry,
|
||||
s.get_nonce(), pst);
|
||||
s.EncryptAndSign();
|
||||
uint8_t* pst_ptr = s.license().pst; // Bad: not in encrypted_license.
|
||||
ASSERT_NE(
|
||||
|
||||
Reference in New Issue
Block a user