Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/crypto/aes_decryptor.h" | 5 #include "media/crypto/aes_decryptor.h" |
| 6 | 6 |
| 7 #include <vector> | |
| 8 | |
| 7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 8 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| 9 #include "base/string_number_conversions.h" | 11 #include "base/string_number_conversions.h" |
| 10 #include "crypto/encryptor.h" | 12 #include "crypto/encryptor.h" |
| 11 #include "crypto/hmac.h" | 13 #include "crypto/hmac.h" |
| 12 #include "crypto/symmetric_key.h" | 14 #include "crypto/symmetric_key.h" |
| 13 #include "media/base/decoder_buffer.h" | 15 #include "media/base/decoder_buffer.h" |
| 14 #include "media/base/decrypt_config.h" | 16 #include "media/base/decrypt_config.h" |
| 15 #include "media/base/decryptor_client.h" | 17 #include "media/base/decryptor_client.h" |
| 16 | 18 |
| 19 using crypto::SymmetricKey; | |
| 20 | |
|
Ryan Sleevi
2012/07/30 06:52:50
FWIW, please see https://groups.google.com/a/chrom
xhwang
2012/07/30 16:22:30
Thanks for the info. Personally I think either way
| |
| 17 namespace media { | 21 namespace media { |
| 18 | 22 |
| 19 // The size is from the WebM encrypted specification. Current encrypted WebM | 23 // The size is from the WebM encrypted specification. Current encrypted WebM |
| 20 // request for comments specification is here | 24 // request for comments specification is here |
| 21 // http://wiki.webmproject.org/encryption/webm-encryption-rfc | 25 // http://wiki.webmproject.org/encryption/webm-encryption-rfc |
| 22 static const int kWebmSha1DigestSize = 20; | 26 static const int kWebmSha1DigestSize = 20; |
| 23 static const char kWebmHmacSeed[] = "hmac-key"; | 27 static const char kWebmHmacSeed[] = "hmac-key"; |
| 24 static const char kWebmEncryptionSeed[] = "encryption-key"; | 28 static const char kWebmEncryptionSeed[] = "encryption-key"; |
| 25 | 29 |
| 26 uint32 AesDecryptor::next_session_id_ = 1; | 30 uint32 AesDecryptor::next_session_id_ = 1; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 | 81 |
| 78 base::StringPiece data_to_check( | 82 base::StringPiece data_to_check( |
| 79 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); | 83 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); |
| 80 | 84 |
| 81 return hmac.VerifyTruncated(data_to_check, | 85 return hmac.VerifyTruncated(data_to_check, |
| 82 input.GetDecryptConfig()->checksum()); | 86 input.GetDecryptConfig()->checksum()); |
| 83 } | 87 } |
| 84 | 88 |
| 85 enum ClearBytesBufferSel { | 89 enum ClearBytesBufferSel { |
| 86 kSrcContainsClearBytes, | 90 kSrcContainsClearBytes, |
| 87 kDstContainsClearBytes, | 91 kDstContainsClearBytes |
| 88 }; | 92 }; |
| 89 | 93 |
| 90 static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, | 94 static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, |
| 91 const ClearBytesBufferSel sel, | 95 const ClearBytesBufferSel sel, |
| 92 const uint8* src, | 96 const uint8* src, |
| 93 uint8* dst) { | 97 uint8* dst) { |
| 94 for (size_t i = 0; i < subsamples.size(); i++) { | 98 for (size_t i = 0; i < subsamples.size(); i++) { |
| 95 const SubsampleEntry& subsample = subsamples[i]; | 99 const SubsampleEntry& subsample = subsamples[i]; |
| 96 if (sel == kSrcContainsClearBytes) { | 100 if (sel == kSrcContainsClearBytes) { |
| 97 src += subsample.clear_bytes; | 101 src += subsample.clear_bytes; |
| 98 } else { | 102 } else { |
| 99 dst += subsample.clear_bytes; | 103 dst += subsample.clear_bytes; |
| 100 } | 104 } |
| 101 memcpy(dst, src, subsample.cypher_bytes); | 105 memcpy(dst, src, subsample.cypher_bytes); |
| 102 src += subsample.cypher_bytes; | 106 src += subsample.cypher_bytes; |
| 103 dst += subsample.cypher_bytes; | 107 dst += subsample.cypher_bytes; |
| 104 } | 108 } |
| 105 } | 109 } |
| 106 | 110 |
| 107 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted | 111 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted |
| 108 // data if decryption succeeded or NULL if decryption failed. | 112 // data if decryption succeeded or NULL if decryption failed. |
| 109 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, | 113 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, |
| 110 crypto::SymmetricKey* key) { | 114 SymmetricKey* key) { |
| 111 CHECK(input.GetDataSize()); | 115 CHECK(input.GetDataSize()); |
| 112 CHECK(input.GetDecryptConfig()); | 116 CHECK(input.GetDecryptConfig()); |
| 113 CHECK(key); | 117 CHECK(key); |
| 114 | 118 |
| 115 crypto::Encryptor encryptor; | 119 crypto::Encryptor encryptor; |
| 116 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { | 120 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { |
| 117 DVLOG(1) << "Could not initialize decryptor."; | 121 DVLOG(1) << "Could not initialize decryptor."; |
| 118 return NULL; | 122 return NULL; |
| 119 } | 123 } |
| 120 | 124 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); | 247 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); |
| 244 return; | 248 return; |
| 245 } | 249 } |
| 246 | 250 |
| 247 if (!decryption_key->Init()) { | 251 if (!decryption_key->Init()) { |
| 248 DVLOG(1) << "Could not initialize decryption key."; | 252 DVLOG(1) << "Could not initialize decryption key."; |
| 249 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); | 253 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); |
| 250 return; | 254 return; |
| 251 } | 255 } |
| 252 | 256 |
| 253 { | 257 SetKey(key_id_string, decryption_key.Pass()); |
| 254 base::AutoLock auto_lock(key_map_lock_); | |
| 255 KeyMap::iterator found = key_map_.find(key_id_string); | |
| 256 if (found != key_map_.end()) { | |
| 257 delete found->second; | |
| 258 key_map_.erase(found); | |
| 259 } | |
| 260 key_map_[key_id_string] = decryption_key.release(); | |
| 261 } | |
| 262 | |
| 263 client_->KeyAdded(key_system, session_id); | 258 client_->KeyAdded(key_system, session_id); |
| 264 } | 259 } |
| 265 | 260 |
| 266 void AesDecryptor::CancelKeyRequest(const std::string& key_system, | 261 void AesDecryptor::CancelKeyRequest(const std::string& key_system, |
| 267 const std::string& session_id) { | 262 const std::string& session_id) { |
| 268 } | 263 } |
| 269 | 264 |
| 270 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, | 265 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, |
| 271 const DecryptCB& decrypt_cb) { | 266 const DecryptCB& decrypt_cb) { |
| 272 CHECK(encrypted->GetDecryptConfig()); | 267 CHECK(encrypted->GetDecryptConfig()); |
| 273 const std::string& key_id = encrypted->GetDecryptConfig()->key_id(); | 268 const std::string& key_id = encrypted->GetDecryptConfig()->key_id(); |
| 274 | 269 |
| 275 DecryptionKey* key = NULL; | 270 DecryptionKey* key = GetKey(key_id); |
| 276 { | |
| 277 base::AutoLock auto_lock(key_map_lock_); | |
| 278 KeyMap::const_iterator found = key_map_.find(key_id); | |
| 279 if (found != key_map_.end()) | |
| 280 key = found->second; | |
| 281 } | |
| 282 | |
| 283 if (!key) { | 271 if (!key) { |
| 284 // TODO(fgalligan): Fire a need_key event here and add a test. | 272 // TODO(xhwang): Fire a need_key event here and add a test. |
| 285 DVLOG(1) << "Could not find a matching key for given key ID."; | 273 DVLOG(1) << "Could not find a matching key for the given key ID."; |
| 286 decrypt_cb.Run(kError, NULL); | 274 decrypt_cb.Run(kError, NULL); |
| 287 return; | 275 return; |
| 288 } | 276 } |
| 289 | 277 |
| 290 int checksum_size = encrypted->GetDecryptConfig()->checksum().size(); | 278 int checksum_size = encrypted->GetDecryptConfig()->checksum().size(); |
| 291 // According to the WebM encrypted specification, it is an open question | 279 // According to the WebM encrypted specification, it is an open question |
| 292 // what should happen when a frame fails the integrity check. | 280 // what should happen when a frame fails the integrity check. |
| 293 // http://wiki.webmproject.org/encryption/webm-encryption-rfc | 281 // http://wiki.webmproject.org/encryption/webm-encryption-rfc |
| 294 if (checksum_size > 0 && | 282 if (checksum_size > 0 && |
| 295 !key->hmac_key().empty() && | 283 !key->hmac_key().empty() && |
| 296 !CheckData(*encrypted, key->hmac_key())) { | 284 !CheckData(*encrypted, key->hmac_key())) { |
| 297 DVLOG(1) << "Integrity check failed."; | 285 DVLOG(1) << "Integrity check failed."; |
| 298 decrypt_cb.Run(kError, NULL); | 286 decrypt_cb.Run(kError, NULL); |
| 299 return; | 287 return; |
| 300 } | 288 } |
| 301 | 289 |
| 302 // TODO(strobe): Currently, presence of checksum is used to indicate the use | 290 // TODO(strobe): Currently, presence of checksum is used to indicate the use |
| 303 // of normal or WebM decryption keys. Consider a more explicit signaling | 291 // of normal or WebM decryption keys. Consider a more explicit signaling |
| 304 // mechanism and the removal of the webm_decryption_key member. | 292 // mechanism and the removal of the webm_decryption_key member. |
| 305 crypto::SymmetricKey* decryption_key = (checksum_size > 0) ? | 293 SymmetricKey* decryption_key = (checksum_size > 0) ? |
| 306 key->webm_decryption_key() : key->decryption_key(); | 294 key->webm_decryption_key() : key->decryption_key(); |
| 307 scoped_refptr<DecoderBuffer> decrypted = | 295 scoped_refptr<DecoderBuffer> decrypted = |
| 308 DecryptData(*encrypted, decryption_key); | 296 DecryptData(*encrypted, decryption_key); |
| 309 if (!decrypted) { | 297 if (!decrypted) { |
| 310 DVLOG(1) << "Decryption failed."; | 298 DVLOG(1) << "Decryption failed."; |
| 311 decrypt_cb.Run(kError, NULL); | 299 decrypt_cb.Run(kError, NULL); |
| 312 return; | 300 return; |
| 313 } | 301 } |
| 314 | 302 |
| 315 decrypted->SetTimestamp(encrypted->GetTimestamp()); | 303 decrypted->SetTimestamp(encrypted->GetTimestamp()); |
| 316 decrypted->SetDuration(encrypted->GetDuration()); | 304 decrypted->SetDuration(encrypted->GetDuration()); |
| 317 decrypt_cb.Run(kSuccess, decrypted); | 305 decrypt_cb.Run(kSuccess, decrypted); |
| 318 } | 306 } |
| 319 | 307 |
| 320 AesDecryptor::DecryptionKey::DecryptionKey( | 308 void AesDecryptor::SetKey(const std::string& key_id, |
| 321 const std::string& secret) | 309 scoped_ptr<DecryptionKey> decryption_key) { |
| 310 base::AutoLock auto_lock(key_map_lock_); | |
| 311 KeyMap::iterator found = key_map_.find(key_id); | |
| 312 if (found != key_map_.end()) { | |
| 313 delete found->second; | |
| 314 key_map_.erase(found); | |
| 315 } | |
| 316 key_map_[key_id] = decryption_key.release(); | |
| 317 } | |
| 318 | |
| 319 AesDecryptor::DecryptionKey* AesDecryptor::GetKey( | |
| 320 const std::string& key_id) const { | |
| 321 base::AutoLock auto_lock(key_map_lock_); | |
| 322 KeyMap::const_iterator found = key_map_.find(key_id); | |
| 323 if (found == key_map_.end()) | |
| 324 return NULL; | |
| 325 | |
| 326 return found->second; | |
| 327 } | |
| 328 | |
| 329 AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret) | |
| 322 : secret_(secret) { | 330 : secret_(secret) { |
| 323 } | 331 } |
| 324 | 332 |
| 325 AesDecryptor::DecryptionKey::~DecryptionKey() {} | 333 AesDecryptor::DecryptionKey::~DecryptionKey() {} |
| 326 | 334 |
| 327 bool AesDecryptor::DecryptionKey::Init() { | 335 bool AesDecryptor::DecryptionKey::Init() { |
| 328 CHECK(!secret_.empty()); | 336 CHECK(!secret_.empty()); |
| 329 decryption_key_.reset( | 337 decryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES, secret_)); |
| 330 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_)); | 338 if (!decryption_key_.get()) |
| 331 if (!decryption_key_.get()) { | |
| 332 return false; | 339 return false; |
| 333 } | |
| 334 | 340 |
| 335 std::string raw_key = DeriveKey(secret_, | 341 std::string raw_key = DeriveKey(secret_, |
| 336 kWebmEncryptionSeed, | 342 kWebmEncryptionSeed, |
| 337 secret_.length()); | 343 secret_.length()); |
| 338 if (raw_key.empty()) { | 344 if (raw_key.empty()) |
| 339 return false; | 345 return false; |
| 340 } | 346 |
| 341 webm_decryption_key_.reset( | 347 webm_decryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES, raw_key)); |
| 342 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); | 348 if (!webm_decryption_key_.get()) |
| 343 if (!webm_decryption_key_.get()) { | |
| 344 return false; | 349 return false; |
| 345 } | |
| 346 | 350 |
| 347 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); | 351 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); |
| 348 if (hmac_key_.empty()) { | 352 if (hmac_key_.empty()) |
| 349 return false; | 353 return false; |
| 350 } | |
| 351 | 354 |
| 352 return true; | 355 return true; |
| 353 } | 356 } |
| 354 | 357 |
| 355 } // namespace media | 358 } // namespace media |
| OLD | NEW |