Unit Test for OEMCrypto_ERROR_KEY_EXPIRED

This is a copy from the Widevine CDM repository:
https://widevine-internal-review.googlesource.com/#/c/9177/4

This CL modifies some unit tests to make sure that OEMCrypto returns
the correct error code when the key has expired.  This behaviour is
required for OEMCrypto version 9.

It also updates the code for the reference implementation and the
Level 3 implementation.

This is half of b/9205119
The other half is for the CDM layer to respond to this error code.

bug: 9205119
Change-Id: I60f934886f4ecdd1ee04825dea289fda1c0a4303
This commit is contained in:
Fred Gylys-Colwell
2014-03-24 12:57:30 -07:00
parent 0a2c9889b0
commit 7a4ae90b5b
4 changed files with 110 additions and 133 deletions

View File

@@ -570,194 +570,198 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key,
}
}
bool SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
// Check there is a content key
if (current_content_key() == NULL) {
LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
const std::vector<uint8_t>& key = current_content_key()->value();
const KeyControlBlock& control = current_content_key()->control();
// Set the AES key.
if (static_cast<int>(key.size()) != AES_BLOCK_SIZE) {
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d",key.size());
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!(control.control_bits() & kControlAllowEncrypt)) {
LOGE("[Generic_Encrypt(): control bit says not allowed.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.duration() > 0) {
if (control.duration() < CurrentTimer()) {
LOGE("[Generic_Encrypt(): key expired.");
return false;
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
LOGE("[Generic_Encrypt(): algorithm bad.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
LOGE("[Generic_Encrypt(): buffers size bad.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const uint8_t* key_u8 = &key[0];
AES_KEY aes_key;
if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
LOGE("[Generic_Encrypt(): FAILURE]");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE);
AES_cbc_encrypt(in_buffer, out_buffer, buffer_length,
&aes_key, iv_buffer, AES_ENCRYPT);
return true;
return OEMCrypto_SUCCESS;
}
bool SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
// Check there is a content key
if (current_content_key() == NULL) {
LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
const std::vector<uint8_t>& key = current_content_key()->value();
const KeyControlBlock& control = current_content_key()->control();
// Set the AES key.
if (static_cast<int>(key.size()) != AES_BLOCK_SIZE) {
LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!(control.control_bits() & kControlAllowDecrypt)) {
LOGE("[Generic_Decrypt(): control bit says not allowed.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.control_bits() & kControlDataPathSecure) {
LOGE("[Generic_Decrypt(): control bit says secure path only.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.duration() > 0) {
if (control.duration() < CurrentTimer()) {
LOGE("[Generic_Decrypt(): key expired.");
return false;
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
LOGE("[Generic_Decrypt(): bad algorithm.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
LOGE("[Generic_Decrypt(): bad buffer size.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const uint8_t* key_u8 = &key[0];
AES_KEY aes_key;
if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
LOGE("[Generic_Decrypt(): FAILURE]");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE);
AES_cbc_encrypt(in_buffer, out_buffer, buffer_length,
&aes_key, iv_buffer, AES_DECRYPT);
return true;
return OEMCrypto_SUCCESS;
}
bool SessionContext::Generic_Sign(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length) {
OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length) {
// Check there is a content key
if (current_content_key() == NULL) {
LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
LOGE("[Generic_Sign(): bad signature length.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const std::vector<uint8_t>& key = current_content_key()->value();
const KeyControlBlock& control = current_content_key()->control();
if (static_cast<int>(key.size()) != SHA256_DIGEST_LENGTH) {
LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size());
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!(control.control_bits() & kControlAllowSign)) {
LOGE("[Generic_Sign(): control bit says not allowed.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.duration() > 0) {
if (control.duration() < CurrentTimer()) {
LOGE("[Generic_Sign(): key expired.");
return false;
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
LOGE("[Generic_Sign(): bad algorithm.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
unsigned int md_len = *signature_length;
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
in_buffer, buffer_length, signature, &md_len)) {
*signature_length = md_len;
return true;
return OEMCrypto_SUCCESS;
}
LOGE("[Generic_Sign(): hmac failed.");
dump_openssl_error();
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
bool SessionContext::Generic_Verify(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length) {
OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length) {
// Check there is a content key
if (current_content_key() == NULL) {
LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signature_length < SHA256_DIGEST_LENGTH) {
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const std::vector<uint8_t>& key = current_content_key()->value();
const KeyControlBlock& control = current_content_key()->control();
if (static_cast<int>(key.size()) != SHA256_DIGEST_LENGTH) {
LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size());
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!(control.control_bits() & kControlAllowVerify)) {
LOGE("[Generic_Verify(): control bit says not allowed.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.duration() > 0) {
if (control.duration() < CurrentTimer()) {
LOGE("[Generic_Verify(): key expired.");
return false;
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
LOGE("[Generic_Verify(): bad algorithm.");
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
unsigned int md_len = signature_length;
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
in_buffer, buffer_length, computed_signature, &md_len)) {
return (0 == memcmp( signature, computed_signature, SHA256_DIGEST_LENGTH));
if (0 == memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) {
return OEMCrypto_SUCCESS;
} else {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
}
LOGE("[Generic_Verify(): HMAC failed.");
dump_openssl_error();
return false;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
bool SessionContext::RefreshKey(const KeyId& key_id,
@@ -928,41 +932,41 @@ bool CryptoEngine::DecryptMessage(SessionContext* session,
return true;
}
bool CryptoEngine::DecryptCTR(SessionContext* session,
const uint8_t* iv,
size_t block_offset,
const uint8_t* cipher_data,
size_t cipher_data_length,
bool is_encrypted,
uint8_t* clear_data,
BufferType buffer_type) {
OEMCryptoResult CryptoEngine::DecryptCTR(SessionContext* session,
const uint8_t* iv, size_t block_offset,
const uint8_t* cipher_data,
size_t cipher_data_length,
bool is_encrypted, uint8_t* clear_data,
BufferType buffer_type) {
// If the data is clear, we do not need a current key selected.
if (!is_encrypted && buffer_type != kBufferTypeDirect) {
memcpy(reinterpret_cast<uint8_t*>(clear_data),
cipher_data, cipher_data_length);
return true;
return OEMCrypto_SUCCESS;
}
// Check there is a content key
if (session->current_content_key() == NULL) {
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
const KeyControlBlock& control = session->current_content_key()->control();
if (control.control_bits() & kControlDataPathSecure) {
if (buffer_type == kBufferTypeClear) {
LOGE("[DecryptCTR(): Secure key with insecure buffer]");
return false;
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
}
if (control.control_bits() & kControlHDCPRequired) {
// For reference implementation, we do not implement any HDCP.
return false;
LOGE("[DecryptCTR(): DECRYPT FAILED: HDCP Required]");
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
if (control.duration() > 0) {
if (control.duration() < session->CurrentTimer()) {
return false;
LOGE("[DecryptCTR(): KEY_EXPIRED]");
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
@@ -971,23 +975,23 @@ bool CryptoEngine::DecryptCTR(SessionContext* session,
// Set the AES key.
if (static_cast<int>(content_key.size()) != AES_BLOCK_SIZE) {
LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %d", content_key.size());
return false;
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
const uint8_t* key_u8 = &content_key[0];
AES_KEY aes_key;
if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
LOGE("[DecryptCTR(): FAILURE]");
return false;
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
if (buffer_type == kBufferTypeDirect) {
// For reference implementation, we quietly drop direct video.
return true;
return OEMCrypto_SUCCESS;
}
if (buffer_type == kBufferTypeSecure) {
// For reference implementation, we also quietly drop secure data.
return true;
return OEMCrypto_SUCCESS;
}
// Local copy (will be modified).
@@ -1011,7 +1015,7 @@ bool CryptoEngine::DecryptCTR(SessionContext* session,
ctr128_inc64(aes_iv);
block_offset = 0;
}
return true;
return OEMCrypto_SUCCESS;
}
void NonceTable::AddNonce(uint32_t nonce) {

View File

@@ -116,26 +116,21 @@ class SessionContext {
size_t message_length,
const uint8_t* signature,
size_t signature_length);
bool Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
bool Generic_Decrypt(const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
bool Generic_Sign(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length);
bool Generic_Verify(const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Decrypt(const uint8_t* in_buffer,
size_t buffer_length, const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
OEMCryptoResult Generic_Sign(const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature, size_t* signature_length);
OEMCryptoResult Generic_Verify(const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
void StartTimer();
uint32_t CurrentTimer(); // (seconds).
bool InstallKey(const KeyId& key_id,
@@ -248,14 +243,10 @@ class CryptoEngine {
const std::vector<uint8_t>& message,
std::vector<uint8_t>* decrypted);
bool DecryptCTR(SessionContext* session,
const uint8_t* iv,
size_t block_offset,
const uint8_t* cipher_data,
size_t cipher_data_length,
bool is_encrypted,
uint8_t* clear_data,
BufferType buffer_type);
OEMCryptoResult DecryptCTR(SessionContext* session, const uint8_t* iv,
size_t block_offset, const uint8_t* cipher_data,
size_t cipher_data_length, bool is_encrypted,
uint8_t* clear_data, BufferType buffer_type);
private:

View File

@@ -582,14 +582,9 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!crypto_engine->DecryptCTR(session_ctx, iv, block_offset,
data_addr, data_length, is_encrypted,
destination, buffer_type)) {
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_DECRYPT_FAILED]");
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
return OEMCrypto_SUCCESS;
return crypto_engine->DecryptCTR(session_ctx, iv, block_offset, data_addr,
data_length, is_encrypted, destination,
buffer_type);
}
extern "C"
@@ -1053,12 +1048,8 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm,
out_buffer)) {
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
return session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm,
out_buffer);
}
extern "C"
@@ -1077,16 +1068,13 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
out_buffer)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (in_buffer == NULL || buffer_length == 0 ||
iv == NULL || out_buffer == NULL) {
LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;
return session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
out_buffer);
}
extern "C"
@@ -1113,11 +1101,8 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm,
signature, signature_length)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
return session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm,
signature, signature_length);
}
extern "C"
@@ -1143,11 +1128,8 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm,
signature, signature_length)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
return session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm,
signature, signature_length);
}
extern "C"

View File

@@ -3014,7 +3014,7 @@ TEST_F(DISABLED_TestKeybox, KeyDuration) {
encryptedData.size(), true, &encryptionIv[0], 0,
&destBuffer,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts);
ASSERT_NE(0, memcmp(&unencryptedData[0], outputBuffer,
unencryptedData.size()));
@@ -4975,7 +4975,7 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationEncrypt) {
memset(encrypted, 0, kBufferSize);
sts = OEMCrypto_Generic_Encrypt(s.session_id(), clear_buffer_, kBufferSize, iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, encrypted);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts);
ASSERT_NE(0, memcmp(encrypted, expected_encrypted, kBufferSize));
s.close();
testTearDown();
@@ -5016,7 +5016,7 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationDecrypt) {
memset(resultant, 0, kBufferSize);
sts = OEMCrypto_Generic_Decrypt(s.session_id(), encrypted, kBufferSize, iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, resultant);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts);
ASSERT_NE(0, memcmp(clear_buffer_, resultant, kBufferSize));
s.close();
testTearDown();
@@ -5063,7 +5063,7 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationSign) {
sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_, kBufferSize,
OEMCrypto_HMAC_SHA256,signature,
&signature_length);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts);
ASSERT_NE(0, memcmp(signature, expected_signature, SHA256_DIGEST_LENGTH));
s.close();
@@ -5104,7 +5104,7 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationVerify) {
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_, kBufferSize,
OEMCrypto_HMAC_SHA256,signature,
SHA256_DIGEST_LENGTH);
ASSERT_NE(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts);
s.close();
testTearDown();