OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/crypto/hmac_aes_decryptor.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/stl_util.h" | |
9 #include "base/string_piece.h" | |
10 #include "crypto/encryptor.h" | |
11 #include "crypto/hmac.h" | |
12 #include "crypto/symmetric_key.h" | |
13 #include "media/base/decoder_buffer.h" | |
14 #include "media/base/decrypt_config.h" | |
15 | |
16 namespace media { | |
17 | |
18 // Derives a key from an HAMC using SHA1. |secret| is the base secret to derive | |
19 // the key from. |seed| is the knwon input to the HMAC. |key_size| is how many | |
20 // bytes are returned in the key. Returns a string containing the key on | |
21 // success. Returns an empty string on failure. | |
22 static std::string DeriveKey(const std::string& secret, | |
23 const std::string& seed, | |
24 int key_size) { | |
25 CHECK_EQ(secret.length(), static_cast<size_t>(DecryptConfig::kKeySize)); | |
26 CHECK(!seed.empty()); | |
27 CHECK_GT(key_size, 0); | |
28 | |
29 std::string key; | |
30 crypto::HMAC hmac(crypto::HMAC::SHA1); | |
31 if (!hmac.Init(reinterpret_cast<const uint8*>(secret.data()), | |
32 secret.size())) { | |
33 DVLOG(1) << "Could not initialize HMAC with secret data."; | |
34 return key; | |
35 } | |
36 | |
37 uint8 calculated_hmac[HmacAesDecryptor::kSha1DigestSize]; | |
38 if (!hmac.Sign(seed, calculated_hmac, HmacAesDecryptor::kSha1DigestSize)) { | |
39 DVLOG(1) << "Could not calculate HMAC."; | |
40 return key; | |
41 } | |
42 key.assign(reinterpret_cast<const char*>(calculated_hmac), key_size); | |
43 return key; | |
44 } | |
45 | |
46 // Integrity check of |input|'s data. Checks that the first | |
47 // |kWebMIntegrityCheckSize| in bytes of |ipunt| matches the rest of the data | |
48 // in |input|. The check is defined in the WebM specification. Current WebM | |
49 // encrypted request for comments specification is here | |
50 // http://wiki.webmproject.org/encryption/webm-encryption-rfc. | |
51 // |hmac_key| is the key of the HMAC algorithm. Returns true if the integrity | |
52 // check is good. | |
53 static bool CheckData(const DecoderBuffer& input, | |
54 const std::string& hmac_key) { | |
55 CHECK(input.GetDataSize()); | |
56 CHECK(input.GetDecryptConfig()); | |
57 CHECK(!hmac_key.empty()); | |
58 | |
59 crypto::HMAC hmac(crypto::HMAC::SHA1); | |
60 if (!hmac.Init(reinterpret_cast<const uint8*>(hmac_key.data()), | |
61 hmac_key.size())) { | |
62 DVLOG(1) << "Could not initialize HMAC."; | |
63 return false; | |
64 } | |
65 | |
66 // The HMAC covers the IV and the frame data. | |
67 const char* iv_frame = reinterpret_cast<const char*>(input.GetData() + | |
ddorwin
2012/06/14 21:41:24
Is "iv_frame" really "iv_and_frame"? As mentioned
fgalligan1
2012/07/03 22:00:15
I only send the IV and frame data in the input buf
| |
68 DecryptConfig::kWebMIntegrityCheckSize); | |
ddorwin
2012/06/14 21:41:24
Would like to avoid any WebM-specific values in th
fgalligan1
2012/07/03 22:00:15
Done.
| |
69 int iv_frame_size = | |
70 input.GetDataSize() - DecryptConfig::kWebMIntegrityCheckSize; | |
71 std::string data_to_check(iv_frame, iv_frame_size); | |
ddorwin
2012/06/14 21:41:24
Can we avoid this memory copy?
(If we separate IV
fgalligan1
2012/07/03 22:00:15
Done.
| |
72 | |
73 uint8 calculated_hmac[HmacAesDecryptor::kSha1DigestSize]; | |
74 if (!hmac.Sign(data_to_check, | |
75 calculated_hmac, | |
76 HmacAesDecryptor::kSha1DigestSize)) { | |
77 DVLOG(1) << "Could not calculate HMAC."; | |
78 return false; | |
79 } | |
80 | |
81 if (memcmp(input.GetDecryptConfig()->hmac(), | |
82 calculated_hmac, | |
83 input.GetDecryptConfig()->hmac_size()) != 0) { | |
84 DVLOG(1) << "Integrity check failure."; | |
85 return false; | |
86 } | |
87 return true; | |
88 } | |
89 | |
90 const char HmacAesDecryptor::kHmacSeed[] = "hmac-key"; | |
91 const char HmacAesDecryptor::kEncryptionSeed[] = "encryption-key"; | |
92 | |
93 HmacAesDecryptor::HmacAesDecryptor() {} | |
94 | |
95 HmacAesDecryptor::~HmacAesDecryptor() { | |
96 STLDeleteValues(&keys_map_); | |
97 } | |
98 | |
99 void HmacAesDecryptor::AddKey(const uint8* key_id, int key_id_size, | |
100 const uint8* key, int key_size) { | |
101 CHECK(key_id && key); | |
102 CHECK_GT(key_id_size, 0); | |
103 CHECK_GT(key_size, 0); | |
104 | |
105 std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); | |
106 std::string key_string(reinterpret_cast<const char*>(key) , key_size); | |
107 | |
108 HmacEncryptionKeys* keys = new HmacEncryptionKeys(key_string); | |
109 if (!keys) { | |
110 DVLOG(1) << "Could not create keys."; | |
111 return; | |
112 } | |
113 if (!keys->Init()) { | |
114 delete keys; | |
115 DVLOG(1) << "Could not create keys."; | |
116 return; | |
117 } | |
118 | |
119 base::AutoLock auto_lock(lock_); | |
120 KeysMap::iterator found = keys_map_.find(key_id_string); | |
121 if (found != keys_map_.end()) { | |
122 delete found->second; | |
123 keys_map_.erase(found); | |
124 } | |
125 keys_map_[key_id_string] = keys; | |
126 } | |
127 | |
128 scoped_refptr<DecoderBuffer> HmacAesDecryptor::Decrypt( | |
129 const scoped_refptr<DecoderBuffer>& encrypted) { | |
130 CHECK(encrypted->GetDecryptConfig()); | |
131 const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); | |
132 const int key_id_size = encrypted->GetDecryptConfig()->key_id_size(); | |
133 | |
134 // TODO(xhwang): Avoid always constructing a string with StringPiece? | |
135 std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); | |
136 | |
137 HmacEncryptionKeys* keys = NULL; | |
138 { | |
139 base::AutoLock auto_lock(lock_); | |
140 KeysMap::const_iterator found = keys_map_.find(key_id_string); | |
141 if (found == keys_map_.end()) { | |
142 DVLOG(1) << "Could not find a matching key for given key ID."; | |
143 return NULL; | |
144 } | |
145 keys = found->second; | |
146 } | |
147 | |
148 if (!CheckData(*encrypted, keys->hmac_key())) { | |
ddorwin
2012/06/14 21:41:24
Support containers without HMAC support:
if (HasHm
fgalligan1
2012/07/03 22:00:15
Done.
| |
149 DVLOG(1) << "Integrity check failed."; | |
150 return NULL; | |
151 } | |
152 | |
153 scoped_refptr<DecoderBuffer> decrypted = | |
154 Decryptor::DecryptData(*encrypted, | |
155 keys->encryption_key(), | |
156 DecryptConfig::kWebMIntegrityCheckSize + | |
157 DecryptConfig::kIvSize); | |
158 if (decrypted) { | |
159 decrypted->SetTimestamp(encrypted->GetTimestamp()); | |
160 decrypted->SetDuration(encrypted->GetDuration()); | |
161 } | |
162 | |
163 return decrypted; | |
164 } | |
165 | |
166 HmacAesDecryptor::HmacEncryptionKeys::HmacEncryptionKeys( | |
167 const std::string& secret) | |
168 : secret_(secret) { | |
169 } | |
170 | |
171 HmacAesDecryptor::HmacEncryptionKeys::~HmacEncryptionKeys() {} | |
172 | |
173 bool HmacAesDecryptor::HmacEncryptionKeys::Init() { | |
174 CHECK(!secret_.empty()); | |
175 | |
176 std::string raw_key = DeriveKey(secret_, | |
177 kEncryptionSeed, | |
178 DecryptConfig::kKeySize); | |
179 if (raw_key.empty()) { | |
180 DVLOG(1) << "Could not create encryption key."; | |
181 return false; | |
182 } | |
183 encryption_key_.reset(crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, | |
184 raw_key)); | |
185 if (!encryption_key_.get()) { | |
186 DVLOG(1) << "Could not create encryption key."; | |
187 return false; | |
188 } | |
189 | |
190 hmac_key_ = DeriveKey(secret_, kHmacSeed, kSha1DigestSize); | |
191 if (hmac_key_.empty()) { | |
192 DVLOG(1) << "Could not create HMAC key."; | |
193 return false; | |
194 } | |
195 return true; | |
196 } | |
197 | |
198 } // namespace media | |
OLD | NEW |