Home | History | Annotate | Download | only in clearkey
      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