Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: content/renderer/webcrypto/webcrypto_impl.cc

Issue 25906002: [webcrypto] Add JWK import for HMAC and AES-CBC key. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixes for eroman Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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 <algorithm>
8 #include <functional>
9 #include <map>
10 #include "base/json/json_reader.h"
11 #include "base/lazy_instance.h"
7 #include "base/logging.h" 12 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h" 13 #include "base/memory/scoped_ptr.h"
9 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" 14 #include "base/strings/string_piece.h"
15 #include "base/values.h"
16 #include "content/renderer/webcrypto/webcrypto_util.h"
10 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" 17 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
11 #include "third_party/WebKit/public/platform/WebCryptoKey.h" 19 #include "third_party/WebKit/public/platform/WebCryptoKey.h"
12 20
13 namespace content { 21 namespace content {
14 22
23 namespace {
24
25 // Binds a specific key length value to a compatible factory function.
26 typedef WebKit::WebCryptoAlgorithm (*AlgFactoryFuncWithOneShortArg)(
27 unsigned short key_length);
28 template <AlgFactoryFuncWithOneShortArg func, unsigned short key_length>
29 WebKit::WebCryptoAlgorithm BindAlgFactoryWithKeyLen() {
eroman 2013/10/28 23:02:00 Ah, clever! I had been thinking something dumber
padolph 2013/10/29 02:25:40 The problem was that the create functions don't al
30 return func(key_length);
31 }
32
33 // Defines a map between a JWK 'alg' string ID and the corresponding Web Crypto
34 // factory function. The signature of the factory function in the map must be
35 // WebKit::WebCryptoAlgorithm func();
eroman 2013/10/28 23:02:00 [optional] Could reference the function typedef na
padolph 2013/10/29 02:25:40 I just removed that part of the comment since it i
36 typedef WebKit::WebCryptoAlgorithm (*AlgFactoryFuncNoArgs)();
37 typedef std::map<std::string, AlgFactoryFuncNoArgs> JwkAlgFactoryMap;
38
39 class JwkAlgorithmFactoryMap {
40 public:
41 JwkAlgorithmFactoryMap() {
42 map_["HS256"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 256>;
43 map_["HS256"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 256>;
44 map_["HS384"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 384>;
45 map_["HS384"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 512>;
46 map_["RS256"] =
47 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 256>;
48 map_["RS384"] =
49 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 384>;
50 map_["RS512"] =
51 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 512>;
52 map_["RSA1_5"] = &CreateRsaEsAlgorithm;
53 map_["RSA-OAEP"] =
54 &BindAlgFactoryWithKeyLen<CreateRsaOaepAlgorithmByKeyLen, 512>;
55 map_["A128KW"] = &WebKit::WebCryptoAlgorithm::createNull;
56 map_["A256KW"] = &WebKit::WebCryptoAlgorithm::createNull;
57 map_["A128GCM"] =
58 &BindAlgFactoryWithKeyLen<CreateAesGcmKeyGenAlgorithm, 256>;
59 map_["A256GCM"] =
60 &BindAlgFactoryWithKeyLen<CreateAesGcmKeyGenAlgorithm, 256>;
61 map_["A128CBC"] =
62 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 128>;
63 map_["A256CBC"] =
64 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 256>;
65 map_["A384CBC"] =
66 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 384>;
67 map_["A512CBC"] =
68 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 512>;
69 }
70 JwkAlgFactoryMap& Map() { return map_; }
eroman 2013/10/28 23:02:00 Please make this a const-reference. (For mutable p
padolph 2013/10/29 02:25:40 Done.
71
72 private:
73 JwkAlgFactoryMap map_;
74 };
75 base::LazyInstance<JwkAlgorithmFactoryMap> jwk_alg_factory_map =
76 LAZY_INSTANCE_INITIALIZER;
77
78 // TODO(padolph) Verify this logic is sufficient to judge algorithm
eroman 2013/10/28 23:02:00 nit: semicolon after closing parenthesis.
padolph 2013/10/29 02:25:40 Done.
79 // "consistency" for JWK import, and for all supported algorithms.
80 bool WebCryptoAlgorithmsConsistent(const WebKit::WebCryptoAlgorithm& lhs,
81 const WebKit::WebCryptoAlgorithm& rhs) {
82 if (lhs.id() == rhs.id()) {
83 if (lhs.id() == WebKit::WebCryptoAlgorithmIdHmac ||
84 lhs.id() == WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
85 lhs.id() == WebKit::WebCryptoAlgorithmIdRsaOaep) {
86 if (WebCryptoAlgorithmsConsistent(lhs.hmacParams()->hash(),
eroman 2013/10/28 23:02:00 Unfortunately getting the hash() is not this easy
padolph 2013/10/29 02:25:40 Oops missed that. Thank you. Done and done.
87 rhs.hmacParams()->hash())) {
88 return true;
89 }
90 return false;
91 }
92 return true;
93 }
94 return false;
95 }
96
97 } // namespace
98
15 WebCryptoImpl::WebCryptoImpl() { 99 WebCryptoImpl::WebCryptoImpl() {
16 Init(); 100 Init();
17 } 101 }
18 102
19 // static 103 WebCryptoImpl::~WebCryptoImpl() {}
eroman 2013/10/28 23:02:00 Doesn't seem like this is needed.
padolph 2013/10/29 02:25:40 I think the same, but clang complained loudly; not
eroman 2013/11/04 21:01:15 What is the error you get without this?
padolph 2013/11/05 03:30:54 I rebuilt again with clang and don't see the error
20 // TODO(eroman): This works by re-allocating a new buffer. It would be better if
21 // the WebArrayBuffer could just be truncated instead.
22 void WebCryptoImpl::ShrinkBuffer(
23 WebKit::WebArrayBuffer* buffer,
24 unsigned new_size) {
25 DCHECK_LE(new_size, buffer->byteLength());
26
27 if (new_size == buffer->byteLength())
28 return;
29
30 WebKit::WebArrayBuffer new_buffer =
31 WebKit::WebArrayBuffer::create(new_size, 1);
32 DCHECK(!new_buffer.isNull());
33 memcpy(new_buffer.data(), buffer->data(), new_size);
34 *buffer = new_buffer;
35 }
36 104
37 void WebCryptoImpl::encrypt( 105 void WebCryptoImpl::encrypt(
38 const WebKit::WebCryptoAlgorithm& algorithm, 106 const WebKit::WebCryptoAlgorithm& algorithm,
39 const WebKit::WebCryptoKey& key, 107 const WebKit::WebCryptoKey& key,
40 const unsigned char* data, 108 const unsigned char* data,
41 unsigned data_size, 109 unsigned data_size,
42 WebKit::WebCryptoResult result) { 110 WebKit::WebCryptoResult result) {
43 WebKit::WebArrayBuffer buffer; 111 WebKit::WebArrayBuffer buffer;
44 if (!EncryptInternal(algorithm, key, data, data_size, &buffer)) { 112 if (!EncryptInternal(algorithm, key, data, data_size, &buffer)) {
45 result.completeWithError(); 113 result.completeWithError();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 WebKit::WebCryptoKeyFormat format, 164 WebKit::WebCryptoKeyFormat format,
97 const unsigned char* key_data, 165 const unsigned char* key_data,
98 unsigned key_data_size, 166 unsigned key_data_size,
99 const WebKit::WebCryptoAlgorithm& algorithm, 167 const WebKit::WebCryptoAlgorithm& algorithm,
100 bool extractable, 168 bool extractable,
101 WebKit::WebCryptoKeyUsageMask usage_mask, 169 WebKit::WebCryptoKeyUsageMask usage_mask,
102 WebKit::WebCryptoResult result) { 170 WebKit::WebCryptoResult result) {
103 WebKit::WebCryptoKeyType type; 171 WebKit::WebCryptoKeyType type;
104 scoped_ptr<WebKit::WebCryptoKeyHandle> handle; 172 scoped_ptr<WebKit::WebCryptoKeyHandle> handle;
105 173
106 if (!ImportKeyInternal(format, 174 if (format == WebKit::WebCryptoKeyFormatJwk) {
107 key_data, 175 if (!ImportKeyJwk(key_data,
108 key_data_size, 176 key_data_size,
109 algorithm, 177 extractable,
110 usage_mask, 178 algorithm,
111 &handle, 179 usage_mask,
112 &type)) { 180 &handle,
113 result.completeWithError(); 181 &type)) {
114 return; 182 result.completeWithError();
183 }
184 } else {
185 if (!ImportKeyInternal(format,
186 key_data,
187 key_data_size,
188 algorithm,
189 usage_mask,
190 &handle,
191 &type)) {
192 result.completeWithError();
193 }
115 } 194 }
116 195
117 WebKit::WebCryptoKey key( 196 WebKit::WebCryptoKey key(WebKit::WebCryptoKey::create(
118 WebKit::WebCryptoKey::create( 197 handle.release(), type, extractable, algorithm, usage_mask));
119 handle.release(), type, extractable, algorithm, usage_mask));
120 198
121 result.completeWithKey(key); 199 result.completeWithKey(key);
122 } 200 }
123 201
124 void WebCryptoImpl::sign( 202 void WebCryptoImpl::sign(
125 const WebKit::WebCryptoAlgorithm& algorithm, 203 const WebKit::WebCryptoAlgorithm& algorithm,
126 const WebKit::WebCryptoKey& key, 204 const WebKit::WebCryptoKey& key,
127 const unsigned char* data, 205 const unsigned char* data,
128 unsigned data_size, 206 unsigned data_size,
129 WebKit::WebCryptoResult result) { 207 WebKit::WebCryptoResult result) {
(...skipping 20 matching lines...) Expand all
150 signature_size, 228 signature_size,
151 data, 229 data,
152 data_size, 230 data_size,
153 &signature_match)) { 231 &signature_match)) {
154 result.completeWithError(); 232 result.completeWithError();
155 } else { 233 } else {
156 result.completeWithBoolean(signature_match); 234 result.completeWithBoolean(signature_match);
157 } 235 }
158 } 236 }
159 237
238 bool WebCryptoImpl::ImportKeyJwk(
239 const unsigned char* key_data,
240 unsigned key_data_size,
241 bool extractable,
242 const WebKit::WebCryptoAlgorithm& algorithm,
243 WebKit::WebCryptoKeyUsageMask usage_mask,
244 scoped_ptr<WebKit::WebCryptoKeyHandle>* handle,
245 WebKit::WebCryptoKeyType* type) {
246
247 // JSON Web Key Format (JWK)
248 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16
249 // TODO(padolph) Not all possible values are handled by this code right now
250 //
251 // A JWK is a simple JSON dictionary with the following entries
252 // - "kty" (Key Type) Parameter, REQUIRED
253 // - <kty-specific parameters, see below>, REQUIRED
254 // - "use" (Key Use) Parameter, OPTIONAL
255 // - "alg" (Algorithm) Parameter, OPTIONAL
256 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE]
257 // (all other entries are ignored)
258 //
259 // Input key_data contains the JWK. To build a Web Crypto Key, the JWK values
260 // are parsed out and used as follows:
261 // Web Crypto Key type <-- (deduced)
262 // Web Crypto Key extractable <-- extractable
263 // Web Crypto Key algorithm <-- alg
264 // Web Crypto Key keyUsage <-- usage
265 // Web Crypto Key keying material <-- kty-specific parameters
266 //
267 // Values for each entry are case-sensitive and defined in
268 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16.
269 // Note that not all values specified by JOSE are handled by this code. Only
270 // handled values are listed.
271 // - kty (Key Type)
272 // +-------+--------------------------------------------------------------+
273 // | "RSA" | RSA [RFC3447] |
274 // | "oct" | Octet sequence (used to represent symmetric keys) |
275 // +-------+--------------------------------------------------------------+
276 // - use (Key Use)
277 // +-------+--------------------------------------------------------------+
278 // | "enc" | encrypt and decrypt operations |
279 // | "sig" | sign and verify (MAC) operations |
280 // | "wrap"| key wrap and unwrap [not yet part of JOSE] |
281 // +-------+--------------------------------------------------------------+
282 // - extractable (Key Exportability)
283 // +-------+--------------------------------------------------------------+
284 // | true | Key may be exported from the trusted environment |
285 // | false | Key cannot exit the trusted environment |
286 // +-------+--------------------------------------------------------------+
287 // - alg (Algorithm)
288 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16
289 // +--------------+-------------------------------------------------------+
290 // | Digital Signature or MAC Algorithm |
291 // +--------------+-------------------------------------------------------+
292 // | "HS256" | HMAC using SHA-256 hash algorithm |
293 // | "HS384" | HMAC using SHA-384 hash algorithm |
294 // | "HS512" | HMAC using SHA-512 hash algorithm |
295 // | "RS256" | RSASSA using SHA-256 hash algorithm |
296 // | "RS384" | RSASSA using SHA-384 hash algorithm |
297 // | "RS512" | RSASSA using SHA-512 hash algorithm |
298 // +--------------+-------------------------------------------------------|
299 // | Key Management Algorithm |
300 // +--------------+-------------------------------------------------------+
301 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] |
302 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding |
303 // | | (OAEP) [RFC3447], with the default parameters |
304 // | | specified by RFC3447 in Section A.2.1 |
305 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm |
306 // | | [RFC3394] using 128 bit keys |
307 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
308 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
309 // | | 128 bit keys |
310 // | "A256GCM" | AES GCM using 256 bit keys |
311 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
312 // | | padding [NIST.800-38A] [not yet part of JOSE] |
313 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] |
314 // | "A384CBC" | AES CBC using 384 bit keys [not yet part of JOSE] |
315 // | "A512CBC" | AES CBC using 512 bit keys [not yet part of JOSE] |
316 // +--------------+-------------------------------------------------------+
317 //
318 // kty-specific parameters
319 // The value of kty determines the type and content of the keying material
320 // carried in the JWK to be imported. Currently only two possibilities are
321 // supported: a raw key or an RSA public key. RSA private keys are not
322 // supported because typical applications seldom need to import a private key,
323 // and the large number of JWK parameters required to describe one.
324 // - kty == "oct" (symmetric or other raw key)
325 // +-------+--------------------------------------------------------------+
326 // | "k" | Contains the value of the symmetric (or other single-valued) |
327 // | | key. It is represented as the base64url encoding of the |
328 // | | octet sequence containing the key value. |
329 // +-------+--------------------------------------------------------------+
330 // - kty == "RSA" (RSA public key)
331 // +-------+--------------------------------------------------------------+
332 // | "n" | Contains the modulus value for the RSA public key. It is |
333 // | | represented as the base64url encoding of the value's |
334 // | | unsigned big endian representation as an octet sequence. |
335 // +-------+--------------------------------------------------------------+
336 // | "e" | Contains the exponent value for the RSA public key. It is |
337 // | | represented as the base64url encoding of the value's |
338 // | | unsigned big endian representation as an octet sequence. |
339 // +-------+--------------------------------------------------------------+
340 //
341 // Conflict resolution
342 // The type, algorithm, extractable, and usage_mask input parameters may be
343 // different from similar values inside the JWK. The Web Crypto spec says that
344 // if a JWK value is present, but is inconsistent with the input value, it is
345 // an error and the operation must fail.
346
347 // Parse the incoming JWK JSON.
348 base::StringPiece json_string(reinterpret_cast<const char*>(key_data),
349 key_data_size);
350 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
351 // Note, bare pointer dict_value is ok since it points into scoped value.
352 base::DictionaryValue* dict_value = NULL;
353 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
354 return false;
355
356 // JWK "kty". Exit early if this required JWK parameter is missing.
357 std::string jwk_kty_value;
358 if (!dict_value->GetString("kty", &jwk_kty_value))
359 return false;
360
361 // JWK "extractable" --> extractable parameter
362 bool jwk_extractable_value;
363 if (dict_value->GetBoolean("extractable", &jwk_extractable_value)) {
364 if (jwk_extractable_value != extractable)
365 return false;
366 }
367
368 // JWK "alg" --> algorithm parameter
369 std::string jwk_alg_value;
370 if (dict_value->GetString("alg", &jwk_alg_value)) {
371 const JwkAlgFactoryMap::const_iterator pos =
372 jwk_alg_factory_map.Get().Map().find(jwk_alg_value);
eroman 2013/10/28 23:02:00 How about moving this logic into the singleton cla
padolph 2013/10/29 02:25:40 Good idea, thank you. Done.
373 if (pos == jwk_alg_factory_map.Get().Map().end())
374 return false;
375 WebKit::WebCryptoAlgorithm webcrypto_algorithm_from_jwk = pos->second();
376 if (webcrypto_algorithm_from_jwk.isNull()) {
377 return false;
378 }
379 if (!WebCryptoAlgorithmsConsistent(webcrypto_algorithm_from_jwk, algorithm))
eroman 2013/10/28 23:02:00 Now that we have made WebCryptoAlgorithm nullable,
padolph 2013/10/29 02:25:40 Done. Added input parameter sanity checks and corr
380 return false;
381 }
382
383 // JWK "use" --> usage_mask parameter
384 std::string jwk_use_value;
385 if (dict_value->GetString("use", &jwk_use_value)) {
386 unsigned jwk_usage_mask;
387 if (jwk_use_value == "enc") {
388 jwk_usage_mask =
389 WebKit::WebCryptoKeyUsageEncrypt | WebKit::WebCryptoKeyUsageDecrypt;
390 } else if (jwk_use_value == "sig") {
391 jwk_usage_mask =
392 WebKit::WebCryptoKeyUsageSign | WebKit::WebCryptoKeyUsageVerify;
393 } else if (jwk_use_value == "wrap") {
394 jwk_usage_mask =
395 WebKit::WebCryptoKeyUsageWrapKey | WebKit::WebCryptoKeyUsageUnwrapKey;
396 } else {
397 return false;
398 }
399 if ((jwk_usage_mask & usage_mask) != usage_mask) {
400 // usage_mask must be a subset of jwk_usage_mask.
eroman 2013/10/28 23:02:00 Please capitalize comments.
padolph 2013/10/29 02:25:40 Done.
401 return false;
402 }
403 }
404
405 // JWK keying material --> ImportKeyInternal()
406 if (jwk_kty_value == "oct") {
407 if (*type != WebKit::WebCryptoKeyTypeSecret)
408 return false;
409 std::string jwk_k_value_url64;
410 if (!dict_value->GetString("k", &jwk_k_value_url64))
411 return false;
412 std::string jwk_k_value;
413 if (!Base64DecodeUrlSafe(jwk_k_value_url64, &jwk_k_value) ||
414 !jwk_k_value.size()) {
415 return false;
416 }
417 if (!ImportKeyInternal(WebKit::WebCryptoKeyFormatRaw,
418 reinterpret_cast<const uint8*>(jwk_k_value.data()),
419 jwk_k_value.size(),
420 algorithm,
421 usage_mask,
422 handle,
423 type)) {
424 return false;
425 }
426 } else if (jwk_kty_value == "RSA") {
427 // Only an RSA public key is currently handled.
428 if (*type != WebKit::WebCryptoKeyTypePublic)
429 return false;
430 // TODO(padolph): JWK import RSA public key
431 return false;
432 } else {
433 return false;
434 }
435
436 return true;
437 }
438
160 } // namespace content 439 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698