From cc77a50e96c3b17f8918e4577df376e97e21a87c Mon Sep 17 00:00:00 2001 From: Edwin Wong Date: Fri, 6 Oct 2017 18:08:27 -0700 Subject: [PATCH] Add tests to validate key length for clearkey plugin. AesCtrDecryptor::decrypt() doesn't check whether the size of "key" is equal to 16 bytes, which may lead to an OOB read problem in the context of mediadrmserver. The fix is in clearkey plugin. Add tests to validate the fix. Test: VTS test adb shell /data/nativetest/VtsHalDrmV1_0TargetTest/VtsHalDrmV1_0TargetTest bug: 63982768 Merged-In: Ife2da17e7f39d8031bc36b83c3b27ba5e9d83eb7 Change-Id: Ife2da17e7f39d8031bc36b83c3b27ba5e9d83eb7 --- .../vts/functional/drm_hal_clearkey_test.cpp | 117 ++++++++++++++++-- 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp index 04f265866c..c27ae62121 100644 --- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp +++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp @@ -85,6 +85,10 @@ static const uint8_t kInvalidUUID[16] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; +static const uint32_t k256SubSampleByteCount = 256; +static const uint32_t k512SubSampleClearBytes = 512; +static const uint32_t k512SubSampleEncryptedBytes = 512; + class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase { public: virtual void SetUp() override { @@ -932,6 +936,8 @@ class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest { const hidl_vec& subSamples, const vector& key); void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv, const hidl_vec& subSamples, const vector& key); + void decryptWithInvalidKeys(hidl_vec& invalidResponse, + vector& iv, const Pattern& noPattern, const vector& subSamples); }; void DrmHalClearkeyDecryptTest::fillRandom(const sp& memory) { @@ -1089,16 +1095,14 @@ TEST_F(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) { EXPECT_OK(res); } - /** * Positive decrypt test. "Decrypt" a single clear segment */ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) { vector iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; - const uint32_t kByteCount = 256; const vector subSamples = { - {.numBytesOfClearData = kByteCount, + {.numBytesOfClearData = k256SubSampleByteCount, .numBytesOfEncryptedData = 0}}; auto sessionId = openSession(); loadKeys(sessionId); @@ -1109,7 +1113,7 @@ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) { const bool kNotSecure = false; uint32_t byteCount = decrypt(Mode::UNENCRYPTED, &iv[0], subSamples, noPattern, Status::OK); - EXPECT_EQ(kByteCount, byteCount); + EXPECT_EQ(k256SubSampleByteCount, byteCount); closeSession(sessionId); } @@ -1121,12 +1125,9 @@ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) { TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) { vector iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; - const uint32_t kClearBytes = 512; - const uint32_t kEncryptedBytes = 512; const vector subSamples = { - {.numBytesOfClearData = kClearBytes, - .numBytesOfEncryptedData = kEncryptedBytes - }}; + {.numBytesOfClearData = k512SubSampleClearBytes, + .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; auto sessionId = openSession(); loadKeys(sessionId); @@ -1136,10 +1137,11 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) { const bool kNotSecure = false; uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples, noPattern, Status::OK); - EXPECT_EQ(kClearBytes + kEncryptedBytes, byteCount); + EXPECT_EQ(k512SubSampleClearBytes + k512SubSampleEncryptedBytes, byteCount); closeSession(sessionId); } + /** * Negative decrypt test. Decrypt without loading keys. */ @@ -1147,8 +1149,8 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) { vector iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; const vector subSamples = { - {.numBytesOfClearData = 256, - .numBytesOfEncryptedData = 256}}; + {.numBytesOfClearData = k256SubSampleByteCount, + .numBytesOfEncryptedData = k256SubSampleByteCount}}; auto sessionId = openSession(); Status status = cryptoPlugin->setMediaDrmSession(sessionId); @@ -1161,3 +1163,94 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) { closeSession(sessionId); } + +/** + * Helper method to test decryption with invalid keys is returned + */ +void DrmHalClearkeyDecryptTest::decryptWithInvalidKeys( + hidl_vec& invalidResponse, + vector& iv, + const Pattern& noPattern, + const vector& subSamples) { + auto sessionId = openSession(); + + auto res = drmPlugin->provideKeyResponse( + sessionId, invalidResponse, + [&](Status status, const hidl_vec& myKeySetId) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, myKeySetId.size()); + }); + ASSERT_OK(res); + + ASSERT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); + + uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples, + noPattern, Status::ERROR_DRM_NO_LICENSE); + EXPECT_EQ(0u, byteCount); + + closeSession(sessionId); +} + +/** + * Negative decrypt test. Decrypt with invalid key. + */ +TEST_F(DrmHalClearkeyDecryptTest, DecryptWithEmptyKey) { + vector iv(AES_BLOCK_SIZE, 0); + const Pattern noPattern = {0, 0}; + const vector subSamples = { + {.numBytesOfClearData = k512SubSampleClearBytes, + .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; + + // base 64 encoded JSON response string, must not contain padding character '=' + const hidl_string emptyKeyResponse = + "{\"keys\":[" \ + "{" \ + "\"kty\":\"oct\"" \ + "\"alg\":\"A128KW2\"" \ + "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" \ + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAyAy\"" \ + "}" \ + "{" \ + "\"kty\":\"oct\"," \ + "\"alg\":\"A128KW2\"" \ + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \ + // empty key follows + "\"k\":\"R\"" \ + "}]" \ + "}"; + const size_t kEmptyKeyResponseSize = emptyKeyResponse.size(); + + hidl_vec invalidResponse; + invalidResponse.resize(kEmptyKeyResponseSize); + memcpy(invalidResponse.data(), emptyKeyResponse.c_str(), kEmptyKeyResponseSize); + decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples); +} + +/** + * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE. + */ +TEST_F(DrmHalClearkeyDecryptTest, DecryptWithKeyTooLong) { + vector iv(AES_BLOCK_SIZE, 0); + const Pattern noPattern = {0, 0}; + const vector subSamples = { + {.numBytesOfClearData = k512SubSampleClearBytes, + .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; + + // base 64 encoded JSON response string, must not contain padding character '=' + const hidl_string keyTooLongResponse = + "{\"keys\":[" \ + "{" \ + "\"kty\":\"oct\"," \ + "\"alg\":\"A128KW2\"" \ + "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \ + // key too long + "\"k\":\"V2lubmllIHRoZSBwb29oIVdpbm5pZSB0aGUgcG9vaCE=\"" \ + "}]" \ + "}"; + const size_t kKeyTooLongResponseSize = keyTooLongResponse.size(); + + hidl_vec invalidResponse; + invalidResponse.resize(kKeyTooLongResponseSize); + memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize); + decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples); +}