tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ClearKeyUtils.cpp (17206B)


      1 /*
      2 * Copyright 2015, Mozilla Foundation and contributors
      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 #include "ClearKeyUtils.h"
     18 
     19 #include <assert.h>
     20 #include <ctype.h>
     21 #include <memory.h>
     22 #include <stdarg.h>
     23 #include <stdint.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 
     27 #include <algorithm>
     28 #include <cctype>
     29 #include <memory>
     30 #include <sstream>
     31 #include <vector>
     32 
     33 #include "BigEndian.h"
     34 #include "ClearKeyBase64.h"
     35 #include "mozilla/Sprintf.h"
     36 #include "pk11pub.h"
     37 #include "prerror.h"
     38 #include "psshparser/PsshParser.h"
     39 #include "secmodt.h"
     40 
     41 using namespace cdm;
     42 using std::string;
     43 using std::stringstream;
     44 using std::vector;
     45 
     46 struct DeleteHelper {
     47  void operator()(PK11Context* value) { PK11_DestroyContext(value, true); }
     48  void operator()(PK11SlotInfo* value) { PK11_FreeSlot(value); }
     49  void operator()(PK11SymKey* value) { PK11_FreeSymKey(value); }
     50 };
     51 
     52 template <class T>
     53 struct MaybeDeleteHelper {
     54  void operator()(T* ptr) {
     55    if (ptr) {
     56      DeleteHelper del;
     57      del(ptr);
     58    }
     59  }
     60 };
     61 
     62 void CK_Log(const char* aFmt, ...) {
     63  FILE* out = stdout;
     64 
     65  if (getenv("CLEARKEY_LOG_FILE")) {
     66    out = fopen(getenv("CLEARKEY_LOG_FILE"), "a");
     67  }
     68 
     69  va_list ap;
     70 
     71  va_start(ap, aFmt);
     72  const size_t len = 1024;
     73  char buf[len];
     74  VsprintfLiteral(buf, aFmt, ap);
     75  va_end(ap);
     76 
     77  fprintf(out, "%s\n", buf);
     78  fflush(out);
     79 
     80  if (out != stdout) {
     81    fclose(out);
     82  }
     83 }
     84 
     85 static bool PrintableAsString(const uint8_t* aBytes, uint32_t aLength) {
     86  return std::all_of(aBytes, aBytes + aLength,
     87                     [](uint8_t c) { return isprint(c) == 1; });
     88 }
     89 
     90 void CK_LogArray(const char* prepend, const uint8_t* aData,
     91                 const uint32_t aDataSize) {
     92  // If the data is valid ascii, use that. Otherwise print the hex
     93  string data = PrintableAsString(aData, aDataSize)
     94                    ? string(aData, aData + aDataSize)
     95                    : ClearKeyUtils::ToHexString(aData, aDataSize);
     96 
     97  CK_LOGD("%s%s", prepend, data.c_str());
     98 }
     99 
    100 /* static */
    101 bool ClearKeyUtils::DecryptCbcs(const vector<uint8_t>& aKey,
    102                                const vector<uint8_t>& aIV,
    103                                mozilla::Span<uint8_t> aSubsample,
    104                                uint32_t aCryptByteBlock,
    105                                uint32_t aSkipByteBlock) {
    106  if (aKey.size() != CENC_KEY_LEN || aIV.size() != CENC_KEY_LEN) {
    107    CK_LOGE("Key and IV size should be 16!");
    108    return false;
    109  }
    110 
    111  if (aSubsample.Length() == 0) {
    112    // Nothing to decrypt.
    113    return true;
    114  }
    115 
    116  std::unique_ptr<PK11SlotInfo, MaybeDeleteHelper<PK11SlotInfo>> slot(
    117      PK11_GetInternalKeySlot());
    118 
    119  if (!slot.get()) {
    120    CK_LOGE("Failed to get internal PK11 slot");
    121    return false;
    122  }
    123 
    124  SECItem keyItem = {siBuffer, (unsigned char*)aKey.data(), CENC_KEY_LEN};
    125  SECItem ivItem = {siBuffer, (unsigned char*)aIV.data(), CENC_KEY_LEN};
    126 
    127  std::unique_ptr<PK11SymKey, MaybeDeleteHelper<PK11SymKey>> key(
    128      PK11_ImportSymKey(slot.get(), CKM_AES_CBC, PK11_OriginUnwrap, CKA_DECRYPT,
    129                        &keyItem, nullptr));
    130 
    131  if (!key.get()) {
    132    CK_LOGE("Failed to import sym key");
    133    return false;
    134  }
    135 
    136  std::unique_ptr<PK11Context, MaybeDeleteHelper<PK11Context>> ctx(
    137      PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_DECRYPT, key.get(), &ivItem));
    138 
    139  if (!ctx) {
    140    CK_LOGE("Failed to get PK11Context!");
    141    return false;
    142  }
    143 
    144  assert(aCryptByteBlock <= 0xFF);
    145  assert(aSkipByteBlock <= 0xFF);
    146 
    147  uint8_t* encryptedSubsample = aSubsample.data();
    148  const uint32_t BLOCK_SIZE = 16;
    149  const uint32_t skipBytes = aSkipByteBlock * BLOCK_SIZE;
    150  const uint32_t totalBlocks = aSubsample.size() / BLOCK_SIZE;
    151  uint32_t blocksProcessed = 0;
    152 
    153  if (aSkipByteBlock == 0) {
    154    // ISO/IEC 23001 - 7 Section 9.6.1
    155    // 'When the fields default_crypt_byte_block and default_skip_byte_block in
    156    // a version 1 Track Encryption Box('tenc') are non - zero numbers, pattern
    157    // encryption SHALL be applied.'
    158    // So if both are 0, then everything is encrypted. Similarly, if skip is 0
    159    // and crypt is non-0, everything is encrypted.
    160    // In this case we can just decrypt all the blocks in one call. This is the
    161    // same outcome as decrypting them using smaller steps, as either way the
    162    // CBC result should be the same.
    163    MOZ_ASSERT(skipBytes == 0);
    164    aCryptByteBlock = totalBlocks;
    165  }
    166 
    167  while (blocksProcessed < totalBlocks) {
    168    uint32_t blocksToDecrypt = aCryptByteBlock <= totalBlocks - blocksProcessed
    169                                   ? aCryptByteBlock
    170                                   : totalBlocks - blocksProcessed;
    171    uint32_t bytesToDecrypt = blocksToDecrypt * BLOCK_SIZE;
    172    int outLen;
    173    SECStatus rv;
    174    rv = PK11_CipherOp(ctx.get(), encryptedSubsample, &outLen, bytesToDecrypt,
    175                       encryptedSubsample, bytesToDecrypt);
    176    if (rv != SECSuccess) {
    177      CK_LOGE("PK11_CipherOp() failed");
    178      return false;
    179    }
    180 
    181    encryptedSubsample += skipBytes + bytesToDecrypt;
    182    blocksProcessed += aSkipByteBlock + blocksToDecrypt;
    183  }
    184 
    185  return true;
    186 }
    187 
    188 /* static */
    189 bool ClearKeyUtils::DecryptAES(const vector<uint8_t>& aKey,
    190                               vector<uint8_t>& aData, vector<uint8_t>& aIV) {
    191  assert(aIV.size() == CENC_KEY_LEN);
    192  assert(aKey.size() == CENC_KEY_LEN);
    193 
    194  PK11SlotInfo* slot = PK11_GetInternalKeySlot();
    195  if (!slot) {
    196    CK_LOGE("Failed to get internal PK11 slot");
    197    return false;
    198  }
    199 
    200  SECItem keyItem = {siBuffer, (unsigned char*)aKey.data(), CENC_KEY_LEN};
    201  PK11SymKey* key = PK11_ImportSymKey(slot, CKM_AES_CTR, PK11_OriginUnwrap,
    202                                      CKA_ENCRYPT, &keyItem, nullptr);
    203  PK11_FreeSlot(slot);
    204  if (!key) {
    205    CK_LOGE("Failed to import sym key");
    206    return false;
    207  }
    208 
    209  CK_AES_CTR_PARAMS params;
    210  params.ulCounterBits = 32;
    211  memcpy(&params.cb, aIV.data(), CENC_KEY_LEN);
    212  SECItem paramItem = {siBuffer, (unsigned char*)&params,
    213                       sizeof(CK_AES_CTR_PARAMS)};
    214 
    215  unsigned int outLen = 0;
    216  auto rv = PK11_Decrypt(key, CKM_AES_CTR, &paramItem, aData.data(), &outLen,
    217                         aData.size(), aData.data(), aData.size());
    218 
    219  aData.resize(outLen);
    220  PK11_FreeSymKey(key);
    221 
    222  if (rv != SECSuccess) {
    223    CK_LOGE("PK11_Decrypt() failed");
    224    return false;
    225  }
    226 
    227  return true;
    228 }
    229 
    230 /**
    231 * ClearKey expects all Key IDs to be base64 encoded with non-standard alphabet
    232 * and padding.
    233 */
    234 static bool EncodeBase64Web(vector<uint8_t> aBinary, string& aEncoded) {
    235  const char sAlphabet[] =
    236      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
    237  const uint8_t sMask = 0x3f;
    238 
    239  aEncoded.resize((aBinary.size() * 8 + 5) / 6);
    240 
    241  // Pad binary data in case there's rubbish past the last byte.
    242  aBinary.push_back(0);
    243 
    244  // Number of bytes not consumed in the previous character
    245  uint32_t shift = 0;
    246 
    247  auto out = aEncoded.begin();
    248  auto data = aBinary.begin();
    249  for (string::size_type i = 0; i < aEncoded.length(); i++) {
    250    if (shift) {
    251      out[i] = (*data << (6 - shift)) & sMask;
    252      data++;
    253    } else {
    254      out[i] = 0;
    255    }
    256 
    257    out[i] += (*data >> (shift + 2)) & sMask;
    258    shift = (shift + 2) % 8;
    259 
    260    // Cast idx to size_t before using it as an array-index,
    261    // to pacify clang 'Wchar-subscripts' warning:
    262    size_t idx = static_cast<size_t>(out[i]);
    263 
    264    // out of bounds index for 'sAlphabet'
    265    assert(idx < std::size(sAlphabet));
    266    out[i] = sAlphabet[idx];
    267  }
    268 
    269  return true;
    270 }
    271 
    272 /* static */
    273 void ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
    274                                   string& aOutRequest,
    275                                   SessionType aSessionType) {
    276  assert(!aKeyIDs.empty() && aOutRequest.empty());
    277 
    278  aOutRequest.append("{\"kids\":[");
    279  for (size_t i = 0; i < aKeyIDs.size(); i++) {
    280    if (i) {
    281      aOutRequest.append(",");
    282    }
    283    aOutRequest.append("\"");
    284 
    285    string base64key;
    286    EncodeBase64Web(aKeyIDs[i], base64key);
    287    aOutRequest.append(base64key);
    288 
    289    aOutRequest.append("\"");
    290  }
    291  aOutRequest.append("],\"type\":");
    292 
    293  aOutRequest.append("\"");
    294  aOutRequest.append(SessionTypeToString(aSessionType));
    295  aOutRequest.append("\"}");
    296 }
    297 
    298 #define EXPECT_SYMBOL(CTX, X)                     \
    299  do {                                            \
    300    if (GetNextSymbol(CTX) != (X)) {              \
    301      CK_LOGE("Unexpected symbol in JWK parser"); \
    302      return false;                               \
    303    }                                             \
    304  } while (false)
    305 
    306 struct ParserContext {
    307  const uint8_t* mIter;
    308  const uint8_t* mEnd;
    309 };
    310 
    311 static uint8_t PeekSymbol(ParserContext& aCtx) {
    312  for (; aCtx.mIter < aCtx.mEnd; (aCtx.mIter)++) {
    313    if (!isspace(*aCtx.mIter)) {
    314      return *aCtx.mIter;
    315    }
    316  }
    317 
    318  return 0;
    319 }
    320 
    321 static uint8_t GetNextSymbol(ParserContext& aCtx) {
    322  uint8_t sym = PeekSymbol(aCtx);
    323  aCtx.mIter++;
    324  return sym;
    325 }
    326 
    327 static bool SkipToken(ParserContext& aCtx);
    328 
    329 static bool SkipString(ParserContext& aCtx) {
    330  EXPECT_SYMBOL(aCtx, '"');
    331  for (uint8_t sym = GetNextSymbol(aCtx); sym; sym = GetNextSymbol(aCtx)) {
    332    if (sym == '\\') {
    333      sym = GetNextSymbol(aCtx);
    334      if (!sym) {
    335        return false;
    336      }
    337    } else if (sym == '"') {
    338      return true;
    339    }
    340  }
    341 
    342  return false;
    343 }
    344 
    345 /**
    346 * Skip whole object and values it contains.
    347 */
    348 static bool SkipObject(ParserContext& aCtx) {
    349  EXPECT_SYMBOL(aCtx, '{');
    350 
    351  if (PeekSymbol(aCtx) == '}') {
    352    GetNextSymbol(aCtx);
    353    return true;
    354  }
    355 
    356  while (true) {
    357    if (!SkipString(aCtx)) return false;
    358    EXPECT_SYMBOL(aCtx, ':');
    359    if (!SkipToken(aCtx)) return false;
    360 
    361    if (PeekSymbol(aCtx) == '}') {
    362      GetNextSymbol(aCtx);
    363      return true;
    364    }
    365    EXPECT_SYMBOL(aCtx, ',');
    366  }
    367 }
    368 
    369 /**
    370 * Skip array value and the values it contains.
    371 */
    372 static bool SkipArray(ParserContext& aCtx) {
    373  EXPECT_SYMBOL(aCtx, '[');
    374 
    375  if (PeekSymbol(aCtx) == ']') {
    376    GetNextSymbol(aCtx);
    377    return true;
    378  }
    379 
    380  while (SkipToken(aCtx)) {
    381    if (PeekSymbol(aCtx) == ']') {
    382      GetNextSymbol(aCtx);
    383      return true;
    384    }
    385    EXPECT_SYMBOL(aCtx, ',');
    386  }
    387 
    388  return false;
    389 }
    390 
    391 /**
    392 * Skip unquoted literals like numbers, |true|, and |null|.
    393 * (XXX and anything else that matches /([:alnum:]|[+-.])+/)
    394 */
    395 static bool SkipLiteral(ParserContext& aCtx) {
    396  for (; aCtx.mIter < aCtx.mEnd; aCtx.mIter++) {
    397    if (!isalnum(*aCtx.mIter) && *aCtx.mIter != '.' && *aCtx.mIter != '-' &&
    398        *aCtx.mIter != '+') {
    399      return true;
    400    }
    401  }
    402 
    403  return false;
    404 }
    405 
    406 static bool SkipToken(ParserContext& aCtx) {
    407  uint8_t startSym = PeekSymbol(aCtx);
    408  if (startSym == '"') {
    409    CK_LOGD("JWK parser skipping string");
    410    return SkipString(aCtx);
    411  } else if (startSym == '{') {
    412    CK_LOGD("JWK parser skipping object");
    413    return SkipObject(aCtx);
    414  } else if (startSym == '[') {
    415    CK_LOGD("JWK parser skipping array");
    416    return SkipArray(aCtx);
    417  } else {
    418    CK_LOGD("JWK parser skipping literal");
    419    return SkipLiteral(aCtx);
    420  }
    421 }
    422 
    423 static bool GetNextLabel(ParserContext& aCtx, string& aOutLabel) {
    424  EXPECT_SYMBOL(aCtx, '"');
    425 
    426  const uint8_t* start = aCtx.mIter;
    427  for (uint8_t sym = GetNextSymbol(aCtx); sym; sym = GetNextSymbol(aCtx)) {
    428    if (sym == '\\') {
    429      GetNextSymbol(aCtx);
    430      continue;
    431    }
    432 
    433    if (sym == '"') {
    434      aOutLabel.assign(start, aCtx.mIter - 1);
    435      return true;
    436    }
    437  }
    438 
    439  return false;
    440 }
    441 
    442 static bool ParseKeyObject(ParserContext& aCtx, KeyIdPair& aOutKey) {
    443  EXPECT_SYMBOL(aCtx, '{');
    444 
    445  // Reject empty objects as invalid licenses.
    446  if (PeekSymbol(aCtx) == '}') {
    447    GetNextSymbol(aCtx);
    448    return false;
    449  }
    450 
    451  string keyId;
    452  string key;
    453 
    454  while (true) {
    455    string label;
    456    string value;
    457 
    458    if (!GetNextLabel(aCtx, label)) {
    459      return false;
    460    }
    461 
    462    EXPECT_SYMBOL(aCtx, ':');
    463    if (label == "kty") {
    464      if (!GetNextLabel(aCtx, value)) return false;
    465      // By spec, type must be "oct".
    466      if (value != "oct") return false;
    467    } else if (label == "k" && PeekSymbol(aCtx) == '"') {
    468      // if this isn't a string we will fall through to the SkipToken() path.
    469      if (!GetNextLabel(aCtx, key)) return false;
    470    } else if (label == "kid" && PeekSymbol(aCtx) == '"') {
    471      if (!GetNextLabel(aCtx, keyId)) return false;
    472    } else {
    473      if (!SkipToken(aCtx)) return false;
    474    }
    475 
    476    uint8_t sym = PeekSymbol(aCtx);
    477    if (!sym || sym == '}') {
    478      break;
    479    }
    480    EXPECT_SYMBOL(aCtx, ',');
    481  }
    482 
    483  return !key.empty() && !keyId.empty() &&
    484         DecodeBase64(keyId, aOutKey.mKeyId) &&
    485         DecodeBase64(key, aOutKey.mKey) && GetNextSymbol(aCtx) == '}';
    486 }
    487 
    488 static bool ParseKeys(ParserContext& aCtx, vector<KeyIdPair>& aOutKeys) {
    489  // Consume start of array.
    490  EXPECT_SYMBOL(aCtx, '[');
    491 
    492  while (true) {
    493    KeyIdPair key;
    494    if (!ParseKeyObject(aCtx, key)) {
    495      CK_LOGE("Failed to parse key object");
    496      return false;
    497    }
    498 
    499    assert(!key.mKey.empty() && !key.mKeyId.empty());
    500    aOutKeys.push_back(key);
    501 
    502    uint8_t sym = PeekSymbol(aCtx);
    503    if (!sym || sym == ']') {
    504      break;
    505    }
    506 
    507    EXPECT_SYMBOL(aCtx, ',');
    508  }
    509 
    510  return GetNextSymbol(aCtx) == ']';
    511 }
    512 
    513 /* static */
    514 bool ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
    515                             vector<KeyIdPair>& aOutKeys,
    516                             SessionType aSessionType) {
    517  ParserContext ctx;
    518  ctx.mIter = aKeyData;
    519  ctx.mEnd = aKeyData + aKeyDataSize;
    520 
    521  // Consume '{' from start of object.
    522  EXPECT_SYMBOL(ctx, '{');
    523 
    524  while (true) {
    525    string label;
    526    // Consume member key.
    527    if (!GetNextLabel(ctx, label)) return false;
    528    EXPECT_SYMBOL(ctx, ':');
    529 
    530    if (label == "keys") {
    531      // Parse "keys" array.
    532      if (!ParseKeys(ctx, aOutKeys)) return false;
    533    } else if (label == "type") {
    534      // Consume type string.
    535      string type;
    536      if (!GetNextLabel(ctx, type)) return false;
    537      if (type != SessionTypeToString(aSessionType)) {
    538        return false;
    539      }
    540    } else {
    541      SkipToken(ctx);
    542    }
    543 
    544    // Check for end of object.
    545    if (PeekSymbol(ctx) == '}') {
    546      break;
    547    }
    548 
    549    // Consume ',' between object members.
    550    EXPECT_SYMBOL(ctx, ',');
    551  }
    552 
    553  // Consume '}' from end of object.
    554  EXPECT_SYMBOL(ctx, '}');
    555 
    556  return true;
    557 }
    558 
    559 static bool ParseKeyIds(ParserContext& aCtx, vector<KeyId>& aOutKeyIds) {
    560  // Consume start of array.
    561  EXPECT_SYMBOL(aCtx, '[');
    562 
    563  while (true) {
    564    string label;
    565    vector<uint8_t> keyId;
    566    if (!GetNextLabel(aCtx, label) || !DecodeBase64(label, keyId)) {
    567      return false;
    568    }
    569    if (!keyId.empty() && keyId.size() <= kMaxKeyIdsLength) {
    570      aOutKeyIds.push_back(keyId);
    571    }
    572 
    573    uint8_t sym = PeekSymbol(aCtx);
    574    if (!sym || sym == ']') {
    575      break;
    576    }
    577 
    578    EXPECT_SYMBOL(aCtx, ',');
    579  }
    580 
    581  return GetNextSymbol(aCtx) == ']';
    582 }
    583 
    584 /* static */
    585 bool ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData,
    586                                        uint32_t aInitDataSize,
    587                                        vector<KeyId>& aOutKeyIds) {
    588  ParserContext ctx;
    589  ctx.mIter = aInitData;
    590  ctx.mEnd = aInitData + aInitDataSize;
    591 
    592  // Consume '{' from start of object.
    593  EXPECT_SYMBOL(ctx, '{');
    594 
    595  while (true) {
    596    string label;
    597    // Consume member kids.
    598    if (!GetNextLabel(ctx, label)) return false;
    599    EXPECT_SYMBOL(ctx, ':');
    600 
    601    if (label == "kids") {
    602      // Parse "kids" array.
    603      if (!ParseKeyIds(ctx, aOutKeyIds) || aOutKeyIds.empty()) {
    604        return false;
    605      }
    606    } else {
    607      SkipToken(ctx);
    608    }
    609 
    610    // Check for end of object.
    611    if (PeekSymbol(ctx) == '}') {
    612      break;
    613    }
    614 
    615    // Consume ',' between object members.
    616    EXPECT_SYMBOL(ctx, ',');
    617  }
    618 
    619  // Consume '}' from end of object.
    620  EXPECT_SYMBOL(ctx, '}');
    621 
    622  return true;
    623 }
    624 
    625 /* static */ const char* ClearKeyUtils::SessionTypeToString(
    626    SessionType aSessionType) {
    627  switch (aSessionType) {
    628    case SessionType::kTemporary:
    629      return "temporary";
    630    case SessionType::kPersistentLicense:
    631      return "persistent-license";
    632    default: {
    633      // We don't support any other license types.
    634      assert(false);
    635      return "invalid";
    636    }
    637  }
    638 }
    639 
    640 /* static */
    641 bool ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength) {
    642  if (aLength > 10) {
    643    // 10 is the max number of characters in UINT32_MAX when
    644    // represented as a string; ClearKey session ids are integers.
    645    return false;
    646  }
    647  for (uint32_t i = 0; i < aLength; i++) {
    648    if (!isdigit(aBuff[i])) {
    649      return false;
    650    }
    651  }
    652  return true;
    653 }
    654 
    655 string ClearKeyUtils::ToHexString(const uint8_t* aBytes, uint32_t aLength) {
    656  stringstream ss;
    657  ss << std::showbase << std::uppercase << std::hex;
    658  for (uint32_t i = 0; i < aLength; ++i) {
    659    ss << std::hex << static_cast<uint32_t>(aBytes[i]);
    660    ss << " ";
    661  }
    662 
    663  return ss.str();
    664 }