Index: media/crypto/aes_decryptor.cc |
diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc |
index e69f4062a8afb3f00ee9d609a19785da40269efe..156da30abbfc0ac8ca387d5ce1b542570b086a5b 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); |
@@ -74,62 +74,115 @@ static bool CheckData(const DecoderBuffer& input, |
if (!hmac.Sign(data_to_check, calculated_hmac.get(), hmac.DigestLength())) |
return false; |
- DCHECK(input.GetDecryptConfig()->checksum_size() <= |
- static_cast<int>(hmac.DigestLength())); |
- if (memcmp(input.GetDecryptConfig()->checksum(), |
+ DCHECK(input.GetDecryptConfig()->checksum().size() <= hmac.DigestLength()); |
+ if (memcmp(input.GetDecryptConfig()->checksum().data(), |
ddorwin
2012/07/24 01:00:10
Please add:
TODO(fgalligan): Use hmac.Verify().
strobe_
2012/07/25 01:05:13
Done.
|
calculated_hmac.get(), |
- input.GetDecryptConfig()->checksum_size()) != 0) |
+ input.GetDecryptConfig()->checksum().size()) != 0) |
return false; |
return true; |
} |
-// 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) |
@@ -193,7 +246,7 @@ void AesDecryptor::AddKey(const std::string& key_system, |
// TODO(fgalligan): When ISO is added we will need to figure out how to |
ddorwin
2012/07/24 01:00:10
This is probably redundant with the TODO below.
strobe_
2012/07/25 01:05:13
Done.
|
// 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; |
@@ -219,16 +272,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; |
} |
@@ -240,7 +289,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 |
@@ -252,10 +301,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. |
ddorwin
2012/07/24 01:00:10
... and remove the webm_ member.
strobe_
2012/07/25 01:05:13
Done.
|
+ 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); |
@@ -274,34 +326,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; |
} |