diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp index 2c6e6b52..82b88de8 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp @@ -182,6 +182,14 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() { return current_time; } +bool CryptoEngine::NonceCollision(uint32_t nonce) { + for (const auto & session_pair : sessions_) { + const SessionContext* session = session_pair.second; + if (session->NonceCollision(nonce)) return true; + } + return false; +} + OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() { return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1; } diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h index 6ad5cd2f..cc132aa3 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -91,6 +91,10 @@ class CryptoEngine { time_t RollbackCorrectedOfflineTime(); + // Verify that this nonce does not collide with another nonce in any session's + // nonce table. + virtual bool NonceCollision(uint32_t nonce); + // Returns the HDCP version currently in use. virtual OEMCrypto_HDCP_Capability config_current_hdcp_capability(); diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.cpp index 19c7613b..65d92692 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.cpp @@ -58,6 +58,13 @@ bool NonceTable::CheckNonce(uint32_t nonce) { return false; } +bool NonceTable::NonceCollision(uint32_t nonce) const { + for (int i = 0; i < kTableSize; ++i) { + if (nonce == nonces_[i]) return true; + } + return false; +} + void NonceTable::Flush() { for (int i = 0; i < kTableSize; ++i) { if (kNTStateFlushPending == state_[i]) { diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.h b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.h index 90175e2d..409e7fcc 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.h +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_nonce_table.h @@ -13,7 +13,7 @@ namespace wvoec_ref { class NonceTable { public: - static const int kTableSize = 16; + static const int kTableSize = 4; NonceTable() { for (int i = 0; i < kTableSize; ++i) { state_[i] = kNTStateInvalid; @@ -22,6 +22,8 @@ class NonceTable { ~NonceTable() {} void AddNonce(uint32_t nonce); bool CheckNonce(uint32_t nonce); + // Verify that the nonce is not the same as any in this table. + bool NonceCollision(uint32_t nonce) const; void Flush(); private: diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp index 67d3e12c..355fbf74 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -189,13 +189,15 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, last_nonce_time = now; } - uint32_t nonce_value; + uint32_t nonce_value = 0; uint8_t* nonce_string = reinterpret_cast(&nonce_value); - // Generate 4 bytes of random data - if (!RAND_bytes(nonce_string, 4)) { - LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + while (nonce_value == 0 || crypto_engine->NonceCollision(nonce_value)) { + // Generate 4 bytes of random data + if (!RAND_bytes(nonce_string, 4)) { + LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } } session_ctx->AddNonce(nonce_value); *nonce = nonce_value; @@ -281,9 +283,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys( !RangeCheck(message_length, enc_mac_keys, true) || !RangeCheck(message_length, pst, true) || !RangeCheck(message_length, srm_restriction_data, true)) { - LOGE( - "[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range " - "check.]"); + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - " + "range check.]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } @@ -293,13 +294,25 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys( !RangeCheck(message_length, key_array[i].key_data_iv, false) || !RangeCheck(message_length, key_array[i].key_control, false) || !RangeCheck(message_length, key_array[i].key_control_iv, false)) { - LOGE( - "[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT -range " - "check %d]", - i); + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - " + "range check %d]", i); return OEMCrypto_ERROR_INVALID_CONTEXT; } } + if (enc_mac_keys.offset >= wvoec::KEY_IV_SIZE && enc_mac_keys.length > 0) { + if (enc_mac_keys_iv.offset + wvoec::KEY_IV_SIZE == enc_mac_keys.offset) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - " + "range check iv]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } else { + if (memcmp(message + enc_mac_keys.offset - wvoec::KEY_IV_SIZE, + message + enc_mac_keys_iv.offset, wvoec::KEY_IV_SIZE) == 0) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - " + "suspicious iv]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + } return session_ctx->LoadKeys(message, message_length, signature, signature_length, enc_mac_keys_iv, enc_mac_keys, num_keys, key_array, pst, srm_restriction_data, diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.h b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.h index bfd81a62..09560566 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.h +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.h @@ -173,6 +173,10 @@ class SessionContext { void AddNonce(uint32_t nonce); bool CheckNonce(uint32_t nonce); + // Verify that the nonce does not match any in this session's nonce table. + bool NonceCollision(uint32_t nonce) const { + return nonce_table_.NonceCollision(nonce); + } void FlushNonces(); virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index a3134553..5684cbe0 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -539,6 +539,7 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce, const std::string& pst) { EXPECT_EQ( 1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); + memset(license_.padding, 0, sizeof(license_.padding)); EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys))); for (unsigned int i = 0; i < num_keys_; i++) { memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 4f1ab1b7..f1627601 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -81,6 +81,7 @@ typedef struct { struct MessageData { MessageKeyData keys[kMaxNumKeys]; uint8_t mac_key_iv[KEY_IV_SIZE]; + uint8_t padding[KEY_IV_SIZE]; uint8_t mac_keys[2 * MAC_KEY_SIZE]; uint8_t pst[kMaxPSTLength]; }; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index c43b2c5b..3df68bf1 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -1215,6 +1215,27 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The IV should not be identical to the data right before the encrypted mac +// keys. +TEST_F(OEMCryptoSessionTests, LoadKeyWithSuspiciousIV) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + // This is suspicious: the data right before the mac keys is identical to the + // iv. + memcpy(s.license().padding, s.license().mac_key_iv, + sizeof(s.license().padding)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), + s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), + s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), + OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + // Test that LoadKeys fails when a key is loaded with no key control block. TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { Session s;