OLD | NEW |
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/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/json/json_writer.h" | |
10 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
11 #include "base/strings/stringprintf.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/strings/string_piece.h" |
| 13 #include "base/values.h" |
12 #include "content/child/webcrypto/crypto_data.h" | 14 #include "content/child/webcrypto/crypto_data.h" |
13 #include "content/child/webcrypto/platform_crypto.h" | 15 #include "content/child/webcrypto/platform_crypto.h" |
14 #include "content/child/webcrypto/shared_crypto.h" | 16 #include "content/child/webcrypto/shared_crypto.h" |
15 #include "content/child/webcrypto/webcrypto_util.h" | 17 #include "content/child/webcrypto/webcrypto_util.h" |
16 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
17 | 18 |
18 namespace content { | 19 namespace content { |
19 | 20 |
20 namespace webcrypto { | 21 namespace webcrypto { |
21 | 22 |
22 namespace { | 23 namespace { |
23 | 24 |
24 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. | |
25 // TODO(padolph): Add 'deriveBits' once supported by Blink. | |
26 const blink::WebCryptoKeyUsageMask kJwkEncUsage = | |
27 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | |
28 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | | |
29 blink::WebCryptoKeyUsageDeriveKey; | |
30 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. | |
31 const blink::WebCryptoKeyUsageMask kJwkSigUsage = | |
32 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | |
33 | |
34 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); | 25 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); |
35 | 26 |
36 class JwkAlgorithmInfo { | 27 class JwkAlgorithmInfo { |
37 public: | 28 public: |
38 JwkAlgorithmInfo() | 29 JwkAlgorithmInfo() |
39 : creation_func_(NULL), | 30 : creation_func_(NULL), |
40 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 31 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
41 | 32 |
42 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) | 33 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) |
43 : creation_func_(algorithm_creation_func), | 34 : creation_func_(algorithm_creation_func), |
44 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 35 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
45 | 36 |
46 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, | 37 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, |
47 unsigned int required_key_length_bits) | 38 unsigned int required_key_length_bits) |
48 : creation_func_(algorithm_creation_func), | 39 : creation_func_(algorithm_creation_func), |
49 required_key_length_bytes_(required_key_length_bits / 8) { | 40 required_key_length_bytes_(required_key_length_bits / 8) { |
50 DCHECK_EQ(0u, required_key_length_bits % 8); | 41 DCHECK((required_key_length_bits % 8) == 0); |
51 } | 42 } |
52 | 43 |
53 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { | 44 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { |
54 *algorithm = creation_func_(); | 45 *algorithm = creation_func_(); |
55 return !algorithm->isNull(); | 46 return !algorithm->isNull(); |
56 } | 47 } |
57 | 48 |
58 bool IsInvalidKeyByteLength(size_t byte_length) const { | 49 bool IsInvalidKeyByteLength(size_t byte_length) const { |
59 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) | 50 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) |
60 return false; | 51 return false; |
(...skipping 11 matching lines...) Expand all Loading... |
72 | 63 |
73 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; | 64 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; |
74 | 65 |
75 class JwkAlgorithmRegistry { | 66 class JwkAlgorithmRegistry { |
76 public: | 67 public: |
77 JwkAlgorithmRegistry() { | 68 JwkAlgorithmRegistry() { |
78 // TODO(eroman): | 69 // TODO(eroman): |
79 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 | 70 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 |
80 // says HMAC with SHA-2 should have a key size at least as large as the | 71 // says HMAC with SHA-2 should have a key size at least as large as the |
81 // hash output. | 72 // hash output. |
82 alg_to_info_["HS1"] = | |
83 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, | |
84 blink::WebCryptoAlgorithmIdSha1>); | |
85 alg_to_info_["HS256"] = | 73 alg_to_info_["HS256"] = |
86 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, | 74 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
87 blink::WebCryptoAlgorithmIdSha256>); | 75 blink::WebCryptoAlgorithmIdSha256>); |
88 alg_to_info_["HS384"] = | 76 alg_to_info_["HS384"] = |
89 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, | 77 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
90 blink::WebCryptoAlgorithmIdSha384>); | 78 blink::WebCryptoAlgorithmIdSha384>); |
91 alg_to_info_["HS512"] = | 79 alg_to_info_["HS512"] = |
92 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, | 80 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
93 blink::WebCryptoAlgorithmIdSha512>); | 81 blink::WebCryptoAlgorithmIdSha512>); |
94 alg_to_info_["RS256"] = | 82 alg_to_info_["RS256"] = |
95 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, | 83 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
96 blink::WebCryptoAlgorithmIdSha256>); | 84 blink::WebCryptoAlgorithmIdSha256>); |
97 alg_to_info_["RS384"] = | 85 alg_to_info_["RS384"] = |
98 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, | 86 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
99 blink::WebCryptoAlgorithmIdSha384>); | 87 blink::WebCryptoAlgorithmIdSha384>); |
100 alg_to_info_["RS512"] = | 88 alg_to_info_["RS512"] = |
101 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, | 89 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
102 blink::WebCryptoAlgorithmIdSha512>); | 90 blink::WebCryptoAlgorithmIdSha512>); |
103 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo( | 91 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo( |
104 &BindAlgorithmId<CreateAlgorithm, | 92 &BindAlgorithmId<CreateAlgorithm, |
105 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>); | 93 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>); |
106 alg_to_info_["RSA-OAEP"] = | 94 alg_to_info_["RSA-OAEP"] = |
107 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, | 95 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
108 blink::WebCryptoAlgorithmIdSha1>); | 96 blink::WebCryptoAlgorithmIdSha1>); |
109 alg_to_info_["A128KW"] = JwkAlgorithmInfo( | 97 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet |
110 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, | 98 alg_to_info_["A128KW"] = |
111 128); | 99 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 128); |
112 alg_to_info_["A192KW"] = JwkAlgorithmInfo( | 100 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet |
113 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, | 101 alg_to_info_["A256KW"] = |
114 192); | 102 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 256); |
115 alg_to_info_["A256KW"] = JwkAlgorithmInfo( | |
116 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, | |
117 256); | |
118 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( | 103 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( |
119 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, | 104 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
120 128); | 105 128); |
121 alg_to_info_["A192GCM"] = JwkAlgorithmInfo( | |
122 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, | |
123 192); | |
124 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( | 106 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( |
125 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, | 107 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
126 256); | 108 256); |
127 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( | 109 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( |
128 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, | 110 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
129 128); | 111 128); |
130 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( | 112 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( |
131 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, | 113 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
132 192); | 114 192); |
133 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( | 115 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 if (!dict->Get(path, &value)) | 190 if (!dict->Get(path, &value)) |
209 return Status::Success(); | 191 return Status::Success(); |
210 | 192 |
211 if (!value->GetAsString(result)) | 193 if (!value->GetAsString(result)) |
212 return Status::ErrorJwkPropertyWrongType(path, "string"); | 194 return Status::ErrorJwkPropertyWrongType(path, "string"); |
213 | 195 |
214 *property_exists = true; | 196 *property_exists = true; |
215 return Status::Success(); | 197 return Status::Success(); |
216 } | 198 } |
217 | 199 |
218 // Extracts the optional array property with key |path| from |dict| and saves | |
219 // the result to |*result| if it was found. If the property exists and is not an | |
220 // array, returns an error. Otherwise returns success, and sets | |
221 // |*property_exists| if it was found. Note that |*result| is owned by |dict|. | |
222 Status GetOptionalJwkList(base::DictionaryValue* dict, | |
223 const std::string& path, | |
224 base::ListValue** result, | |
225 bool* property_exists) { | |
226 *property_exists = false; | |
227 base::Value* value = NULL; | |
228 if (!dict->Get(path, &value)) | |
229 return Status::Success(); | |
230 | |
231 if (!value->GetAsList(result)) | |
232 return Status::ErrorJwkPropertyWrongType(path, "list"); | |
233 | |
234 *property_exists = true; | |
235 return Status::Success(); | |
236 } | |
237 | |
238 // Extracts the required string property with key |path| from |dict| and saves | 200 // Extracts the required string property with key |path| from |dict| and saves |
239 // the base64url-decoded bytes to |*result|. If the property does not exist or | 201 // the base64-decoded bytes to |*result|. If the property does not exist or is |
240 // is not a string, or could not be base64url-decoded, returns an error. | 202 // not a string, or could not be base64-decoded, returns an error. |
241 Status GetJwkBytes(base::DictionaryValue* dict, | 203 Status GetJwkBytes(base::DictionaryValue* dict, |
242 const std::string& path, | 204 const std::string& path, |
243 std::string* result) { | 205 std::string* result) { |
244 std::string base64_string; | 206 std::string base64_string; |
245 Status status = GetJwkString(dict, path, &base64_string); | 207 Status status = GetJwkString(dict, path, &base64_string); |
246 if (status.IsError()) | 208 if (status.IsError()) |
247 return status; | 209 return status; |
248 | 210 |
249 if (!Base64DecodeUrlSafe(base64_string, result)) | 211 if (!Base64DecodeUrlSafe(base64_string, result)) |
250 return Status::ErrorJwkBase64Decode(path); | 212 return Status::ErrorJwkBase64Decode(path); |
(...skipping 14 matching lines...) Expand all Loading... |
265 if (!dict->Get(path, &value)) | 227 if (!dict->Get(path, &value)) |
266 return Status::Success(); | 228 return Status::Success(); |
267 | 229 |
268 if (!value->GetAsBoolean(result)) | 230 if (!value->GetAsBoolean(result)) |
269 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | 231 return Status::ErrorJwkPropertyWrongType(path, "boolean"); |
270 | 232 |
271 *property_exists = true; | 233 *property_exists = true; |
272 return Status::Success(); | 234 return Status::Success(); |
273 } | 235 } |
274 | 236 |
275 // Returns true if the set bits in b make up a subset of the set bits in a. | |
276 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a, | |
277 blink::WebCryptoKeyUsageMask b) { | |
278 return (a & b) == b; | |
279 } | |
280 | |
281 // Writes a secret/symmetric key to a JWK dictionary. | |
282 void WriteSecretKey(const blink::WebArrayBuffer& raw_key, | |
283 base::DictionaryValue* jwk_dict) { | |
284 DCHECK(jwk_dict); | |
285 jwk_dict->SetString("kty", "oct"); | |
286 // For a secret/symmetric key, the only extra JWK field is 'k', containing the | |
287 // base64url encoding of the raw key. | |
288 DCHECK(!raw_key.isNull()); | |
289 DCHECK(raw_key.data()); | |
290 DCHECK(raw_key.byteLength()); | |
291 unsigned int key_length_bytes = raw_key.byteLength(); | |
292 const base::StringPiece key_str(static_cast<const char*>(raw_key.data()), | |
293 key_length_bytes); | |
294 jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str)); | |
295 } | |
296 | |
297 // Writes a Web Crypto usage mask to a JWK dictionary. | |
298 void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages, | |
299 base::DictionaryValue* jwk_dict) { | |
300 jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages)); | |
301 } | |
302 | |
303 // Writes a Web Crypto extractable value to a JWK dictionary. | |
304 void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) { | |
305 jwk_dict->SetBoolean("ext", extractable); | |
306 } | |
307 | |
308 // Writes a Web Crypto algorithm to a JWK dictionary. | |
309 Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, | |
310 unsigned int raw_key_length_bytes, | |
311 base::DictionaryValue* jwk_dict) { | |
312 switch (algorithm.paramsType()) { | |
313 case blink::WebCryptoKeyAlgorithmParamsTypeAes: { | |
314 const char* aes_prefix = ""; | |
315 switch (raw_key_length_bytes) { | |
316 case 16: | |
317 aes_prefix = "A128"; | |
318 break; | |
319 case 24: | |
320 aes_prefix = "A192"; | |
321 break; | |
322 case 32: | |
323 aes_prefix = "A256"; | |
324 break; | |
325 default: | |
326 NOTREACHED(); // bad key length means algorithm was built improperly | |
327 return Status::ErrorUnexpected(); | |
328 } | |
329 const char* aes_suffix = ""; | |
330 switch (algorithm.id()) { | |
331 case blink::WebCryptoAlgorithmIdAesCbc: | |
332 aes_suffix = "CBC"; | |
333 break; | |
334 case blink::WebCryptoAlgorithmIdAesCtr: | |
335 aes_suffix = "CTR"; | |
336 break; | |
337 case blink::WebCryptoAlgorithmIdAesGcm: | |
338 aes_suffix = "GCM"; | |
339 break; | |
340 case blink::WebCryptoAlgorithmIdAesKw: | |
341 aes_suffix = "KW"; | |
342 break; | |
343 default: | |
344 return Status::ErrorUnsupported(); | |
345 } | |
346 jwk_dict->SetString("alg", | |
347 base::StringPrintf("%s%s", aes_prefix, aes_suffix)); | |
348 break; | |
349 } | |
350 case blink::WebCryptoKeyAlgorithmParamsTypeHmac: { | |
351 DCHECK(algorithm.hmacParams()); | |
352 switch (algorithm.hmacParams()->hash().id()) { | |
353 case blink::WebCryptoAlgorithmIdSha1: | |
354 jwk_dict->SetString("alg", "HS1"); | |
355 break; | |
356 case blink::WebCryptoAlgorithmIdSha224: | |
357 jwk_dict->SetString("alg", "HS224"); | |
358 break; | |
359 case blink::WebCryptoAlgorithmIdSha256: | |
360 jwk_dict->SetString("alg", "HS256"); | |
361 break; | |
362 case blink::WebCryptoAlgorithmIdSha384: | |
363 jwk_dict->SetString("alg", "HS384"); | |
364 break; | |
365 case blink::WebCryptoAlgorithmIdSha512: | |
366 jwk_dict->SetString("alg", "HS512"); | |
367 break; | |
368 default: | |
369 NOTREACHED(); | |
370 return Status::ErrorUnexpected(); | |
371 } | |
372 break; | |
373 } | |
374 case blink::WebCryptoKeyAlgorithmParamsTypeRsa: | |
375 case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: | |
376 // TODO(padolph): Handle RSA key | |
377 return Status::ErrorUnsupported(); | |
378 default: | |
379 return Status::ErrorUnsupported(); | |
380 } | |
381 return Status::Success(); | |
382 } | |
383 | |
384 } // namespace | 237 } // namespace |
385 | 238 |
386 Status ImportKeyJwk(const CryptoData& key_data, | 239 Status ImportKeyJwk(const CryptoData& key_data, |
387 const blink::WebCryptoAlgorithm& algorithm_or_null, | 240 const blink::WebCryptoAlgorithm& algorithm_or_null, |
388 bool extractable, | 241 bool extractable, |
389 blink::WebCryptoKeyUsageMask usage_mask, | 242 blink::WebCryptoKeyUsageMask usage_mask, |
390 blink::WebCryptoKey* key) { | 243 blink::WebCryptoKey* key) { |
391 // TODO(padolph): Generalize this comment to include export, and move to top | |
392 // of file. | |
393 | 244 |
394 // The goal of this method is to extract key material and meta data from the | 245 // The goal of this method is to extract key material and meta data from the |
395 // incoming JWK, combine them with the input parameters, and ultimately import | 246 // incoming JWK, combine them with the input parameters, and ultimately import |
396 // a Web Crypto Key. | 247 // a Web Crypto Key. |
397 // | 248 // |
398 // JSON Web Key Format (JWK) | 249 // JSON Web Key Format (JWK) |
399 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 | 250 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16 |
| 251 // TODO(padolph): Not all possible values are handled by this code right now |
400 // | 252 // |
401 // A JWK is a simple JSON dictionary with the following entries | 253 // A JWK is a simple JSON dictionary with the following entries |
402 // - "kty" (Key Type) Parameter, REQUIRED | 254 // - "kty" (Key Type) Parameter, REQUIRED |
403 // - <kty-specific parameters, see below>, REQUIRED | 255 // - <kty-specific parameters, see below>, REQUIRED |
404 // - "use" (Key Use) Parameter, OPTIONAL | 256 // - "use" (Key Use) Parameter, OPTIONAL |
405 // - "key_ops" (Key Operations) Parameter, OPTIONAL | |
406 // - "alg" (Algorithm) Parameter, OPTIONAL | 257 // - "alg" (Algorithm) Parameter, OPTIONAL |
407 // - "ext" (Key Exportability), OPTIONAL | 258 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE, |
| 259 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796] |
408 // (all other entries are ignored) | 260 // (all other entries are ignored) |
409 // | 261 // |
410 // OPTIONAL here means that this code does not require the entry to be present | 262 // OPTIONAL here means that this code does not require the entry to be present |
411 // in the incoming JWK, because the method input parameters contain similar | 263 // in the incoming JWK, because the method input parameters contain similar |
412 // information. If the optional JWK entry is present, it will be validated | 264 // information. If the optional JWK entry is present, it will be validated |
413 // against the corresponding input parameter for consistency and combined with | 265 // against the corresponding input parameter for consistency and combined with |
414 // it according to rules defined below. A special case is that the method | 266 // it according to rules defined below. A special case is that the method |
415 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg' | 267 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg' |
416 // value (if present) is used as a fallback. | 268 // value (if present) is used as a fallback. |
417 // | 269 // |
418 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK | 270 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK |
419 // values are parsed out and combined with the method input parameters to | 271 // values are parsed out and combined with the method input parameters to |
420 // build a Web Crypto Key: | 272 // build a Web Crypto Key: |
421 // Web Crypto Key type <-- (deduced) | 273 // Web Crypto Key type <-- (deduced) |
422 // Web Crypto Key extractable <-- JWK ext + input extractable | 274 // Web Crypto Key extractable <-- JWK extractable + input extractable |
423 // Web Crypto Key algorithm <-- JWK alg + input algorithm | 275 // Web Crypto Key algorithm <-- JWK alg + input algorithm |
424 // Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask | 276 // Web Crypto Key keyUsage <-- JWK use + input usage_mask |
425 // Web Crypto Key keying material <-- kty-specific parameters | 277 // Web Crypto Key keying material <-- kty-specific parameters |
426 // | 278 // |
427 // Values for each JWK entry are case-sensitive and defined in | 279 // Values for each JWK entry are case-sensitive and defined in |
428 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18. | 280 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16. |
429 // Note that not all values specified by JOSE are handled by this code. Only | 281 // Note that not all values specified by JOSE are handled by this code. Only |
430 // handled values are listed. | 282 // handled values are listed. |
431 // - kty (Key Type) | 283 // - kty (Key Type) |
432 // +-------+--------------------------------------------------------------+ | 284 // +-------+--------------------------------------------------------------+ |
433 // | "RSA" | RSA [RFC3447] | | 285 // | "RSA" | RSA [RFC3447] | |
434 // | "oct" | Octet sequence (used to represent symmetric keys) | | 286 // | "oct" | Octet sequence (used to represent symmetric keys) | |
435 // +-------+--------------------------------------------------------------+ | 287 // +-------+--------------------------------------------------------------+ |
436 // | 288 // - use (Key Use) |
437 // - key_ops (Key Use Details) | |
438 // The key_ops field is an array that contains one or more strings from | |
439 // the table below, and describes the operations for which this key may be | |
440 // used. | |
441 // +-------+--------------------------------------------------------------+ | 289 // +-------+--------------------------------------------------------------+ |
442 // | "encrypt" | encrypt operations | | 290 // | "enc" | encrypt and decrypt operations | |
443 // | "decrypt" | decrypt operations | | 291 // | "sig" | sign and verify (MAC) operations | |
444 // | "sign" | sign (MAC) operations | | 292 // | "wrap"| key wrap and unwrap [not yet part of JOSE] | |
445 // | "verify" | verify (MAC) operations | | |
446 // | "wrapKey" | key wrap | | |
447 // | "unwrapKey" | key unwrap | | |
448 // | "deriveKey" | key derivation | | |
449 // | "deriveBits" | key derivation TODO(padolph): not currently supported | | |
450 // +-------+--------------------------------------------------------------+ | 293 // +-------+--------------------------------------------------------------+ |
451 // | 294 // - extractable (Key Exportability) |
452 // - use (Key Use) | |
453 // The use field contains a single entry from the table below. | |
454 // +-------+--------------------------------------------------------------+ | |
455 // | "sig" | equivalent to key_ops of [sign, verify] | | |
456 // | "enc" | equivalent to key_ops of [encrypt, decrypt, wrapKey, | | |
457 // | | unwrapKey, deriveKey, deriveBits] | | |
458 // +-------+--------------------------------------------------------------+ | |
459 // | |
460 // NOTE: If both "use" and "key_ops" JWK members are present, the usages | |
461 // specified by them MUST be consistent. In particular, the "use" value | |
462 // "sig" corresponds to "sign" and/or "verify". The "use" value "enc" | |
463 // corresponds to all other values defined above. If "key_ops" values | |
464 // corresponding to both "sig" and "enc" "use" values are present, the "use" | |
465 // member SHOULD NOT be present, and if present, its value MUST NOT be | |
466 // either "sig" or "enc". | |
467 // | |
468 // - ext (Key Exportability) | |
469 // +-------+--------------------------------------------------------------+ | 295 // +-------+--------------------------------------------------------------+ |
470 // | true | Key may be exported from the trusted environment | | 296 // | true | Key may be exported from the trusted environment | |
471 // | false | Key cannot exit the trusted environment | | 297 // | false | Key cannot exit the trusted environment | |
472 // +-------+--------------------------------------------------------------+ | 298 // +-------+--------------------------------------------------------------+ |
473 // | |
474 // - alg (Algorithm) | 299 // - alg (Algorithm) |
475 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18 | 300 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16 |
476 // +--------------+-------------------------------------------------------+ | 301 // +--------------+-------------------------------------------------------+ |
477 // | Digital Signature or MAC Algorithm | | 302 // | Digital Signature or MAC Algorithm | |
478 // +--------------+-------------------------------------------------------+ | 303 // +--------------+-------------------------------------------------------+ |
479 // | "HS1" | HMAC using SHA-1 hash algorithm | | |
480 // | "HS256" | HMAC using SHA-256 hash algorithm | | 304 // | "HS256" | HMAC using SHA-256 hash algorithm | |
481 // | "HS384" | HMAC using SHA-384 hash algorithm | | 305 // | "HS384" | HMAC using SHA-384 hash algorithm | |
482 // | "HS512" | HMAC using SHA-512 hash algorithm | | 306 // | "HS512" | HMAC using SHA-512 hash algorithm | |
483 // | "RS256" | RSASSA using SHA-256 hash algorithm | | 307 // | "RS256" | RSASSA using SHA-256 hash algorithm | |
484 // | "RS384" | RSASSA using SHA-384 hash algorithm | | 308 // | "RS384" | RSASSA using SHA-384 hash algorithm | |
485 // | "RS512" | RSASSA using SHA-512 hash algorithm | | 309 // | "RS512" | RSASSA using SHA-512 hash algorithm | |
486 // +--------------+-------------------------------------------------------| | 310 // +--------------+-------------------------------------------------------| |
487 // | Key Management Algorithm | | 311 // | Key Management Algorithm | |
488 // +--------------+-------------------------------------------------------+ | 312 // +--------------+-------------------------------------------------------+ |
489 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] | | 313 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] | |
490 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | | 314 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | |
491 // | | (OAEP) [RFC3447], with the default parameters | | 315 // | | (OAEP) [RFC3447], with the default parameters | |
492 // | | specified by RFC3447 in Section A.2.1 | | 316 // | | specified by RFC3447 in Section A.2.1 | |
493 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | | 317 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | |
494 // | | [RFC3394] using 128 bit keys | | 318 // | | [RFC3394] using 128 bit keys | |
495 // | "A192KW" | AES Key Wrap Algorithm using 192 bit keys | | |
496 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | | 319 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | |
497 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | | 320 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | |
498 // | | 128 bit keys | | 321 // | | 128 bit keys | |
499 // | "A192GCM" | AES GCM using 192 bit keys | | |
500 // | "A256GCM" | AES GCM using 256 bit keys | | 322 // | "A256GCM" | AES GCM using 256 bit keys | |
501 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | | 323 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | |
502 // | | padding [NIST.800-38A] | | 324 // | | padding [NIST.800-38A] [not yet part of JOSE, see | |
503 // | "A192CBC" | AES CBC using 192 bit keys | | 325 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 | |
504 // | "A256CBC" | AES CBC using 256 bit keys | | 326 // | "A192CBC" | AES CBC using 192 bit keys [not yet part of JOSE] | |
| 327 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] | |
505 // +--------------+-------------------------------------------------------+ | 328 // +--------------+-------------------------------------------------------+ |
506 // | 329 // |
507 // kty-specific parameters | 330 // kty-specific parameters |
508 // The value of kty determines the type and content of the keying material | 331 // The value of kty determines the type and content of the keying material |
509 // carried in the JWK to be imported. Currently only two possibilities are | 332 // carried in the JWK to be imported. Currently only two possibilities are |
510 // supported: a raw key or an RSA public key. RSA private keys are not | 333 // supported: a raw key or an RSA public key. RSA private keys are not |
511 // supported because typical applications seldom need to import a private key, | 334 // supported because typical applications seldom need to import a private key, |
512 // and the large number of JWK parameters required to describe one. | 335 // and the large number of JWK parameters required to describe one. |
513 // - kty == "oct" (symmetric or other raw key) | 336 // - kty == "oct" (symmetric or other raw key) |
514 // +-------+--------------------------------------------------------------+ | 337 // +-------+--------------------------------------------------------------+ |
(...skipping 21 matching lines...) Expand all Loading... |
536 // | 359 // |
537 // algorithm | 360 // algorithm |
538 // If an algorithm is provided by both the input parameter and the JWK, | 361 // If an algorithm is provided by both the input parameter and the JWK, |
539 // consistency between the two is based only on algorithm ID's (including an | 362 // consistency between the two is based only on algorithm ID's (including an |
540 // inner hash algorithm if present). In this case if the consistency | 363 // inner hash algorithm if present). In this case if the consistency |
541 // check is passed, the input algorithm is used. If only one of either the | 364 // check is passed, the input algorithm is used. If only one of either the |
542 // input algorithm and JWK alg is provided, it is used as the final | 365 // input algorithm and JWK alg is provided, it is used as the final |
543 // algorithm. | 366 // algorithm. |
544 // | 367 // |
545 // extractable | 368 // extractable |
546 // If the JWK ext field is true but the input parameter is false, make the | 369 // If the JWK extractable is true but the input parameter is false, make the |
547 // Web Crypto Key non-extractable. Conversely, if the JWK ext field is | 370 // Web Crypto Key non-extractable. Conversely, if the JWK extractable is |
548 // false but the input parameter is true, it is an inconsistency. If both | 371 // false but the input parameter is true, it is an inconsistency. If both |
549 // are true or both are false, use that value. | 372 // are true or both are false, use that value. |
550 // | 373 // |
551 // usage_mask | 374 // usage_mask |
552 // The input usage_mask must be a strict subset of the interpreted JWK use | 375 // The input usage_mask must be a strict subset of the interpreted JWK use |
553 // value, else it is judged inconsistent. In all cases the input usage_mask | 376 // value, else it is judged inconsistent. In all cases the input usage_mask |
554 // is used as the final usage_mask. | 377 // is used as the final usage_mask. |
555 // | 378 // |
556 | 379 |
557 if (!key_data.byte_length()) | 380 if (!key_data.byte_length()) |
558 return Status::ErrorImportEmptyKeyData(); | 381 return Status::ErrorImportEmptyKeyData(); |
559 DCHECK(key); | 382 DCHECK(key); |
560 | 383 |
561 // Parse the incoming JWK JSON. | 384 // Parse the incoming JWK JSON. |
562 base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), | 385 base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), |
563 key_data.byte_length()); | 386 key_data.byte_length()); |
564 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); | 387 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
565 // Note, bare pointer dict_value is ok since it points into scoped value. | 388 // Note, bare pointer dict_value is ok since it points into scoped value. |
566 base::DictionaryValue* dict_value = NULL; | 389 base::DictionaryValue* dict_value = NULL; |
567 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) | 390 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
568 return Status::ErrorJwkNotDictionary(); | 391 return Status::ErrorJwkNotDictionary(); |
569 | 392 |
570 // JWK "kty". Exit early if this required JWK parameter is missing. | 393 // JWK "kty". Exit early if this required JWK parameter is missing. |
571 std::string jwk_kty_value; | 394 std::string jwk_kty_value; |
572 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); | 395 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); |
573 if (status.IsError()) | 396 if (status.IsError()) |
574 return status; | 397 return status; |
575 | 398 |
576 // JWK "ext" (optional) --> extractable parameter | 399 // JWK "extractable" (optional) --> extractable parameter |
577 { | 400 { |
578 bool jwk_ext_value = false; | 401 bool jwk_extractable_value = false; |
579 bool has_jwk_ext; | 402 bool has_jwk_extractable; |
580 status = | 403 status = GetOptionalJwkBool(dict_value, |
581 GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); | 404 "extractable", |
| 405 &jwk_extractable_value, |
| 406 &has_jwk_extractable); |
582 if (status.IsError()) | 407 if (status.IsError()) |
583 return status; | 408 return status; |
584 if (has_jwk_ext && !jwk_ext_value && extractable) | 409 if (has_jwk_extractable && !jwk_extractable_value && extractable) |
585 return Status::ErrorJwkExtInconsistent(); | 410 return Status::ErrorJwkExtractableInconsistent(); |
586 } | 411 } |
587 | 412 |
588 // JWK "alg" (optional) --> algorithm parameter | 413 // JWK "alg" (optional) --> algorithm parameter |
589 // Note: input algorithm is also optional, so we have six cases to handle. | 414 // Note: input algorithm is also optional, so we have six cases to handle. |
590 // 1. JWK alg present but unrecognized: error | 415 // 1. JWK alg present but unrecognized: error |
591 // 2. JWK alg valid AND input algorithm isNull: use JWK value | 416 // 2. JWK alg valid AND input algorithm isNull: use JWK value |
592 // 3. JWK alg valid AND input algorithm specified, but JWK value | 417 // 3. JWK alg valid AND input algorithm specified, but JWK value |
593 // inconsistent with input: error | 418 // inconsistent with input: error |
594 // 4. JWK alg valid AND input algorithm specified, both consistent: use | 419 // 4. JWK alg valid AND input algorithm specified, both consistent: use |
595 // input value (because it has potentially more details) | 420 // input value (because it has potentially more details) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
628 algorithm = algorithm_or_null; // case 4 | 453 algorithm = algorithm_or_null; // case 4 |
629 } | 454 } |
630 } else { | 455 } else { |
631 // JWK alg missing | 456 // JWK alg missing |
632 if (algorithm_or_null.isNull()) | 457 if (algorithm_or_null.isNull()) |
633 return Status::ErrorJwkAlgorithmMissing(); // case 5 | 458 return Status::ErrorJwkAlgorithmMissing(); // case 5 |
634 algorithm = algorithm_or_null; // case 6 | 459 algorithm = algorithm_or_null; // case 6 |
635 } | 460 } |
636 DCHECK(!algorithm.isNull()); | 461 DCHECK(!algorithm.isNull()); |
637 | 462 |
638 // JWK "key_ops" (optional) --> usage_mask parameter | |
639 base::ListValue* jwk_key_ops_value = NULL; | |
640 bool has_jwk_key_ops; | |
641 status = GetOptionalJwkList( | |
642 dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); | |
643 if (status.IsError()) | |
644 return status; | |
645 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; | |
646 if (has_jwk_key_ops) { | |
647 status = | |
648 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); | |
649 if (status.IsError()) | |
650 return status; | |
651 // The input usage_mask must be a subset of jwk_key_ops_mask. | |
652 if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) | |
653 return Status::ErrorJwkKeyopsInconsistent(); | |
654 } | |
655 | |
656 // JWK "use" (optional) --> usage_mask parameter | 463 // JWK "use" (optional) --> usage_mask parameter |
657 std::string jwk_use_value; | 464 std::string jwk_use_value; |
658 bool has_jwk_use; | 465 bool has_jwk_use; |
659 status = | 466 status = |
660 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); | 467 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); |
661 if (status.IsError()) | 468 if (status.IsError()) |
662 return status; | 469 return status; |
663 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; | |
664 if (has_jwk_use) { | 470 if (has_jwk_use) { |
665 if (jwk_use_value == "enc") | 471 blink::WebCryptoKeyUsageMask jwk_usage_mask = 0; |
666 jwk_use_mask = kJwkEncUsage; | 472 if (jwk_use_value == "enc") { |
667 else if (jwk_use_value == "sig") | 473 jwk_usage_mask = |
668 jwk_use_mask = kJwkSigUsage; | 474 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; |
669 else | 475 } else if (jwk_use_value == "sig") { |
670 return Status::ErrorJwkUnrecognizedUse(); | 476 jwk_usage_mask = |
671 // The input usage_mask must be a subset of jwk_use_mask. | 477 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
672 if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) | 478 } else if (jwk_use_value == "wrap") { |
673 return Status::ErrorJwkUseInconsistent(); | 479 jwk_usage_mask = |
| 480 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey; |
| 481 } else { |
| 482 return Status::ErrorJwkUnrecognizedUsage(); |
| 483 } |
| 484 if ((jwk_usage_mask & usage_mask) != usage_mask) { |
| 485 // A usage_mask must be a subset of jwk_usage_mask. |
| 486 return Status::ErrorJwkUsageInconsistent(); |
| 487 } |
674 } | 488 } |
675 | 489 |
676 // If both 'key_ops' and 'use' are present, ensure they are consistent. | |
677 if (has_jwk_key_ops && has_jwk_use && | |
678 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) | |
679 return Status::ErrorJwkUseAndKeyopsInconsistent(); | |
680 | |
681 // JWK keying material --> ImportKeyInternal() | 490 // JWK keying material --> ImportKeyInternal() |
682 if (jwk_kty_value == "oct") { | 491 if (jwk_kty_value == "oct") { |
| 492 |
683 std::string jwk_k_value; | 493 std::string jwk_k_value; |
684 status = GetJwkBytes(dict_value, "k", &jwk_k_value); | 494 status = GetJwkBytes(dict_value, "k", &jwk_k_value); |
685 if (status.IsError()) | 495 if (status.IsError()) |
686 return status; | 496 return status; |
687 | 497 |
688 // Some JWK alg ID's embed information about the key length in the alg ID | 498 // Some JWK alg ID's embed information about the key length in the alg ID |
689 // string. For example "A128CBC" implies the JWK carries 128 bits | 499 // string. For example "A128CBC" implies the JWK carries 128 bits |
690 // of key material. For such keys validate that enough bytes were provided. | 500 // of key material. For such keys validate that enough bytes were provided. |
691 // If this validation is not done, then it would be possible to select a | 501 // If this validation is not done, then it would be possible to select a |
692 // different algorithm by passing a different lengthed key, since that is | 502 // different algorithm by passing a different lengthed key, since that is |
693 // how WebCrypto interprets things. | 503 // how WebCrypto interprets things. |
694 if (algorithm_info && | 504 if (algorithm_info && |
695 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { | 505 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { |
696 return Status::ErrorJwkIncorrectKeyLength(); | 506 return Status::ErrorJwkIncorrectKeyLength(); |
697 } | 507 } |
698 | 508 |
699 return ImportKey(blink::WebCryptoKeyFormatRaw, | 509 return ImportKey(blink::WebCryptoKeyFormatRaw, |
700 CryptoData(jwk_k_value), | 510 CryptoData(jwk_k_value), |
701 algorithm, | 511 algorithm, |
702 extractable, | 512 extractable, |
703 usage_mask, | 513 usage_mask, |
704 key); | 514 key); |
705 } else if (jwk_kty_value == "RSA") { | 515 } else if (jwk_kty_value == "RSA") { |
| 516 |
706 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | 517 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry |
707 // in the JWK, while an RSA private key must have those, plus at least a "d" | 518 // in the JWK, while an RSA private key must have those, plus at least a "d" |
708 // (private exponent) entry. | 519 // (private exponent) entry. |
709 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | 520 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
710 // section 6.3. | 521 // section 6.3. |
711 | 522 |
712 // RSA private key import is not currently supported, so fail here if a "d" | 523 // RSA private key import is not currently supported, so fail here if a "d" |
713 // entry is found. | 524 // entry is found. |
714 // TODO(padolph): Support RSA private key import. | 525 // TODO(padolph): Support RSA private key import. |
715 if (dict_value->HasKey("d")) | 526 if (dict_value->HasKey("d")) |
(...skipping 15 matching lines...) Expand all Loading... |
731 CryptoData(jwk_e_value), | 542 CryptoData(jwk_e_value), |
732 key); | 543 key); |
733 | 544 |
734 } else { | 545 } else { |
735 return Status::ErrorJwkUnrecognizedKty(); | 546 return Status::ErrorJwkUnrecognizedKty(); |
736 } | 547 } |
737 | 548 |
738 return Status::Success(); | 549 return Status::Success(); |
739 } | 550 } |
740 | 551 |
741 Status ExportKeyJwk(const blink::WebCryptoKey& key, | |
742 blink::WebArrayBuffer* buffer) { | |
743 base::DictionaryValue jwk_dict; | |
744 Status status = Status::Error(); | |
745 blink::WebArrayBuffer exported_key; | |
746 | |
747 if (key.type() == blink::WebCryptoKeyTypeSecret) { | |
748 status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); | |
749 if (status.IsError()) | |
750 return status; | |
751 WriteSecretKey(exported_key, &jwk_dict); | |
752 } else { | |
753 // TODO(padolph): Handle asymmetric keys, at least the public key. | |
754 return Status::ErrorUnsupported(); | |
755 } | |
756 | |
757 WriteKeyOps(key.usages(), &jwk_dict); | |
758 WriteExt(key.extractable(), &jwk_dict); | |
759 status = WriteAlg(key.algorithm(), exported_key.byteLength(), &jwk_dict); | |
760 if (status.IsError()) | |
761 return status; | |
762 | |
763 std::string json; | |
764 base::JSONWriter::Write(&jwk_dict, &json); | |
765 *buffer = CreateArrayBuffer(reinterpret_cast<const uint8*>(json.data()), | |
766 json.size()); | |
767 return Status::Success(); | |
768 } | |
769 | |
770 } // namespace webcrypto | 552 } // namespace webcrypto |
771 | 553 |
772 } // namespace content | 554 } // namespace content |
OLD | NEW |