Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c312e21b authored by Edwin Wong's avatar Edwin Wong Committed by Keun Soo Yim
Browse files

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
(cherry picked from commit cc77a50e)
parent 1d93dbfb
Loading
Loading
Loading
Loading
+105 −12
Original line number Original line Diff line number Diff line
@@ -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,
    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 {
class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase {
   public:
   public:
    virtual void SetUp() override {
    virtual void SetUp() override {
@@ -932,6 +936,8 @@ class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
    void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
    void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
            const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
    void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
            vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
};
};


void DrmHalClearkeyDecryptTest::fillRandom(const sp<IMemory>& memory) {
void DrmHalClearkeyDecryptTest::fillRandom(const sp<IMemory>& memory) {
@@ -1089,16 +1095,14 @@ TEST_F(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) {
    EXPECT_OK(res);
    EXPECT_OK(res);
}
}



/**
/**
 * Positive decrypt test.  "Decrypt" a single clear segment
 * Positive decrypt test.  "Decrypt" a single clear segment
 */
 */
TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    const Pattern noPattern = {0, 0};
    const Pattern noPattern = {0, 0};
    const uint32_t kByteCount = 256;
    const vector<SubSample> subSamples = {
    const vector<SubSample> subSamples = {
        {.numBytesOfClearData = kByteCount,
        {.numBytesOfClearData = k256SubSampleByteCount,
         .numBytesOfEncryptedData = 0}};
         .numBytesOfEncryptedData = 0}};
    auto sessionId = openSession();
    auto sessionId = openSession();
    loadKeys(sessionId);
    loadKeys(sessionId);
@@ -1109,7 +1113,7 @@ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
    const bool kNotSecure = false;
    const bool kNotSecure = false;
    uint32_t byteCount = decrypt(Mode::UNENCRYPTED, &iv[0], subSamples,
    uint32_t byteCount = decrypt(Mode::UNENCRYPTED, &iv[0], subSamples,
            noPattern, Status::OK);
            noPattern, Status::OK);
    EXPECT_EQ(kByteCount, byteCount);
    EXPECT_EQ(k256SubSampleByteCount, byteCount);


    closeSession(sessionId);
    closeSession(sessionId);
}
}
@@ -1121,12 +1125,9 @@ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    const Pattern noPattern = {0, 0};
    const Pattern noPattern = {0, 0};
    const uint32_t kClearBytes = 512;
    const uint32_t kEncryptedBytes = 512;
    const vector<SubSample> subSamples = {
    const vector<SubSample> subSamples = {
        {.numBytesOfClearData = kClearBytes,
        {.numBytesOfClearData = k512SubSampleClearBytes,
         .numBytesOfEncryptedData = kEncryptedBytes
         .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}};
        }};
    auto sessionId = openSession();
    auto sessionId = openSession();
    loadKeys(sessionId);
    loadKeys(sessionId);


@@ -1136,10 +1137,11 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) {
    const bool kNotSecure = false;
    const bool kNotSecure = false;
    uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples,
    uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples,
            noPattern, Status::OK);
            noPattern, Status::OK);
    EXPECT_EQ(kClearBytes + kEncryptedBytes, byteCount);
    EXPECT_EQ(k512SubSampleClearBytes + k512SubSampleEncryptedBytes, byteCount);


    closeSession(sessionId);
    closeSession(sessionId);
}
}

/**
/**
 * Negative decrypt test. Decrypt without loading keys.
 * Negative decrypt test. Decrypt without loading keys.
 */
 */
@@ -1147,8 +1149,8 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
    const Pattern noPattern = {0, 0};
    const Pattern noPattern = {0, 0};
    const vector<SubSample> subSamples = {
    const vector<SubSample> subSamples = {
        {.numBytesOfClearData = 256,
        {.numBytesOfClearData = k256SubSampleByteCount,
         .numBytesOfEncryptedData = 256}};
         .numBytesOfEncryptedData = k256SubSampleByteCount}};
    auto sessionId = openSession();
    auto sessionId = openSession();


    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
    Status status = cryptoPlugin->setMediaDrmSession(sessionId);
@@ -1161,3 +1163,94 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {


    closeSession(sessionId);
    closeSession(sessionId);
}
}

/**
 * Helper method to test decryption with invalid keys is returned
 */
void DrmHalClearkeyDecryptTest::decryptWithInvalidKeys(
        hidl_vec<uint8_t>& invalidResponse,
        vector<uint8_t>& iv,
        const Pattern& noPattern,
        const vector<SubSample>& subSamples) {
    auto sessionId = openSession();

    auto res = drmPlugin->provideKeyResponse(
        sessionId, invalidResponse,
        [&](Status status, const hidl_vec<uint8_t>& 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<uint8_t> iv(AES_BLOCK_SIZE, 0);
    const Pattern noPattern = {0, 0};
    const vector<SubSample> 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<uint8_t> 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<uint8_t> iv(AES_BLOCK_SIZE, 0);
    const Pattern noPattern = {0, 0};
    const vector<SubSample> 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<uint8_t> invalidResponse;
    invalidResponse.resize(kKeyTooLongResponseSize);
    memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
    decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
}