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

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 plus more tests 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() {
30 return func(key_length);
31 }
32
33 // Defines a map between a JWK 'alg' string ID and a corresponding Web Crypto
34 // factory function.
35 typedef WebKit::WebCryptoAlgorithm (*AlgFactoryFuncNoArgs)();
36 typedef std::map<std::string, AlgFactoryFuncNoArgs> JwkAlgFactoryMap;
37
38 class JwkAlgorithmFactoryMap {
39 public:
40 JwkAlgorithmFactoryMap() {
41 map_["HS256"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 256>;
42 map_["HS256"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 256>;
43 map_["HS384"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 384>;
44 map_["HS384"] = &BindAlgFactoryWithKeyLen<CreateHmacAlgorithmByKeyLen, 512>;
45 map_["RS256"] =
46 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 256>;
47 map_["RS384"] =
48 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 384>;
49 map_["RS512"] =
50 &BindAlgFactoryWithKeyLen<CreateRsaSsaAlgorithmByKeyLen, 512>;
51 map_["RSA1_5"] = &CreateRsaEsAlgorithm;
52 map_["RSA-OAEP"] =
53 &BindAlgFactoryWithKeyLen<CreateRsaOaepAlgorithmByKeyLen, 512>;
54 map_["A128KW"] = &WebKit::WebCryptoAlgorithm::createNull;
55 map_["A256KW"] = &WebKit::WebCryptoAlgorithm::createNull;
56 map_["A128GCM"] =
57 &BindAlgFactoryWithKeyLen<CreateAesGcmKeyGenAlgorithm, 256>;
58 map_["A256GCM"] =
59 &BindAlgFactoryWithKeyLen<CreateAesGcmKeyGenAlgorithm, 256>;
60 map_["A128CBC"] =
61 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 128>;
62 map_["A256CBC"] =
63 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 256>;
64 map_["A384CBC"] =
65 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 384>;
66 map_["A512CBC"] =
67 &BindAlgFactoryWithKeyLen<CreateAesCbcKeyGenAlgorithm, 512>;
68 }
69 WebKit::WebCryptoAlgorithm CreateAlgorithmFromName(
70 const std::string& alg_id) {
71 const JwkAlgFactoryMap::const_iterator pos = map_.find(alg_id);
72 if (pos == map_.end())
73 return WebKit::WebCryptoAlgorithm::createNull();
74 return pos->second();
75 }
76
77 private:
78 JwkAlgFactoryMap map_;
79 };
80 base::LazyInstance<JwkAlgorithmFactoryMap> jwk_alg_factory =
81 LAZY_INSTANCE_INITIALIZER;
82
83 // TODO(padolph): Verify this logic is sufficient to judge algorithm
84 // "consistency" for JWK import, and for all supported algorithms.
85 bool WebCryptoAlgorithmsConsistent(const WebKit::WebCryptoAlgorithm& lhs,
86 const WebKit::WebCryptoAlgorithm& rhs) {
87 if (lhs.id() == rhs.id()) {
88 if (lhs.id() == WebKit::WebCryptoAlgorithmIdHmac ||
89 lhs.id() == WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
90 lhs.id() == WebKit::WebCryptoAlgorithmIdRsaOaep) {
91 if (WebCryptoAlgorithmsConsistent(GetInnerHashAlgorithm(lhs),
92 GetInnerHashAlgorithm(rhs))) {
93 return true;
94 }
95 return false;
96 }
97 return true;
98 }
99 return false;
100 }
101
102 } // namespace
103
15 WebCryptoImpl::WebCryptoImpl() { 104 WebCryptoImpl::WebCryptoImpl() {
16 Init(); 105 Init();
17 } 106 }
18 107
19 // static 108 WebCryptoImpl::~WebCryptoImpl() {}
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 109
37 void WebCryptoImpl::encrypt( 110 void WebCryptoImpl::encrypt(
38 const WebKit::WebCryptoAlgorithm& algorithm, 111 const WebKit::WebCryptoAlgorithm& algorithm,
39 const WebKit::WebCryptoKey& key, 112 const WebKit::WebCryptoKey& key,
40 const unsigned char* data, 113 const unsigned char* data,
41 unsigned data_size, 114 unsigned data_size,
42 WebKit::WebCryptoResult result) { 115 WebKit::WebCryptoResult result) {
43 WebKit::WebArrayBuffer buffer; 116 WebKit::WebArrayBuffer buffer;
44 if (!EncryptInternal(algorithm, key, data, data_size, &buffer)) { 117 if (!EncryptInternal(algorithm, key, data, data_size, &buffer)) {
45 result.completeWithError(); 118 result.completeWithError();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 WebKit::WebCryptoKeyFormat format, 169 WebKit::WebCryptoKeyFormat format,
97 const unsigned char* key_data, 170 const unsigned char* key_data,
98 unsigned key_data_size, 171 unsigned key_data_size,
99 const WebKit::WebCryptoAlgorithm& algorithm, 172 const WebKit::WebCryptoAlgorithm& algorithm,
100 bool extractable, 173 bool extractable,
101 WebKit::WebCryptoKeyUsageMask usage_mask, 174 WebKit::WebCryptoKeyUsageMask usage_mask,
102 WebKit::WebCryptoResult result) { 175 WebKit::WebCryptoResult result) {
103 WebKit::WebCryptoKeyType type; 176 WebKit::WebCryptoKeyType type;
104 scoped_ptr<WebKit::WebCryptoKeyHandle> handle; 177 scoped_ptr<WebKit::WebCryptoKeyHandle> handle;
105 178
106 if (!ImportKeyInternal(format, 179 if (format == WebKit::WebCryptoKeyFormatJwk) {
107 key_data, 180 if (!ImportKeyJwk(key_data,
108 key_data_size, 181 key_data_size,
109 algorithm, 182 extractable,
110 usage_mask, 183 algorithm,
111 &handle, 184 usage_mask,
112 &type)) { 185 &handle,
113 result.completeWithError(); 186 &type)) {
114 return; 187 result.completeWithError();
188 }
189 } else {
190 if (!ImportKeyInternal(format,
191 key_data,
192 key_data_size,
193 algorithm,
194 usage_mask,
195 &handle,
196 &type)) {
197 result.completeWithError();
198 }
115 } 199 }
116 200
117 WebKit::WebCryptoKey key( 201 WebKit::WebCryptoKey key(WebKit::WebCryptoKey::create(
118 WebKit::WebCryptoKey::create( 202 handle.release(), type, extractable, algorithm, usage_mask));
119 handle.release(), type, extractable, algorithm, usage_mask));
120 203
121 result.completeWithKey(key); 204 result.completeWithKey(key);
122 } 205 }
123 206
124 void WebCryptoImpl::sign( 207 void WebCryptoImpl::sign(
125 const WebKit::WebCryptoAlgorithm& algorithm, 208 const WebKit::WebCryptoAlgorithm& algorithm,
126 const WebKit::WebCryptoKey& key, 209 const WebKit::WebCryptoKey& key,
127 const unsigned char* data, 210 const unsigned char* data,
128 unsigned data_size, 211 unsigned data_size,
129 WebKit::WebCryptoResult result) { 212 WebKit::WebCryptoResult result) {
(...skipping 20 matching lines...) Expand all
150 signature_size, 233 signature_size,
151 data, 234 data,
152 data_size, 235 data_size,
153 &signature_match)) { 236 &signature_match)) {
154 result.completeWithError(); 237 result.completeWithError();
155 } else { 238 } else {
156 result.completeWithBoolean(signature_match); 239 result.completeWithBoolean(signature_match);
157 } 240 }
158 } 241 }
159 242
243 bool WebCryptoImpl::ImportKeyJwk(
244 const unsigned char* key_data,
245 unsigned key_data_size,
246 bool extractable,
247 const WebKit::WebCryptoAlgorithm& algorithm,
248 WebKit::WebCryptoKeyUsageMask usage_mask,
249 scoped_ptr<WebKit::WebCryptoKeyHandle>* handle,
250 WebKit::WebCryptoKeyType* type) {
251
252 // JSON Web Key Format (JWK)
253 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16
254 // TODO(padolph): Not all possible values are handled by this code right now
255 //
256 // A JWK is a simple JSON dictionary with the following entries
257 // - "kty" (Key Type) Parameter, REQUIRED
258 // - <kty-specific parameters, see below>, REQUIRED
259 // - "use" (Key Use) Parameter, OPTIONAL
260 // - "alg" (Algorithm) Parameter, OPTIONAL
261 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE]
262 // (all other entries are ignored)
263 //
264 // Input key_data contains the JWK. To build a Web Crypto Key, the JWK values
265 // are parsed out and used as follows:
266 // Web Crypto Key type <-- (deduced)
267 // Web Crypto Key extractable <-- extractable
268 // Web Crypto Key algorithm <-- alg
269 // Web Crypto Key keyUsage <-- usage
270 // Web Crypto Key keying material <-- kty-specific parameters
271 //
272 // Values for each entry are case-sensitive and defined in
273 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16.
274 // Note that not all values specified by JOSE are handled by this code. Only
275 // handled values are listed.
276 // - kty (Key Type)
277 // +-------+--------------------------------------------------------------+
278 // | "RSA" | RSA [RFC3447] |
279 // | "oct" | Octet sequence (used to represent symmetric keys) |
280 // +-------+--------------------------------------------------------------+
281 // - use (Key Use)
282 // +-------+--------------------------------------------------------------+
283 // | "enc" | encrypt and decrypt operations |
284 // | "sig" | sign and verify (MAC) operations |
285 // | "wrap"| key wrap and unwrap [not yet part of JOSE] |
286 // +-------+--------------------------------------------------------------+
287 // - extractable (Key Exportability)
288 // +-------+--------------------------------------------------------------+
289 // | true | Key may be exported from the trusted environment |
290 // | false | Key cannot exit the trusted environment |
291 // +-------+--------------------------------------------------------------+
292 // - alg (Algorithm)
293 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16
294 // +--------------+-------------------------------------------------------+
295 // | Digital Signature or MAC Algorithm |
296 // +--------------+-------------------------------------------------------+
297 // | "HS256" | HMAC using SHA-256 hash algorithm |
298 // | "HS384" | HMAC using SHA-384 hash algorithm |
299 // | "HS512" | HMAC using SHA-512 hash algorithm |
300 // | "RS256" | RSASSA using SHA-256 hash algorithm |
301 // | "RS384" | RSASSA using SHA-384 hash algorithm |
302 // | "RS512" | RSASSA using SHA-512 hash algorithm |
303 // +--------------+-------------------------------------------------------|
304 // | Key Management Algorithm |
305 // +--------------+-------------------------------------------------------+
306 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] |
307 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding |
308 // | | (OAEP) [RFC3447], with the default parameters |
309 // | | specified by RFC3447 in Section A.2.1 |
310 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm |
311 // | | [RFC3394] using 128 bit keys |
312 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
313 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
314 // | | 128 bit keys |
315 // | "A256GCM" | AES GCM using 256 bit keys |
316 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
317 // | | padding [NIST.800-38A] [not yet part of JOSE] |
318 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] |
319 // | "A384CBC" | AES CBC using 384 bit keys [not yet part of JOSE] |
320 // | "A512CBC" | AES CBC using 512 bit keys [not yet part of JOSE] |
321 // +--------------+-------------------------------------------------------+
322 //
323 // kty-specific parameters
324 // The value of kty determines the type and content of the keying material
325 // carried in the JWK to be imported. Currently only two possibilities are
326 // supported: a raw key or an RSA public key. RSA private keys are not
327 // supported because typical applications seldom need to import a private key,
328 // and the large number of JWK parameters required to describe one.
329 // - kty == "oct" (symmetric or other raw key)
330 // +-------+--------------------------------------------------------------+
331 // | "k" | Contains the value of the symmetric (or other single-valued) |
332 // | | key. It is represented as the base64url encoding of the |
333 // | | octet sequence containing the key value. |
334 // +-------+--------------------------------------------------------------+
335 // - kty == "RSA" (RSA public key)
336 // +-------+--------------------------------------------------------------+
337 // | "n" | Contains the modulus value for the RSA public key. It is |
338 // | | represented as the base64url encoding of the value's |
339 // | | unsigned big endian representation as an octet sequence. |
340 // +-------+--------------------------------------------------------------+
341 // | "e" | Contains the exponent value for the RSA public key. It is |
342 // | | represented as the base64url encoding of the value's |
343 // | | unsigned big endian representation as an octet sequence. |
344 // +-------+--------------------------------------------------------------+
345 //
346 // Conflict resolution
347 // The type, algorithm, extractable, and usage_mask input parameters may be
348 // different from similar values inside the JWK. The Web Crypto spec says that
349 // if a JWK value is present, but is inconsistent with the input value, it is
350 // an error and the operation must fail.
351
352 // Initial argument sanity check.
353 if (!key_data || !key_data_size || algorithm.isNull() || !handle || !type)
eroman 2013/10/29 03:03:46 My interpretation is that it is legitimate for the
padolph 2013/10/29 22:57:01 I put in logic to handle input and JWK algorithms
354 return false;
355
356 // Parse the incoming JWK JSON.
357 base::StringPiece json_string(reinterpret_cast<const char*>(key_data),
358 key_data_size);
359 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
360 // Note, bare pointer dict_value is ok since it points into scoped value.
361 base::DictionaryValue* dict_value = NULL;
362 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
363 return false;
364
365 // JWK "kty". Exit early if this required JWK parameter is missing.
366 std::string jwk_kty_value;
367 if (!dict_value->GetString("kty", &jwk_kty_value))
368 return false;
369
370 // JWK "extractable" --> extractable parameter
371 bool jwk_extractable_value;
372 if (dict_value->GetBoolean("extractable", &jwk_extractable_value)) {
373 if (jwk_extractable_value != extractable)
374 return false;
375 }
376
377 // JWK "alg" --> algorithm parameter
378 std::string jwk_alg_value;
379 if (dict_value->GetString("alg", &jwk_alg_value)) {
380 const WebKit::WebCryptoAlgorithm webcrypto_algorithm_from_jwk =
381 jwk_alg_factory.Get().CreateAlgorithmFromName(jwk_alg_value);
382 if (webcrypto_algorithm_from_jwk.isNull())
383 return false;
384 if (!WebCryptoAlgorithmsConsistent(webcrypto_algorithm_from_jwk, algorithm))
385 return false;
386 }
387
388 // JWK "use" --> usage_mask parameter
389 std::string jwk_use_value;
390 if (dict_value->GetString("use", &jwk_use_value)) {
391 unsigned jwk_usage_mask;
392 if (jwk_use_value == "enc") {
393 jwk_usage_mask =
394 WebKit::WebCryptoKeyUsageEncrypt | WebKit::WebCryptoKeyUsageDecrypt;
395 } else if (jwk_use_value == "sig") {
396 jwk_usage_mask =
397 WebKit::WebCryptoKeyUsageSign | WebKit::WebCryptoKeyUsageVerify;
398 } else if (jwk_use_value == "wrap") {
399 jwk_usage_mask =
400 WebKit::WebCryptoKeyUsageWrapKey | WebKit::WebCryptoKeyUsageUnwrapKey;
401 } else {
402 return false;
403 }
404 if ((jwk_usage_mask & usage_mask) != usage_mask) {
405 // A usage_mask must be a subset of jwk_usage_mask.
406 return false;
407 }
408 }
409
410 // JWK keying material --> ImportKeyInternal()
411 if (jwk_kty_value == "oct") {
412 if (*type != WebKit::WebCryptoKeyTypeSecret)
413 return false;
414 std::string jwk_k_value_url64;
415 if (!dict_value->GetString("k", &jwk_k_value_url64))
416 return false;
417 std::string jwk_k_value;
418 if (!Base64DecodeUrlSafe(jwk_k_value_url64, &jwk_k_value) ||
419 !jwk_k_value.size()) {
420 return false;
421 }
422 if (!ImportKeyInternal(WebKit::WebCryptoKeyFormatRaw,
423 reinterpret_cast<const uint8*>(jwk_k_value.data()),
424 jwk_k_value.size(),
425 algorithm,
426 usage_mask,
427 handle,
428 type)) {
429 return false;
430 }
431 } else if (jwk_kty_value == "RSA") {
432 // Only an RSA public key is currently handled.
433 if (*type != WebKit::WebCryptoKeyTypePublic)
434 return false;
435 // TODO(padolph): JWK import RSA public key
436 return false;
437 } else {
438 return false;
439 }
440
441 return true;
442 }
443
160 } // namespace content 444 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698