Chromium Code Reviews| Index: media/crypto/hmac_aes_decryptor.cc |
| diff --git a/media/crypto/hmac_aes_decryptor.cc b/media/crypto/hmac_aes_decryptor.cc |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..64f380032c565dd5ac94fdab1508878a02bffb2e |
| --- /dev/null |
| +++ b/media/crypto/hmac_aes_decryptor.cc |
| @@ -0,0 +1,198 @@ |
| +// Copyright (c) 2012 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/crypto/hmac_aes_decryptor.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/stl_util.h" |
| +#include "base/string_piece.h" |
| +#include "crypto/encryptor.h" |
| +#include "crypto/hmac.h" |
| +#include "crypto/symmetric_key.h" |
| +#include "media/base/decoder_buffer.h" |
| +#include "media/base/decrypt_config.h" |
| + |
| +namespace media { |
| + |
| +// Derives a key from an HAMC using SHA1. |secret| is the base secret to derive |
| +// the key from. |seed| is the knwon input to the HMAC. |key_size| is how many |
| +// bytes are returned in the key. Returns a string containing the key on |
| +// success. Returns an empty string on failure. |
| +static std::string DeriveKey(const std::string& secret, |
| + const std::string& seed, |
| + int key_size) { |
| + CHECK_EQ(secret.length(), static_cast<size_t>(DecryptConfig::kKeySize)); |
| + CHECK(!seed.empty()); |
| + CHECK_GT(key_size, 0); |
| + |
| + std::string key; |
| + crypto::HMAC hmac(crypto::HMAC::SHA1); |
| + if (!hmac.Init(reinterpret_cast<const uint8*>(secret.data()), |
| + secret.size())) { |
| + DVLOG(1) << "Could not initialize HMAC with secret data."; |
| + return key; |
| + } |
| + |
| + uint8 calculated_hmac[HmacAesDecryptor::kSha1DigestSize]; |
| + if (!hmac.Sign(seed, calculated_hmac, HmacAesDecryptor::kSha1DigestSize)) { |
| + DVLOG(1) << "Could not calculate HMAC."; |
| + return key; |
| + } |
| + key.assign(reinterpret_cast<const char*>(calculated_hmac), key_size); |
| + return key; |
| +} |
| + |
| +// Integrity check of |input|'s data. Checks that the first |
| +// |kWebMIntegrityCheckSize| in bytes of |ipunt| matches the rest of the data |
| +// in |input|. The check is defined in the WebM specification. Current WebM |
| +// encrypted request for comments specification is here |
| +// http://wiki.webmproject.org/encryption/webm-encryption-rfc. |
| +// |hmac_key| is the key of the HMAC algorithm. Returns true if the integrity |
| +// check is good. |
| +static bool CheckData(const DecoderBuffer& input, |
| + const std::string& hmac_key) { |
| + CHECK(input.GetDataSize()); |
| + CHECK(input.GetDecryptConfig()); |
| + CHECK(!hmac_key.empty()); |
| + |
| + crypto::HMAC hmac(crypto::HMAC::SHA1); |
| + if (!hmac.Init(reinterpret_cast<const uint8*>(hmac_key.data()), |
| + hmac_key.size())) { |
| + DVLOG(1) << "Could not initialize HMAC."; |
| + return false; |
| + } |
| + |
| + // The HMAC covers the IV and the frame data. |
| + const char* iv_frame = reinterpret_cast<const char*>(input.GetData() + |
|
ddorwin
2012/06/14 21:41:24
Is "iv_frame" really "iv_and_frame"? As mentioned
fgalligan1
2012/07/03 22:00:15
I only send the IV and frame data in the input buf
|
| + DecryptConfig::kWebMIntegrityCheckSize); |
|
ddorwin
2012/06/14 21:41:24
Would like to avoid any WebM-specific values in th
fgalligan1
2012/07/03 22:00:15
Done.
|
| + int iv_frame_size = |
| + input.GetDataSize() - DecryptConfig::kWebMIntegrityCheckSize; |
| + std::string data_to_check(iv_frame, iv_frame_size); |
|
ddorwin
2012/06/14 21:41:24
Can we avoid this memory copy?
(If we separate IV
fgalligan1
2012/07/03 22:00:15
Done.
|
| + |
| + uint8 calculated_hmac[HmacAesDecryptor::kSha1DigestSize]; |
| + if (!hmac.Sign(data_to_check, |
| + calculated_hmac, |
| + HmacAesDecryptor::kSha1DigestSize)) { |
| + DVLOG(1) << "Could not calculate HMAC."; |
| + return false; |
| + } |
| + |
| + if (memcmp(input.GetDecryptConfig()->hmac(), |
| + calculated_hmac, |
| + input.GetDecryptConfig()->hmac_size()) != 0) { |
| + DVLOG(1) << "Integrity check failure."; |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +const char HmacAesDecryptor::kHmacSeed[] = "hmac-key"; |
| +const char HmacAesDecryptor::kEncryptionSeed[] = "encryption-key"; |
| + |
| +HmacAesDecryptor::HmacAesDecryptor() {} |
| + |
| +HmacAesDecryptor::~HmacAesDecryptor() { |
| + STLDeleteValues(&keys_map_); |
| +} |
| + |
| +void HmacAesDecryptor::AddKey(const uint8* key_id, int key_id_size, |
| + const uint8* key, int key_size) { |
| + CHECK(key_id && key); |
| + CHECK_GT(key_id_size, 0); |
| + CHECK_GT(key_size, 0); |
| + |
| + std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); |
| + std::string key_string(reinterpret_cast<const char*>(key) , key_size); |
| + |
| + HmacEncryptionKeys* keys = new HmacEncryptionKeys(key_string); |
| + if (!keys) { |
| + DVLOG(1) << "Could not create keys."; |
| + return; |
| + } |
| + if (!keys->Init()) { |
| + delete keys; |
| + DVLOG(1) << "Could not create keys."; |
| + return; |
| + } |
| + |
| + base::AutoLock auto_lock(lock_); |
| + KeysMap::iterator found = keys_map_.find(key_id_string); |
| + if (found != keys_map_.end()) { |
| + delete found->second; |
| + keys_map_.erase(found); |
| + } |
| + keys_map_[key_id_string] = keys; |
| +} |
| + |
| +scoped_refptr<DecoderBuffer> HmacAesDecryptor::Decrypt( |
| + const scoped_refptr<DecoderBuffer>& encrypted) { |
| + 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); |
| + |
| + HmacEncryptionKeys* keys = NULL; |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + KeysMap::const_iterator found = keys_map_.find(key_id_string); |
| + if (found == keys_map_.end()) { |
| + DVLOG(1) << "Could not find a matching key for given key ID."; |
| + return NULL; |
| + } |
| + keys = found->second; |
| + } |
| + |
| + if (!CheckData(*encrypted, keys->hmac_key())) { |
|
ddorwin
2012/06/14 21:41:24
Support containers without HMAC support:
if (HasHm
fgalligan1
2012/07/03 22:00:15
Done.
|
| + DVLOG(1) << "Integrity check failed."; |
| + return NULL; |
| + } |
| + |
| + scoped_refptr<DecoderBuffer> decrypted = |
| + Decryptor::DecryptData(*encrypted, |
| + keys->encryption_key(), |
| + DecryptConfig::kWebMIntegrityCheckSize + |
| + DecryptConfig::kIvSize); |
| + if (decrypted) { |
| + decrypted->SetTimestamp(encrypted->GetTimestamp()); |
| + decrypted->SetDuration(encrypted->GetDuration()); |
| + } |
| + |
| + return decrypted; |
| +} |
| + |
| +HmacAesDecryptor::HmacEncryptionKeys::HmacEncryptionKeys( |
| + const std::string& secret) |
| + : secret_(secret) { |
| +} |
| + |
| +HmacAesDecryptor::HmacEncryptionKeys::~HmacEncryptionKeys() {} |
| + |
| +bool HmacAesDecryptor::HmacEncryptionKeys::Init() { |
| + CHECK(!secret_.empty()); |
| + |
| + std::string raw_key = DeriveKey(secret_, |
| + kEncryptionSeed, |
| + DecryptConfig::kKeySize); |
| + if (raw_key.empty()) { |
| + DVLOG(1) << "Could not create encryption key."; |
| + return false; |
| + } |
| + encryption_key_.reset(crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, |
| + raw_key)); |
| + if (!encryption_key_.get()) { |
| + DVLOG(1) << "Could not create encryption key."; |
| + return false; |
| + } |
| + |
| + hmac_key_ = DeriveKey(secret_, kHmacSeed, kSha1DigestSize); |
| + if (hmac_key_.empty()) { |
| + DVLOG(1) << "Could not create HMAC key."; |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +} // namespace media |