Regular sync.

Changes include:
1. Fix refreshkeys when handling renewal response.
2. Change ECM start detect method.
3. Fix signing key truncation.
4. Reformat C++ code.
5. Return license_id in LICENSE_CAS_READY payload.
6. Expose OEMCrypto API version in the license request.
7. Add support for newly added widevine cas ids.
8. Store content iv and encryption mode info to entitled key.
9. Upgrade ODK library to 16.4.
This commit is contained in:
huihli
2020-10-21 11:16:23 -07:00
parent 0f6db6f751
commit 2feec02df2
39 changed files with 703 additions and 546 deletions

View File

@@ -12,10 +12,10 @@
/* The version of this library. */
#define ODK_MAJOR_VERSION 16
#define ODK_MINOR_VERSION 3
#define ODK_MINOR_VERSION 4
/* ODK Version string. Date changed automatically on each release. */
#define ODK_RELEASE_DATE "ODK v16.3 2020-06-02"
#define ODK_RELEASE_DATE "ODK v16.4 2020-10-07"
/* The lowest version number for an ODK message. */
#define ODK_FIRST_VERSION 16

View File

@@ -299,20 +299,24 @@ OEMCryptoResult ODK_ParseLicense(
return ODK_ERROR_CORE_MESSAGE;
}
if (parsed_license->nonce_required) {
if (initial_license_load) {
if (nonce_values->nonce !=
license_response.request.core_message.nonce_values.nonce ||
nonce_values->session_id !=
license_response.request.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
} else { /* !initial_license_load */
nonce_values->nonce =
license_response.request.core_message.nonce_values.nonce;
nonce_values->session_id =
license_response.request.core_message.nonce_values.session_id;
/* If this is the first time we load this license, then we verify that the
* nonce values are the correct, otherwise we copy the nonce values. If the
* nonce values are not required to be correct, then we don't know if this is
* an initial load or not. In that case, we also copy the values so that we
* can use the nonce values later for a renewal.
*/
if (parsed_license->nonce_required && initial_license_load) {
if (nonce_values->nonce !=
license_response.request.core_message.nonce_values.nonce ||
nonce_values->session_id !=
license_response.request.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
} else { /* !initial_license_load, or can't tell if initial. */
nonce_values->nonce =
license_response.request.core_message.nonce_values.nonce;
nonce_values->session_id =
license_response.request.core_message.nonce_values.session_id;
}
/* For v16, in order to be backwards compatible with a v15 license server,
* OEMCrypto stores a hash of the core license request and only signs the
@@ -359,9 +363,12 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
*/
/* If a renewal request is lost in transit, we should throw it out and create
* a new one. We use the timestamp to make sure we have the latest request.
* We only do this if playback has already started. This allows us to reload
* an offline license and also reload a renewal before starting playback.
*/
if (clock_values->time_of_renewal_request <
renewal_response.request.playback_time) {
if (clock_values->timer_status != ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED &&
clock_values->time_of_renewal_request <
renewal_response.request.playback_time) {
return ODK_STALE_RENEWAL;
}
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,

View File

@@ -13,6 +13,8 @@
#include <utility>
#include <vector>
#include "OEMCryptoCAS.h"
namespace wvoec_ref {
class KeyControlBlock {
@@ -66,18 +68,21 @@ class Key {
class EntitledKey {
public:
explicit EntitledKey(std::vector<uint8_t> key_string)
: value_(std::move(key_string)), ctr_mode_(true){};
: value_(std::move(key_string)), cipher_mode_(OEMCrypto_CipherMode_CTR){};
EntitledKey(const EntitledKey&) = default;
EntitledKey(EntitledKey&&) = default;
~EntitledKey() = default;
const std::vector<uint8_t>& value() const { return value_; }
bool ctr_mode() const { return ctr_mode_; }
void set_ctr_mode(bool ctr_mode) { ctr_mode_ = ctr_mode; }
OEMCryptoCipherMode cipher_mode() const { return cipher_mode_; }
void set_cipher_mode(OEMCryptoCipherMode mode) { cipher_mode_ = mode; }
void set_content_iv(const std::vector<uint8_t>& iv) { content_iv_ = iv; }
const std::vector<uint8_t>& content_iv() const { return content_iv_; }
private:
std::vector<uint8_t> value_;
bool ctr_mode_;
OEMCryptoCipherMode cipher_mode_;
std::vector<uint8_t> content_iv_;
};
} // namespace wvoec_ref

View File

@@ -7,11 +7,6 @@
#include "oemcrypto_session.h"
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/cmac.h>
@@ -23,6 +18,11 @@
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include "advance_iv_ctr.h"
#include "keys.h"
@@ -97,8 +97,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
CryptoEngine::kApiVersion, sid);
}
SessionContext::~SessionContext() {
}
SessionContext::~SessionContext() {}
// Internal utility function to derive key using CMAC-128
bool SessionContext::DeriveKey(const std::vector<uint8_t>& key,
@@ -562,11 +561,13 @@ OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce,
OEMCryptoResult SessionContext::CheckNonceOrEntry(
const KeyControlBlock& key_control_block) {
switch (key_control_block.control_bits() & wvoec::kControlReplayMask) {
case wvoec::kControlNonceRequired: // Online license. Nonce always required.
case wvoec::kControlNonceRequired: // Online license. Nonce always
// required.
return CheckStatusOnline(key_control_block.nonce(),
key_control_block.control_bits());
break;
case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on first use.
case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on
// first use.
return CheckStatusOffline(key_control_block.nonce(),
key_control_block.control_bits());
break;
@@ -725,9 +726,8 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
message + key_array[i].key_control_iv.offset,
message + key_array[i].key_control_iv.offset + wvoec::KEY_IV_SIZE);
OEMCryptoResult result =
InstallKey(key_id, enc_key_data, key_data_iv, key_control,
key_control_iv);
OEMCryptoResult result = InstallKey(key_id, enc_key_data, key_data_iv,
key_control, key_control_iv);
if (result != OEMCrypto_SUCCESS) {
status = result;
break;
@@ -882,9 +882,20 @@ OEMCryptoResult SessionContext::LoadEntitledCasKeys(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!key_session->AddOrUpdateContentKey(
entitlement_key, content_key_id,
std::unique_ptr<EntitledKey>(new EntitledKey(content_key)))) {
auto content_key_obj =
std::unique_ptr<EntitledKey>(new EntitledKey(content_key));
// Store content iv and encryption info.
if (key_data->content_iv.length > 0) {
std::vector<uint8_t> content_iv;
content_iv.assign(
message + key_data->content_iv.offset,
message + key_data->content_iv.offset + key_data->content_iv.length);
content_key_obj->set_content_iv(content_iv);
}
content_key_obj->set_cipher_mode(key_data->cipher_mode);
if (!key_session->AddOrUpdateContentKey(entitlement_key, content_key_id,
std::move(content_key_obj))) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
@@ -1172,7 +1183,7 @@ OEMCryptoResult SessionContext::CheckKeyControlBlockUse(
LOGE("[%s(): CGMS required, but buffer is clear", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
if ( ce_->analog_display_active() && !ce_->cgms_a_active()) {
if (ce_->analog_display_active() && !ce_->cgms_a_active()) {
LOGE("[%s(): control bit says CGMS required", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
@@ -1200,8 +1211,9 @@ OEMCryptoResult SessionContext::Generic_Encrypt(
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result =
CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
LOGE("[Generic_Encrypt(): algorithm bad");
@@ -1242,8 +1254,9 @@ OEMCryptoResult SessionContext::Generic_Decrypt(
LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result =
CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
@@ -1327,8 +1340,8 @@ OEMCryptoResult SessionContext::Generic_Verify(
LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Verify", wvoec::kControlAllowVerify,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result = CheckKeyUse(
"Generic_Verify", wvoec::kControlAllowVerify, OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_HMAC_SHA256) {
LOGE("[Generic_Verify(): bad algorithm");
@@ -1411,7 +1424,7 @@ OEMCryptoResult SessionContext::SelectEntitledContentKey(
LOGE("No key matches key id");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR);
content_key->set_cipher_mode(cipher_mode);
// Update current content key selection.
key_session->SetCurrentKey(key_id);
@@ -1579,9 +1592,11 @@ OEMCryptoResult SessionContext::DecryptSamples(
subsample_source += subsample_length;
advance_dest_buffer(&subsample_dest, subsample_length);
if (subsample.num_bytes_encrypted > 0) {
bool is_ctr_mode = key_session == nullptr
? current_content_key()->ctr_mode()
: key_session->CurrentContentKey()->ctr_mode();
bool is_ctr_mode =
key_session == nullptr
? current_content_key()->ctr_mode()
: key_session->CurrentContentKey()->cipher_mode() ==
OEMCrypto_CipherMode_CTR;
if (is_ctr_mode) {
wvutil::AdvanceIvCtr(
&subsample_iv,
@@ -1702,7 +1717,8 @@ OEMCryptoResult SessionContext::ChooseDecrypt(
}
bool is_ctr_mode = key_session == nullptr
? current_content_key()->ctr_mode()
: key_session->CurrentContentKey()->ctr_mode();
: key_session->CurrentContentKey()->cipher_mode() ==
OEMCrypto_CipherMode_CTR;
if (!is_ctr_mode) {
if (block_offset > 0 || pattern->encrypt == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;

View File

@@ -154,13 +154,14 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
// tests are failing when the device has the wrong keybox installed.
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01";
"OEMCrypto unit tests for API 16.4. Tests last updated 2020-10-07";
cout << " " << log_message << "\n";
LOGI("%s", log_message.c_str());
// If any of the following fail, then it is time to update the log message
// above.
EXPECT_EQ(ODK_MAJOR_VERSION, 16);
EXPECT_EQ(ODK_MINOR_VERSION, 3);
// Note on minor versions. Widevine requires version 16.3 or greater for CE
// CDM and Android devices. For CAS devices that do not support usage
// tables, we strongly recommend 16.4.
EXPECT_GE(ODK_MINOR_VERSION, 3);
EXPECT_LE(ODK_MINOR_VERSION, 4);
EXPECT_EQ(kCurrentAPI, 16u);
const char* level = OEMCrypto_SecurityLevel();
ASSERT_NE(nullptr, level);