Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(522)

Unified Diff: media/crypto/hmac_aes_decryptor.cc

Issue 10535029: Add support for encrypted WebM files as defined in the RFC. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: –Fix DecodeEncryptedFrame_WrongKey test. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698