Chromium Code Reviews| Index: media/crypto/aes_decryptor.cc |
| diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc |
| index e69f4062a8afb3f00ee9d609a19785da40269efe..156da30abbfc0ac8ca387d5ce1b542570b086a5b 100644 |
| --- a/media/crypto/aes_decryptor.cc |
| +++ b/media/crypto/aes_decryptor.cc |
| @@ -59,7 +59,7 @@ static bool CheckData(const DecoderBuffer& input, |
| const base::StringPiece& hmac_key) { |
| CHECK(input.GetDataSize()); |
| CHECK(input.GetDecryptConfig()); |
| - CHECK_GT(input.GetDecryptConfig()->checksum_size(), 0); |
| + CHECK_GT(input.GetDecryptConfig()->checksum().size(), 0u); |
| CHECK(!hmac_key.empty()); |
| crypto::HMAC hmac(crypto::HMAC::SHA1); |
| @@ -74,62 +74,115 @@ static bool CheckData(const DecoderBuffer& input, |
| if (!hmac.Sign(data_to_check, calculated_hmac.get(), hmac.DigestLength())) |
| return false; |
| - DCHECK(input.GetDecryptConfig()->checksum_size() <= |
| - static_cast<int>(hmac.DigestLength())); |
| - if (memcmp(input.GetDecryptConfig()->checksum(), |
| + DCHECK(input.GetDecryptConfig()->checksum().size() <= hmac.DigestLength()); |
| + if (memcmp(input.GetDecryptConfig()->checksum().data(), |
|
ddorwin
2012/07/24 01:00:10
Please add:
TODO(fgalligan): Use hmac.Verify().
strobe_
2012/07/25 01:05:13
Done.
|
| calculated_hmac.get(), |
| - input.GetDecryptConfig()->checksum_size()) != 0) |
| + input.GetDecryptConfig()->checksum().size()) != 0) |
| return false; |
| return true; |
| } |
| -// Decrypts |input| using |key|. |encrypted_data_offset| is the number of bytes |
| -// into |input| that the encrypted data starts. |
| -// Returns a DecoderBuffer with the decrypted data if decryption succeeded or |
| -// NULL if decryption failed. |
| +enum ClearBytesBufferSel { |
| + kSrcContainsClearBytes, |
| + kDstContainsClearBytes, |
| +}; |
| + |
| +static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, |
| + const ClearBytesBufferSel sel, |
| + const uint8* src, |
| + uint8* 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; |
| + } |
| +} |
| + |
| +// Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted |
| +// data if decryption succeeded or NULL if decryption failed. |
| static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, |
| - crypto::SymmetricKey* key, |
| - int encrypted_data_offset) { |
| + crypto::SymmetricKey* key) { |
| CHECK(input.GetDataSize()); |
| CHECK(input.GetDecryptConfig()); |
| CHECK(key); |
| - // Initialize decryptor. |
| crypto::Encryptor encryptor; |
| if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { |
| DVLOG(1) << "Could not initialize decryptor."; |
| return NULL; |
| } |
| - DCHECK_EQ(input.GetDecryptConfig()->iv_size(), |
| - DecryptConfig::kDecryptionKeySize); |
| - // Set the counter block. |
| - base::StringPiece counter_block( |
| - reinterpret_cast<const char*>(input.GetDecryptConfig()->iv()), |
| - input.GetDecryptConfig()->iv_size()); |
| - if (counter_block.empty()) { |
| - DVLOG(1) << "Could not generate counter block."; |
| + DCHECK_EQ(input.GetDecryptConfig()->iv().size(), |
| + static_cast<size_t>(DecryptConfig::kDecryptionKeySize)); |
| + if (!encryptor.SetCounter(input.GetDecryptConfig()->iv())) { |
| + DVLOG(1) << "Could not set counter block."; |
| return NULL; |
| } |
| - if (!encryptor.SetCounter(counter_block)) { |
| - DVLOG(1) << "Could not set counter block."; |
| + |
| + const int data_offset = input.GetDecryptConfig()->data_offset(); |
| + const char* sample = |
| + reinterpret_cast<const char*>(input.GetData() + data_offset); |
| + int sample_size = input.GetDataSize() - data_offset; |
| + |
| + if (input.GetDecryptConfig()->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 NULL; |
| + } |
| + |
| + // TODO(xhwang): Find a way to avoid this data copy. |
| + return DecoderBuffer::CopyFrom( |
| + reinterpret_cast<const uint8*>(decrypted_text.data()), |
| + decrypted_text.size()); |
| + } |
| + |
| + const std::vector<SubsampleEntry>& subsamples = |
| + input.GetDecryptConfig()->subsamples(); |
| + |
| + int total_clear_size = 0; |
| + int total_encrypted_size = 0; |
| + for (size_t i = 0; i < subsamples.size(); i++) { |
| + total_clear_size += subsamples[i].clear_bytes; |
| + total_encrypted_size += subsamples[i].cypher_bytes; |
| + } |
| + if (total_clear_size + total_encrypted_size != sample_size) { |
| + DVLOG(1) << "Subsample sizes do not equal input size"; |
| return NULL; |
| } |
| + // 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 |
| + scoped_array<uint8> encrypted_bytes(new uint8[total_encrypted_size]); |
| + CopySubsamples(subsamples, kSrcContainsClearBytes, |
| + reinterpret_cast<const uint8*>(sample), encrypted_bytes.get()); |
| + |
| + base::StringPiece encrypted_text( |
| + reinterpret_cast<const char*>(encrypted_bytes.get()), |
| + total_encrypted_size); |
| std::string decrypted_text; |
| - const char* frame = |
| - reinterpret_cast<const char*>(input.GetData() + encrypted_data_offset); |
| - int frame_size = input.GetDataSize() - encrypted_data_offset; |
| - base::StringPiece encrypted_text(frame, frame_size); |
| if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { |
| DVLOG(1) << "Could not decrypt data."; |
| return NULL; |
| } |
| - // TODO(xhwang): Find a way to avoid this data copy. |
| - return DecoderBuffer::CopyFrom( |
| - reinterpret_cast<const uint8*>(decrypted_text.data()), |
| - decrypted_text.size()); |
| + scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom( |
| + reinterpret_cast<const uint8*>(sample), sample_size); |
| + CopySubsamples(subsamples, kDstContainsClearBytes, |
| + reinterpret_cast<const uint8*>(decrypted_text.data()), |
| + output->GetWritableData()); |
| + return output; |
| } |
| AesDecryptor::AesDecryptor(DecryptorClient* client) |
| @@ -193,7 +246,7 @@ void AesDecryptor::AddKey(const std::string& key_system, |
| // TODO(fgalligan): When ISO is added we will need to figure out how to |
|
ddorwin
2012/07/24 01:00:10
This is probably redundant with the TODO below.
strobe_
2012/07/25 01:05:13
Done.
|
| // detect if the encrypted data will contain an HMAC. |
| - if (!decryption_key->Init(true)) { |
| + if (!decryption_key->Init()) { |
| DVLOG(1) << "Could not initialize decryption key."; |
| client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); |
| return; |
| @@ -219,16 +272,12 @@ void AesDecryptor::CancelKeyRequest(const std::string& key_system, |
| void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, |
| const DecryptCB& decrypt_cb) { |
| CHECK(encrypted->GetDecryptConfig()); |
| - const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); |
| - const int key_id_size = encrypted->GetDecryptConfig()->key_id_size(); |
| - |
| - // TODO(xhwang): Avoid always constructing a string with StringPiece? |
| - std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); |
| + const std::string& key_id = encrypted->GetDecryptConfig()->key_id(); |
| DecryptionKey* key = NULL; |
| { |
| base::AutoLock auto_lock(key_map_lock_); |
| - KeyMap::const_iterator found = key_map_.find(key_id_string); |
| + KeyMap::const_iterator found = key_map_.find(key_id); |
| if (found != key_map_.end()) |
| key = found->second; |
| } |
| @@ -240,7 +289,7 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, |
| return; |
| } |
| - int checksum_size = encrypted->GetDecryptConfig()->checksum_size(); |
| + int checksum_size = encrypted->GetDecryptConfig()->checksum().size(); |
| // According to the WebM encrypted specification, it is an open question |
| // what should happen when a frame fails the integrity check. |
| // http://wiki.webmproject.org/encryption/webm-encryption-rfc |
| @@ -252,10 +301,13 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, |
| return; |
| } |
| + // TODO(strobe): Currently, presence of checksum is used to indicate the use |
| + // of normal or WebM decryption keys. Consider a more explicit signaling |
| + // mechanism. |
|
ddorwin
2012/07/24 01:00:10
... and remove the webm_ member.
strobe_
2012/07/25 01:05:13
Done.
|
| + crypto::SymmetricKey* decryption_key = (checksum_size > 0) ? |
| + key->webm_decryption_key() : key->decryption_key(); |
| scoped_refptr<DecoderBuffer> decrypted = |
| - DecryptData(*encrypted, |
| - key->decryption_key(), |
| - encrypted->GetDecryptConfig()->encrypted_frame_offset()); |
| + DecryptData(*encrypted, decryption_key); |
| if (!decrypted) { |
| DVLOG(1) << "Decryption failed."; |
| decrypt_cb.Run(kError, NULL); |
| @@ -274,34 +326,31 @@ AesDecryptor::DecryptionKey::DecryptionKey( |
| AesDecryptor::DecryptionKey::~DecryptionKey() {} |
| -bool AesDecryptor::DecryptionKey::Init(bool derive_webm_keys) { |
| +bool AesDecryptor::DecryptionKey::Init() { |
| CHECK(!secret_.empty()); |
| - |
| - if (derive_webm_keys) { |
| - std::string raw_key = DeriveKey(secret_, |
| - kWebmEncryptionSeed, |
| - secret_.length()); |
| - if (raw_key.empty()) { |
| - return false; |
| - } |
| - decryption_key_.reset( |
| - crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); |
| - if (!decryption_key_.get()) { |
| - return false; |
| - } |
| - |
| - hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); |
| - if (hmac_key_.empty()) { |
| - return false; |
| - } |
| - return true; |
| - } |
| - |
| decryption_key_.reset( |
| crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_)); |
| if (!decryption_key_.get()) { |
| return false; |
| } |
| + |
| + std::string raw_key = DeriveKey(secret_, |
| + kWebmEncryptionSeed, |
| + secret_.length()); |
| + if (raw_key.empty()) { |
| + return false; |
| + } |
| + webm_decryption_key_.reset( |
| + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); |
| + if (!webm_decryption_key_.get()) { |
| + return false; |
| + } |
| + |
| + hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); |
| + if (hmac_key_.empty()) { |
| + return false; |
| + } |
| + |
| return true; |
| } |