1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "ClearKeyCryptoPlugin" 19 #include <utils/Log.h> 20 21 #include <endian.h> 22 #include <media/stagefright/foundation/AString.h> 23 #include <media/stagefright/foundation/base64.h> 24 #include <media/stagefright/MediaErrors.h> 25 #include <string.h> 26 27 #include "InitDataParser.h" 28 29 #include "ClearKeyUUID.h" 30 #include "Utils.h" 31 32 namespace clearkeydrm { 33 34 using android::AString; 35 using android::String8; 36 using android::Vector; 37 38 namespace { 39 const size_t kKeyIdSize = 16; 40 const size_t kSystemIdSize = 16; 41 } 42 43 android::status_t InitDataParser::parse(const Vector<uint8_t>& initData, 44 const String8& initDataType, 45 Vector<uint8_t>* licenseRequest) { 46 // Build a list of the key IDs 47 Vector<const uint8_t*> keyIds; 48 if (initDataType == "cenc") { 49 android::status_t res = parsePssh(initData, &keyIds); 50 if (res != android::OK) { 51 return res; 52 } 53 } else if (initDataType == "webm") { 54 // WebM "init data" is just a single key ID 55 if (initData.size() != kKeyIdSize) { 56 return android::ERROR_DRM_CANNOT_HANDLE; 57 } 58 keyIds.push(initData.array()); 59 } else { 60 return android::ERROR_DRM_CANNOT_HANDLE; 61 } 62 63 // Build the request 64 String8 requestJson = generateRequest(keyIds); 65 licenseRequest->clear(); 66 licenseRequest->appendArray( 67 reinterpret_cast<const uint8_t*>(requestJson.string()), 68 requestJson.size()); 69 return android::OK; 70 } 71 72 android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData, 73 Vector<const uint8_t*>* keyIds) { 74 size_t readPosition = 0; 75 76 // Validate size field 77 uint32_t expectedSize = initData.size(); 78 expectedSize = htonl(expectedSize); 79 if (memcmp(&initData[readPosition], &expectedSize, 80 sizeof(expectedSize)) != 0) { 81 return android::ERROR_DRM_CANNOT_HANDLE; 82 } 83 readPosition += sizeof(expectedSize); 84 85 // Validate PSSH box identifier 86 const char psshIdentifier[4] = {'p', 's', 's', 'h'}; 87 if (memcmp(&initData[readPosition], psshIdentifier, 88 sizeof(psshIdentifier)) != 0) { 89 return android::ERROR_DRM_CANNOT_HANDLE; 90 } 91 readPosition += sizeof(psshIdentifier); 92 93 // Validate EME version number 94 const uint8_t psshVersion1[4] = {1, 0, 0, 0}; 95 if (memcmp(&initData[readPosition], psshVersion1, 96 sizeof(psshVersion1)) != 0) { 97 return android::ERROR_DRM_CANNOT_HANDLE; 98 } 99 readPosition += sizeof(psshVersion1); 100 101 // Validate system ID 102 if (!isClearKeyUUID(&initData[readPosition])) { 103 return android::ERROR_DRM_CANNOT_HANDLE; 104 } 105 readPosition += kSystemIdSize; 106 107 // Read key ID count 108 uint32_t keyIdCount; 109 memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount)); 110 keyIdCount = ntohl(keyIdCount); 111 readPosition += sizeof(keyIdCount); 112 if (readPosition + (keyIdCount * kKeyIdSize) != 113 initData.size() - sizeof(uint32_t)) { 114 return android::ERROR_DRM_CANNOT_HANDLE; 115 } 116 117 // Calculate the key ID offsets 118 for (uint32_t i = 0; i < keyIdCount; ++i) { 119 size_t keyIdPosition = readPosition + (i * kKeyIdSize); 120 keyIds->push(&initData[keyIdPosition]); 121 } 122 return android::OK; 123 } 124 125 String8 InitDataParser::generateRequest(const Vector<const uint8_t*>& keyIds) { 126 const String8 kRequestPrefix("{\"kids\":["); 127 const String8 kRequestSuffix("],\"type\":\"temporary\"}"); 128 const String8 kBase64Padding("="); 129 130 String8 request(kRequestPrefix); 131 AString encodedId; 132 for (size_t i = 0; i < keyIds.size(); ++i) { 133 encodedId.clear(); 134 android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId); 135 if (i != 0) { 136 request.append(","); 137 } 138 request.appendFormat("\"%s\"", encodedId.c_str()); 139 } 140 request.append(kRequestSuffix); 141 142 // Android's Base64 encoder produces padding. EME forbids padding. 143 request.removeAll(kBase64Padding); 144 return request; 145 } 146 147 } // namespace clearkeydrm 148