Chromium Code Reviews| 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/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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |