Full decrypt path testing
Merge from master branch of Widevine repo of http://go/wvgerrit/66080 Merge from oemcrypto-v15 branch of Widevine repo of http://go/wvgerrit/64002 This CL updates OEMCrypto reference code and unit tests to support full decrypt path testing. Test: unit tests Test: tested as part of http://go/ag/5501993 Bug: 34078913 Change-Id: Ia67374599d6619698a336f41513068ad04294e7f
This commit is contained in:
@@ -1617,4 +1617,52 @@ extern "C" OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
|
||||
pst_length);
|
||||
}
|
||||
|
||||
extern "C" uint32_t OEMCrypto_SupportsDecryptHash() {
|
||||
return OEMCrypto_CRC_Clear_Buffer;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_InitializeDecryptHash(
|
||||
OEMCrypto_SESSION session) {
|
||||
if (!crypto_engine) {
|
||||
LOGE("OEMCrypto_InitializeDecryptHash: OEMCrypto Not Initialized.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_InitializeDecryptHash(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
return session_ctx->InitializeDecryptHash();
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session,
|
||||
uint32_t frame_number,
|
||||
const uint8_t* hash,
|
||||
size_t hash_length) {
|
||||
if (!crypto_engine) {
|
||||
LOGE("OEMCrypto_SetDecryptHash: OEMCrypto Not Initialized.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_SetDecryptHash(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
return session_ctx->SetDecryptHash(frame_number, hash, hash_length);
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHashErrorCode(
|
||||
OEMCrypto_SESSION session, uint32_t* failed_frame_number) {
|
||||
if (!crypto_engine) {
|
||||
LOGE("OEMCrypto_GetHashErrorCode: OEMCrypto Not Initialized.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_GetHashErrorCode(): ERROR_INVALID_SESSION]");
|
||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||
}
|
||||
return session_ctx->GetHashErrorCode(failed_frame_number);
|
||||
}
|
||||
|
||||
} // namespace wvoec_ref
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "oemcrypto_types.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wvcrc32.h"
|
||||
|
||||
static const int kPssSaltLength = 20;
|
||||
|
||||
@@ -1167,6 +1168,7 @@ OEMCryptoResult SessionContext::SelectContentKey(
|
||||
LOGE("No key matches key id");
|
||||
return OEMCrypto_ERROR_NO_CONTENT_KEY;
|
||||
}
|
||||
compute_hash_ = false;
|
||||
content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR);
|
||||
current_content_key_ = content_key;
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
@@ -1284,6 +1286,29 @@ OEMCryptoResult SessionContext::DecryptCENC(
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data,
|
||||
size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data,
|
||||
OEMCryptoBufferType buffer_type) {
|
||||
OEMCryptoResult result =
|
||||
ChooseDecrypt(iv, block_offset, pattern, cipher_data, cipher_data_length,
|
||||
is_encrypted, clear_data, buffer_type);
|
||||
if (compute_hash_) {
|
||||
if (current_content_key() == NULL ||
|
||||
(current_content_key()->control().control_bits() &
|
||||
wvoec::kControlAllowHashVerification) == 0) {
|
||||
// This should not happen: this check should already have occured in
|
||||
// InitializeDecryptHash or the hash should have been discarded in
|
||||
// SelectContentKey. But it doesn't hurt to double check.
|
||||
LOGE("[DecryptCENC(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
current_hash_ = wvcrc32Cont(clear_data, cipher_data_length, current_hash_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::ChooseDecrypt(
|
||||
const uint8_t* iv, size_t block_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data,
|
||||
size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data,
|
||||
OEMCryptoBufferType buffer_type) {
|
||||
// If the data is clear, we do not need a current key selected.
|
||||
if (!is_encrypted) {
|
||||
if (buffer_type != OEMCrypto_BufferType_Direct) {
|
||||
@@ -1501,4 +1526,62 @@ OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8,
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::InitializeDecryptHash() {
|
||||
// Check there is a content key, and it is allowed.
|
||||
if (current_content_key() == NULL ||
|
||||
(current_content_key()->control().control_bits() &
|
||||
wvoec::kControlAllowHashVerification) == 0) {
|
||||
LOGE("[InitializeDecryptHash(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
||||
compute_hash_ = false;
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
compute_hash_ = true;
|
||||
current_hash_ = wvcrc32Init();
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::SetDecryptHash(uint32_t frame_number,
|
||||
const uint8_t* hash,
|
||||
size_t hash_length) {
|
||||
// Check there is a content key, and it is allowed.
|
||||
if (current_content_key() == NULL ||
|
||||
(current_content_key()->control().control_bits() &
|
||||
wvoec::kControlAllowHashVerification) == 0) {
|
||||
LOGE("[SetDecryptHash(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!compute_hash_) {
|
||||
// This would happen if somebody computes the hash, and then changes keys.
|
||||
LOGE("[SetDecryptHash(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
compute_hash_ = false;
|
||||
if (hash_length < sizeof(uint32_t)) {
|
||||
LOGE("[SetDecryptHash(): short buffer]");
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (hash_length > sizeof(uint32_t)) {
|
||||
LOGE("[SetDecryptHash(): long buffer]");
|
||||
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
|
||||
}
|
||||
uint32_t given_hash = *reinterpret_cast<const uint32_t*>(hash);
|
||||
if (current_hash_ != given_hash) {
|
||||
LOGE("CRC for frame %d is %08x, should be %08x\n", frame_number,
|
||||
current_hash_, given_hash);
|
||||
// Update bad_frame_number_ only if this is the first bad frame.
|
||||
if (hash_error_ == OEMCrypto_SUCCESS) bad_frame_number_ = frame_number;
|
||||
hash_error_ = OEMCrypto_ERROR_BAD_HASH;
|
||||
}
|
||||
// Return success if the hash was compared, even if there was an error.
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::GetHashErrorCode(
|
||||
uint32_t* failed_frame_number) {
|
||||
if (failed_frame_number == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (hash_error_ != OEMCrypto_SUCCESS)
|
||||
*failed_frame_number = bad_frame_number_;
|
||||
return hash_error_;
|
||||
}
|
||||
|
||||
} // namespace wvoec_ref
|
||||
|
||||
@@ -73,7 +73,11 @@ class SessionContext {
|
||||
allowed_schemes_(kSign_RSASSA_PSS),
|
||||
usage_entry_(NULL),
|
||||
srm_requirements_status_(NoSRMVersion),
|
||||
usage_entry_status_(kNoUsageEntry) {}
|
||||
usage_entry_status_(kNoUsageEntry),
|
||||
compute_hash_(false),
|
||||
current_hash_(0),
|
||||
bad_frame_number_(0),
|
||||
hash_error_(OEMCrypto_SUCCESS) {}
|
||||
virtual ~SessionContext();
|
||||
|
||||
bool isValid() { return valid_; }
|
||||
@@ -148,6 +152,11 @@ class SessionContext {
|
||||
virtual bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data);
|
||||
virtual OEMCryptoResult SelectContentKey(const KeyId& key_id,
|
||||
OEMCryptoCipherMode cipher_mode);
|
||||
virtual OEMCryptoResult InitializeDecryptHash();
|
||||
virtual OEMCryptoResult SetDecryptHash(uint32_t frame_number,
|
||||
const uint8_t* hash,
|
||||
size_t hash_length);
|
||||
virtual OEMCryptoResult GetHashErrorCode(uint32_t* failed_frame_number);
|
||||
const Key* current_content_key(void) { return current_content_key_; }
|
||||
void set_mac_key_server(const std::vector<uint8_t>& mac_key_server) {
|
||||
mac_key_server_ = mac_key_server;
|
||||
@@ -200,6 +209,12 @@ class SessionContext {
|
||||
OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control);
|
||||
// Check that the usage entry status is valid for offline use.
|
||||
OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control);
|
||||
OEMCryptoResult ChooseDecrypt(const uint8_t* iv, size_t block_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
const uint8_t* cipher_data,
|
||||
size_t cipher_data_length, bool is_encrypted,
|
||||
uint8_t* clear_data,
|
||||
OEMCryptoBufferType buffer_type);
|
||||
OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern,
|
||||
const uint8_t* cipher_data,
|
||||
@@ -239,6 +254,13 @@ class SessionContext {
|
||||
kUsageEntryLoaded, // After loading entry or loading keys.
|
||||
};
|
||||
UsageEntryStatus usage_entry_status_;
|
||||
|
||||
// These are used when doing full decrypt path testing.
|
||||
bool compute_hash_; // True if the current frame needs a hash.
|
||||
uint32_t current_hash_; // Running CRC hash of frame.
|
||||
uint32_t bad_frame_number_; // Frame number with bad hash.
|
||||
OEMCryptoResult hash_error_; // Error code for first bad frame.
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,14 @@ uint32_t wvcrc32(const uint8_t* p_begin, int i_count) {
|
||||
return(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
|
||||
}
|
||||
|
||||
uint32_t wvcrc32Init() {
|
||||
return INIT_CRC32;
|
||||
}
|
||||
|
||||
uint32_t wvcrc32Cont(const uint8_t* p_begin, int i_count, uint32_t prev_crc) {
|
||||
return(wvrunningcrc32(p_begin, i_count, prev_crc));
|
||||
}
|
||||
|
||||
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count) {
|
||||
return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t wvcrc32(const uint8_t* p_begin, int i_count);
|
||||
uint32_t wvcrc32Init();
|
||||
uint32_t wvcrc32Cont(const uint8_t* p_begin, int i_count, uint32_t prev_crc);
|
||||
|
||||
// Convert to network byte order
|
||||
uint32_t wvcrc32n(const uint8_t* p_begin, int i_count);
|
||||
|
||||
Reference in New Issue
Block a user