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

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

Issue 184043021: [webcrypto] JWK: Updated import(ext, key_ops) and added export of symmetric keys (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@wcAesKw_nss1
Patch Set: Created 6 years, 9 months 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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 <algorithm> 5 #include <algorithm>
6 #include <functional> 6 #include <functional>
7 #include <map> 7 #include <map>
8 #include "base/base64.h"
8 #include "base/json/json_reader.h" 9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
9 #include "base/lazy_instance.h" 11 #include "base/lazy_instance.h"
10 #include "base/logging.h" 12 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h" 13 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_piece.h" 14 #include "base/strings/string_piece.h"
13 #include "base/values.h" 15 #include "base/strings/stringprintf.h"
14 #include "content/renderer/webcrypto/crypto_data.h" 16 #include "content/renderer/webcrypto/crypto_data.h"
15 #include "content/renderer/webcrypto/platform_crypto.h" 17 #include "content/renderer/webcrypto/platform_crypto.h"
16 #include "content/renderer/webcrypto/shared_crypto.h" 18 #include "content/renderer/webcrypto/shared_crypto.h"
17 #include "content/renderer/webcrypto/webcrypto_util.h" 19 #include "content/renderer/webcrypto/webcrypto_util.h"
20 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
18 21
19 namespace content { 22 namespace content {
20 23
21 namespace webcrypto { 24 namespace webcrypto {
22 25
23 namespace { 26 namespace {
24 27
25 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); 28 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
26 29
30 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
31 // TODO(padolph): Add 'deriveBits' once supported by Blink.
32 const blink::WebCryptoKeyUsageMask kJwkEncUsage =
33 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
34 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
35 blink::WebCryptoKeyUsageDeriveKey;
36 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
37 const blink::WebCryptoKeyUsageMask kJwkSigUsage =
38 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
39
40 // TODO(padolph): Should these IsAlgorithm* functions be moved to
41 // webcrypto_util?
42 bool IsAlgorithmAsymmetric(const blink::WebCryptoKeyAlgorithm& algorithm) {
43 // TODO(padolph): include all other asymmetric algorithms once they are
44 // defined, e.g. EC and DH.
45 return algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
46 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
47 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep;
48 }
49
50 bool IsAlgorithmAes(const blink::WebCryptoKeyAlgorithm& algorithm) {
51 // TODO(padolph): include other AES symmetric algorithms once they are
eroman 2014/03/04 02:55:14 How about: return algorithm.paramsType() == blin
padolph 2014/03/05 03:08:51 Done.
52 // defined.
53 const blink::WebCryptoAlgorithmId id = algorithm.id();
54 return id == blink::WebCryptoAlgorithmIdAesCbc ||
55 id == blink::WebCryptoAlgorithmIdAesCtr ||
56 id == blink::WebCryptoAlgorithmIdAesGcm ||
57 id == blink::WebCryptoAlgorithmIdAesKw;
58 }
59
60 bool IsAlgorithmSymmetric(const blink::WebCryptoKeyAlgorithm& algorithm) {
61 // TODO(padolph): include all other symmetric algorithms once they are
62 // defined.
63 return IsAlgorithmAes(algorithm) ||
64 algorithm.id() == blink::WebCryptoAlgorithmIdHmac;
65 }
66
27 class JwkAlgorithmInfo { 67 class JwkAlgorithmInfo {
28 public: 68 public:
29 JwkAlgorithmInfo() 69 JwkAlgorithmInfo()
30 : creation_func_(NULL), 70 : creation_func_(NULL),
31 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} 71 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
32 72
33 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) 73 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
34 : creation_func_(algorithm_creation_func), 74 : creation_func_(algorithm_creation_func),
35 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} 75 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
36 76
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 blink::WebCryptoAlgorithmIdSha384>); 127 blink::WebCryptoAlgorithmIdSha384>);
88 alg_to_info_["RS512"] = 128 alg_to_info_["RS512"] =
89 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, 129 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
90 blink::WebCryptoAlgorithmIdSha512>); 130 blink::WebCryptoAlgorithmIdSha512>);
91 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo( 131 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo(
92 &BindAlgorithmId<CreateAlgorithm, 132 &BindAlgorithmId<CreateAlgorithm,
93 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>); 133 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>);
94 alg_to_info_["RSA-OAEP"] = 134 alg_to_info_["RSA-OAEP"] =
95 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, 135 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
96 blink::WebCryptoAlgorithmIdSha1>); 136 blink::WebCryptoAlgorithmIdSha1>);
97 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet 137 alg_to_info_["A128KW"] = JwkAlgorithmInfo(
98 alg_to_info_["A128KW"] = 138 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>);
eroman 2014/03/04 02:55:14 Include the key length as the final parameter, lik
padolph 2014/03/05 03:08:51 Done.
99 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 128); 139 alg_to_info_["A192KW"] = JwkAlgorithmInfo(
100 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet 140 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>);
101 alg_to_info_["A256KW"] = 141 alg_to_info_["A256KW"] = JwkAlgorithmInfo(
102 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 256); 142 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>);
103 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( 143 alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
104 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, 144 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
105 128); 145 128);
146 alg_to_info_["A192GCM"] = JwkAlgorithmInfo(
147 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
148 192);
106 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( 149 alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
107 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, 150 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
108 256); 151 256);
109 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( 152 alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
110 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, 153 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
111 128); 154 128);
112 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( 155 alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
113 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, 156 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
114 192); 157 192);
115 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( 158 alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 if (!dict->Get(path, &value)) 233 if (!dict->Get(path, &value))
191 return Status::Success(); 234 return Status::Success();
192 235
193 if (!value->GetAsString(result)) 236 if (!value->GetAsString(result))
194 return Status::ErrorJwkPropertyWrongType(path, "string"); 237 return Status::ErrorJwkPropertyWrongType(path, "string");
195 238
196 *property_exists = true; 239 *property_exists = true;
197 return Status::Success(); 240 return Status::Success();
198 } 241 }
199 242
243 // Extracts the optional array property with key |path| from |dict| and saves
244 // the result to |*result| if it was found. If the property exists and is not an
245 // array, returns an error. Otherwise returns success, and sets
246 // |*property_exists| if it was found.
eroman 2014/03/04 02:55:14 Can you mention that the pointer *result is owned
padolph 2014/03/05 03:08:51 Done.
247 Status GetOptionalJwkList(base::DictionaryValue* dict,
248 const std::string& path,
249 base::ListValue** result,
250 bool* property_exists) {
251 *property_exists = false;
252 base::Value* value = NULL;
253 if (!dict->Get(path, &value))
254 return Status::Success();
255
256 if (!value->GetAsList(result))
257 return Status::ErrorJwkPropertyWrongType(path, "list");
258
259 *property_exists = true;
260 return Status::Success();
261 }
262
200 // Extracts the required string property with key |path| from |dict| and saves 263 // Extracts the required string property with key |path| from |dict| and saves
201 // the base64-decoded bytes to |*result|. If the property does not exist or is 264 // the base64url-decoded bytes to |*result|. If the property does not exist or
202 // not a string, or could not be base64-decoded, returns an error. 265 // is not a string, or could not be base64url-decoded, returns an error.
203 Status GetJwkBytes(base::DictionaryValue* dict, 266 Status GetJwkBytes(base::DictionaryValue* dict,
204 const std::string& path, 267 const std::string& path,
205 std::string* result) { 268 std::string* result) {
206 std::string base64_string; 269 std::string base64_string;
207 Status status = GetJwkString(dict, path, &base64_string); 270 Status status = GetJwkString(dict, path, &base64_string);
208 if (status.IsError()) 271 if (status.IsError())
209 return status; 272 return status;
210 273
211 if (!Base64DecodeUrlSafe(base64_string, result)) 274 if (!Base64DecodeUrlSafe(base64_string, result))
212 return Status::ErrorJwkBase64Decode(path); 275 return Status::ErrorJwkBase64Decode(path);
(...skipping 14 matching lines...) Expand all
227 if (!dict->Get(path, &value)) 290 if (!dict->Get(path, &value))
228 return Status::Success(); 291 return Status::Success();
229 292
230 if (!value->GetAsBoolean(result)) 293 if (!value->GetAsBoolean(result))
231 return Status::ErrorJwkPropertyWrongType(path, "boolean"); 294 return Status::ErrorJwkPropertyWrongType(path, "boolean");
232 295
233 *property_exists = true; 296 *property_exists = true;
234 return Status::Success(); 297 return Status::Success();
235 } 298 }
236 299
300 // Returns true if the set bits in b are a strict subset of the set bits in a.
eroman 2014/03/04 02:55:14 I don't quite understand the comment "strict subse
padolph 2014/03/05 03:08:51 Done.
301 bool IsBitwiseSubset(unsigned int a, unsigned int b) { return (a & b) == b; }
302
237 } // namespace 303 } // namespace
238 304
239 Status ImportKeyJwk(const CryptoData& key_data, 305 Status ImportKeyJwk(const CryptoData& key_data,
240 const blink::WebCryptoAlgorithm& algorithm_or_null, 306 const blink::WebCryptoAlgorithm& algorithm_or_null,
241 bool extractable, 307 bool extractable,
242 blink::WebCryptoKeyUsageMask usage_mask, 308 blink::WebCryptoKeyUsageMask usage_mask,
243 blink::WebCryptoKey* key) { 309 blink::WebCryptoKey* key) {
244 310
245 // The goal of this method is to extract key material and meta data from the 311 // The goal of this method is to extract key material and meta data from the
246 // incoming JWK, combine them with the input parameters, and ultimately import 312 // incoming JWK, combine them with the input parameters, and ultimately import
247 // a Web Crypto Key. 313 // a Web Crypto Key.
248 // 314 //
249 // JSON Web Key Format (JWK) 315 // JSON Web Key Format (JWK)
250 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16 316 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
251 // TODO(padolph): Not all possible values are handled by this code right now
252 // 317 //
253 // A JWK is a simple JSON dictionary with the following entries 318 // A JWK is a simple JSON dictionary with the following entries
254 // - "kty" (Key Type) Parameter, REQUIRED 319 // - "kty" (Key Type) Parameter, REQUIRED
255 // - <kty-specific parameters, see below>, REQUIRED 320 // - <kty-specific parameters, see below>, REQUIRED
256 // - "use" (Key Use) Parameter, OPTIONAL 321 // - "use" (Key Use) Parameter, OPTIONAL
322 // - "key_ops" (Key Operations) Parameter, OPTIONAL
257 // - "alg" (Algorithm) Parameter, OPTIONAL 323 // - "alg" (Algorithm) Parameter, OPTIONAL
258 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE, 324 // - "ext" (Key Exportability), OPTIONAL
259 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796]
260 // (all other entries are ignored) 325 // (all other entries are ignored)
261 // 326 //
262 // OPTIONAL here means that this code does not require the entry to be present 327 // OPTIONAL here means that this code does not require the entry to be present
eroman 2014/03/04 02:55:14 Perhaps as a separate changelist, I suggest moving
padolph 2014/03/05 03:08:51 Agreed. Added a TODO.
263 // in the incoming JWK, because the method input parameters contain similar 328 // in the incoming JWK, because the method input parameters contain similar
264 // information. If the optional JWK entry is present, it will be validated 329 // information. If the optional JWK entry is present, it will be validated
265 // against the corresponding input parameter for consistency and combined with 330 // against the corresponding input parameter for consistency and combined with
266 // it according to rules defined below. A special case is that the method 331 // it according to rules defined below. A special case is that the method
267 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg' 332 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg'
268 // value (if present) is used as a fallback. 333 // value (if present) is used as a fallback.
269 // 334 //
270 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK 335 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
271 // values are parsed out and combined with the method input parameters to 336 // values are parsed out and combined with the method input parameters to
272 // build a Web Crypto Key: 337 // build a Web Crypto Key:
273 // Web Crypto Key type <-- (deduced) 338 // Web Crypto Key type <-- (deduced)
274 // Web Crypto Key extractable <-- JWK extractable + input extractable 339 // Web Crypto Key extractable <-- JWK ext + input extractable
275 // Web Crypto Key algorithm <-- JWK alg + input algorithm 340 // Web Crypto Key algorithm <-- JWK alg + input algorithm
276 // Web Crypto Key keyUsage <-- JWK use + input usage_mask 341 // Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask
277 // Web Crypto Key keying material <-- kty-specific parameters 342 // Web Crypto Key keying material <-- kty-specific parameters
278 // 343 //
279 // Values for each JWK entry are case-sensitive and defined in 344 // Values for each JWK entry are case-sensitive and defined in
280 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16. 345 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18.
281 // Note that not all values specified by JOSE are handled by this code. Only 346 // Note that not all values specified by JOSE are handled by this code. Only
282 // handled values are listed. 347 // handled values are listed.
283 // - kty (Key Type) 348 // - kty (Key Type)
284 // +-------+--------------------------------------------------------------+ 349 // +-------+--------------------------------------------------------------+
285 // | "RSA" | RSA [RFC3447] | 350 // | "RSA" | RSA [RFC3447] |
286 // | "oct" | Octet sequence (used to represent symmetric keys) | 351 // | "oct" | Octet sequence (used to represent symmetric keys) |
287 // +-------+--------------------------------------------------------------+ 352 // +-------+--------------------------------------------------------------+
353 //
354 // - key_ops (Key Use Details)
355 // The key_ops field is an array that contains one or more strings from
356 // the table below, and describes the operations for which this key may be
357 // used.
358 // +-------+--------------------------------------------------------------+
359 // | "encrypt" | encrypt operations |
360 // | "decrypt" | decrypt operations |
361 // | "sign" | sign (MAC) operations |
362 // | "verify" | verify (MAC) operations |
363 // | "wrapKey" | key wrap |
364 // | "unwrapKey" | key unwrap |
365 // | "deriveKey" | key derivation |
366 // | "deriveBits" | key derivation TODO(padolph): not currently supported |
367 // +-------+--------------------------------------------------------------+
368 //
288 // - use (Key Use) 369 // - use (Key Use)
370 // The use field contains a single entry from the table below.
289 // +-------+--------------------------------------------------------------+ 371 // +-------+--------------------------------------------------------------+
290 // | "enc" | encrypt and decrypt operations | 372 // | "sig" | equivalent to key_ops of [sign, verify] |
291 // | "sig" | sign and verify (MAC) operations | 373 // | "enc" | equivalent to key_ops of [encrypt, decrypt, wrapKey, |
292 // | "wrap"| key wrap and unwrap [not yet part of JOSE] | 374 // | | unwrapKey, deriveKey, deriveBits] |
293 // +-------+--------------------------------------------------------------+ 375 // +-------+--------------------------------------------------------------+
294 // - extractable (Key Exportability) 376 //
377 // NOTE: If both "use" and "key_ops" JWK members are present, the usages
378 // specified by them MUST be consistent. In particular, the "use" value
379 // "sig" corresponds to "sign" and/or "verify". The "use" value "enc"
380 // corresponds to all other values defined above. If "key_ops" values
381 // corresponding to both "sig" and "enc" "use" values are present, the "use"
382 // member SHOULD NOT be present, and if present, its value MUST NOT be
383 // either "sig" or "enc".
384 //
385 // - ext (Key Exportability)
295 // +-------+--------------------------------------------------------------+ 386 // +-------+--------------------------------------------------------------+
296 // | true | Key may be exported from the trusted environment | 387 // | true | Key may be exported from the trusted environment |
297 // | false | Key cannot exit the trusted environment | 388 // | false | Key cannot exit the trusted environment |
298 // +-------+--------------------------------------------------------------+ 389 // +-------+--------------------------------------------------------------+
390 //
299 // - alg (Algorithm) 391 // - alg (Algorithm)
300 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16 392 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18
301 // +--------------+-------------------------------------------------------+ 393 // +--------------+-------------------------------------------------------+
302 // | Digital Signature or MAC Algorithm | 394 // | Digital Signature or MAC Algorithm |
303 // +--------------+-------------------------------------------------------+ 395 // +--------------+-------------------------------------------------------+
304 // | "HS256" | HMAC using SHA-256 hash algorithm | 396 // | "HS256" | HMAC using SHA-256 hash algorithm |
305 // | "HS384" | HMAC using SHA-384 hash algorithm | 397 // | "HS384" | HMAC using SHA-384 hash algorithm |
306 // | "HS512" | HMAC using SHA-512 hash algorithm | 398 // | "HS512" | HMAC using SHA-512 hash algorithm |
307 // | "RS256" | RSASSA using SHA-256 hash algorithm | 399 // | "RS256" | RSASSA using SHA-256 hash algorithm |
308 // | "RS384" | RSASSA using SHA-384 hash algorithm | 400 // | "RS384" | RSASSA using SHA-384 hash algorithm |
309 // | "RS512" | RSASSA using SHA-512 hash algorithm | 401 // | "RS512" | RSASSA using SHA-512 hash algorithm |
310 // +--------------+-------------------------------------------------------| 402 // +--------------+-------------------------------------------------------|
311 // | Key Management Algorithm | 403 // | Key Management Algorithm |
312 // +--------------+-------------------------------------------------------+ 404 // +--------------+-------------------------------------------------------+
313 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] | 405 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] |
314 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | 406 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding |
315 // | | (OAEP) [RFC3447], with the default parameters | 407 // | | (OAEP) [RFC3447], with the default parameters |
316 // | | specified by RFC3447 in Section A.2.1 | 408 // | | specified by RFC3447 in Section A.2.1 |
317 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | 409 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm |
318 // | | [RFC3394] using 128 bit keys | 410 // | | [RFC3394] using 128 bit keys |
411 // | "A192KW" | AES Key Wrap Algorithm using 192 bit keys |
319 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | 412 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
320 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | 413 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
321 // | | 128 bit keys | 414 // | | 128 bit keys |
415 // | "A192GCM" | AES GCM using 192 bit keys |
322 // | "A256GCM" | AES GCM using 256 bit keys | 416 // | "A256GCM" | AES GCM using 256 bit keys |
323 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | 417 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
324 // | | padding [NIST.800-38A] [not yet part of JOSE, see | 418 // | | padding [NIST.800-38A] [not yet part of JOSE, see |
325 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 | 419 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 |
326 // | "A192CBC" | AES CBC using 192 bit keys [not yet part of JOSE] | 420 // | "A192CBC" | AES CBC using 192 bit keys |
eroman 2014/03/04 02:55:14 thanks for updating these comments!
327 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] | 421 // | "A256CBC" | AES CBC using 256 bit keys |
328 // +--------------+-------------------------------------------------------+ 422 // +--------------+-------------------------------------------------------+
329 // 423 //
330 // kty-specific parameters 424 // kty-specific parameters
331 // The value of kty determines the type and content of the keying material 425 // The value of kty determines the type and content of the keying material
332 // carried in the JWK to be imported. Currently only two possibilities are 426 // carried in the JWK to be imported. Currently only two possibilities are
333 // supported: a raw key or an RSA public key. RSA private keys are not 427 // supported: a raw key or an RSA public key. RSA private keys are not
334 // supported because typical applications seldom need to import a private key, 428 // supported because typical applications seldom need to import a private key,
335 // and the large number of JWK parameters required to describe one. 429 // and the large number of JWK parameters required to describe one.
336 // - kty == "oct" (symmetric or other raw key) 430 // - kty == "oct" (symmetric or other raw key)
337 // +-------+--------------------------------------------------------------+ 431 // +-------+--------------------------------------------------------------+
(...skipping 21 matching lines...) Expand all
359 // 453 //
360 // algorithm 454 // algorithm
361 // If an algorithm is provided by both the input parameter and the JWK, 455 // If an algorithm is provided by both the input parameter and the JWK,
362 // consistency between the two is based only on algorithm ID's (including an 456 // consistency between the two is based only on algorithm ID's (including an
363 // inner hash algorithm if present). In this case if the consistency 457 // inner hash algorithm if present). In this case if the consistency
364 // check is passed, the input algorithm is used. If only one of either the 458 // check is passed, the input algorithm is used. If only one of either the
365 // input algorithm and JWK alg is provided, it is used as the final 459 // input algorithm and JWK alg is provided, it is used as the final
366 // algorithm. 460 // algorithm.
367 // 461 //
368 // extractable 462 // extractable
369 // If the JWK extractable is true but the input parameter is false, make the 463 // If the JWK ext field is true but the input parameter is false, make the
370 // Web Crypto Key non-extractable. Conversely, if the JWK extractable is 464 // Web Crypto Key non-extractable. Conversely, if the JWK ext field is
371 // false but the input parameter is true, it is an inconsistency. If both 465 // false but the input parameter is true, it is an inconsistency. If both
372 // are true or both are false, use that value. 466 // are true or both are false, use that value.
373 // 467 //
374 // usage_mask 468 // usage_mask
375 // The input usage_mask must be a strict subset of the interpreted JWK use 469 // The input usage_mask must be a strict subset of the interpreted JWK use
376 // value, else it is judged inconsistent. In all cases the input usage_mask 470 // value, else it is judged inconsistent. In all cases the input usage_mask
377 // is used as the final usage_mask. 471 // is used as the final usage_mask.
378 // 472 //
379 473
380 if (!key_data.byte_length()) 474 if (!key_data.byte_length())
381 return Status::ErrorImportEmptyKeyData(); 475 return Status::ErrorImportEmptyKeyData();
382 DCHECK(key); 476 DCHECK(key);
383 477
384 // Parse the incoming JWK JSON. 478 // Parse the incoming JWK JSON.
385 base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), 479 base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()),
386 key_data.byte_length()); 480 key_data.byte_length());
387 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); 481 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
388 // Note, bare pointer dict_value is ok since it points into scoped value. 482 // Note, bare pointer dict_value is ok since it points into scoped value.
389 base::DictionaryValue* dict_value = NULL; 483 base::DictionaryValue* dict_value = NULL;
390 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) 484 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
391 return Status::ErrorJwkNotDictionary(); 485 return Status::ErrorJwkNotDictionary();
392 486
393 // JWK "kty". Exit early if this required JWK parameter is missing. 487 // JWK "kty". Exit early if this required JWK parameter is missing.
394 std::string jwk_kty_value; 488 std::string jwk_kty_value;
395 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); 489 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value);
396 if (status.IsError()) 490 if (status.IsError())
397 return status; 491 return status;
398 492
399 // JWK "extractable" (optional) --> extractable parameter 493 // JWK "ext" (optional) --> extractable parameter
400 { 494 {
401 bool jwk_extractable_value = false; 495 bool jwk_ext_value = false;
402 bool has_jwk_extractable; 496 bool has_jwk_ext;
403 status = GetOptionalJwkBool(dict_value, 497 status =
404 "extractable", 498 GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext);
405 &jwk_extractable_value,
406 &has_jwk_extractable);
407 if (status.IsError()) 499 if (status.IsError())
408 return status; 500 return status;
409 if (has_jwk_extractable && !jwk_extractable_value && extractable) 501 if (has_jwk_ext && !jwk_ext_value && extractable)
410 return Status::ErrorJwkExtractableInconsistent(); 502 return Status::ErrorJwkExtInconsistent();
411 } 503 }
412 504
413 // JWK "alg" (optional) --> algorithm parameter 505 // JWK "alg" (optional) --> algorithm parameter
414 // Note: input algorithm is also optional, so we have six cases to handle. 506 // Note: input algorithm is also optional, so we have six cases to handle.
415 // 1. JWK alg present but unrecognized: error 507 // 1. JWK alg present but unrecognized: error
416 // 2. JWK alg valid AND input algorithm isNull: use JWK value 508 // 2. JWK alg valid AND input algorithm isNull: use JWK value
417 // 3. JWK alg valid AND input algorithm specified, but JWK value 509 // 3. JWK alg valid AND input algorithm specified, but JWK value
418 // inconsistent with input: error 510 // inconsistent with input: error
419 // 4. JWK alg valid AND input algorithm specified, both consistent: use 511 // 4. JWK alg valid AND input algorithm specified, both consistent: use
420 // input value (because it has potentially more details) 512 // input value (because it has potentially more details)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 algorithm = algorithm_or_null; // case 4 545 algorithm = algorithm_or_null; // case 4
454 } 546 }
455 } else { 547 } else {
456 // JWK alg missing 548 // JWK alg missing
457 if (algorithm_or_null.isNull()) 549 if (algorithm_or_null.isNull())
458 return Status::ErrorJwkAlgorithmMissing(); // case 5 550 return Status::ErrorJwkAlgorithmMissing(); // case 5
459 algorithm = algorithm_or_null; // case 6 551 algorithm = algorithm_or_null; // case 6
460 } 552 }
461 DCHECK(!algorithm.isNull()); 553 DCHECK(!algorithm.isNull());
462 554
555 // JWK "key_ops" (optional) --> usage_mask parameter
556 base::ListValue* jwk_key_ops_value = NULL;
557 bool has_jwk_key_ops;
558 status = GetOptionalJwkList(
559 dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops);
560 if (status.IsError())
561 return status;
562 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0;
563 if (has_jwk_key_ops) {
564 Status status =
eroman 2014/03/04 02:55:14 I prefer not shadowing the variable (can be tricky
padolph 2014/03/05 03:08:51 Done.
565 GetUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask);
566 if (status.IsError())
567 return status;
568 // The input usage_mask must be a strict subset of jwk_key_ops_mask.
569 if (!IsBitwiseSubset(jwk_key_ops_mask, usage_mask))
570 return Status::ErrorJwkKeyopsInconsistent();
571 }
572
463 // JWK "use" (optional) --> usage_mask parameter 573 // JWK "use" (optional) --> usage_mask parameter
464 std::string jwk_use_value; 574 std::string jwk_use_value;
465 bool has_jwk_use; 575 bool has_jwk_use;
466 status = 576 status =
467 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); 577 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use);
468 if (status.IsError()) 578 if (status.IsError())
469 return status; 579 return status;
580 blink::WebCryptoKeyUsageMask jwk_use_mask = 0;
470 if (has_jwk_use) { 581 if (has_jwk_use) {
471 blink::WebCryptoKeyUsageMask jwk_usage_mask = 0; 582 if (jwk_use_value == "enc")
472 if (jwk_use_value == "enc") { 583 jwk_use_mask = kJwkEncUsage;
473 jwk_usage_mask = 584 else if (jwk_use_value == "sig")
474 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; 585 jwk_use_mask = kJwkSigUsage;
475 } else if (jwk_use_value == "sig") { 586 else
476 jwk_usage_mask = 587 return Status::ErrorJwkUnrecognizedUse();
477 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; 588 // The input usage_mask must be a subset of jwk_use_mask.
478 } else if (jwk_use_value == "wrap") { 589 if (!IsBitwiseSubset(jwk_use_mask, usage_mask))
479 jwk_usage_mask = 590 return Status::ErrorJwkUseInconsistent();
480 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey; 591 }
481 } else { 592
482 return Status::ErrorJwkUnrecognizedUsage(); 593 // If both 'key_ops' and 'use' are present, ensure they are consistent.
483 } 594 if (has_jwk_key_ops && has_jwk_use) {
484 if ((jwk_usage_mask & usage_mask) != usage_mask) { 595 if ((jwk_use_mask == kJwkEncUsage) &&
eroman 2014/03/04 02:55:14 Can you express this in a more future proof way? M
padolph 2014/03/05 03:08:51 Yes - that works perfectly. Thanks.
485 // A usage_mask must be a subset of jwk_usage_mask. 596 !IsBitwiseSubset(kJwkEncUsage, jwk_key_ops_mask))
486 return Status::ErrorJwkUsageInconsistent(); 597 return Status::ErrorJwkUseAndKeyopsInconsistent();
487 } 598 if ((jwk_use_mask == kJwkSigUsage) &&
599 !IsBitwiseSubset(kJwkSigUsage, jwk_key_ops_mask))
600 return Status::ErrorJwkUseAndKeyopsInconsistent();
488 } 601 }
489 602
490 // JWK keying material --> ImportKeyInternal() 603 // JWK keying material --> ImportKeyInternal()
491 if (jwk_kty_value == "oct") { 604 if (jwk_kty_value == "oct") {
492 605
493 std::string jwk_k_value; 606 std::string jwk_k_value;
494 status = GetJwkBytes(dict_value, "k", &jwk_k_value); 607 status = GetJwkBytes(dict_value, "k", &jwk_k_value);
495 if (status.IsError()) 608 if (status.IsError())
496 return status; 609 return status;
497 610
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 CryptoData(jwk_e_value), 655 CryptoData(jwk_e_value),
543 key); 656 key);
544 657
545 } else { 658 } else {
546 return Status::ErrorJwkUnrecognizedKty(); 659 return Status::ErrorJwkUnrecognizedKty();
547 } 660 }
548 661
549 return Status::Success(); 662 return Status::Success();
550 } 663 }
551 664
665 Status ExportKeyJwk(const blink::WebCryptoKey& key,
666 blink::WebArrayBuffer* buffer) {
667
668 // JWK has the following JSON structure
eroman 2014/03/04 02:55:14 This seems like a duplication of the bigger commen
padolph 2014/03/05 03:08:51 Removed.
669 // {
670 // 'kty': key type, e.g. 'oct' or 'RSA'
671 // 'key_ops': an array of key usage values from ["sign", "verify",
672 // "encrypt", "decrypt", "wrapKey", "unwrapKey", "deriveKey"]
673 // 'ext': true or false
674 // 'alg': key algorithm, e.g. 'RSA1_5, 'A128CBC', etc.
675 // <kty-dependent parms>
676 // }
677 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
678
679 base::DictionaryValue jwk_dict;
680
681 // ---- 'kty' and kty-dependent parms
682 unsigned key_length_bytes = 0;
683 if (IsAlgorithmSymmetric(key.algorithm())) {
684 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type());
eroman 2014/03/04 02:55:14 In fact, would this not be a simpler way to implem
padolph 2014/03/05 03:08:51 Done.
685 jwk_dict.SetString("kty", "oct");
686 // For a symmetric key, the only extra field is 'k', containing the
687 // base64url encoding of the raw key.
688 blink::WebArrayBuffer raw_key;
689 Status status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key);
690 if (status.IsError())
691 return status;
692 DCHECK(!raw_key.isNull());
693 DCHECK(raw_key.data());
694 DCHECK(raw_key.byteLength());
695 key_length_bytes = raw_key.byteLength();
696 const base::StringPiece key_str(static_cast<const char*>(raw_key.data()),
697 key_length_bytes);
698 std::string key_b64url;
699 webcrypto::Base64EncodeUrlSafe(key_str, &key_b64url);
700 jwk_dict.SetString("k", key_b64url);
701 } else {
eroman 2014/03/04 02:55:14 Please split this function up into smaller functio
padolph 2014/03/05 03:08:51 Done.
702 // TODO(padolph): Handle asymmetric keys, at least the public key.
703 return Status::ErrorUnsupported();
704 }
705
706 // ---- 'key_ops'
707 base::ListValue* key_ops = new base::ListValue;
708 if (key.usages() & blink::WebCryptoKeyUsageEncrypt)
eroman 2014/03/04 02:55:14 This is the opposite of what we use for import rig
padolph 2014/03/05 03:08:51 Done, but I'm not sure I like the way it turned ou
709 key_ops->AppendString("encrypt");
710 if (key.usages() & blink::WebCryptoKeyUsageDecrypt)
711 key_ops->AppendString("decrypt");
712 if (key.usages() & blink::WebCryptoKeyUsageDeriveKey)
713 key_ops->AppendString("deriveKey");
714 if (key.usages() & blink::WebCryptoKeyUsageSign)
715 key_ops->AppendString("sign");
716 if (key.usages() & blink::WebCryptoKeyUsageUnwrapKey)
717 key_ops->AppendString("unwrapKey");
718 if (key.usages() & blink::WebCryptoKeyUsageVerify)
719 key_ops->AppendString("verify");
720 if (key.usages() & blink::WebCryptoKeyUsageWrapKey)
721 key_ops->AppendString("wrapKey");
722 jwk_dict.Set("key_ops", key_ops);
723
724 // ---- 'ext'
725 jwk_dict.SetBoolean("ext", key.extractable());
726
727 // ---- 'alg'
728 const char* aes_prefix = "";
729 if (IsAlgorithmAes(key.algorithm())) {
730 switch (key_length_bytes) {
731 case 16:
732 aes_prefix = "A128";
733 break;
734 case 24:
735 aes_prefix = "A192";
736 break;
737 case 32:
738 aes_prefix = "A256";
739 break;
740 default:
741 NOTREACHED(); // bad key length means algorithm was built improperly
742 return Status::ErrorUnexpected();
743 }
744 }
745 switch (key.algorithm().id()) {
eroman 2014/03/04 02:55:14 Is there a good way to re-use the table mapping fr
padolph 2014/03/05 03:08:51 Not as it stands now. First, reverse table lookup
746 case blink::WebCryptoAlgorithmIdAesCbc:
747 jwk_dict.SetString("alg", base::StringPrintf("%s%s", aes_prefix, "CBC"));
748 break;
749 case blink::WebCryptoAlgorithmIdAesCtr:
750 jwk_dict.SetString("alg", base::StringPrintf("%s%s", aes_prefix, "CTR"));
751 break;
752 case blink::WebCryptoAlgorithmIdAesGcm:
753 jwk_dict.SetString("alg", base::StringPrintf("%s%s", aes_prefix, "GCM"));
754 break;
755 case blink::WebCryptoAlgorithmIdAesKw:
756 jwk_dict.SetString("alg", base::StringPrintf("%s%s", aes_prefix, "KW"));
757 break;
758 case blink::WebCryptoAlgorithmIdHmac:
759 DCHECK(key.algorithm().hmacParams());
eroman 2014/03/04 02:55:14 Have you considered making this outter switch be o
padolph 2014/03/05 03:08:51 Done. Maybe a little more readable now, and does n
760 switch (key.algorithm().hmacParams()->hash().id()) {
761 case blink::WebCryptoAlgorithmIdSha256:
762 jwk_dict.SetString("alg", "HS256");
763 break;
764 case blink::WebCryptoAlgorithmIdSha384:
765 jwk_dict.SetString("alg", "HS384");
766 break;
767 case blink::WebCryptoAlgorithmIdSha512:
768 jwk_dict.SetString("alg", "HS512");
769 break;
770 default:
771 // JWA does not allow any other HMAC inner hashes.
772 return Status::ErrorJwkUnsupportedHmacHash();
eroman 2014/03/04 02:55:14 This is in disagreement with the latest revision o
padolph 2014/03/05 03:08:51 Done.
773 }
774 break;
775 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
776 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
777 case blink::WebCryptoAlgorithmIdRsaOaep:
778 // TODO(padolph): Handle RSA key export, at least the public key.
779 return Status::ErrorUnsupported();
780 default:
781 // TODO(padolph): Handle other key export.
782 return Status::ErrorUnsupported();
783 }
784
785 std::string json;
786 base::JSONWriter::Write(&jwk_dict, &json);
787 *buffer = webcrypto::CreateArrayBuffer(
788 reinterpret_cast<const uint8*>(json.data()), json.size());
789
790 return Status::Success();
791 }
792
552 } // namespace webcrypto 793 } // namespace webcrypto
553 794
554 } // namespace content 795 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/renderer/webcrypto/platform_crypto_nss.cc » ('j') | content/renderer/webcrypto/platform_crypto_nss.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698