summaryrefslogtreecommitdiffstats
path: root/media/crypto/aes_decryptor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/crypto/aes_decryptor.cc')
-rw-r--r--media/crypto/aes_decryptor.cc180
1 files changed, 113 insertions, 67 deletions
diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc
index 730a9b8..552197b 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);
@@ -72,65 +72,117 @@ static bool CheckData(const DecoderBuffer& input,
// Here, check that checksum size is not greater than the hash
// algorithm's digest length.
- DCHECK_LE(input.GetDecryptConfig()->checksum_size(),
- static_cast<int>(hmac.DigestLength()));
+ DCHECK_LE(input.GetDecryptConfig()->checksum().size(),
+ hmac.DigestLength());
base::StringPiece data_to_check(
reinterpret_cast<const char*>(input.GetData()), input.GetDataSize());
- base::StringPiece digest(
- reinterpret_cast<const char*>(input.GetDecryptConfig()->checksum()),
- input.GetDecryptConfig()->checksum_size());
- return hmac.VerifyTruncated(data_to_check, digest);
+ return hmac.VerifyTruncated(data_to_check,
+ input.GetDecryptConfig()->checksum());
}
-// 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)
@@ -192,9 +244,7 @@ void AesDecryptor::AddKey(const std::string& key_system,
return;
}
- // TODO(fgalligan): When ISO is added we will need to figure out how to
- // 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;
@@ -220,16 +270,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;
}
@@ -241,7 +287,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
@@ -253,10 +299,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 and the removal of the webm_decryption_key member.
+ 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);
@@ -275,34 +324,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;
}