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 "MimeType.h" 31#include "Utils.h" 32 33namespace clearkeydrm { 34 35using android::AString; 36using android::String8; 37using android::Vector; 38 39namespace { 40 const size_t kKeyIdSize = 16; 41 const size_t kSystemIdSize = 16; 42} 43 44android::status_t InitDataParser::parse(const Vector<uint8_t>& initData, 45 const String8& type, 46 Vector<uint8_t>* licenseRequest) { 47 // Build a list of the key IDs 48 Vector<const uint8_t*> keyIds; 49 if (type == kIsoBmffVideoMimeType || 50 type == kIsoBmffAudioMimeType || 51 type == kCencInitDataFormat) { 52 android::status_t res = parsePssh(initData, &keyIds); 53 if (res != android::OK) { 54 return res; 55 } 56 } else if (type == kWebmVideoMimeType || 57 type == kWebmAudioMimeType || 58 type == kWebmInitDataFormat) { 59 // WebM "init data" is just a single key ID 60 if (initData.size() != kKeyIdSize) { 61 return android::ERROR_DRM_CANNOT_HANDLE; 62 } 63 keyIds.push(initData.array()); 64 } else { 65 return android::ERROR_DRM_CANNOT_HANDLE; 66 } 67 68 // Build the request 69 String8 requestJson = generateRequest(keyIds); 70 licenseRequest->clear(); 71 licenseRequest->appendArray( 72 reinterpret_cast<const uint8_t*>(requestJson.string()), 73 requestJson.size()); 74 return android::OK; 75} 76 77android::status_t InitDataParser::parsePssh(const Vector<uint8_t>& initData, 78 Vector<const uint8_t*>* keyIds) { 79 size_t readPosition = 0; 80 81 // Validate size field 82 uint32_t expectedSize = initData.size(); 83 expectedSize = htonl(expectedSize); 84 if (memcmp(&initData[readPosition], &expectedSize, 85 sizeof(expectedSize)) != 0) { 86 return android::ERROR_DRM_CANNOT_HANDLE; 87 } 88 readPosition += sizeof(expectedSize); 89 90 // Validate PSSH box identifier 91 const char psshIdentifier[4] = {'p', 's', 's', 'h'}; 92 if (memcmp(&initData[readPosition], psshIdentifier, 93 sizeof(psshIdentifier)) != 0) { 94 return android::ERROR_DRM_CANNOT_HANDLE; 95 } 96 readPosition += sizeof(psshIdentifier); 97 98 // Validate EME version number 99 const uint8_t psshVersion1[4] = {1, 0, 0, 0}; 100 if (memcmp(&initData[readPosition], psshVersion1, 101 sizeof(psshVersion1)) != 0) { 102 return android::ERROR_DRM_CANNOT_HANDLE; 103 } 104 readPosition += sizeof(psshVersion1); 105 106 // Validate system ID 107 if (!isClearKeyUUID(&initData[readPosition])) { 108 return android::ERROR_DRM_CANNOT_HANDLE; 109 } 110 readPosition += kSystemIdSize; 111 112 // Read key ID count 113 uint32_t keyIdCount; 114 memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount)); 115 keyIdCount = ntohl(keyIdCount); 116 readPosition += sizeof(keyIdCount); 117 if (readPosition + ((uint64_t)keyIdCount * kKeyIdSize) != 118 initData.size() - sizeof(uint32_t)) { 119 return android::ERROR_DRM_CANNOT_HANDLE; 120 } 121 122 // Calculate the key ID offsets 123 for (uint32_t i = 0; i < keyIdCount; ++i) { 124 size_t keyIdPosition = readPosition + (i * kKeyIdSize); 125 keyIds->push(&initData[keyIdPosition]); 126 } 127 return android::OK; 128} 129 130String8 InitDataParser::generateRequest(const Vector<const uint8_t*>& keyIds) { 131 const String8 kRequestPrefix("{\"kids\":["); 132 const String8 kRequestSuffix("],\"type\":\"temporary\"}"); 133 const String8 kBase64Padding("="); 134 135 String8 request(kRequestPrefix); 136 AString encodedId; 137 for (size_t i = 0; i < keyIds.size(); ++i) { 138 encodedId.clear(); 139 android::encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId); 140 if (i != 0) { 141 request.append(","); 142 } 143 request.appendFormat("\"%s\"", encodedId.c_str()); 144 } 145 request.append(kRequestSuffix); 146 147 // Android's Base64 encoder produces padding. EME forbids padding. 148 request.removeAll(kBase64Padding); 149 return request; 150} 151 152} // namespace clearkeydrm 153