diff --git a/libclearkeydrmengine/Android.mk b/libclearkeydrmengine/Android.mk new file mode 100644 index 00000000..8bc652b4 --- /dev/null +++ b/libclearkeydrmengine/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH := $(call my-dir) + +# We depend on the static libraries from our subdirectories to build this +# shared library +include $(call all-subdir-makefiles) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/WVCreateDrmPluginFactory.cpp \ + src/WVDrmPluginFactory.cpp \ + +LOCAL_C_INCLUDES := \ + frameworks/native/include \ + frameworks/av/include \ + vendor/widevine/libclearkeydrmengine/include \ + vendor/widevine/libclearkeydrmengine/crypto/include \ + vendor/widevine/libclearkeydrmengine/oemcrypto/include \ + +LOCAL_STATIC_LIBRARIES := \ + libwvclearkeycryptoplugin \ + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libdl \ + +LOCAL_MODULE := libclearkeydrmengine + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/libclearkeydrmengine/crypto/Android.mk b/libclearkeydrmengine/crypto/Android.mk new file mode 100644 index 00000000..00b57f05 --- /dev/null +++ b/libclearkeydrmengine/crypto/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/WVCryptoPlugin.cpp \ + +LOCAL_C_INCLUDES := \ + frameworks/native/include \ + frameworks/av/include \ + vendor/widevine/libclearkeydrmengine/crypto/include \ + vendor/widevine/libclearkeydrmengine/oemcrypto/include \ + +LOCAL_MODULE := libwvclearkeycryptoplugin + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/libclearkeydrmengine/crypto/include/WVCryptoPlugin.h b/libclearkeydrmengine/crypto/include/WVCryptoPlugin.h new file mode 100644 index 00000000..da70f6de --- /dev/null +++ b/libclearkeydrmengine/crypto/include/WVCryptoPlugin.h @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#ifndef WV_CRYPTO_PLUGIN_H_ +#define WV_CRYPTO_PLUGIN_H_ + +#include "media/stagefright/foundation/ABase.h" +#include "media/hardware/CryptoAPI.h" +#include "OEMCryptoDASH.h" + +namespace wvclearkey { + +class WVCryptoPlugin : public android::CryptoPlugin { +public: + WVCryptoPlugin(); + virtual ~WVCryptoPlugin(); + + virtual bool requiresSecureDecoderComponent(const char *mime) const { + return false; + } + + virtual ssize_t decrypt( + bool secure, + const uint8_t key[16], + const uint8_t iv[16], + Mode mode, + const void *srcPtr, + const SubSample *subSamples, size_t numSubSamples, + void *dstPtr, + android::AString *errorDetailMsg); + +private: + DISALLOW_EVIL_CONSTRUCTORS(WVCryptoPlugin); + + OEMCrypto_SESSION mSession; +}; + +} // namespace wvclearkey + +#endif // WV_CRYPTO_PLUGIN_H_ diff --git a/libclearkeydrmengine/crypto/src/WVCryptoPlugin.cpp b/libclearkeydrmengine/crypto/src/WVCryptoPlugin.cpp new file mode 100644 index 00000000..b4b9fcea --- /dev/null +++ b/libclearkeydrmengine/crypto/src/WVCryptoPlugin.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "wv_clear_key" +#include + +#include "WVCryptoPlugin.h" + +namespace wvclearkey { + +WVCryptoPlugin::WVCryptoPlugin() { + OEMCryptoResult res = OEMCrypto_OpenSession(&mSession); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("OEMCrypto_OpenSession error result: %d", res); + } +} + +WVCryptoPlugin::~WVCryptoPlugin() { + OEMCryptoResult res = OEMCrypto_CloseSession(mSession); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("OEMCrypto_OpenSession error result: %d", res); + } +} + +// Returns negative values for error codes and positive values for the size of +// the decrypted data. +ssize_t WVCryptoPlugin::decrypt( + bool secure, + const uint8_t key[16], + const uint8_t iv[16], + Mode mode, + const void *srcPtr, + const SubSample *subSamples, size_t numSubSamples, + void *dstPtr, + android::AString *errorDetailMsg) { + if (mode != kMode_Unencrypted && mode != kMode_AES_CTR) { + return android::BAD_TYPE; + } + + const uint8_t *const source = static_cast(srcPtr); + uint8_t *const dest = static_cast(dstPtr); + + if (secure) { + // Can't do secure on this implementation + return -EPERM; + } + + // For this special clear-key version of OEMCrypto ONLY, SelectKey actually + // has the meaning "use this key, passed in the clear." This is only the + // case for libclearkeydrmengine and not any other OEMCrypto. + OEMCryptoResult res = OEMCrypto_SelectKey(mSession, key, 16); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("Key selection error in session %d: %d", mSession, res); + return -EINVAL; + } + + size_t offset = 0; + + for (size_t i = 0; i < numSubSamples; ++i) { + const SubSample &subSample = subSamples[i]; + + if (mode == kMode_Unencrypted && + subSample.mNumBytesOfEncryptedData != 0) { + return -EINVAL; + } + + // "Decrypt" any unencrypted data. By convention, + // (see frameworks/av/include/media/stagefright/MetaData.h) + // clear data comes before encrypted data. + if (subSample.mNumBytesOfClearData != 0) { + OEMCrypto_DestBufferDesc output; + output.type = OEMCrypto_BufferType_Clear; + output.buffer.clear.address = dest + offset; + output.buffer.clear.max_length = subSample.mNumBytesOfClearData; + + res = OEMCrypto_DecryptCTR( + mSession, + source + offset, subSample.mNumBytesOfClearData, + false, + iv, + offset % 16, + &output); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("Decrypt error result in session %d: %d", mSession, res); + return -EINVAL; + } + + offset += subSample.mNumBytesOfClearData; + } + + // Decrypt any encrypted data. + if (subSample.mNumBytesOfEncryptedData != 0) { + OEMCrypto_DestBufferDesc output; + output.type = OEMCrypto_BufferType_Clear; + output.buffer.clear.address = dest + offset; + output.buffer.clear.max_length = subSample.mNumBytesOfEncryptedData; + + res = OEMCrypto_DecryptCTR( + mSession, + source + offset, subSample.mNumBytesOfEncryptedData, + true, + iv, + offset % 16, + &output); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("Decrypt error result in session %d: %d", mSession, res); + return -EINVAL; + } + + offset += subSample.mNumBytesOfEncryptedData; + } + } + + return static_cast(offset); +} + +} // namespace wvclearkey diff --git a/libclearkeydrmengine/crypto/test/Android.mk b/libclearkeydrmengine/crypto/test/Android.mk new file mode 100644 index 00000000..6056093e --- /dev/null +++ b/libclearkeydrmengine/crypto/test/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + WVCryptoPlugin_test.cpp \ + +LOCAL_C_INCLUDES := \ + external/gtest/include \ + external/stlport/stlport \ + bionic \ + frameworks/native/include \ + frameworks/av/include \ + vendor/widevine/libclearkeydrmengine/crypto/include \ + vendor/widevine/libclearkeydrmengine/oemcrypto/include \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + libwvclearkeycryptoplugin \ + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + liblog \ + libutils \ + +LOCAL_MODULE := libwvclearkeycryptoplugin_test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libclearkeydrmengine/crypto/test/WVCryptoPlugin_test.cpp b/libclearkeydrmengine/crypto/test/WVCryptoPlugin_test.cpp new file mode 100644 index 00000000..004b6a8d --- /dev/null +++ b/libclearkeydrmengine/crypto/test/WVCryptoPlugin_test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "media/stagefright/foundation/ABase.h" +#include "utils/UniquePtr.h" +#include "WVCryptoPlugin.h" +#include "OEMCryptoDASH.h" +#include "gtest/gtest.h" + +using namespace wvclearkey; + +using android::status_t; + +bool oemCryptoSessionOpened = false; +OEMCrypto_SESSION openedCryptoSession = 0; +bool oemCryptoSessionClosed = false; +OEMCrypto_SESSION closedCryptoSession = 0; + +extern "C" { + OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session) { + oemCryptoSessionOpened = true; + openedCryptoSession = 5; + *session = openedCryptoSession; + return OEMCrypto_SUCCESS; + } + + OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + oemCryptoSessionClosed = true; + closedCryptoSession = session; + return OEMCrypto_SUCCESS; + } + +} + +TEST(WVCryptoPluginTest, ManagesASession) { + oemCryptoSessionOpened = false; + openedCryptoSession = 0; + oemCryptoSessionClosed = false; + closedCryptoSession = 0; + + UniquePtr plugin = new WVCryptoPlugin(); + + EXPECT_TRUE(oemCryptoSessionOpened) << + "WVCryptoPlugin did not call OEMCrypto_OpenSession()"; + + plugin.clear(); + + EXPECT_TRUE(oemCryptoSessionClosed) << + "WVCryptoPlugin did not call OEMCrypto_CloseSession()"; + EXPECT_EQ(openedCryptoSession, closedCryptoSession) << + "WVCryptoPlugin closed the wrong session"; +} + +TEST(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) { + UniquePtr plugin = new WVCryptoPlugin(); + + EXPECT_FALSE(plugin->requiresSecureDecoderComponent("video/mp4")) << + "WVCryptoPlugin incorrectly expects a secure video decoder"; + EXPECT_FALSE(plugin->requiresSecureDecoderComponent("audio/aac")) << + "WVCryptoPlugin incorrectly expects a secure audio decoder"; +} + +typedef const uint8_t* constChars; + +bool selectKeyCalled; +constChars selectedKey; +size_t selectedKeyLength; + +uint32_t decryptCallCount; +constChars decryptInPointers[3]; +size_t decryptInLengths[3]; +bool decryptIsEncryptedValues[3]; +constChars decryptIvs[3]; +size_t decryptOffsets[3]; +OEMCrypto_DestBufferDesc decryptOutBuffers[3]; + +extern "C" { + OEMCryptoResult OEMCrypto_SelectKey( + const OEMCrypto_SESSION session, + const uint8_t* key_id, + const size_t key_id_length) { + selectKeyCalled = true; + selectedKey = key_id; + selectedKeyLength = key_id_length; + return OEMCrypto_SUCCESS; + } + + OEMCryptoResult OEMCrypto_DecryptCTR( + OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer) { + if (decryptCallCount < 3) { + decryptInPointers[decryptCallCount] = data_addr; + decryptInLengths[decryptCallCount] = data_length; + decryptIsEncryptedValues[decryptCallCount] = is_encrypted; + decryptIvs[decryptCallCount] = iv; + decryptOffsets[decryptCallCount] = offset; + decryptOutBuffers[decryptCallCount] = *out_buffer; + } + + decryptCallCount++; + return OEMCrypto_SUCCESS; + } +} + +TEST(WVCryptoPluginTest, AttemptsToDecrypt) { + selectKeyCalled = false; + selectedKey = NULL; + selectedKeyLength = 0; + decryptCallCount = 0; + memset(decryptInPointers, 0, sizeof(decryptInPointers)); + memset(decryptInLengths, 0, sizeof(decryptInLengths)); + memset(decryptIsEncryptedValues, 0, sizeof(decryptIsEncryptedValues)); + memset(decryptIvs, 0, sizeof(decryptIvs)); + memset(decryptOffsets, 0, sizeof(decryptOffsets)); + memset(decryptOutBuffers, 0, sizeof(decryptOutBuffers)); + + UniquePtr plugin = new WVCryptoPlugin(); + + android::CryptoPlugin::SubSample subSamples[3]; + subSamples[0].mNumBytesOfEncryptedData = 16; + subSamples[1].mNumBytesOfEncryptedData = 24; + subSamples[2].mNumBytesOfEncryptedData = 8; + + uint8_t key[16] = {}; + uint8_t iv[16] = {}; + + uint8_t in[48] = {}; + uint8_t out[48] = {}; + + ssize_t decrypted = plugin->decrypt( + false, + key, + iv, + android::CryptoPlugin::kMode_AES_CTR, + in, + subSamples, 3, + out, + NULL); + + EXPECT_EQ(48, decrypted) << + "WVCryptoPlugin decrypted the wrong number of bytes"; + ASSERT_EQ(3u, decryptCallCount) << + "WVCryptoPlugin called OEMCrypto_DecryptCTR the wrong number of times"; + + // Test the 1st call + EXPECT_EQ(in, decryptInPointers[0]) << + "1st OEMCrypto_DecryptCTR call targetted the wrong input location"; + EXPECT_EQ(16u, decryptInLengths[0]) << + "1st OEMCrypto_DecryptCTR call targetted the wrong input length"; + EXPECT_TRUE(decryptIsEncryptedValues[0]) << + "1st OEMCrypto_DecryptCTR call thought data was unencrypted"; + EXPECT_EQ(iv, decryptIvs[0]) << + "1st OEMCrypto_DecryptCTR call had the wrong iv"; + EXPECT_EQ(0u, decryptOffsets[0]) << + "1st OEMCrypto_DecryptCTR call had the wrong offset"; + EXPECT_EQ(out, decryptOutBuffers[0].buffer.clear.address) << + "1st OEMCrypto_DecryptCTR call targetted the wrong output location"; + + // Test the 2nd call + EXPECT_EQ(in + 16, decryptInPointers[0]) << + "2nd OEMCrypto_DecryptCTR call targetted the wrong input location"; + EXPECT_EQ(24u, decryptInLengths[0]) << + "2nd OEMCrypto_DecryptCTR call targetted the wrong input length"; + EXPECT_TRUE(decryptIsEncryptedValues[0]) << + "2nd OEMCrypto_DecryptCTR call thought data was unencrypted"; + EXPECT_EQ(iv, decryptIvs[0]) << + "2nd OEMCrypto_DecryptCTR call had the wrong iv"; + EXPECT_EQ(0u, decryptOffsets[0]) << + "2nd OEMCrypto_DecryptCTR call had the wrong offset"; + EXPECT_EQ(out + 16, decryptOutBuffers[0].buffer.clear.address) << + "2nd OEMCrypto_DecryptCTR call targetted the wrong output location"; + + // Test the 3rd call + EXPECT_EQ(in + 40, decryptInPointers[0]) << + "3rd OEMCrypto_DecryptCTR call targetted the wrong input location"; + EXPECT_EQ(8u, decryptInLengths[0]) << + "3rd OEMCrypto_DecryptCTR call targetted the wrong input length"; + EXPECT_TRUE(decryptIsEncryptedValues[0]) << + "3rd OEMCrypto_DecryptCTR call thought data was unencrypted"; + EXPECT_EQ(iv, decryptIvs[0]) << + "3rd OEMCrypto_DecryptCTR call had the wrong iv"; + EXPECT_EQ(8u, decryptOffsets[0]) << + "3rd OEMCrypto_DecryptCTR call had the wrong offset"; + EXPECT_EQ(out + 40, decryptOutBuffers[0].buffer.clear.address) << + "3rd OEMCrypto_DecryptCTR call targetted the wrong output location"; +} diff --git a/libclearkeydrmengine/include/WVCreateDrmPluginFactory.h b/libclearkeydrmengine/include/WVCreateDrmPluginFactory.h new file mode 100644 index 00000000..caac3090 --- /dev/null +++ b/libclearkeydrmengine/include/WVCreateDrmPluginFactory.h @@ -0,0 +1,15 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#ifndef WV_CREATE_DRM_PLUGIN_FACTORY_H_ +#define WV_CREATE_DRM_PLUGIN_FACTORY_H_ + +#include "media/drm/DrmEngineAPI.h" + +extern "C" { + android::DrmPluginFactory* createDrmPluginFactory(); + android::CryptoFactory *createCryptoFactory(); +} + +#endif // WV_CREATE_DRM_PLUGIN_FACTORY_H_ diff --git a/libclearkeydrmengine/include/WVDrmPluginFactory.h b/libclearkeydrmengine/include/WVDrmPluginFactory.h new file mode 100644 index 00000000..4368ec30 --- /dev/null +++ b/libclearkeydrmengine/include/WVDrmPluginFactory.h @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#ifndef WV_DRM_PLUGIN_FACTORY_H_ +#define WV_DRM_PLUGIN_FACTORY_H_ + +#include "media/stagefright/foundation/ABase.h" +#include "media/drm/DrmEngineAPI.h" +#include "media/hardware/CryptoAPI.h" + +namespace wvclearkey { + +using android::status_t; + +class WVDrmPluginFactory : public android::DrmPluginFactory, public android::CryptoFactory { +public: + WVDrmPluginFactory(); + virtual ~WVDrmPluginFactory(); + + // Implement DrmPluginFactory + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const; + + virtual status_t createCryptoPlugin( + const uint8_t uuid[16], const void *data, size_t size, + android::CryptoPlugin **plugin); + + virtual status_t createDrmClientPlugin( + const uint8_t uuid[16], const void *data, size_t size, + android::DrmClientPlugin **plugin); + + // Implement CryptoFactory + virtual status_t createPlugin( + const uint8_t uuid[16], const void *data, size_t size, + android::CryptoPlugin **plugin) { + return createCryptoPlugin(uuid, data, size, plugin); + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(WVDrmPluginFactory); + + void *mLegacyLibraryHandle; + android::CryptoFactory *mLegacyFactory; +}; + +} // namespace wvclearkey + +#endif // WV_DRM_PLUGIN_FACTORY_H_ diff --git a/libclearkeydrmengine/oemcrypto/TODO b/libclearkeydrmengine/oemcrypto/TODO new file mode 100644 index 00000000..7268443e --- /dev/null +++ b/libclearkeydrmengine/oemcrypto/TODO @@ -0,0 +1 @@ +Fred will fill this out diff --git a/libclearkeydrmengine/oemcrypto/include/OEMCryptoDASH.h b/libclearkeydrmengine/oemcrypto/include/OEMCryptoDASH.h new file mode 100644 index 00000000..d47274dd --- /dev/null +++ b/libclearkeydrmengine/oemcrypto/include/OEMCryptoDASH.h @@ -0,0 +1,711 @@ +/********************************************************************* + * OEMCryptoDASH.h + * + * (c) Copyright 2011-2012 Google, Inc. + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef OEMCRYPTO_DASH_H_ +#define OEMCRYPTO_DASH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define OEMCRYPTO_VERSION "4.0" +static const char oec_version[] = OEMCRYPTO_VERSION; + +#include + +typedef uint32_t OEMCrypto_SESSION; + +typedef enum OEMCryptoResult { + OEMCrypto_SUCCESS = 0, + OEMCrypto_ERROR_INIT_FAILED, + OEMCrypto_ERROR_TERMINATE_FAILED, + OEMCrypto_ERROR_OPEN_FAILURE, + OEMCrypto_ERROR_CLOSE_FAILURE, + OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED, + OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED, + OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_ERROR_NO_DEVICE_KEY, + OEMCrypto_ERROR_NO_ASSET_KEY, + OEMCrypto_ERROR_KEYBOX_INVALID, + OEMCrypto_ERROR_NO_KEYDATA, + OEMCrypto_ERROR_NO_CW, + OEMCrypto_ERROR_DECRYPT_FAILED, + OEMCrypto_ERROR_WRITE_KEYBOX, + OEMCrypto_ERROR_WRAP_KEYBOX, + OEMCrypto_ERROR_BAD_MAGIC, + OEMCrypto_ERROR_BAD_CRC, + OEMCrypto_ERROR_NO_DEVICEID, + OEMCrypto_ERROR_RNG_FAILED, + OEMCrypto_ERROR_RNG_NOT_SUPPORTED, + OEMCrypto_ERROR_SETUP, + OEMCrypto_ERROR_OPEN_SESSION_FAILED, + OEMCrypto_ERROR_CLOSE_SESSION_FAILED, + OEMCrypto_ERROR_INVALID_SESSION, + OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_ERROR_NO_CONTENT_KEY, + OEMCrypto_ERROR_CONTROL_INVALID, + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_ERROR_INVALID_CONTEXT, + OEMCrypto_ERROR_SIGNATURE_FAILURE +} OEMCryptoResult; + +/* + * OEMCrypto_DestBufferDesc + * Describes the type and access information for the memory to receive + * decrypted data. + * + * The OEMCrypto API supports a range of client device architectures. + * Different architectures have different methods for acquiring and securing + * buffers that will hold portions of the audio or video stream after + * decryption. Three basic strategies are recognized for handling decrypted + * stream data: + * 1. Return the decrypted data in the clear into normal user memory + * (ClearBuffer). The caller uses normal memory allocation methods to + * acquire a buffer, and supplies the memory address of the buffer in the + * descriptor. + * 2. Place the decrypted data into protected memory (SecureBuffer). The + * caller uses a platform-specific method to acquire the protected buffer + * and a user-memory handle that references it. The handle is supplied + * to the decrypt call in the descriptor. + * 3. Place the decrypted data directly into the audio or video decoder fifo + * (Direct). The caller will use platform-specific methods to initialize + * the fifo and the decoders. The decrypted stream data is not accessible + * to the caller. + * + * Specific fields are as follows: + * + * (type == OEMCrypto_BufferType_Clear) + * address - Address of start of user memory buffer. + * max_length - Size of user memory buffer. + * (type == OEMCrypto_BufferType_Secure) + * buffer - handle to a platform-specific secure buffer. + * max_length - Size of platform-specific secure buffer. + * (type == OEMCrypto_BufferType_Direct) + * is_video - If true, decrypted bytes are routed to the video + * decoder. If false, decrypted bytes are routed to the + * audio decoder. + */ +typedef enum OEMCryptoBufferType { + OEMCrypto_BufferType_Clear, + OEMCrypto_BufferType_Secure, + OEMCrypto_BufferType_Direct +} OEMCrytoBufferType; + +typedef struct { + OEMCryptoBufferType type; + union { + struct { // type == OEMCrypto_BufferType_Clear + uint8_t* address; + size_t max_length; + } clear; + struct { // type == OEMCrypto_BufferType_Secure + void* handle; + size_t max_length; + } secure; + struct { // type == OEMCrypto_BufferType_Direct + bool is_video; + } direct; + } buffer; +} OEMCrypto_DestBufferDesc; + +/* + * OEMCrypto_KeyObject + * Points to the relevant fields for a content key. The fields are extracted + * from the License Response message offered to OEMCrypto_LoadKeys(). Each + * field points to one of the components of the key. All fields are 128 bits + * (16 bytes): + * key_id - the unique id of this key. + * key_data_iv - the IV for performing AES-128-CBC decryption of the + * key_data field. + * key_data - the key data. It is encrypted (AES-128-CBC) with the + * session's derived encrypt key and the key_data_iv. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. + * + * The memory for the OEMCrypto_KeyObject fields is allocated and freed + * by the caller of OEMCrypto_LoadKeys(). + */ +typedef struct { + const uint8_t* key_id; + const uint8_t* key_data_iv; + const uint8_t* key_data; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyObject; + +/* + * OEMCrypto_KeyRefreshObject + * Points to the relevant fields for renewing a content key. The fields are + * extracted from the License Renewal Response message offered to + * OEMCrypto_RefreshKeys(). Each field points to one of the components of + * the key. All fields are 128 bits (16 bytes): + * key_id - the unique id of this key. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. + * + * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some + * Key Control Block fields, especially those related to key lifetime, may + * change. + * + * The memory for the OEMCrypto_KeyRefreshObject fields is allocated and freed + * by the caller of OEMCrypto_RefreshKeys(). + */ +typedef struct { + const uint8_t* key_id; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyRefreshObject; + +#define OEMCrypto_Initialize _oec01 +#define OEMCrypto_Terminate _oec02 +#define OEMCrypto_SetEntitlementKey _oec03 +#define OEMCrypto_DeriveControlWord _oec04 +#define OEMCrypto_DecryptVideo _oec05 +#define OEMCrypto_DecryptAudio _oec06 +#define OEMCrypto_InstallKeybox _oec07 +#define OEMCrypto_GetKeyData _oec08 +#define OEMCrypto_IsKeyboxValid _oec09 +#define OEMCrypto_GetRandom _oec10 +#define OEMCrypto_GetDeviceID _oec11 +#define OEMCrypto_EnterSecurePlayback _oec12 +#define OEMCrypto_ExitSecurePlayback _oec13 +#define OEMCrypto_WrapKeybox _oec14 +#define OEMCrypto_OpenSession _oec15 +#define OEMCrypto_CloseSession _oec16 +#define OEMCrypto_SetContentKey _oec17 +#define OEMCrypto_DecryptCTR _oec18 +#define OEMCrypto_DecryptCTS _oec19 +#define OEMCrypto_GenerateDerivedKeys _oec20 +#define OEMCrypto_GenerateSignature _oec21 +#define OEMCrypto_GenerateNonce _oec22 +#define OEMCrypto_LoadKeys _oec23 +#define OEMCrypto_RefreshKeys _oec24 +#define OEMCrypto_SelectKey _oec25 + +/* + * OEMCrypto_Initialize + * + * Description: + * Initialize the crypto firmware/hardware. + * + * Parameters: + * N/A + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + */ +OEMCryptoResult OEMCrypto_Initialize(void); + +/* + * OEMCrypto_Terminate + * + * Description: + * The API closes the crypto operation and releases all resources used. + * + * Parameters: + * N/A + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware + */ +OEMCryptoResult OEMCrypto_Terminate(void); + +/* + * OEMCrypto_OpenSession + * + * Description: + * The API provides for session based crypto initialization for AES CTR mode. + * + * Parameters: + * session (out) - pointer to crypto session identifier. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_OPEN_SESSION_FAILED failed to initialize the crypto session + */ +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); + +/* + * OEMCrypto_CloseSession + * + * Description: + * The API provides for session based crypto termination for AES CTR mode. + * + * Parameters: + * session (in) - crypto session identifier. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_CLOSE_SESSION_FAILED failed to terminate the crypto session + */ +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); + +/* + * OEMCrypto_GenerateDerivedKeys + * + * Description: + * Generates a pair of secondary keys, mac_key and encrypt_key, for handling + * signing and content key decryption under the license server protocol + * for AES CTR mode. + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details + * + * Parameters: + * session (in) - crypto session identifier. + * context (in) - pointer to memory containing context data for computing the + * secondary keys. + * context_length (in) - length of the context data. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, + const uint8_t *context, + size_t context_length); + +/* + * OEMCrypto_GenerateNonce + * + * Description: + * Generates a 32-bit nonce to detect possible replay attack on the key + * control block. + * + * Refer to documents "OEMCrypto Changes for V2 License Protocol" and "Key + * Control Block Definition" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be signed. + * message_length (in) - length of the message. + * signature (out) - pointer to memory to received the computed signature. + * signature_length (in/out) - (in) length of the signature buffer. + * (out) actual length of the signature + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateNonce( + OEMCrypto_SESSION session, + uint32_t* nonce); + +/* + * OEMCrypto_GenerateSignature + * + * Description: + * Generates a HMAC-SHA256 signature for license request signing under the + * license server protocol for AES CTR mode. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be signed. + * message_length (in) - length of the message. + * signature (out) - pointer to memory to received the computed signature. + * signature_length (in/out) - (in) length of the signature buffer. + * (out) actual length of the signature + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateSignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + +/* + * OEMCrypto_LoadKeys + * + * Description: + * Installs a set of keys for performing decryption in the current session. + * + * The relevant fields have been extracted from the License Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the derived mac_key). + * If the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * The mac_key is encrypted with the current encrypt_key and the offered IV. + * It replaces the mac_key created by OEMCrypto_GenerateDerivedkeys(). + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature. + * enc_mac_key_iv (in) - IV for decrypting new mac_key. Size is 128 bits. + * enc_mac_key (in) - encrypted mac_key for generating new mac_key. Size is + * 128 bits. + * num_keys (in) - number of keys present. + * key_array (in) - set of keys to be installed. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + */ +OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array); + +/* + * OEMCrypto_RefreshKeys + * + * Description: + * Updates an existing set of keys for continuing decryption in the + * current session. + * + * The relevant fields have been extracted from the Renewal Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the current mac_key). + * If the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish + * the mac_key + * + * Refer to document OEMCrypto Changes for V2 License Protocol for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature. + * num_keys (in) - number of keys present. + * key_array (in) - set of keys to be installed. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + */ +OEMCryptoResult +OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); + +/* + * OEMCrypto_SelectKey + * + * Description: + * Select a content key and install it in the hardware key ladder for + * subsequent decryption operations (OEMCrypto_DecryptCTR()). + * + * This operation is supported only while performing CTR mode decryption + * (see OEMCrypto_DecryptCTR). The specified key must have been previously + * "installed" via OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys(). + * + * A key control block is associated with the key and the session, and is used + * to configure the session context. The Key Control data is documented in + * "Key Control Block Definition". + * + * Step 1: Lookup the content key data via the offered key_id. The key data + * includes the key value, the content key IV, the key control + * block, and the key control block IV. + * + * Step 2: Lookup the encrypt_key (derived key). Latch the result in the + * hardware key ladder. + * + * Step 3: use the encrypt_key to decrypt (AES-128-CBC) the content key data, + * using the content key IV. Latch result in the hardware key ladder. + * + * Step 4: use the latched content key to decrypt (AES-128-CBC) the key + * control block using the key control block IV. Verify the key + * control block and apply it to the current session. + * + * Step 5: use the latched content key to decrypt (AES-128-CTR) + * to decrypt buffers passed in via OEMCrypto_DecryptCTR(). Continue + * to use this key until OEMCrypto_SelectKey() is called again, or + * until OEMCrypto_CloseSession() is called. + * + * Parameters: + * session (in) - crypto session identifier + * key_id (in) - pointer to the Key ID + * key_id_length (in) - length of the Key ID in bytes + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open + * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key + * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key + * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input + * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + */ +OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + const size_t key_id_length); + +/* + * OEMCrypto_DecryptCTR + * + * Description: + * + * The API decrypts (AES-CTR) the payload in the buffer referenced by + * the buffer_addr parameter into an internal buffer, using the key + * identified by key_id. + * + * Parameters: + * session (in) - crypto session identifier. + * data_addr (in) - An unaligned pointer to this segment of the stream. + * data_length (in) - The length of this segment of the stream. + * is_encrypted (in) - True if the buffer described by data_addr, + * data_length is encrypted. If is_encrypted is false, only the + * data_addr and data_length parameters are used. The iv and offset + * arguments are ignored. + * iv (in) - The initial value block to be used for content decryption. + * This is discussed further below. + * offset (in) - If non-zero, the decryption block boundary is different + * from the start of the data. offset should be subtracted from + * data_addr to compute the starting address of the first decrypted + * block. The bytes between the decryption block start address and + * data_addr are discarded after decryption. + * out_buffer (in) - A caller-owned descriptor that specifies the + * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc + * for details. + * + * AES CTR is a stream cipher. The stream may be composed of arbitrary- + * length clear and encrypted segments. The encrypted portions of a sample + * are collectively treated as a continuous sequence of decryption + * block-sized blocks even though the sequence is interrupted by clear blocks. + * This means a given encrypted segment may not start or end on a decryption + * block boundary. + * + * If data_addr is not aligned with a decryption block boundary (offset != 0), + * the additional offset bytes before data_addr (pre-padding) are included in + * the decrypt operation, and they are dropped after decryption. If + * data_length + offset is not a multiple of the decryption block size, the + * extra bytes in the final decryption block (post-padding) are also dropped + * after decryption. The caller is responsible for guaranteeing that all + * memory addresses from (data-addr - pre-padding) to (data-addr + + * data-length + post-padding) are valid memory addresses. + * + * After decrypting the entire buffer including any pre-padding and + * post-padding, send data_length bytes starting at data_addr to the decoder. + * + * NOTES: + * IV points to the counter value to be used for the initial + * encrypted block of the input buffer. The IV length is the AES + * block size. For subsequent encrypted AES blocks the IV is + * calculated by incrementing the lower 64 bits (byte 8-15) of the + * IV value used for the previous block. The counter rolls over to + * zero when it reaches its maximum value (0xFFFFFFFFFFFFFFFF). + * The upper 64 bits (byte 0-7) of the IV do not change. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_DECRYPT_FAILED + */ +OEMCryptoResult +OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer); + +/* + * OEMCrypto_InstallKeybox + * + * Description: + * Unwrap and store the keybox to persistent memory. + * The device key must be stored securely. The device key will be decrypted + * and latched into hardware key ladder by OEMCrypto_SetEntitlementKey. + * + * This function is used once to load the keybox onto the device at + * provisioning time. + * + * Parameters: + * keybox (in) - Pointer to clear keybox data. Must have been originally + * wrapped with OEMCrypto_WrapKeybox. + * keyboxLength (in) - Length of the keybox data in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox + */ +OEMCryptoResult OEMCrypto_InstallKeybox(uint8_t *keybox, + size_t keyBoxLength); + +/* + * OEMCrypto_IsKeyboxValid + * + * Description: + * Validate the Widevine Keybox stored on the device. + * + * The API performs two verification steps on the Keybox. It first verifies + * the MAGIC field contains a valid signature (must be 'kbox'). The API then + * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the + * checksum to the CRC stored in the Keybox. The CRC is computed over the + * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]). + * + * Parameters: + * none + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + */ +OEMCryptoResult OEMCrypto_IsKeyboxValid(void); + +/* + * OEMCrypto_GetDeviceID + * + * Description: + * Retrieve the device's unique identifier from the Keybox. + * + * Parameters: + * deviceId (out) - pointer to the buffer that receives the Device ID + * idLength (in/out) - on input, size of the caller's device ID buffer. + * On output, the number of bytes written into the buffer. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID + * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + */ +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t *idLength); + +/* + * OEMCrypto_GetKeyData + * + * Description: + * Returns the Key Data field from the Keybox. The Key Data field does not + * need to be encrypted by an OEM root key, but may be if desired. + * + * If the Key Data field was encrypted with an OEM root key when the Keybox + * was stored on the device, then this function should decrypt it and return + * the clear Key Data. If the Key Data was not encrypted, then this function + * should just access and return the clear Key data. + * + * Parameters: + * keyData (out) - pointer to a caller-managed buffer to hold the Key Data + * field from the Keybox + * dataLength (in/out) - on input, the allocated buffer size. On output, + * the number of bytes in KeyData. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData + * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData + */ +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t *keyDataLength); + +/* + * OEMCrypto_GetRandom + * + * Description: + * Return a buffer filled with hardware-generated random bytes. If the + * hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * + * Parameters: + * randomData (out) - Pointer to caller-manager buffer that will receive the + * random data. + * dataLength (in) - Length of the random data buffer in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + */ +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, + size_t dataLength); + +/* + * OEMCrypto_WrapKeybox + * + * Description: + * Wrap the Keybox with a key derived for the device key. If transportKey + * is not NULL, the input keybox is encrypted with transportKey. If so, + * decrypt the input keybox before wrapping it, using transportKey in AES-CBC + * mode with an IV of all zeroes. This function is only needed if the + * if the provisioning method involves saving the keybox to the file system. + * + * Parameters: + * keybox (in) - Pointer to keybox data. + * keyboxLength - Length of the Keybox data in bytes + * wrappedKeybox (out) - Pointer to wrapped keybox + * wrappedKeyboxLength (out) - Pointer to the length of the wrapped keybox in + * bytes + * transportKey (in) - An optional AES transport key. If provided, the input + * keybox is encrypted with this transport key with AES-CBC + * and a null IV. + * transportKeyLength - number of bytes in the transportKey + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox + */ +OEMCryptoResult OEMCrypto_WrapKeybox(uint8_t *keybox, + size_t keyBoxLength, + uint8_t *wrappedKeybox, + size_t *wrappedKeyBoxLength, + uint8_t *transportKey, + size_t transportKeyLength); + +#ifdef __cplusplus +} +#endif + +#endif // OEMCRYPTO_DASH_H_ diff --git a/libclearkeydrmengine/oemcrypto/src/TODO b/libclearkeydrmengine/oemcrypto/src/TODO new file mode 100644 index 00000000..7268443e --- /dev/null +++ b/libclearkeydrmengine/oemcrypto/src/TODO @@ -0,0 +1 @@ +Fred will fill this out diff --git a/libclearkeydrmengine/oemcrypto/test/TODO b/libclearkeydrmengine/oemcrypto/test/TODO new file mode 100644 index 00000000..7268443e --- /dev/null +++ b/libclearkeydrmengine/oemcrypto/test/TODO @@ -0,0 +1 @@ +Fred will fill this out diff --git a/libclearkeydrmengine/src/WVCreateDrmPluginFactory.cpp b/libclearkeydrmengine/src/WVCreateDrmPluginFactory.cpp new file mode 100644 index 00000000..993b93e8 --- /dev/null +++ b/libclearkeydrmengine/src/WVCreateDrmPluginFactory.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "WVCreateDrmPluginFactory.h" +#include "WVDrmPluginFactory.h" + +namespace wvclearkey { + +WVDrmPluginFactory* pluginFactory = NULL; + +WVDrmPluginFactory* createPluginFactory() { + if (pluginFactory == NULL) { + pluginFactory = new WVDrmPluginFactory(); + } + + return pluginFactory; +} + +} // namespace wvclearkey + +extern "C" android::DrmPluginFactory* createDrmPluginFactory() { + return wvclearkey::createPluginFactory(); +} + +extern "C" android::CryptoFactory *createCryptoFactory() { + return wvclearkey::createPluginFactory(); +} diff --git a/libclearkeydrmengine/src/WVDrmPluginFactory.cpp b/libclearkeydrmengine/src/WVDrmPluginFactory.cpp new file mode 100644 index 00000000..fd738552 --- /dev/null +++ b/libclearkeydrmengine/src/WVDrmPluginFactory.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "wv_clear_key" +#include + +#include "WVDrmPluginFactory.h" +#include "WVCryptoPlugin.h" +#include "OEMCryptoDASH.h" +#include "utils/Errors.h" + +#include + +namespace wvclearkey { + +WVDrmPluginFactory::WVDrmPluginFactory() : + mLegacyLibraryHandle(NULL) { + OEMCryptoResult res = OEMCrypto_Initialize(); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("OEMCrypto_Initialize error result: %d", res); + } + + mLegacyLibraryHandle = dlopen("libdrmdecrypt.so", RTLD_NOW); + + if (mLegacyLibraryHandle == NULL) { + ALOGE("Unable to locate libdrmdecrypt.so"); + } else { + using android::CryptoFactory; + typedef CryptoFactory *(*CreateCryptoFactoryFunc)(); + + CreateCryptoFactoryFunc legacyCreateCryptoFactory = + (CreateCryptoFactoryFunc)dlsym(mLegacyLibraryHandle, "createCryptoFactory"); + + if (legacyCreateCryptoFactory == NULL) { + ALOGE("Unable to find legacy symbol 'createCryptoFactory'."); + } else { + mLegacyFactory = legacyCreateCryptoFactory(); + if (mLegacyFactory == NULL) { + ALOGE("Legacy createCryptoFactory() failed."); + } + } + } +} + +WVDrmPluginFactory::~WVDrmPluginFactory() { + OEMCryptoResult res = OEMCrypto_Terminate(); + + if (res != OEMCrypto_SUCCESS) { + ALOGE("OEMCrypto_Terminate error result: %d", res); + } + + if (mLegacyFactory != NULL) { + delete mLegacyFactory; + mLegacyFactory = NULL; + } + + if (mLegacyLibraryHandle != NULL) { + dlclose(mLegacyLibraryHandle); + mLegacyLibraryHandle = NULL; + } +} + +bool WVDrmPluginFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const { + const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED + }; + + const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 + }; + + return !memcmp(uuid, kWidevineUUID, 16) || + !memcmp(uuid, kOldNetflixWidevineUUID, 16); +} + +status_t WVDrmPluginFactory::createCryptoPlugin( + const uint8_t uuid[16], const void *data, size_t size, + android::CryptoPlugin **plugin) { + if (!isCryptoSchemeSupported(uuid)) { + *plugin = NULL; + return android::BAD_VALUE; + } + + // 0 size means they want an old-style Widevine plugin + if (size == 0) { + if (mLegacyFactory == NULL) { + return -EINVAL; + } + + return mLegacyFactory->createPlugin(uuid, data, size, plugin); + } + + // We received a session ID, so create a CENC plugin. + // Note that we ignore the session in the clear-key solution, but we won't + // in the real plugin. + *plugin = new WVCryptoPlugin(); + + return android::OK; +} + +status_t WVDrmPluginFactory::createDrmClientPlugin( + const uint8_t uuid[16], const void *data, size_t size, + android::DrmClientPlugin **plugin) { + if (!isCryptoSchemeSupported(uuid)) { + *plugin = NULL; + return android::BAD_VALUE; + } + + *plugin = NULL; + + // In this demo code, there is no need for a Drm Client Plugin + return -EPERM; +} + +} // namespace wvclearkey diff --git a/libclearkeydrmengine/test/Android.mk b/libclearkeydrmengine/test/Android.mk new file mode 100644 index 00000000..c8f3b874 --- /dev/null +++ b/libclearkeydrmengine/test/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + WVCreateDrmPluginFactory_test.cpp \ + WVDrmPluginFactory_test.cpp \ + EmptyWVCryptoPlugin.cpp \ + ../src/WVCreateDrmPluginFactory.cpp \ + ../src/WVDrmPluginFactory.cpp \ + +LOCAL_C_INCLUDES := \ + external/gtest/include \ + external/stlport/stlport \ + bionic \ + frameworks/native/include \ + frameworks/av/include \ + vendor/widevine/libclearkeydrmengine/include \ + vendor/widevine/libclearkeydrmengine/crypto/include \ + vendor/widevine/libclearkeydrmengine/oemcrypto/include \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + liblog \ + libutils \ + +LOCAL_MODULE := libclearkeydrmengine_test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libclearkeydrmengine/test/EmptyWVCryptoPlugin.cpp b/libclearkeydrmengine/test/EmptyWVCryptoPlugin.cpp new file mode 100644 index 00000000..24759d94 --- /dev/null +++ b/libclearkeydrmengine/test/EmptyWVCryptoPlugin.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "WVCryptoPlugin.h" + +namespace wvclearkey { + WVCryptoPlugin::WVCryptoPlugin() {} + WVCryptoPlugin::~WVCryptoPlugin() {} + ssize_t WVCryptoPlugin::decrypt( + bool secure, + const uint8_t key[16], + const uint8_t iv[16], + Mode mode, + const void *srcPtr, + const SubSample *subSamples, size_t numSubSamples, + void *dstPtr, + android::AString *errorDetailMsg) { + return 0; + } +} diff --git a/libclearkeydrmengine/test/WVCreateDrmPluginFactory_test.cpp b/libclearkeydrmengine/test/WVCreateDrmPluginFactory_test.cpp new file mode 100644 index 00000000..6e52263e --- /dev/null +++ b/libclearkeydrmengine/test/WVCreateDrmPluginFactory_test.cpp @@ -0,0 +1,14 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "utils/UniquePtr.h" +#include "WVCreateDrmPluginFactory.h" +#include "gtest/gtest.h" + +TEST(CreateDrmPluginFactoryTest, CreatesObject) { + UniquePtr factory = createDrmPluginFactory(); + + EXPECT_NE((android::DrmPluginFactory*)NULL, factory) << + "createDrmPluginFactory() returned null"; +} diff --git a/libclearkeydrmengine/test/WVDrmPluginFactory_test.cpp b/libclearkeydrmengine/test/WVDrmPluginFactory_test.cpp new file mode 100644 index 00000000..1f8c9d32 --- /dev/null +++ b/libclearkeydrmengine/test/WVDrmPluginFactory_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "utils/UniquePtr.h" +#include "WVDrmPluginFactory.h" +#include "OEMCryptoDASH.h" +#include "gtest/gtest.h" + +using namespace wvclearkey; + +const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED +}; + +const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 +}; + +const uint8_t kUnknownUUID[16] = { + 0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20, + 0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26 +}; + +TEST(WVDrmPluginFactoryTest, SupportsSupportedCryptoSchemes) { + UniquePtr factory = new WVDrmPluginFactory(); + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) << + "WVDrmPluginFactory does not support Widevine's UUID"; + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) << + "WVDrmPluginFactory does not support the old Netflix Widevine UUID"; +} + +TEST(WVDrmPluginFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) { + UniquePtr factory = new WVDrmPluginFactory(); + + EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) << + "WVDrmPluginFactory incorrectly claims to support an unknown UUID"; +} + +TEST(WVDrmPluginFactoryTest, CreatesCryptoPlugins) { + UniquePtr factory = new WVDrmPluginFactory(); + UniquePtr plugin; + + status_t result = factory->createCryptoPlugin(kWidevineUUID, NULL, 0, &plugin); + + EXPECT_EQ(android::OK, result) << + "WVDrmPluginFactory returned error from createCryptoPlugin()"; + EXPECT_NE((android::CryptoPlugin *)NULL, plugin) << + "WVDrmPluginFactory's createCryptoPlugin() did not create a plugin"; +} + +TEST(WVDrmPluginFactoryTest, DoesNotCreateDrmClientPlugins) { + UniquePtr factory = new WVDrmPluginFactory(); + UniquePtr plugin; + + status_t result = factory->createDrmClientPlugin(kWidevineUUID, NULL, 0, &plugin); + + EXPECT_EQ(-EPERM, result) << + "WVDrmPluginFactory did not indicate that createDrmClientPlugin() is not implemented"; + EXPECT_EQ((android::DrmClientPlugin *)NULL, plugin) << + "WVDrmPluginFactory's createDrmClientPlugin() created a plugin (?!?)"; +} + +TEST(WVDrmPluginFactoryTest, RefusesToCreateWithUnsupportedCryptoScheme) { + UniquePtr factory = new WVDrmPluginFactory(); + UniquePtr cryptoPlugin; + UniquePtr drmClientPlugin; + status_t result; + + result = factory->createCryptoPlugin(kUnknownUUID, NULL, 0, &cryptoPlugin); + EXPECT_EQ(android::BAD_VALUE, result) << + "WVDrmPluginFactory did not reject unknown UUID when creating a CryptoPlugin"; + EXPECT_EQ((android::CryptoPlugin *)NULL, cryptoPlugin) << + "WVDrmPluginFactory created a CryptoPlugin despite having an unknown UUID"; + + result = factory->createDrmClientPlugin(kUnknownUUID, NULL, 0, &drmClientPlugin); + EXPECT_EQ(android::BAD_VALUE, result) << + "WVDrmPluginFactory did not reject unknown UUID when creating a DrmClientPlugin"; + EXPECT_EQ((android::DrmClientPlugin *)NULL, drmClientPlugin) << + "WVDrmPluginFactory created a DrmClientPlugin despite having an unknown UUID"; +} + +bool oemCryptoInitialized; +bool oemCryptoTerminated; + +extern "C" { + OEMCryptoResult OEMCrypto_Initialize(void) { + oemCryptoInitialized = true; + return OEMCrypto_SUCCESS; + } + + OEMCryptoResult OEMCrypto_Terminate(void) { + oemCryptoTerminated = true; + return OEMCrypto_SUCCESS; + } + +} + +TEST(WVDrmPluginFactoryTest, CallsOemCrypto) { + oemCryptoInitialized = false; + oemCryptoTerminated = false; + + WVDrmPluginFactory *factory = new WVDrmPluginFactory(); + + EXPECT_EQ(true, oemCryptoInitialized) << + "WVDrmPluginFactory did not call OEMCrypto_Initialize()"; + + delete factory; + + EXPECT_EQ(true, oemCryptoTerminated) << + "WVDrmPluginFactory did not call OEMCrypto_Terminate()"; +}