| 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 "jwk.h" | 5 #include "content/child/webcrypto/jwk.h" |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <functional> | |
| 9 #include <map> | |
| 10 | 6 |
| 11 #include "base/base64.h" | 7 #include "base/base64.h" |
| 12 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
| 13 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 14 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| 15 #include "base/strings/string_piece.h" | 11 #include "base/strings/string_piece.h" |
| 16 #include "content/child/webcrypto/crypto_data.h" | 12 #include "content/child/webcrypto/crypto_data.h" |
| 17 #include "content/child/webcrypto/status.h" | 13 #include "content/child/webcrypto/status.h" |
| 18 #include "content/child/webcrypto/webcrypto_util.h" | 14 #include "content/child/webcrypto/webcrypto_util.h" |
| 19 | 15 |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 | 209 |
| 214 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. | 210 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
| 215 const blink::WebCryptoKeyUsageMask kJwkEncUsage = | 211 const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
| 216 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | 212 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| 217 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | | 213 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | |
| 218 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; | 214 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; |
| 219 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. | 215 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
| 220 const blink::WebCryptoKeyUsageMask kJwkSigUsage = | 216 const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
| 221 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | 217 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| 222 | 218 |
| 223 class JwkWriter { | 219 // Checks that the "ext" member of the JWK is consistent with |
| 224 public: | 220 // "expected_extractable". |
| 225 JwkWriter(const std::string& algorithm, | 221 Status VerifyExt(const JwkReader& jwk, bool expected_extractable) { |
| 226 bool extractable, | |
| 227 blink::WebCryptoKeyUsageMask usages, | |
| 228 const std::string& kty) { | |
| 229 dict_.SetString("alg", algorithm); | |
| 230 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages)); | |
| 231 dict_.SetBoolean("ext", extractable); | |
| 232 dict_.SetString("kty", kty); | |
| 233 } | |
| 234 | |
| 235 void Set(const std::string& key, const std::string& value) { | |
| 236 dict_.SetString(key, value); | |
| 237 } | |
| 238 | |
| 239 void SetBase64Encoded(const std::string& key, const CryptoData& value) { | |
| 240 dict_.SetString(key, | |
| 241 Base64EncodeUrlSafe(base::StringPiece( | |
| 242 reinterpret_cast<const char*>(value.bytes()), | |
| 243 value.byte_length()))); | |
| 244 } | |
| 245 | |
| 246 void ToBytes(std::vector<uint8_t>* utf8_bytes) { | |
| 247 std::string json; | |
| 248 base::JSONWriter::Write(&dict_, &json); | |
| 249 utf8_bytes->assign(json.begin(), json.end()); | |
| 250 } | |
| 251 | |
| 252 private: | |
| 253 base::DictionaryValue dict_; | |
| 254 }; | |
| 255 | |
| 256 // Extracts the required string property with key |path| from |dict| and saves | |
| 257 // the result to |*result|. If the property does not exist or is not a string, | |
| 258 // returns an error. | |
| 259 Status GetJwkString(base::DictionaryValue* dict, | |
| 260 const std::string& path, | |
| 261 std::string* result) { | |
| 262 base::Value* value = NULL; | |
| 263 if (!dict->Get(path, &value)) | |
| 264 return Status::ErrorJwkPropertyMissing(path); | |
| 265 if (!value->GetAsString(result)) | |
| 266 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
| 267 return Status::Success(); | |
| 268 } | |
| 269 | |
| 270 // Extracts the optional string property with key |path| from |dict| and saves | |
| 271 // the result to |*result| if it was found. If the property exists and is not a | |
| 272 // string, returns an error. Otherwise returns success, and sets | |
| 273 // |*property_exists| if it was found. | |
| 274 Status GetOptionalJwkString(base::DictionaryValue* dict, | |
| 275 const std::string& path, | |
| 276 std::string* result, | |
| 277 bool* property_exists) { | |
| 278 *property_exists = false; | |
| 279 base::Value* value = NULL; | |
| 280 if (!dict->Get(path, &value)) | |
| 281 return Status::Success(); | |
| 282 | |
| 283 if (!value->GetAsString(result)) | |
| 284 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
| 285 | |
| 286 *property_exists = true; | |
| 287 return Status::Success(); | |
| 288 } | |
| 289 | |
| 290 // Extracts the optional array property with key |path| from |dict| and saves | |
| 291 // the result to |*result| if it was found. If the property exists and is not an | |
| 292 // array, returns an error. Otherwise returns success, and sets | |
| 293 // |*property_exists| if it was found. Note that |*result| is owned by |dict|. | |
| 294 Status GetOptionalJwkList(base::DictionaryValue* dict, | |
| 295 const std::string& path, | |
| 296 base::ListValue** result, | |
| 297 bool* property_exists) { | |
| 298 *property_exists = false; | |
| 299 base::Value* value = NULL; | |
| 300 if (!dict->Get(path, &value)) | |
| 301 return Status::Success(); | |
| 302 | |
| 303 if (!value->GetAsList(result)) | |
| 304 return Status::ErrorJwkPropertyWrongType(path, "list"); | |
| 305 | |
| 306 *property_exists = true; | |
| 307 return Status::Success(); | |
| 308 } | |
| 309 | |
| 310 // Extracts the required string property with key |path| from |dict| and saves | |
| 311 // the base64url-decoded bytes to |*result|. If the property does not exist or | |
| 312 // is not a string, or could not be base64url-decoded, returns an error. | |
| 313 Status GetJwkBytes(base::DictionaryValue* dict, | |
| 314 const std::string& path, | |
| 315 std::string* result) { | |
| 316 std::string base64_string; | |
| 317 Status status = GetJwkString(dict, path, &base64_string); | |
| 318 if (status.IsError()) | |
| 319 return status; | |
| 320 | |
| 321 if (!Base64DecodeUrlSafe(base64_string, result)) | |
| 322 return Status::ErrorJwkBase64Decode(path); | |
| 323 | |
| 324 return Status::Success(); | |
| 325 } | |
| 326 | |
| 327 // Extracts the required base64url property, which is interpreted as being a | |
| 328 // big-endian unsigned integer. | |
| 329 Status GetJwkBigInteger(base::DictionaryValue* dict, | |
| 330 const std::string& path, | |
| 331 std::string* result) { | |
| 332 Status status = GetJwkBytes(dict, path, result); | |
| 333 if (status.IsError()) | |
| 334 return status; | |
| 335 | |
| 336 if (result->empty()) | |
| 337 return Status::ErrorJwkEmptyBigInteger(path); | |
| 338 | |
| 339 // The JWA spec says that "The octet sequence MUST utilize the minimum number | |
| 340 // of octets to represent the value." This means there shouldn't be any | |
| 341 // leading zeros. | |
| 342 if (result->size() > 1 && (*result)[0] == 0) | |
| 343 return Status::ErrorJwkBigIntegerHasLeadingZero(path); | |
| 344 | |
| 345 return Status::Success(); | |
| 346 } | |
| 347 | |
| 348 // Extracts the optional boolean property with key |path| from |dict| and saves | |
| 349 // the result to |*result| if it was found. If the property exists and is not a | |
| 350 // boolean, returns an error. Otherwise returns success, and sets | |
| 351 // |*property_exists| if it was found. | |
| 352 Status GetOptionalJwkBool(base::DictionaryValue* dict, | |
| 353 const std::string& path, | |
| 354 bool* result, | |
| 355 bool* property_exists) { | |
| 356 *property_exists = false; | |
| 357 base::Value* value = NULL; | |
| 358 if (!dict->Get(path, &value)) | |
| 359 return Status::Success(); | |
| 360 | |
| 361 if (!value->GetAsBoolean(result)) | |
| 362 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | |
| 363 | |
| 364 *property_exists = true; | |
| 365 return Status::Success(); | |
| 366 } | |
| 367 | |
| 368 Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { | |
| 369 // JWK "ext" (optional) --> extractable parameter | 222 // JWK "ext" (optional) --> extractable parameter |
| 370 bool jwk_ext_value = false; | 223 bool jwk_ext_value = false; |
| 371 bool has_jwk_ext; | 224 bool has_jwk_ext; |
| 372 Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); | 225 Status status = jwk.GetOptionalBool("ext", &jwk_ext_value, &has_jwk_ext); |
| 373 if (status.IsError()) | 226 if (status.IsError()) |
| 374 return status; | 227 return status; |
| 375 if (has_jwk_ext && expected_extractable && !jwk_ext_value) | 228 if (has_jwk_ext && expected_extractable && !jwk_ext_value) |
| 376 return Status::ErrorJwkExtInconsistent(); | 229 return Status::ErrorJwkExtInconsistent(); |
| 377 return Status::Success(); | 230 return Status::Success(); |
| 378 } | 231 } |
| 379 | 232 |
| 380 Status VerifyUsages(base::DictionaryValue* dict, | 233 // Checks that the usages ("use" and "key_ops") of the JWK is consistent with |
| 234 // "expected_usages". |
| 235 Status VerifyUsages(const JwkReader& jwk, |
| 381 blink::WebCryptoKeyUsageMask expected_usages) { | 236 blink::WebCryptoKeyUsageMask expected_usages) { |
| 382 // JWK "key_ops" (optional) --> usages parameter | 237 // JWK "key_ops" (optional) --> usages parameter |
| 383 base::ListValue* jwk_key_ops_value = NULL; | 238 base::ListValue* jwk_key_ops_value = NULL; |
| 384 bool has_jwk_key_ops; | 239 bool has_jwk_key_ops; |
| 385 Status status = | 240 Status status = |
| 386 GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); | 241 jwk.GetOptionalList("key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
| 387 if (status.IsError()) | 242 if (status.IsError()) |
| 388 return status; | 243 return status; |
| 389 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; | 244 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
| 390 if (has_jwk_key_ops) { | 245 if (has_jwk_key_ops) { |
| 391 status = | 246 status = |
| 392 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); | 247 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
| 393 if (status.IsError()) | 248 if (status.IsError()) |
| 394 return status; | 249 return status; |
| 395 // The input usages must be a subset of jwk_key_ops_mask. | 250 // The input usages must be a subset of jwk_key_ops_mask. |
| 396 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usages)) | 251 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usages)) |
| 397 return Status::ErrorJwkKeyopsInconsistent(); | 252 return Status::ErrorJwkKeyopsInconsistent(); |
| 398 } | 253 } |
| 399 | 254 |
| 400 // JWK "use" (optional) --> usages parameter | 255 // JWK "use" (optional) --> usages parameter |
| 401 std::string jwk_use_value; | 256 std::string jwk_use_value; |
| 402 bool has_jwk_use; | 257 bool has_jwk_use; |
| 403 status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); | 258 status = jwk.GetOptionalString("use", &jwk_use_value, &has_jwk_use); |
| 404 if (status.IsError()) | 259 if (status.IsError()) |
| 405 return status; | 260 return status; |
| 406 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; | 261 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
| 407 if (has_jwk_use) { | 262 if (has_jwk_use) { |
| 408 if (jwk_use_value == "enc") | 263 if (jwk_use_value == "enc") |
| 409 jwk_use_mask = kJwkEncUsage; | 264 jwk_use_mask = kJwkEncUsage; |
| 410 else if (jwk_use_value == "sig") | 265 else if (jwk_use_value == "sig") |
| 411 jwk_use_mask = kJwkSigUsage; | 266 jwk_use_mask = kJwkSigUsage; |
| 412 else | 267 else |
| 413 return Status::ErrorJwkUnrecognizedUse(); | 268 return Status::ErrorJwkUnrecognizedUse(); |
| 414 // The input usages must be a subset of jwk_use_mask. | 269 // The input usages must be a subset of jwk_use_mask. |
| 415 if (!ContainsKeyUsages(jwk_use_mask, expected_usages)) | 270 if (!ContainsKeyUsages(jwk_use_mask, expected_usages)) |
| 416 return Status::ErrorJwkUseInconsistent(); | 271 return Status::ErrorJwkUseInconsistent(); |
| 417 } | 272 } |
| 418 | 273 |
| 419 // If both 'key_ops' and 'use' are present, ensure they are consistent. | 274 // If both 'key_ops' and 'use' are present, ensure they are consistent. |
| 420 if (has_jwk_key_ops && has_jwk_use && | 275 if (has_jwk_key_ops && has_jwk_use && |
| 421 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) | 276 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
| 422 return Status::ErrorJwkUseAndKeyopsInconsistent(); | 277 return Status::ErrorJwkUseAndKeyopsInconsistent(); |
| 423 | 278 |
| 424 return Status::Success(); | 279 return Status::Success(); |
| 425 } | 280 } |
| 426 | 281 |
| 427 Status VerifyAlg(base::DictionaryValue* dict, | 282 } // namespace |
| 428 const std::string& expected_algorithm) { | |
| 429 // JWK "alg" --> algorithm parameter | |
| 430 bool has_jwk_alg; | |
| 431 std::string jwk_alg_value; | |
| 432 Status status = | |
| 433 GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); | |
| 434 if (status.IsError()) | |
| 435 return status; | |
| 436 | 283 |
| 437 if (has_jwk_alg && jwk_alg_value != expected_algorithm) | 284 JwkReader::JwkReader() { |
| 438 return Status::ErrorJwkAlgorithmInconsistent(); | |
| 439 | |
| 440 return Status::Success(); | |
| 441 } | 285 } |
| 442 | 286 |
| 443 Status ParseJwkCommon(const CryptoData& bytes, | 287 JwkReader::~JwkReader() { |
| 444 bool expected_extractable, | 288 } |
| 445 blink::WebCryptoKeyUsageMask expected_usages, | 289 |
| 446 std::string* kty, | 290 Status JwkReader::Init(const CryptoData& bytes, |
| 447 scoped_ptr<base::DictionaryValue>* dict) { | 291 bool expected_extractable, |
| 292 blink::WebCryptoKeyUsageMask expected_usages, |
| 293 const std::string& expected_kty, |
| 294 const std::string& expected_alg) { |
| 295 if (!bytes.byte_length()) |
| 296 return Status::ErrorImportEmptyKeyData(); |
| 297 |
| 448 // Parse the incoming JWK JSON. | 298 // Parse the incoming JWK JSON. |
| 449 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), | 299 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), |
| 450 bytes.byte_length()); | 300 bytes.byte_length()); |
| 451 | 301 |
| 452 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); | 302 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
| 453 base::DictionaryValue* dict_value = NULL; | 303 base::DictionaryValue* dict_value = NULL; |
| 454 | 304 |
| 455 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) | 305 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
| 456 return Status::ErrorJwkNotDictionary(); | 306 return Status::ErrorJwkNotDictionary(); |
| 457 | 307 |
| 458 // Release |value|, as ownership will be transferred to |dict| via | 308 // Release |value|, as ownership will be transferred to |dict| via |
| 459 // |dict_value|, which points to the same object as |value|. | 309 // |dict_value|, which points to the same object as |value|. |
| 460 ignore_result(value.release()); | 310 ignore_result(value.release()); |
| 461 dict->reset(dict_value); | 311 dict_.reset(dict_value); |
| 462 | 312 |
| 463 // JWK "kty". Exit early if this required JWK parameter is missing. | 313 // JWK "kty". Exit early if this required JWK parameter is missing. |
| 464 Status status = GetJwkString(dict_value, "kty", kty); | 314 std::string kty; |
| 315 Status status = GetString("kty", &kty); |
| 465 if (status.IsError()) | 316 if (status.IsError()) |
| 466 return status; | 317 return status; |
| 467 | 318 |
| 468 status = VerifyExt(dict_value, expected_extractable); | 319 if (kty != expected_kty) |
| 320 return Status::ErrorJwkUnexpectedKty(expected_kty); |
| 321 |
| 322 status = VerifyExt(*this, expected_extractable); |
| 469 if (status.IsError()) | 323 if (status.IsError()) |
| 470 return status; | 324 return status; |
| 471 | 325 |
| 472 status = VerifyUsages(dict_value, expected_usages); | 326 status = VerifyUsages(*this, expected_usages); |
| 473 if (status.IsError()) | 327 if (status.IsError()) |
| 474 return status; | 328 return status; |
| 475 | 329 |
| 330 // Verify the algorithm if an expectation was provided. |
| 331 if (!expected_alg.empty()) { |
| 332 status = VerifyAlg(expected_alg); |
| 333 if (status.IsError()) |
| 334 return status; |
| 335 } |
| 336 |
| 476 return Status::Success(); | 337 return Status::Success(); |
| 477 } | 338 } |
| 478 | 339 |
| 340 bool JwkReader::HasMember(const std::string& member_name) const { |
| 341 return dict_->HasKey(member_name); |
| 342 } |
| 343 |
| 344 Status JwkReader::GetString(const std::string& member_name, |
| 345 std::string* result) const { |
| 346 base::Value* value = NULL; |
| 347 if (!dict_->Get(member_name, &value)) |
| 348 return Status::ErrorJwkPropertyMissing(member_name); |
| 349 if (!value->GetAsString(result)) |
| 350 return Status::ErrorJwkPropertyWrongType(member_name, "string"); |
| 351 return Status::Success(); |
| 352 } |
| 353 |
| 354 Status JwkReader::GetOptionalString(const std::string& member_name, |
| 355 std::string* result, |
| 356 bool* member_exists) const { |
| 357 *member_exists = false; |
| 358 base::Value* value = NULL; |
| 359 if (!dict_->Get(member_name, &value)) |
| 360 return Status::Success(); |
| 361 |
| 362 if (!value->GetAsString(result)) |
| 363 return Status::ErrorJwkPropertyWrongType(member_name, "string"); |
| 364 |
| 365 *member_exists = true; |
| 366 return Status::Success(); |
| 367 } |
| 368 |
| 369 Status JwkReader::GetOptionalList(const std::string& member_name, |
| 370 base::ListValue** result, |
| 371 bool* member_exists) const { |
| 372 *member_exists = false; |
| 373 base::Value* value = NULL; |
| 374 if (!dict_->Get(member_name, &value)) |
| 375 return Status::Success(); |
| 376 |
| 377 if (!value->GetAsList(result)) |
| 378 return Status::ErrorJwkPropertyWrongType(member_name, "list"); |
| 379 |
| 380 *member_exists = true; |
| 381 return Status::Success(); |
| 382 } |
| 383 |
| 384 Status JwkReader::GetBytes(const std::string& member_name, |
| 385 std::string* result) const { |
| 386 std::string base64_string; |
| 387 Status status = GetString(member_name, &base64_string); |
| 388 if (status.IsError()) |
| 389 return status; |
| 390 |
| 391 if (!Base64DecodeUrlSafe(base64_string, result)) |
| 392 return Status::ErrorJwkBase64Decode(member_name); |
| 393 |
| 394 return Status::Success(); |
| 395 } |
| 396 |
| 397 Status JwkReader::GetBigInteger(const std::string& member_name, |
| 398 std::string* result) const { |
| 399 Status status = GetBytes(member_name, result); |
| 400 if (status.IsError()) |
| 401 return status; |
| 402 |
| 403 if (result->empty()) |
| 404 return Status::ErrorJwkEmptyBigInteger(member_name); |
| 405 |
| 406 // The JWA spec says that "The octet sequence MUST utilize the minimum number |
| 407 // of octets to represent the value." This means there shouldn't be any |
| 408 // leading zeros. |
| 409 if (result->size() > 1 && (*result)[0] == 0) |
| 410 return Status::ErrorJwkBigIntegerHasLeadingZero(member_name); |
| 411 |
| 412 return Status::Success(); |
| 413 } |
| 414 |
| 415 Status JwkReader::GetOptionalBool(const std::string& member_name, |
| 416 bool* result, |
| 417 bool* member_exists) const { |
| 418 *member_exists = false; |
| 419 base::Value* value = NULL; |
| 420 if (!dict_->Get(member_name, &value)) |
| 421 return Status::Success(); |
| 422 |
| 423 if (!value->GetAsBoolean(result)) |
| 424 return Status::ErrorJwkPropertyWrongType(member_name, "boolean"); |
| 425 |
| 426 *member_exists = true; |
| 427 return Status::Success(); |
| 428 } |
| 429 |
| 430 Status JwkReader::GetAlg(std::string* alg, bool* has_alg) const { |
| 431 return GetOptionalString("alg", alg, has_alg); |
| 432 } |
| 433 |
| 434 Status JwkReader::VerifyAlg(const std::string& expected_alg) const { |
| 435 bool has_jwk_alg; |
| 436 std::string jwk_alg_value; |
| 437 Status status = GetAlg(&jwk_alg_value, &has_jwk_alg); |
| 438 if (status.IsError()) |
| 439 return status; |
| 440 |
| 441 if (has_jwk_alg && jwk_alg_value != expected_alg) |
| 442 return Status::ErrorJwkAlgorithmInconsistent(); |
| 443 |
| 444 return Status::Success(); |
| 445 } |
| 446 |
| 447 JwkWriter::JwkWriter(const std::string& algorithm, |
| 448 bool extractable, |
| 449 blink::WebCryptoKeyUsageMask usages, |
| 450 const std::string& kty) { |
| 451 dict_.SetString("alg", algorithm); |
| 452 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages)); |
| 453 dict_.SetBoolean("ext", extractable); |
| 454 dict_.SetString("kty", kty); |
| 455 } |
| 456 |
| 457 void JwkWriter::SetString(const std::string& member_name, |
| 458 const std::string& value) { |
| 459 dict_.SetString(member_name, value); |
| 460 } |
| 461 |
| 462 void JwkWriter::SetBytes(const std::string& member_name, |
| 463 const CryptoData& value) { |
| 464 dict_.SetString( |
| 465 member_name, |
| 466 Base64EncodeUrlSafe(base::StringPiece( |
| 467 reinterpret_cast<const char*>(value.bytes()), value.byte_length()))); |
| 468 } |
| 469 |
| 470 void JwkWriter::ToJson(std::vector<uint8_t>* utf8_bytes) const { |
| 471 std::string json; |
| 472 base::JSONWriter::Write(&dict_, &json); |
| 473 utf8_bytes->assign(json.begin(), json.end()); |
| 474 } |
| 475 |
| 479 Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, | 476 Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, |
| 480 bool expected_extractable, | 477 bool expected_extractable, |
| 481 blink::WebCryptoKeyUsageMask expected_usages, | 478 blink::WebCryptoKeyUsageMask expected_usages, |
| 482 std::vector<uint8_t>* raw_key_data, | 479 std::vector<uint8_t>* raw_key_data, |
| 483 scoped_ptr<base::DictionaryValue>* dict) { | 480 JwkReader* jwk) { |
| 484 if (!key_data.byte_length()) | 481 Status status = jwk->Init( |
| 485 return Status::ErrorImportEmptyKeyData(); | 482 key_data, expected_extractable, expected_usages, "oct", std::string()); |
| 486 | |
| 487 std::string kty; | |
| 488 Status status = ParseJwkCommon( | |
| 489 key_data, expected_extractable, expected_usages, &kty, dict); | |
| 490 if (status.IsError()) | 483 if (status.IsError()) |
| 491 return status; | 484 return status; |
| 492 | 485 |
| 493 if (kty != "oct") | |
| 494 return Status::ErrorJwkUnexpectedKty("oct"); | |
| 495 | |
| 496 std::string jwk_k_value; | 486 std::string jwk_k_value; |
| 497 status = GetJwkBytes(dict->get(), "k", &jwk_k_value); | 487 status = jwk->GetBytes("k", &jwk_k_value); |
| 498 if (status.IsError()) | 488 if (status.IsError()) |
| 499 return status; | 489 return status; |
| 500 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); | 490 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); |
| 501 | 491 |
| 502 return Status::Success(); | 492 return Status::Success(); |
| 503 } | 493 } |
| 504 | 494 |
| 505 } // namespace | |
| 506 | |
| 507 void WriteSecretKeyJwk(const CryptoData& raw_key_data, | 495 void WriteSecretKeyJwk(const CryptoData& raw_key_data, |
| 508 const std::string& algorithm, | 496 const std::string& algorithm, |
| 509 bool extractable, | 497 bool extractable, |
| 510 blink::WebCryptoKeyUsageMask usages, | 498 blink::WebCryptoKeyUsageMask usages, |
| 511 std::vector<uint8_t>* jwk_key_data) { | 499 std::vector<uint8_t>* jwk_key_data) { |
| 512 JwkWriter writer(algorithm, extractable, usages, "oct"); | 500 JwkWriter writer(algorithm, extractable, usages, "oct"); |
| 513 writer.SetBase64Encoded("k", raw_key_data); | 501 writer.SetBytes("k", raw_key_data); |
| 514 writer.ToBytes(jwk_key_data); | 502 writer.ToJson(jwk_key_data); |
| 515 } | 503 } |
| 516 | 504 |
| 517 Status ReadSecretKeyJwk(const CryptoData& key_data, | 505 Status ReadSecretKeyJwk(const CryptoData& key_data, |
| 518 const std::string& expected_algorithm, | 506 const std::string& expected_alg, |
| 519 bool expected_extractable, | 507 bool expected_extractable, |
| 520 blink::WebCryptoKeyUsageMask expected_usages, | 508 blink::WebCryptoKeyUsageMask expected_usages, |
| 521 std::vector<uint8_t>* raw_key_data) { | 509 std::vector<uint8_t>* raw_key_data) { |
| 522 scoped_ptr<base::DictionaryValue> dict; | 510 JwkReader jwk; |
| 523 Status status = ReadSecretKeyNoExpectedAlg( | 511 Status status = ReadSecretKeyNoExpectedAlg( |
| 524 key_data, expected_extractable, expected_usages, raw_key_data, &dict); | 512 key_data, expected_extractable, expected_usages, raw_key_data, &jwk); |
| 525 if (status.IsError()) | 513 if (status.IsError()) |
| 526 return status; | 514 return status; |
| 527 return VerifyAlg(dict.get(), expected_algorithm); | 515 return jwk.VerifyAlg(expected_alg); |
| 528 } | 516 } |
| 529 | 517 |
| 530 std::string MakeJwkAesAlgorithmName(const std::string& suffix, | 518 std::string MakeJwkAesAlgorithmName(const std::string& suffix, |
| 531 unsigned int keylen_bytes) { | 519 unsigned int keylen_bytes) { |
| 532 if (keylen_bytes == 16) | 520 if (keylen_bytes == 16) |
| 533 return std::string("A128") + suffix; | 521 return std::string("A128") + suffix; |
| 534 if (keylen_bytes == 24) | 522 if (keylen_bytes == 24) |
| 535 return std::string("A192") + suffix; | 523 return std::string("A192") + suffix; |
| 536 if (keylen_bytes == 32) | 524 if (keylen_bytes == 32) |
| 537 return std::string("A256") + suffix; | 525 return std::string("A256") + suffix; |
| 538 return std::string(); | 526 return std::string(); |
| 539 } | 527 } |
| 540 | 528 |
| 541 Status ReadAesSecretKeyJwk(const CryptoData& key_data, | 529 Status ReadAesSecretKeyJwk(const CryptoData& key_data, |
| 542 const std::string& algorithm_name_suffix, | 530 const std::string& algorithm_name_suffix, |
| 543 bool expected_extractable, | 531 bool expected_extractable, |
| 544 blink::WebCryptoKeyUsageMask expected_usages, | 532 blink::WebCryptoKeyUsageMask expected_usages, |
| 545 std::vector<uint8_t>* raw_key_data) { | 533 std::vector<uint8_t>* raw_key_data) { |
| 546 scoped_ptr<base::DictionaryValue> dict; | 534 JwkReader jwk; |
| 547 Status status = ReadSecretKeyNoExpectedAlg( | 535 Status status = ReadSecretKeyNoExpectedAlg( |
| 548 key_data, expected_extractable, expected_usages, raw_key_data, &dict); | 536 key_data, expected_extractable, expected_usages, raw_key_data, &jwk); |
| 549 if (status.IsError()) | 537 if (status.IsError()) |
| 550 return status; | 538 return status; |
| 551 | 539 |
| 552 bool has_jwk_alg; | 540 bool has_jwk_alg; |
| 553 std::string jwk_alg; | 541 std::string jwk_alg; |
| 554 status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); | 542 status = jwk.GetAlg(&jwk_alg, &has_jwk_alg); |
| 555 if (status.IsError()) | 543 if (status.IsError()) |
| 556 return status; | 544 return status; |
| 557 | 545 |
| 558 if (has_jwk_alg) { | 546 if (has_jwk_alg) { |
| 559 std::string expected_algorithm_name = | 547 std::string expected_algorithm_name = |
| 560 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); | 548 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); |
| 561 | 549 |
| 562 if (jwk_alg != expected_algorithm_name) { | 550 if (jwk_alg != expected_algorithm_name) { |
| 563 // Give a different error message if the key length was wrong. | 551 // Give a different error message if the key length was wrong. |
| 564 if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || | 552 if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || |
| 565 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || | 553 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || |
| 566 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { | 554 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { |
| 567 return Status::ErrorJwkIncorrectKeyLength(); | 555 return Status::ErrorJwkIncorrectKeyLength(); |
| 568 } | 556 } |
| 569 return Status::ErrorJwkAlgorithmInconsistent(); | 557 return Status::ErrorJwkAlgorithmInconsistent(); |
| 570 } | 558 } |
| 571 } | 559 } |
| 572 | 560 |
| 573 return Status::Success(); | 561 return Status::Success(); |
| 574 } | 562 } |
| 575 | 563 |
| 576 // Writes an RSA public key to a JWK dictionary | 564 // Writes an RSA public key to a JWK dictionary |
| 577 void WriteRsaPublicKeyJwk(const CryptoData& n, | 565 void WriteRsaPublicKeyJwk(const CryptoData& n, |
| 578 const CryptoData& e, | 566 const CryptoData& e, |
| 579 const std::string& algorithm, | 567 const std::string& algorithm, |
| 580 bool extractable, | 568 bool extractable, |
| 581 blink::WebCryptoKeyUsageMask usages, | 569 blink::WebCryptoKeyUsageMask usages, |
| 582 std::vector<uint8_t>* jwk_key_data) { | 570 std::vector<uint8_t>* jwk_key_data) { |
| 583 JwkWriter writer(algorithm, extractable, usages, "RSA"); | 571 JwkWriter writer(algorithm, extractable, usages, "RSA"); |
| 584 writer.SetBase64Encoded("n", n); | 572 writer.SetBytes("n", n); |
| 585 writer.SetBase64Encoded("e", e); | 573 writer.SetBytes("e", e); |
| 586 writer.ToBytes(jwk_key_data); | 574 writer.ToJson(jwk_key_data); |
| 587 } | 575 } |
| 588 | 576 |
| 589 // Writes an RSA private key to a JWK dictionary | 577 // Writes an RSA private key to a JWK dictionary |
| 590 void WriteRsaPrivateKeyJwk(const CryptoData& n, | 578 void WriteRsaPrivateKeyJwk(const CryptoData& n, |
| 591 const CryptoData& e, | 579 const CryptoData& e, |
| 592 const CryptoData& d, | 580 const CryptoData& d, |
| 593 const CryptoData& p, | 581 const CryptoData& p, |
| 594 const CryptoData& q, | 582 const CryptoData& q, |
| 595 const CryptoData& dp, | 583 const CryptoData& dp, |
| 596 const CryptoData& dq, | 584 const CryptoData& dq, |
| 597 const CryptoData& qi, | 585 const CryptoData& qi, |
| 598 const std::string& algorithm, | 586 const std::string& algorithm, |
| 599 bool extractable, | 587 bool extractable, |
| 600 blink::WebCryptoKeyUsageMask usages, | 588 blink::WebCryptoKeyUsageMask usages, |
| 601 std::vector<uint8_t>* jwk_key_data) { | 589 std::vector<uint8_t>* jwk_key_data) { |
| 602 JwkWriter writer(algorithm, extractable, usages, "RSA"); | 590 JwkWriter writer(algorithm, extractable, usages, "RSA"); |
| 603 | 591 |
| 604 writer.SetBase64Encoded("n", n); | 592 writer.SetBytes("n", n); |
| 605 writer.SetBase64Encoded("e", e); | 593 writer.SetBytes("e", e); |
| 606 writer.SetBase64Encoded("d", d); | 594 writer.SetBytes("d", d); |
| 607 // Although these are "optional" in the JWA, WebCrypto spec requires them to | 595 // Although these are "optional" in the JWA, WebCrypto spec requires them to |
| 608 // be emitted. | 596 // be emitted. |
| 609 writer.SetBase64Encoded("p", p); | 597 writer.SetBytes("p", p); |
| 610 writer.SetBase64Encoded("q", q); | 598 writer.SetBytes("q", q); |
| 611 writer.SetBase64Encoded("dp", dp); | 599 writer.SetBytes("dp", dp); |
| 612 writer.SetBase64Encoded("dq", dq); | 600 writer.SetBytes("dq", dq); |
| 613 writer.SetBase64Encoded("qi", qi); | 601 writer.SetBytes("qi", qi); |
| 614 writer.ToBytes(jwk_key_data); | 602 writer.ToJson(jwk_key_data); |
| 615 } | 603 } |
| 616 | 604 |
| 617 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { | 605 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { |
| 618 } | 606 } |
| 619 | 607 |
| 620 JwkRsaInfo::~JwkRsaInfo() { | 608 JwkRsaInfo::~JwkRsaInfo() { |
| 621 } | 609 } |
| 622 | 610 |
| 623 Status ReadRsaKeyJwk(const CryptoData& key_data, | 611 Status ReadRsaKeyJwk(const CryptoData& key_data, |
| 624 const std::string& expected_algorithm, | 612 const std::string& expected_alg, |
| 625 bool expected_extractable, | 613 bool expected_extractable, |
| 626 blink::WebCryptoKeyUsageMask expected_usages, | 614 blink::WebCryptoKeyUsageMask expected_usages, |
| 627 JwkRsaInfo* result) { | 615 JwkRsaInfo* result) { |
| 628 if (!key_data.byte_length()) | 616 JwkReader jwk; |
| 629 return Status::ErrorImportEmptyKeyData(); | 617 Status status = jwk.Init( |
| 630 | 618 key_data, expected_extractable, expected_usages, "RSA", expected_alg); |
| 631 scoped_ptr<base::DictionaryValue> dict; | |
| 632 std::string kty; | |
| 633 Status status = ParseJwkCommon( | |
| 634 key_data, expected_extractable, expected_usages, &kty, &dict); | |
| 635 if (status.IsError()) | 619 if (status.IsError()) |
| 636 return status; | 620 return status; |
| 637 | 621 |
| 638 status = VerifyAlg(dict.get(), expected_algorithm); | |
| 639 if (status.IsError()) | |
| 640 return status; | |
| 641 | |
| 642 if (kty != "RSA") | |
| 643 return Status::ErrorJwkUnexpectedKty("RSA"); | |
| 644 | |
| 645 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | 622 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry |
| 646 // in the JWK, while an RSA private key must have those, plus at least a "d" | 623 // in the JWK, while an RSA private key must have those, plus at least a "d" |
| 647 // (private exponent) entry. | 624 // (private exponent) entry. |
| 648 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | 625 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| 649 // section 6.3. | 626 // section 6.3. |
| 650 status = GetJwkBigInteger(dict.get(), "n", &result->n); | 627 status = jwk.GetBigInteger("n", &result->n); |
| 651 if (status.IsError()) | 628 if (status.IsError()) |
| 652 return status; | 629 return status; |
| 653 status = GetJwkBigInteger(dict.get(), "e", &result->e); | 630 status = jwk.GetBigInteger("e", &result->e); |
| 654 if (status.IsError()) | 631 if (status.IsError()) |
| 655 return status; | 632 return status; |
| 656 | 633 |
| 657 result->is_private_key = dict->HasKey("d"); | 634 result->is_private_key = jwk.HasMember("d"); |
| 658 if (!result->is_private_key) | 635 if (!result->is_private_key) |
| 659 return Status::Success(); | 636 return Status::Success(); |
| 660 | 637 |
| 661 status = GetJwkBigInteger(dict.get(), "d", &result->d); | 638 status = jwk.GetBigInteger("d", &result->d); |
| 662 if (status.IsError()) | 639 if (status.IsError()) |
| 663 return status; | 640 return status; |
| 664 | 641 |
| 665 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA | 642 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA |
| 666 // spec. However they are required by Chromium's WebCrypto implementation. | 643 // spec. However they are required by Chromium's WebCrypto implementation. |
| 667 | 644 |
| 668 status = GetJwkBigInteger(dict.get(), "p", &result->p); | 645 status = jwk.GetBigInteger("p", &result->p); |
| 669 if (status.IsError()) | 646 if (status.IsError()) |
| 670 return status; | 647 return status; |
| 671 | 648 |
| 672 status = GetJwkBigInteger(dict.get(), "q", &result->q); | 649 status = jwk.GetBigInteger("q", &result->q); |
| 673 if (status.IsError()) | 650 if (status.IsError()) |
| 674 return status; | 651 return status; |
| 675 | 652 |
| 676 status = GetJwkBigInteger(dict.get(), "dp", &result->dp); | 653 status = jwk.GetBigInteger("dp", &result->dp); |
| 677 if (status.IsError()) | 654 if (status.IsError()) |
| 678 return status; | 655 return status; |
| 679 | 656 |
| 680 status = GetJwkBigInteger(dict.get(), "dq", &result->dq); | 657 status = jwk.GetBigInteger("dq", &result->dq); |
| 681 if (status.IsError()) | 658 if (status.IsError()) |
| 682 return status; | 659 return status; |
| 683 | 660 |
| 684 status = GetJwkBigInteger(dict.get(), "qi", &result->qi); | 661 status = jwk.GetBigInteger("qi", &result->qi); |
| 685 if (status.IsError()) | 662 if (status.IsError()) |
| 686 return status; | 663 return status; |
| 687 | 664 |
| 688 return Status::Success(); | 665 return Status::Success(); |
| 689 } | 666 } |
| 690 | 667 |
| 691 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { | 668 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { |
| 692 switch (hash) { | 669 switch (hash) { |
| 693 case blink::WebCryptoAlgorithmIdSha1: | 670 case blink::WebCryptoAlgorithmIdSha1: |
| 694 return "HS1"; | 671 return "HS1"; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 725 | 702 |
| 726 std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) { | 703 std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) { |
| 727 const base::StringPiece string_piece( | 704 const base::StringPiece string_piece( |
| 728 reinterpret_cast<const char*>(vector_as_array(&input)), input.size()); | 705 reinterpret_cast<const char*>(vector_as_array(&input)), input.size()); |
| 729 return Base64EncodeUrlSafe(string_piece); | 706 return Base64EncodeUrlSafe(string_piece); |
| 730 } | 707 } |
| 731 | 708 |
| 732 } // namespace webcrypto | 709 } // namespace webcrypto |
| 733 | 710 |
| 734 } // namespace content | 711 } // namespace content |
| OLD | NEW |