diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/media/cdm | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) | |
download | qtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/media/cdm')
-rw-r--r-- | chromium/media/cdm/aes_decryptor.cc | 221 | ||||
-rw-r--r-- | chromium/media/cdm/aes_decryptor.h | 6 | ||||
-rw-r--r-- | chromium/media/cdm/aes_decryptor_unittest.cc | 945 | ||||
-rw-r--r-- | chromium/media/cdm/ppapi/cdm_wrapper.cc | 64 |
4 files changed, 680 insertions, 556 deletions
diff --git a/chromium/media/cdm/aes_decryptor.cc b/chromium/media/cdm/aes_decryptor.cc index da11442a37e..33717e03a58 100644 --- a/chromium/media/cdm/aes_decryptor.cc +++ b/chromium/media/cdm/aes_decryptor.cc @@ -6,9 +6,13 @@ #include <vector> +#include "base/base64.h" +#include "base/json/json_reader.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/values.h" #include "crypto/encryptor.h" #include "crypto/symmetric_key.h" #include "media/base/audio_decoder_config.h" @@ -26,6 +30,8 @@ enum ClearBytesBufferSel { kDstContainsClearBytes }; +typedef std::vector<std::pair<std::string, std::string> > JWKKeys; + static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, const ClearBytesBufferSel sel, const uint8* src, @@ -43,6 +49,105 @@ static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, } } +// Processes a JSON Web Key to extract the key id and key value. Adds the +// id/value pair to |jwk_keys| and returns true on success. +static bool ProcessSymmetricKeyJWK(const DictionaryValue& jwk, + JWKKeys* jwk_keys) { + // A symmetric keys JWK looks like the following in JSON: + // { "kty":"oct", + // "kid":"AAECAwQFBgcICQoLDA0ODxAREhM=", + // "k":"FBUWFxgZGhscHR4fICEiIw==" } + // There may be other properties specified, but they are ignored. + // Ref: http://tools.ietf.org/html/draft-ietf-jose-json-web-key-14 + // and: + // http://tools.ietf.org/html/draft-jones-jose-json-private-and-symmetric-key-00 + + // Have found a JWK, start by checking that it is a symmetric key. + std::string type; + if (!jwk.GetString("kty", &type) || type != "oct") { + DVLOG(1) << "JWK is not a symmetric key"; + return false; + } + + // Get the key id and actual key parameters. + std::string encoded_key_id; + std::string encoded_key; + if (!jwk.GetString("kid", &encoded_key_id)) { + DVLOG(1) << "Missing 'kid' parameter"; + return false; + } + if (!jwk.GetString("k", &encoded_key)) { + DVLOG(1) << "Missing 'k' parameter"; + return false; + } + + // Key ID and key are base64-encoded strings, so decode them. + // TODO(jrummell): The JWK spec and the EME spec don't say that 'kid' must be + // base64-encoded (they don't say anything at all). Verify with the EME spec. + std::string decoded_key_id; + std::string decoded_key; + if (!base::Base64Decode(encoded_key_id, &decoded_key_id) || + decoded_key_id.empty()) { + DVLOG(1) << "Invalid 'kid' value"; + return false; + } + if (!base::Base64Decode(encoded_key, &decoded_key) || + decoded_key.length() != + static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { + DVLOG(1) << "Invalid length of 'k' " << decoded_key.length(); + return false; + } + + // Add the decoded key ID and the decoded key to the list. + jwk_keys->push_back(std::make_pair(decoded_key_id, decoded_key)); + return true; +} + +// Extracts the JSON Web Keys from a JSON Web Key Set. If |input| looks like +// a valid JWK Set, then true is returned and |jwk_keys| is updated to contain +// the list of keys found. Otherwise return false. +static bool ExtractJWKKeys(const std::string& input, JWKKeys* jwk_keys) { + // TODO(jrummell): The EME spec references a smaller set of allowed ASCII + // values. Verify with spec that the smaller character set is needed. + if (!IsStringASCII(input)) + return false; + + scoped_ptr<Value> root(base::JSONReader().ReadToValue(input)); + if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) + return false; + + // A JSON Web Key Set looks like the following in JSON: + // { "keys": [ JWK1, JWK2, ... ] } + // (See ProcessSymmetricKeyJWK() for description of JWK.) + // There may be other properties specified, but they are ignored. + // Locate the set from the dictionary. + DictionaryValue* dictionary = static_cast<DictionaryValue*>(root.get()); + ListValue* list_val = NULL; + if (!dictionary->GetList("keys", &list_val)) { + DVLOG(1) << "Missing 'keys' parameter or not a list in JWK Set"; + return false; + } + + // Create a local list of keys, so that |jwk_keys| only gets updated on + // success. + JWKKeys local_keys; + for (size_t i = 0; i < list_val->GetSize(); ++i) { + DictionaryValue* jwk = NULL; + if (!list_val->GetDictionary(i, &jwk)) { + DVLOG(1) << "Unable to access 'keys'[" << i << "] in JWK Set"; + return false; + } + if (!ProcessSymmetricKeyJWK(*jwk, &local_keys)) { + DVLOG(1) << "Error from 'keys'[" << i << "]"; + return false; + } + } + + // Successfully processed all JWKs in the set. + jwk_keys->swap(local_keys); + return true; +} + // 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, @@ -67,10 +172,11 @@ static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, const int data_offset = input.decrypt_config()->data_offset(); const char* sample = reinterpret_cast<const char*>(input.data() + data_offset); - int sample_size = input.data_size() - data_offset; + DCHECK_GT(input.data_size(), data_offset); + size_t sample_size = static_cast<size_t>(input.data_size() - data_offset); - DCHECK_GT(sample_size, 0) << "No sample data to be decrypted."; - if (sample_size <= 0) + DCHECK_GT(sample_size, 0U) << "No sample data to be decrypted."; + if (sample_size == 0) return NULL; if (input.decrypt_config()->subsamples().empty()) { @@ -90,13 +196,18 @@ static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, const std::vector<SubsampleEntry>& subsamples = input.decrypt_config()->subsamples(); - int total_clear_size = 0; - int total_encrypted_size = 0; + size_t total_clear_size = 0; + size_t 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; + // Check for overflow. This check is valid because *_size is unsigned. + DCHECK(total_clear_size >= subsamples[i].clear_bytes); + if (total_encrypted_size < subsamples[i].cypher_bytes) + return NULL; } - if (total_clear_size + total_encrypted_size != sample_size) { + size_t total_size = total_clear_size + total_encrypted_size; + if (total_size < total_clear_size || total_size != sample_size) { DVLOG(1) << "Subsample sizes do not equal input size"; return NULL; } @@ -170,41 +281,65 @@ void AesDecryptor::AddKey(const uint8* key, CHECK(key); CHECK_GT(key_length, 0); + // AddKey() is called from update(), where the key(s) are passed as a JSON + // Web Key (JWK) set. Each JWK needs to be a symmetric key ('kty' = "oct"), + // with 'kid' being the base64-encoded key id, and 'k' being the + // base64-encoded key. + // + // For backwards compatibility with v0.1b of the spec (where |key| is the raw + // key and |init_data| is the key id), if |key| is not valid JSON, then + // attempt to process it as a raw key. + // 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) { - DVLOG(1) << "Invalid key length: " << key_length; - key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); - return; - } - // TODO(xhwang): Fix the decryptor to accept no |init_data|. See - // http://crbug.com/123265. Until then, ensure a non-empty value is passed. - static const uint8 kDummyInitData[1] = { 0 }; - if (!init_data) { - init_data = kDummyInitData; - init_data_length = arraysize(kDummyInitData); - } + std::string key_string(reinterpret_cast<const char*>(key), key_length); + JWKKeys jwk_keys; + if (ExtractJWKKeys(key_string, &jwk_keys)) { + // Since |key| represents valid JSON, init_data must be empty. + DCHECK(!init_data); + DCHECK_EQ(init_data_length, 0); - // TODO(xhwang): For now, use |init_data| for key ID. Make this more spec - // compliant later (http://crbug.com/123262, http://crbug.com/123265). - 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) { - DVLOG(1) << "Could not create key."; - key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); - return; - } + // Make sure that at least one key was extracted. + if (jwk_keys.empty()) { + key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); + return; + } + for (JWKKeys::iterator it = jwk_keys.begin() ; it != jwk_keys.end(); ++it) { + if (!AddDecryptionKey(it->first, it->second)) { + key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); + return; + } + } + } else { + // v0.1b backwards compatibility support. + // TODO(jrummell): Remove this code once v0.1b no longer supported. - if (!decryption_key->Init()) { - DVLOG(1) << "Could not initialize decryption key."; - key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); - return; - } + if (key_string.length() != + static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { + DVLOG(1) << "Invalid key length: " << key_string.length(); + key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); + return; + } + + // TODO(xhwang): Fix the decryptor to accept no |init_data|. See + // http://crbug.com/123265. Until then, ensure a non-empty value is passed. + static const uint8 kDummyInitData[1] = {0}; + if (!init_data) { + init_data = kDummyInitData; + init_data_length = arraysize(kDummyInitData); + } - SetKey(key_id_string, decryption_key.Pass()); + // TODO(xhwang): For now, use |init_data| for key ID. Make this more spec + // compliant later (http://crbug.com/123262, http://crbug.com/123265). + std::string key_id_string(reinterpret_cast<const char*>(init_data), + init_data_length); + if (!AddDecryptionKey(key_id_string, key_string)) { + // Error logged in AddDecryptionKey() + key_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); + return; + } + } if (!new_audio_key_cb_.is_null()) new_audio_key_cb_.Run(); @@ -306,8 +441,19 @@ void AesDecryptor::DeinitializeDecoder(StreamType stream_type) { NOTREACHED() << "AesDecryptor does not support audio/video decoding"; } -void AesDecryptor::SetKey(const std::string& key_id, - scoped_ptr<DecryptionKey> decryption_key) { +bool AesDecryptor::AddDecryptionKey(const std::string& key_id, + const std::string& key_string) { + scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); + if (!decryption_key) { + DVLOG(1) << "Could not create key."; + return false; + } + + if (!decryption_key->Init()) { + DVLOG(1) << "Could not initialize decryption key."; + return false; + } + base::AutoLock auto_lock(key_map_lock_); KeyMap::iterator found = key_map_.find(key_id); if (found != key_map_.end()) { @@ -315,6 +461,7 @@ void AesDecryptor::SetKey(const std::string& key_id, key_map_.erase(found); } key_map_[key_id] = decryption_key.release(); + return true; } AesDecryptor::DecryptionKey* AesDecryptor::GetKey( diff --git a/chromium/media/cdm/aes_decryptor.h b/chromium/media/cdm/aes_decryptor.h index fda5a0facab..3ab4bc0f9f4 100644 --- a/chromium/media/cdm/aes_decryptor.h +++ b/chromium/media/cdm/aes_decryptor.h @@ -86,8 +86,10 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys, public Decryptor { DISALLOW_COPY_AND_ASSIGN(DecryptionKey); }; - // Sets |key| for |key_id|. The AesDecryptor takes the ownership of the |key|. - void SetKey(const std::string& key_id, scoped_ptr<DecryptionKey> key); + // Creates a DecryptionKey using |key_string| and associates it with |key_id|. + // Returns true if successful. + bool AddDecryptionKey(const std::string& key_id, + const std::string& key_string); // Gets a DecryptionKey associated with |key_id|. The AesDecryptor still owns // the key. Returns NULL if no key is associated with |key_id|. diff --git a/chromium/media/cdm/aes_decryptor_unittest.cc b/chromium/media/cdm/aes_decryptor_unittest.cc index 1edb8e82220..a4b865c4690 100644 --- a/chromium/media/cdm/aes_decryptor_unittest.cc +++ b/chromium/media/cdm/aes_decryptor_unittest.cc @@ -16,124 +16,79 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; -using ::testing::ElementsAreArray; using ::testing::Gt; using ::testing::IsNull; using ::testing::NotNull; using ::testing::SaveArg; -using ::testing::StrEq; using ::testing::StrNe; MATCHER(IsEmpty, "") { return arg.empty(); } 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. Frame 3 is unencrypted. -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 - { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf0, 0xd1, 0x12, 0xd5, 0x24, 0x81, 0x96, - 0x55, 0x1b, 0x68, 0x9f, 0x38, 0x91, 0x85 - }, 23 - }, { - // 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 - { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x57, 0x66, 0xf4, 0x12, 0x1a, 0xed, 0xb5, - 0x79, 0x1c, 0x8e, 0x25, 0xd7, 0x17, 0xe7, 0x5e, - 0x16, 0xe3, 0x40, 0x08, 0x27, 0x11, 0xe9 - }, 31 - }, { - // 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 - { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9c, 0x71, 0x26, 0x57, 0x3e, 0x25, 0x37, - 0xf7, 0x31, 0x81, 0x19, 0x64, 0xce, 0xbc - }, 23 - }, { - // plaintext - "Changed Original data.", 22, - // 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 - { 0x00, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, - 0x20, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, - 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e - }, 23 - } -}; - -static const uint8 kWebmWrongSizedKey[] = { 0x20, 0x20 }; - -static const uint8 kSubsampleOriginalData[] = "Original subsample data."; -static const int kSubsampleOriginalDataSize = 24; +static const uint8 kOriginalData[] = "Original subsample data."; +static const int kOriginalDataSize = 24; -static const uint8 kSubsampleKeyId[] = { 0x00, 0x01, 0x02, 0x03 }; +static const uint8 kKeyId[] = { + // base64 equivalent is AAECAw== + 0x00, 0x01, 0x02, 0x03 +}; -static const uint8 kSubsampleKey[] = { - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13 +static const uint8 kKey[] = { + // base64 equivalent is BAUGBwgJCgsMDQ4PEBESEw== + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13 }; -static const uint8 kSubsampleIv[] = { +static const char kKeyAsJWK[] = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAw==\"," + " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\"" + " }" + " ]" + "}"; + +static const char kWrongKeyAsJWK[] = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAw==\"," + " \"k\": \"7u7u7u7u7u7u7u7u7u7u7g==\"" + " }" + " ]" + "}"; + +static const char kWrongSizedKeyAsJWK[] = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAw==\"," + " \"k\": \"AAECAw==\"" + " }" + " ]" + "}"; + +static const uint8 kIv[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -// kSubsampleOriginalData encrypted with kSubsampleKey and kSubsampleIv using +// kOriginalData encrypted with kKey and kIv but without any subsamples (or +// equivalently using kSubsampleEntriesCypherOnly). +static const uint8 kEncryptedData[] = { + 0x2f, 0x03, 0x09, 0xef, 0x71, 0xaf, 0x31, 0x16, + 0xfa, 0x9d, 0x18, 0x43, 0x1e, 0x96, 0x71, 0xb5, + 0xbf, 0xf5, 0x30, 0x53, 0x9a, 0x20, 0xdf, 0x95 +}; + +// kOriginalData encrypted with kSubsampleKey and kSubsampleIv using // kSubsampleEntriesNormal. static const uint8 kSubsampleEncryptedData[] = { 0x4f, 0x72, 0x09, 0x16, 0x09, 0xe6, 0x79, 0xad, @@ -141,25 +96,42 @@ static const uint8 kSubsampleEncryptedData[] = { 0x4d, 0x08, 0xd7, 0x78, 0xa4, 0xa7, 0xf1, 0x2e }; -// kSubsampleEncryptedData with 8 bytes padding at the beginning. -static const uint8 kPaddedSubsampleEncryptedData[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x4f, 0x72, 0x09, 0x16, 0x09, 0xe6, 0x79, 0xad, - 0x70, 0x73, 0x75, 0x62, 0x09, 0xbb, 0x83, 0x1d, - 0x4d, 0x08, 0xd7, 0x78, 0xa4, 0xa7, 0xf1, 0x2e +static const uint8 kOriginalData2[] = "Changed Original data."; + +static const uint8 kIv2[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -// kSubsampleOriginalData encrypted with kSubsampleKey and kSubsampleIv but -// without any subsamples (or equivalently using kSubsampleEntriesCypherOnly). -static const uint8 kEncryptedData[] = { - 0x2f, 0x03, 0x09, 0xef, 0x71, 0xaf, 0x31, 0x16, - 0xfa, 0x9d, 0x18, 0x43, 0x1e, 0x96, 0x71, 0xb5, - 0xbf, 0xf5, 0x30, 0x53, 0x9a, 0x20, 0xdf, 0x95 +static const uint8 kKeyId2[] = { + // base64 equivalent is AAECAwQFBgcICQoLDA0ODxAREhM= + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 +}; + +static const char kKey2AsJWK[] = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM=\"," + " \"k\": \"FBUWFxgZGhscHR4fICEiIw==\"" + " }" + " ]" + "}"; + +// 'k' in bytes is x14x15x16x17x18x19x1ax1bx1cx1dx1ex1fx20x21x22x23 + +static const uint8 kEncryptedData2[] = { + 0x57, 0x66, 0xf4, 0x12, 0x1a, 0xed, 0xb5, 0x79, + 0x1c, 0x8e, 0x25, 0xd7, 0x17, 0xe7, 0x5e, 0x16, + 0xe3, 0x40, 0x08, 0x27, 0x11, 0xe9 }; // Subsample entries for testing. The sum of |cypher_bytes| and |clear_bytes| of -// all entries must be equal to kSubsampleOriginalDataSize to make the subsample -// entries valid. +// all entries must be equal to kOriginalDataSize to make the subsample entries +// valid. static const SubsampleEntry kSubsampleEntriesNormal[] = { { 2, 7 }, @@ -167,6 +139,18 @@ static const SubsampleEntry kSubsampleEntriesNormal[] = { { 1, 0 } }; +static const SubsampleEntry kSubsampleEntriesWrongSize[] = { + { 3, 6 }, // This entry doesn't match the correct entry. + { 3, 11 }, + { 1, 0 } +}; + +static const SubsampleEntry kSubsampleEntriesInvalidTotalSize[] = { + { 1, 1000 }, // This entry is too large. + { 3, 11 }, + { 1, 0 } +}; + static const SubsampleEntry kSubsampleEntriesClearOnly[] = { { 7, 0 }, { 8, 0 }, @@ -179,74 +163,24 @@ static const SubsampleEntry kSubsampleEntriesCypherOnly[] = { { 0, 10 } }; -// 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. -// |iv_size| is the size of |iv| in bytes. Returns a string of -// kDecryptionKeySize bytes. -static std::string GenerateCounterBlock(const uint8* iv, int iv_size) { - CHECK_GT(iv_size, 0); - CHECK_LE(iv_size, DecryptConfig::kDecryptionKeySize); - - std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); - counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); - return counter_block; -} - -// 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 -// a signal byte prepended to a frame. If the frame is encrypted then an IV is -// prepended to the Block. Current encrypted WebM request for comments -// specification is here -// http://wiki.webmproject.org/encryption/webm-encryption-rfc -static 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, data_size); - CHECK(encrypted_buffer.get()); - DCHECK_EQ(kWebMSignalByteSize, 1); - - uint8 signal_byte = data[0]; - int data_offset = kWebMSignalByteSize; - - // Setting the DecryptConfig object of the buffer while leaving the - // initialization vector empty will tell the decryptor that the frame is - // unencrypted. - std::string counter_block_str; - - if (signal_byte & kWebMFlagEncryptedFrame) { - counter_block_str = GenerateCounterBlock(data + data_offset, kWebMIvSize); - data_offset += kWebMIvSize; - } - - encrypted_buffer->set_decrypt_config( - scoped_ptr<DecryptConfig>(new DecryptConfig( - std::string(reinterpret_cast<const char*>(key_id), key_id_size), - counter_block_str, - data_offset, - std::vector<SubsampleEntry>()))); - return encrypted_buffer; -} - -// TODO(xhwang): Refactor this function to encapsulate more details about -// creating an encrypted DecoderBuffer with subsamples so we don't have so much -// boilerplate code in each test before calling this function. -static scoped_refptr<DecoderBuffer> CreateSubsampleEncryptedBuffer( - const uint8* data, int data_size, - const uint8* key_id, int key_id_size, - const uint8* iv, int iv_size, - int data_offset, +static scoped_refptr<DecoderBuffer> CreateEncryptedBuffer( + const std::vector<uint8>& data, + const std::vector<uint8>& key_id, + const std::vector<uint8>& iv, + int offset, const std::vector<SubsampleEntry>& subsample_entries) { - scoped_refptr<DecoderBuffer> encrypted_buffer = - DecoderBuffer::CopyFrom(data, data_size); + DCHECK(!data.empty()); + int padded_size = offset + data.size(); + scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(padded_size)); + memcpy(encrypted_buffer->writable_data() + offset, &data[0], data.size()); CHECK(encrypted_buffer.get()); - encrypted_buffer->set_decrypt_config( - scoped_ptr<DecryptConfig>(new DecryptConfig( - std::string(reinterpret_cast<const char*>(key_id), key_id_size), - std::string(reinterpret_cast<const char*>(iv), iv_size), - data_offset, - subsample_entries))); + std::string key_id_string( + reinterpret_cast<const char*>(key_id.empty() ? NULL : &key_id[0]), + key_id.size()); + std::string iv_string( + reinterpret_cast<const char*>(iv.empty() ? NULL : &iv[0]), iv.size()); + encrypted_buffer->set_decrypt_config(scoped_ptr<DecryptConfig>( + new DecryptConfig(key_id_string, iv_string, offset, subsample_entries))); return encrypted_buffer; } @@ -259,78 +193,116 @@ class AesDecryptorTest : public testing::Test { base::Bind(&AesDecryptorTest::KeyMessage, base::Unretained(this))), decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted, base::Unretained(this))), - subsample_entries_normal_( + original_data_(kOriginalData, kOriginalData + kOriginalDataSize), + encrypted_data_(kEncryptedData, + kEncryptedData + arraysize(kEncryptedData)), + subsample_encrypted_data_( + kSubsampleEncryptedData, + kSubsampleEncryptedData + arraysize(kSubsampleEncryptedData)), + key_id_(kKeyId, kKeyId + arraysize(kKeyId)), + iv_(kIv, kIv + arraysize(kIv)), + normal_subsample_entries_( kSubsampleEntriesNormal, kSubsampleEntriesNormal + arraysize(kSubsampleEntriesNormal)) { } protected: - void GenerateKeyRequest(const uint8* key_id, int key_id_size) { - EXPECT_CALL(*this, KeyMessage( - StrNe(std::string()), ElementsAreArray(key_id, key_id_size), "")) + void GenerateKeyRequest(const std::vector<uint8>& key_id) { + DCHECK(!key_id.empty()); + EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), key_id, "")) .WillOnce(SaveArg<0>(&session_id_string_)); EXPECT_TRUE(decryptor_.GenerateKeyRequest( - std::string(), key_id, key_id_size)); + std::string(), &key_id[0], key_id.size())); } - void AddKeyAndExpectToSucceed(const uint8* key_id, int key_id_size, - const uint8* key, int key_size) { - EXPECT_CALL(*this, KeyAdded(session_id_string_)); - decryptor_.AddKey(key, key_size, key_id, key_id_size, + enum AddKeyExpectation { + KEY_ADDED, + KEY_ERROR + }; + + void AddRawKeyAndExpect(const std::vector<uint8>& key_id, + const std::vector<uint8>& key, + AddKeyExpectation result) { + // TODO(jrummell): Remove once raw keys no longer supported. + DCHECK(!key_id.empty()); + DCHECK(!key.empty()); + + if (result == KEY_ADDED) { + EXPECT_CALL(*this, KeyAdded(session_id_string_)); + } else if (result == KEY_ERROR) { + EXPECT_CALL(*this, KeyError(session_id_string_, + MediaKeys::kUnknownError, 0)); + } else { + NOTREACHED(); + } + + decryptor_.AddKey(&key[0], key.size(), &key_id[0], key_id.size(), session_id_string_); } - void AddKeyAndExpectToFail(const uint8* key_id, int key_id_size, - const uint8* key, int key_size) { - EXPECT_CALL(*this, KeyError(session_id_string_, - MediaKeys::kUnknownError, 0)); - decryptor_.AddKey(key, key_size, key_id, key_id_size, session_id_string_); + void AddKeyAndExpect(const std::string& key, AddKeyExpectation result) { + DCHECK(!key.empty()); + + if (result == KEY_ADDED) { + EXPECT_CALL(*this, KeyAdded(session_id_string_)); + } else if (result == KEY_ERROR) { + EXPECT_CALL(*this, + KeyError(session_id_string_, MediaKeys::kUnknownError, 0)); + } else { + NOTREACHED(); + } + + decryptor_.AddKey(reinterpret_cast<const uint8*>(key.c_str()), key.length(), + NULL, 0, + session_id_string_); } MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status, const scoped_refptr<DecoderBuffer>&)); - void DecryptAndExpectToSucceed(const scoped_refptr<DecoderBuffer>& encrypted, - const uint8* plain_text, int plain_text_size) { - scoped_refptr<DecoderBuffer> decrypted; - EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) - .WillOnce(SaveArg<1>(&decrypted)); - - decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_); - ASSERT_TRUE(decrypted.get()); - ASSERT_EQ(plain_text_size, decrypted->data_size()); - EXPECT_EQ(0, memcmp(plain_text, decrypted->data(), plain_text_size)); - } + enum DecryptExpectation { + SUCCESS, + DATA_MISMATCH, + DATA_AND_SIZE_MISMATCH, + DECRYPT_ERROR + }; - void DecryptAndExpectDataMismatch( - const scoped_refptr<DecoderBuffer>& encrypted, - const uint8* plain_text, int plain_text_size) { + void DecryptAndExpect(const scoped_refptr<DecoderBuffer>& encrypted, + const std::vector<uint8>& plain_text, + DecryptExpectation result) { scoped_refptr<DecoderBuffer> decrypted; - EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) - .WillOnce(SaveArg<1>(&decrypted)); - - decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_); - ASSERT_TRUE(decrypted.get()); - ASSERT_EQ(plain_text_size, decrypted->data_size()); - EXPECT_NE(0, memcmp(plain_text, decrypted->data(), plain_text_size)); - } - void DecryptAndExpectSizeDataMismatch( - const scoped_refptr<DecoderBuffer>& encrypted, - const uint8* plain_text, int plain_text_size) { - scoped_refptr<DecoderBuffer> decrypted; - EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) - .WillOnce(SaveArg<1>(&decrypted)); + if (result != DECRYPT_ERROR) { + EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, NotNull())) + .WillOnce(SaveArg<1>(&decrypted)); + } else { + EXPECT_CALL(*this, BufferDecrypted(Decryptor::kError, IsNull())) + .WillOnce(SaveArg<1>(&decrypted)); + } decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_); - ASSERT_TRUE(decrypted.get()); - EXPECT_NE(plain_text_size, decrypted->data_size()); - EXPECT_NE(0, memcmp(plain_text, decrypted->data(), plain_text_size)); - } - void DecryptAndExpectToFail(const scoped_refptr<DecoderBuffer>& encrypted) { - EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kError, IsNull())); - decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_); + std::vector<uint8> decrypted_text; + if (decrypted && decrypted->data_size()) { + decrypted_text.assign( + decrypted->data(), decrypted->data() + decrypted->data_size()); + } + + switch (result) { + case SUCCESS: + EXPECT_EQ(plain_text, decrypted_text); + break; + case DATA_MISMATCH: + EXPECT_EQ(plain_text.size(), decrypted_text.size()); + EXPECT_NE(plain_text, decrypted_text); + break; + case DATA_AND_SIZE_MISMATCH: + EXPECT_NE(plain_text.size(), decrypted_text.size()); + break; + case DECRYPT_ERROR: + EXPECT_TRUE(decrypted_text.empty()); + break; + } } MOCK_METHOD1(KeyAdded, void(const std::string&)); @@ -343,7 +315,15 @@ class AesDecryptorTest : public testing::Test { AesDecryptor decryptor_; std::string session_id_string_; AesDecryptor::DecryptCB decrypt_cb_; - std::vector<SubsampleEntry> subsample_entries_normal_; + + // Constants for testing. + const std::vector<uint8> original_data_; + const std::vector<uint8> encrypted_data_; + const std::vector<uint8> subsample_encrypted_data_; + const std::vector<uint8> key_id_; + const std::vector<uint8> iv_; + const std::vector<SubsampleEntry> normal_subsample_entries_; + const std::vector<SubsampleEntry> no_subsample_entries_; }; TEST_F(AesDecryptorTest, GenerateKeyRequestWithNullInitData) { @@ -351,314 +331,311 @@ TEST_F(AesDecryptorTest, GenerateKeyRequestWithNullInitData) { EXPECT_TRUE(decryptor_.GenerateKeyRequest(std::string(), NULL, 0)); } -TEST_F(AesDecryptorTest, NormalWebMDecryption) { - 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); - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - frame.plain_text, - frame.plain_text_size)); +TEST_F(AesDecryptorTest, NormalDecryption) { + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } -TEST_F(AesDecryptorTest, UnencryptedFrameWebMDecryption) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[3]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - frame.plain_text, - frame.plain_text_size)); +TEST_F(AesDecryptorTest, DecryptionWithOffset) { + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 23, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); +} + +TEST_F(AesDecryptorTest, UnencryptedFrame) { + // An empty iv string signals that the frame is unencrypted. + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + original_data_, key_id_, std::vector<uint8>(), 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } TEST_F(AesDecryptorTest, WrongKey) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - - // Change the first byte of the key. - std::vector<uint8> wrong_key(frame.key, frame.key + frame.key_size); - wrong_key[0]++; - - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - &wrong_key[0], frame.key_size); - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kWrongKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); } TEST_F(AesDecryptorTest, NoKey) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, frame.encrypted_data_size, - frame.key_id, frame.key_id_size); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, no_subsample_entries_); EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kNoKey, IsNull())); - decryptor_.Decrypt(Decryptor::kVideo, encrypted_data, decrypt_cb_); + decryptor_.Decrypt(Decryptor::kVideo, encrypted_buffer, decrypt_cb_); } TEST_F(AesDecryptorTest, KeyReplacement) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - - // Change the first byte of the key. - std::vector<uint8> wrong_key(frame.key, frame.key + frame.key_size); - wrong_key[0]++; - - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - &wrong_key[0], frame.key_size); - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - frame.plain_text, - frame.plain_text_size)); + GenerateKeyRequest(key_id_); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, no_subsample_entries_); + + AddKeyAndExpect(kWrongKeyAsJWK, KEY_ADDED); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpect( + encrypted_buffer, original_data_, DATA_MISMATCH)); + + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + ASSERT_NO_FATAL_FAILURE( + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); } 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)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kWrongSizedKeyAsJWK, KEY_ERROR); + + // Repeat for a raw key. Use "-1" to create a wrong sized key. + std::vector<uint8> wrong_sized_key(kKey, kKey + arraysize(kKey) - 1); + AddRawKeyAndExpect(key_id_, wrong_sized_key, KEY_ERROR); } 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); - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(frame.encrypted_data, - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - frame.plain_text, - frame.plain_text_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]; - scoped_refptr<DecoderBuffer> encrypted_data1 = - CreateWebMEncryptedBuffer(frame1.encrypted_data, - frame1.encrypted_data_size, - frame1.key_id, frame1.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data1, - frame1.plain_text, - frame1.plain_text_size)); - - scoped_refptr<DecoderBuffer> encrypted_data2 = - CreateWebMEncryptedBuffer(frame2.encrypted_data, - frame2.encrypted_data_size, - frame2.key_id, frame2.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data2, - frame2.plain_text, - frame2.plain_text_size)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 10, no_subsample_entries_); + ASSERT_NO_FATAL_FAILURE( + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); + + AddKeyAndExpect(kKey2AsJWK, KEY_ADDED); + + // The first key is still available after we added a second key. + ASSERT_NO_FATAL_FAILURE( + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS)); + + // The second key is also available. + encrypted_buffer = CreateEncryptedBuffer( + std::vector<uint8>(kEncryptedData2, + kEncryptedData2 + arraysize(kEncryptedData2)), + std::vector<uint8>(kKeyId2, kKeyId2 + arraysize(kKeyId2)), + std::vector<uint8>(kIv2, kIv2 + arraysize(kIv2)), + 30, + no_subsample_entries_); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpect( + encrypted_buffer, + std::vector<uint8>(kOriginalData2, + kOriginalData2 + arraysize(kOriginalData2) - 1), + SUCCESS)); } TEST_F(AesDecryptorTest, CorruptedIv) { - 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); - - // Change byte 13 to modify the IV. Bytes 13-20 of WebM encrypted data - // contains the IV. - std::vector<uint8> frame_with_bad_iv( - frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); - frame_with_bad_iv[1]++; - - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(&frame_with_bad_iv[0], - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + + std::vector<uint8> bad_iv = iv_; + bad_iv[1]++; + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, bad_iv, 0, no_subsample_entries_); + + DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); } TEST_F(AesDecryptorTest, CorruptedData) { - 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); - - // Change last byte to modify the data. Bytes 21+ of WebM encrypted data - // contains the encrypted frame. - std::vector<uint8> frame_with_bad_vp8_data( - frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); - frame_with_bad_vp8_data[frame.encrypted_data_size - 1]++; - - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(&frame_with_bad_vp8_data[0], - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); -} + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); -TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) { - 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); - - // Change signal byte from an encrypted frame to an unencrypted frame. Byte - // 12 of WebM encrypted data contains the signal byte. - std::vector<uint8> frame_with_wrong_signal_byte( - frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); - frame_with_wrong_signal_byte[0] = 0; - - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(&frame_with_wrong_signal_byte[0], - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE( - DecryptAndExpectSizeDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); + std::vector<uint8> bad_data = encrypted_data_; + bad_data[1]++; + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + bad_data, key_id_, iv_, 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); } -TEST_F(AesDecryptorTest, UnencryptedAsEncryptedFailure) { - const WebmEncryptedData& frame = kWebmEncryptedFrames[3]; - GenerateKeyRequest(frame.key_id, frame.key_id_size); - AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, - frame.key, frame.key_size); - - // Change signal byte from an unencrypted frame to an encrypted frame. Byte - // 0 of WebM encrypted data contains the signal byte. - std::vector<uint8> frame_with_wrong_signal_byte( - frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); - frame_with_wrong_signal_byte[0] = kWebMFlagEncryptedFrame; - - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(&frame_with_wrong_signal_byte[0], - frame.encrypted_data_size, - frame.key_id, frame.key_id_size); - ASSERT_NO_FATAL_FAILURE( - DecryptAndExpectSizeDataMismatch(encrypted_data, - frame.plain_text, - frame.plain_text_size)); +TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) { + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, std::vector<uint8>(), 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); } TEST_F(AesDecryptorTest, SubsampleDecryption) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kSubsampleEncryptedData, arraysize(kSubsampleEncryptedData), - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - 0, - subsample_entries_normal_); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( - encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + subsample_encrypted_data_, key_id_, iv_, 0, normal_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } // Ensures noninterference of data offset and subsample mechanisms. We never // expect to encounter this in the wild, but since the DecryptConfig doesn't // disallow such a configuration, it should be covered. TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kPaddedSubsampleEncryptedData, arraysize(kPaddedSubsampleEncryptedData), - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - arraysize(kPaddedSubsampleEncryptedData) - - arraysize(kSubsampleEncryptedData), - subsample_entries_normal_); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( - encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + subsample_encrypted_data_, key_id_, iv_, 23, normal_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } -// No subsample or offset. -TEST_F(AesDecryptorTest, NormalDecryption) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kEncryptedData, arraysize(kEncryptedData), - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - 0, - std::vector<SubsampleEntry>()); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( - encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); +TEST_F(AesDecryptorTest, SubsampleWrongSize) { + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + + std::vector<SubsampleEntry> subsample_entries_wrong_size( + kSubsampleEntriesWrongSize, + kSubsampleEntriesWrongSize + arraysize(kSubsampleEntriesWrongSize)); + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + subsample_encrypted_data_, key_id_, iv_, 0, subsample_entries_wrong_size); + DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH); } -TEST_F(AesDecryptorTest, IncorrectSubsampleSize) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - std::vector<SubsampleEntry> entries = subsample_entries_normal_; - entries[2].cypher_bytes += 1; - - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kSubsampleEncryptedData, arraysize(kSubsampleEncryptedData), - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - 0, - entries); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); +TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) { + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + + std::vector<SubsampleEntry> subsample_entries_invalid_total_size( + kSubsampleEntriesInvalidTotalSize, + kSubsampleEntriesInvalidTotalSize + + arraysize(kSubsampleEntriesInvalidTotalSize)); + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + subsample_encrypted_data_, key_id_, iv_, 0, + subsample_entries_invalid_total_size); + DecryptAndExpect(encrypted_buffer, original_data_, DECRYPT_ERROR); } // No cypher bytes in any of the subsamples. TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - std::vector<SubsampleEntry> subsample_entries_clear_only( + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + + std::vector<SubsampleEntry> clear_only_subsample_entries( kSubsampleEntriesClearOnly, kSubsampleEntriesClearOnly + arraysize(kSubsampleEntriesClearOnly)); - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kSubsampleOriginalData, kSubsampleOriginalDataSize, - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - 0, - subsample_entries_clear_only); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - kSubsampleOriginalData, kSubsampleOriginalDataSize)); + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + original_data_, key_id_, iv_, 0, clear_only_subsample_entries); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } // No clear bytes in any of the subsamples. TEST_F(AesDecryptorTest, SubsampleCypherBytesOnly) { - GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); - AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleKey, arraysize(kSubsampleKey)); - std::vector<SubsampleEntry> subsample_entries_cypher_only( + GenerateKeyRequest(key_id_); + AddKeyAndExpect(kKeyAsJWK, KEY_ADDED); + + std::vector<SubsampleEntry> cypher_only_subsample_entries( kSubsampleEntriesCypherOnly, kSubsampleEntriesCypherOnly + arraysize(kSubsampleEntriesCypherOnly)); - scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( - kEncryptedData, arraysize(kEncryptedData), - kSubsampleKeyId, arraysize(kSubsampleKeyId), - kSubsampleIv, arraysize(kSubsampleIv), - 0, - subsample_entries_cypher_only); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, - kSubsampleOriginalData, kSubsampleOriginalDataSize)); + + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, cypher_only_subsample_entries); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); +} + +TEST_F(AesDecryptorTest, JWKKey) { + // Try a simple JWK key (i.e. not in a set) + const std::string key1 = + "{" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM=\"," + " \"k\": \"FBUWFxgZGhscHR4fICEiIw==\"" + "}"; + AddKeyAndExpect(key1, KEY_ERROR); + + // Try a key list with multiple entries. + const std::string key2 = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM=\"," + " \"k\": \"FBUWFxgZGhscHR4fICEiIw==\"" + " }," + " {" + " \"kty\": \"oct\"," + " \"kid\": \"JCUmJygpKissLS4vMA==\"," + " \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA==\"" + " }" + " ]" + "}"; + AddKeyAndExpect(key2, KEY_ADDED); + + // Try a key with no spaces and some \n plus additional fields. + const std::string key3 = + "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\"," + "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM=\",\"k\":\"GawgguFyGrWKav7AX4VKUg=" + "=\",\"foo\":\"bar\"}]}\n\n"; + AddKeyAndExpect(key3, KEY_ADDED); + + // Try some non-ASCII characters. + AddKeyAndExpect("This is not ASCII due to \xff\xfe\xfd in it.", KEY_ERROR); + + // Try a badly formatted key. Assume that the JSON parser is fully tested, + // so we won't try a lot of combinations. However, need a test to ensure + // that the code doesn't crash if invalid JSON received. + AddKeyAndExpect("This is not a JSON key.", KEY_ERROR); + + // Try passing some valid JSON that is not a dictionary at the top level. + AddKeyAndExpect("40", KEY_ERROR); + + // Try an empty dictionary. + AddKeyAndExpect("{ }", KEY_ERROR); + + // Try an empty 'keys' dictionary. + AddKeyAndExpect("{ \"keys\": [] }", KEY_ERROR); + + // Try with 'keys' not a dictionary. + AddKeyAndExpect("{ \"keys\":\"1\" }", KEY_ERROR); + + // Try with 'keys' a list of integers. + AddKeyAndExpect("{ \"keys\": [ 1, 2, 3 ] }", KEY_ERROR); + + // Try a key missing padding(=) at end of base64 string. + const std::string key4 = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAw==\"," + " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" + " }" + " ]" + "}"; + AddKeyAndExpect(key4, KEY_ERROR); + + // Try a key ID missing padding(=) at end of base64 string. + const std::string key5 = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"AAECAw\"," + " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\"" + " }" + " ]" + "}"; + AddKeyAndExpect(key5, KEY_ERROR); + + // Try a key with invalid base64 encoding. + const std::string key6 = + "{" + " \"keys\": [" + " {" + " \"kty\": \"oct\"," + " \"kid\": \"!@#$%^&*()==\"," + " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\"" + " }" + " ]" + "}"; + AddKeyAndExpect(key6, KEY_ERROR); +} + +TEST_F(AesDecryptorTest, RawKey) { + // Verify that v0.1b keys (raw key) is still supported. Raw keys are + // 16 bytes long. Use the undecoded value of |kKey|. + GenerateKeyRequest(key_id_); + AddRawKeyAndExpect( + key_id_, std::vector<uint8>(kKey, kKey + arraysize(kKey)), KEY_ADDED); + scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer( + encrypted_data_, key_id_, iv_, 0, no_subsample_entries_); + DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS); } } // namespace media diff --git a/chromium/media/cdm/ppapi/cdm_wrapper.cc b/chromium/media/cdm/ppapi/cdm_wrapper.cc index 6348f1954d0..66ae43d4d65 100644 --- a/chromium/media/cdm/ppapi/cdm_wrapper.cc +++ b/chromium/media/cdm/ppapi/cdm_wrapper.cc @@ -498,8 +498,9 @@ class CdmWrapper : public pp::Instance, // PPP_ContentDecryptor_Private implementation. // Note: Results of calls to these methods must be reported through the // PPB_ContentDecryptor_Private interface. - virtual void GenerateKeyRequest(const std::string& key_system, - const std::string& type, + virtual void Initialize(const std::string& key_system, + bool can_challenge_platform) OVERRIDE; + virtual void GenerateKeyRequest(const std::string& type, pp::VarArrayBuffer init_data) OVERRIDE; virtual void AddKey(const std::string& session_id, pp::VarArrayBuffer key, @@ -636,11 +637,25 @@ bool CdmWrapper::CreateCdmInstance(const std::string& key_system) { return (cdm_ != NULL); } -void CdmWrapper::GenerateKeyRequest(const std::string& key_system, - const std::string& type, - pp::VarArrayBuffer init_data) { +void CdmWrapper::Initialize(const std::string& key_system, + bool can_challenge_platform) { PP_DCHECK(!key_system.empty()); - PP_DCHECK(key_system_.empty() || key_system_ == key_system); + PP_DCHECK(key_system_.empty() || (key_system_ == key_system && cdm_)); + + if (!cdm_) { + if (!CreateCdmInstance(key_system)) { + // TODO(jrummell): Is UnknownKeyError the correct response? + SendUnknownKeyError(key_system, std::string()); + return; + } + } + PP_DCHECK(cdm_); + key_system_ = key_system; +} + +void CdmWrapper::GenerateKeyRequest(const std::string& type, + pp::VarArrayBuffer init_data) { + PP_DCHECK(cdm_); // Initialize() should have succeeded. #if defined(CHECK_DOCUMENT_URL) PP_URLComponents_Dev url_components = {}; @@ -652,36 +667,19 @@ void CdmWrapper::GenerateKeyRequest(const std::string& key_system, PP_DCHECK(0 < url_components.host.len); #endif // defined(CHECK_DOCUMENT_URL) - if (!cdm_) { - if (!CreateCdmInstance(key_system)) { - SendUnknownKeyError(key_system, std::string()); - return; - } - } - PP_DCHECK(cdm_); - - // Must be set here in case the CDM synchronously calls a cdm::Host method. - // Clear below on error. - // TODO(ddorwin): Set/clear key_system_ & cdm_ at same time; clear both on - // error below. - key_system_ = key_system; cdm::Status status = cdm_->GenerateKeyRequest( type.data(), type.size(), static_cast<const uint8_t*>(init_data.Map()), init_data.ByteLength()); PP_DCHECK(status == cdm::kSuccess || status == cdm::kSessionError); - if (status != cdm::kSuccess) { - key_system_.clear(); // See comment above. - return; - } - - key_system_ = key_system; + if (status != cdm::kSuccess) + SendUnknownKeyError(key_system_, std::string()); } void CdmWrapper::AddKey(const std::string& session_id, pp::VarArrayBuffer key, pp::VarArrayBuffer init_data) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. if (!cdm_) { SendUnknownKeyError(key_system_, session_id); return; @@ -711,7 +709,7 @@ void CdmWrapper::AddKey(const std::string& session_id, } void CdmWrapper::CancelKeyRequest(const std::string& session_id) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. if (!cdm_) { SendUnknownKeyError(key_system_, session_id); return; @@ -729,7 +727,7 @@ void CdmWrapper::CancelKeyRequest(const std::string& session_id) { void CdmWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. PP_DCHECK(!encrypted_buffer.is_null()); // Release a buffer that the caller indicated it is finished with. @@ -759,7 +757,7 @@ void CdmWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer, void CdmWrapper::InitializeAudioDecoder( const PP_AudioDecoderConfig& decoder_config, pp::Buffer_Dev extra_data_buffer) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. cdm::Status status = cdm::kSessionError; if (cdm_) { @@ -786,7 +784,7 @@ void CdmWrapper::InitializeAudioDecoder( void CdmWrapper::InitializeVideoDecoder( const PP_VideoDecoderConfig& decoder_config, pp::Buffer_Dev extra_data_buffer) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. cdm::Status status = cdm::kSessionError; if (cdm_) { @@ -815,7 +813,7 @@ void CdmWrapper::InitializeVideoDecoder( void CdmWrapper::DeinitializeDecoder(PP_DecryptorStreamType decoder_type, uint32_t request_id) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. if (cdm_) { cdm_->DeinitializeDecoder( PpDecryptorStreamTypeToCdmStreamType(decoder_type)); @@ -829,7 +827,7 @@ void CdmWrapper::DeinitializeDecoder(PP_DecryptorStreamType decoder_type, void CdmWrapper::ResetDecoder(PP_DecryptorStreamType decoder_type, uint32_t request_id) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. if (cdm_) cdm_->ResetDecoder(PpDecryptorStreamTypeToCdmStreamType(decoder_type)); @@ -842,7 +840,7 @@ void CdmWrapper::DecryptAndDecode( PP_DecryptorStreamType decoder_type, pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) { - PP_DCHECK(cdm_); // GenerateKeyRequest() should have succeeded. + PP_DCHECK(cdm_); // Initialize() should have succeeded. // Release a buffer that the caller indicated it is finished with. allocator_.Release(encrypted_block_info.tracking_info.buffer_id); |