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 
parse(const Vector<uint8_t> & initData,const String8 & initDataType,Vector<uint8_t> * licenseRequest)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 
parsePssh(const Vector<uint8_t> & initData,Vector<const uint8_t * > * keyIds)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 
generateRequest(const Vector<const uint8_t * > & keyIds)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