Add vts tests to verify DRM AIDL interface
The DRM AIDL interface is in change 15329852.
The default implementation of the interface is in the clearkey
HAL in change 15958954.
[TODO] APIs pending vts coverage:
+ ICryptoFactory
+ isCryptoSchemeSupported
+ ICryptoPlugin
+ getLogMessages
+ notifyResolution
+ requiresSecureDecoderComponent
+ IDrmFactory
+ getSupportedCryptoSchemes
+ isContentTypeSupported
+ IDrmPlugin
+ decrypt
+ encrypt
+ getLogMessages
+ getMetrics
+ getNumberOfSessions
+ getPropertyByteArray
+ getPropertyString
+ getSecureStop
+ getSecureStopIds
+ getSecureStops
+ queryKeyStatus
+ releaseAllSecureStops
+ releaseSecureStop
+ releaseSecureStops
+ removeAllSecureStops
+ removeKeys
+ removeSecureStop
+ requiresSecureDecoder
+ requiresSecureDecoderDefault
+ restoreKeys
+ setCipherAlgorithm
+ setMacAlgorithm
+ setPlaybackId
+ setPropertyByteArray
+ sign
+ signRSA
+ verify
Bug: 170964303
Bug: 200055138
Test: atest VtsAidlHalDrmTargetTest
Change-Id: If8b582796fdbc34d3d7720fa45df8291f72cd46a
diff --git a/drm/aidl/vts/drm_hal_test.cpp b/drm/aidl/vts/drm_hal_test.cpp
new file mode 100644
index 0000000..3ac9f5c
--- /dev/null
+++ b/drm/aidl/vts/drm_hal_test.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm_hal_test"
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <openssl/aes.h>
+
+#include <memory>
+#include <vector>
+
+#include "drm_hal_common.h"
+
+using ::aidl::android::hardware::drm::EventType;
+using ::aidl::android::hardware::drm::HdcpLevels;
+using ::aidl::android::hardware::drm::KeyRequest;
+using ::aidl::android::hardware::drm::HdcpLevel;
+using ::aidl::android::hardware::drm::IDrmPluginListener;
+using ::aidl::android::hardware::drm::KeyRequestType;
+using ::aidl::android::hardware::drm::KeySetId;
+using ::aidl::android::hardware::drm::KeyStatus;
+using ::aidl::android::hardware::drm::KeyStatusType;
+using ::aidl::android::hardware::drm::KeyType;
+using ::aidl::android::hardware::drm::Mode;
+using ::aidl::android::hardware::drm::OfflineLicenseState;
+using ::aidl::android::hardware::drm::Pattern;
+using ::aidl::android::hardware::drm::SecurityLevel;
+using ::aidl::android::hardware::drm::Status;
+using ::aidl::android::hardware::drm::SubSample;
+using ::aidl::android::hardware::drm::Uuid;
+
+using ::aidl::android::hardware::drm::vts::DrmErr;
+using ::aidl::android::hardware::drm::vts::DrmHalClearkeyTest;
+using ::aidl::android::hardware::drm::vts::DrmHalPluginListener;
+using ::aidl::android::hardware::drm::vts::DrmHalTest;
+using ::aidl::android::hardware::drm::vts::ListenerArgs;
+using ::aidl::android::hardware::drm::vts::kCallbackKeysChange;
+using ::aidl::android::hardware::drm::vts::kCallbackLostState;
+
+using std::string;
+using std::vector;
+
+static const char* const kVideoMp4 = "video/mp4";
+static const char* const kBadMime = "video/unknown";
+static const char* const kDrmErrorTestKey = "drmErrorTest";
+static const char* const kDrmErrorInvalidState = "invalidState";
+static const char* const kDrmErrorResourceContention = "resourceContention";
+static constexpr SecurityLevel kSwSecureCrypto = SecurityLevel::SW_SECURE_CRYPTO;
+static constexpr SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
+
+/**
+ * Ensure drm factory supports module UUID Scheme
+ */
+TEST_P(DrmHalTest, VendorUuidSupported) {
+ bool result = false;
+ auto ret =
+ drmFactory->isCryptoSchemeSupported(getAidlUUID(), kVideoMp4, kSwSecureCrypto, &result);
+ ALOGI("kVideoMp4 = %s res %d", kVideoMp4, static_cast<bool>(result));
+ EXPECT_OK(ret);
+ EXPECT_TRUE(result);
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalTest, InvalidPluginNotSupported) {
+ const vector<uint8_t> kInvalidUUID = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+ bool result = false;
+ auto ret = drmFactory->isCryptoSchemeSupported(toAidlUuid(kInvalidUUID), kVideoMp4,
+ kSwSecureCrypto, &result);
+ EXPECT_OK(ret);
+ EXPECT_FALSE(result);
+}
+
+/**
+ * Ensure drm factory doesn't support an empty UUID
+ */
+TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
+ vector<uint8_t> emptyUUID(16);
+ memset(emptyUUID.data(), 0, 16);
+ bool result = false;
+ auto ret = drmFactory->isCryptoSchemeSupported(toAidlUuid(emptyUUID), kVideoMp4,
+ kSwSecureCrypto, &result);
+ EXPECT_OK(ret);
+ EXPECT_FALSE(result);
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid mime type
+ */
+TEST_P(DrmHalTest, BadMimeNotSupported) {
+ bool result = false;
+ auto ret =
+ drmFactory->isCryptoSchemeSupported(getAidlUUID(), kBadMime, kSwSecureCrypto, &result);
+ EXPECT_OK(ret);
+ EXPECT_FALSE(result);
+}
+
+/**
+ * DrmPlugin tests
+ */
+
+/**
+ * Test that a DRM plugin can handle provisioning. While
+ * it is not required that a DRM scheme require provisioning,
+ * it should at least return appropriate status values. If
+ * a provisioning request is returned, it is passed to the
+ * vendor module which should provide a provisioning response
+ * that is delivered back to the HAL.
+ */
+TEST_P(DrmHalTest, DoProvisioning) {
+ for (auto level : {kHwSecureAll, kSwSecureCrypto}) {
+ Status err = Status::OK;
+ auto sid = openSession(level, &err);
+ if (err == Status::OK) {
+ closeSession(sid);
+ } else if (err == Status::ERROR_DRM_CANNOT_HANDLE) {
+ continue;
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_NOT_PROVISIONED, err);
+ provision();
+ }
+ }
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ vector<uint8_t> initData;
+ KeyedVector optionalParameters;
+ KeyRequest result;
+ auto ret = drmPlugin->getKeyRequest(invalidSessionId, initData, kVideoMp4, KeyType::STREAMING,
+ optionalParameters, &result);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_P(DrmHalTest, GetKeyRequestBadMime) {
+ auto sessionId = openSession();
+ vector<uint8_t> initData;
+ KeyedVector optionalParameters;
+ KeyRequest result;
+ auto ret = drmPlugin->getKeyRequest(sessionId, initData, kBadMime, KeyType::STREAMING,
+ optionalParameters, &result);
+ EXPECT_EQ(EX_SERVICE_SPECIFIC, ret.getExceptionCode());
+ closeSession(sessionId);
+}
+
+/**
+ * Test drm plugin offline key support
+ */
+TEST_P(DrmHalTest, OfflineLicenseTest) {
+ auto sessionId = openSession();
+ vector<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+ closeSession(sessionId);
+
+ vector<KeySetId> result;
+ auto ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
+ EXPECT_OK(ret);
+ bool found = false;
+ for (KeySetId keySetId2 : result) {
+ if (keySetId == keySetId2.keySetId) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << "keySetId not found";
+
+ ret = drmPlugin->removeOfflineLicense({keySetId});
+ EXPECT_OK(ret);
+
+ ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
+ EXPECT_OK(ret);
+ for (KeySetId keySetId2 : result) {
+ EXPECT_NE(keySetId, keySetId2.keySetId);
+ }
+
+ ret = drmPlugin->removeOfflineLicense({keySetId});
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
+}
+
+/**
+ * Test drm plugin offline key state
+ */
+TEST_P(DrmHalTest, OfflineLicenseStateTest) {
+ auto sessionId = openSession();
+ DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
+ vector<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
+ closeSession(sessionId);
+
+ OfflineLicenseState result{};
+ auto ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
+ EXPECT_OK(ret);
+ EXPECT_EQ(OfflineLicenseState::USABLE, result);
+
+ vector<uint8_t> keyRequest = getKeyRequest(keySetId, content, KeyType::RELEASE);
+ ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
+ EXPECT_OK(ret);
+ EXPECT_EQ(OfflineLicenseState::INACTIVE, result);
+
+ /**
+ * Get key response from vendor module
+ */
+ vector<uint8_t> keyResponse = vendorModule->handleKeyRequest(keyRequest, content.serverUrl);
+ EXPECT_GT(keyResponse.size(), 0u);
+
+ result = OfflineLicenseState::UNKNOWN;
+ provideKeyResponse(keySetId, keyResponse);
+ ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
+ EXPECT_EQ(OfflineLicenseState::UNKNOWN, result);
+}
+
+/**
+ * Negative offline license test. Remove empty keySetId
+ */
+TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
+ KeySetId emptyKeySetId;
+ auto ret = drmPlugin->removeOfflineLicense(emptyKeySetId);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
+}
+
+/**
+ * Negative offline license test. Get empty keySetId state
+ */
+TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
+ KeySetId emptyKeySetId;
+ OfflineLicenseState result;
+ auto ret = drmPlugin->getOfflineLicenseState(emptyKeySetId, &result);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
+ EXPECT_EQ(OfflineLicenseState::UNKNOWN, result);
+}
+
+/**
+ * Test that the plugin returns valid connected and max HDCP levels
+ */
+TEST_P(DrmHalTest, GetHdcpLevels) {
+ HdcpLevels result;
+ auto ret = drmPlugin->getHdcpLevels(&result);
+ EXPECT_OK(ret);
+ EXPECT_GE(result.connectedLevel, HdcpLevel::HDCP_NONE);
+ EXPECT_LE(result.maxLevel, HdcpLevel::HDCP_V2_3);
+}
+
+/**
+ * CryptoPlugin Decrypt tests
+ */
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear segment
+ */
+TEST_P(DrmHalTest, ClearSegmentTest) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_OK(ret);
+
+ uint32_t byteCount =
+ decrypt(Mode::UNENCRYPTED, key.isSecure, toStdArray(key.keyId), &iv[0],
+ subSamples, noPattern, key.clearContentKey, Status::OK);
+ EXPECT_EQ(kSegmentSize, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Positive decrypt test. Decrypt a single segment using aes_ctr.
+ * Verify data matches.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_OK(ret);
+
+ uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure, toStdArray(key.keyId), &iv[0],
+ subSamples, noPattern, key.clearContentKey, Status::OK);
+ EXPECT_EQ(kSegmentSize, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Negative decrypt test. Decrypted frame too large to fit in output buffer
+ */
+TEST_P(DrmHalTest, ErrorFrameTooLarge) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_OK(ret);
+
+ decrypt(Mode::UNENCRYPTED, key.isSecure, toStdArray(key.keyId), &iv[0], subSamples,
+ noPattern, key.clearContentKey, Status::ERROR_DRM_FRAME_TOO_LARGE);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Negative decrypt test. Decrypt without loading keys.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = 256, .numBytesOfEncryptedData = 256}};
+ auto sessionId = openSession();
+
+ auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_OK(ret);
+
+ uint32_t byteCount =
+ decrypt(Mode::AES_CTR, key.isSecure, toStdArray(key.keyId), &iv[0], subSamples,
+ noPattern, key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
+ EXPECT_EQ(0u, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Ensure clearkey drm factory doesn't support security level higher than supported
+ */
+TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
+ bool result = false;
+ auto ret = drmFactory->isCryptoSchemeSupported(getAidlUUID(), kVideoMp4, kHwSecureAll, &result);
+ EXPECT_OK(ret);
+ EXPECT_FALSE(result);
+}
+
+/**
+ * Test resource contention during attempt to generate key request
+ */
+TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
+ auto ret = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
+ EXPECT_OK(ret);
+
+ auto sessionId = openSession();
+ vector<uint8_t> initData;
+ KeyedVector optionalParameters;
+ KeyRequest result;
+ ret = drmPlugin->getKeyRequest(sessionId, initData, kVideoMp4, KeyType::STREAMING,
+ optionalParameters, &result);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(Status::ERROR_DRM_RESOURCE_CONTENTION, DrmErr(ret));
+
+ ret = drmPlugin->closeSession(sessionId);
+ EXPECT_TXN(ret);
+ EXPECT_NE(Status::OK, DrmErr(ret));
+}
+
+/**
+ * Test clearkey plugin offline key with mock error
+ */
+TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
+ auto sessionId = openSession();
+ vector<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+ auto ret = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
+ EXPECT_OK(ret);
+
+ // everything should start failing
+ const Status kInvalidState = Status::ERROR_DRM_INVALID_STATE;
+ vector<KeySetId> result;
+ ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(kInvalidState, DrmErr(ret));
+ EXPECT_EQ(0u, result.size());
+
+ OfflineLicenseState state = OfflineLicenseState::UNKNOWN;
+ ret = drmPlugin->getOfflineLicenseState({keySetId}, &state);
+ EXPECT_TXN(ret);
+ EXPECT_EQ(kInvalidState, DrmErr(ret));
+ EXPECT_EQ(OfflineLicenseState::UNKNOWN, state);
+
+ ret = drmPlugin->removeOfflineLicense({keySetId});
+ EXPECT_TXN(ret);
+ EXPECT_EQ(kInvalidState, DrmErr(ret));
+ closeSession(sessionId);
+}
+
+/**
+ * Test listener is triggered on key response
+ */
+TEST_P(DrmHalClearkeyTest, ListenerCallbacks) {
+ auto listener = ndk::SharedRefBase::make<DrmHalPluginListener>();
+ auto res = drmPlugin->setListener(listener);
+ EXPECT_OK(res);
+
+ auto sessionId = openSession();
+ loadKeys(sessionId, KeyType::STREAMING);
+ closeSession(sessionId);
+
+ auto args = listener->getEventArgs();
+ EXPECT_EQ(EventType::VENDOR_DEFINED, args.eventType);
+ EXPECT_EQ(sessionId, args.data);
+ EXPECT_EQ(sessionId, args.sessionId);
+
+ args = listener->getExpirationUpdateArgs();
+ EXPECT_EQ(sessionId, args.sessionId);
+ EXPECT_EQ(100, args.expiryTimeInMS);
+
+ args = listener->getKeysChangeArgs();
+ const vector<KeyStatus> keyStatusList = {
+ {{0xa, 0xb, 0xc}, KeyStatusType::USABLE},
+ {{0xd, 0xe, 0xf}, KeyStatusType::EXPIRED},
+ {{0x0, 0x1, 0x2}, KeyStatusType::USABLEINFUTURE},
+ };
+ EXPECT_EQ(sessionId, args.sessionId);
+ EXPECT_EQ(keyStatusList, args.keyStatusList);
+ EXPECT_TRUE(args.hasNewUsableKey);
+}
+
+/**
+ * Test SessionLostState is triggered on error
+ */
+TEST_P(DrmHalClearkeyTest, SessionLostState) {
+ auto listener = ndk::SharedRefBase::make<DrmHalPluginListener>();
+ auto res = drmPlugin->setListener(listener);
+ EXPECT_OK(res);
+
+ res = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
+ EXPECT_OK(res);
+
+ auto sessionId = openSession();
+ auto ret = drmPlugin->closeSession(sessionId);
+
+ auto args = listener->getSessionLostStateArgs();
+ EXPECT_EQ(sessionId, args.sessionId);
+}
+
+/**
+ * Negative decrypt test. Decrypt with invalid key.
+ */
+TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const uint32_t kClearBytes = 512;
+ const uint32_t kEncryptedBytes = 512;
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kClearBytes, .numBytesOfEncryptedData = kEncryptedBytes}};
+
+ // base 64 encoded JSON response string, must not contain padding character '='
+ const 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();
+
+ vector<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_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const uint32_t kClearBytes = 512;
+ const uint32_t kEncryptedBytes = 512;
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kClearBytes, .numBytesOfEncryptedData = kEncryptedBytes}};
+
+ // base 64 encoded JSON response string, must not contain padding character '='
+ const string keyTooLongResponse =
+ "{\"keys\":["
+ "{"
+ "\"kty\":\"oct\","
+ "\"alg\":\"A128KW2\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," // key too long
+ "\"k\":\"V2lubmllIHRoZSBwb29oIVdpbm5pZSB0aGUgcG9vaCE=\""
+ "}]"
+ "}";
+ const size_t kKeyTooLongResponseSize = keyTooLongResponse.size();
+
+ vector<uint8_t> invalidResponse;
+ invalidResponse.resize(kKeyTooLongResponseSize);
+ memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
+ decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
+}