| 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
|
|
|