Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / components / webcrypto / jwk.cc
blob98a8b1b85f40efe0853fa3f4f0c5b3c439ea24d5
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
3 // found in the LICENSE file.
5 #include "components/webcrypto/jwk.h"
7 #include <set>
9 #include "base/base64.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/stringprintf.h"
15 #include "components/webcrypto/crypto_data.h"
16 #include "components/webcrypto/status.h"
17 #include "components/webcrypto/webcrypto_util.h"
19 // TODO(eroman): The algorithm-specific logic in this file for AES and RSA
20 // should be moved into the corresponding AlgorithmImplementation file. It
21 // exists in this file to avoid duplication between OpenSSL and NSS
22 // implementations.
24 // JSON Web Key Format (JWK) is defined by:
25 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key
27 // A JWK is a simple JSON dictionary with the following members:
28 // - "kty" (Key Type) Parameter, REQUIRED
29 // - <kty-specific parameters, see below>, REQUIRED
30 // - "use" (Key Use) OPTIONAL
31 // - "key_ops" (Key Operations) OPTIONAL
32 // - "alg" (Algorithm) OPTIONAL
33 // - "ext" (Key Exportability), OPTIONAL
34 // (all other entries are ignored)
36 // The <kty-specific parameters> are defined by the JWA spec:
37 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms
39 namespace webcrypto {
41 namespace {
43 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
44 const blink::WebCryptoKeyUsageMask kJwkEncUsage =
45 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
46 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey;
47 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
48 const blink::WebCryptoKeyUsageMask kJwkSigUsage =
49 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
51 // Checks that the "ext" member of the JWK is consistent with
52 // "expected_extractable".
53 Status VerifyExt(const JwkReader& jwk, bool expected_extractable) {
54 // JWK "ext" (optional) --> extractable parameter
55 bool jwk_ext_value = false;
56 bool has_jwk_ext;
57 Status status = jwk.GetOptionalBool("ext", &jwk_ext_value, &has_jwk_ext);
58 if (status.IsError())
59 return status;
60 if (has_jwk_ext && expected_extractable && !jwk_ext_value)
61 return Status::ErrorJwkExtInconsistent();
62 return Status::Success();
65 struct JwkToWebCryptoUsageMapping {
66 const char* const jwk_key_op;
67 const blink::WebCryptoKeyUsage webcrypto_usage;
70 // Keep this ordered the same as WebCrypto's "recognized key usage
71 // values". While this is not required for spec compliance,
72 // it makes the ordering of key_ops match that of WebCrypto's Key.usages.
73 const JwkToWebCryptoUsageMapping kJwkWebCryptoUsageMap[] = {
74 {"encrypt", blink::WebCryptoKeyUsageEncrypt},
75 {"decrypt", blink::WebCryptoKeyUsageDecrypt},
76 {"sign", blink::WebCryptoKeyUsageSign},
77 {"verify", blink::WebCryptoKeyUsageVerify},
78 {"deriveKey", blink::WebCryptoKeyUsageDeriveKey},
79 {"deriveBits", blink::WebCryptoKeyUsageDeriveBits},
80 {"wrapKey", blink::WebCryptoKeyUsageWrapKey},
81 {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}};
83 bool JwkKeyOpToWebCryptoUsage(const std::string& key_op,
84 blink::WebCryptoKeyUsage* usage) {
85 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
86 if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) {
87 *usage = kJwkWebCryptoUsageMap[i].webcrypto_usage;
88 return true;
91 return false;
94 // Creates a JWK key_ops list from a Web Crypto usage mask.
95 scoped_ptr<base::ListValue> CreateJwkKeyOpsFromWebCryptoUsages(
96 blink::WebCryptoKeyUsageMask usages) {
97 scoped_ptr<base::ListValue> jwk_key_ops(new base::ListValue());
98 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
99 if (usages & kJwkWebCryptoUsageMap[i].webcrypto_usage)
100 jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op);
102 return jwk_key_ops.Pass();
105 // Composes a Web Crypto usage mask from an array of JWK key_ops values.
106 Status GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue* key_ops,
107 blink::WebCryptoKeyUsageMask* usages) {
108 // This set keeps track of all unrecognized key_ops values.
109 std::set<std::string> unrecognized_usages;
111 *usages = 0;
112 for (size_t i = 0; i < key_ops->GetSize(); ++i) {
113 std::string key_op;
114 if (!key_ops->GetString(i, &key_op)) {
115 return Status::ErrorJwkMemberWrongType(
116 base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string");
119 blink::WebCryptoKeyUsage usage;
120 if (JwkKeyOpToWebCryptoUsage(key_op, &usage)) {
121 // Ensure there are no duplicate usages.
122 if (*usages & usage)
123 return Status::ErrorJwkDuplicateKeyOps();
124 *usages |= usage;
127 // Reaching here means the usage was unrecognized. Such usages are skipped
128 // over, however they are kept track of in a set to ensure there were no
129 // duplicates.
130 if (!unrecognized_usages.insert(key_op).second)
131 return Status::ErrorJwkDuplicateKeyOps();
133 return Status::Success();
136 // Checks that the usages ("use" and "key_ops") of the JWK is consistent with
137 // "expected_usages".
138 Status VerifyUsages(const JwkReader& jwk,
139 blink::WebCryptoKeyUsageMask expected_usages) {
140 // JWK "key_ops" (optional) --> usages parameter
141 base::ListValue* jwk_key_ops_value = NULL;
142 bool has_jwk_key_ops;
143 Status status =
144 jwk.GetOptionalList("key_ops", &jwk_key_ops_value, &has_jwk_key_ops);
145 if (status.IsError())
146 return status;
147 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0;
148 if (has_jwk_key_ops) {
149 status =
150 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask);
151 if (status.IsError())
152 return status;
153 // The input usages must be a subset of jwk_key_ops_mask.
154 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usages))
155 return Status::ErrorJwkKeyopsInconsistent();
158 // JWK "use" (optional) --> usages parameter
159 std::string jwk_use_value;
160 bool has_jwk_use;
161 status = jwk.GetOptionalString("use", &jwk_use_value, &has_jwk_use);
162 if (status.IsError())
163 return status;
164 blink::WebCryptoKeyUsageMask jwk_use_mask = 0;
165 if (has_jwk_use) {
166 if (jwk_use_value == "enc")
167 jwk_use_mask = kJwkEncUsage;
168 else if (jwk_use_value == "sig")
169 jwk_use_mask = kJwkSigUsage;
170 else
171 return Status::ErrorJwkUnrecognizedUse();
172 // The input usages must be a subset of jwk_use_mask.
173 if (!ContainsKeyUsages(jwk_use_mask, expected_usages))
174 return Status::ErrorJwkUseInconsistent();
177 // If both 'key_ops' and 'use' are present, ensure they are consistent.
178 if (has_jwk_key_ops && has_jwk_use &&
179 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask))
180 return Status::ErrorJwkUseAndKeyopsInconsistent();
182 return Status::Success();
185 } // namespace
187 JwkReader::JwkReader() {
190 JwkReader::~JwkReader() {
193 Status JwkReader::Init(const CryptoData& bytes,
194 bool expected_extractable,
195 blink::WebCryptoKeyUsageMask expected_usages,
196 const std::string& expected_kty,
197 const std::string& expected_alg) {
198 // Parse the incoming JWK JSON.
199 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()),
200 bytes.byte_length());
202 scoped_ptr<base::Value> value = base::JSONReader::Read(json_string);
203 base::DictionaryValue* dict_value = NULL;
205 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
206 return Status::ErrorJwkNotDictionary();
208 // Release |value|, as ownership will be transferred to |dict| via
209 // |dict_value|, which points to the same object as |value|.
210 ignore_result(value.release());
211 dict_.reset(dict_value);
213 // JWK "kty". Exit early if this required JWK parameter is missing.
214 std::string kty;
215 Status status = GetString("kty", &kty);
216 if (status.IsError())
217 return status;
219 if (kty != expected_kty)
220 return Status::ErrorJwkUnexpectedKty(expected_kty);
222 status = VerifyExt(*this, expected_extractable);
223 if (status.IsError())
224 return status;
226 status = VerifyUsages(*this, expected_usages);
227 if (status.IsError())
228 return status;
230 // Verify the algorithm if an expectation was provided.
231 if (!expected_alg.empty()) {
232 status = VerifyAlg(expected_alg);
233 if (status.IsError())
234 return status;
237 return Status::Success();
240 bool JwkReader::HasMember(const std::string& member_name) const {
241 return dict_->HasKey(member_name);
244 Status JwkReader::GetString(const std::string& member_name,
245 std::string* result) const {
246 base::Value* value = NULL;
247 if (!dict_->Get(member_name, &value))
248 return Status::ErrorJwkMemberMissing(member_name);
249 if (!value->GetAsString(result))
250 return Status::ErrorJwkMemberWrongType(member_name, "string");
251 return Status::Success();
254 Status JwkReader::GetOptionalString(const std::string& member_name,
255 std::string* result,
256 bool* member_exists) const {
257 *member_exists = false;
258 base::Value* value = NULL;
259 if (!dict_->Get(member_name, &value))
260 return Status::Success();
262 if (!value->GetAsString(result))
263 return Status::ErrorJwkMemberWrongType(member_name, "string");
265 *member_exists = true;
266 return Status::Success();
269 Status JwkReader::GetOptionalList(const std::string& member_name,
270 base::ListValue** result,
271 bool* member_exists) const {
272 *member_exists = false;
273 base::Value* value = NULL;
274 if (!dict_->Get(member_name, &value))
275 return Status::Success();
277 if (!value->GetAsList(result))
278 return Status::ErrorJwkMemberWrongType(member_name, "list");
280 *member_exists = true;
281 return Status::Success();
284 Status JwkReader::GetBytes(const std::string& member_name,
285 std::string* result) const {
286 std::string base64_string;
287 Status status = GetString(member_name, &base64_string);
288 if (status.IsError())
289 return status;
291 if (!Base64DecodeUrlSafe(base64_string, result))
292 return Status::ErrorJwkBase64Decode(member_name);
294 return Status::Success();
297 Status JwkReader::GetBigInteger(const std::string& member_name,
298 std::string* result) const {
299 Status status = GetBytes(member_name, result);
300 if (status.IsError())
301 return status;
303 if (result->empty())
304 return Status::ErrorJwkEmptyBigInteger(member_name);
306 // The JWA spec says that "The octet sequence MUST utilize the minimum number
307 // of octets to represent the value." This means there shouldn't be any
308 // leading zeros.
309 if (result->size() > 1 && (*result)[0] == 0)
310 return Status::ErrorJwkBigIntegerHasLeadingZero(member_name);
312 return Status::Success();
315 Status JwkReader::GetOptionalBool(const std::string& member_name,
316 bool* result,
317 bool* member_exists) const {
318 *member_exists = false;
319 base::Value* value = NULL;
320 if (!dict_->Get(member_name, &value))
321 return Status::Success();
323 if (!value->GetAsBoolean(result))
324 return Status::ErrorJwkMemberWrongType(member_name, "boolean");
326 *member_exists = true;
327 return Status::Success();
330 Status JwkReader::GetAlg(std::string* alg, bool* has_alg) const {
331 return GetOptionalString("alg", alg, has_alg);
334 Status JwkReader::VerifyAlg(const std::string& expected_alg) const {
335 bool has_jwk_alg;
336 std::string jwk_alg_value;
337 Status status = GetAlg(&jwk_alg_value, &has_jwk_alg);
338 if (status.IsError())
339 return status;
341 if (has_jwk_alg && jwk_alg_value != expected_alg)
342 return Status::ErrorJwkAlgorithmInconsistent();
344 return Status::Success();
347 JwkWriter::JwkWriter(const std::string& algorithm,
348 bool extractable,
349 blink::WebCryptoKeyUsageMask usages,
350 const std::string& kty) {
351 if (!algorithm.empty())
352 dict_.SetString("alg", algorithm);
353 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages).release());
354 dict_.SetBoolean("ext", extractable);
355 dict_.SetString("kty", kty);
358 void JwkWriter::SetString(const std::string& member_name,
359 const std::string& value) {
360 dict_.SetString(member_name, value);
363 void JwkWriter::SetBytes(const std::string& member_name,
364 const CryptoData& value) {
365 dict_.SetString(member_name, Base64EncodeUrlSafe(base::StringPiece(
366 reinterpret_cast<const char*>(value.bytes()),
367 value.byte_length())));
370 void JwkWriter::ToJson(std::vector<uint8_t>* utf8_bytes) const {
371 std::string json;
372 base::JSONWriter::Write(dict_, &json);
373 utf8_bytes->assign(json.begin(), json.end());
376 Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data,
377 bool expected_extractable,
378 blink::WebCryptoKeyUsageMask expected_usages,
379 std::vector<uint8_t>* raw_key_data,
380 JwkReader* jwk) {
381 Status status = jwk->Init(key_data, expected_extractable, expected_usages,
382 "oct", std::string());
383 if (status.IsError())
384 return status;
386 std::string jwk_k_value;
387 status = jwk->GetBytes("k", &jwk_k_value);
388 if (status.IsError())
389 return status;
390 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end());
392 return Status::Success();
395 void WriteSecretKeyJwk(const CryptoData& raw_key_data,
396 const std::string& algorithm,
397 bool extractable,
398 blink::WebCryptoKeyUsageMask usages,
399 std::vector<uint8_t>* jwk_key_data) {
400 JwkWriter writer(algorithm, extractable, usages, "oct");
401 writer.SetBytes("k", raw_key_data);
402 writer.ToJson(jwk_key_data);
405 Status ReadSecretKeyJwk(const CryptoData& key_data,
406 const std::string& expected_alg,
407 bool expected_extractable,
408 blink::WebCryptoKeyUsageMask expected_usages,
409 std::vector<uint8_t>* raw_key_data) {
410 JwkReader jwk;
411 Status status = ReadSecretKeyNoExpectedAlg(
412 key_data, expected_extractable, expected_usages, raw_key_data, &jwk);
413 if (status.IsError())
414 return status;
415 return jwk.VerifyAlg(expected_alg);
418 std::string MakeJwkAesAlgorithmName(const std::string& suffix,
419 size_t keylen_bytes) {
420 if (keylen_bytes == 16)
421 return std::string("A128") + suffix;
422 if (keylen_bytes == 24)
423 return std::string("A192") + suffix;
424 if (keylen_bytes == 32)
425 return std::string("A256") + suffix;
426 return std::string();
429 Status ReadAesSecretKeyJwk(const CryptoData& key_data,
430 const std::string& algorithm_name_suffix,
431 bool expected_extractable,
432 blink::WebCryptoKeyUsageMask expected_usages,
433 std::vector<uint8_t>* raw_key_data) {
434 JwkReader jwk;
435 Status status = ReadSecretKeyNoExpectedAlg(
436 key_data, expected_extractable, expected_usages, raw_key_data, &jwk);
437 if (status.IsError())
438 return status;
440 bool has_jwk_alg;
441 std::string jwk_alg;
442 status = jwk.GetAlg(&jwk_alg, &has_jwk_alg);
443 if (status.IsError())
444 return status;
446 if (has_jwk_alg) {
447 std::string expected_algorithm_name =
448 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size());
450 if (jwk_alg != expected_algorithm_name) {
451 // Give a different error message if the key length was wrong.
452 if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) ||
453 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) ||
454 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) {
455 return Status::ErrorJwkIncorrectKeyLength();
457 return Status::ErrorJwkAlgorithmInconsistent();
461 return Status::Success();
464 // Writes an RSA public key to a JWK dictionary
465 void WriteRsaPublicKeyJwk(const CryptoData& n,
466 const CryptoData& e,
467 const std::string& algorithm,
468 bool extractable,
469 blink::WebCryptoKeyUsageMask usages,
470 std::vector<uint8_t>* jwk_key_data) {
471 JwkWriter writer(algorithm, extractable, usages, "RSA");
472 writer.SetBytes("n", n);
473 writer.SetBytes("e", e);
474 writer.ToJson(jwk_key_data);
477 // Writes an RSA private key to a JWK dictionary
478 void WriteRsaPrivateKeyJwk(const CryptoData& n,
479 const CryptoData& e,
480 const CryptoData& d,
481 const CryptoData& p,
482 const CryptoData& q,
483 const CryptoData& dp,
484 const CryptoData& dq,
485 const CryptoData& qi,
486 const std::string& algorithm,
487 bool extractable,
488 blink::WebCryptoKeyUsageMask usages,
489 std::vector<uint8_t>* jwk_key_data) {
490 JwkWriter writer(algorithm, extractable, usages, "RSA");
492 writer.SetBytes("n", n);
493 writer.SetBytes("e", e);
494 writer.SetBytes("d", d);
495 // Although these are "optional" in the JWA, WebCrypto spec requires them to
496 // be emitted.
497 writer.SetBytes("p", p);
498 writer.SetBytes("q", q);
499 writer.SetBytes("dp", dp);
500 writer.SetBytes("dq", dq);
501 writer.SetBytes("qi", qi);
502 writer.ToJson(jwk_key_data);
505 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) {
508 JwkRsaInfo::~JwkRsaInfo() {
511 Status ReadRsaKeyJwk(const CryptoData& key_data,
512 const std::string& expected_alg,
513 bool expected_extractable,
514 blink::WebCryptoKeyUsageMask expected_usages,
515 JwkRsaInfo* result) {
516 JwkReader jwk;
517 Status status = jwk.Init(key_data, expected_extractable, expected_usages,
518 "RSA", expected_alg);
519 if (status.IsError())
520 return status;
522 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
523 // in the JWK, while an RSA private key must have those, plus at least a "d"
524 // (private exponent) entry.
525 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
526 // section 6.3.
527 status = jwk.GetBigInteger("n", &result->n);
528 if (status.IsError())
529 return status;
530 status = jwk.GetBigInteger("e", &result->e);
531 if (status.IsError())
532 return status;
534 result->is_private_key = jwk.HasMember("d");
535 if (!result->is_private_key)
536 return Status::Success();
538 status = jwk.GetBigInteger("d", &result->d);
539 if (status.IsError())
540 return status;
542 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
543 // spec. However they are required by Chromium's WebCrypto implementation.
545 status = jwk.GetBigInteger("p", &result->p);
546 if (status.IsError())
547 return status;
549 status = jwk.GetBigInteger("q", &result->q);
550 if (status.IsError())
551 return status;
553 status = jwk.GetBigInteger("dp", &result->dp);
554 if (status.IsError())
555 return status;
557 status = jwk.GetBigInteger("dq", &result->dq);
558 if (status.IsError())
559 return status;
561 status = jwk.GetBigInteger("qi", &result->qi);
562 if (status.IsError())
563 return status;
565 return Status::Success();
568 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) {
569 switch (hash) {
570 case blink::WebCryptoAlgorithmIdSha1:
571 return "HS1";
572 case blink::WebCryptoAlgorithmIdSha256:
573 return "HS256";
574 case blink::WebCryptoAlgorithmIdSha384:
575 return "HS384";
576 case blink::WebCryptoAlgorithmIdSha512:
577 return "HS512";
578 default:
579 return NULL;
583 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
584 // The JSON web signature spec specifically says that padding is omitted.
585 if (input.find_first_of("+/=") != std::string::npos)
586 return false;
588 std::string base64_encoded_text(input);
589 std::replace(base64_encoded_text.begin(), base64_encoded_text.end(), '-',
590 '+');
591 std::replace(base64_encoded_text.begin(), base64_encoded_text.end(), '_',
592 '/');
593 base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '=');
594 return base::Base64Decode(base64_encoded_text, output);
597 std::string Base64EncodeUrlSafe(const base::StringPiece& input) {
598 std::string output;
599 base::Base64Encode(input, &output);
600 std::replace(output.begin(), output.end(), '+', '-');
601 std::replace(output.begin(), output.end(), '/', '_');
602 output.erase(std::remove(output.begin(), output.end(), '='), output.end());
603 return output;
606 std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) {
607 const base::StringPiece string_piece(
608 reinterpret_cast<const char*>(vector_as_array(&input)), input.size());
609 return Base64EncodeUrlSafe(string_piece);
612 Status GetWebCryptoUsagesFromJwkKeyOpsForTest(
613 const base::ListValue* key_ops,
614 blink::WebCryptoKeyUsageMask* usages) {
615 return GetWebCryptoUsagesFromJwkKeyOps(key_ops, usages);
618 } // namespace webcrypto