OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "content/renderer/webcrypto/webcrypto_impl.h" | 5 #include "content/renderer/webcrypto/webcrypto_impl.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 #include <openssl/aes.h> | 8 #include <openssl/aes.h> |
9 #include <openssl/evp.h> | 9 #include <openssl/evp.h> |
10 #include <openssl/hmac.h> | 10 #include <openssl/hmac.h> |
11 #include <openssl/rand.h> | 11 #include <openssl/rand.h> |
12 #include <openssl/sha.h> | 12 #include <openssl/sha.h> |
13 | 13 |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "content/renderer/webcrypto/webcrypto_util.h" | 15 #include "content/renderer/webcrypto/webcrypto_util.h" |
16 #include "crypto/openssl_util.h" | 16 #include "crypto/openssl_util.h" |
17 #include "crypto/secure_util.h" | 17 #include "crypto/secure_util.h" |
18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | 18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" |
19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | 19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | 20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
21 | 21 |
22 namespace content { | 22 namespace content { |
23 | 23 |
| 24 using webcrypto::Status; |
| 25 |
24 namespace { | 26 namespace { |
25 | 27 |
26 class SymKeyHandle : public blink::WebCryptoKeyHandle { | 28 class SymKeyHandle : public blink::WebCryptoKeyHandle { |
27 public: | 29 public: |
28 SymKeyHandle(const unsigned char* key_data, unsigned key_data_size) | 30 SymKeyHandle(const unsigned char* key_data, unsigned key_data_size) |
29 : key_(key_data, key_data + key_data_size) {} | 31 : key_(key_data, key_data + key_data_size) {} |
30 | 32 |
31 const std::vector<unsigned char>& key() const { return key_; } | 33 const std::vector<unsigned char>& key() const { return key_; } |
32 | 34 |
33 private: | 35 private: |
(...skipping 15 matching lines...) Expand all Loading... |
49 return NULL; | 51 return NULL; |
50 } | 52 } |
51 } | 53 } |
52 | 54 |
53 // OpenSSL constants for EVP_CipherInit_ex(), do not change | 55 // OpenSSL constants for EVP_CipherInit_ex(), do not change |
54 enum CipherOperation { | 56 enum CipherOperation { |
55 kDoDecrypt = 0, | 57 kDoDecrypt = 0, |
56 kDoEncrypt = 1 | 58 kDoEncrypt = 1 |
57 }; | 59 }; |
58 | 60 |
59 bool AesCbcEncryptDecrypt(CipherOperation cipher_operation, | 61 Status AesCbcEncryptDecrypt(CipherOperation cipher_operation, |
60 const blink::WebCryptoAlgorithm& algorithm, | 62 const blink::WebCryptoAlgorithm& algorithm, |
61 const blink::WebCryptoKey& key, | 63 const blink::WebCryptoKey& key, |
62 const unsigned char* data, | 64 const unsigned char* data, |
63 unsigned data_size, | 65 unsigned data_size, |
64 blink::WebArrayBuffer* buffer) { | 66 blink::WebArrayBuffer* buffer) { |
65 | 67 DCHECK_EQ(blink::WebCryptoAlgorithmIdAesCbc, algorithm.id()); |
66 // TODO(padolph): Handle other encrypt operations and then remove this gate | |
67 if (algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) | |
68 return false; | |
69 | |
70 DCHECK_EQ(algorithm.id(), key.algorithm().id()); | 68 DCHECK_EQ(algorithm.id(), key.algorithm().id()); |
71 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | 69 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
72 | 70 |
73 if (data_size >= INT_MAX - AES_BLOCK_SIZE) { | 71 if (data_size >= INT_MAX - AES_BLOCK_SIZE) { |
74 // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right | 72 // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right |
75 // now it doesn't make much difference since the one-shot API would end up | 73 // now it doesn't make much difference since the one-shot API would end up |
76 // blowing out the memory and crashing anyway. However a newer version of | 74 // blowing out the memory and crashing anyway. |
77 // the spec allows for a sequence<CryptoData> so this will be relevant. | 75 return Status::ErrorDataTooLarge(); |
78 return false; | |
79 } | 76 } |
80 | 77 |
81 // Note: PKCS padding is enabled by default | 78 // Note: PKCS padding is enabled by default |
82 crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context( | 79 crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context( |
83 EVP_CIPHER_CTX_new()); | 80 EVP_CIPHER_CTX_new()); |
84 | 81 |
85 if (!context.get()) | 82 if (!context.get()) |
86 return false; | 83 return Status::Error(); |
87 | 84 |
88 SymKeyHandle* const sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); | 85 SymKeyHandle* const sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
89 | 86 |
90 const EVP_CIPHER* const cipher = | 87 const EVP_CIPHER* const cipher = |
91 GetAESCipherByKeyLength(sym_key->key().size()); | 88 GetAESCipherByKeyLength(sym_key->key().size()); |
92 DCHECK(cipher); | 89 DCHECK(cipher); |
93 | 90 |
94 const blink::WebCryptoAesCbcParams* const params = algorithm.aesCbcParams(); | 91 const blink::WebCryptoAesCbcParams* const params = algorithm.aesCbcParams(); |
95 if (params->iv().size() != AES_BLOCK_SIZE) | 92 if (params->iv().size() != AES_BLOCK_SIZE) |
96 return false; | 93 return Status::ErrorIncorrectSizeAesCbcIv(); |
97 | 94 |
98 if (!EVP_CipherInit_ex(context.get(), | 95 if (!EVP_CipherInit_ex(context.get(), |
99 cipher, | 96 cipher, |
100 NULL, | 97 NULL, |
101 &sym_key->key()[0], | 98 &sym_key->key()[0], |
102 params->iv().data(), | 99 params->iv().data(), |
103 cipher_operation)) { | 100 cipher_operation)) { |
104 return false; | 101 return Status::Error(); |
105 } | 102 } |
106 | 103 |
107 // According to the openssl docs, the amount of data written may be as large | 104 // According to the openssl docs, the amount of data written may be as large |
108 // as (data_size + cipher_block_size - 1), constrained to a multiple of | 105 // as (data_size + cipher_block_size - 1), constrained to a multiple of |
109 // cipher_block_size. | 106 // cipher_block_size. |
110 unsigned output_max_len = data_size + AES_BLOCK_SIZE - 1; | 107 unsigned output_max_len = data_size + AES_BLOCK_SIZE - 1; |
111 const unsigned remainder = output_max_len % AES_BLOCK_SIZE; | 108 const unsigned remainder = output_max_len % AES_BLOCK_SIZE; |
112 if (remainder != 0) | 109 if (remainder != 0) |
113 output_max_len += AES_BLOCK_SIZE - remainder; | 110 output_max_len += AES_BLOCK_SIZE - remainder; |
114 DCHECK_GT(output_max_len, data_size); | 111 DCHECK_GT(output_max_len, data_size); |
115 | 112 |
116 *buffer = blink::WebArrayBuffer::create(output_max_len, 1); | 113 *buffer = blink::WebArrayBuffer::create(output_max_len, 1); |
117 | 114 |
118 unsigned char* const buffer_data = | 115 unsigned char* const buffer_data = |
119 reinterpret_cast<unsigned char*>(buffer->data()); | 116 reinterpret_cast<unsigned char*>(buffer->data()); |
120 | 117 |
121 int output_len = 0; | 118 int output_len = 0; |
122 if (!EVP_CipherUpdate( | 119 if (!EVP_CipherUpdate( |
123 context.get(), buffer_data, &output_len, data, data_size)) | 120 context.get(), buffer_data, &output_len, data, data_size)) |
124 return false; | 121 return Status::Error(); |
125 int final_output_chunk_len = 0; | 122 int final_output_chunk_len = 0; |
126 if (!EVP_CipherFinal_ex( | 123 if (!EVP_CipherFinal_ex( |
127 context.get(), buffer_data + output_len, &final_output_chunk_len)) | 124 context.get(), buffer_data + output_len, &final_output_chunk_len)) |
128 return false; | 125 return Status::Error(); |
129 | 126 |
130 const unsigned final_output_len = | 127 const unsigned final_output_len = |
131 static_cast<unsigned>(output_len) + | 128 static_cast<unsigned>(output_len) + |
132 static_cast<unsigned>(final_output_chunk_len); | 129 static_cast<unsigned>(final_output_chunk_len); |
133 DCHECK_LE(final_output_len, output_max_len); | 130 DCHECK_LE(final_output_len, output_max_len); |
134 | 131 |
135 webcrypto::ShrinkBuffer(buffer, final_output_len); | 132 webcrypto::ShrinkBuffer(buffer, final_output_len); |
136 | 133 |
137 return true; | 134 return Status::Success(); |
138 } | 135 } |
139 | 136 |
140 bool ExportKeyInternalRaw( | 137 Status ExportKeyInternalRaw( |
141 const blink::WebCryptoKey& key, | 138 const blink::WebCryptoKey& key, |
142 blink::WebArrayBuffer* buffer) { | 139 blink::WebArrayBuffer* buffer) { |
143 | 140 |
144 DCHECK(key.handle()); | 141 DCHECK(key.handle()); |
145 DCHECK(buffer); | 142 DCHECK(buffer); |
146 | 143 |
147 if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable()) | 144 if (key.type() != blink::WebCryptoKeyTypeSecret) |
148 return false; | 145 return Status::ErrorUnexpectedKeyType(); |
| 146 |
| 147 // TODO(eroman): This should be in a more generic location. |
| 148 if (!key.extractable()) |
| 149 return Status::ErrorKeyNotExtractable(); |
149 | 150 |
150 const SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); | 151 const SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
151 | 152 |
152 *buffer = webcrypto::CreateArrayBuffer( | 153 *buffer = webcrypto::CreateArrayBuffer( |
153 webcrypto::Uint8VectorStart(sym_key->key()), sym_key->key().size()); | 154 webcrypto::Uint8VectorStart(sym_key->key()), sym_key->key().size()); |
154 | 155 |
155 return true; | 156 return Status::Success(); |
156 } | 157 } |
157 | 158 |
158 } // namespace | 159 } // namespace |
159 | 160 |
160 void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); } | 161 void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); } |
161 | 162 |
162 bool WebCryptoImpl::EncryptInternal(const blink::WebCryptoAlgorithm& algorithm, | 163 Status WebCryptoImpl::EncryptInternal( |
163 const blink::WebCryptoKey& key, | 164 const blink::WebCryptoAlgorithm& algorithm, |
164 const unsigned char* data, | 165 const blink::WebCryptoKey& key, |
165 unsigned data_size, | 166 const unsigned char* data, |
166 blink::WebArrayBuffer* buffer) { | 167 unsigned data_size, |
| 168 blink::WebArrayBuffer* buffer) { |
167 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { | 169 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { |
168 return AesCbcEncryptDecrypt( | 170 return AesCbcEncryptDecrypt( |
169 kDoEncrypt, algorithm, key, data, data_size, buffer); | 171 kDoEncrypt, algorithm, key, data, data_size, buffer); |
170 } | 172 } |
171 | 173 |
172 return false; | 174 return Status::ErrorUnsupported(); |
173 } | 175 } |
174 | 176 |
175 bool WebCryptoImpl::DecryptInternal(const blink::WebCryptoAlgorithm& algorithm, | 177 Status WebCryptoImpl::DecryptInternal( |
176 const blink::WebCryptoKey& key, | 178 const blink::WebCryptoAlgorithm& algorithm, |
177 const unsigned char* data, | 179 const blink::WebCryptoKey& key, |
178 unsigned data_size, | 180 const unsigned char* data, |
179 blink::WebArrayBuffer* buffer) { | 181 unsigned data_size, |
| 182 blink::WebArrayBuffer* buffer) { |
180 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { | 183 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { |
181 return AesCbcEncryptDecrypt( | 184 return AesCbcEncryptDecrypt( |
182 kDoDecrypt, algorithm, key, data, data_size, buffer); | 185 kDoDecrypt, algorithm, key, data, data_size, buffer); |
183 } | 186 } |
184 | 187 |
185 return false; | 188 return Status::ErrorUnsupported(); |
186 } | 189 } |
187 | 190 |
188 bool WebCryptoImpl::DigestInternal(const blink::WebCryptoAlgorithm& algorithm, | 191 Status WebCryptoImpl::DigestInternal(const blink::WebCryptoAlgorithm& algorithm, |
189 const unsigned char* data, | 192 const unsigned char* data, |
190 unsigned data_size, | 193 unsigned data_size, |
191 blink::WebArrayBuffer* buffer) { | 194 blink::WebArrayBuffer* buffer) { |
192 | 195 |
193 crypto::OpenSSLErrStackTracer(FROM_HERE); | 196 crypto::OpenSSLErrStackTracer(FROM_HERE); |
194 | 197 |
195 const EVP_MD* digest_algorithm; | 198 const EVP_MD* digest_algorithm; |
196 switch (algorithm.id()) { | 199 switch (algorithm.id()) { |
197 case blink::WebCryptoAlgorithmIdSha1: | 200 case blink::WebCryptoAlgorithmIdSha1: |
198 digest_algorithm = EVP_sha1(); | 201 digest_algorithm = EVP_sha1(); |
199 break; | 202 break; |
200 case blink::WebCryptoAlgorithmIdSha224: | 203 case blink::WebCryptoAlgorithmIdSha224: |
201 digest_algorithm = EVP_sha224(); | 204 digest_algorithm = EVP_sha224(); |
202 break; | 205 break; |
203 case blink::WebCryptoAlgorithmIdSha256: | 206 case blink::WebCryptoAlgorithmIdSha256: |
204 digest_algorithm = EVP_sha256(); | 207 digest_algorithm = EVP_sha256(); |
205 break; | 208 break; |
206 case blink::WebCryptoAlgorithmIdSha384: | 209 case blink::WebCryptoAlgorithmIdSha384: |
207 digest_algorithm = EVP_sha384(); | 210 digest_algorithm = EVP_sha384(); |
208 break; | 211 break; |
209 case blink::WebCryptoAlgorithmIdSha512: | 212 case blink::WebCryptoAlgorithmIdSha512: |
210 digest_algorithm = EVP_sha512(); | 213 digest_algorithm = EVP_sha512(); |
211 break; | 214 break; |
212 default: | 215 default: |
213 // Not a digest algorithm. | 216 // Not a digest algorithm. |
214 return false; | 217 return Status::ErrorUnsupported(); |
215 } | 218 } |
216 | 219 |
217 crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context( | 220 crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context( |
218 EVP_MD_CTX_create()); | 221 EVP_MD_CTX_create()); |
219 if (!digest_context.get()) { | 222 if (!digest_context.get()) { |
220 return false; | 223 return Status::Error(); |
221 } | 224 } |
222 | 225 |
223 if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, NULL) || | 226 if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, NULL) || |
224 !EVP_DigestUpdate(digest_context.get(), data, data_size)) { | 227 !EVP_DigestUpdate(digest_context.get(), data, data_size)) { |
225 return false; | 228 return Status::Error(); |
226 } | 229 } |
227 | 230 |
228 const int hash_expected_size = EVP_MD_CTX_size(digest_context.get()); | 231 const int hash_expected_size = EVP_MD_CTX_size(digest_context.get()); |
229 if (hash_expected_size <= 0) { | 232 if (hash_expected_size <= 0) { |
230 return false; | 233 return Status::ErrorUnexpected(); |
231 } | 234 } |
232 DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); | 235 DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); |
233 | 236 |
234 *buffer = blink::WebArrayBuffer::create(hash_expected_size, 1); | 237 *buffer = blink::WebArrayBuffer::create(hash_expected_size, 1); |
235 unsigned char* const hash_buffer = | 238 unsigned char* const hash_buffer = |
236 reinterpret_cast<unsigned char* const>(buffer->data()); | 239 reinterpret_cast<unsigned char* const>(buffer->data()); |
237 | 240 |
238 unsigned hash_size = 0; | 241 unsigned hash_size = 0; |
239 if (!EVP_DigestFinal_ex(digest_context.get(), hash_buffer, &hash_size) || | 242 if (!EVP_DigestFinal_ex(digest_context.get(), hash_buffer, &hash_size) || |
240 static_cast<int>(hash_size) != hash_expected_size) { | 243 static_cast<int>(hash_size) != hash_expected_size) { |
241 buffer->reset(); | 244 buffer->reset(); |
242 return false; | 245 return Status::Error(); |
243 } | 246 } |
244 | 247 |
245 return true; | 248 return Status::Success(); |
246 } | 249 } |
247 | 250 |
248 bool WebCryptoImpl::GenerateKeyInternal( | 251 Status WebCryptoImpl::GenerateKeyInternal( |
249 const blink::WebCryptoAlgorithm& algorithm, | 252 const blink::WebCryptoAlgorithm& algorithm, |
250 bool extractable, | 253 bool extractable, |
251 blink::WebCryptoKeyUsageMask usage_mask, | 254 blink::WebCryptoKeyUsageMask usage_mask, |
252 blink::WebCryptoKey* key) { | 255 blink::WebCryptoKey* key) { |
253 | 256 |
254 unsigned keylen_bytes = 0; | 257 unsigned keylen_bytes = 0; |
255 blink::WebCryptoKeyType key_type; | 258 blink::WebCryptoKeyType key_type; |
256 switch (algorithm.id()) { | 259 switch (algorithm.id()) { |
257 case blink::WebCryptoAlgorithmIdAesCbc: { | 260 case blink::WebCryptoAlgorithmIdAesCbc: { |
258 const blink::WebCryptoAesKeyGenParams* params = | 261 const blink::WebCryptoAesKeyGenParams* params = |
259 algorithm.aesKeyGenParams(); | 262 algorithm.aesKeyGenParams(); |
260 DCHECK(params); | 263 DCHECK(params); |
261 if (params->lengthBits() % 8) | 264 if (params->lengthBits() % 8) |
262 return false; | 265 return Status::ErrorGenerateKeyLength(); |
263 keylen_bytes = params->lengthBits() / 8; | 266 keylen_bytes = params->lengthBits() / 8; |
264 if (!GetAESCipherByKeyLength(keylen_bytes)) { | 267 if (!GetAESCipherByKeyLength(keylen_bytes)) { |
265 return false; | 268 return Status::Error(); |
266 } | 269 } |
267 key_type = blink::WebCryptoKeyTypeSecret; | 270 key_type = blink::WebCryptoKeyTypeSecret; |
268 break; | 271 break; |
269 } | 272 } |
270 case blink::WebCryptoAlgorithmIdHmac: { | 273 case blink::WebCryptoAlgorithmIdHmac: { |
271 const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams(); | 274 const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams(); |
272 DCHECK(params); | 275 DCHECK(params); |
273 if (params->hasLengthBytes()) { | 276 if (params->hasLengthBytes()) { |
274 keylen_bytes = params->optionalLengthBytes(); | 277 keylen_bytes = params->optionalLengthBytes(); |
275 } else { | 278 } else { |
276 keylen_bytes = webcrypto::ShaBlockSizeBytes(params->hash().id()); | 279 keylen_bytes = webcrypto::ShaBlockSizeBytes(params->hash().id()); |
277 } | 280 } |
278 key_type = blink::WebCryptoKeyTypeSecret; | 281 key_type = blink::WebCryptoKeyTypeSecret; |
279 break; | 282 break; |
280 } | 283 } |
281 | 284 |
282 default: { return false; } | 285 default: { return Status::ErrorUnsupported(); } |
283 } | 286 } |
284 | 287 |
285 if (keylen_bytes == 0) { | 288 if (keylen_bytes == 0) { |
286 return false; | 289 return Status::ErrorGenerateKeyLength(); |
287 } | 290 } |
288 | 291 |
289 crypto::OpenSSLErrStackTracer(FROM_HERE); | 292 crypto::OpenSSLErrStackTracer(FROM_HERE); |
290 | 293 |
291 std::vector<unsigned char> random_bytes(keylen_bytes, 0); | 294 std::vector<unsigned char> random_bytes(keylen_bytes, 0); |
292 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) { | 295 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) { |
293 return false; | 296 return Status::Error(); |
294 } | 297 } |
295 | 298 |
296 *key = blink::WebCryptoKey::create( | 299 *key = blink::WebCryptoKey::create( |
297 new SymKeyHandle(&random_bytes[0], random_bytes.size()), | 300 new SymKeyHandle(&random_bytes[0], random_bytes.size()), |
298 key_type, extractable, algorithm, usage_mask); | 301 key_type, extractable, algorithm, usage_mask); |
299 | 302 |
300 return true; | 303 return Status::Success(); |
301 } | 304 } |
302 | 305 |
303 bool WebCryptoImpl::GenerateKeyPairInternal( | 306 Status WebCryptoImpl::GenerateKeyPairInternal( |
304 const blink::WebCryptoAlgorithm& algorithm, | 307 const blink::WebCryptoAlgorithm& algorithm, |
305 bool extractable, | 308 bool extractable, |
306 blink::WebCryptoKeyUsageMask usage_mask, | 309 blink::WebCryptoKeyUsageMask usage_mask, |
307 blink::WebCryptoKey* public_key, | 310 blink::WebCryptoKey* public_key, |
308 blink::WebCryptoKey* private_key) { | 311 blink::WebCryptoKey* private_key) { |
309 // TODO(padolph): Placeholder for OpenSSL implementation. | 312 // TODO(padolph): Placeholder for OpenSSL implementation. |
310 // Issue http://crbug.com/267888. | 313 // Issue http://crbug.com/267888. |
311 return false; | 314 return Status::ErrorUnsupported(); |
312 } | 315 } |
313 | 316 |
314 bool WebCryptoImpl::ImportKeyInternal( | 317 Status WebCryptoImpl::ImportKeyInternal( |
315 blink::WebCryptoKeyFormat format, | 318 blink::WebCryptoKeyFormat format, |
316 const unsigned char* key_data, | 319 const unsigned char* key_data, |
317 unsigned key_data_size, | 320 unsigned key_data_size, |
318 const blink::WebCryptoAlgorithm& algorithm_or_null, | 321 const blink::WebCryptoAlgorithm& algorithm_or_null, |
319 bool extractable, | 322 bool extractable, |
320 blink::WebCryptoKeyUsageMask usage_mask, | 323 blink::WebCryptoKeyUsageMask usage_mask, |
321 blink::WebCryptoKey* key) { | 324 blink::WebCryptoKey* key) { |
322 // TODO(eroman): Currently expects algorithm to always be specified, as it is | 325 // TODO(eroman): Currently expects algorithm to always be specified, as it is |
323 // required for raw format. | 326 // required for raw format. |
324 if (algorithm_or_null.isNull()) | 327 if (algorithm_or_null.isNull()) |
325 return false; | 328 return Status::ErrorMissingAlgorithmImportRawKey(); |
326 const blink::WebCryptoAlgorithm& algorithm = algorithm_or_null; | 329 const blink::WebCryptoAlgorithm& algorithm = algorithm_or_null; |
327 | 330 |
328 // TODO(padolph): Support all relevant alg types and then remove this gate. | 331 // TODO(padolph): Support all relevant alg types and then remove this gate. |
329 if (algorithm.id() != blink::WebCryptoAlgorithmIdHmac && | 332 if (algorithm.id() != blink::WebCryptoAlgorithmIdHmac && |
330 algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) { | 333 algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) { |
331 return false; | 334 return Status::ErrorUnsupported(); |
332 } | 335 } |
333 | 336 |
334 // TODO(padolph): Need to split handling for symmetric (raw format) and | 337 // TODO(padolph): Need to split handling for symmetric (raw format) and |
335 // asymmetric (spki or pkcs8 format) keys. | 338 // asymmetric (spki or pkcs8 format) keys. |
336 // Currently only supporting symmetric. | 339 // Currently only supporting symmetric. |
337 | 340 |
338 // Symmetric keys are always type secret | 341 // Symmetric keys are always type secret |
339 blink::WebCryptoKeyType type = blink::WebCryptoKeyTypeSecret; | 342 blink::WebCryptoKeyType type = blink::WebCryptoKeyTypeSecret; |
340 | 343 |
341 const unsigned char* raw_key_data; | 344 const unsigned char* raw_key_data; |
342 unsigned raw_key_data_size; | 345 unsigned raw_key_data_size; |
343 switch (format) { | 346 switch (format) { |
344 case blink::WebCryptoKeyFormatRaw: | 347 case blink::WebCryptoKeyFormatRaw: |
345 raw_key_data = key_data; | 348 raw_key_data = key_data; |
346 raw_key_data_size = key_data_size; | 349 raw_key_data_size = key_data_size; |
347 // The NSS implementation fails when importing a raw AES key with a length | 350 // The NSS implementation fails when importing a raw AES key with a length |
348 // incompatible with AES. The line below is to match this behavior. | 351 // incompatible with AES. The line below is to match this behavior. |
349 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc && | 352 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc && |
350 !GetAESCipherByKeyLength(raw_key_data_size)) { | 353 !GetAESCipherByKeyLength(raw_key_data_size)) { |
351 return false; | 354 return Status::Error(); |
352 } | 355 } |
353 break; | 356 break; |
354 case blink::WebCryptoKeyFormatJwk: | 357 case blink::WebCryptoKeyFormatJwk: |
355 // TODO(padolph): Handle jwk format; need simple JSON parser. | 358 return Status::ErrorUnexpected(); |
356 // break; | |
357 return false; | |
358 default: | 359 default: |
359 return false; | 360 return Status::ErrorUnsupported(); |
360 } | 361 } |
361 | 362 |
362 *key = blink::WebCryptoKey::create( | 363 *key = blink::WebCryptoKey::create( |
363 new SymKeyHandle(raw_key_data, raw_key_data_size), | 364 new SymKeyHandle(raw_key_data, raw_key_data_size), |
364 type, extractable, algorithm, usage_mask); | 365 type, extractable, algorithm, usage_mask); |
365 | 366 |
366 return true; | 367 return Status::Success(); |
367 } | 368 } |
368 | 369 |
369 bool WebCryptoImpl::ExportKeyInternal( | 370 Status WebCryptoImpl::ExportKeyInternal( |
370 blink::WebCryptoKeyFormat format, | 371 blink::WebCryptoKeyFormat format, |
371 const blink::WebCryptoKey& key, | 372 const blink::WebCryptoKey& key, |
372 blink::WebArrayBuffer* buffer) { | 373 blink::WebArrayBuffer* buffer) { |
373 switch (format) { | 374 switch (format) { |
374 case blink::WebCryptoKeyFormatRaw: | 375 case blink::WebCryptoKeyFormatRaw: |
375 return ExportKeyInternalRaw(key, buffer); | 376 return ExportKeyInternalRaw(key, buffer); |
376 case blink::WebCryptoKeyFormatSpki: | 377 case blink::WebCryptoKeyFormatSpki: |
377 // TODO(padolph): Implement spki export | 378 // TODO(padolph): Implement spki export |
378 return false; | 379 return Status::ErrorUnsupported(); |
379 case blink::WebCryptoKeyFormatPkcs8: | 380 case blink::WebCryptoKeyFormatPkcs8: |
380 // TODO(padolph): Implement pkcs8 export | 381 // TODO(padolph): Implement pkcs8 export |
381 return false; | 382 return Status::ErrorUnsupported(); |
382 default: | 383 default: |
383 return false; | 384 return Status::ErrorUnsupported(); |
384 } | 385 } |
385 return false; | 386 return Status::ErrorUnsupported(); |
386 } | 387 } |
387 | 388 |
388 bool WebCryptoImpl::SignInternal( | 389 Status WebCryptoImpl::SignInternal( |
389 const blink::WebCryptoAlgorithm& algorithm, | 390 const blink::WebCryptoAlgorithm& algorithm, |
390 const blink::WebCryptoKey& key, | 391 const blink::WebCryptoKey& key, |
391 const unsigned char* data, | 392 const unsigned char* data, |
392 unsigned data_size, | 393 unsigned data_size, |
393 blink::WebArrayBuffer* buffer) { | 394 blink::WebArrayBuffer* buffer) { |
394 | 395 |
395 blink::WebArrayBuffer result; | 396 blink::WebArrayBuffer result; |
396 | 397 |
397 switch (algorithm.id()) { | 398 switch (algorithm.id()) { |
398 case blink::WebCryptoAlgorithmIdHmac: { | 399 case blink::WebCryptoAlgorithmIdHmac: { |
399 | 400 |
400 DCHECK_EQ(key.algorithm().id(), blink::WebCryptoAlgorithmIdHmac); | 401 DCHECK_EQ(key.algorithm().id(), blink::WebCryptoAlgorithmIdHmac); |
401 DCHECK_NE(0, key.usages() & blink::WebCryptoKeyUsageSign); | 402 DCHECK_NE(0, key.usages() & blink::WebCryptoKeyUsageSign); |
402 | 403 |
403 const blink::WebCryptoHmacParams* const params = algorithm.hmacParams(); | 404 const blink::WebCryptoHmacParams* const params = algorithm.hmacParams(); |
404 if (!params) | 405 if (!params) |
405 return false; | 406 return Status::ErrorUnexpected(); |
406 | 407 |
407 const EVP_MD* evp_sha = 0; | 408 const EVP_MD* evp_sha = 0; |
408 unsigned int hmac_expected_length = 0; | 409 unsigned int hmac_expected_length = 0; |
409 // Note that HMAC length is determined by the hash used. | 410 // Note that HMAC length is determined by the hash used. |
410 switch (params->hash().id()) { | 411 switch (params->hash().id()) { |
411 case blink::WebCryptoAlgorithmIdSha1: | 412 case blink::WebCryptoAlgorithmIdSha1: |
412 evp_sha = EVP_sha1(); | 413 evp_sha = EVP_sha1(); |
413 hmac_expected_length = SHA_DIGEST_LENGTH; | 414 hmac_expected_length = SHA_DIGEST_LENGTH; |
414 break; | 415 break; |
415 case blink::WebCryptoAlgorithmIdSha224: | 416 case blink::WebCryptoAlgorithmIdSha224: |
416 evp_sha = EVP_sha224(); | 417 evp_sha = EVP_sha224(); |
417 hmac_expected_length = SHA224_DIGEST_LENGTH; | 418 hmac_expected_length = SHA224_DIGEST_LENGTH; |
418 break; | 419 break; |
419 case blink::WebCryptoAlgorithmIdSha256: | 420 case blink::WebCryptoAlgorithmIdSha256: |
420 evp_sha = EVP_sha256(); | 421 evp_sha = EVP_sha256(); |
421 hmac_expected_length = SHA256_DIGEST_LENGTH; | 422 hmac_expected_length = SHA256_DIGEST_LENGTH; |
422 break; | 423 break; |
423 case blink::WebCryptoAlgorithmIdSha384: | 424 case blink::WebCryptoAlgorithmIdSha384: |
424 evp_sha = EVP_sha384(); | 425 evp_sha = EVP_sha384(); |
425 hmac_expected_length = SHA384_DIGEST_LENGTH; | 426 hmac_expected_length = SHA384_DIGEST_LENGTH; |
426 break; | 427 break; |
427 case blink::WebCryptoAlgorithmIdSha512: | 428 case blink::WebCryptoAlgorithmIdSha512: |
428 evp_sha = EVP_sha512(); | 429 evp_sha = EVP_sha512(); |
429 hmac_expected_length = SHA512_DIGEST_LENGTH; | 430 hmac_expected_length = SHA512_DIGEST_LENGTH; |
430 break; | 431 break; |
431 default: | 432 default: |
432 // Not a digest algorithm. | 433 // Not a digest algorithm. |
433 return false; | 434 return Status::ErrorUnsupported(); |
434 } | 435 } |
435 | 436 |
436 SymKeyHandle* const sym_key = | 437 SymKeyHandle* const sym_key = |
437 reinterpret_cast<SymKeyHandle*>(key.handle()); | 438 reinterpret_cast<SymKeyHandle*>(key.handle()); |
438 const std::vector<unsigned char>& raw_key = sym_key->key(); | 439 const std::vector<unsigned char>& raw_key = sym_key->key(); |
439 | 440 |
440 // OpenSSL wierdness here. | 441 // OpenSSL wierdness here. |
441 // First, HMAC() needs a void* for the key data, so make one up front as a | 442 // First, HMAC() needs a void* for the key data, so make one up front as a |
442 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, | 443 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, |
443 // which will result if the raw_key vector is empty; an entirely valid | 444 // which will result if the raw_key vector is empty; an entirely valid |
(...skipping 10 matching lines...) Expand all Loading... |
454 | 455 |
455 unsigned int hmac_actual_length; | 456 unsigned int hmac_actual_length; |
456 unsigned char* const success = HMAC(evp_sha, | 457 unsigned char* const success = HMAC(evp_sha, |
457 raw_key_voidp, | 458 raw_key_voidp, |
458 raw_key.size(), | 459 raw_key.size(), |
459 data, | 460 data, |
460 data_size, | 461 data_size, |
461 hmac_result.safe_buffer(), | 462 hmac_result.safe_buffer(), |
462 &hmac_actual_length); | 463 &hmac_actual_length); |
463 if (!success || hmac_actual_length != hmac_expected_length) | 464 if (!success || hmac_actual_length != hmac_expected_length) |
464 return false; | 465 return Status::Error(); |
465 | 466 |
466 break; | 467 break; |
467 } | 468 } |
468 default: | 469 default: |
469 return false; | 470 return Status::ErrorUnsupported(); |
470 } | 471 } |
471 | 472 |
472 *buffer = result; | 473 *buffer = result; |
473 return true; | 474 return Status::Success(); |
474 } | 475 } |
475 | 476 |
476 bool WebCryptoImpl::VerifySignatureInternal( | 477 Status WebCryptoImpl::VerifySignatureInternal( |
477 const blink::WebCryptoAlgorithm& algorithm, | 478 const blink::WebCryptoAlgorithm& algorithm, |
478 const blink::WebCryptoKey& key, | 479 const blink::WebCryptoKey& key, |
479 const unsigned char* signature, | 480 const unsigned char* signature, |
480 unsigned signature_size, | 481 unsigned signature_size, |
481 const unsigned char* data, | 482 const unsigned char* data, |
482 unsigned data_size, | 483 unsigned data_size, |
483 bool* signature_match) { | 484 bool* signature_match) { |
484 switch (algorithm.id()) { | 485 switch (algorithm.id()) { |
485 case blink::WebCryptoAlgorithmIdHmac: { | 486 case blink::WebCryptoAlgorithmIdHmac: { |
486 blink::WebArrayBuffer result; | 487 blink::WebArrayBuffer result; |
487 if (!SignInternal(algorithm, key, data, data_size, &result)) { | 488 Status status = SignInternal(algorithm, key, data, data_size, &result); |
488 return false; | 489 if (status.IsError()) { |
| 490 return status; |
489 } | 491 } |
490 | 492 |
491 // Handling of truncated signatures is underspecified in the WebCrypto | 493 // Handling of truncated signatures is underspecified in the WebCrypto |
492 // spec, so here we fail verification if a truncated signature is being | 494 // spec, so here we fail verification if a truncated signature is being |
493 // verified. | 495 // verified. |
494 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097 | 496 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097 |
495 *signature_match = | 497 *signature_match = |
496 result.byteLength() == signature_size && | 498 result.byteLength() == signature_size && |
497 crypto::SecureMemEqual(result.data(), signature, signature_size); | 499 crypto::SecureMemEqual(result.data(), signature, signature_size); |
498 | 500 |
499 break; | 501 break; |
500 } | 502 } |
501 default: | 503 default: |
502 return false; | 504 return Status::ErrorUnsupported(); |
503 } | 505 } |
504 return true; | 506 return Status::Success(); |
505 } | 507 } |
506 | 508 |
507 bool WebCryptoImpl::ImportRsaPublicKeyInternal( | 509 Status WebCryptoImpl::ImportRsaPublicKeyInternal( |
508 const unsigned char* modulus_data, | 510 const unsigned char* modulus_data, |
509 unsigned modulus_size, | 511 unsigned modulus_size, |
510 const unsigned char* exponent_data, | 512 const unsigned char* exponent_data, |
511 unsigned exponent_size, | 513 unsigned exponent_size, |
512 const blink::WebCryptoAlgorithm& algorithm, | 514 const blink::WebCryptoAlgorithm& algorithm, |
513 bool extractable, | 515 bool extractable, |
514 blink::WebCryptoKeyUsageMask usage_mask, | 516 blink::WebCryptoKeyUsageMask usage_mask, |
515 blink::WebCryptoKey* key) { | 517 blink::WebCryptoKey* key) { |
516 // TODO(padolph): Placeholder for OpenSSL implementation. | 518 // TODO(padolph): Placeholder for OpenSSL implementation. |
517 // Issue http://crbug.com/267888. | 519 // Issue http://crbug.com/267888. |
518 return false; | 520 return Status::ErrorUnsupported(); |
519 } | 521 } |
520 | 522 |
521 } // namespace content | 523 } // namespace content |
OLD | NEW |