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() + |
+ DecryptConfig::kWebMIntegrityCheckSize); |
+ int iv_frame_size = |
+ input.GetDataSize() - DecryptConfig::kWebMIntegrityCheckSize; |
+ std::string data_to_check(iv_frame, iv_frame_size); |
+ |
+ 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())) { |
+ 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 |