OLD | NEW |
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> | |
8 #include <functional> | |
9 #include <map> | |
10 #include "base/json/json_reader.h" | |
11 #include "base/lazy_instance.h" | |
12 #include "base/logging.h" | 7 #include "base/logging.h" |
13 #include "base/memory/scoped_ptr.h" | 8 #include "content/renderer/webcrypto/crypto_data.h" |
14 #include "base/strings/string_piece.h" | 9 #include "content/renderer/webcrypto/shared_crypto.h" |
15 #include "base/values.h" | |
16 #include "content/renderer/webcrypto/webcrypto_util.h" | 10 #include "content/renderer/webcrypto/webcrypto_util.h" |
17 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | |
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
19 #include "third_party/WebKit/public/platform/WebCryptoKey.h" | |
20 #include "third_party/WebKit/public/platform/WebString.h" | 11 #include "third_party/WebKit/public/platform/WebString.h" |
21 | 12 |
22 namespace content { | 13 namespace content { |
23 | 14 |
24 using webcrypto::Status; | 15 using webcrypto::Status; |
25 | 16 |
26 namespace { | 17 namespace { |
27 | 18 |
28 void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { | 19 void CompleteWithError(const Status& status, blink::WebCryptoResult* result) { |
29 DCHECK(status.IsError()); | 20 DCHECK(status.IsError()); |
30 if (status.HasErrorDetails()) | 21 if (status.HasErrorDetails()) |
31 result->completeWithError(blink::WebString::fromUTF8(status.ToString())); | 22 result->completeWithError(blink::WebString::fromUTF8(status.ToString())); |
32 else | 23 else |
33 result->completeWithError(); | 24 result->completeWithError(); |
34 } | 25 } |
35 | 26 |
36 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) { | 27 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) { |
37 // TODO(padolph): include all other asymmetric algorithms once they are | 28 // TODO(padolph): include all other asymmetric algorithms once they are |
38 // defined, e.g. EC and DH. | 29 // defined, e.g. EC and DH. |
39 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || | 30 return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || |
40 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || | 31 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || |
41 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep); | 32 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep); |
42 } | 33 } |
43 | 34 |
44 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); | |
45 | |
46 class JwkAlgorithmInfo { | |
47 public: | |
48 JwkAlgorithmInfo() | |
49 : creation_func_(NULL), | |
50 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) { | |
51 | |
52 } | |
53 | |
54 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) | |
55 : creation_func_(algorithm_creation_func), | |
56 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) { | |
57 } | |
58 | |
59 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, | |
60 unsigned int required_key_length_bits) | |
61 : creation_func_(algorithm_creation_func), | |
62 required_key_length_bytes_(required_key_length_bits / 8) { | |
63 DCHECK((required_key_length_bits % 8) == 0); | |
64 } | |
65 | |
66 bool CreateAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { | |
67 *algorithm = creation_func_(); | |
68 return !algorithm->isNull(); | |
69 } | |
70 | |
71 bool IsInvalidKeyByteLength(size_t byte_length) const { | |
72 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) | |
73 return false; | |
74 return required_key_length_bytes_ != byte_length; | |
75 } | |
76 | |
77 private: | |
78 enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX }; | |
79 | |
80 AlgorithmCreationFunc creation_func_; | |
81 | |
82 // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT. | |
83 unsigned int required_key_length_bytes_; | |
84 | |
85 }; | |
86 | |
87 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; | |
88 | |
89 class JwkAlgorithmRegistry { | |
90 public: | |
91 JwkAlgorithmRegistry() { | |
92 // TODO(eroman): | |
93 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 | |
94 // says HMAC with SHA-2 should have a key size at least as large as the | |
95 // hash output. | |
96 alg_to_info_["HS256"] = JwkAlgorithmInfo( | |
97 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
98 blink::WebCryptoAlgorithmIdSha256>); | |
99 alg_to_info_["HS384"] = JwkAlgorithmInfo( | |
100 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
101 blink::WebCryptoAlgorithmIdSha384>); | |
102 alg_to_info_["HS512"] = JwkAlgorithmInfo( | |
103 &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId, | |
104 blink::WebCryptoAlgorithmIdSha512>); | |
105 alg_to_info_["RS256"] = JwkAlgorithmInfo( | |
106 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
107 blink::WebCryptoAlgorithmIdSha256>); | |
108 alg_to_info_["RS384"] = JwkAlgorithmInfo( | |
109 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
110 blink::WebCryptoAlgorithmIdSha384>); | |
111 alg_to_info_["RS512"] = JwkAlgorithmInfo( | |
112 &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm, | |
113 blink::WebCryptoAlgorithmIdSha512>); | |
114 alg_to_info_["RSA1_5"] = JwkAlgorithmInfo( | |
115 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
116 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>); | |
117 alg_to_info_["RSA-OAEP"] = JwkAlgorithmInfo( | |
118 &BindAlgorithmId<webcrypto::CreateRsaOaepAlgorithm, | |
119 blink::WebCryptoAlgorithmIdSha1>); | |
120 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet | |
121 alg_to_info_["A128KW"] = | |
122 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 128); | |
123 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet | |
124 alg_to_info_["A256KW"] = | |
125 JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 256); | |
126 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( | |
127 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
128 blink::WebCryptoAlgorithmIdAesGcm>, 128); | |
129 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( | |
130 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
131 blink::WebCryptoAlgorithmIdAesGcm>, 256); | |
132 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( | |
133 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
134 blink::WebCryptoAlgorithmIdAesCbc>, 128); | |
135 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( | |
136 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
137 blink::WebCryptoAlgorithmIdAesCbc>, 192); | |
138 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( | |
139 &BindAlgorithmId<webcrypto::CreateAlgorithm, | |
140 blink::WebCryptoAlgorithmIdAesCbc>, 256); | |
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 = | |
165 LAZY_INSTANCE_INITIALIZER; | |
166 | |
167 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, | |
168 const blink::WebCryptoAlgorithm& alg2) { | |
169 DCHECK(!alg1.isNull()); | |
170 DCHECK(!alg2.isNull()); | |
171 if (alg1.id() != alg2.id()) | |
172 return false; | |
173 switch (alg1.id()) { | |
174 case blink::WebCryptoAlgorithmIdHmac: | |
175 case blink::WebCryptoAlgorithmIdRsaOaep: | |
176 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: | |
177 if (WebCryptoAlgorithmsConsistent( | |
178 webcrypto::GetInnerHashAlgorithm(alg1), | |
179 webcrypto::GetInnerHashAlgorithm(alg2))) { | |
180 return true; | |
181 } | |
182 break; | |
183 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: | |
184 case blink::WebCryptoAlgorithmIdSha1: | |
185 case blink::WebCryptoAlgorithmIdSha224: | |
186 case blink::WebCryptoAlgorithmIdSha256: | |
187 case blink::WebCryptoAlgorithmIdSha384: | |
188 case blink::WebCryptoAlgorithmIdSha512: | |
189 case blink::WebCryptoAlgorithmIdAesCbc: | |
190 case blink::WebCryptoAlgorithmIdAesGcm: | |
191 case blink::WebCryptoAlgorithmIdAesCtr: | |
192 return true; | |
193 default: | |
194 NOTREACHED(); // Not a supported algorithm. | |
195 break; | |
196 } | |
197 return false; | |
198 } | |
199 | |
200 // Extracts the required string property with key |path| from |dict| and saves | |
201 // the result to |*result|. If the property does not exist or is not a string, | |
202 // returns an error. | |
203 Status GetJwkString(base::DictionaryValue* dict, | |
204 const std::string& path, | |
205 std::string* result) { | |
206 base::Value* value = NULL; | |
207 if (!dict->Get(path, &value)) | |
208 return Status::ErrorJwkPropertyMissing(path); | |
209 if (!value->GetAsString(result)) | |
210 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
211 return Status::Success(); | |
212 } | |
213 | |
214 // Extracts the optional string property with key |path| from |dict| and saves | |
215 // the result to |*result| if it was found. If the property exists and is not a | |
216 // string, returns an error. Otherwise returns success, and sets | |
217 // |*property_exists| if it was found. | |
218 Status GetOptionalJwkString(base::DictionaryValue* dict, | |
219 const std::string& path, | |
220 std::string* result, | |
221 bool* property_exists) { | |
222 *property_exists = false; | |
223 base::Value* value = NULL; | |
224 if (!dict->Get(path, &value)) | |
225 return Status::Success(); | |
226 | |
227 if (!value->GetAsString(result)) | |
228 return Status::ErrorJwkPropertyWrongType(path, "string"); | |
229 | |
230 *property_exists = true; | |
231 return Status::Success(); | |
232 } | |
233 | |
234 // Extracts the required string property with key |path| from |dict| and saves | |
235 // the base64-decoded bytes to |*result|. If the property does not exist or is | |
236 // not a string, or could not be base64-decoded, returns an error. | |
237 Status GetJwkBytes(base::DictionaryValue* dict, | |
238 const std::string& path, | |
239 std::string* result) { | |
240 std::string base64_string; | |
241 Status status = GetJwkString(dict, path, &base64_string); | |
242 if (status.IsError()) | |
243 return status; | |
244 | |
245 if (!webcrypto::Base64DecodeUrlSafe(base64_string, result)) | |
246 return Status::ErrorJwkBase64Decode(path); | |
247 | |
248 return Status::Success(); | |
249 } | |
250 | |
251 // Extracts the optional boolean property with key |path| from |dict| and saves | |
252 // the result to |*result| if it was found. If the property exists and is not a | |
253 // boolean, returns an error. Otherwise returns success, and sets | |
254 // |*property_exists| if it was found. | |
255 Status GetOptionalJwkBool(base::DictionaryValue* dict, | |
256 const std::string& path, | |
257 bool* result, | |
258 bool* property_exists) { | |
259 *property_exists = false; | |
260 base::Value* value = NULL; | |
261 if (!dict->Get(path, &value)) | |
262 return Status::Success(); | |
263 | |
264 if (!value->GetAsBoolean(result)) | |
265 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | |
266 | |
267 *property_exists = true; | |
268 return Status::Success(); | |
269 } | |
270 | 35 |
271 } // namespace | 36 } // namespace |
272 | 37 |
273 WebCryptoImpl::WebCryptoImpl() { | 38 WebCryptoImpl::WebCryptoImpl() { |
274 Init(); | 39 webcrypto::Init(); |
275 } | 40 } |
276 | 41 |
| 42 WebCryptoImpl::~WebCryptoImpl() {} |
| 43 |
277 void WebCryptoImpl::encrypt( | 44 void WebCryptoImpl::encrypt( |
278 const blink::WebCryptoAlgorithm& algorithm, | 45 const blink::WebCryptoAlgorithm& algorithm, |
279 const blink::WebCryptoKey& key, | 46 const blink::WebCryptoKey& key, |
280 const unsigned char* data, | 47 const unsigned char* data, |
281 unsigned int data_size, | 48 unsigned int data_size, |
282 blink::WebCryptoResult result) { | 49 blink::WebCryptoResult result) { |
283 DCHECK(!algorithm.isNull()); | 50 DCHECK(!algorithm.isNull()); |
284 blink::WebArrayBuffer buffer; | 51 blink::WebArrayBuffer buffer; |
285 Status status = EncryptInternal(algorithm, key, data, data_size, &buffer); | 52 Status status = webcrypto::Encrypt( |
| 53 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
286 if (status.IsError()) | 54 if (status.IsError()) |
287 CompleteWithError(status, &result); | 55 CompleteWithError(status, &result); |
288 else | 56 else |
289 result.completeWithBuffer(buffer); | 57 result.completeWithBuffer(buffer); |
290 } | 58 } |
291 | 59 |
292 void WebCryptoImpl::decrypt( | 60 void WebCryptoImpl::decrypt( |
293 const blink::WebCryptoAlgorithm& algorithm, | 61 const blink::WebCryptoAlgorithm& algorithm, |
294 const blink::WebCryptoKey& key, | 62 const blink::WebCryptoKey& key, |
295 const unsigned char* data, | 63 const unsigned char* data, |
296 unsigned int data_size, | 64 unsigned int data_size, |
297 blink::WebCryptoResult result) { | 65 blink::WebCryptoResult result) { |
298 DCHECK(!algorithm.isNull()); | 66 DCHECK(!algorithm.isNull()); |
299 blink::WebArrayBuffer buffer; | 67 blink::WebArrayBuffer buffer; |
300 Status status = DecryptInternal(algorithm, key, data, data_size, &buffer); | 68 Status status = webcrypto::Decrypt( |
| 69 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
301 if (status.IsError()) | 70 if (status.IsError()) |
302 CompleteWithError(status, &result); | 71 CompleteWithError(status, &result); |
303 else | 72 else |
304 result.completeWithBuffer(buffer); | 73 result.completeWithBuffer(buffer); |
305 } | 74 } |
306 | 75 |
307 void WebCryptoImpl::digest( | 76 void WebCryptoImpl::digest( |
308 const blink::WebCryptoAlgorithm& algorithm, | 77 const blink::WebCryptoAlgorithm& algorithm, |
309 const unsigned char* data, | 78 const unsigned char* data, |
310 unsigned int data_size, | 79 unsigned int data_size, |
311 blink::WebCryptoResult result) { | 80 blink::WebCryptoResult result) { |
312 DCHECK(!algorithm.isNull()); | 81 DCHECK(!algorithm.isNull()); |
313 blink::WebArrayBuffer buffer; | 82 blink::WebArrayBuffer buffer; |
314 Status status = DigestInternal(algorithm, data, data_size, &buffer); | 83 Status status = webcrypto::Digest( |
| 84 algorithm, webcrypto::CryptoData(data, data_size), &buffer); |
315 if (status.IsError()) | 85 if (status.IsError()) |
316 CompleteWithError(status, &result); | 86 CompleteWithError(status, &result); |
317 else | 87 else |
318 result.completeWithBuffer(buffer); | 88 result.completeWithBuffer(buffer); |
319 } | 89 } |
320 | 90 |
321 void WebCryptoImpl::generateKey( | 91 void WebCryptoImpl::generateKey( |
322 const blink::WebCryptoAlgorithm& algorithm, | 92 const blink::WebCryptoAlgorithm& algorithm, |
323 bool extractable, | 93 bool extractable, |
324 blink::WebCryptoKeyUsageMask usage_mask, | 94 blink::WebCryptoKeyUsageMask usage_mask, |
325 blink::WebCryptoResult result) { | 95 blink::WebCryptoResult result) { |
326 DCHECK(!algorithm.isNull()); | 96 DCHECK(!algorithm.isNull()); |
327 if (IsAlgorithmAsymmetric(algorithm)) { | 97 if (IsAlgorithmAsymmetric(algorithm)) { |
328 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | 98 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
329 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | 99 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
330 Status status = GenerateKeyPairInternal( | 100 Status status = webcrypto::GenerateKeyPair( |
331 algorithm, extractable, usage_mask, &public_key, &private_key); | 101 algorithm, extractable, usage_mask, &public_key, &private_key); |
332 if (status.IsError()) { | 102 if (status.IsError()) { |
333 CompleteWithError(status, &result); | 103 CompleteWithError(status, &result); |
334 } else { | 104 } else { |
335 DCHECK(public_key.handle()); | 105 DCHECK(public_key.handle()); |
336 DCHECK(private_key.handle()); | 106 DCHECK(private_key.handle()); |
337 DCHECK_EQ(algorithm.id(), public_key.algorithm().id()); | 107 DCHECK_EQ(algorithm.id(), public_key.algorithm().id()); |
338 DCHECK_EQ(algorithm.id(), private_key.algorithm().id()); | 108 DCHECK_EQ(algorithm.id(), private_key.algorithm().id()); |
339 DCHECK_EQ(true, public_key.extractable()); | 109 DCHECK_EQ(true, public_key.extractable()); |
340 DCHECK_EQ(extractable, private_key.extractable()); | 110 DCHECK_EQ(extractable, private_key.extractable()); |
341 DCHECK_EQ(usage_mask, public_key.usages()); | 111 DCHECK_EQ(usage_mask, public_key.usages()); |
342 DCHECK_EQ(usage_mask, private_key.usages()); | 112 DCHECK_EQ(usage_mask, private_key.usages()); |
343 result.completeWithKeyPair(public_key, private_key); | 113 result.completeWithKeyPair(public_key, private_key); |
344 } | 114 } |
345 } else { | 115 } else { |
346 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 116 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
347 Status status = GenerateSecretKeyInternal( | 117 Status status = |
348 algorithm, extractable, usage_mask, &key); | 118 webcrypto::GenerateSecretKey(algorithm, extractable, usage_mask, &key); |
349 if (status.IsError()) { | 119 if (status.IsError()) { |
350 CompleteWithError(status, &result); | 120 CompleteWithError(status, &result); |
351 } else { | 121 } else { |
352 DCHECK(key.handle()); | 122 DCHECK(key.handle()); |
353 DCHECK_EQ(algorithm.id(), key.algorithm().id()); | 123 DCHECK_EQ(algorithm.id(), key.algorithm().id()); |
354 DCHECK_EQ(extractable, key.extractable()); | 124 DCHECK_EQ(extractable, key.extractable()); |
355 DCHECK_EQ(usage_mask, key.usages()); | 125 DCHECK_EQ(usage_mask, key.usages()); |
356 result.completeWithKey(key); | 126 result.completeWithKey(key); |
357 } | 127 } |
358 } | 128 } |
359 } | 129 } |
360 | 130 |
361 void WebCryptoImpl::importKey( | 131 void WebCryptoImpl::importKey( |
362 blink::WebCryptoKeyFormat format, | 132 blink::WebCryptoKeyFormat format, |
363 const unsigned char* key_data, | 133 const unsigned char* key_data, |
364 unsigned int key_data_size, | 134 unsigned int key_data_size, |
365 const blink::WebCryptoAlgorithm& algorithm_or_null, | 135 const blink::WebCryptoAlgorithm& algorithm_or_null, |
366 bool extractable, | 136 bool extractable, |
367 blink::WebCryptoKeyUsageMask usage_mask, | 137 blink::WebCryptoKeyUsageMask usage_mask, |
368 blink::WebCryptoResult result) { | 138 blink::WebCryptoResult result) { |
369 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 139 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
370 Status status = Status::Error(); | 140 Status status = |
371 if (format == blink::WebCryptoKeyFormatJwk) { | 141 webcrypto::ImportKey(format, |
372 status = ImportKeyJwk(key_data, | 142 webcrypto::CryptoData(key_data, key_data_size), |
373 key_data_size, | 143 algorithm_or_null, |
374 algorithm_or_null, | 144 extractable, |
375 extractable, | 145 usage_mask, |
376 usage_mask, | 146 &key); |
377 &key); | |
378 } else { | |
379 status = ImportKeyInternal(format, | |
380 key_data, | |
381 key_data_size, | |
382 algorithm_or_null, | |
383 extractable, | |
384 usage_mask, | |
385 &key); | |
386 } | |
387 if (status.IsError()) { | 147 if (status.IsError()) { |
388 CompleteWithError(status, &result); | 148 CompleteWithError(status, &result); |
389 } else { | 149 } else { |
390 DCHECK(key.handle()); | 150 DCHECK(key.handle()); |
391 DCHECK(!key.algorithm().isNull()); | 151 DCHECK(!key.algorithm().isNull()); |
392 DCHECK_EQ(extractable, key.extractable()); | 152 DCHECK_EQ(extractable, key.extractable()); |
393 result.completeWithKey(key); | 153 result.completeWithKey(key); |
394 } | 154 } |
395 } | 155 } |
396 | 156 |
397 void WebCryptoImpl::exportKey( | 157 void WebCryptoImpl::exportKey( |
398 blink::WebCryptoKeyFormat format, | 158 blink::WebCryptoKeyFormat format, |
399 const blink::WebCryptoKey& key, | 159 const blink::WebCryptoKey& key, |
400 blink::WebCryptoResult result) { | 160 blink::WebCryptoResult result) { |
401 blink::WebArrayBuffer buffer; | 161 blink::WebArrayBuffer buffer; |
402 Status status = ExportKeyInternal(format, key, &buffer); | 162 Status status = webcrypto::ExportKey(format, key, &buffer); |
403 if (status.IsError()) | 163 if (status.IsError()) |
404 CompleteWithError(status, &result); | 164 CompleteWithError(status, &result); |
405 else | 165 else |
406 result.completeWithBuffer(buffer); | 166 result.completeWithBuffer(buffer); |
407 } | 167 } |
408 | 168 |
409 void WebCryptoImpl::sign( | 169 void WebCryptoImpl::sign( |
410 const blink::WebCryptoAlgorithm& algorithm, | 170 const blink::WebCryptoAlgorithm& algorithm, |
411 const blink::WebCryptoKey& key, | 171 const blink::WebCryptoKey& key, |
412 const unsigned char* data, | 172 const unsigned char* data, |
413 unsigned int data_size, | 173 unsigned int data_size, |
414 blink::WebCryptoResult result) { | 174 blink::WebCryptoResult result) { |
415 DCHECK(!algorithm.isNull()); | 175 DCHECK(!algorithm.isNull()); |
416 blink::WebArrayBuffer buffer; | 176 blink::WebArrayBuffer buffer; |
417 Status status = SignInternal(algorithm, key, data, data_size, &buffer); | 177 Status status = webcrypto::Sign( |
| 178 algorithm, key, webcrypto::CryptoData(data, data_size), &buffer); |
418 if (status.IsError()) | 179 if (status.IsError()) |
419 CompleteWithError(status, &result); | 180 CompleteWithError(status, &result); |
420 else | 181 else |
421 result.completeWithBuffer(buffer); | 182 result.completeWithBuffer(buffer); |
422 } | 183 } |
423 | 184 |
424 void WebCryptoImpl::verifySignature( | 185 void WebCryptoImpl::verifySignature( |
425 const blink::WebCryptoAlgorithm& algorithm, | 186 const blink::WebCryptoAlgorithm& algorithm, |
426 const blink::WebCryptoKey& key, | 187 const blink::WebCryptoKey& key, |
427 const unsigned char* signature, | 188 const unsigned char* signature, |
428 unsigned int signature_size, | 189 unsigned int signature_size, |
429 const unsigned char* data, | 190 const unsigned char* data, |
430 unsigned int data_size, | 191 unsigned int data_size, |
431 blink::WebCryptoResult result) { | 192 blink::WebCryptoResult result) { |
432 DCHECK(!algorithm.isNull()); | 193 DCHECK(!algorithm.isNull()); |
433 bool signature_match = false; | 194 bool signature_match = false; |
434 Status status = VerifySignatureInternal(algorithm, | 195 Status status = webcrypto::VerifySignature( |
435 key, | 196 algorithm, |
436 signature, | 197 key, |
437 signature_size, | 198 webcrypto::CryptoData(signature, signature_size), |
438 data, | 199 webcrypto::CryptoData(data, data_size), |
439 data_size, | 200 &signature_match); |
440 &signature_match); | |
441 if (status.IsError()) | 201 if (status.IsError()) |
442 CompleteWithError(status, &result); | 202 CompleteWithError(status, &result); |
443 else | 203 else |
444 result.completeWithBoolean(signature_match); | 204 result.completeWithBoolean(signature_match); |
445 } | 205 } |
446 | 206 |
447 Status WebCryptoImpl::ImportKeyJwk( | |
448 const unsigned char* key_data, | |
449 unsigned int key_data_size, | |
450 const blink::WebCryptoAlgorithm& algorithm_or_null, | |
451 bool extractable, | |
452 blink::WebCryptoKeyUsageMask usage_mask, | |
453 blink::WebCryptoKey* key) { | |
454 | |
455 // The goal of this method is to extract key material and meta data from the | |
456 // incoming JWK, combine them with the input parameters, and ultimately import | |
457 // a Web Crypto Key. | |
458 // | |
459 // JSON Web Key Format (JWK) | |
460 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16 | |
461 // TODO(padolph): Not all possible values are handled by this code right now | |
462 // | |
463 // A JWK is a simple JSON dictionary with the following entries | |
464 // - "kty" (Key Type) Parameter, REQUIRED | |
465 // - <kty-specific parameters, see below>, REQUIRED | |
466 // - "use" (Key Use) Parameter, OPTIONAL | |
467 // - "alg" (Algorithm) Parameter, OPTIONAL | |
468 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE, | |
469 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796] | |
470 // (all other entries are ignored) | |
471 // | |
472 // OPTIONAL here means that this code does not require the entry to be present | |
473 // in the incoming JWK, because the method input parameters contain similar | |
474 // information. If the optional JWK entry is present, it will be validated | |
475 // against the corresponding input parameter for consistency and combined with | |
476 // it according to rules defined below. A special case is that the method | |
477 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg' | |
478 // value (if present) is used as a fallback. | |
479 // | |
480 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK | |
481 // values are parsed out and combined with the method input parameters to | |
482 // build a Web Crypto Key: | |
483 // Web Crypto Key type <-- (deduced) | |
484 // Web Crypto Key extractable <-- JWK extractable + input extractable | |
485 // Web Crypto Key algorithm <-- JWK alg + input algorithm | |
486 // Web Crypto Key keyUsage <-- JWK use + input usage_mask | |
487 // Web Crypto Key keying material <-- kty-specific parameters | |
488 // | |
489 // Values for each JWK entry are case-sensitive and defined in | |
490 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16. | |
491 // Note that not all values specified by JOSE are handled by this code. Only | |
492 // handled values are listed. | |
493 // - kty (Key Type) | |
494 // +-------+--------------------------------------------------------------+ | |
495 // | "RSA" | RSA [RFC3447] | | |
496 // | "oct" | Octet sequence (used to represent symmetric keys) | | |
497 // +-------+--------------------------------------------------------------+ | |
498 // - use (Key Use) | |
499 // +-------+--------------------------------------------------------------+ | |
500 // | "enc" | encrypt and decrypt operations | | |
501 // | "sig" | sign and verify (MAC) operations | | |
502 // | "wrap"| key wrap and unwrap [not yet part of JOSE] | | |
503 // +-------+--------------------------------------------------------------+ | |
504 // - extractable (Key Exportability) | |
505 // +-------+--------------------------------------------------------------+ | |
506 // | true | Key may be exported from the trusted environment | | |
507 // | false | Key cannot exit the trusted environment | | |
508 // +-------+--------------------------------------------------------------+ | |
509 // - alg (Algorithm) | |
510 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16 | |
511 // +--------------+-------------------------------------------------------+ | |
512 // | Digital Signature or MAC Algorithm | | |
513 // +--------------+-------------------------------------------------------+ | |
514 // | "HS256" | HMAC using SHA-256 hash algorithm | | |
515 // | "HS384" | HMAC using SHA-384 hash algorithm | | |
516 // | "HS512" | HMAC using SHA-512 hash algorithm | | |
517 // | "RS256" | RSASSA using SHA-256 hash algorithm | | |
518 // | "RS384" | RSASSA using SHA-384 hash algorithm | | |
519 // | "RS512" | RSASSA using SHA-512 hash algorithm | | |
520 // +--------------+-------------------------------------------------------| | |
521 // | Key Management Algorithm | | |
522 // +--------------+-------------------------------------------------------+ | |
523 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] | | |
524 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | | |
525 // | | (OAEP) [RFC3447], with the default parameters | | |
526 // | | specified by RFC3447 in Section A.2.1 | | |
527 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | | |
528 // | | [RFC3394] using 128 bit keys | | |
529 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | | |
530 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | | |
531 // | | 128 bit keys | | |
532 // | "A256GCM" | AES GCM using 256 bit keys | | |
533 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | | |
534 // | | padding [NIST.800-38A] [not yet part of JOSE, see | | |
535 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 | | |
536 // | "A192CBC" | AES CBC using 192 bit keys [not yet part of JOSE] | | |
537 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] | | |
538 // +--------------+-------------------------------------------------------+ | |
539 // | |
540 // kty-specific parameters | |
541 // The value of kty determines the type and content of the keying material | |
542 // carried in the JWK to be imported. Currently only two possibilities are | |
543 // supported: a raw key or an RSA public key. RSA private keys are not | |
544 // supported because typical applications seldom need to import a private key, | |
545 // and the large number of JWK parameters required to describe one. | |
546 // - kty == "oct" (symmetric or other raw key) | |
547 // +-------+--------------------------------------------------------------+ | |
548 // | "k" | Contains the value of the symmetric (or other single-valued) | | |
549 // | | key. It is represented as the base64url encoding of the | | |
550 // | | octet sequence containing the key value. | | |
551 // +-------+--------------------------------------------------------------+ | |
552 // - kty == "RSA" (RSA public key) | |
553 // +-------+--------------------------------------------------------------+ | |
554 // | "n" | Contains the modulus value for the RSA public key. It is | | |
555 // | | represented as the base64url encoding of the value's | | |
556 // | | unsigned big endian representation as an octet sequence. | | |
557 // +-------+--------------------------------------------------------------+ | |
558 // | "e" | Contains the exponent value for the RSA public key. It is | | |
559 // | | represented as the base64url encoding of the value's | | |
560 // | | unsigned big endian representation as an octet sequence. | | |
561 // +-------+--------------------------------------------------------------+ | |
562 // | |
563 // Consistency and conflict resolution | |
564 // The 'algorithm_or_null', 'extractable', and 'usage_mask' input parameters | |
565 // may be different than the corresponding values inside the JWK. The Web | |
566 // Crypto spec says that if a JWK value is present but is inconsistent with | |
567 // the input value, it is an error and the operation must fail. If no | |
568 // inconsistency is found, the input and JWK values are combined as follows: | |
569 // | |
570 // algorithm | |
571 // If an algorithm is provided by both the input parameter and the JWK, | |
572 // consistency between the two is based only on algorithm ID's (including an | |
573 // inner hash algorithm if present). In this case if the consistency | |
574 // check is passed, the input algorithm is used. If only one of either the | |
575 // input algorithm and JWK alg is provided, it is used as the final | |
576 // algorithm. | |
577 // | |
578 // extractable | |
579 // If the JWK extractable is true but the input parameter is false, make the | |
580 // Web Crypto Key non-extractable. Conversely, if the JWK extractable is | |
581 // false but the input parameter is true, it is an inconsistency. If both | |
582 // are true or both are false, use that value. | |
583 // | |
584 // usage_mask | |
585 // The input usage_mask must be a strict subset of the interpreted JWK use | |
586 // value, else it is judged inconsistent. In all cases the input usage_mask | |
587 // is used as the final usage_mask. | |
588 // | |
589 | |
590 if (!key_data_size) | |
591 return Status::ErrorImportEmptyKeyData(); | |
592 DCHECK(key); | |
593 | |
594 // Parse the incoming JWK JSON. | |
595 base::StringPiece json_string(reinterpret_cast<const char*>(key_data), | |
596 key_data_size); | |
597 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); | |
598 // Note, bare pointer dict_value is ok since it points into scoped value. | |
599 base::DictionaryValue* dict_value = NULL; | |
600 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) | |
601 return Status::ErrorJwkNotDictionary(); | |
602 | |
603 // JWK "kty". Exit early if this required JWK parameter is missing. | |
604 std::string jwk_kty_value; | |
605 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); | |
606 if (status.IsError()) | |
607 return status; | |
608 | |
609 // JWK "extractable" (optional) --> extractable parameter | |
610 { | |
611 bool jwk_extractable_value = false; | |
612 bool has_jwk_extractable; | |
613 status = GetOptionalJwkBool(dict_value, | |
614 "extractable", | |
615 &jwk_extractable_value, | |
616 &has_jwk_extractable); | |
617 if (status.IsError()) | |
618 return status; | |
619 if (has_jwk_extractable && !jwk_extractable_value && extractable) | |
620 return Status::ErrorJwkExtractableInconsistent(); | |
621 } | |
622 | |
623 // JWK "alg" (optional) --> algorithm parameter | |
624 // Note: input algorithm is also optional, so we have six cases to handle. | |
625 // 1. JWK alg present but unrecognized: error | |
626 // 2. JWK alg valid AND input algorithm isNull: use JWK value | |
627 // 3. JWK alg valid AND input algorithm specified, but JWK value | |
628 // inconsistent with input: error | |
629 // 4. JWK alg valid AND input algorithm specified, both consistent: use | |
630 // input value (because it has potentially more details) | |
631 // 5. JWK alg missing AND input algorithm isNull: error | |
632 // 6. JWK alg missing AND input algorithm specified: use input value | |
633 blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm::createNull(); | |
634 const JwkAlgorithmInfo* algorithm_info = NULL; | |
635 std::string jwk_alg_value; | |
636 bool has_jwk_alg; | |
637 status = | |
638 GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg); | |
639 if (status.IsError()) | |
640 return status; | |
641 | |
642 if (has_jwk_alg) { | |
643 // JWK alg present | |
644 | |
645 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can | |
646 // only be from the RSA family. | |
647 | |
648 blink::WebCryptoAlgorithm jwk_algorithm = | |
649 blink::WebCryptoAlgorithm::createNull(); | |
650 algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value); | |
651 if (!algorithm_info || !algorithm_info->CreateAlgorithm(&jwk_algorithm)) | |
652 return Status::ErrorJwkUnrecognizedAlgorithm(); // case 1 | |
653 | |
654 // JWK alg valid | |
655 if (algorithm_or_null.isNull()) { | |
656 // input algorithm not specified | |
657 algorithm = jwk_algorithm; // case 2 | |
658 } else { | |
659 // input algorithm specified | |
660 if (!WebCryptoAlgorithmsConsistent(jwk_algorithm, algorithm_or_null)) | |
661 return Status::ErrorJwkAlgorithmInconsistent(); // case 3 | |
662 algorithm = algorithm_or_null; // case 4 | |
663 } | |
664 } else { | |
665 // JWK alg missing | |
666 if (algorithm_or_null.isNull()) | |
667 return Status::ErrorJwkAlgorithmMissing(); // case 5 | |
668 algorithm = algorithm_or_null; // case 6 | |
669 } | |
670 DCHECK(!algorithm.isNull()); | |
671 | |
672 // JWK "use" (optional) --> usage_mask parameter | |
673 std::string jwk_use_value; | |
674 bool has_jwk_use; | |
675 status = | |
676 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); | |
677 if (status.IsError()) | |
678 return status; | |
679 if (has_jwk_use) { | |
680 blink::WebCryptoKeyUsageMask jwk_usage_mask = 0; | |
681 if (jwk_use_value == "enc") { | |
682 jwk_usage_mask = | |
683 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; | |
684 } else if (jwk_use_value == "sig") { | |
685 jwk_usage_mask = | |
686 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | |
687 } else if (jwk_use_value == "wrap") { | |
688 jwk_usage_mask = | |
689 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey; | |
690 } else { | |
691 return Status::ErrorJwkUnrecognizedUsage(); | |
692 } | |
693 if ((jwk_usage_mask & usage_mask) != usage_mask) { | |
694 // A usage_mask must be a subset of jwk_usage_mask. | |
695 return Status::ErrorJwkUsageInconsistent(); | |
696 } | |
697 } | |
698 | |
699 // JWK keying material --> ImportKeyInternal() | |
700 if (jwk_kty_value == "oct") { | |
701 | |
702 std::string jwk_k_value; | |
703 status = GetJwkBytes(dict_value, "k", &jwk_k_value); | |
704 if (status.IsError()) | |
705 return status; | |
706 | |
707 // Some JWK alg ID's embed information about the key length in the alg ID | |
708 // string. For example "A128CBC" implies the JWK carries 128 bits | |
709 // of key material. For such keys validate that enough bytes were provided. | |
710 // If this validation is not done, then it would be possible to select a | |
711 // different algorithm by passing a different lengthed key, since that is | |
712 // how WebCrypto interprets things. | |
713 if (algorithm_info && | |
714 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { | |
715 return Status::ErrorJwkIncorrectKeyLength(); | |
716 } | |
717 | |
718 return ImportKeyInternal(blink::WebCryptoKeyFormatRaw, | |
719 reinterpret_cast<const uint8*>(jwk_k_value.data()), | |
720 jwk_k_value.size(), | |
721 algorithm, | |
722 extractable, | |
723 usage_mask, | |
724 key); | |
725 } else if (jwk_kty_value == "RSA") { | |
726 | |
727 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | |
728 // in the JWK, while an RSA private key must have those, plus at least a "d" | |
729 // (private exponent) entry. | |
730 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
731 // section 6.3. | |
732 | |
733 // RSA private key import is not currently supported, so fail here if a "d" | |
734 // entry is found. | |
735 // TODO(padolph): Support RSA private key import. | |
736 if (dict_value->HasKey("d")) | |
737 return Status::ErrorJwkRsaPrivateKeyUnsupported(); | |
738 | |
739 std::string jwk_n_value; | |
740 status = GetJwkBytes(dict_value, "n", &jwk_n_value); | |
741 if (status.IsError()) | |
742 return status; | |
743 std::string jwk_e_value; | |
744 status = GetJwkBytes(dict_value, "e", &jwk_e_value); | |
745 if (status.IsError()) | |
746 return status; | |
747 | |
748 return ImportRsaPublicKeyInternal( | |
749 reinterpret_cast<const uint8*>(jwk_n_value.data()), | |
750 jwk_n_value.size(), | |
751 reinterpret_cast<const uint8*>(jwk_e_value.data()), | |
752 jwk_e_value.size(), | |
753 algorithm, | |
754 extractable, | |
755 usage_mask, | |
756 key); | |
757 | |
758 } else { | |
759 return Status::ErrorJwkUnrecognizedKty(); | |
760 } | |
761 | |
762 return Status::Success(); | |
763 } | |
764 | |
765 } // namespace content | 207 } // namespace content |
OLD | NEW |