summaryrefslogtreecommitdiffstats
path: root/drm
diff options
context:
space:
mode:
Diffstat (limited to 'drm')
-rw-r--r--drm/libdrmframework/Android.mk4
-rw-r--r--drm/libdrmframework/DrmManagerClient.cpp2
-rw-r--r--drm/libdrmframework/DrmManagerClientImpl.cpp17
-rw-r--r--drm/libdrmframework/NoOpDrmManagerClientImpl.cpp152
-rw-r--r--drm/libdrmframework/include/DrmManagerClientImpl.h66
-rw-r--r--drm/libdrmframework/include/NoOpDrmManagerClientImpl.h74
-rw-r--r--drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp1
-rw-r--r--drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp67
-rw-r--r--drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h44
-rw-r--r--drm/mediadrm/plugins/clearkey/Android.mk62
-rw-r--r--drm/mediadrm/plugins/clearkey/ClearKeyTypes.h38
-rw-r--r--drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp32
-rw-r--r--drm/mediadrm/plugins/clearkey/ClearKeyUUID.h28
-rw-r--r--drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp32
-rw-r--r--drm/mediadrm/plugins/clearkey/CreatePluginFactories.h28
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoFactory.cpp52
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoFactory.h44
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp83
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoPlugin.h56
-rw-r--r--drm/mediadrm/plugins/clearkey/DrmFactory.cpp52
-rw-r--r--drm/mediadrm/plugins/clearkey/DrmFactory.h46
-rw-r--r--drm/mediadrm/plugins/clearkey/DrmPlugin.cpp91
-rw-r--r--drm/mediadrm/plugins/clearkey/DrmPlugin.h230
-rw-r--r--drm/mediadrm/plugins/clearkey/InitDataParser.cpp147
-rw-r--r--drm/mediadrm/plugins/clearkey/InitDataParser.h47
-rw-r--r--drm/mediadrm/plugins/clearkey/JsonWebKey.cpp269
-rw-r--r--drm/mediadrm/plugins/clearkey/JsonWebKey.h62
-rw-r--r--drm/mediadrm/plugins/clearkey/Session.cpp85
-rw-r--r--drm/mediadrm/plugins/clearkey/Session.h64
-rw-r--r--drm/mediadrm/plugins/clearkey/SessionLibrary.cpp78
-rw-r--r--drm/mediadrm/plugins/clearkey/SessionLibrary.h59
-rw-r--r--drm/mediadrm/plugins/clearkey/Utils.cpp30
-rw-r--r--drm/mediadrm/plugins/clearkey/Utils.h32
-rw-r--r--drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp421
-rw-r--r--drm/mediadrm/plugins/clearkey/tests/Android.mk52
-rw-r--r--drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp235
-rw-r--r--drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp321
-rw-r--r--drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp39
-rw-r--r--drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h16
39 files changed, 3213 insertions, 45 deletions
diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk
index 49c4f9b..33f9d3b 100644
--- a/drm/libdrmframework/Android.mk
+++ b/drm/libdrmframework/Android.mk
@@ -19,12 +19,14 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
DrmManagerClientImpl.cpp \
- DrmManagerClient.cpp
+ DrmManagerClient.cpp \
+ NoOpDrmManagerClientImpl.cpp
LOCAL_MODULE:= libdrmframework
LOCAL_SHARED_LIBRARIES := \
libutils \
+ libcutils \
liblog \
libbinder \
libdl
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index ea30d01..440dd91 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -29,7 +29,7 @@ DrmManagerClient::DrmManagerClient():
}
DrmManagerClient::~DrmManagerClient() {
- DrmManagerClientImpl::remove(mUniqueId);
+ mDrmManagerClientImpl->remove(mUniqueId);
mDrmManagerClientImpl->removeClient(mUniqueId);
mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
}
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index ffefd74..2d2c90e 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -21,8 +21,10 @@
#include <utils/String8.h>
#include <utils/Vector.h>
#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
#include "DrmManagerClientImpl.h"
+#include "NoOpDrmManagerClientImpl.h"
using namespace android;
@@ -35,9 +37,12 @@ const String8 DrmManagerClientImpl::EMPTY_STRING("");
DrmManagerClientImpl* DrmManagerClientImpl::create(
int* pUniqueId, bool isNative) {
- *pUniqueId = getDrmManagerService()->addUniqueId(isNative);
-
- return new DrmManagerClientImpl();
+ sp<IDrmManagerService> service = getDrmManagerService();
+ if (service != NULL) {
+ *pUniqueId = getDrmManagerService()->addUniqueId(isNative);
+ return new DrmManagerClientImpl();
+ }
+ return new NoOpDrmManagerClientImpl();
}
void DrmManagerClientImpl::remove(int uniqueId) {
@@ -47,6 +52,12 @@ void DrmManagerClientImpl::remove(int uniqueId) {
const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() {
Mutex::Autolock lock(sMutex);
if (NULL == sDrmManagerService.get()) {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("drm.service.enabled", value, NULL) == 0) {
+ // Drm is undefined for this device
+ return sDrmManagerService;
+ }
+
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
diff --git a/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp b/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp
new file mode 100644
index 0000000..dab583d
--- /dev/null
+++ b/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "NoOpDrmManagerClientImpl.h"
+
+namespace android {
+
+void NoOpDrmManagerClientImpl::remove(int uniqueId) {
+}
+
+void NoOpDrmManagerClientImpl::addClient(int uniqueId) {
+}
+
+void NoOpDrmManagerClientImpl::removeClient(int uniqueId) {
+}
+
+status_t NoOpDrmManagerClientImpl::setOnInfoListener(
+ int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
+ return UNKNOWN_ERROR;
+}
+
+DrmConstraints* NoOpDrmManagerClientImpl::getConstraints(int uniqueId, const String8* path, const int action) {
+ return NULL;
+}
+
+DrmMetadata* NoOpDrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) {
+ return NULL;
+}
+
+bool NoOpDrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
+ return false;
+}
+
+DrmInfoStatus* NoOpDrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
+ return NULL;
+}
+
+DrmInfo* NoOpDrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
+ return NULL;
+}
+
+status_t NoOpDrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath) {
+ return UNKNOWN_ERROR;
+}
+
+String8 NoOpDrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path, int fd) {
+ return String8();
+}
+
+int NoOpDrmManagerClientImpl::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
+ return -1;
+}
+
+int NoOpDrmManagerClientImpl::checkRightsStatus(int uniqueId, const String8& path, int action) {
+ return -1;
+}
+
+status_t NoOpDrmManagerClientImpl::consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve) {
+ return UNKNOWN_ERROR;
+}
+
+status_t NoOpDrmManagerClientImpl::setPlaybackStatus(
+ int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position) {
+ return UNKNOWN_ERROR;
+}
+
+bool NoOpDrmManagerClientImpl::validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description) {
+ return false;
+}
+
+status_t NoOpDrmManagerClientImpl::removeRights(int uniqueId, const String8& path) {
+ return UNKNOWN_ERROR;
+}
+
+status_t NoOpDrmManagerClientImpl::removeAllRights(int uniqueId) {
+ return UNKNOWN_ERROR;
+}
+
+int NoOpDrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) {
+ return -1;
+}
+
+DrmConvertedStatus* NoOpDrmManagerClientImpl::convertData(int uniqueId, int convertId, const DrmBuffer* inputData) {
+ return NULL;
+}
+
+DrmConvertedStatus* NoOpDrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) {
+ return NULL;
+}
+
+status_t NoOpDrmManagerClientImpl::getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) {
+ return UNKNOWN_ERROR;
+}
+
+sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession(
+ int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) {
+ return NULL;
+}
+
+sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession(
+ int uniqueId, const char* uri, const char* mime) {
+ return NULL;
+}
+
+sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession(int uniqueId, const DrmBuffer& buf,
+ const String8& mimeType) {
+ return NULL;
+}
+
+status_t NoOpDrmManagerClientImpl::closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle) {
+ return UNKNOWN_ERROR;
+}
+
+status_t NoOpDrmManagerClientImpl::initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo) {
+ return UNKNOWN_ERROR;
+}
+
+status_t NoOpDrmManagerClientImpl::decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
+ return UNKNOWN_ERROR;
+}
+
+status_t NoOpDrmManagerClientImpl::finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId) {
+ return UNKNOWN_ERROR;
+}
+
+ssize_t NoOpDrmManagerClientImpl::pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ void* buffer, ssize_t numBytes, off64_t offset) {
+ return -1;
+}
+
+status_t NoOpDrmManagerClientImpl::notify(const DrmInfoEvent& event) {
+ return UNKNOWN_ERROR;
+}
+
+}
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
index 3400cb1..3858675 100644
--- a/drm/libdrmframework/include/DrmManagerClientImpl.h
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -34,30 +34,30 @@ class DrmInfoEvent;
*
*/
class DrmManagerClientImpl : public BnDrmServiceListener {
-private:
+protected:
DrmManagerClientImpl() { }
public:
static DrmManagerClientImpl* create(int* pUniqueId, bool isNative);
- static void remove(int uniqueId);
-
virtual ~DrmManagerClientImpl() { }
public:
+ virtual void remove(int uniqueId);
+
/**
* Adds the client respective to given unique id.
*
* @param[in] uniqueId Unique identifier for a session
*/
- void addClient(int uniqueId);
+ virtual void addClient(int uniqueId);
/**
* Removes the client respective to given unique id.
*
* @param[in] uniqueId Unique identifier for a session
*/
- void removeClient(int uniqueId);
+ virtual void removeClient(int uniqueId);
/**
* Register a callback to be invoked when the caller required to
@@ -68,7 +68,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t setOnInfoListener(
+ virtual status_t setOnInfoListener(
int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener);
/**
@@ -83,7 +83,7 @@ public:
* @note
* In case of error, return NULL
*/
- DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+ virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
/**
* Get metadata information associated with input content.
@@ -95,7 +95,7 @@ public:
* @note
* In case of error, return NULL
*/
- DrmMetadata* getMetadata(int uniqueId, const String8* path);
+ virtual DrmMetadata* getMetadata(int uniqueId, const String8* path);
/**
* Check whether the given mimetype or path can be handled
@@ -106,7 +106,7 @@ public:
* @return
* True if DrmManager can handle given path or mime type.
*/
- bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+ virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
/**
* Executes given drm information based on its type
@@ -116,7 +116,7 @@ public:
* @return DrmInfoStatus
* instance as a result of processing given input
*/
- DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+ virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
/**
* Retrieves necessary information for registration, unregistration or rights
@@ -127,7 +127,7 @@ public:
* @return DrmInfo
* instance as a result of processing given input
*/
- DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+ virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
/**
* Save DRM rights to specified rights path
@@ -140,7 +140,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ virtual status_t saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath);
/**
@@ -152,7 +152,7 @@ public:
* @return String8
* Returns mime-type of the original content, such as "video/mpeg"
*/
- String8 getOriginalMimeType(int uniqueId, const String8& path, int fd);
+ virtual String8 getOriginalMimeType(int uniqueId, const String8& path, int fd);
/**
* Retrieves the type of the protected object (content, rights, etc..)
@@ -165,7 +165,7 @@ public:
* @return type of the DRM content,
* such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT
*/
- int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+ virtual int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
/**
* Check whether the given content has valid rights or not
@@ -176,7 +176,7 @@ public:
* @return the status of the rights for the protected content,
* such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc.
*/
- int checkRightsStatus(int uniqueId, const String8& path, int action);
+ virtual int checkRightsStatus(int uniqueId, const String8& path, int action);
/**
* Consumes the rights for a content.
@@ -190,7 +190,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve);
+ virtual status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve);
/**
* Informs the DRM engine about the playback actions performed on the DRM files.
@@ -203,7 +203,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t setPlaybackStatus(
+ virtual status_t setPlaybackStatus(
int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position);
/**
@@ -215,7 +215,7 @@ public:
* @param[in] description Detailed description of the action
* @return true if the action is allowed.
*/
- bool validateAction(
+ virtual bool validateAction(
int uniqueId, const String8& path, int action, const ActionDescription& description);
/**
@@ -226,7 +226,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t removeRights(int uniqueId, const String8& path);
+ virtual status_t removeRights(int uniqueId, const String8& path);
/**
* Removes all the rights information of each plug-in associated with
@@ -236,7 +236,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t removeAllRights(int uniqueId);
+ virtual status_t removeAllRights(int uniqueId);
/**
* This API is for Forward Lock based DRM scheme.
@@ -248,7 +248,7 @@ public:
* @param[in] mimeType Description/MIME type of the input data packet
* @return Return handle for the convert session
*/
- int openConvertSession(int uniqueId, const String8& mimeType);
+ virtual int openConvertSession(int uniqueId, const String8& mimeType);
/**
* Accepts and converts the input data which is part of DRM file.
@@ -263,7 +263,7 @@ public:
* the output converted data and offset. In this case the
* application will ignore the offset information.
*/
- DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+ virtual DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
/**
* Informs the Drm Agent when there is no more data which need to be converted
@@ -279,7 +279,7 @@ public:
* the application on which offset these signature data
* should be appended.
*/
- DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+ virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
/**
* Retrieves all DrmSupportInfo instance that native DRM framework can handle.
@@ -292,7 +292,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+ virtual status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
/**
* Open the decrypt session to decrypt the given protected content
@@ -305,7 +305,7 @@ public:
* @return
* Handle for the decryption session
*/
- sp<DecryptHandle> openDecryptSession(
+ virtual sp<DecryptHandle> openDecryptSession(
int uniqueId, int fd, off64_t offset, off64_t length, const char* mime);
/**
@@ -317,7 +317,7 @@ public:
* @return
* Handle for the decryption session
*/
- sp<DecryptHandle> openDecryptSession(
+ virtual sp<DecryptHandle> openDecryptSession(
int uniqueId, const char* uri, const char* mime);
/**
@@ -329,7 +329,7 @@ public:
* @return
* Handle for the decryption session
*/
- sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf,
+ virtual sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf,
const String8& mimeType);
/**
@@ -340,7 +340,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle);
+ virtual status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle);
/**
* Initialize decryption for the given unit of the protected content
@@ -352,7 +352,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ virtual status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle,
int decryptUnitId, const DrmBuffer* headerInfo);
/**
@@ -372,7 +372,7 @@ public:
* DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
* DRM_ERROR_DECRYPT for failure.
*/
- status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId,
+ virtual status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId,
const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
/**
@@ -384,7 +384,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId);
+ virtual status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId);
/**
* Reads the specified number of bytes from an open DRM file.
@@ -397,7 +397,7 @@ public:
*
* @return Number of bytes read. Returns -1 for Failure.
*/
- ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ virtual ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
void* buffer, ssize_t numBytes, off64_t offset);
/**
@@ -407,7 +407,7 @@ public:
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
*/
- status_t notify(const DrmInfoEvent& event);
+ virtual status_t notify(const DrmInfoEvent& event);
private:
Mutex mLock;
diff --git a/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h b/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h
new file mode 100644
index 0000000..e8e8f42
--- /dev/null
+++ b/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __NO_OP_DRM_MANAGER_CLIENT_IMPL_H__
+#define __NO_OP_DRM_MANAGER_CLIENT_IMPL_H__
+
+#include "DrmManagerClientImpl.h"
+
+namespace android {
+
+class NoOpDrmManagerClientImpl : public DrmManagerClientImpl {
+public:
+ NoOpDrmManagerClientImpl() { }
+
+ void remove(int uniqueId);
+ void addClient(int uniqueId);
+ void removeClient(int uniqueId);
+ status_t setOnInfoListener(
+ int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener);
+ DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action);
+
+ DrmMetadata* getMetadata(int uniqueId, const String8* path);
+ bool canHandle(int uniqueId, const String8& path, const String8& mimeType);
+ DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo);
+ DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest);
+ status_t saveRights(int uniqueId, const DrmRights& drmRights,
+ const String8& rightsPath, const String8& contentPath);
+ String8 getOriginalMimeType(int uniqueId, const String8& path, int fd);
+ int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType);
+ int checkRightsStatus(int uniqueId, const String8& path, int action);
+ status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve);
+ status_t setPlaybackStatus(
+ int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position);
+ bool validateAction(
+ int uniqueId, const String8& path, int action, const ActionDescription& description);
+ status_t removeRights(int uniqueId, const String8& path);
+ status_t removeAllRights(int uniqueId);
+ int openConvertSession(int uniqueId, const String8& mimeType);
+ DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData);
+ DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId);
+ status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
+ sp<DecryptHandle> openDecryptSession(
+ int uniqueId, int fd, off64_t offset, off64_t length, const char* mime);
+ sp<DecryptHandle> openDecryptSession(
+ int uniqueId, const char* uri, const char* mime);
+ sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf,
+ const String8& mimeType);
+ status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle);
+ status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ int decryptUnitId, const DrmBuffer* headerInfo);
+ status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId,
+ const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
+ status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId);
+ ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle,
+ void* buffer, ssize_t numBytes, off64_t offset);
+ status_t notify(const DrmInfoEvent& event);
+};
+
+}
+
+#endif // __NO_OP_DRM_MANAGER_CLIENT_IMPL_H
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 234aef2..f400732 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -316,6 +316,7 @@ String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path,
if (-1 < fileDesc) {
if (FwdLockFile_attach(fileDesc) < 0) {
+ close(fileDesc);
return mimeString;
}
const char* pMimeType = FwdLockFile_GetContentType(fileDesc);
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
new file mode 100644
index 0000000..01f8d65
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <openssl/aes.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+static const size_t kBlockBitCount = kBlockSize * 8;
+
+android::status_t AesCtrDecryptor::decrypt(const android::Vector<uint8_t>& key,
+ const Iv iv, const uint8_t* source,
+ uint8_t* destination,
+ const SubSample* subSamples,
+ size_t numSubSamples,
+ size_t* bytesDecryptedOut) {
+ uint32_t blockOffset = 0;
+ uint8_t previousEncryptedCounter[kBlockSize];
+ memset(previousEncryptedCounter, 0, kBlockSize);
+
+ size_t offset = 0;
+ AES_KEY opensslKey;
+ AES_set_encrypt_key(key.array(), kBlockBitCount, &opensslKey);
+ Iv opensslIv;
+ memcpy(opensslIv, iv, sizeof(opensslIv));
+
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ const SubSample& subSample = subSamples[i];
+
+ if (subSample.mNumBytesOfClearData > 0) {
+ memcpy(destination + offset, source + offset,
+ subSample.mNumBytesOfClearData);
+ offset += subSample.mNumBytesOfClearData;
+ }
+
+ if (subSample.mNumBytesOfEncryptedData > 0) {
+ AES_ctr128_encrypt(source + offset, destination + offset,
+ subSample.mNumBytesOfEncryptedData, &opensslKey,
+ opensslIv, previousEncryptedCounter,
+ &blockOffset);
+ offset += subSample.mNumBytesOfEncryptedData;
+ }
+ }
+
+ *bytesDecryptedOut = offset;
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
new file mode 100644
index 0000000..b416266
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_AES_CTR_DECRYPTOR_H_
+#define CLEARKEY_AES_CTR_DECRYPTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <Utils.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class AesCtrDecryptor {
+public:
+ AesCtrDecryptor() {}
+
+ android::status_t decrypt(const android::Vector<uint8_t>& key, const Iv iv,
+ const uint8_t* source, uint8_t* destination,
+ const SubSample* subSamples, size_t numSubSamples,
+ size_t* bytesDecryptedOut);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(AesCtrDecryptor);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_AES_CTR_DECRYPTOR_H_
diff --git a/drm/mediadrm/plugins/clearkey/Android.mk b/drm/mediadrm/plugins/clearkey/Android.mk
new file mode 100644
index 0000000..22a85b4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Android.mk
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2014 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AesCtrDecryptor.cpp \
+ ClearKeyUUID.cpp \
+ CreatePluginFactories.cpp \
+ CryptoFactory.cpp \
+ CryptoPlugin.cpp \
+ DrmFactory.cpp \
+ DrmPlugin.cpp \
+ InitDataParser.cpp \
+ JsonWebKey.cpp \
+ Session.cpp \
+ SessionLibrary.cpp \
+ Utils.cpp \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ external/jsmn \
+ external/openssl/include \
+ frameworks/av/drm/mediadrm/plugins/clearkey \
+ frameworks/av/include \
+ frameworks/native/include \
+
+LOCAL_MODULE := libdrmclearkeyplugin
+
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := mediadrm
+
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ liblog \
+ libstagefright_foundation \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+ libjsmn \
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################################################################
+# Build unit tests
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
new file mode 100644
index 0000000..a28959a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_TYPES_H_
+#define CLEARKEY_TYPES_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <openssl/aes.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+const uint8_t kBlockSize = AES_BLOCK_SIZE;
+typedef uint8_t KeyId[kBlockSize];
+typedef uint8_t Iv[kBlockSize];
+
+typedef android::CryptoPlugin::SubSample SubSample;
+
+typedef android::KeyedVector<android::Vector<uint8_t>,
+ android::Vector<uint8_t> > KeyMap;
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_TYPES_H_
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
new file mode 100644
index 0000000..ed050f7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <string.h>
+
+#include "ClearKeyUUID.h"
+
+namespace clearkeydrm {
+
+bool isClearKeyUUID(const uint8_t uuid[16]) {
+ static const uint8_t kClearKeyUUID[16] = {
+ 0x10,0x77,0xEF,0xEC,0xC0,0xB2,0x4D,0x02,
+ 0xAC,0xE3,0x3C,0x1E,0x52,0xE2,0xFB,0x4B
+ };
+
+ return !memcmp(uuid, kClearKeyUUID, sizeof(kClearKeyUUID));
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
new file mode 100644
index 0000000..ac99418
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_UUID_H_
+#define CLEARKEY_UUID_H_
+
+#include <stdint.h>
+
+namespace clearkeydrm {
+
+bool isClearKeyUUID(const uint8_t uuid[16]);
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_UUID_H_
diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
new file mode 100644
index 0000000..ec1420e
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "CreatePluginFactories.h"
+
+#include "CryptoFactory.h"
+#include "DrmFactory.h"
+
+extern "C" {
+
+android::DrmFactory* createDrmFactory() {
+ return new clearkeydrm::DrmFactory();
+}
+
+android::CryptoFactory* createCryptoFactory() {
+ return new clearkeydrm::CryptoFactory();
+}
+
+} // extern "C"
diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
new file mode 100644
index 0000000..d9acec1
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
+#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/hardware/CryptoAPI.h>
+
+extern "C" {
+ android::DrmFactory* createDrmFactory();
+ android::CryptoFactory* createCryptoFactory();
+}
+
+#endif // CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
new file mode 100644
index 0000000..ee3189b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "CryptoFactory.h"
+
+#include "ClearKeyUUID.h"
+#include "CryptoPlugin.h"
+#include "Session.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool CryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const {
+ return isClearKeyUUID(uuid);
+}
+
+android::status_t CryptoFactory::createPlugin(
+ const uint8_t uuid[16],
+ const void* data, size_t size,
+ android::CryptoPlugin** plugin) {
+ if (!isCryptoSchemeSupported(uuid)) {
+ *plugin = NULL;
+ return android::BAD_VALUE;
+ }
+
+ android::sp<Session> session = SessionLibrary::get()->findSession(
+ data, size);
+ *plugin = new CryptoPlugin(session);
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
new file mode 100644
index 0000000..568bc4b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_CRYPTO_FACTORY_H_
+#define CLEARKEY_CRYPTO_FACTORY_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+namespace clearkeydrm {
+
+class CryptoFactory : public android::CryptoFactory {
+public:
+ CryptoFactory() {}
+ virtual ~CryptoFactory() {}
+
+ virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const;
+
+ virtual android::status_t createPlugin(
+ const uint8_t uuid[16],
+ const void* data, size_t size,
+ android::CryptoPlugin** plugin);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CryptoFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
new file mode 100644
index 0000000..adad136
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+
+#include "CryptoPlugin.h"
+
+namespace clearkeydrm {
+
+using android::Vector;
+using android::AString;
+using android::status_t;
+
+// Returns negative values for error code and positive values for the size of
+// decrypted data. In theory, the output size can be larger than the input
+// size, but in practice this will never happen for AES-CTR.
+ssize_t CryptoPlugin::decrypt(bool secure, const KeyId keyId, const Iv iv,
+ Mode mode, const void* srcPtr,
+ const SubSample* subSamples, size_t numSubSamples,
+ void* dstPtr, AString* errorDetailMsg) {
+ if (secure) {
+ errorDetailMsg->setTo("Secure decryption is not supported with "
+ "ClearKey.");
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ if (mode == kMode_Unencrypted) {
+ size_t offset = 0;
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ const SubSample& subSample = subSamples[i];
+
+ if (subSample.mNumBytesOfEncryptedData != 0) {
+ errorDetailMsg->setTo(
+ "Encrypted subsamples found in allegedly unencrypted "
+ "data.");
+ return android::ERROR_DRM_DECRYPT;
+ }
+
+ if (subSample.mNumBytesOfClearData != 0) {
+ memcpy(reinterpret_cast<uint8_t*>(dstPtr) + offset,
+ reinterpret_cast<const uint8_t*>(srcPtr) + offset,
+ subSample.mNumBytesOfClearData);
+ offset += subSample.mNumBytesOfClearData;
+ }
+ }
+ return static_cast<ssize_t>(offset);
+ } else if (mode == kMode_AES_CTR) {
+ size_t bytesDecrypted;
+ status_t res = mSession->decrypt(keyId, iv, srcPtr, dstPtr, subSamples,
+ numSubSamples, &bytesDecrypted);
+ if (res == android::OK) {
+ return static_cast<ssize_t>(bytesDecrypted);
+ } else {
+ errorDetailMsg->setTo("Decryption Error");
+ return static_cast<ssize_t>(res);
+ }
+ } else {
+ errorDetailMsg->setTo(
+ "Selected encryption mode is not supported by the ClearKey DRM "
+ "Plugin.");
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
new file mode 100644
index 0000000..002d9e0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_CRYPTO_PLUGIN_H_
+#define CLEARKEY_CRYPTO_PLUGIN_H_
+
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "ClearKeyTypes.h"
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class CryptoPlugin : public android::CryptoPlugin {
+public:
+ CryptoPlugin(const android::sp<Session>& session) : mSession(session) {}
+ virtual ~CryptoPlugin() {}
+
+ virtual bool requiresSecureDecoderComponent(const char* mime) const {
+ UNUSED(mime);
+ return false;
+ }
+
+ virtual ssize_t decrypt(
+ bool secure, const KeyId keyId, const Iv iv,
+ Mode mode, const void* srcPtr,
+ const SubSample* subSamples, size_t numSubSamples,
+ void* dstPtr, android::AString* errorDetailMsg);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(CryptoPlugin);
+
+ android::sp<Session> mSession;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_CRYPTO_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
new file mode 100644
index 0000000..40275cf
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+
+#include "DrmFactory.h"
+
+#include "DrmPlugin.h"
+#include "ClearKeyUUID.h"
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+bool DrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) {
+ return isClearKeyUUID(uuid);
+}
+
+bool DrmFactory::isContentTypeSupported(const android::String8 &initDataType) {
+ // This should match the types handed by InitDataParser.
+ return initDataType == "cenc" ||
+ initDataType == "webm";
+}
+
+android::status_t DrmFactory::createDrmPlugin(
+ const uint8_t uuid[16], android::DrmPlugin** plugin) {
+ if (!isCryptoSchemeSupported(uuid)) {
+ *plugin = NULL;
+ return android::BAD_VALUE;
+ }
+
+ *plugin = new DrmPlugin(SessionLibrary::get());
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/DrmFactory.h
new file mode 100644
index 0000000..164d3d0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmFactory.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_DRM_FACTORY_H_
+#define CLEARKEY_DRM_FACTORY_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class DrmFactory : public android::DrmFactory {
+public:
+ DrmFactory() {}
+ virtual ~DrmFactory() {}
+
+ virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]);
+
+ virtual bool isContentTypeSupported(const android::String8 &initDataType);
+
+ virtual android::status_t createDrmPlugin(
+ const uint8_t uuid[16], android::DrmPlugin** plugin);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmFactory);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_FACTORY_H_
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
new file mode 100644
index 0000000..96fca94
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/StrongPointer.h>
+
+#include "DrmPlugin.h"
+
+#include "Session.h"
+
+namespace clearkeydrm {
+
+using android::sp;
+
+status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
+ sp<Session> session = mSessionLibrary->createSession();
+ sessionId = session->sessionId();
+ return android::OK;
+}
+
+status_t DrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
+ sp<Session> session = mSessionLibrary->findSession(sessionId);
+ mSessionLibrary->destroySession(session);
+ return android::OK;
+}
+
+status_t DrmPlugin::getKeyRequest(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ KeyType keyType,
+ const KeyedVector<String8, String8>& optionalParameters,
+ Vector<uint8_t>& request,
+ String8& defaultUrl) {
+ UNUSED(optionalParameters);
+ if (keyType != kKeyType_Streaming) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ sp<Session> session = mSessionLibrary->findSession(scope);
+ defaultUrl.clear();
+ return session->getKeyRequest(initData, initDataType, &request);
+}
+
+status_t DrmPlugin::provideKeyResponse(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& keySetId) {
+ sp<Session> session = mSessionLibrary->findSession(scope);
+ status_t res = session->provideKeyResponse(response);
+ if (res == android::OK) {
+ keySetId.clear();
+ }
+ return res;
+}
+
+status_t DrmPlugin::getPropertyString(
+ const String8& name, String8& value) const {
+ if (name == "vendor") {
+ value = "Google";
+ } else if (name == "version") {
+ value = "1.0";
+ } else if (name == "description") {
+ value = "ClearKey CDM";
+ } else if (name == "algorithms") {
+ value = "";
+ } else {
+ ALOGE("App requested unknown string property %s", name.string());
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ return android::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
new file mode 100644
index 0000000..bfbc6bf
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_DRM_PLUGIN_H_
+#define CLEARKEY_DRM_PLUGIN_H_
+
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "SessionLibrary.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::List;
+using android::status_t;
+using android::String8;
+using android::Vector;
+
+class DrmPlugin : public android::DrmPlugin {
+public:
+ DrmPlugin(SessionLibrary* sessionLibrary)
+ : mSessionLibrary(sessionLibrary) {}
+ virtual ~DrmPlugin() {}
+
+ virtual status_t openSession(Vector<uint8_t>& sessionId);
+
+ virtual status_t closeSession(const Vector<uint8_t>& sessionId);
+
+ virtual status_t getKeyRequest(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ KeyType keyType,
+ const KeyedVector<String8, String8>& optionalParameters,
+ Vector<uint8_t>& request,
+ String8& defaultUrl);
+
+ virtual status_t provideKeyResponse(
+ const Vector<uint8_t>& scope,
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& keySetId);
+
+ virtual status_t removeKeys(const Vector<uint8_t>& sessionId) {
+ UNUSED(sessionId);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t restoreKeys(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keySetId) {
+ UNUSED(sessionId);
+ UNUSED(keySetId);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t queryKeyStatus(
+ const Vector<uint8_t>& sessionId,
+ KeyedVector<String8, String8>& infoMap) const {
+ UNUSED(sessionId);
+ UNUSED(infoMap);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getProvisionRequest(
+ const String8& cert_type,
+ const String8& cert_authority,
+ Vector<uint8_t>& request,
+ String8& defaultUrl) {
+ UNUSED(cert_type);
+ UNUSED(cert_authority);
+ UNUSED(request);
+ UNUSED(defaultUrl);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t provideProvisionResponse(
+ const Vector<uint8_t>& response,
+ Vector<uint8_t>& certificate,
+ Vector<uint8_t>& wrappedKey) {
+ UNUSED(response);
+ UNUSED(certificate);
+ UNUSED(wrappedKey);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getSecureStops(List<Vector<uint8_t> >& secureStops) {
+ UNUSED(secureStops);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t releaseSecureStops(const Vector<uint8_t>& ssRelease) {
+ UNUSED(ssRelease);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t getPropertyString(
+ const String8& name, String8& value) const;
+
+ virtual status_t getPropertyByteArray(
+ const String8& name, Vector<uint8_t>& value) const {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setPropertyString(
+ const String8& name, const String8& value) {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setPropertyByteArray(
+ const String8& name, const Vector<uint8_t>& value) {
+ UNUSED(name);
+ UNUSED(value);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setCipherAlgorithm(
+ const Vector<uint8_t>& sessionId, const String8& algorithm) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t setMacAlgorithm(
+ const Vector<uint8_t>& sessionId, const String8& algorithm) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t encrypt(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& input,
+ const Vector<uint8_t>& iv,
+ Vector<uint8_t>& output) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(input);
+ UNUSED(iv);
+ UNUSED(output);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t decrypt(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& input,
+ const Vector<uint8_t>& iv,
+ Vector<uint8_t>& output) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(input);
+ UNUSED(iv);
+ UNUSED(output);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t sign(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& message,
+ Vector<uint8_t>& signature) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(message);
+ UNUSED(signature);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t verify(
+ const Vector<uint8_t>& sessionId,
+ const Vector<uint8_t>& keyId,
+ const Vector<uint8_t>& message,
+ const Vector<uint8_t>& signature, bool& match) {
+ UNUSED(sessionId);
+ UNUSED(keyId);
+ UNUSED(message);
+ UNUSED(signature);
+ UNUSED(match);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ virtual status_t signRSA(
+ const Vector<uint8_t>& sessionId,
+ const String8& algorithm,
+ const Vector<uint8_t>& message,
+ const Vector<uint8_t>& wrappedKey,
+ Vector<uint8_t>& signature) {
+ UNUSED(sessionId);
+ UNUSED(algorithm);
+ UNUSED(message);
+ UNUSED(wrappedKey);
+ UNUSED(signature);
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+
+ SessionLibrary* mSessionLibrary;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_DRM_PLUGIN_H_
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
new file mode 100644
index 0000000..c22d73a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <endian.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/MediaErrors.h>
+#include <string.h>
+
+#include "InitDataParser.h"
+
+#include "ClearKeyUUID.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+using android::AString;
+using android::String8;
+using android::Vector;
+
+namespace {
+ const size_t kKeyIdSize = 16;
+ const size_t kSystemIdSize = 16;
+}
+
+android::status_t InitDataParser::parse(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* licenseRequest) {
+ // Build a list of the key IDs
+ Vector<const uint8_t*> keyIds;
+ if (initDataType == "cenc") {
+ android::status_t res = parsePssh(initData, &keyIds);
+ if (res != android::OK) {
+ return res;
+ }
+ } else if (initDataType == "webm") {
+ // WebM "init data" is just a single key ID
+ if (initData.size() != kKeyIdSize) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ keyIds.push(initData.array());
+ } else {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Build the request
+ String8 requestJson = generateRequest(keyIds);
+ licenseRequest->clear();
+ licenseRequest->appendArray(
+ reinterpret_cast<const uint8_t*>(requestJson.string()),
+ requestJson.size());
+ return android::OK;
+}
+
+android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData,
+ Vector<const uint8_t*>* keyIds) {
+ size_t readPosition = 0;
+
+ // Validate size field
+ uint32_t expectedSize = initData.size();
+ expectedSize = htonl(expectedSize);
+ if (memcmp(&initData[readPosition], &expectedSize,
+ sizeof(expectedSize)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(expectedSize);
+
+ // Validate PSSH box identifier
+ const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+ if (memcmp(&initData[readPosition], psshIdentifier,
+ sizeof(psshIdentifier)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshIdentifier);
+
+ // Validate EME version number
+ const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+ if (memcmp(&initData[readPosition], psshVersion1,
+ sizeof(psshVersion1)) != 0) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshVersion1);
+
+ // Validate system ID
+ if (!isClearKeyUUID(&initData[readPosition])) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+ readPosition += kSystemIdSize;
+
+ // Read key ID count
+ uint32_t keyIdCount;
+ memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
+ keyIdCount = ntohl(keyIdCount);
+ readPosition += sizeof(keyIdCount);
+ if (readPosition + (keyIdCount * kKeyIdSize) !=
+ initData.size() - sizeof(uint32_t)) {
+ return android::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ // Calculate the key ID offsets
+ for (uint32_t i = 0; i < keyIdCount; ++i) {
+ size_t keyIdPosition = readPosition + (i * kKeyIdSize);
+ keyIds->push(&initData[keyIdPosition]);
+ }
+ return android::OK;
+}
+
+String8 InitDataParser::generateRequest(const Vector<const uint8_t*>& keyIds) {
+ const String8 kRequestPrefix("{\"kids\":[");
+ const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+ const String8 kBase64Padding("=");
+
+ String8 request(kRequestPrefix);
+ AString encodedId;
+ for (size_t i = 0; i < keyIds.size(); ++i) {
+ encodedId.clear();
+ android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+ if (i != 0) {
+ request.append(",");
+ }
+ request.appendFormat("\"%s\"", encodedId.c_str());
+ }
+ request.append(kRequestSuffix);
+
+ // Android's Base64 encoder produces padding. EME forbids padding.
+ request.removeAll(kBase64Padding);
+ return request;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/InitDataParser.h
new file mode 100644
index 0000000..9505d2a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_INIT_DATA_PARSER_H_
+#define CLEARKEY_INIT_DATA_PARSER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace clearkeydrm {
+
+class InitDataParser {
+public:
+ InitDataParser() {}
+
+ android::status_t parse(const android::Vector<uint8_t>& initData,
+ const android::String8& initDataType,
+ android::Vector<uint8_t>* licenseRequest);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(InitDataParser);
+
+ android::status_t parsePssh(const android::Vector<uint8_t>& initData,
+ android::Vector<const uint8_t*>* keyIds);
+
+ android::String8 generateRequest(
+ const android::Vector<const uint8_t*>& keyIds);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_INIT_DATA_PARSER_H_
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
new file mode 100644
index 0000000..53ffae4
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 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 "JsonWebKey"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+namespace {
+const android::String8 kKeysTag("keys");
+const android::String8 kKeyTypeTag("kty");
+const android::String8 kSymmetricKeyValue("oct");
+const android::String8 kKeyTag("k");
+const android::String8 kKeyIdTag("kid");
+const android::String8 kBase64Padding("=");
+}
+
+namespace clearkeydrm {
+
+using android::ABuffer;
+using android::AString;
+
+JsonWebKey::JsonWebKey() {
+}
+
+JsonWebKey::~JsonWebKey() {
+}
+
+/*
+ * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key
+ * pairs from the JSON Web Key Set. Both key ids and keys are base64url
+ * encoded. The KeyMap contains base64url decoded key id:key pairs.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+ KeyMap* keys) {
+
+ keys->clear();
+ if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) {
+ return false;
+ }
+
+ // mJsonObjects[0] contains the entire JSON Web Key Set, including
+ // all the base64 encoded keys. Each key is also stored separately as
+ // a JSON object in mJsonObjects[1..n] where n is the total
+ // number of keys in the set.
+ if (!isJsonWebKeySet(mJsonObjects[0])) {
+ return false;
+ }
+
+ String8 encodedKey, encodedKeyId;
+ Vector<uint8_t> decodedKey, decodedKeyId;
+
+ // mJsonObjects[1] contains the first JSON Web Key in the set
+ for (size_t i = 1; i < mJsonObjects.size(); ++i) {
+ encodedKeyId.clear();
+ encodedKey.clear();
+
+ if (!parseJsonObject(mJsonObjects[i], &mTokens))
+ return false;
+
+ if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) {
+ if (encodedKeyId.isEmpty() || encodedKey.isEmpty()) {
+ ALOGE("Must have both key id and key in the JsonWebKey set.");
+ continue;
+ }
+
+ if (!decodeBase64String(encodedKeyId, &decodedKeyId)) {
+ ALOGE("Failed to decode key id(%s)", encodedKeyId.string());
+ continue;
+ }
+
+ if (!decodeBase64String(encodedKey, &decodedKey)) {
+ ALOGE("Failed to decode key(%s)", encodedKey.string());
+ continue;
+ }
+
+ keys->add(decodedKeyId, decodedKey);
+ }
+ }
+ return true;
+}
+
+bool JsonWebKey::decodeBase64String(const String8& encodedText,
+ Vector<uint8_t>* decodedText) {
+
+ decodedText->clear();
+
+ // encodedText should not contain padding characters as per EME spec.
+ if (encodedText.find(kBase64Padding) != -1) {
+ return false;
+ }
+
+ // Since android::decodeBase64() requires padding characters,
+ // add them so length of encodedText is exactly a multiple of 4.
+ int remainder = encodedText.length() % 4;
+ String8 paddedText(encodedText);
+ if (remainder > 0) {
+ for (int i = 0; i < 4 - remainder; ++i) {
+ paddedText.append(kBase64Padding);
+ }
+ }
+
+ android::sp<ABuffer> buffer =
+ android::decodeBase64(AString(paddedText.string()));
+ if (buffer == NULL) {
+ ALOGE("Malformed base64 encoded content found.");
+ return false;
+ }
+
+ decodedText->appendArray(buffer->base(), buffer->size());
+ return true;
+}
+
+bool JsonWebKey::findKey(const String8& jsonObject, String8* keyId,
+ String8* encodedKey) {
+
+ String8 key, value;
+
+ // Only allow symmetric key, i.e. "kty":"oct" pair.
+ if (jsonObject.find(kKeyTypeTag) >= 0) {
+ findValue(kKeyTypeTag, &value);
+ if (0 != value.compare(kSymmetricKeyValue))
+ return false;
+ }
+
+ if (jsonObject.find(kKeyIdTag) >= 0) {
+ findValue(kKeyIdTag, keyId);
+ }
+
+ if (jsonObject.find(kKeyTag) >= 0) {
+ findValue(kKeyTag, encodedKey);
+ }
+ return true;
+}
+
+void JsonWebKey::findValue(const String8 &key, String8* value) {
+ value->clear();
+ const char* valueToken;
+ for (Vector<String8>::const_iterator nextToken = mTokens.begin();
+ nextToken != mTokens.end(); ++nextToken) {
+ if (0 == (*nextToken).compare(key)) {
+ if (nextToken + 1 == mTokens.end())
+ break;
+ valueToken = (*(nextToken + 1)).string();
+ value->setTo(valueToken);
+ nextToken++;
+ break;
+ }
+ }
+}
+
+bool JsonWebKey::isJsonWebKeySet(const String8& jsonObject) const {
+ if (jsonObject.find(kKeysTag) == -1) {
+ ALOGE("JSON Web Key does not contain keys.");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Parses a JSON objects string and initializes a vector of tokens.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonObject(const String8& jsonObject,
+ Vector<String8>* tokens) {
+ jsmn_parser parser;
+
+ jsmn_init(&parser);
+ int numTokens = jsmn_parse(&parser,
+ jsonObject.string(), jsonObject.size(), NULL, 0);
+ if (numTokens < 0) {
+ ALOGE("Parser returns error code=%d", numTokens);
+ return false;
+ }
+
+ unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+ mJsmnTokens.clear();
+ mJsmnTokens.setCapacity(jsmnTokensSize);
+
+ jsmn_init(&parser);
+ int status = jsmn_parse(&parser, jsonObject.string(),
+ jsonObject.size(), mJsmnTokens.editArray(), numTokens);
+ if (status < 0) {
+ ALOGE("Parser returns error code=%d", status);
+ return false;
+ }
+
+ tokens->clear();
+ String8 token;
+ const char *pjs;
+ for (int j = 0; j < numTokens; ++j) {
+ pjs = jsonObject.string() + mJsmnTokens[j].start;
+ if (mJsmnTokens[j].type == JSMN_STRING ||
+ mJsmnTokens[j].type == JSMN_PRIMITIVE) {
+ token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
+ tokens->add(token);
+ }
+ }
+ return true;
+}
+
+/*
+ * Parses JSON Web Key Set string and initializes a vector of JSON objects.
+ *
+ * @return Returns false for errors, true for success.
+ */
+bool JsonWebKey::parseJsonWebKeySet(const String8& jsonWebKeySet,
+ Vector<String8>* jsonObjects) {
+ if (jsonWebKeySet.isEmpty()) {
+ ALOGE("Empty JSON Web Key");
+ return false;
+ }
+
+ // The jsmn parser only supports unicode encoding.
+ jsmn_parser parser;
+
+ // Computes number of tokens. A token marks the type, offset in
+ // the original string.
+ jsmn_init(&parser);
+ int numTokens = jsmn_parse(&parser,
+ jsonWebKeySet.string(), jsonWebKeySet.size(), NULL, 0);
+ if (numTokens < 0) {
+ ALOGE("Parser returns error code=%d", numTokens);
+ return false;
+ }
+
+ unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
+ mJsmnTokens.setCapacity(jsmnTokensSize);
+
+ jsmn_init(&parser);
+ int status = jsmn_parse(&parser, jsonWebKeySet.string(),
+ jsonWebKeySet.size(), mJsmnTokens.editArray(), numTokens);
+ if (status < 0) {
+ ALOGE("Parser returns error code=%d", status);
+ return false;
+ }
+
+ String8 token;
+ const char *pjs;
+ for (int i = 0; i < numTokens; ++i) {
+ pjs = jsonWebKeySet.string() + mJsmnTokens[i].start;
+ if (mJsmnTokens[i].type == JSMN_OBJECT) {
+ token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
+ jsonObjects->add(token);
+ }
+ }
+ return true;
+}
+
+} // clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
new file mode 100644
index 0000000..6ae50ee
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef CLEARKEY_JSON_WEB_KEY_H_
+#define CLEARKEY_JSON_WEB_KEY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "jsmn.h"
+#include "Utils.h"
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+using android::KeyedVector;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+class JsonWebKey {
+ public:
+ JsonWebKey();
+ virtual ~JsonWebKey();
+
+ bool extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet,
+ KeyMap* keys);
+
+ private:
+ Vector<jsmntok_t> mJsmnTokens;
+ Vector<String8> mJsonObjects;
+ Vector<String8> mTokens;
+
+ bool decodeBase64String(const String8& encodedText,
+ Vector<uint8_t>* decodedText);
+ bool findKey(const String8& jsonObject, String8* keyId,
+ String8* encodedKey);
+ void findValue(const String8 &key, String8* value);
+ bool isJsonWebKeySet(const String8& jsonObject) const;
+ bool parseJsonObject(const String8& jsonObject, Vector<String8>* tokens);
+ bool parseJsonWebKeySet(const String8& jsonWebKeySet, Vector<String8>* jsonObjects);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JsonWebKey);
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_JSON_WEB_KEY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/Session.cpp
new file mode 100644
index 0000000..95016f5
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+#include "Session.h"
+
+#include "AesCtrDecryptor.h"
+#include "InitDataParser.h"
+#include "JsonWebKey.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::String8;
+using android::Vector;
+using android::status_t;
+
+status_t Session::getKeyRequest(
+ const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* keyRequest) const {
+ InitDataParser parser;
+ return parser.parse(initData, initDataType, keyRequest);
+}
+
+status_t Session::provideKeyResponse(const Vector<uint8_t>& response) {
+ String8 responseString(
+ reinterpret_cast<const char*>(response.array()), response.size());
+ KeyMap keys;
+
+ Mutex::Autolock lock(mMapLock);
+ JsonWebKey parser;
+ if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) {
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const KeyMap::key_type& keyId = keys.keyAt(i);
+ const KeyMap::value_type& key = keys.valueAt(i);
+ mKeyMap.add(keyId, key);
+ }
+ return android::OK;
+ } else {
+ return android::ERROR_DRM_UNKNOWN;
+ }
+}
+
+status_t Session::decrypt(
+ const KeyId keyId, const Iv iv, const void* source,
+ void* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut) {
+ Mutex::Autolock lock(mMapLock);
+
+ Vector<uint8_t> keyIdVector;
+ keyIdVector.appendArray(keyId, kBlockSize);
+ if (mKeyMap.indexOfKey(keyIdVector) < 0) {
+ return android::ERROR_DRM_NO_LICENSE;
+ }
+
+ const Vector<uint8_t>& key = mKeyMap.valueFor(keyIdVector);
+ AesCtrDecryptor decryptor;
+ return decryptor.decrypt(
+ key, iv,
+ reinterpret_cast<const uint8_t*>(source),
+ reinterpret_cast<uint8_t*>(destination), subSamples,
+ numSubSamples, bytesDecryptedOut);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/Session.h
new file mode 100644
index 0000000..cab0dc3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Session.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_SESSION_H_
+#define CLEARKEY_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "ClearKeyTypes.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class Session : public android::RefBase {
+public:
+ explicit Session(const android::Vector<uint8_t>& sessionId)
+ : mSessionId(sessionId) {}
+ virtual ~Session() {}
+
+ const android::Vector<uint8_t>& sessionId() const { return mSessionId; }
+
+ android::status_t getKeyRequest(
+ const android::Vector<uint8_t>& initData,
+ const android::String8& initDataType,
+ android::Vector<uint8_t>* keyRequest) const;
+
+ android::status_t provideKeyResponse(
+ const android::Vector<uint8_t>& response);
+
+ android::status_t decrypt(
+ const KeyId keyId, const Iv iv, const void* source,
+ void* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Session);
+
+ const android::Vector<uint8_t> mSessionId;
+
+ android::Mutex mMapLock;
+ KeyMap mKeyMap;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_H_
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
new file mode 100644
index 0000000..d047c53
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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_NDEBUG 0
+#define LOG_TAG "ClearKeyCryptoPlugin"
+#include <utils/Log.h>
+
+#include <utils/String8.h>
+
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+using android::Mutex;
+using android::sp;
+using android::String8;
+using android::Vector;
+
+Mutex SessionLibrary::sSingletonLock;
+SessionLibrary* SessionLibrary::sSingleton = NULL;
+
+SessionLibrary* SessionLibrary::get() {
+ Mutex::Autolock lock(sSingletonLock);
+
+ if (sSingleton == NULL) {
+ ALOGD("Instantiating Session Library Singleton.");
+ sSingleton = new SessionLibrary();
+ }
+
+ return sSingleton;
+}
+
+const sp<Session>& SessionLibrary::createSession() {
+ Mutex::Autolock lock(mSessionsLock);
+
+ String8 sessionIdString = String8::format("%u", mNextSessionId);
+ mNextSessionId += 1;
+ Vector<uint8_t> sessionId;
+ sessionId.appendArray(
+ reinterpret_cast<const uint8_t*>(sessionIdString.string()),
+ sessionIdString.size());
+
+ mSessions.add(sessionId, new Session(sessionId));
+ return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+ const Vector<uint8_t>& sessionId) {
+ Mutex::Autolock lock(mSessionsLock);
+ return mSessions.valueFor(sessionId);
+}
+
+const sp<Session>& SessionLibrary::findSession(
+ const void* data, size_t size) {
+ Vector<uint8_t> sessionId;
+ sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size);
+ return findSession(sessionId);
+}
+
+void SessionLibrary::destroySession(const sp<Session>& session) {
+ Mutex::Autolock lock(mSessionsLock);\
+ mSessions.removeItem(session->sessionId());
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
new file mode 100644
index 0000000..56c8828
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_SESSION_LIBRARY_H_
+#define CLEARKEY_SESSION_LIBRARY_H_
+
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "Session.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+
+class SessionLibrary {
+public:
+ static SessionLibrary* get();
+
+ const android::sp<Session>& createSession();
+
+ const android::sp<Session>& findSession(
+ const android::Vector<uint8_t>& sessionId);
+
+ const android::sp<Session>& findSession(const void* data, size_t size);
+
+ void destroySession(const android::sp<Session>& session);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(SessionLibrary);
+
+ SessionLibrary() : mNextSessionId(1) {}
+
+ static android::Mutex sSingletonLock;
+ static SessionLibrary* sSingleton;
+
+ android::Mutex mSessionsLock;
+ uint32_t mNextSessionId;
+ android::KeyedVector<android::Vector<uint8_t>, android::sp<Session> >
+ mSessions;
+};
+
+} // namespace clearkeydrm
+
+#endif // CLEARKEY_SESSION_LIBRARY_H_
diff --git a/drm/mediadrm/plugins/clearkey/Utils.cpp b/drm/mediadrm/plugins/clearkey/Utils.cpp
new file mode 100644
index 0000000..93c643b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "Utils.h"
+
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs) {
+ if (lhs.size() < rhs.size()) {
+ return true;
+ } else if (lhs.size() > rhs.size()) {
+ return false;
+ }
+ return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
+}
+
+} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/Utils.h b/drm/mediadrm/plugins/clearkey/Utils.h
new file mode 100644
index 0000000..2543124
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/Utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef CLEARKEY_UTILS_H_
+#define CLEARKEY_UTILS_H_
+
+#include <utils/Vector.h>
+
+// Add a comparison operator for this Vector specialization so that it can be
+// used as a key in a KeyedVector.
+namespace android {
+
+bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs);
+
+} // namespace android
+
+#define UNUSED(x) (void)(x);
+
+#endif // CLEARKEY_UTILS_H_
diff --git a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
new file mode 100644
index 0000000..039e402
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "AesCtrDecryptor.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+class AesCtrDecryptorTest : public ::testing::Test {
+ protected:
+ typedef uint8_t Key[kBlockSize];
+
+ status_t attemptDecrypt(const Key& key, const Iv& iv, const uint8_t* source,
+ uint8_t* destination, const SubSample* subSamples,
+ size_t numSubSamples, size_t* bytesDecryptedOut) {
+ Vector<uint8_t> keyVector;
+ keyVector.appendArray(key, kBlockSize);
+
+ AesCtrDecryptor decryptor;
+ return decryptor.decrypt(keyVector, iv, source, destination, subSamples,
+ numSubSamples, bytesDecryptedOut);
+ }
+
+ template <size_t totalSize>
+ void attemptDecryptExpectingSuccess(const Key& key, const Iv& iv,
+ const uint8_t* encrypted,
+ const uint8_t* decrypted,
+ const SubSample* subSamples,
+ size_t numSubSamples) {
+ uint8_t outputBuffer[totalSize] = {};
+ size_t bytesDecrypted = 0;
+ ASSERT_EQ(android::OK, attemptDecrypt(key, iv, encrypted, outputBuffer,
+ subSamples, numSubSamples,
+ &bytesDecrypted));
+ EXPECT_EQ(totalSize, bytesDecrypted);
+ EXPECT_EQ(0, memcmp(outputBuffer, decrypted, totalSize));
+ }
+};
+
+TEST_F(AesCtrDecryptorTest, DecryptsContiguousEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 1;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 64}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedBifurcatedEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 2;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 32},
+ {0, 32}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedBifurcatedEncryptedBlock) {
+ const size_t kTotalSize = 64;
+ const size_t kNumSubsamples = 2;
+
+ // Test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {0, 29},
+ {0, 35}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsOneMixedSubSample) {
+ const size_t kTotalSize = 72;
+ const size_t kNumSubsamples = 1;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 64 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 64}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsAlignedMixedSubSamples) {
+ const size_t kTotalSize = 80;
+ const size_t kNumSubsamples = 2;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 32 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 32 encrypted bytes
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 32},
+ {8, 32}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsUnalignedMixedSubSamples) {
+ const size_t kTotalSize = 80;
+ const size_t kNumSubsamples = 2;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 8 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ // 30 encrypted bytes
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 34 encrypted bytes
+ 0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+ 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0,
+ 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe,
+ 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00,
+ 0x9c, 0xee
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x00, 0x64, 0x0b, 0xbb,
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba,
+ 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {8, 30},
+ {8, 34}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+TEST_F(AesCtrDecryptorTest, DecryptsComplexMixedSubSamples) {
+ const size_t kTotalSize = 72;
+ const size_t kNumSubsamples = 6;
+
+ // Based on test vectors from NIST-800-38A
+ Key key = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+
+ Iv iv = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+
+ uint8_t encrypted[kTotalSize] = {
+ // 4 clear bytes
+ 0xf0, 0x13, 0xca, 0xc7,
+ // 1 encrypted bytes
+ 0x87,
+ // 9 encrypted bytes
+ 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b,
+ 0xef,
+ // 11 clear bytes
+ 0x81, 0x4f, 0x24, 0x87, 0x0e, 0xde, 0xba, 0xad,
+ 0x11, 0x9b, 0x46,
+ // 20 encrypted bytes
+ 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff,
+ // 8 clear bytes
+ 0x94, 0xba, 0x88, 0x2e, 0x0e, 0x12, 0x11, 0x55,
+ // 3 clear bytes
+ 0x10, 0xf5, 0x22,
+ // 14 encrypted bytes
+ 0xfd, 0xff, 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5,
+ 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
+ // 2 clear bytes
+ 0x02, 0x01
+ };
+
+ uint8_t decrypted[kTotalSize] = {
+ 0xf0, 0x13, 0xca, 0xc7, 0x6b, 0xc1, 0xbe, 0xe2,
+ 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x81, 0x4f,
+ 0x24, 0x87, 0x0e, 0xde, 0xba, 0xad, 0x11, 0x9b,
+ 0x46, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae,
+ 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e,
+ 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x94, 0xba, 0x88,
+ 0x2e, 0x0e, 0x12, 0x11, 0x55, 0x10, 0xf5, 0x22,
+ 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c,
+ 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x02, 0x01
+ };
+
+ SubSample subSamples[kNumSubsamples] = {
+ {4, 1},
+ {0, 9},
+ {11, 20},
+ {8, 0},
+ {3, 14},
+ {2, 0}
+ };
+
+ attemptDecryptExpectingSuccess<kTotalSize>(key, iv, encrypted, decrypted,
+ subSamples, kNumSubsamples);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk
new file mode 100644
index 0000000..ac5bb21
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2014 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.
+#
+# ----------------------------------------------------------------
+# Builds ClearKey Drm Tests
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ClearKeyDrmUnitTest
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ AesCtrDecryptorUnittest.cpp \
+ InitDataParserUnittest.cpp \
+ JsonWebKeyUnittest.cpp \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ external/gtest/include \
+ external/jsmn \
+ external/openssl/include \
+ external/stlport/stlport \
+ frameworks/av/drm/mediadrm/plugins/clearkey \
+ frameworks/av/include \
+ frameworks/native/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest \
+ libgtest_main \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libdrmclearkeyplugin \
+ liblog \
+ libstagefright_foundation \
+ libstlport \
+ libutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
new file mode 100644
index 0000000..4ba65ed
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include "InitDataParser.h"
+
+namespace clearkeydrm {
+
+using namespace android;
+
+namespace {
+ const size_t kKeyIdSize = 16;
+ const String8 kCencType("cenc");
+ const String8 kWebMType("webm");
+ const String8 kBase64Padding("=");
+}
+
+class InitDataParserTest : public ::testing::Test {
+ protected:
+ status_t attemptParse(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ Vector<uint8_t>* licenseRequest) {
+ InitDataParser parser;
+ return parser.parse(initData, initDataType, licenseRequest);
+ }
+
+ void attemptParseExpectingSuccess(const Vector<uint8_t>& initData,
+ const String8& initDataType,
+ const Vector<String8>& expectedKeys) {
+ const String8 kRequestPrefix("{\"kids\":[");
+ const String8 kRequestSuffix("],\"type\":\"temporary\"}");
+ Vector<uint8_t> request;
+ ASSERT_EQ(android::OK, attemptParse(initData, initDataType, &request));
+
+ String8 requestString(reinterpret_cast<const char*>(request.array()),
+ request.size());
+ EXPECT_EQ(0, requestString.find(kRequestPrefix));
+ EXPECT_EQ(requestString.size() - kRequestSuffix.size(),
+ requestString.find(kRequestSuffix));
+ for (size_t i = 0; i < expectedKeys.size(); ++i) {
+ AString encodedIdAString;
+ android::encodeBase64(expectedKeys[i], kKeyIdSize,
+ &encodedIdAString);
+ String8 encodedId(encodedIdAString.c_str());
+ encodedId.removeAll(kBase64Padding);
+ EXPECT_TRUE(requestString.contains(encodedId));
+ }
+ }
+
+ void attemptParseExpectingFailure(const Vector<uint8_t>& initData,
+ const String8& initDataType) {
+ Vector<uint8_t> request;
+ ASSERT_NE(android::OK, attemptParse(initData, initDataType, &request));
+ EXPECT_EQ(0, request.size());
+ }
+};
+
+TEST_F(InitDataParserTest, ParsesSingleKeyPssh) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+
+ attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesMultipleKeyPssh) {
+ uint8_t pssh[84] = {
+ 0, 0, 0, 84, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 3, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, // Key ID #2
+ 0x43, 0x6c, 0x65, 0x61, 0x72, 0x4b, 0x65, 0x79, // "ClearKeyClearKey"
+ 0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, // Key ID #3
+ 0x20, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x20, // " GOOGLE GOOGLE "
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 84);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+ expectedKeys.push(String8("ClearKeyClearKey"));
+ expectedKeys.push(String8(" GOOGLE GOOGLE "));
+
+ attemptParseExpectingSuccess(initData, kCencType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, ParsesWebM) {
+ uint8_t initDataRaw[16] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(initDataRaw, 16);
+
+ Vector<String8> expectedKeys;
+ expectedKeys.push(String8("01234567890ABCDE"));
+
+ attemptParseExpectingSuccess(initData, kWebMType, expectedKeys);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshTooSmall) {
+ uint8_t pssh[16] = {
+ 0, 0, 0, 52,
+ 'p', 's', 's', 'h',
+ 1, 0, 0, 0,
+ 0x10, 0x77, 0xef, 0xec
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 16);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForWebMTooSmall) {
+ uint8_t initDataRaw[8] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(initDataRaw, 8);
+
+ attemptParseExpectingFailure(initData, kWebMType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSystemId) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, // System ID
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadSize) {
+ uint8_t pssh[52] = {
+ 0, 0, 70, 200, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshWrongVersion) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 0, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 1, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+TEST_F(InitDataParserTest, FailsForPsshBadKeyCount) {
+ uint8_t pssh[52] = {
+ 0, 0, 0, 52, // Total Size
+ 'p', 's', 's', 'h', // PSSH
+ 1, 0, 0, 0, // Version
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // System ID
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ 0, 0, 0, 7, // Key Count
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Key ID #1
+ 0x38, 0x39, 0x30, 0x41, 0x42, 0x43, 0x44, 0x45, // "01234567890ABCDE"
+ 0, 0, 0, 0 // Data Size (always 0)
+ };
+ Vector<uint8_t> initData;
+ initData.appendArray(pssh, 52);
+
+ attemptParseExpectingFailure(initData, kCencType);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
new file mode 100644
index 0000000..c3b0d84
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+#include "gtest/gtest.h"
+#include "Utils.h"
+
+namespace clearkeydrm {
+using android::String8;
+using android::Vector;
+
+class JsonWebKeyTest : public ::testing::Test {
+ protected:
+ JsonWebKey* jwk;
+
+ JsonWebKeyTest() {
+ jwk = new JsonWebKey;
+ }
+
+ virtual ~JsonWebKeyTest() {
+ if (jwk)
+ delete jwk;
+ }
+};
+
+void stringFromVector(const Vector<uint8_t>& input,
+ String8* converted) {
+ converted->clear();
+ if (input.isEmpty()) {
+ return;
+ }
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ converted->appendFormat("%c", input.itemAt(i));
+ }
+}
+
+void verifyKeys(const KeyMap& keys, const String8* clearKeys) {
+ if (keys.isEmpty()) {
+ return;
+ }
+
+ String8 keyString;
+ for (size_t i = 0; i < keys.size(); ++i) {
+ stringFromVector(keys.valueAt(i), &keyString);
+ EXPECT_EQ(keyString, clearKeys[i]);
+ }
+}
+
+TEST_F(JsonWebKeyTest, NoSymmetricKey) {
+ const String8 js(
+ "{"
+ "[{"
+ "\"kty\":\"rsa\","
+ "\"alg\":\"A128KW1\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+ "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeysTag) {
+ const String8 js(
+ "{"
+ "[{"
+ "\"kty\":\"oct\","
+ "\"alg\":\"A128KW1\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\","
+ "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\""
+ "},"
+ "{"
+ "\"kty\":\"oct\","
+ "\"alg\":\"A128KW2\","
+ "\"k\":\"R29vZCBkYXkh\","
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, NoKeyId) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, NoKey) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"kid\":\"`\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, MalformedKey) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":"
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ ":\"GawgguFyGrWKav7AX4V???\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.size() == 1);
+
+ const String8 clearKeys("Good day!");
+ verifyKeys(keys, &clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, EmptyJsonWebKey) {
+ const String8 js;
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, MalformedJsonWebKey) {
+ // Missing begin array '['
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"GawgguFyGrWKav7AX4VKUg\""
+ "\"kid\":\"67ef0gd8pvfd0=\""
+ "}"
+ "]"
+ "}");
+
+ KeyMap keys;
+ EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys));
+ EXPECT_TRUE(keys.isEmpty());
+}
+
+TEST_F(JsonWebKeyTest, SameKeyId) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 2);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!"), String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractWellFormedKeys) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 2);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!"), String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+TEST_F(JsonWebKeyTest, ExtractKeys) {
+ const String8 js(
+ "{"
+ "\"keys\":"
+ "[{"
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
+ "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW1\""
+ "}"
+ "{"
+ "\"kty\":\"oct\""
+ "\"alg\":\"A128KW2\""
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
+ "}"
+ "{"
+ "\"kty\":\"rsa\""
+ "\"alg\":\"A128KW-rsa\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kid\":\"rsa-67ef0gd8pvfd0=\""
+ "}"
+ "{"
+ "\"alg\":\"A128KW3\""
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
+ "\"k\":\"R29vZCBkYXkh\""
+ "\"kty\":\"oct\""
+ "}]"
+ "}");
+
+ KeyMap keys;
+ jwk->extractKeysFromJsonWebKeySet(js, &keys);
+ EXPECT_TRUE(keys.size() == 3);
+
+ const String8 clearKeys[] =
+ { String8("Hello Friend!!"), String8("Hello Friend!"),
+ String8("Good day!") };
+ verifyKeys(keys, clearKeys);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 69fa7a0..6efc712 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -45,7 +45,7 @@ namespace android {
// MockDrmFactory
bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16])
{
- return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+ return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
}
bool MockDrmFactory::isContentTypeSupported(const String8 &mimeType)
@@ -65,7 +65,7 @@ namespace android {
// MockCryptoFactory
bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const
{
- return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
+ return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
}
status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
@@ -254,7 +254,9 @@ namespace android {
return OK;
}
- status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request,
+ status_t MockDrmPlugin::getProvisionRequest(String8 const &certType,
+ String8 const &certAuthority,
+ Vector<uint8_t> &request,
String8 &defaultUrl)
{
Mutex::Autolock lock(mLock);
@@ -282,7 +284,9 @@ namespace android {
return OK;
}
- status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response)
+ status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response,
+ Vector<uint8_t> &certificate,
+ Vector<uint8_t> &wrappedKey)
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
@@ -600,6 +604,33 @@ namespace android {
return OK;
}
+ status_t MockDrmPlugin::signRSA(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &wrappedKey,
+ Vector<uint8_t> &signature)
+ {
+ Mutex::Autolock lock(mLock);
+ ALOGD("MockDrmPlugin::signRSA(sessionId=%s, algorithm=%s, keyId=%s, "
+ "message=%s, signature=%s)",
+ vectorToString(sessionId).string(),
+ algorithm.string(),
+ vectorToString(message).string(),
+ vectorToString(wrappedKey).string(),
+ vectorToString(signature).string());
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] wrappedKey -> mock-wrappedkey
+ // byte[] message -> mock-message
+ // byte[] signature -> mock-signature
+ mByteArrayProperties.add(String8("mock-sessionid"), sessionId);
+ mStringProperties.add(String8("mock-algorithm"), algorithm);
+ mByteArrayProperties.add(String8("mock-message"), message);
+ mByteArrayProperties.add(String8("mock-wrappedkey"), wrappedKey);
+ mByteArrayProperties.add(String8("mock-signature"), signature);
+ return OK;
+ }
+
ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
{
ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
index 2297f9b..97d7052 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
@@ -76,10 +76,14 @@ namespace android {
status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
KeyedVector<String8, String8> &infoMap) const;
- status_t getProvisionRequest(Vector<uint8_t> &request,
- String8 &defaultUrl);
+ status_t getProvisionRequest(String8 const &certType,
+ String8 const &certAuthority,
+ Vector<uint8_t> &request,
+ String8 &defaultUrl);
- status_t provideProvisionResponse(Vector<uint8_t> const &response);
+ status_t provideProvisionResponse(Vector<uint8_t> const &response,
+ Vector<uint8_t> &certificate,
+ Vector<uint8_t> &wrappedKey);
status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
@@ -122,6 +126,12 @@ namespace android {
Vector<uint8_t> const &signature,
bool &match);
+ status_t signRSA(Vector<uint8_t> const &sessionId,
+ String8 const &algorithm,
+ Vector<uint8_t> const &message,
+ Vector<uint8_t> const &wrappedKey,
+ Vector<uint8_t> &signature);
+
private:
String8 vectorToString(Vector<uint8_t> const &vector) const;
String8 arrayToString(uint8_t const *array, size_t len) const;