diff options
-rw-r--r-- | media/base/decrypt_config.cc | 20 | ||||
-rw-r--r-- | media/base/decrypt_config.h | 39 | ||||
-rw-r--r-- | media/crypto/aes_decryptor.cc | 186 | ||||
-rw-r--r-- | media/crypto/aes_decryptor.h | 46 | ||||
-rw-r--r-- | media/crypto/aes_decryptor_unittest.cc | 368 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder_unittest.cc | 87 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test.cc | 16 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 43 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.h | 4 | ||||
-rw-r--r-- | media/webm/webm_constants.h | 7 | ||||
-rw-r--r-- | media/webm/webm_content_encodings.cc | 3 | ||||
-rw-r--r-- | media/webm/webm_content_encodings.h | 9 | ||||
-rw-r--r-- | media/webm/webm_content_encodings_client.cc | 29 | ||||
-rw-r--r-- | media/webm/webm_parser.cc | 9 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 1 |
15 files changed, 138 insertions, 729 deletions
diff --git a/media/base/decrypt_config.cc b/media/base/decrypt_config.cc index fb502df..9ae5f19 100644 --- a/media/base/decrypt_config.cc +++ b/media/base/decrypt_config.cc @@ -8,25 +8,11 @@ namespace media { -DecryptConfig::DecryptConfig(const uint8* key_id, int key_id_size, - const uint8* iv, int iv_size, - const uint8* checksum, int checksum_size, - int encrypted_frame_offset) - : key_id_(new uint8[key_id_size]), - key_id_size_(key_id_size), - iv_(new uint8[iv_size]), - iv_size_(iv_size), - checksum_(checksum_size > 0 ? new uint8[checksum_size] : NULL), - checksum_size_(checksum_size), - encrypted_frame_offset_(encrypted_frame_offset) { +DecryptConfig::DecryptConfig(const uint8* key_id, int key_id_size) + : key_id_size_(key_id_size) { CHECK_GT(key_id_size, 0); - CHECK_EQ(iv_size, DecryptConfig::kDecryptionKeySize); - CHECK_GE(checksum_size, 0); - CHECK_GE(encrypted_frame_offset, 0); + key_id_.reset(new uint8[key_id_size]); memcpy(key_id_.get(), key_id, key_id_size); - memcpy(iv_.get(), iv, iv_size); - if (checksum_size > 0) - memcpy(checksum_.get(), checksum, checksum_size); } DecryptConfig::~DecryptConfig() {} diff --git a/media/base/decrypt_config.h b/media/base/decrypt_config.h index 68a3fbd..5fca787 100644 --- a/media/base/decrypt_config.h +++ b/media/base/decrypt_config.h @@ -11,49 +11,18 @@ namespace media { -// Contains all information that a decryptor needs to decrypt a frame. +// Contains all information that a decryptor needs to decrypt. class MEDIA_EXPORT DecryptConfig { public: - // Keys are always 128 bits. - static const int kDecryptionKeySize = 16; - - // |key_id| is the ID that references the decryption key for this frame. |iv| - // is the initialization vector defined by the encrypted format. Currently - // |iv_size| must be 16 bytes as defined by WebM and ISO. |checksum| is the - // hash value of the encrypted buffer. |checksum| is defined by the - // encrypted format and may be NULL. |encrypted_frame_offset| is the offset - // into the encrypted buffer that the encrypted frame starts. The class - // will copy the data from |key_id|, |iv|, and |checksum|. - DecryptConfig(const uint8* key_id, int key_id_size, - const uint8* iv, int iv_size, - const uint8* checksum, int checksum_size, - int encrypted_frame_offset); + explicit DecryptConfig(const uint8* key_id, int key_id_size); ~DecryptConfig(); const uint8* key_id() const { return key_id_.get(); } int key_id_size() const { return key_id_size_; } - const uint8* iv() const { return iv_.get(); } - int iv_size() const { return iv_size_; } - const uint8* checksum() const { return checksum_.get(); } - int checksum_size() const { return checksum_size_; } - int encrypted_frame_offset() const { return encrypted_frame_offset_; } private: - const scoped_array<uint8> key_id_; - const int key_id_size_; - - // Initialization vector. - const scoped_array<uint8> iv_; - const int iv_size_; - - // Checksum of the data to be verified before decrypting the data. This may - // be NULL for some formats. - const scoped_array<uint8> checksum_; - const int checksum_size_; - - // This is the offset in bytes to where the encrypted data starts within - // the input buffer. - const int encrypted_frame_offset_; + scoped_array<uint8> key_id_; + int key_id_size_; DISALLOW_COPY_AND_ASSIGN(DecryptConfig); }; diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc index e69f406..f677f0d 100644 --- a/media/crypto/aes_decryptor.cc +++ b/media/crypto/aes_decryptor.cc @@ -7,8 +7,8 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/string_number_conversions.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" @@ -16,111 +16,31 @@ namespace media { -// The size is from the WebM encrypted specification. Current encrypted WebM -// request for comments specification is here -// http://wiki.webmproject.org/encryption/webm-encryption-rfc -static const int kWebmSha1DigestSize = 20; -static const char kWebmHmacSeed[] = "hmac-key"; -static const char kWebmEncryptionSeed[] = "encryption-key"; +// TODO(xhwang): Get real IV from frames. +static const char kInitialCounter[] = "0000000000000000"; uint32 AesDecryptor::next_session_id_ = 1; -// Derives a key using SHA1 HMAC. |secret| is the base secret to derive -// the key from. |seed| is the known message to the HMAC algorithm. |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 base::StringPiece& secret, - const base::StringPiece& seed, - int key_size) { - CHECK(!secret.empty()); - CHECK(!seed.empty()); - CHECK_GT(key_size, 0); - - crypto::HMAC hmac(crypto::HMAC::SHA1); - if (!hmac.Init(secret)) { - DVLOG(1) << "Could not initialize HMAC with secret data."; - return std::string(); - } - - scoped_array<uint8> calculated_hmac(new uint8[hmac.DigestLength()]); - if (!hmac.Sign(seed, calculated_hmac.get(), hmac.DigestLength())) { - DVLOG(1) << "Could not calculate HMAC."; - return std::string(); - } - - return std::string(reinterpret_cast<const char*>(calculated_hmac.get()), - key_size); -} - -// Checks data in |input| matches the HMAC in |input|. The check is using the -// SHA1 algorithm. |hmac_key| is the key of the HMAC algorithm. Returns true if -// the integrity check passes. -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(!hmac_key.empty()); - - crypto::HMAC hmac(crypto::HMAC::SHA1); - if (!hmac.Init(hmac_key)) - return false; - - // The HMAC covers the IV and the frame data. - base::StringPiece data_to_check( - reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); - - scoped_array<uint8> calculated_hmac(new uint8[hmac.DigestLength()]); - 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(), - calculated_hmac.get(), - 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. +// Decrypt |input| using |key|. +// Return a DecoderBuffer with the decrypted data if decryption succeeded. +// Return 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. + // Initialize encryption data. + // The IV must be exactly as long as the cipher block size. 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."; - return NULL; - } - if (!encryptor.SetCounter(counter_block)) { - DVLOG(1) << "Could not set counter block."; + if (!encryptor.Init(key, crypto::Encryptor::CBC, kInitialCounter)) { + DVLOG(1) << "Could not initialize encryptor."; return NULL; } 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); + base::StringPiece encrypted_text( + reinterpret_cast<const char*>(input.GetData()), + input.GetDataSize()); if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { DVLOG(1) << "Could not decrypt data."; return NULL; @@ -165,7 +85,9 @@ void AesDecryptor::AddKey(const std::string& key_system, // TODO(xhwang): Add |session_id| check after we figure out how: // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16550 - if (key_length != DecryptConfig::kDecryptionKeySize) { + + const int kSupportedKeyLength = 16; // 128-bit key. + if (key_length != kSupportedKeyLength) { DVLOG(1) << "Invalid key length: " << key_length; client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); return; @@ -184,17 +106,10 @@ void AesDecryptor::AddKey(const std::string& key_system, std::string key_id_string(reinterpret_cast<const char*>(init_data), init_data_length); std::string key_string(reinterpret_cast<const char*>(key) , key_length); - scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); - if (!decryption_key.get()) { - DVLOG(1) << "Could not create key."; - client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); - 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)) { - DVLOG(1) << "Could not initialize decryption key."; + crypto::SymmetricKey* symmetric_key = crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key_string); + if (!symmetric_key) { + DVLOG(1) << "Could not import key."; client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); return; } @@ -206,7 +121,7 @@ void AesDecryptor::AddKey(const std::string& key_system, delete found->second; key_map_.erase(found); } - key_map_[key_id_string] = decryption_key.release(); + key_map_[key_id_string] = symmetric_key; } client_->KeyAdded(key_system, session_id); @@ -225,7 +140,7 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, // TODO(xhwang): Avoid always constructing a string with StringPiece? std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); - DecryptionKey* key = NULL; + crypto::SymmetricKey* key = NULL; { base::AutoLock auto_lock(key_map_lock_); KeyMap::const_iterator found = key_map_.find(key_id_string); @@ -234,28 +149,13 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, } if (!key) { - // TODO(fgalligan): Fire a need_key event here and add a test. DVLOG(1) << "Could not find a matching key for given key ID."; decrypt_cb.Run(kError, NULL); return; } - 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 - if (checksum_size > 0 && - !key->hmac_key().empty() && - !CheckData(*encrypted, key->hmac_key())) { - DVLOG(1) << "Integrity check failed."; - decrypt_cb.Run(kError, NULL); - return; - } + scoped_refptr<DecoderBuffer> decrypted = DecryptData(*encrypted, key); - scoped_refptr<DecoderBuffer> decrypted = - DecryptData(*encrypted, - key->decryption_key(), - encrypted->GetDecryptConfig()->encrypted_frame_offset()); if (!decrypted) { DVLOG(1) << "Decryption failed."; decrypt_cb.Run(kError, NULL); @@ -267,42 +167,4 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, decrypt_cb.Run(kSuccess, decrypted); } -AesDecryptor::DecryptionKey::DecryptionKey( - const std::string& secret) - : secret_(secret) { -} - -AesDecryptor::DecryptionKey::~DecryptionKey() {} - -bool AesDecryptor::DecryptionKey::Init(bool derive_webm_keys) { - 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; - } - return true; -} - } // namespace media diff --git a/media/crypto/aes_decryptor.h b/media/crypto/aes_decryptor.h index 224035c..6d0bf47 100644 --- a/media/crypto/aes_decryptor.h +++ b/media/crypto/aes_decryptor.h @@ -10,8 +10,6 @@ #include "base/basictypes.h" #include "base/hash_tables.h" #include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/string_piece.h" #include "base/synchronization/lock.h" #include "media/base/decryptor.h" #include "media/base/media_export.h" @@ -24,9 +22,7 @@ namespace media { class DecryptorClient; -// Decrypts an AES encrypted buffer into an unencrypted buffer. The AES -// encryption must be CTR with a key size of 128bits. Optionally checks the -// integrity of the encrypted data. +// Decryptor implementation that decrypts AES-encrypted buffer. class MEDIA_EXPORT AesDecryptor : public Decryptor { public: // The AesDecryptor does not take ownership of the |client|. The |client| @@ -46,49 +42,13 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { const std::string& session_id) OVERRIDE; virtual void CancelKeyRequest(const std::string& key_system, const std::string& session_id) OVERRIDE; - // Decrypts |encrypted| buffer. |encrypted| should not be NULL. |encrypted| - // will signal if an integrity check must be performed before decryption. - // Returns a DecoderBuffer with the decrypted data if the decryption - // succeeded through |decrypt_cb|. virtual void Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, const DecryptCB& decrypt_cb) OVERRIDE; private: - // Helper class that manages the decryption key and HMAC key. The HMAC key - // may be NULL. - class DecryptionKey { - public: - explicit DecryptionKey(const std::string& secret); - ~DecryptionKey(); - - // Creates the encryption key and HMAC. If |derive_webm_keys| is true then - // the object will derive the decryption key and the HMAC key from - // |secret_|. - bool Init(bool derive_webm_keys); - - crypto::SymmetricKey* decryption_key() { return decryption_key_.get(); } - base::StringPiece hmac_key() { return base::StringPiece(hmac_key_); } - - private: - // The base secret that is used to derive the decryption key and optionally - // the HMAC key. - const std::string secret_; - - // The key used to decrypt the data. - scoped_ptr<crypto::SymmetricKey> decryption_key_; - - // The key used to perform the integrity check. Currently the HMAC key is - // defined by the WebM encrypted specification. Current encrypted WebM - // request for comments specification is here - // http://wiki.webmproject.org/encryption/webm-encryption-rfc - std::string hmac_key_; - - DISALLOW_COPY_AND_ASSIGN(DecryptionKey); - }; - - // KeyMap owns the DecryptionKey* and must delete them when they are + // KeyMap owns the crypto::SymmetricKey* and must delete them when they are // not needed any more. - typedef base::hash_map<std::string, DecryptionKey*> KeyMap; + typedef base::hash_map<std::string, crypto::SymmetricKey*> KeyMap; // Since only Decrypt() is called off the renderer thread, we only need to // protect |key_map_|, the only member variable that is shared between diff --git a/media/crypto/aes_decryptor_unittest.cc b/media/crypto/aes_decryptor_unittest.cc index 34354a4..3d9d983 100644 --- a/media/crypto/aes_decryptor_unittest.cc +++ b/media/crypto/aes_decryptor_unittest.cc @@ -6,12 +6,10 @@ #include "base/basictypes.h" #include "base/bind.h" -#include "base/sys_byteorder.h" #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" #include "media/base/mock_filters.h" #include "media/crypto/aes_decryptor.h" -#include "media/webm/webm_constants.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,123 +22,32 @@ using ::testing::StrNe; namespace media { -// |encrypted_data| is encrypted from |plain_text| using |key|. |key_id| is -// used to distinguish |key|. -struct WebmEncryptedData { - uint8 plain_text[32]; - int plain_text_size; - uint8 key_id[32]; - int key_id_size; - uint8 key[32]; - int key_size; - uint8 encrypted_data[64]; - int encrypted_data_size; -}; - static const char kClearKeySystem[] = "org.w3.clearkey"; - -// Frames 0 & 1 are encrypted with the same key. Frame 2 is encrypted with a -// different key. -const WebmEncryptedData kWebmEncryptedFrames[] = { - { - // plaintext - "Original data.", 14, - // key_id - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, - }, 20, - // key - { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - }, 16, - // encrypted_data - { 0xfb, 0xe7, 0x1d, 0xbb, 0x4c, 0x23, 0xce, 0xba, - 0xcc, 0xf8, 0xda, 0xc0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x99, 0xaa, 0xff, 0xb7, - 0x74, 0x02, 0x4e, 0x1c, 0x75, 0x3d, 0xee, 0xcb, - 0x64, 0xf7, - }, 34, - }, - { - // plaintext - "Changed Original data.", 22, - // key_id - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, - }, 20, - // key - { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, - 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - }, 16, - // encrypted_data - { 0x43, 0xe4, 0x78, 0x7a, 0x43, 0xe1, 0x49, 0xbb, - 0x44, 0x38, 0xdf, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xec, 0x8e, 0x87, 0x21, - 0xd3, 0xb9, 0x1c, 0x61, 0xf6, 0x5a, 0x60, 0xaa, - 0x07, 0x0e, 0x96, 0xd0, 0x54, 0x5d, 0x35, 0x9a, - 0x4a, 0xd3, - }, 42, - }, - { - // plaintext - "Original data.", 14, - // key_id - { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - }, 13, - // key - { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, - }, 16, - // encrypted_data - { 0xd9, 0x43, 0x30, 0xfd, 0x82, 0x77, 0x62, 0x04, - 0x08, 0xc2, 0x48, 0x89, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x48, 0x5e, 0x4a, 0x41, - 0x2a, 0x8b, 0xf4, 0xc6, 0x47, 0x54, 0x90, 0x34, - 0xf4, 0x8b, - }, 34, - }, +static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; +// |kEncryptedData| is encrypted from |kOriginalData| using |kRightKey|. +// Modifying any of these independently would fail the test. +static const uint8 kOriginalData[] = { + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e }; - -static const uint8 kWebmWrongKey[] = { +static const uint8 kEncryptedData[] = { + 0x82, 0x3A, 0x76, 0x92, 0xEC, 0x7F, 0xF8, 0x85, + 0xEC, 0x23, 0x52, 0xFB, 0x19, 0xB1, 0xB9, 0x09 +}; +static const uint8 kRightKey[] = { + 0x41, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, + 0x66, 0x75, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x21 +}; +static const uint8 kWrongKey[] = { 0x49, 0x27, 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x2e }; -static const uint8 kWebmWrongSizedKey[] = { 0x20, 0x20 }; - -// This is the encrypted data from frame 0 of |kWebmEncryptedFrames| except -// byte 0 is changed from 0xfb to 0xfc. Bytes 0-11 of WebM encrypted data -// contains the HMAC. -static const unsigned char kWebmFrame0HmacDataChanged[] = { - 0xfc, 0xe7, 0x1d, 0xbb, 0x4c, 0x23, 0xce, 0xba, - 0xcc, 0xf8, 0xda, 0xc0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x99, 0xaa, 0xff, 0xb7, - 0x74, 0x02, 0x4e, 0x1c, 0x75, 0x3d, 0xee, 0xcb, - 0x64, 0xf7 +static const uint8 kWrongSizedKey[] = { 0x20, 0x20 }; +static const uint8 kKeyId1[] = { + 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44, 0x20, 0x31 }; - -// This is the encrypted data from frame 0 of |kWebmEncryptedFrames| except -// byte 12 is changed from 0xff to 0x0f. Bytes 12-19 of WebM encrypted data -// contains the IV. -static const unsigned char kWebmFrame0IvDataChanged[] = { - 0xfb, 0xe7, 0x1d, 0xbb, 0x4c, 0x23, 0xce, 0xba, - 0xcc, 0xf8, 0xda, 0xc0, 0x0f, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x99, 0xaa, 0xff, 0xb7, - 0x74, 0x02, 0x4e, 0x1c, 0x75, 0x3d, 0xee, 0xcb, - 0x64, 0xf7 -}; - -// This is the encrypted data from frame 0 of |kWebmEncryptedFrames| except -// byte 33 is changed from 0xf7 to 0xf8. Bytes 20+ of WebM encrypted data -// contains the encrypted frame. -static const unsigned char kWebmFrame0FrameDataChanged[] = { - 0xfb, 0xe7, 0x1d, 0xbb, 0x4c, 0x23, 0xce, 0xba, - 0xcc, 0xf8, 0xda, 0xc0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x99, 0xaa, 0xff, 0xb7, - 0x74, 0x02, 0x4e, 0x1c, 0x75, 0x3d, 0xee, 0xcb, - 0x64, 0xf8 +static const uint8 kKeyId2[] = { + 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44, 0x20, 0x32 }; class AesDecryptorTest : public testing::Test { @@ -149,103 +56,60 @@ class AesDecryptorTest : public testing::Test { : decryptor_(&client_), decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted, base::Unretained(this))) { + encrypted_data_ = DecoderBuffer::CopyFrom(kEncryptedData, + arraysize(kEncryptedData)); } protected: - // Returns a 16 byte CTR counter block. The CTR counter block format is a - // CTR IV appended with a CTR block counter. |iv| is a CTR IV. |iv_size| is - // the size of |iv| in bytes. - static std::string GenerateCounterBlock(const uint8* iv, int iv_size) { - const int kDecryptionKeySize = 16; - CHECK_GT(iv_size, 0); - CHECK_LE(iv_size, kDecryptionKeySize); - char counter_block_data[kDecryptionKeySize]; - - // Set the IV. - memcpy(counter_block_data, iv, iv_size); - - // Set block counter to all 0's. - memset(counter_block_data + iv_size, 0, kDecryptionKeySize - iv_size); - - return std::string(counter_block_data, kDecryptionKeySize); - } - - // Creates a WebM encrypted buffer that the demuxer would pass to the - // decryptor. |data| is the payload of a WebM encrypted Block. |key_id| is - // initialization data from the WebM file. Every encrypted Block has - // an HMAC and IV prepended to an encrypted frame. Current encrypted WebM - // request for comments specification is here - // http://wiki.webmproject.org/encryption/webm-encryption-rfc - scoped_refptr<DecoderBuffer> CreateWebMEncryptedBuffer(const uint8* data, - int data_size, - const uint8* key_id, - int key_id_size) { - scoped_refptr<DecoderBuffer> encrypted_buffer = DecoderBuffer::CopyFrom( - data + kWebMHmacSize, data_size - kWebMHmacSize); - CHECK(encrypted_buffer); - - uint64 network_iv; - memcpy(&network_iv, data + kWebMHmacSize, sizeof(network_iv)); - const uint64 iv = base::NetToHost64(network_iv); - std::string webm_iv = - GenerateCounterBlock(reinterpret_cast<const uint8*>(&iv), sizeof(iv)); - encrypted_buffer->SetDecryptConfig( - scoped_ptr<DecryptConfig>(new DecryptConfig( - key_id, key_id_size, - reinterpret_cast<const uint8*>(webm_iv.data()), webm_iv.size(), - data, kWebMHmacSize, - sizeof(iv)))); - return encrypted_buffer; - } - - void GenerateKeyRequest(const uint8* key_id, int key_id_size) { + void GenerateKeyRequest() { EXPECT_CALL(client_, KeyMessageMock(kClearKeySystem, StrNe(std::string()), NotNull(), Gt(0), "")) .WillOnce(SaveArg<1>(&session_id_string_)); - decryptor_.GenerateKeyRequest(kClearKeySystem, key_id, key_id_size); + decryptor_.GenerateKeyRequest(kClearKeySystem, + kInitData, arraysize(kInitData)); } - void AddKeyAndExpectToSucceed(const uint8* key_id, int key_id_size, - const uint8* key, int key_size) { + template <int KeyIdSize, int KeySize> + void AddKeyAndExpectToSucceed(const uint8 (&key_id)[KeyIdSize], + const uint8 (&key)[KeySize]) { EXPECT_CALL(client_, KeyAdded(kClearKeySystem, session_id_string_)); - decryptor_.AddKey(kClearKeySystem, key, key_size, key_id, key_id_size, + decryptor_.AddKey(kClearKeySystem, key, KeySize, key_id, KeyIdSize, session_id_string_); } - void AddKeyAndExpectToFail(const uint8* key_id, int key_id_size, - const uint8* key, int key_size) { + template <int KeyIdSize, int KeySize> + void AddKeyAndExpectToFail(const uint8 (&key_id)[KeyIdSize], + const uint8 (&key)[KeySize]) { EXPECT_CALL(client_, KeyError(kClearKeySystem, session_id_string_, Decryptor::kUnknownError, 0)); - decryptor_.AddKey(kClearKeySystem, key, key_size, key_id, key_id_size, + decryptor_.AddKey(kClearKeySystem, key, KeySize, key_id, KeyIdSize, session_id_string_); } + template <int KeyIdSize> + void SetKeyIdForEncryptedData(const uint8 (&key_id)[KeyIdSize]) { + encrypted_data_->SetDecryptConfig( + scoped_ptr<DecryptConfig>(new DecryptConfig(key_id, KeyIdSize))); + } + MOCK_METHOD2(BufferDecrypted, void(Decryptor::DecryptStatus, const scoped_refptr<DecoderBuffer>&)); - void DecryptAndExpectToSucceed(const uint8* data, int data_size, - const uint8* plain_text, - int plain_text_size, - const uint8* key_id, int key_id_size) { - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(data, data_size, key_id, key_id_size); + void DecryptAndExpectToSucceed() { scoped_refptr<DecoderBuffer> decrypted; EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) .WillOnce(SaveArg<1>(&decrypted)); - decryptor_.Decrypt(encrypted_data, decrypt_cb_); + decryptor_.Decrypt(encrypted_data_, decrypt_cb_); ASSERT_TRUE(decrypted); - ASSERT_EQ(plain_text_size, decrypted->GetDataSize()); - EXPECT_EQ(0, memcmp(plain_text, decrypted->GetData(), plain_text_size)); + int data_length = sizeof(kOriginalData); + ASSERT_EQ(data_length, decrypted->GetDataSize()); + EXPECT_EQ(0, memcmp(kOriginalData, decrypted->GetData(), data_length)); } - void DecryptAndExpectToFail(const uint8* data, int data_size, - const uint8* plain_text, int plain_text_size, - const uint8* key_id, int key_id_size) { - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(data, data_size, key_id, key_id_size); + void DecryptAndExpectToFail() { EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kError, IsNull())); - decryptor_.Decrypt(encrypted_data, decrypt_cb_); + decryptor_.Decrypt(encrypted_data_, decrypt_cb_); } scoped_refptr<DecoderBuffer> encrypted_data_; @@ -256,129 +120,39 @@ class AesDecryptorTest : public testing::Test { }; TEST_F(AesDecryptorTest, NormalDecryption) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + GenerateKeyRequest(); + AddKeyAndExpectToSucceed(kKeyId1, kRightKey); + SetKeyIdForEncryptedData(kKeyId1); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed()); } TEST_F(AesDecryptorTest, WrongKey) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - kWebmWrongKey, arraysize(kWebmWrongKey)); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); -} - -TEST_F(AesDecryptorTest, KeyReplacement) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - kWebmWrongKey, arraysize(kWebmWrongKey)); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); -} - -TEST_F(AesDecryptorTest, WrongSizedKey) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToFail(frame.key_id, frame.key_id_size, - kWebmWrongSizedKey, arraysize(kWebmWrongSizedKey)); -} - -TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); - - const WebmEncryptedData& frame2 = kWebmEncryptedFrames[2]; - GenerateKeyRequest(frame2.key_id, frame2.key_id_size); - AddKeyAndExpectToSucceed(frame2.key_id, frame2.key_id_size, - frame2.key, frame2.key_size); - - const WebmEncryptedData& frame1 = kWebmEncryptedFrames[1]; - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame1.encrypted_data, - frame1.encrypted_data_size, - frame1.plain_text, - frame1.plain_text_size, - frame1.key_id, - frame1.key_id_size)); - - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame2.encrypted_data, - frame2.encrypted_data_size, - frame2.plain_text, - frame2.plain_text_size, - frame2.key_id, - frame2.key_id_size)); + GenerateKeyRequest(); + AddKeyAndExpectToSucceed(kKeyId1, kWrongKey); + SetKeyIdForEncryptedData(kKeyId1); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail()); } -TEST_F(AesDecryptorTest, HmacCheckFailure) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0HmacDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); +TEST_F(AesDecryptorTest, MultipleKeys) { + GenerateKeyRequest(); + AddKeyAndExpectToSucceed(kKeyId1, kRightKey); + AddKeyAndExpectToSucceed(kKeyId2, kWrongKey); + SetKeyIdForEncryptedData(kKeyId1); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed()); } -TEST_F(AesDecryptorTest, IvCheckFailure) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0IvDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); +TEST_F(AesDecryptorTest, KeyReplacement) { + GenerateKeyRequest(); + SetKeyIdForEncryptedData(kKeyId1); + AddKeyAndExpectToSucceed(kKeyId1, kWrongKey); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail()); + AddKeyAndExpectToSucceed(kKeyId1, kRightKey); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed()); } -TEST_F(AesDecryptorTest, DataCheckFailure) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0FrameDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); +TEST_F(AesDecryptorTest, WrongSizedKey) { + GenerateKeyRequest(); + AddKeyAndExpectToFail(kKeyId1, kWrongSizedKey); } -} // namespace media +} // media diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index b60dce9..0131826 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -43,24 +43,17 @@ static const gfx::Rect kVisibleRect(320, 240); static const gfx::Size kNaturalSize(522, 288); static const AVRational kFrameRate = { 100, 1 }; static const AVRational kAspectRatio = { 1, 1 }; - static const char kClearKeySystem[] = "org.w3.clearkey"; static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; -static const uint8 kWrongSecret[] = { +static const uint8 kRightKey[] = { + 0x41, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, + 0x66, 0x75, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x21 +}; +static const uint8 kWrongKey[] = { 0x49, 0x27, 0x6d, 0x20, 0x61, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x2e }; static const uint8 kKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 }; -static const uint8 kSecretKey[] = { - 0xfb, 0x67, 0x8a, 0x91, 0x19, 0x12, 0x7b, 0x6b, - 0x0b, 0x63, 0x11, 0xf8, 0x6f, 0xe1, 0xc4, 0x2d -}; - -static const uint64 kIv = 3735928559; -static const uint8 kHmac[] = { - 0x16, 0xc0, 0x65, 0x1f, 0xf8, 0x0b, 0x36, 0x16, - 0xb8, 0x32, 0x35, 0x56 -}; ACTION_P(ReturnBuffer, buffer) { arg0.Run(buffer ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); @@ -84,13 +77,8 @@ class FFmpegVideoDecoderTest : public testing::Test { end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer(); i_frame_buffer_ = ReadTestDataFile("vp8-I-frame-320x240"); corrupt_i_frame_buffer_ = ReadTestDataFile("vp8-corrupt-I-frame"); - { - scoped_refptr<DecoderBuffer> temp_buffer = ReadTestDataFile( - "vp8-encrypted-I-frame-320x240"); - encrypted_i_frame_buffer_ = DecoderBuffer::CopyFrom( - temp_buffer->GetData() + arraysize(kHmac), - temp_buffer->GetDataSize() - arraysize(kHmac)); - } + encrypted_i_frame_buffer_ = ReadTestDataFile( + "vp8-encrypted-I-frame-320x240"); config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat, kCodedSize, kVisibleRect, @@ -219,24 +207,6 @@ class FFmpegVideoDecoderTest : public testing::Test { message_loop_.RunAllPending(); } - // Generates a 16 byte CTR counter block. The CTR counter block format is a - // CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. - static scoped_array<uint8> GenerateCounterBlock(uint64 iv) { - scoped_array<uint8> counter_block_data( - new uint8[DecryptConfig::kDecryptionKeySize]); - - // Set the IV. - memcpy(counter_block_data.get(), &iv, sizeof(iv)); - - // Set block counter to all 0's. - memset(counter_block_data.get() + sizeof(iv), - 0, - DecryptConfig::kDecryptionKeySize - sizeof(iv)); - - return counter_block_data.Pass(); - } - - MOCK_METHOD2(FrameReady, void(VideoDecoder::DecoderStatus, const scoped_refptr<VideoFrame>&)); @@ -418,9 +388,7 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_SmallerHeight) { DecodeIFrameThenTestFile("vp8-I-frame-320x120", 320, 120); } -// TODO(fgalligan): Enable test when encrypted test data is updated and new -// decryption code is landed. http://crbug.com/132801 -TEST_F(FFmpegVideoDecoderTest, DISABLED_DecodeEncryptedFrame_Normal) { +TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_Normal) { Initialize(); std::string sessing_id_string; EXPECT_CALL(decryptor_client_, @@ -430,18 +398,12 @@ TEST_F(FFmpegVideoDecoderTest, DISABLED_DecodeEncryptedFrame_Normal) { decryptor_->GenerateKeyRequest(kClearKeySystem, kInitData, arraysize(kInitData)); EXPECT_CALL(decryptor_client_, KeyAdded(kClearKeySystem, sessing_id_string)); - decryptor_->AddKey(kClearKeySystem, kSecretKey, arraysize(kSecretKey), + decryptor_->AddKey(kClearKeySystem, kRightKey, arraysize(kRightKey), kKeyId, arraysize(kKeyId), sessing_id_string); // Simulate decoding a single encrypted frame. - scoped_array<uint8> counter_block(GenerateCounterBlock(kIv)); encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>( - new DecryptConfig( - kKeyId, arraysize(kKeyId), - counter_block.get(), DecryptConfig::kDecryptionKeySize, - kHmac, arraysize(kHmac), - sizeof(kIv)))); - + new DecryptConfig(kKeyId, arraysize(kKeyId)))); VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; DecodeSingleFrame(encrypted_i_frame_buffer_, &status, &video_frame); @@ -456,13 +418,8 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_NoKey) { Initialize(); // Simulate decoding a single encrypted frame. - scoped_array<uint8> counter_block(GenerateCounterBlock(kIv)); encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>( - new DecryptConfig( - kKeyId, arraysize(kKeyId), - counter_block.get(), DecryptConfig::kDecryptionKeySize, - kHmac, arraysize(kHmac), - sizeof(kIv)))); + new DecryptConfig(kKeyId, arraysize(kKeyId)))); EXPECT_CALL(*demuxer_, Read(_)) .WillRepeatedly(ReturnBuffer(encrypted_i_frame_buffer_)); @@ -482,27 +439,31 @@ TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_NoKey) { TEST_F(FFmpegVideoDecoderTest, DecodeEncryptedFrame_WrongKey) { Initialize(); EXPECT_CALL(decryptor_client_, KeyAdded("", "")); - decryptor_->AddKey("", kWrongSecret, arraysize(kWrongSecret), + decryptor_->AddKey("", kWrongKey, arraysize(kWrongKey), kKeyId, arraysize(kKeyId), ""); - // Simulate decoding a single encrypted frame. - scoped_array<uint8> counter_block(GenerateCounterBlock(kIv)); encrypted_i_frame_buffer_->SetDecryptConfig(scoped_ptr<DecryptConfig>( - new DecryptConfig( - kKeyId, arraysize(kKeyId), - counter_block.get(), DecryptConfig::kDecryptionKeySize, - kHmac, arraysize(kHmac), - sizeof(kIv)))); - + new DecryptConfig(kKeyId, arraysize(kKeyId)))); EXPECT_CALL(*demuxer_, Read(_)) .WillRepeatedly(ReturnBuffer(encrypted_i_frame_buffer_)); + // Using the wrong key on some platforms doesn't cause an decryption error but + // actually attempts to decode the content, however we're unable to + // distinguish between the two (see http://crbug.com/124434). +#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + EXPECT_CALL(statistics_cb_, OnStatistics(_)); +#endif + // Our read should still get satisfied with end of stream frame during an // error. VideoDecoder::DecoderStatus status; scoped_refptr<VideoFrame> video_frame; Read(&status, &video_frame); +#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + EXPECT_EQ(VideoDecoder::kDecodeError, status); +#else EXPECT_EQ(VideoDecoder::kDecryptError, status); +#endif EXPECT_FALSE(video_frame); message_loop_.RunAllPending(); diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc index cae8b78..c207858 100644 --- a/media/filters/pipeline_integration_test.cc +++ b/media/filters/pipeline_integration_test.cc @@ -18,12 +18,6 @@ static const char kSourceId[] = "SourceId"; static const char kClearKeySystem[] = "org.w3.clearkey"; static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; -// Key used to encrypt video track in test file "bear-320x240-encrypted.webm". -static const uint8 kSecretKey[] = { - 0xfb, 0x67, 0x8a, 0x91, 0x19, 0x12, 0x7b, 0x6b, - 0x0b, 0x63, 0x11, 0xf8, 0x6f, 0xe1, 0xc4, 0x2d -}; - // Helper class that emulates calls made on the ChunkDemuxer by the // Media Source API. class MockMediaSource : public ChunkDemuxerClient { @@ -102,7 +96,7 @@ class MockMediaSource : public ChunkDemuxerClient { virtual void DemuxerNeedKey(scoped_array<uint8> init_data, int init_data_size) { DCHECK(init_data.get()); - DCHECK_GT(init_data_size, 0); + DCHECK_EQ(init_data_size, 16); DCHECK(decryptor_client_); decryptor_client_->NeedKey("", "", init_data.Pass(), init_data_size); } @@ -172,7 +166,9 @@ class FakeDecryptorClient : public DecryptorClient { EXPECT_FALSE(current_key_system_.empty()); EXPECT_FALSE(current_session_id_.empty()); - decryptor_.AddKey(current_key_system_, kSecretKey, arraysize(kSecretKey), + // In test file bear-320x240-encrypted.webm, the decryption key is equal to + // |init_data|. + decryptor_.AddKey(current_key_system_, init_data.get(), init_data_length, init_data.get(), init_data_length, current_session_id_); } @@ -284,9 +280,7 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource) { Stop(); } -// TODO(fgalligan): Enable test when encrypted test data is updated and new -// decryption code is landed. http://crbug.com/132801 -TEST_F(PipelineIntegrationTest, DISABLED_EncryptedPlayback) { +TEST_F(PipelineIntegrationTest, EncryptedPlayback) { MockMediaSource source("bear-320x240-encrypted.webm", 219726, true, true); FakeDecryptorClient encrypted_media; StartPipelineWithEncryptedMedia(&source, &encrypted_media); diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index 20d131e..c5a2fdc 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -5,31 +5,12 @@ #include "media/webm/webm_cluster_parser.h" #include "base/logging.h" -#include "base/sys_byteorder.h" #include "media/base/data_buffer.h" #include "media/base/decrypt_config.h" #include "media/webm/webm_constants.h" namespace media { -// Generates a 16 byte CTR counter block. The CTR counter block format is a -// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. -// Always returns a valid pointer to a buffer of kDecryptionKeySize bytes. -static scoped_array<uint8> GenerateCounterBlock(uint64 iv) { - scoped_array<uint8> counter_block_data( - new uint8[DecryptConfig::kDecryptionKeySize]); - - // Set the IV. - memcpy(counter_block_data.get(), &iv, sizeof(iv)); - - // Set block counter to all 0's. - memset(counter_block_data.get() + sizeof(iv), - 0, - DecryptConfig::kDecryptionKeySize - sizeof(iv)); - - return counter_block_data.Pass(); -} - WebMClusterParser::WebMClusterParser(int64 timecode_scale, int audio_track_num, int video_track_num, @@ -212,33 +193,15 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( (cluster_timecode_ + timecode) * timecode_multiplier_); - // Every encrypted Block has an HMAC and IV prepended to it. Current encrypted - // WebM request for comments specification is here - // http://wiki.webmproject.org/encryption/webm-encryption-rfc - bool encrypted = track_num == video_.track_num() && - video_encryption_key_id_.get(); - // If encrypted skip past the HMAC. Encrypted buffers must include the IV and - // the encrypted frame because the decryptor will verify this data before - // decryption. The HMAC and IV will be copied into DecryptConfig. - int offset = (encrypted) ? kWebMHmacSize : 0; - // The first bit of the flags is set when the block contains only keyframes. // http://www.matroska.org/technical/specs/index.html bool is_keyframe = (flags & 0x80) != 0; scoped_refptr<StreamParserBuffer> buffer = - StreamParserBuffer::CopyFrom(data + offset, size - offset, is_keyframe); - - if (encrypted) { - uint64 network_iv; - memcpy(&network_iv, data + kWebMHmacSize, sizeof(network_iv)); - const uint64 iv = base::NetToHost64(network_iv); + StreamParserBuffer::CopyFrom(data, size, is_keyframe); - scoped_array<uint8> counter_block(GenerateCounterBlock(iv)); + if (track_num == video_.track_num() && video_encryption_key_id_.get()) { buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( - video_encryption_key_id_.get(), video_encryption_key_id_size_, - counter_block.get(), DecryptConfig::kDecryptionKeySize, - data, kWebMHmacSize, - sizeof(iv)))); + video_encryption_key_id_.get(), video_encryption_key_id_size_))); } buffer->SetTimestamp(timestamp); diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h index 2bdb1e7..0af0361 100644 --- a/media/webm/webm_cluster_parser.h +++ b/media/webm/webm_cluster_parser.h @@ -19,10 +19,6 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { public: typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; - // Size is defined by the WebM encryption specification. - // http://wiki.webmproject.org/encryption/webm-encryption-rfc - static const int kIvSize = 8; - WebMClusterParser(int64 timecode_scale, int audio_track_num, int video_track_num, diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h index 93041b2..b6ba0a2 100644 --- a/media/webm/webm_constants.h +++ b/media/webm/webm_constants.h @@ -12,7 +12,6 @@ namespace media { // WebM element IDs. // This is a subset of the IDs in the Matroska spec. // http://www.matroska.org/technical/specs/index.html -const int kWebMIdAESSettingsCipherMode = 0x47E8; const int kWebMIdAspectRatioType = 0x54B3; const int kWebMIdAttachedFile = 0x61A7; const int kWebMIdAttachmentLink = 0x7446; @@ -63,7 +62,6 @@ const int kWebMIdColorSpace = 0x2EB524; const int kWebMIdContentCompAlgo = 0x4254; const int kWebMIdContentCompression = 0x5034; const int kWebMIdContentCompSettings = 0x4255; -const int kWebMIdContentEncAESSettings = 0x47E7; const int kWebMIdContentEncAlgo = 0x47E1; const int kWebMIdContentEncKeyID = 0x47E2; const int kWebMIdContentEncoding = 0x6240; @@ -199,11 +197,6 @@ const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF); const uint8 kWebMFlagKeyframe = 0x80; -// The size is from the WebM encrypted specification. Current encrypted WebM -// request for comments specification is here -// http://wiki.webmproject.org/encryption/webm-encryption-rfc -const int kWebMHmacSize = 12; - } // namespace media #endif // MEDIA_WEBM_WEBM_CONSTANTS_H_ diff --git a/media/webm/webm_content_encodings.cc b/media/webm/webm_content_encodings.cc index 540ac99..a5b7bfe 100644 --- a/media/webm/webm_content_encodings.cc +++ b/media/webm/webm_content_encodings.cc @@ -12,8 +12,7 @@ ContentEncoding::ContentEncoding() scope_(kScopeInvalid), type_(kTypeInvalid), encryption_algo_(kEncAlgoInvalid), - encryption_key_id_size_(0), - cipher_mode_(kCipherModeInvalid) { + encryption_key_id_size_(0) { } ContentEncoding::~ContentEncoding() {} diff --git a/media/webm/webm_content_encodings.h b/media/webm/webm_content_encodings.h index ea903d8..2947a26 100644 --- a/media/webm/webm_content_encodings.h +++ b/media/webm/webm_content_encodings.h @@ -42,11 +42,6 @@ class MEDIA_EXPORT ContentEncoding { kEncAlgoAes = 5, }; - enum CipherMode { - kCipherModeInvalid = 0, - kCipherModeCtr = 1, - }; - ContentEncoding(); ~ContentEncoding(); @@ -69,9 +64,6 @@ class MEDIA_EXPORT ContentEncoding { void SetEncryptionKeyId(const uint8* encryption_key_id, int size); - CipherMode cipher_mode() const { return cipher_mode_; } - void set_cipher_mode(CipherMode mode) { cipher_mode_ = mode; } - private: int64 order_; Scope scope_; @@ -79,7 +71,6 @@ class MEDIA_EXPORT ContentEncoding { EncryptionAlgo encryption_algo_; scoped_array<uint8> encryption_key_id_; int encryption_key_id_size_; - CipherMode cipher_mode_; DISALLOW_COPY_AND_ASSIGN(ContentEncoding); }; diff --git a/media/webm/webm_content_encodings_client.cc b/media/webm/webm_content_encodings_client.cc index a24b4bf..086f85b 100644 --- a/media/webm/webm_content_encodings_client.cc +++ b/media/webm/webm_content_encodings_client.cc @@ -50,11 +50,6 @@ WebMParserClient* WebMContentEncodingsClient::OnListStart(int id) { return this; } - if (id == kWebMIdContentEncAESSettings) { - DCHECK(cur_content_encoding_.get()); - return this; - } - // This should not happen if WebMListParser is working properly. DCHECK(false); return NULL; @@ -126,13 +121,6 @@ bool WebMContentEncodingsClient::OnListEnd(int id) { return true; } - if (id == kWebMIdContentEncAESSettings) { - if (cur_content_encoding_->cipher_mode() == - ContentEncoding::kCipherModeInvalid) - cur_content_encoding_->set_cipher_mode(ContentEncoding::kCipherModeCtr); - return true; - } - // This should not happen if WebMListParser is working properly. DCHECK(false); return false; @@ -218,23 +206,6 @@ bool WebMContentEncodingsClient::OnUInt(int id, int64 val) { return true; } - if (id == kWebMIdAESSettingsCipherMode) { - if (cur_content_encoding_->cipher_mode() != - ContentEncoding::kCipherModeInvalid) { - DVLOG(1) << "Unexpected multiple AESSettingsCipherMode."; - return false; - } - - if (val != ContentEncoding::kCipherModeCtr) { - DVLOG(1) << "Unexpected AESSettingsCipherMode " << val << "."; - return false; - } - - cur_content_encoding_->set_cipher_mode( - static_cast<ContentEncoding::CipherMode>(val)); - return true; - } - // This should not happen if WebMListParser is working properly. DCHECK(false); return false; diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc index 9952efa..28c847b 100644 --- a/media/webm/webm_parser.cc +++ b/media/webm/webm_parser.cc @@ -7,9 +7,6 @@ // This file contains code to parse WebM file elements. It was created // from information in the Matroska spec. // http://www.matroska.org/technical/specs/index.html -// This file contains code for encrypted WebM. Current WebM -// encrypted request for comments specification is here -// http://wiki.webmproject.org/encryption/webm-encryption-rfc #include <iomanip> @@ -235,7 +232,6 @@ static const ElementIdInfo kContentCompressionIds[] = { }; static const ElementIdInfo kContentEncryptionIds[] = { - {LIST, kWebMIdContentEncAESSettings}, {UINT, kWebMIdContentEncAlgo}, {BINARY, kWebMIdContentEncKeyID}, {BINARY, kWebMIdContentSignature}, @@ -244,10 +240,6 @@ static const ElementIdInfo kContentEncryptionIds[] = { {UINT, kWebMIdContentSigHashAlgo}, }; -static const ElementIdInfo kContentEncAESSettingsIds[] = { - {UINT, kWebMIdAESSettingsCipherMode}, -}; - static const ElementIdInfo kCuesIds[] = { {LIST, kWebMIdCuePoint}, }; @@ -384,7 +376,6 @@ static const ListElementInfo kListElementInfo[] = { LIST_ELEMENT_INFO(kWebMIdContentEncoding, 4, kContentEncodingIds), LIST_ELEMENT_INFO(kWebMIdContentCompression, 5, kContentCompressionIds), LIST_ELEMENT_INFO(kWebMIdContentEncryption, 5, kContentEncryptionIds), - LIST_ELEMENT_INFO(kWebMIdContentEncAESSettings, 6, kContentEncAESSettingsIds), LIST_ELEMENT_INFO(kWebMIdCues, 1, kCuesIds), LIST_ELEMENT_INFO(kWebMIdCuePoint, 2, kCuePointIds), LIST_ELEMENT_INFO(kWebMIdCueTrackPositions, 3, kCueTrackPositionsIds), diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc index d141bfd..602d227 100644 --- a/media/webm/webm_stream_parser.cc +++ b/media/webm/webm_stream_parser.cc @@ -352,7 +352,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { if (tracks_parser.video_encryption_key_id()) { int key_id_size = tracks_parser.video_encryption_key_id_size(); CHECK_GT(key_id_size, 0); - CHECK_LT(key_id_size, 2048); scoped_array<uint8> key_id(new uint8[key_id_size]); memcpy(key_id.get(), tracks_parser.video_encryption_key_id(), key_id_size); need_key_cb_.Run(key_id.Pass(), key_id_size); |