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

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

Issue 141853006: [webcrypto] Validate JWK import of AES keys: key length must match algorithm. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | content/renderer/webcrypto/webcrypto_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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 "content/renderer/webcrypto/webcrypto_impl.h" 5 #include "content/renderer/webcrypto/webcrypto_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <functional> 8 #include <functional>
9 #include <map> 9 #include <map>
10 #include "base/json/json_reader.h" 10 #include "base/json/json_reader.h"
(...skipping 20 matching lines...) Expand all
31 } 31 }
32 32
33 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) { 33 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) {
34 // TODO(padolph): include all other asymmetric algorithms once they are 34 // TODO(padolph): include all other asymmetric algorithms once they are
35 // defined, e.g. EC and DH. 35 // defined, e.g. EC and DH.
36 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || 36 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
37 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || 37 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
38 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep); 38 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep);
39 } 39 }
40 40
41 // Binds a specific key length value to a compatible factory function. 41 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
42 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncWithOneShortArg)(
43 unsigned short);
44 template <AlgFactoryFuncWithOneShortArg func, unsigned short key_length>
45 blink::WebCryptoAlgorithm BindAlgFactoryWithKeyLen() {
46 return func(key_length);
47 }
48 42
49 // Binds a WebCryptoAlgorithmId value to a compatible factory function. 43 class JwkAlgorithmInfo {
Ryan Sleevi 2014/01/31 20:55:59 Can we not avoid having to create this class by ma
eroman 2014/01/31 21:02:50 Not sure I follow, can u explain further?
Ryan Sleevi 2014/01/31 21:15:09 Well, I was thinking you could smuggle required_ke
eroman 2014/01/31 21:51:24 I am open to moving more JWK to the blink side, so
50 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncWithWebCryptoAlgIdArg)(
51 blink::WebCryptoAlgorithmId);
52 template <AlgFactoryFuncWithWebCryptoAlgIdArg func,
53 blink::WebCryptoAlgorithmId algorithm_id>
54 blink::WebCryptoAlgorithm BindAlgFactoryAlgorithmId() {
55 return func(algorithm_id);
56 }
57
58 // Defines a map between a JWK 'alg' string ID and a corresponding Web Crypto
59 // factory function.
60 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncNoArgs)();
61 typedef std::map<std::string, AlgFactoryFuncNoArgs> JwkAlgFactoryMap;
62
63 class JwkAlgorithmFactoryMap {
64 public: 44 public:
65 JwkAlgorithmFactoryMap() { 45 JwkAlgorithmInfo() :
66 map_["HS256"] = 46 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT),
67 &BindAlgFactoryAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, 47 creation_func_(NULL) {
68 blink::WebCryptoAlgorithmIdSha256>;
69 map_["HS384"] =
70 &BindAlgFactoryAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
71 blink::WebCryptoAlgorithmIdSha384>;
72 map_["HS512"] =
73 &BindAlgFactoryAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
74 blink::WebCryptoAlgorithmIdSha512>;
75 map_["RS256"] =
76 &BindAlgFactoryAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
77 blink::WebCryptoAlgorithmIdSha256>;
78 map_["RS384"] =
79 &BindAlgFactoryAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
80 blink::WebCryptoAlgorithmIdSha384>;
81 map_["RS512"] =
82 &BindAlgFactoryAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
83 blink::WebCryptoAlgorithmIdSha512>;
84 map_["RSA1_5"] =
85 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
86 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>;
87 map_["RSA-OAEP"] =
88 &BindAlgFactoryAlgorithmId<webcrypto::CreateRsaOaepAlgorithm,
89 blink::WebCryptoAlgorithmIdSha1>;
90 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet
91 map_["A128KW"] = &blink::WebCryptoAlgorithm::createNull;
92 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet
93 map_["A256KW"] = &blink::WebCryptoAlgorithm::createNull;
94 map_["A128GCM"] =
95 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
96 blink::WebCryptoAlgorithmIdAesGcm>;
97 map_["A256GCM"] =
98 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
99 blink::WebCryptoAlgorithmIdAesGcm>;
100 map_["A128CBC"] =
101 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
102 blink::WebCryptoAlgorithmIdAesCbc>;
103 map_["A192CBC"] =
104 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
105 blink::WebCryptoAlgorithmIdAesCbc>;
106 map_["A256CBC"] =
107 &BindAlgFactoryAlgorithmId<webcrypto::CreateAlgorithm,
108 blink::WebCryptoAlgorithmIdAesCbc>;
109 } 48 }
110 49
111 blink::WebCryptoAlgorithm CreateAlgorithmFromName(const std::string& alg_id) 50 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
112 const { 51 : required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT),
113 const JwkAlgFactoryMap::const_iterator pos = map_.find(alg_id); 52 creation_func_(algorithm_creation_func) {
114 if (pos == map_.end()) 53 }
115 return blink::WebCryptoAlgorithm::createNull(); 54
116 return pos->second(); 55 JwkAlgorithmInfo(unsigned int required_key_length_bits,
56 AlgorithmCreationFunc algorithm_creation_func)
57 : required_key_length_bytes_(required_key_length_bits / 8),
58 creation_func_(algorithm_creation_func) {
59 DCHECK((required_key_length_bits % 8) == 0);
60 }
61
62 bool CreateAlgorithm(blink::WebCryptoAlgorithm* algorithm) const {
63 *algorithm = creation_func_();
64 return !algorithm->isNull();
65 }
66
67 bool IsInvalidKeyByteLength(size_t byte_length) const {
68 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT)
69 return false;
70 return required_key_length_bytes_ != byte_length;
Ryan Sleevi 2014/01/31 21:15:09 nit: delete extra " "
117 } 71 }
118 72
119 private: 73 private:
120 JwkAlgFactoryMap map_; 74 enum {NO_KEY_SIZE_REQUIREMENT = UINT_MAX};
75
76 // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT.
77 unsigned int required_key_length_bytes_;
78
79 AlgorithmCreationFunc creation_func_;
Ryan Sleevi 2014/01/31 21:15:09 pedantic nit: It seems a little structurally clean
121 }; 80 };
122 81
123 base::LazyInstance<JwkAlgorithmFactoryMap> jwk_alg_factory = 82 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap;
83
84 class JwkAlgorithmRegistry {
85 public:
86 JwkAlgorithmRegistry() {
87 // TODO(eroman):
88 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20
89 // says HMAC with SHA-2 should have a key size at least as large as the
90 // hash output.
91 alg_to_info_["HS256"] = JwkAlgorithmInfo(
92 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
93 blink::WebCryptoAlgorithmIdSha256>);
94 alg_to_info_["HS384"] = JwkAlgorithmInfo(
95 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
96 blink::WebCryptoAlgorithmIdSha384>);
97 alg_to_info_["HS512"] = JwkAlgorithmInfo(
98 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
99 blink::WebCryptoAlgorithmIdSha512>);
100 alg_to_info_["RS256"] = JwkAlgorithmInfo(
101 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
102 blink::WebCryptoAlgorithmIdSha256>);
103 alg_to_info_["RS384"] = JwkAlgorithmInfo(
104 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
105 blink::WebCryptoAlgorithmIdSha384>);
106 alg_to_info_["RS512"] = JwkAlgorithmInfo(
107 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
108 blink::WebCryptoAlgorithmIdSha512>);
109 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo(
110 &BindAlgorithmId<webcrypto::CreateAlgorithm,
111 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>);
112 alg_to_info_["RSA-OAEP"] = JwkAlgorithmInfo(
113 &BindAlgorithmId<webcrypto::CreateRsaOaepAlgorithm,
114 blink::WebCryptoAlgorithmIdSha1>);
115 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet
116 alg_to_info_["A128KW"] =
117 JwkAlgorithmInfo(128, &blink::WebCryptoAlgorithm::createNull);
118 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet
119 alg_to_info_["A256KW"] =
120 JwkAlgorithmInfo(256, &blink::WebCryptoAlgorithm::createNull);
121 alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
122 128,
123 &BindAlgorithmId<webcrypto::CreateAlgorithm,
124 blink::WebCryptoAlgorithmIdAesGcm>);
125 alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
126 256,
127 &BindAlgorithmId<webcrypto::CreateAlgorithm,
128 blink::WebCryptoAlgorithmIdAesGcm>);
129 alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
130 128,
131 &BindAlgorithmId<webcrypto::CreateAlgorithm,
132 blink::WebCryptoAlgorithmIdAesCbc>);
133 alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
134 192,
135 &BindAlgorithmId<webcrypto::CreateAlgorithm,
136 blink::WebCryptoAlgorithmIdAesCbc>);
137 alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
138 256,
139 &BindAlgorithmId<webcrypto::CreateAlgorithm,
140 blink::WebCryptoAlgorithmIdAesCbc>);
141 }
142
143 // Returns NULL if the algorithm name was not registered.
144 const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const {
145 const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg);
146 if (pos == alg_to_info_.end())
147 return NULL;
148 return &pos->second;
149 }
150
151 private:
152 // Binds a WebCryptoAlgorithmId value to a compatible factory function.
153 typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)(
154 blink::WebCryptoAlgorithmId);
155 template <FuncWithWebCryptoAlgIdArg func,
156 blink::WebCryptoAlgorithmId algorithm_id>
157 static blink::WebCryptoAlgorithm BindAlgorithmId() {
158 return func(algorithm_id);
159 }
160
161 JwkAlgorithmInfoMap alg_to_info_;
162 };
163
164 base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry =
124 LAZY_INSTANCE_INITIALIZER; 165 LAZY_INSTANCE_INITIALIZER;
125 166
126 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, 167 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1,
127 const blink::WebCryptoAlgorithm& alg2) { 168 const blink::WebCryptoAlgorithm& alg2) {
128 DCHECK(!alg1.isNull()); 169 DCHECK(!alg1.isNull());
129 DCHECK(!alg2.isNull()); 170 DCHECK(!alg2.isNull());
130 if (alg1.id() != alg2.id()) 171 if (alg1.id() != alg2.id())
131 return false; 172 return false;
132 switch (alg1.id()) { 173 switch (alg1.id()) {
133 case blink::WebCryptoAlgorithmIdHmac: 174 case blink::WebCryptoAlgorithmIdHmac:
(...skipping 20 matching lines...) Expand all
154 break; 195 break;
155 } 196 }
156 return false; 197 return false;
157 } 198 }
158 199
159 bool GetDecodedUrl64ValueByKey( 200 bool GetDecodedUrl64ValueByKey(
160 const base::DictionaryValue& dict, 201 const base::DictionaryValue& dict,
161 const std::string& key, 202 const std::string& key,
162 std::string* decoded) { 203 std::string* decoded) {
163 std::string value_url64; 204 std::string value_url64;
164 if (!dict.GetString(key, &value_url64) || 205 return dict.GetString(key, &value_url64) &&
165 !webcrypto::Base64DecodeUrlSafe(value_url64, decoded) || 206 webcrypto::Base64DecodeUrlSafe(value_url64, decoded);
166 !decoded->size()) {
167 return false;
168 }
169 return true;
170 } 207 }
171 208
172 } // namespace 209 } // namespace
173 210
174 WebCryptoImpl::WebCryptoImpl() { 211 WebCryptoImpl::WebCryptoImpl() {
175 Init(); 212 Init();
176 } 213 }
177 214
178 void WebCryptoImpl::encrypt( 215 void WebCryptoImpl::encrypt(
179 const blink::WebCryptoAlgorithm& algorithm, 216 const blink::WebCryptoAlgorithm& algorithm,
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 // 1. JWK alg present but unrecognized: error 566 // 1. JWK alg present but unrecognized: error
530 // 2. JWK alg valid AND input algorithm isNull: use JWK value 567 // 2. JWK alg valid AND input algorithm isNull: use JWK value
531 // 3. JWK alg valid AND input algorithm specified, but JWK value 568 // 3. JWK alg valid AND input algorithm specified, but JWK value
532 // inconsistent with input: error 569 // inconsistent with input: error
533 // 4. JWK alg valid AND input algorithm specified, both consistent: use 570 // 4. JWK alg valid AND input algorithm specified, both consistent: use
534 // input value (because it has potentially more details) 571 // input value (because it has potentially more details)
535 // 5. JWK alg missing AND input algorithm isNull: error 572 // 5. JWK alg missing AND input algorithm isNull: error
536 // 6. JWK alg missing AND input algorithm specified: use input value 573 // 6. JWK alg missing AND input algorithm specified: use input value
537 // TODO(eroman): Should error if "alg" was specified but not a string. 574 // TODO(eroman): Should error if "alg" was specified but not a string.
538 blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm::createNull(); 575 blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm::createNull();
576 const JwkAlgorithmInfo* algorithm_info = NULL;
539 std::string jwk_alg_value; 577 std::string jwk_alg_value;
540 if (dict_value->GetString("alg", &jwk_alg_value)) { 578 if (dict_value->GetString("alg", &jwk_alg_value)) {
541 // JWK alg present 579 // JWK alg present
542 580
543 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can 581 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
544 // only be from the RSA family. 582 // only be from the RSA family.
545 583
546 const blink::WebCryptoAlgorithm jwk_algorithm = 584 blink::WebCryptoAlgorithm jwk_algorithm =
547 jwk_alg_factory.Get().CreateAlgorithmFromName(jwk_alg_value); 585 blink::WebCryptoAlgorithm::createNull();
548 if (jwk_algorithm.isNull()) { 586 algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value);
549 // JWK alg unrecognized 587 if (!algorithm_info || !algorithm_info->CreateAlgorithm(&jwk_algorithm))
550 return Status::ErrorJwkUnrecognizedAlgorithm(); // case 1 588 return Status::ErrorJwkUnrecognizedAlgorithm(); // case 1
551 } 589
552 // JWK alg valid 590 // JWK alg valid
553 if (algorithm_or_null.isNull()) { 591 if (algorithm_or_null.isNull()) {
554 // input algorithm not specified 592 // input algorithm not specified
555 algorithm = jwk_algorithm; // case 2 593 algorithm = jwk_algorithm; // case 2
556 } else { 594 } else {
557 // input algorithm specified 595 // input algorithm specified
558 if (!WebCryptoAlgorithmsConsistent(jwk_algorithm, algorithm_or_null)) 596 if (!WebCryptoAlgorithmsConsistent(jwk_algorithm, algorithm_or_null))
559 return Status::ErrorJwkAlgorithmInconsistent(); // case 3 597 return Status::ErrorJwkAlgorithmInconsistent(); // case 3
560 algorithm = algorithm_or_null; // case 4 598 algorithm = algorithm_or_null; // case 4
561 } 599 }
(...skipping 28 matching lines...) Expand all
590 } 628 }
591 } 629 }
592 630
593 // JWK keying material --> ImportKeyInternal() 631 // JWK keying material --> ImportKeyInternal()
594 if (jwk_kty_value == "oct") { 632 if (jwk_kty_value == "oct") {
595 633
596 std::string jwk_k_value; 634 std::string jwk_k_value;
597 if (!GetDecodedUrl64ValueByKey(*dict_value, "k", &jwk_k_value)) 635 if (!GetDecodedUrl64ValueByKey(*dict_value, "k", &jwk_k_value))
598 return Status::ErrorJwkDecodeK(); 636 return Status::ErrorJwkDecodeK();
599 637
600 // TODO(padolph): Some JWK alg ID's embed information about the key length 638 // Some JWK alg ID's embed information about the key length in the alg ID
601 // in the alg ID string. For example "A128" implies the JWK carries 128 bits 639 // string. For example "A128CBC" implies the JWK carries 128 bits
602 // of key material, and "HS512" implies the JWK carries _at least_ 512 bits 640 // of key material. For such keys validate that enough bytes were provided.
603 // of key material. For such keys validate the actual key length against the 641 // If this validation is not done, then it would be possible to select a
604 // value in the ID. 642 // different algorithm by passing a different lengthed key, since that is
643 // how WebCrypto interprets things.
644 if (algorithm_info &&
645 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) {
646 return Status::ErrorJwkIncorrectKeyLength();
647 }
605 648
606 return ImportKeyInternal(blink::WebCryptoKeyFormatRaw, 649 return ImportKeyInternal(blink::WebCryptoKeyFormatRaw,
607 reinterpret_cast<const uint8*>(jwk_k_value.data()), 650 reinterpret_cast<const uint8*>(jwk_k_value.data()),
608 jwk_k_value.size(), 651 jwk_k_value.size(),
609 algorithm, 652 algorithm,
610 extractable, 653 extractable,
611 usage_mask, 654 usage_mask,
612 key); 655 key);
613 } else if (jwk_kty_value == "RSA") { 656 } else if (jwk_kty_value == "RSA") {
614 657
(...skipping 27 matching lines...) Expand all
642 key); 685 key);
643 686
644 } else { 687 } else {
645 return Status::ErrorJwkUnrecognizedKty(); 688 return Status::ErrorJwkUnrecognizedKty();
646 } 689 }
647 690
648 return Status::Success(); 691 return Status::Success();
649 } 692 }
650 693
651 } // namespace content 694 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/renderer/webcrypto/webcrypto_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698