// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cdm/cenc_decryptor.h" #include #include #include #include #include "base/logging.h" #include "base/strings/string_piece.h" #include "crypto/encryptor.h" #include "crypto/symmetric_key.h" #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" #include "media/base/subsample_entry.h" namespace media { namespace { enum ClearBytesBufferSel { kSrcContainsClearBytes, kDstContainsClearBytes }; // Copy the cypher bytes as specified by |subsamples| from |src| to |dst|. // If |sel| == kSrcContainsClearBytes, then |src| is expected to contain any // clear bytes specified by |subsamples| and will be skipped. This is used // when copying all the protected data out of a sample. If |sel| == // kDstContainsClearBytes, then any clear bytes mentioned in |subsamples| // will be skipped in |dst|. This is used when copying the decrypted bytes // back into the buffer, replacing the encrypted portions. void CopySubsamples(const std::vector& subsamples, const ClearBytesBufferSel sel, const uint8_t* src, uint8_t* dst) { for (size_t i = 0; i < subsamples.size(); i++) { const SubsampleEntry& subsample = subsamples[i]; if (sel == kSrcContainsClearBytes) { src += subsample.clear_bytes; } else { dst += subsample.clear_bytes; } memcpy(dst, src, subsample.cypher_bytes); src += subsample.cypher_bytes; dst += subsample.cypher_bytes; } } // TODO(crbug.com/840983): This should be done in DecoderBuffer so that // additional fields are more easily handled. void CopyExtraSettings(const DecoderBuffer& input, DecoderBuffer* output) { output->set_timestamp(input.timestamp()); output->set_duration(input.duration()); output->set_is_key_frame(input.is_key_frame()); output->CopySideDataFrom(input.side_data(), input.side_data_size()); } } // namespace scoped_refptr DecryptCencBuffer( const DecoderBuffer& input, const crypto::SymmetricKey& key) { const char* sample = reinterpret_cast(input.data()); const size_t sample_size = input.data_size(); DCHECK(sample_size) << "No data to decrypt."; const DecryptConfig* decrypt_config = input.decrypt_config(); DCHECK(decrypt_config) << "No need to call Decrypt() on unencrypted buffer."; DCHECK_EQ(EncryptionScheme::kCenc, decrypt_config->encryption_scheme()); const std::string& iv = decrypt_config->iv(); DCHECK_EQ(iv.size(), static_cast(DecryptConfig::kDecryptionKeySize)); crypto::Encryptor encryptor; if (!encryptor.Init(&key, crypto::Encryptor::CTR, "")) { DVLOG(1) << "Could not initialize decryptor."; return nullptr; } if (!encryptor.SetCounter(iv)) { DVLOG(1) << "Could not set counter block."; return nullptr; } const std::vector& subsamples = decrypt_config->subsamples(); if (subsamples.empty()) { std::string decrypted_text; base::StringPiece encrypted_text(sample, sample_size); if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { DVLOG(1) << "Could not decrypt data."; return nullptr; } // TODO(xhwang): Find a way to avoid this data copy. auto output = DecoderBuffer::CopyFrom( reinterpret_cast(decrypted_text.data()), decrypted_text.size()); CopyExtraSettings(input, output.get()); return output; } if (!VerifySubsamplesMatchSize(subsamples, sample_size)) { DVLOG(1) << "Subsample sizes do not equal input size"; return nullptr; } // Compute the size of the encrypted portion. Overflow, etc. checked by // the call to VerifySubsamplesMatchSize(). size_t total_encrypted_size = 0; for (const auto& subsample : subsamples) total_encrypted_size += subsample.cypher_bytes; // No need to decrypt if there is no encrypted data. if (total_encrypted_size == 0) { auto output = DecoderBuffer::CopyFrom(input.data(), sample_size); CopyExtraSettings(input, output.get()); return output; } // The encrypted portions of all subsamples must form a contiguous block, // such that an encrypted subsample that ends away from a block boundary is // immediately followed by the start of the next encrypted subsample. We // copy all encrypted subsamples to a contiguous buffer, decrypt them, then // copy the decrypted bytes over the encrypted bytes in the output. // TODO(strobe): attempt to reduce number of memory copies std::unique_ptr encrypted_bytes(new uint8_t[total_encrypted_size]); CopySubsamples(subsamples, kSrcContainsClearBytes, reinterpret_cast(sample), encrypted_bytes.get()); base::StringPiece encrypted_text( reinterpret_cast(encrypted_bytes.get()), total_encrypted_size); std::string decrypted_text; if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { DVLOG(1) << "Could not decrypt data."; return nullptr; } DCHECK_EQ(decrypted_text.size(), encrypted_text.size()); scoped_refptr output = DecoderBuffer::CopyFrom( reinterpret_cast(sample), sample_size); CopySubsamples(subsamples, kDstContainsClearBytes, reinterpret_cast(decrypted_text.data()), output->writable_data()); CopyExtraSettings(input, output.get()); return output; } } // namespace media