From 6b3c1473199927264691bb50445cf0b35c2f8892 Mon Sep 17 00:00:00 2001 From: "John \"Juce\" Bruce" Date: Tue, 10 Jun 2014 21:12:56 -0700 Subject: Implement ClearKey Init Data Parser Implements an object that can convert ISO-CENC or WebM initialization data into a ClearKey license request. Change-Id: Ib95012afcf40fc9e3f45510a468c305fb7bc216e --- drm/mediadrm/plugins/clearkey/Android.mk | 1 + drm/mediadrm/plugins/clearkey/InitDataParser.cpp | 147 +++++++++++++++++++++++ drm/mediadrm/plugins/clearkey/InitDataParser.h | 47 ++++++++ 3 files changed, 195 insertions(+) create mode 100644 drm/mediadrm/plugins/clearkey/InitDataParser.cpp create mode 100644 drm/mediadrm/plugins/clearkey/InitDataParser.h (limited to 'drm') diff --git a/drm/mediadrm/plugins/clearkey/Android.mk b/drm/mediadrm/plugins/clearkey/Android.mk index 59e992b..9cc81e8 100644 --- a/drm/mediadrm/plugins/clearkey/Android.mk +++ b/drm/mediadrm/plugins/clearkey/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ AesCtrDecryptor.cpp \ + InitDataParser.cpp \ JsonWebKey.cpp \ Utils.cpp \ 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 + +#include +#include +#include +#include +#include + +#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& initData, + const String8& initDataType, + Vector* licenseRequest) { + // Build a list of the key IDs + Vector 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(requestJson.string()), + requestJson.size()); + return android::OK; +} + +android::status_t InitDataParser::parsePssh(const Vector& initData, + Vector* 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& 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 +#include +#include +#include + +namespace clearkeydrm { + +class InitDataParser { +public: + InitDataParser() {} + + android::status_t parse(const android::Vector& initData, + const android::String8& initDataType, + android::Vector* licenseRequest); + +private: + DISALLOW_EVIL_CONSTRUCTORS(InitDataParser); + + android::status_t parsePssh(const android::Vector& initData, + android::Vector* keyIds); + + android::String8 generateRequest( + const android::Vector& keyIds); +}; + +} // namespace clearkeydrm + +#endif // CLEARKEY_INIT_DATA_PARSER_H_ -- cgit v1.1