OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include "content/child/webcrypto/test/test_helpers.h" |
6 #include <string> | |
7 #include <vector> | |
8 | 6 |
9 #include "base/basictypes.h" | |
10 #include "base/file_util.h" | 7 #include "base/file_util.h" |
11 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
12 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
13 #include "base/logging.h" | 10 #include "base/logging.h" |
14 #include "base/memory/ref_counted.h" | |
15 #include "base/path_service.h" | 11 #include "base/path_service.h" |
16 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
17 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
19 #include "base/strings/stringprintf.h" | 15 #include "base/values.h" |
20 #include "content/child/webcrypto/algorithm_dispatch.h" | 16 #include "content/child/webcrypto/algorithm_dispatch.h" |
21 #include "content/child/webcrypto/crypto_data.h" | 17 #include "content/child/webcrypto/crypto_data.h" |
22 #include "content/child/webcrypto/status.h" | 18 #include "content/child/webcrypto/status.h" |
23 #include "content/child/webcrypto/webcrypto_util.h" | 19 #include "content/child/webcrypto/webcrypto_util.h" |
24 #include "content/public/common/content_paths.h" | 20 #include "content/public/common/content_paths.h" |
25 #include "testing/gtest/include/gtest/gtest.h" | |
26 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | |
27 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | 21 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
28 #include "third_party/WebKit/public/platform/WebCryptoKey.h" | |
29 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | 22 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
30 #include "third_party/re2/re2/re2.h" | 23 #include "third_party/re2/re2/re2.h" |
31 | 24 |
32 #if !defined(USE_OPENSSL) | 25 #if !defined(USE_OPENSSL) |
33 #include <nss.h> | 26 #include <nss.h> |
34 #include <pk11pub.h> | 27 #include <pk11pub.h> |
35 | 28 |
36 #include "crypto/nss_util.h" | 29 #include "crypto/nss_util.h" |
37 #include "crypto/scoped_nss_types.h" | 30 #include "crypto/scoped_nss_types.h" |
38 #endif | 31 #endif |
39 | 32 |
40 #define EXPECT_BYTES_EQ(expected, actual) \ | |
41 EXPECT_EQ(CryptoData(expected), CryptoData(actual)) | |
42 | |
43 #define EXPECT_BYTES_EQ_HEX(expected_hex, actual_bytes) \ | |
44 EXPECT_BYTES_EQ(HexStringToBytes(expected_hex), actual_bytes) | |
45 | |
46 namespace content { | 33 namespace content { |
47 | 34 |
48 namespace webcrypto { | 35 namespace webcrypto { |
49 | 36 |
50 // These functions are used by GTEST to support EXPECT_EQ() for | |
51 // webcrypto::Status and webcrypto::CryptoData | |
52 | |
53 void PrintTo(const Status& status, ::std::ostream* os) { | 37 void PrintTo(const Status& status, ::std::ostream* os) { |
54 if (status.IsSuccess()) | 38 if (status.IsSuccess()) |
55 *os << "Success"; | 39 *os << "Success"; |
56 else | 40 else |
57 *os << "Error type: " << status.error_type() | 41 *os << "Error type: " << status.error_type() |
58 << " Error details: " << status.error_details(); | 42 << " Error details: " << status.error_details(); |
59 } | 43 } |
60 | 44 |
61 bool operator==(const Status& a, const Status& b) { | 45 bool operator==(const Status& a, const Status& b) { |
62 if (a.IsSuccess() != b.IsSuccess()) | 46 if (a.IsSuccess() != b.IsSuccess()) |
(...skipping 14 matching lines...) Expand all Loading... |
77 | 61 |
78 bool operator==(const CryptoData& a, const CryptoData& b) { | 62 bool operator==(const CryptoData& a, const CryptoData& b) { |
79 return a.byte_length() == b.byte_length() && | 63 return a.byte_length() == b.byte_length() && |
80 memcmp(a.bytes(), b.bytes(), a.byte_length()) == 0; | 64 memcmp(a.bytes(), b.bytes(), a.byte_length()) == 0; |
81 } | 65 } |
82 | 66 |
83 bool operator!=(const CryptoData& a, const CryptoData& b) { | 67 bool operator!=(const CryptoData& a, const CryptoData& b) { |
84 return !(a == b); | 68 return !(a == b); |
85 } | 69 } |
86 | 70 |
87 namespace { | |
88 | |
89 // ----------------------------------------------------------------------------- | |
90 | |
91 // TODO(eroman): For Linux builds using system NSS, AES-GCM support is a | |
92 // runtime dependency. Test it by trying to import a key. | |
93 // TODO(padolph): Consider caching the result of the import key test. | |
94 bool SupportsAesGcm() { | 71 bool SupportsAesGcm() { |
95 std::vector<uint8_t> key_raw(16, 0); | 72 std::vector<uint8_t> key_raw(16, 0); |
96 | 73 |
97 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 74 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
98 Status status = ImportKey(blink::WebCryptoKeyFormatRaw, | 75 Status status = ImportKey(blink::WebCryptoKeyFormatRaw, |
99 CryptoData(key_raw), | 76 CryptoData(key_raw), |
100 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | 77 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), |
101 true, | 78 true, |
102 blink::WebCryptoKeyUsageEncrypt, | 79 blink::WebCryptoKeyUsageEncrypt, |
103 &key); | 80 &key); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); | 122 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); |
146 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | 123 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( |
147 algorithm_id, | 124 algorithm_id, |
148 new blink::WebCryptoRsaHashedKeyGenParams( | 125 new blink::WebCryptoRsaHashedKeyGenParams( |
149 CreateAlgorithm(hash_id), | 126 CreateAlgorithm(hash_id), |
150 modulus_length, | 127 modulus_length, |
151 vector_as_array(&public_exponent), | 128 vector_as_array(&public_exponent), |
152 public_exponent.size())); | 129 public_exponent.size())); |
153 } | 130 } |
154 | 131 |
155 // Creates an RSA-OAEP algorithm | |
156 blink::WebCryptoAlgorithm CreateRsaOaepAlgorithm( | |
157 const std::vector<uint8_t>& label) { | |
158 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
159 blink::WebCryptoAlgorithmIdRsaOaep, | |
160 new blink::WebCryptoRsaOaepParams( | |
161 !label.empty(), vector_as_array(&label), label.size())); | |
162 } | |
163 | |
164 // Creates an AES-CBC algorithm. | |
165 blink::WebCryptoAlgorithm CreateAesCbcAlgorithm( | |
166 const std::vector<uint8_t>& iv) { | |
167 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
168 blink::WebCryptoAlgorithmIdAesCbc, | |
169 new blink::WebCryptoAesCbcParams(vector_as_array(&iv), iv.size())); | |
170 } | |
171 | |
172 // Creates an AES-GCM algorithm. | |
173 blink::WebCryptoAlgorithm CreateAesGcmAlgorithm( | |
174 const std::vector<uint8_t>& iv, | |
175 const std::vector<uint8_t>& additional_data, | |
176 unsigned int tag_length_bits) { | |
177 EXPECT_TRUE(SupportsAesGcm()); | |
178 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
179 blink::WebCryptoAlgorithmIdAesGcm, | |
180 new blink::WebCryptoAesGcmParams(vector_as_array(&iv), | |
181 iv.size(), | |
182 true, | |
183 vector_as_array(&additional_data), | |
184 additional_data.size(), | |
185 true, | |
186 tag_length_bits)); | |
187 } | |
188 | |
189 // Creates an HMAC algorithm whose parameters struct is compatible with key | |
190 // generation. It is an error to call this with a hash_id that is not a SHA*. | |
191 // The key_length_bits parameter is optional, with zero meaning unspecified. | |
192 blink::WebCryptoAlgorithm CreateHmacKeyGenAlgorithm( | |
193 blink::WebCryptoAlgorithmId hash_id, | |
194 unsigned int key_length_bits) { | |
195 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id)); | |
196 // key_length_bytes == 0 means unspecified | |
197 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
198 blink::WebCryptoAlgorithmIdHmac, | |
199 new blink::WebCryptoHmacKeyGenParams( | |
200 CreateAlgorithm(hash_id), (key_length_bits != 0), key_length_bits)); | |
201 } | |
202 | |
203 // Returns a slightly modified version of the input vector. | |
204 // | |
205 // - For non-empty inputs a single bit is inverted. | |
206 // - For empty inputs, a byte is added. | |
207 std::vector<uint8_t> Corrupted(const std::vector<uint8_t>& input) { | 132 std::vector<uint8_t> Corrupted(const std::vector<uint8_t>& input) { |
208 std::vector<uint8_t> corrupted_data(input); | 133 std::vector<uint8_t> corrupted_data(input); |
209 if (corrupted_data.empty()) | 134 if (corrupted_data.empty()) |
210 corrupted_data.push_back(0); | 135 corrupted_data.push_back(0); |
211 corrupted_data[corrupted_data.size() / 2] ^= 0x01; | 136 corrupted_data[corrupted_data.size() / 2] ^= 0x01; |
212 return corrupted_data; | 137 return corrupted_data; |
213 } | 138 } |
214 | 139 |
215 std::vector<uint8_t> HexStringToBytes(const std::string& hex) { | 140 std::vector<uint8_t> HexStringToBytes(const std::string& hex) { |
216 std::vector<uint8_t> bytes; | 141 std::vector<uint8_t> bytes; |
217 base::HexStringToBytes(hex, &bytes); | 142 base::HexStringToBytes(hex, &bytes); |
218 return bytes; | 143 return bytes; |
219 } | 144 } |
220 | 145 |
221 std::vector<uint8_t> MakeJsonVector(const std::string& json_string) { | 146 std::vector<uint8_t> MakeJsonVector(const std::string& json_string) { |
222 return std::vector<uint8_t>(json_string.begin(), json_string.end()); | 147 return std::vector<uint8_t>(json_string.begin(), json_string.end()); |
223 } | 148 } |
224 | 149 |
225 std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict) { | 150 std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict) { |
226 std::string json; | 151 std::string json; |
227 base::JSONWriter::Write(&dict, &json); | 152 base::JSONWriter::Write(&dict, &json); |
228 return MakeJsonVector(json); | 153 return MakeJsonVector(json); |
229 } | 154 } |
230 | 155 |
231 // ---------------------------------------------------------------- | |
232 // Helpers for working with JSON data files for test expectations. | |
233 // ---------------------------------------------------------------- | |
234 | |
235 // Reads a file in "src/content/test/data/webcrypto" to a base::Value. | |
236 // The file must be JSON, however it can also include C++ style comments. | |
237 ::testing::AssertionResult ReadJsonTestFile(const char* test_file_name, | 156 ::testing::AssertionResult ReadJsonTestFile(const char* test_file_name, |
238 scoped_ptr<base::Value>* value) { | 157 scoped_ptr<base::Value>* value) { |
239 base::FilePath test_data_dir; | 158 base::FilePath test_data_dir; |
240 if (!PathService::Get(DIR_TEST_DATA, &test_data_dir)) | 159 if (!PathService::Get(DIR_TEST_DATA, &test_data_dir)) |
241 return ::testing::AssertionFailure() << "Couldn't retrieve test dir"; | 160 return ::testing::AssertionFailure() << "Couldn't retrieve test dir"; |
242 | 161 |
243 base::FilePath file_path = | 162 base::FilePath file_path = |
244 test_data_dir.AppendASCII("webcrypto").AppendASCII(test_file_name); | 163 test_data_dir.AppendASCII("webcrypto").AppendASCII(test_file_name); |
245 | 164 |
246 std::string file_contents; | 165 std::string file_contents; |
247 if (!base::ReadFileToString(file_path, &file_contents)) { | 166 if (!base::ReadFileToString(file_path, &file_contents)) { |
248 return ::testing::AssertionFailure() | 167 return ::testing::AssertionFailure() |
249 << "Couldn't read test file: " << file_path.value(); | 168 << "Couldn't read test file: " << file_path.value(); |
250 } | 169 } |
251 | 170 |
252 // Strip C++ style comments out of the "json" file, otherwise it cannot be | 171 // Strip C++ style comments out of the "json" file, otherwise it cannot be |
253 // parsed. | 172 // parsed. |
254 re2::RE2::GlobalReplace(&file_contents, re2::RE2("\\s*//.*"), ""); | 173 re2::RE2::GlobalReplace(&file_contents, re2::RE2("\\s*//.*"), ""); |
255 | 174 |
256 // Parse the JSON to a dictionary. | 175 // Parse the JSON to a dictionary. |
257 value->reset(base::JSONReader::Read(file_contents)); | 176 value->reset(base::JSONReader::Read(file_contents)); |
258 if (!value->get()) { | 177 if (!value->get()) { |
259 return ::testing::AssertionFailure() | 178 return ::testing::AssertionFailure() |
260 << "Couldn't parse test file JSON: " << file_path.value(); | 179 << "Couldn't parse test file JSON: " << file_path.value(); |
261 } | 180 } |
262 | 181 |
263 return ::testing::AssertionSuccess(); | 182 return ::testing::AssertionSuccess(); |
264 } | 183 } |
265 | 184 |
266 // Same as ReadJsonTestFile(), but return the value as a List. | |
267 ::testing::AssertionResult ReadJsonTestFileToList( | 185 ::testing::AssertionResult ReadJsonTestFileToList( |
268 const char* test_file_name, | 186 const char* test_file_name, |
269 scoped_ptr<base::ListValue>* list) { | 187 scoped_ptr<base::ListValue>* list) { |
270 // Read the JSON. | 188 // Read the JSON. |
271 scoped_ptr<base::Value> json; | 189 scoped_ptr<base::Value> json; |
272 ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json); | 190 ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json); |
273 if (!result) | 191 if (!result) |
274 return result; | 192 return result; |
275 | 193 |
276 // Cast to an ListValue. | 194 // Cast to an ListValue. |
277 base::ListValue* list_value = NULL; | 195 base::ListValue* list_value = NULL; |
278 if (!json->GetAsList(&list_value) || !list_value) | 196 if (!json->GetAsList(&list_value) || !list_value) |
279 return ::testing::AssertionFailure() << "The JSON was not a list"; | 197 return ::testing::AssertionFailure() << "The JSON was not a list"; |
280 | 198 |
281 list->reset(list_value); | 199 list->reset(list_value); |
282 ignore_result(json.release()); | 200 ignore_result(json.release()); |
283 | 201 |
284 return ::testing::AssertionSuccess(); | 202 return ::testing::AssertionSuccess(); |
285 } | 203 } |
286 | 204 |
287 // Read a string property from the dictionary with path |property_name| | |
288 // (which can include periods for nested dictionaries). Interprets the | |
289 // string as a hex encoded string and converts it to a bytes list. | |
290 // | |
291 // Returns empty vector on failure. | |
292 std::vector<uint8_t> GetBytesFromHexString(base::DictionaryValue* dict, | 205 std::vector<uint8_t> GetBytesFromHexString(base::DictionaryValue* dict, |
293 const char* property_name) { | 206 const char* property_name) { |
294 std::string hex_string; | 207 std::string hex_string; |
295 if (!dict->GetString(property_name, &hex_string)) { | 208 if (!dict->GetString(property_name, &hex_string)) { |
296 EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; | 209 EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; |
297 return std::vector<uint8_t>(); | 210 return std::vector<uint8_t>(); |
298 } | 211 } |
299 | 212 |
300 return HexStringToBytes(hex_string); | 213 return HexStringToBytes(hex_string); |
301 } | 214 } |
302 | 215 |
303 // Reads a string property with path "property_name" and converts it to a | |
304 // WebCryptoAlgorith. Returns null algorithm on failure. | |
305 blink::WebCryptoAlgorithm GetDigestAlgorithm(base::DictionaryValue* dict, | 216 blink::WebCryptoAlgorithm GetDigestAlgorithm(base::DictionaryValue* dict, |
306 const char* property_name) { | 217 const char* property_name) { |
307 std::string algorithm_name; | 218 std::string algorithm_name; |
308 if (!dict->GetString(property_name, &algorithm_name)) { | 219 if (!dict->GetString(property_name, &algorithm_name)) { |
309 EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; | 220 EXPECT_TRUE(false) << "Couldn't get string property: " << property_name; |
310 return blink::WebCryptoAlgorithm::createNull(); | 221 return blink::WebCryptoAlgorithm::createNull(); |
311 } | 222 } |
312 | 223 |
313 struct { | 224 struct { |
314 const char* name; | 225 const char* name; |
315 blink::WebCryptoAlgorithmId id; | 226 blink::WebCryptoAlgorithmId id; |
316 } kDigestNameToId[] = { | 227 } kDigestNameToId[] = { |
317 {"sha-1", blink::WebCryptoAlgorithmIdSha1}, | 228 {"sha-1", blink::WebCryptoAlgorithmIdSha1}, |
318 {"sha-256", blink::WebCryptoAlgorithmIdSha256}, | 229 {"sha-256", blink::WebCryptoAlgorithmIdSha256}, |
319 {"sha-384", blink::WebCryptoAlgorithmIdSha384}, | 230 {"sha-384", blink::WebCryptoAlgorithmIdSha384}, |
320 {"sha-512", blink::WebCryptoAlgorithmIdSha512}, | 231 {"sha-512", blink::WebCryptoAlgorithmIdSha512}, |
321 }; | 232 }; |
322 | 233 |
323 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDigestNameToId); ++i) { | 234 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDigestNameToId); ++i) { |
324 if (kDigestNameToId[i].name == algorithm_name) | 235 if (kDigestNameToId[i].name == algorithm_name) |
325 return CreateAlgorithm(kDigestNameToId[i].id); | 236 return CreateAlgorithm(kDigestNameToId[i].id); |
326 } | 237 } |
327 | 238 |
328 return blink::WebCryptoAlgorithm::createNull(); | 239 return blink::WebCryptoAlgorithm::createNull(); |
329 } | 240 } |
330 | 241 |
331 // Helper for ImportJwkRsaFailures. Restores the JWK JSON | |
332 // dictionary to a good state | |
333 void RestoreJwkRsaDictionary(base::DictionaryValue* dict) { | |
334 dict->Clear(); | |
335 dict->SetString("kty", "RSA"); | |
336 dict->SetString("alg", "RS256"); | |
337 dict->SetString("use", "sig"); | |
338 dict->SetBoolean("ext", false); | |
339 dict->SetString( | |
340 "n", | |
341 "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" | |
342 "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" | |
343 "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); | |
344 dict->SetString("e", "AQAB"); | |
345 } | |
346 | |
347 // Returns true if any of the vectors in the input list have identical content. | 242 // Returns true if any of the vectors in the input list have identical content. |
348 // Dumb O(n^2) implementation but should be fast enough for the input sizes that | 243 // Dumb O(n^2) implementation but should be fast enough for the input sizes that |
349 // are used. | 244 // are used. |
350 bool CopiesExist(const std::vector<std::vector<uint8_t> >& bufs) { | 245 bool CopiesExist(const std::vector<std::vector<uint8_t> >& bufs) { |
351 for (size_t i = 0; i < bufs.size(); ++i) { | 246 for (size_t i = 0; i < bufs.size(); ++i) { |
352 for (size_t j = i + 1; j < bufs.size(); ++j) { | 247 for (size_t j = i + 1; j < bufs.size(); ++j) { |
353 if (CryptoData(bufs[i]) == CryptoData(bufs[j])) | 248 if (CryptoData(bufs[i]) == CryptoData(bufs[j])) |
354 return true; | 249 return true; |
355 } | 250 } |
356 } | 251 } |
357 return false; | 252 return false; |
358 } | 253 } |
359 | 254 |
360 blink::WebCryptoAlgorithm CreateAesKeyGenAlgorithm( | 255 blink::WebCryptoAlgorithm CreateAesKeyGenAlgorithm( |
361 blink::WebCryptoAlgorithmId aes_alg_id, | 256 blink::WebCryptoAlgorithmId aes_alg_id, |
362 unsigned short length) { | 257 unsigned short length) { |
363 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | 258 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( |
364 aes_alg_id, new blink::WebCryptoAesKeyGenParams(length)); | 259 aes_alg_id, new blink::WebCryptoAesKeyGenParams(length)); |
365 } | 260 } |
366 | 261 |
367 blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm( | |
368 unsigned short key_length_bits) { | |
369 return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesCbc, | |
370 key_length_bits); | |
371 } | |
372 | |
373 blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm( | |
374 unsigned short key_length_bits) { | |
375 EXPECT_TRUE(SupportsAesGcm()); | |
376 return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesGcm, | |
377 key_length_bits); | |
378 } | |
379 | |
380 blink::WebCryptoAlgorithm CreateAesKwKeyGenAlgorithm( | |
381 unsigned short key_length_bits) { | |
382 return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesKw, | |
383 key_length_bits); | |
384 } | |
385 | |
386 // The following key pair is comprised of the SPKI (public key) and PKCS#8 | 262 // The following key pair is comprised of the SPKI (public key) and PKCS#8 |
387 // (private key) representations of the key pair provided in Example 1 of the | 263 // (private key) representations of the key pair provided in Example 1 of the |
388 // NIST test vectors at | 264 // NIST test vectors at |
389 // ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt | 265 // ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt |
390 const unsigned int kModulusLengthBits = 1024; | 266 const unsigned int kModulusLengthBits = 1024; |
391 const char* const kPublicKeySpkiDerHex = | 267 const char* const kPublicKeySpkiDerHex = |
392 "30819f300d06092a864886f70d010101050003818d0030818902818100a5" | 268 "30819f300d06092a864886f70d010101050003818d0030818902818100a5" |
393 "6e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad9" | 269 "6e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad9" |
394 "91d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfc" | 270 "91d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfc" |
395 "e0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e" | 271 "e0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e" |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
479 private_key_usage_mask, | 355 private_key_usage_mask, |
480 private_key)); | 356 private_key)); |
481 EXPECT_FALSE(private_key->isNull()); | 357 EXPECT_FALSE(private_key->isNull()); |
482 EXPECT_TRUE(private_key->handle()); | 358 EXPECT_TRUE(private_key->handle()); |
483 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type()); | 359 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type()); |
484 EXPECT_EQ(algorithm.id(), private_key->algorithm().id()); | 360 EXPECT_EQ(algorithm.id(), private_key->algorithm().id()); |
485 EXPECT_EQ(extractable, private_key->extractable()); | 361 EXPECT_EQ(extractable, private_key->extractable()); |
486 EXPECT_EQ(private_key_usage_mask, private_key->usages()); | 362 EXPECT_EQ(private_key_usage_mask, private_key->usages()); |
487 } | 363 } |
488 | 364 |
489 Status AesGcmEncrypt(const blink::WebCryptoKey& key, | |
490 const std::vector<uint8_t>& iv, | |
491 const std::vector<uint8_t>& additional_data, | |
492 unsigned int tag_length_bits, | |
493 const std::vector<uint8_t>& plain_text, | |
494 std::vector<uint8_t>* cipher_text, | |
495 std::vector<uint8_t>* authentication_tag) { | |
496 EXPECT_TRUE(SupportsAesGcm()); | |
497 blink::WebCryptoAlgorithm algorithm = | |
498 CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); | |
499 | |
500 std::vector<uint8_t> output; | |
501 Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output); | |
502 if (status.IsError()) | |
503 return status; | |
504 | |
505 if ((tag_length_bits % 8) != 0) { | |
506 EXPECT_TRUE(false) << "Encrypt should have failed."; | |
507 return Status::OperationError(); | |
508 } | |
509 | |
510 size_t tag_length_bytes = tag_length_bits / 8; | |
511 | |
512 if (tag_length_bytes > output.size()) { | |
513 EXPECT_TRUE(false) << "tag length is larger than output"; | |
514 return Status::OperationError(); | |
515 } | |
516 | |
517 // The encryption result is cipher text with authentication tag appended. | |
518 cipher_text->assign(output.begin(), | |
519 output.begin() + (output.size() - tag_length_bytes)); | |
520 authentication_tag->assign(output.begin() + cipher_text->size(), | |
521 output.end()); | |
522 | |
523 return Status::Success(); | |
524 } | |
525 | |
526 Status AesGcmDecrypt(const blink::WebCryptoKey& key, | |
527 const std::vector<uint8_t>& iv, | |
528 const std::vector<uint8_t>& additional_data, | |
529 unsigned int tag_length_bits, | |
530 const std::vector<uint8_t>& cipher_text, | |
531 const std::vector<uint8_t>& authentication_tag, | |
532 std::vector<uint8_t>* plain_text) { | |
533 EXPECT_TRUE(SupportsAesGcm()); | |
534 blink::WebCryptoAlgorithm algorithm = | |
535 CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); | |
536 | |
537 // Join cipher text and authentication tag. | |
538 std::vector<uint8_t> cipher_text_with_tag; | |
539 cipher_text_with_tag.reserve(cipher_text.size() + authentication_tag.size()); | |
540 cipher_text_with_tag.insert( | |
541 cipher_text_with_tag.end(), cipher_text.begin(), cipher_text.end()); | |
542 cipher_text_with_tag.insert(cipher_text_with_tag.end(), | |
543 authentication_tag.begin(), | |
544 authentication_tag.end()); | |
545 | |
546 return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text); | |
547 } | |
548 | |
549 Status ImportKeyJwk(const CryptoData& key_data, | |
550 const blink::WebCryptoAlgorithm& algorithm, | |
551 bool extractable, | |
552 blink::WebCryptoKeyUsageMask usage_mask, | |
553 blink::WebCryptoKey* key) { | |
554 return ImportKey(blink::WebCryptoKeyFormatJwk, | |
555 key_data, | |
556 algorithm, | |
557 extractable, | |
558 usage_mask, | |
559 key); | |
560 } | |
561 | |
562 Status ImportKeyJwkFromDict(const base::DictionaryValue& dict, | 365 Status ImportKeyJwkFromDict(const base::DictionaryValue& dict, |
563 const blink::WebCryptoAlgorithm& algorithm, | 366 const blink::WebCryptoAlgorithm& algorithm, |
564 bool extractable, | 367 bool extractable, |
565 blink::WebCryptoKeyUsageMask usage_mask, | 368 blink::WebCryptoKeyUsageMask usage_mask, |
566 blink::WebCryptoKey* key) { | 369 blink::WebCryptoKey* key) { |
567 return ImportKeyJwk(CryptoData(MakeJsonVector(dict)), | 370 return ImportKey(blink::WebCryptoKeyFormatJwk, |
568 algorithm, | 371 CryptoData(MakeJsonVector(dict)), |
569 extractable, | 372 algorithm, |
570 usage_mask, | 373 extractable, |
571 key); | 374 usage_mask, |
| 375 key); |
572 } | 376 } |
573 | 377 |
574 // Parses a vector of JSON into a dictionary. | |
575 scoped_ptr<base::DictionaryValue> GetJwkDictionary( | 378 scoped_ptr<base::DictionaryValue> GetJwkDictionary( |
576 const std::vector<uint8_t>& json) { | 379 const std::vector<uint8_t>& json) { |
577 base::StringPiece json_string( | 380 base::StringPiece json_string( |
578 reinterpret_cast<const char*>(vector_as_array(&json)), json.size()); | 381 reinterpret_cast<const char*>(vector_as_array(&json)), json.size()); |
579 base::Value* value = base::JSONReader::Read(json_string); | 382 base::Value* value = base::JSONReader::Read(json_string); |
580 EXPECT_TRUE(value); | 383 EXPECT_TRUE(value); |
581 base::DictionaryValue* dict_value = NULL; | 384 base::DictionaryValue* dict_value = NULL; |
582 value->GetAsDictionary(&dict_value); | 385 value->GetAsDictionary(&dict_value); |
583 return scoped_ptr<base::DictionaryValue>(dict_value); | 386 return scoped_ptr<base::DictionaryValue>(dict_value); |
584 } | 387 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
625 if (status.IsError()) | 428 if (status.IsError()) |
626 return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; | 429 return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; |
627 if (key_ops_mask != use_mask_expected) | 430 if (key_ops_mask != use_mask_expected) |
628 return ::testing::AssertionFailure() | 431 return ::testing::AssertionFailure() |
629 << "Expected 'key_ops' mask to be " << use_mask_expected | 432 << "Expected 'key_ops' mask to be " << use_mask_expected |
630 << " but found " << key_ops_mask << " (" << value_string << ")"; | 433 << " but found " << key_ops_mask << " (" << value_string << ")"; |
631 | 434 |
632 return ::testing::AssertionSuccess(); | 435 return ::testing::AssertionSuccess(); |
633 } | 436 } |
634 | 437 |
635 // Verifies that the JSON in the input vector contains the provided | |
636 // expected values. Exact matches are required on the fields examined. | |
637 ::testing::AssertionResult VerifySecretJwk( | 438 ::testing::AssertionResult VerifySecretJwk( |
638 const std::vector<uint8_t>& json, | 439 const std::vector<uint8_t>& json, |
639 const std::string& alg_expected, | 440 const std::string& alg_expected, |
640 const std::string& k_expected_hex, | 441 const std::string& k_expected_hex, |
641 blink::WebCryptoKeyUsageMask use_mask_expected) { | 442 blink::WebCryptoKeyUsageMask use_mask_expected) { |
642 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); | 443 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); |
643 if (!dict.get() || dict->empty()) | 444 if (!dict.get() || dict->empty()) |
644 return ::testing::AssertionFailure() << "JSON parsing failed"; | 445 return ::testing::AssertionFailure() << "JSON parsing failed"; |
645 | 446 |
646 // ---- k | 447 // ---- k |
647 std::string value_string; | 448 std::string value_string; |
648 if (!dict->GetString("k", &value_string)) | 449 if (!dict->GetString("k", &value_string)) |
649 return ::testing::AssertionFailure() << "Missing 'k'"; | 450 return ::testing::AssertionFailure() << "Missing 'k'"; |
650 std::string k_value; | 451 std::string k_value; |
651 if (!Base64DecodeUrlSafe(value_string, &k_value)) | 452 if (!Base64DecodeUrlSafe(value_string, &k_value)) |
652 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; | 453 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; |
653 if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()), | 454 if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()), |
654 k_expected_hex.c_str())) { | 455 k_expected_hex.c_str())) { |
655 return ::testing::AssertionFailure() << "Expected 'k' to be " | 456 return ::testing::AssertionFailure() << "Expected 'k' to be " |
656 << k_expected_hex | 457 << k_expected_hex |
657 << " but found something different"; | 458 << " but found something different"; |
658 } | 459 } |
659 | 460 |
660 return VerifyJwk(dict, "oct", alg_expected, use_mask_expected); | 461 return VerifyJwk(dict, "oct", alg_expected, use_mask_expected); |
661 } | 462 } |
662 | 463 |
663 // Verifies that the JSON in the input vector contains the provided | |
664 // expected values. Exact matches are required on the fields examined. | |
665 ::testing::AssertionResult VerifyPublicJwk( | 464 ::testing::AssertionResult VerifyPublicJwk( |
666 const std::vector<uint8_t>& json, | 465 const std::vector<uint8_t>& json, |
667 const std::string& alg_expected, | 466 const std::string& alg_expected, |
668 const std::string& n_expected_hex, | 467 const std::string& n_expected_hex, |
669 const std::string& e_expected_hex, | 468 const std::string& e_expected_hex, |
670 blink::WebCryptoKeyUsageMask use_mask_expected) { | 469 blink::WebCryptoKeyUsageMask use_mask_expected) { |
671 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); | 470 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); |
672 if (!dict.get() || dict->empty()) | 471 if (!dict.get() || dict->empty()) |
673 return ::testing::AssertionFailure() << "JSON parsing failed"; | 472 return ::testing::AssertionFailure() << "JSON parsing failed"; |
674 | 473 |
(...skipping 19 matching lines...) Expand all Loading... |
694 if (!LowerCaseEqualsASCII(base::HexEncode(e_value.data(), e_value.size()), | 493 if (!LowerCaseEqualsASCII(base::HexEncode(e_value.data(), e_value.size()), |
695 e_expected_hex.c_str())) { | 494 e_expected_hex.c_str())) { |
696 return ::testing::AssertionFailure() << "Expected 'e' to be " | 495 return ::testing::AssertionFailure() << "Expected 'e' to be " |
697 << e_expected_hex | 496 << e_expected_hex |
698 << " but found something different"; | 497 << " but found something different"; |
699 } | 498 } |
700 | 499 |
701 return VerifyJwk(dict, "RSA", alg_expected, use_mask_expected); | 500 return VerifyJwk(dict, "RSA", alg_expected, use_mask_expected); |
702 } | 501 } |
703 | 502 |
704 // Tests several Status objects against their expected hard coded values, as | |
705 // well as ensuring that comparison of Status objects works. | |
706 // Comparison should take into account both the error details, as well as the | |
707 // error type. | |
708 TEST(WebCryptoStatusTest, Basic) { | |
709 // Even though the error message is the same, these should not be considered | |
710 // the same by the tests because the error type is different. | |
711 EXPECT_NE(Status::DataError(), Status::OperationError()); | |
712 EXPECT_NE(Status::Success(), Status::OperationError()); | |
713 | |
714 EXPECT_EQ(Status::Success(), Status::Success()); | |
715 EXPECT_EQ(Status::ErrorJwkPropertyWrongType("kty", "string"), | |
716 Status::ErrorJwkPropertyWrongType("kty", "string")); | |
717 | |
718 Status status = Status::Success(); | |
719 | |
720 EXPECT_FALSE(status.IsError()); | |
721 EXPECT_EQ("", status.error_details()); | |
722 | |
723 status = Status::OperationError(); | |
724 EXPECT_TRUE(status.IsError()); | |
725 EXPECT_EQ("", status.error_details()); | |
726 EXPECT_EQ(blink::WebCryptoErrorTypeOperation, status.error_type()); | |
727 | |
728 status = Status::DataError(); | |
729 EXPECT_TRUE(status.IsError()); | |
730 EXPECT_EQ("", status.error_details()); | |
731 EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); | |
732 | |
733 status = Status::ErrorUnsupported(); | |
734 EXPECT_TRUE(status.IsError()); | |
735 EXPECT_EQ("The requested operation is unsupported", status.error_details()); | |
736 EXPECT_EQ(blink::WebCryptoErrorTypeNotSupported, status.error_type()); | |
737 | |
738 status = Status::ErrorJwkPropertyMissing("kty"); | |
739 EXPECT_TRUE(status.IsError()); | |
740 EXPECT_EQ("The required JWK property \"kty\" was missing", | |
741 status.error_details()); | |
742 EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); | |
743 | |
744 status = Status::ErrorJwkPropertyWrongType("kty", "string"); | |
745 EXPECT_TRUE(status.IsError()); | |
746 EXPECT_EQ("The JWK property \"kty\" must be a string", | |
747 status.error_details()); | |
748 EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); | |
749 | |
750 status = Status::ErrorJwkBase64Decode("n"); | |
751 EXPECT_TRUE(status.IsError()); | |
752 EXPECT_EQ("The JWK property \"n\" could not be base64 decoded", | |
753 status.error_details()); | |
754 EXPECT_EQ(blink::WebCryptoErrorTypeData, status.error_type()); | |
755 } | |
756 | |
757 TEST(WebCryptoShaTest, DigestSampleSets) { | |
758 scoped_ptr<base::ListValue> tests; | |
759 // TODO(eroman): rename to sha.json | |
760 ASSERT_TRUE(ReadJsonTestFileToList("digest.json", &tests)); | |
761 | |
762 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
763 SCOPED_TRACE(test_index); | |
764 base::DictionaryValue* test; | |
765 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
766 | |
767 blink::WebCryptoAlgorithm test_algorithm = | |
768 GetDigestAlgorithm(test, "algorithm"); | |
769 std::vector<uint8_t> test_input = GetBytesFromHexString(test, "input"); | |
770 std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output"); | |
771 | |
772 std::vector<uint8_t> output; | |
773 ASSERT_EQ(Status::Success(), | |
774 Digest(test_algorithm, CryptoData(test_input), &output)); | |
775 EXPECT_BYTES_EQ(test_output, output); | |
776 } | |
777 } | |
778 | |
779 TEST(WebCryptoShaTest, DigestSampleSetsInChunks) { | |
780 scoped_ptr<base::ListValue> tests; | |
781 ASSERT_TRUE(ReadJsonTestFileToList("digest.json", &tests)); | |
782 | |
783 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
784 SCOPED_TRACE(test_index); | |
785 base::DictionaryValue* test; | |
786 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
787 | |
788 blink::WebCryptoAlgorithm test_algorithm = | |
789 GetDigestAlgorithm(test, "algorithm"); | |
790 std::vector<uint8_t> test_input = GetBytesFromHexString(test, "input"); | |
791 std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output"); | |
792 | |
793 // Test the chunk version of the digest functions. Test with 129 byte chunks | |
794 // because the SHA-512 chunk size is 128 bytes. | |
795 unsigned char* output; | |
796 unsigned int output_length; | |
797 static const size_t kChunkSizeBytes = 129; | |
798 size_t length = test_input.size(); | |
799 scoped_ptr<blink::WebCryptoDigestor> digestor( | |
800 CreateDigestor(test_algorithm.id())); | |
801 std::vector<uint8_t>::iterator begin = test_input.begin(); | |
802 size_t chunk_index = 0; | |
803 while (begin != test_input.end()) { | |
804 size_t chunk_length = std::min(kChunkSizeBytes, length - chunk_index); | |
805 std::vector<uint8_t> chunk(begin, begin + chunk_length); | |
806 ASSERT_TRUE(chunk.size() > 0); | |
807 EXPECT_TRUE(digestor->consume(&chunk.front(), chunk.size())); | |
808 chunk_index = chunk_index + chunk_length; | |
809 begin = begin + chunk_length; | |
810 } | |
811 EXPECT_TRUE(digestor->finish(output, output_length)); | |
812 EXPECT_BYTES_EQ(test_output, CryptoData(output, output_length)); | |
813 } | |
814 } | |
815 | |
816 TEST(WebCryptoHmacTest, HMACSampleSets) { | |
817 scoped_ptr<base::ListValue> tests; | |
818 ASSERT_TRUE(ReadJsonTestFileToList("hmac.json", &tests)); | |
819 // TODO(padolph): Missing known answer tests for HMAC SHA384, and SHA512. | |
820 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
821 SCOPED_TRACE(test_index); | |
822 base::DictionaryValue* test; | |
823 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
824 | |
825 blink::WebCryptoAlgorithm test_hash = GetDigestAlgorithm(test, "hash"); | |
826 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
827 const std::vector<uint8_t> test_message = | |
828 GetBytesFromHexString(test, "message"); | |
829 const std::vector<uint8_t> test_mac = GetBytesFromHexString(test, "mac"); | |
830 | |
831 blink::WebCryptoAlgorithm algorithm = | |
832 CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac); | |
833 | |
834 blink::WebCryptoAlgorithm import_algorithm = | |
835 CreateHmacImportAlgorithm(test_hash.id()); | |
836 | |
837 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
838 test_key, | |
839 import_algorithm, | |
840 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify); | |
841 | |
842 EXPECT_EQ(test_hash.id(), key.algorithm().hmacParams()->hash().id()); | |
843 EXPECT_EQ(test_key.size() * 8, key.algorithm().hmacParams()->lengthBits()); | |
844 | |
845 // Verify exported raw key is identical to the imported data | |
846 std::vector<uint8_t> raw_key; | |
847 EXPECT_EQ(Status::Success(), | |
848 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
849 EXPECT_BYTES_EQ(test_key, raw_key); | |
850 | |
851 std::vector<uint8_t> output; | |
852 | |
853 ASSERT_EQ(Status::Success(), | |
854 Sign(algorithm, key, CryptoData(test_message), &output)); | |
855 | |
856 EXPECT_BYTES_EQ(test_mac, output); | |
857 | |
858 bool signature_match = false; | |
859 EXPECT_EQ(Status::Success(), | |
860 Verify(algorithm, | |
861 key, | |
862 CryptoData(output), | |
863 CryptoData(test_message), | |
864 &signature_match)); | |
865 EXPECT_TRUE(signature_match); | |
866 | |
867 // Ensure truncated signature does not verify by passing one less byte. | |
868 EXPECT_EQ(Status::Success(), | |
869 Verify(algorithm, | |
870 key, | |
871 CryptoData(vector_as_array(&output), output.size() - 1), | |
872 CryptoData(test_message), | |
873 &signature_match)); | |
874 EXPECT_FALSE(signature_match); | |
875 | |
876 // Ensure truncated signature does not verify by passing no bytes. | |
877 EXPECT_EQ(Status::Success(), | |
878 Verify(algorithm, | |
879 key, | |
880 CryptoData(), | |
881 CryptoData(test_message), | |
882 &signature_match)); | |
883 EXPECT_FALSE(signature_match); | |
884 | |
885 // Ensure extra long signature does not cause issues and fails. | |
886 const unsigned char kLongSignature[1024] = {0}; | |
887 EXPECT_EQ(Status::Success(), | |
888 Verify(algorithm, | |
889 key, | |
890 CryptoData(kLongSignature, sizeof(kLongSignature)), | |
891 CryptoData(test_message), | |
892 &signature_match)); | |
893 EXPECT_FALSE(signature_match); | |
894 } | |
895 } | |
896 | |
897 blink::WebCryptoKey GetTestAesCbcKey() { | |
898 const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c"; | |
899 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
900 HexStringToBytes(key_hex), | |
901 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
902 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); | |
903 | |
904 // Verify exported raw key is identical to the imported data | |
905 std::vector<uint8_t> raw_key; | |
906 EXPECT_EQ(Status::Success(), | |
907 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
908 EXPECT_BYTES_EQ_HEX(key_hex, raw_key); | |
909 return key; | |
910 } | |
911 | |
912 TEST(WebCryptoAesCbcTest, IvTooSmall) { | |
913 std::vector<uint8_t> output; | |
914 | |
915 // Use an invalid |iv| (fewer than 16 bytes) | |
916 std::vector<uint8_t> input(32); | |
917 std::vector<uint8_t> iv; | |
918 EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), | |
919 Encrypt(CreateAesCbcAlgorithm(iv), | |
920 GetTestAesCbcKey(), | |
921 CryptoData(input), | |
922 &output)); | |
923 EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), | |
924 Decrypt(CreateAesCbcAlgorithm(iv), | |
925 GetTestAesCbcKey(), | |
926 CryptoData(input), | |
927 &output)); | |
928 } | |
929 | |
930 TEST(WebCryptoAesCbcTest, IvTooLarge) { | |
931 std::vector<uint8_t> output; | |
932 | |
933 // Use an invalid |iv| (more than 16 bytes) | |
934 std::vector<uint8_t> input(32); | |
935 std::vector<uint8_t> iv(17); | |
936 EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), | |
937 Encrypt(CreateAesCbcAlgorithm(iv), | |
938 GetTestAesCbcKey(), | |
939 CryptoData(input), | |
940 &output)); | |
941 EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(), | |
942 Decrypt(CreateAesCbcAlgorithm(iv), | |
943 GetTestAesCbcKey(), | |
944 CryptoData(input), | |
945 &output)); | |
946 } | |
947 | |
948 TEST(WebCryptoAesCbcTest, InputTooLarge) { | |
949 std::vector<uint8_t> output; | |
950 | |
951 // Give an input that is too large (would cause integer overflow when | |
952 // narrowing to an int). Note that both OpenSSL and NSS operate on signed int | |
953 // lengths. | |
954 std::vector<uint8_t> iv(16); | |
955 | |
956 // Pretend the input is large. Don't pass data pointer as NULL in case that | |
957 // is special cased; the implementation shouldn't actually dereference the | |
958 // data. | |
959 CryptoData input(&iv[0], INT_MAX - 3); | |
960 | |
961 EXPECT_EQ( | |
962 Status::ErrorDataTooLarge(), | |
963 Encrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output)); | |
964 EXPECT_EQ( | |
965 Status::ErrorDataTooLarge(), | |
966 Decrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output)); | |
967 } | |
968 | |
969 TEST(WebCryptoAesCbcTest, KeyTooSmall) { | |
970 std::vector<uint8_t> output; | |
971 | |
972 // Fail importing the key (too few bytes specified) | |
973 std::vector<uint8_t> key_raw(1); | |
974 std::vector<uint8_t> iv(16); | |
975 | |
976 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
977 EXPECT_EQ(Status::ErrorImportAesKeyLength(), | |
978 ImportKey(blink::WebCryptoKeyFormatRaw, | |
979 CryptoData(key_raw), | |
980 CreateAesCbcAlgorithm(iv), | |
981 true, | |
982 blink::WebCryptoKeyUsageEncrypt, | |
983 &key)); | |
984 } | |
985 | |
986 TEST(WebCryptoAesCbcTest, ExportKeyUnsupportedFormat) { | |
987 std::vector<uint8_t> output; | |
988 | |
989 // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret | |
990 // keys). | |
991 EXPECT_EQ( | |
992 Status::ErrorUnsupportedExportKeyFormat(), | |
993 ExportKey(blink::WebCryptoKeyFormatSpki, GetTestAesCbcKey(), &output)); | |
994 EXPECT_EQ( | |
995 Status::ErrorUnsupportedExportKeyFormat(), | |
996 ExportKey(blink::WebCryptoKeyFormatPkcs8, GetTestAesCbcKey(), &output)); | |
997 } | |
998 | |
999 TEST(WebCryptoAesCbcTest, ImportKeyUnsupportedFormat) { | |
1000 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1001 ASSERT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
1002 ImportKey(blink::WebCryptoKeyFormatSpki, | |
1003 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
1004 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1005 true, | |
1006 blink::WebCryptoKeyUsageEncrypt, | |
1007 &key)); | |
1008 ASSERT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
1009 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
1010 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
1011 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1012 true, | |
1013 blink::WebCryptoKeyUsageEncrypt, | |
1014 &key)); | |
1015 } | |
1016 | |
1017 TEST(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) { | |
1018 scoped_ptr<base::ListValue> tests; | |
1019 ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests)); | |
1020 | |
1021 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
1022 SCOPED_TRACE(test_index); | |
1023 base::DictionaryValue* test; | |
1024 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
1025 | |
1026 std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
1027 std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); | |
1028 std::vector<uint8_t> test_plain_text = | |
1029 GetBytesFromHexString(test, "plain_text"); | |
1030 std::vector<uint8_t> test_cipher_text = | |
1031 GetBytesFromHexString(test, "cipher_text"); | |
1032 | |
1033 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
1034 test_key, | |
1035 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1036 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); | |
1037 | |
1038 EXPECT_EQ(test_key.size() * 8, key.algorithm().aesParams()->lengthBits()); | |
1039 | |
1040 // Verify exported raw key is identical to the imported data | |
1041 std::vector<uint8_t> raw_key; | |
1042 EXPECT_EQ(Status::Success(), | |
1043 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
1044 EXPECT_BYTES_EQ(test_key, raw_key); | |
1045 | |
1046 std::vector<uint8_t> output; | |
1047 | |
1048 // Test encryption. | |
1049 EXPECT_EQ(Status::Success(), | |
1050 Encrypt(CreateAesCbcAlgorithm(test_iv), | |
1051 key, | |
1052 CryptoData(test_plain_text), | |
1053 &output)); | |
1054 EXPECT_BYTES_EQ(test_cipher_text, output); | |
1055 | |
1056 // Test decryption. | |
1057 EXPECT_EQ(Status::Success(), | |
1058 Decrypt(CreateAesCbcAlgorithm(test_iv), | |
1059 key, | |
1060 CryptoData(test_cipher_text), | |
1061 &output)); | |
1062 EXPECT_BYTES_EQ(test_plain_text, output); | |
1063 } | |
1064 } | |
1065 | |
1066 TEST(WebCryptoAesCbcTest, DecryptTruncatedCipherText) { | |
1067 scoped_ptr<base::ListValue> tests; | |
1068 ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests)); | |
1069 | |
1070 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
1071 SCOPED_TRACE(test_index); | |
1072 base::DictionaryValue* test; | |
1073 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
1074 | |
1075 std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
1076 std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); | |
1077 std::vector<uint8_t> test_cipher_text = | |
1078 GetBytesFromHexString(test, "cipher_text"); | |
1079 | |
1080 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
1081 test_key, | |
1082 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1083 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); | |
1084 | |
1085 std::vector<uint8_t> output; | |
1086 | |
1087 const unsigned int kAesCbcBlockSize = 16; | |
1088 | |
1089 // Decrypt with a padding error by stripping the last block. This also ends | |
1090 // up testing decryption over empty cipher text. | |
1091 if (test_cipher_text.size() >= kAesCbcBlockSize) { | |
1092 EXPECT_EQ(Status::OperationError(), | |
1093 Decrypt(CreateAesCbcAlgorithm(test_iv), | |
1094 key, | |
1095 CryptoData(&test_cipher_text[0], | |
1096 test_cipher_text.size() - kAesCbcBlockSize), | |
1097 &output)); | |
1098 } | |
1099 | |
1100 // Decrypt cipher text which is not a multiple of block size by stripping | |
1101 // a few bytes off the cipher text. | |
1102 if (test_cipher_text.size() > 3) { | |
1103 EXPECT_EQ( | |
1104 Status::OperationError(), | |
1105 Decrypt(CreateAesCbcAlgorithm(test_iv), | |
1106 key, | |
1107 CryptoData(&test_cipher_text[0], test_cipher_text.size() - 3), | |
1108 &output)); | |
1109 } | |
1110 } | |
1111 } | |
1112 | |
1113 // TODO(eroman): Do this same test for AES-GCM, AES-KW, AES-CTR ? | |
1114 TEST(WebCryptoAesCbcTest, GenerateKeyIsRandom) { | |
1115 // Check key generation for each allowed key length. | |
1116 std::vector<blink::WebCryptoAlgorithm> algorithm; | |
1117 const unsigned short kKeyLength[] = {128, 256}; | |
1118 for (size_t key_length_i = 0; key_length_i < ARRAYSIZE_UNSAFE(kKeyLength); | |
1119 ++key_length_i) { | |
1120 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1121 | |
1122 std::vector<std::vector<uint8_t> > keys; | |
1123 std::vector<uint8_t> key_bytes; | |
1124 | |
1125 // Generate a small sample of keys. | |
1126 for (int j = 0; j < 16; ++j) { | |
1127 ASSERT_EQ(Status::Success(), | |
1128 GenerateSecretKey( | |
1129 CreateAesCbcKeyGenAlgorithm(kKeyLength[key_length_i]), | |
1130 true, | |
1131 0, | |
1132 &key)); | |
1133 EXPECT_TRUE(key.handle()); | |
1134 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
1135 ASSERT_EQ(Status::Success(), | |
1136 ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_bytes)); | |
1137 EXPECT_EQ(key_bytes.size() * 8, | |
1138 key.algorithm().aesParams()->lengthBits()); | |
1139 keys.push_back(key_bytes); | |
1140 } | |
1141 // Ensure all entries in the key sample set are unique. This is a simplistic | |
1142 // estimate of whether the generated keys appear random. | |
1143 EXPECT_FALSE(CopiesExist(keys)); | |
1144 } | |
1145 } | |
1146 | |
1147 TEST(WebCryptoAesCbcTest, GenerateKeyBadLength) { | |
1148 const unsigned short kKeyLen[] = {0, 127, 257}; | |
1149 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1150 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLen); ++i) { | |
1151 SCOPED_TRACE(i); | |
1152 EXPECT_EQ(Status::ErrorGenerateKeyLength(), | |
1153 GenerateSecretKey( | |
1154 CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); | |
1155 } | |
1156 } | |
1157 | |
1158 TEST(WebCryptoAesKwTest, GenerateKeyBadLength) { | |
1159 const unsigned short kKeyLen[] = {0, 127, 257}; | |
1160 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1161 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLen); ++i) { | |
1162 SCOPED_TRACE(i); | |
1163 EXPECT_EQ(Status::ErrorGenerateKeyLength(), | |
1164 GenerateSecretKey( | |
1165 CreateAesKwKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); | |
1166 } | |
1167 } | |
1168 | |
1169 TEST(WebCryptoAesGcmTest, GenerateKeyBadLength) { | |
1170 if (!SupportsAesGcm()) | |
1171 return; | |
1172 | |
1173 const unsigned short kKeyLen[] = {0, 127, 257}; | |
1174 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1175 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLen); ++i) { | |
1176 SCOPED_TRACE(i); | |
1177 EXPECT_EQ(Status::ErrorGenerateKeyLength(), | |
1178 GenerateSecretKey( | |
1179 CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, 0, &key)); | |
1180 } | |
1181 } | |
1182 | |
1183 TEST(WebCryptoHmacTest, GenerateKeyIsRandom) { | |
1184 // Generate a small sample of HMAC keys. | |
1185 std::vector<std::vector<uint8_t> > keys; | |
1186 for (int i = 0; i < 16; ++i) { | |
1187 std::vector<uint8_t> key_bytes; | |
1188 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1189 blink::WebCryptoAlgorithm algorithm = | |
1190 CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 512); | |
1191 ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); | |
1192 EXPECT_FALSE(key.isNull()); | |
1193 EXPECT_TRUE(key.handle()); | |
1194 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
1195 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
1196 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
1197 key.algorithm().hmacParams()->hash().id()); | |
1198 EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); | |
1199 | |
1200 std::vector<uint8_t> raw_key; | |
1201 ASSERT_EQ(Status::Success(), | |
1202 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
1203 EXPECT_EQ(64U, raw_key.size()); | |
1204 keys.push_back(raw_key); | |
1205 } | |
1206 // Ensure all entries in the key sample set are unique. This is a simplistic | |
1207 // estimate of whether the generated keys appear random. | |
1208 EXPECT_FALSE(CopiesExist(keys)); | |
1209 } | |
1210 | |
1211 // If the key length is not provided, then the block size is used. | |
1212 TEST(WebCryptoHmacTest, GenerateKeyNoLengthSha1) { | |
1213 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1214 blink::WebCryptoAlgorithm algorithm = | |
1215 CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); | |
1216 ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); | |
1217 EXPECT_TRUE(key.handle()); | |
1218 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
1219 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
1220 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
1221 key.algorithm().hmacParams()->hash().id()); | |
1222 EXPECT_EQ(512u, key.algorithm().hmacParams()->lengthBits()); | |
1223 std::vector<uint8_t> raw_key; | |
1224 ASSERT_EQ(Status::Success(), | |
1225 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
1226 EXPECT_EQ(64U, raw_key.size()); | |
1227 } | |
1228 | |
1229 // If the key length is not provided, then the block size is used. | |
1230 TEST(WebCryptoHmacTest, GenerateKeyNoLengthSha512) { | |
1231 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1232 blink::WebCryptoAlgorithm algorithm = | |
1233 CreateHmacKeyGenAlgorithm(blink::WebCryptoAlgorithmIdSha512, 0); | |
1234 ASSERT_EQ(Status::Success(), GenerateSecretKey(algorithm, true, 0, &key)); | |
1235 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
1236 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha512, | |
1237 key.algorithm().hmacParams()->hash().id()); | |
1238 EXPECT_EQ(1024u, key.algorithm().hmacParams()->lengthBits()); | |
1239 std::vector<uint8_t> raw_key; | |
1240 ASSERT_EQ(Status::Success(), | |
1241 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
1242 EXPECT_EQ(128U, raw_key.size()); | |
1243 } | |
1244 | |
1245 // If key_ops is specified but empty, no key usages are allowed for the key. | |
1246 TEST(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) { | |
1247 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1248 base::DictionaryValue dict; | |
1249 dict.SetString("kty", "oct"); | |
1250 dict.SetBoolean("ext", false); | |
1251 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1252 dict.Set("key_ops", new base::ListValue); // Takes ownership. | |
1253 | |
1254 EXPECT_EQ( | |
1255 Status::Success(), | |
1256 ImportKeyJwkFromDict(dict, | |
1257 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1258 false, | |
1259 0, | |
1260 &key)); | |
1261 | |
1262 EXPECT_EQ(0, key.usages()); | |
1263 | |
1264 // The JWK does not contain encrypt usages. | |
1265 EXPECT_EQ( | |
1266 Status::ErrorJwkKeyopsInconsistent(), | |
1267 ImportKeyJwkFromDict(dict, | |
1268 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1269 false, | |
1270 blink::WebCryptoKeyUsageEncrypt, | |
1271 &key)); | |
1272 | |
1273 // The JWK does not contain sign usage (nor is it applicable). | |
1274 EXPECT_EQ( | |
1275 Status::ErrorCreateKeyBadUsages(), | |
1276 ImportKeyJwkFromDict(dict, | |
1277 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1278 false, | |
1279 blink::WebCryptoKeyUsageSign, | |
1280 &key)); | |
1281 } | |
1282 | |
1283 // If key_ops is missing, then any key usages can be specified. | |
1284 TEST(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) { | |
1285 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1286 base::DictionaryValue dict; | |
1287 dict.SetString("kty", "oct"); | |
1288 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1289 | |
1290 EXPECT_EQ( | |
1291 Status::Success(), | |
1292 ImportKeyJwkFromDict(dict, | |
1293 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1294 false, | |
1295 blink::WebCryptoKeyUsageEncrypt, | |
1296 &key)); | |
1297 | |
1298 EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages()); | |
1299 | |
1300 // The JWK does not contain sign usage (nor is it applicable). | |
1301 EXPECT_EQ( | |
1302 Status::ErrorCreateKeyBadUsages(), | |
1303 ImportKeyJwkFromDict(dict, | |
1304 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1305 false, | |
1306 blink::WebCryptoKeyUsageVerify, | |
1307 &key)); | |
1308 } | |
1309 | |
1310 TEST(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) { | |
1311 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1312 base::DictionaryValue dict; | |
1313 dict.SetString("kty", "oct"); | |
1314 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1315 base::ListValue* key_ops = new base::ListValue; | |
1316 dict.Set("key_ops", key_ops); // Takes ownership. | |
1317 | |
1318 key_ops->AppendString("encrypt"); | |
1319 | |
1320 EXPECT_EQ( | |
1321 Status::Success(), | |
1322 ImportKeyJwkFromDict(dict, | |
1323 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1324 false, | |
1325 blink::WebCryptoKeyUsageEncrypt, | |
1326 &key)); | |
1327 | |
1328 EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages()); | |
1329 | |
1330 key_ops->AppendString("decrypt"); | |
1331 | |
1332 EXPECT_EQ( | |
1333 Status::Success(), | |
1334 ImportKeyJwkFromDict(dict, | |
1335 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1336 false, | |
1337 blink::WebCryptoKeyUsageDecrypt, | |
1338 &key)); | |
1339 | |
1340 EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt, key.usages()); | |
1341 | |
1342 EXPECT_EQ( | |
1343 Status::Success(), | |
1344 ImportKeyJwkFromDict( | |
1345 dict, | |
1346 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1347 false, | |
1348 blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt, | |
1349 &key)); | |
1350 | |
1351 EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
1352 key.usages()); | |
1353 } | |
1354 | |
1355 // Test failure if input usage is NOT a strict subset of the JWK usage. | |
1356 TEST(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) { | |
1357 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1358 base::DictionaryValue dict; | |
1359 dict.SetString("kty", "oct"); | |
1360 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1361 base::ListValue* key_ops = new base::ListValue; | |
1362 dict.Set("key_ops", key_ops); // Takes ownership. | |
1363 | |
1364 key_ops->AppendString("encrypt"); | |
1365 | |
1366 EXPECT_EQ( | |
1367 Status::ErrorJwkKeyopsInconsistent(), | |
1368 ImportKeyJwkFromDict( | |
1369 dict, | |
1370 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1371 false, | |
1372 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
1373 &key)); | |
1374 } | |
1375 | |
1376 TEST(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) { | |
1377 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1378 base::DictionaryValue dict; | |
1379 dict.SetString("kty", "oct"); | |
1380 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1381 base::ListValue* key_ops = new base::ListValue; | |
1382 dict.Set("key_ops", key_ops); // Takes ownership. | |
1383 | |
1384 key_ops->AppendString("sign"); | |
1385 | |
1386 EXPECT_EQ(Status::Success(), | |
1387 ImportKeyJwkFromDict( | |
1388 dict, | |
1389 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
1390 false, | |
1391 blink::WebCryptoKeyUsageSign, | |
1392 &key)); | |
1393 | |
1394 EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); | |
1395 | |
1396 key_ops->AppendString("verify"); | |
1397 | |
1398 EXPECT_EQ(Status::Success(), | |
1399 ImportKeyJwkFromDict( | |
1400 dict, | |
1401 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
1402 false, | |
1403 blink::WebCryptoKeyUsageVerify, | |
1404 &key)); | |
1405 | |
1406 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
1407 } | |
1408 | |
1409 TEST(WebCryptoAesKwTest, ImportKeyJwkKeyOpsWrapUnwrap) { | |
1410 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1411 base::DictionaryValue dict; | |
1412 dict.SetString("kty", "oct"); | |
1413 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1414 base::ListValue* key_ops = new base::ListValue; | |
1415 dict.Set("key_ops", key_ops); // Takes ownership. | |
1416 | |
1417 key_ops->AppendString("wrapKey"); | |
1418 | |
1419 EXPECT_EQ( | |
1420 Status::Success(), | |
1421 ImportKeyJwkFromDict(dict, | |
1422 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), | |
1423 false, | |
1424 blink::WebCryptoKeyUsageWrapKey, | |
1425 &key)); | |
1426 | |
1427 EXPECT_EQ(blink::WebCryptoKeyUsageWrapKey, key.usages()); | |
1428 | |
1429 key_ops->AppendString("unwrapKey"); | |
1430 | |
1431 EXPECT_EQ( | |
1432 Status::Success(), | |
1433 ImportKeyJwkFromDict(dict, | |
1434 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), | |
1435 false, | |
1436 blink::WebCryptoKeyUsageUnwrapKey, | |
1437 &key)); | |
1438 | |
1439 EXPECT_EQ(blink::WebCryptoKeyUsageUnwrapKey, key.usages()); | |
1440 } | |
1441 | |
1442 // Test 'use' inconsistent with 'key_ops'. | |
1443 TEST(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) { | |
1444 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1445 base::DictionaryValue dict; | |
1446 dict.SetString("kty", "oct"); | |
1447 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1448 base::ListValue* key_ops = new base::ListValue; | |
1449 dict.Set("key_ops", key_ops); // Takes ownership. | |
1450 | |
1451 dict.SetString("alg", "HS256"); | |
1452 dict.SetString("use", "sig"); | |
1453 key_ops->AppendString("sign"); | |
1454 key_ops->AppendString("verify"); | |
1455 key_ops->AppendString("encrypt"); | |
1456 EXPECT_EQ(Status::ErrorJwkUseAndKeyopsInconsistent(), | |
1457 ImportKeyJwkFromDict( | |
1458 dict, | |
1459 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
1460 false, | |
1461 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
1462 &key)); | |
1463 } | |
1464 | |
1465 // Test JWK composite 'sig' use | |
1466 TEST(WebCryptoHmacTest, ImportKeyJwkUseSig) { | |
1467 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1468 base::DictionaryValue dict; | |
1469 dict.SetString("kty", "oct"); | |
1470 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1471 | |
1472 dict.SetString("use", "sig"); | |
1473 EXPECT_EQ(Status::Success(), | |
1474 ImportKeyJwkFromDict( | |
1475 dict, | |
1476 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
1477 false, | |
1478 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
1479 &key)); | |
1480 | |
1481 EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
1482 key.usages()); | |
1483 } | |
1484 | |
1485 TEST(WebCryptoAesCbcTest, ImportKeyJwkUseEnc) { | |
1486 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1487 base::DictionaryValue dict; | |
1488 dict.SetString("kty", "oct"); | |
1489 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1490 | |
1491 // Test JWK composite use 'enc' usage | |
1492 dict.SetString("alg", "A128CBC"); | |
1493 dict.SetString("use", "enc"); | |
1494 EXPECT_EQ( | |
1495 Status::Success(), | |
1496 ImportKeyJwkFromDict(dict, | |
1497 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1498 false, | |
1499 blink::WebCryptoKeyUsageDecrypt | | |
1500 blink::WebCryptoKeyUsageEncrypt | | |
1501 blink::WebCryptoKeyUsageWrapKey | | |
1502 blink::WebCryptoKeyUsageUnwrapKey, | |
1503 &key)); | |
1504 EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | | |
1505 blink::WebCryptoKeyUsageWrapKey | | |
1506 blink::WebCryptoKeyUsageUnwrapKey, | |
1507 key.usages()); | |
1508 } | |
1509 | |
1510 TEST(WebCryptoAesCbcTest, ImportJwkInvalidJson) { | |
1511 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1512 // Fail on empty JSON. | |
1513 EXPECT_EQ(Status::ErrorImportEmptyKeyData(), | |
1514 ImportKeyJwk(CryptoData(MakeJsonVector("")), | |
1515 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1516 false, | |
1517 blink::WebCryptoKeyUsageEncrypt, | |
1518 &key)); | |
1519 | |
1520 // Fail on invalid JSON. | |
1521 const std::vector<uint8_t> bad_json_vec = MakeJsonVector( | |
1522 "{" | |
1523 "\"kty\" : \"oct\"," | |
1524 "\"alg\" : \"HS256\"," | |
1525 "\"use\" : "); | |
1526 EXPECT_EQ(Status::ErrorJwkNotDictionary(), | |
1527 ImportKeyJwk(CryptoData(bad_json_vec), | |
1528 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1529 false, | |
1530 blink::WebCryptoKeyUsageEncrypt, | |
1531 &key)); | |
1532 } | |
1533 | |
1534 // Fail on JWK alg present but incorrect (expecting A128CBC). | |
1535 TEST(WebCryptoAesCbcTest, ImportJwkIncorrectAlg) { | |
1536 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1537 | |
1538 base::DictionaryValue dict; | |
1539 dict.SetString("kty", "oct"); | |
1540 dict.SetString("alg", "A127CBC"); // Not valid. | |
1541 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1542 | |
1543 EXPECT_EQ( | |
1544 Status::ErrorJwkAlgorithmInconsistent(), | |
1545 ImportKeyJwkFromDict(dict, | |
1546 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1547 false, | |
1548 blink::WebCryptoKeyUsageEncrypt, | |
1549 &key)); | |
1550 } | |
1551 | |
1552 // Fail on invalid kty. | |
1553 TEST(WebCryptoAesCbcTest, ImportJwkInvalidKty) { | |
1554 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1555 | |
1556 base::DictionaryValue dict; | |
1557 dict.SetString("kty", "foo"); | |
1558 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1559 EXPECT_EQ( | |
1560 Status::ErrorJwkUnexpectedKty("oct"), | |
1561 ImportKeyJwkFromDict(dict, | |
1562 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1563 false, | |
1564 blink::WebCryptoKeyUsageEncrypt, | |
1565 &key)); | |
1566 } | |
1567 | |
1568 // Fail on missing kty. | |
1569 TEST(WebCryptoAesCbcTest, ImportJwkMissingKty) { | |
1570 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1571 | |
1572 base::DictionaryValue dict; | |
1573 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1574 EXPECT_EQ( | |
1575 Status::ErrorJwkPropertyMissing("kty"), | |
1576 ImportKeyJwkFromDict(dict, | |
1577 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1578 false, | |
1579 blink::WebCryptoKeyUsageEncrypt, | |
1580 &key)); | |
1581 } | |
1582 | |
1583 // Fail on kty wrong type. | |
1584 TEST(WebCryptoAesCbcTest, ImportJwkKtyWrongType) { | |
1585 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1586 | |
1587 base::DictionaryValue dict; | |
1588 dict.SetDouble("kty", 0.1); | |
1589 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1590 | |
1591 EXPECT_EQ( | |
1592 Status::ErrorJwkPropertyWrongType("kty", "string"), | |
1593 ImportKeyJwkFromDict(dict, | |
1594 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1595 false, | |
1596 blink::WebCryptoKeyUsageEncrypt, | |
1597 &key)); | |
1598 } | |
1599 | |
1600 // Fail on invalid use. | |
1601 TEST(WebCryptoAesCbcTest, ImportJwkUnrecognizedUse) { | |
1602 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1603 | |
1604 base::DictionaryValue dict; | |
1605 dict.SetString("kty", "oct"); | |
1606 dict.SetString("use", "foo"); | |
1607 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1608 | |
1609 EXPECT_EQ( | |
1610 Status::ErrorJwkUnrecognizedUse(), | |
1611 ImportKeyJwkFromDict(dict, | |
1612 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1613 false, | |
1614 blink::WebCryptoKeyUsageEncrypt, | |
1615 &key)); | |
1616 } | |
1617 | |
1618 // Fail on invalid use (wrong type). | |
1619 TEST(WebCryptoAesCbcTest, ImportJwkUseWrongType) { | |
1620 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1621 | |
1622 base::DictionaryValue dict; | |
1623 dict.SetString("kty", "oct"); | |
1624 dict.SetBoolean("use", true); | |
1625 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1626 | |
1627 EXPECT_EQ( | |
1628 Status::ErrorJwkPropertyWrongType("use", "string"), | |
1629 ImportKeyJwkFromDict(dict, | |
1630 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1631 false, | |
1632 blink::WebCryptoKeyUsageEncrypt, | |
1633 &key)); | |
1634 } | |
1635 | |
1636 // Fail on invalid extractable (wrong type). | |
1637 TEST(WebCryptoAesCbcTest, ImportJwkExtWrongType) { | |
1638 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1639 | |
1640 base::DictionaryValue dict; | |
1641 dict.SetString("kty", "oct"); | |
1642 dict.SetInteger("ext", 0); | |
1643 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1644 | |
1645 EXPECT_EQ( | |
1646 Status::ErrorJwkPropertyWrongType("ext", "boolean"), | |
1647 ImportKeyJwkFromDict(dict, | |
1648 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1649 false, | |
1650 blink::WebCryptoKeyUsageEncrypt, | |
1651 &key)); | |
1652 } | |
1653 | |
1654 // Fail on invalid key_ops (wrong type). | |
1655 TEST(WebCryptoAesCbcTest, ImportJwkKeyOpsWrongType) { | |
1656 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1657 | |
1658 base::DictionaryValue dict; | |
1659 dict.SetString("kty", "oct"); | |
1660 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1661 dict.SetBoolean("key_ops", true); | |
1662 | |
1663 EXPECT_EQ( | |
1664 Status::ErrorJwkPropertyWrongType("key_ops", "list"), | |
1665 ImportKeyJwkFromDict(dict, | |
1666 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1667 false, | |
1668 blink::WebCryptoKeyUsageEncrypt, | |
1669 &key)); | |
1670 } | |
1671 | |
1672 // Fail on inconsistent key_ops - asking for "encrypt" however JWK contains | |
1673 // only "foo". | |
1674 TEST(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) { | |
1675 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1676 | |
1677 base::DictionaryValue dict; | |
1678 dict.SetString("kty", "oct"); | |
1679 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1680 | |
1681 base::ListValue* key_ops = new base::ListValue; | |
1682 // Note: the following call makes dict assume ownership of key_ops. | |
1683 dict.Set("key_ops", key_ops); | |
1684 key_ops->AppendString("foo"); | |
1685 EXPECT_EQ( | |
1686 Status::ErrorJwkKeyopsInconsistent(), | |
1687 ImportKeyJwkFromDict(dict, | |
1688 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1689 false, | |
1690 blink::WebCryptoKeyUsageEncrypt, | |
1691 &key)); | |
1692 } | |
1693 | |
1694 // Import a JWK with unrecognized values for "key_ops". | |
1695 TEST(WebCryptoAesCbcTest, ImportJwkUnrecognizedKeyOps) { | |
1696 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1697 blink::WebCryptoAlgorithm algorithm = | |
1698 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
1699 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; | |
1700 | |
1701 base::DictionaryValue dict; | |
1702 dict.SetString("kty", "oct"); | |
1703 dict.SetString("alg", "A128CBC"); | |
1704 dict.SetString("use", "enc"); | |
1705 dict.SetBoolean("ext", false); | |
1706 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1707 | |
1708 base::ListValue* key_ops = new base::ListValue; | |
1709 dict.Set("key_ops", key_ops); | |
1710 key_ops->AppendString("foo"); | |
1711 key_ops->AppendString("bar"); | |
1712 key_ops->AppendString("baz"); | |
1713 key_ops->AppendString("encrypt"); | |
1714 EXPECT_EQ(Status::Success(), | |
1715 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
1716 } | |
1717 | |
1718 // Import a JWK with a value in key_ops array that is not a string. | |
1719 TEST(WebCryptoAesCbcTest, ImportJwkNonStringKeyOp) { | |
1720 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1721 blink::WebCryptoAlgorithm algorithm = | |
1722 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
1723 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; | |
1724 | |
1725 base::DictionaryValue dict; | |
1726 dict.SetString("kty", "oct"); | |
1727 dict.SetString("alg", "A128CBC"); | |
1728 dict.SetString("use", "enc"); | |
1729 dict.SetBoolean("ext", false); | |
1730 dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg=="); | |
1731 | |
1732 base::ListValue* key_ops = new base::ListValue; | |
1733 dict.Set("key_ops", key_ops); | |
1734 key_ops->AppendString("encrypt"); | |
1735 key_ops->AppendInteger(3); | |
1736 EXPECT_EQ(Status::ErrorJwkPropertyWrongType("key_ops[1]", "string"), | |
1737 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
1738 } | |
1739 | |
1740 // Fail on missing k. | |
1741 TEST(WebCryptoAesCbcTest, ImportJwkMissingK) { | |
1742 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1743 | |
1744 base::DictionaryValue dict; | |
1745 dict.SetString("kty", "oct"); | |
1746 | |
1747 EXPECT_EQ( | |
1748 Status::ErrorJwkPropertyMissing("k"), | |
1749 ImportKeyJwkFromDict(dict, | |
1750 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1751 false, | |
1752 blink::WebCryptoKeyUsageEncrypt, | |
1753 &key)); | |
1754 } | |
1755 | |
1756 // Fail on bad b64 encoding for k. | |
1757 TEST(WebCryptoAesCbcTest, ImportJwkBadB64ForK) { | |
1758 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1759 | |
1760 base::DictionaryValue dict; | |
1761 dict.SetString("kty", "oct"); | |
1762 dict.SetString("k", "Qk3f0DsytU8lfza2au #$% Htaw2xpop9GYyTuH0p5GghxTI="); | |
1763 EXPECT_EQ( | |
1764 Status::ErrorJwkBase64Decode("k"), | |
1765 ImportKeyJwkFromDict(dict, | |
1766 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1767 false, | |
1768 blink::WebCryptoKeyUsageEncrypt, | |
1769 &key)); | |
1770 } | |
1771 | |
1772 // Fail on empty k. | |
1773 TEST(WebCryptoAesCbcTest, ImportJwkEmptyK) { | |
1774 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1775 | |
1776 base::DictionaryValue dict; | |
1777 dict.SetString("kty", "oct"); | |
1778 dict.SetString("k", ""); | |
1779 | |
1780 EXPECT_EQ( | |
1781 Status::ErrorImportAesKeyLength(), | |
1782 ImportKeyJwkFromDict(dict, | |
1783 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1784 false, | |
1785 blink::WebCryptoKeyUsageEncrypt, | |
1786 &key)); | |
1787 } | |
1788 | |
1789 // Fail on empty k (with alg specified). | |
1790 TEST(WebCryptoAesCbcTest, ImportJwkEmptyK2) { | |
1791 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1792 | |
1793 base::DictionaryValue dict; | |
1794 dict.SetString("kty", "oct"); | |
1795 dict.SetString("alg", "A128CBC"); | |
1796 dict.SetString("k", ""); | |
1797 | |
1798 EXPECT_EQ( | |
1799 Status::ErrorJwkIncorrectKeyLength(), | |
1800 ImportKeyJwkFromDict(dict, | |
1801 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1802 false, | |
1803 blink::WebCryptoKeyUsageEncrypt, | |
1804 &key)); | |
1805 } | |
1806 | |
1807 // Fail on k actual length (120 bits) inconsistent with the embedded JWK alg | |
1808 // value (128) for an AES key. | |
1809 TEST(WebCryptoAesCbcTest, ImportJwkInconsistentKLength) { | |
1810 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1811 | |
1812 base::DictionaryValue dict; | |
1813 dict.SetString("kty", "oct"); | |
1814 dict.SetString("alg", "A128CBC"); | |
1815 dict.SetString("k", "AVj42h0Y5aqGtE3yluKL"); | |
1816 EXPECT_EQ( | |
1817 Status::ErrorJwkIncorrectKeyLength(), | |
1818 ImportKeyJwkFromDict(dict, | |
1819 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1820 false, | |
1821 blink::WebCryptoKeyUsageEncrypt, | |
1822 &key)); | |
1823 } | |
1824 | |
1825 // Fail on k actual length (192 bits) inconsistent with the embedded JWK alg | |
1826 // value (128) for an AES key. | |
1827 TEST(WebCryptoAesCbcTest, ImportJwkInconsistentKLength2) { | |
1828 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1829 | |
1830 base::DictionaryValue dict; | |
1831 dict.SetString("kty", "oct"); | |
1832 dict.SetString("alg", "A128CBC"); | |
1833 dict.SetString("k", "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n"); | |
1834 EXPECT_EQ( | |
1835 Status::ErrorJwkIncorrectKeyLength(), | |
1836 ImportKeyJwkFromDict(dict, | |
1837 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
1838 false, | |
1839 blink::WebCryptoKeyUsageEncrypt, | |
1840 &key)); | |
1841 } | |
1842 | |
1843 TEST(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) { | |
1844 struct TestCase { | |
1845 const blink::WebCryptoAlgorithmId hash; | |
1846 const blink::WebCryptoKeyUsageMask usage; | |
1847 const char* const jwk_alg; | |
1848 }; | |
1849 const TestCase kTests[] = { | |
1850 {blink::WebCryptoAlgorithmIdSha1, blink::WebCryptoKeyUsageVerify, "RS1"}, | |
1851 {blink::WebCryptoAlgorithmIdSha256, blink::WebCryptoKeyUsageVerify, | |
1852 "RS256"}, | |
1853 {blink::WebCryptoAlgorithmIdSha384, blink::WebCryptoKeyUsageVerify, | |
1854 "RS384"}, | |
1855 {blink::WebCryptoAlgorithmIdSha512, blink::WebCryptoKeyUsageVerify, | |
1856 "RS512"}}; | |
1857 | |
1858 for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); | |
1859 ++test_index) { | |
1860 SCOPED_TRACE(test_index); | |
1861 const TestCase& test = kTests[test_index]; | |
1862 | |
1863 const blink::WebCryptoAlgorithm import_algorithm = | |
1864 CreateRsaHashedImportAlgorithm( | |
1865 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, test.hash); | |
1866 | |
1867 // Import the spki to create a public key | |
1868 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
1869 ASSERT_EQ(Status::Success(), | |
1870 ImportKey(blink::WebCryptoKeyFormatSpki, | |
1871 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
1872 import_algorithm, | |
1873 true, | |
1874 test.usage, | |
1875 &public_key)); | |
1876 | |
1877 // Export the public key as JWK and verify its contents | |
1878 std::vector<uint8_t> jwk; | |
1879 ASSERT_EQ(Status::Success(), | |
1880 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); | |
1881 EXPECT_TRUE(VerifyPublicJwk(jwk, | |
1882 test.jwk_alg, | |
1883 kPublicKeyModulusHex, | |
1884 kPublicKeyExponentHex, | |
1885 test.usage)); | |
1886 | |
1887 // Import the JWK back in to create a new key | |
1888 blink::WebCryptoKey public_key2 = blink::WebCryptoKey::createNull(); | |
1889 ASSERT_EQ( | |
1890 Status::Success(), | |
1891 ImportKeyJwk( | |
1892 CryptoData(jwk), import_algorithm, true, test.usage, &public_key2)); | |
1893 ASSERT_TRUE(public_key2.handle()); | |
1894 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); | |
1895 EXPECT_TRUE(public_key2.extractable()); | |
1896 EXPECT_EQ(import_algorithm.id(), public_key2.algorithm().id()); | |
1897 | |
1898 // Export the new key as spki and compare to the original. | |
1899 std::vector<uint8_t> spki; | |
1900 ASSERT_EQ(Status::Success(), | |
1901 ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); | |
1902 EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); | |
1903 } | |
1904 } | |
1905 | |
1906 TEST(WebCryptoRsaOaepTest, ImportExportJwkRsaPublicKey) { | |
1907 if (!SupportsRsaOaep()) { | |
1908 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
1909 return; | |
1910 } | |
1911 | |
1912 struct TestCase { | |
1913 const blink::WebCryptoAlgorithmId hash; | |
1914 const blink::WebCryptoKeyUsageMask usage; | |
1915 const char* const jwk_alg; | |
1916 }; | |
1917 const TestCase kTests[] = {{blink::WebCryptoAlgorithmIdSha1, | |
1918 blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP"}, | |
1919 {blink::WebCryptoAlgorithmIdSha256, | |
1920 blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-256"}, | |
1921 {blink::WebCryptoAlgorithmIdSha384, | |
1922 blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-384"}, | |
1923 {blink::WebCryptoAlgorithmIdSha512, | |
1924 blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-512"}}; | |
1925 | |
1926 for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); | |
1927 ++test_index) { | |
1928 SCOPED_TRACE(test_index); | |
1929 const TestCase& test = kTests[test_index]; | |
1930 | |
1931 const blink::WebCryptoAlgorithm import_algorithm = | |
1932 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, | |
1933 test.hash); | |
1934 | |
1935 // Import the spki to create a public key | |
1936 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
1937 ASSERT_EQ(Status::Success(), | |
1938 ImportKey(blink::WebCryptoKeyFormatSpki, | |
1939 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
1940 import_algorithm, | |
1941 true, | |
1942 test.usage, | |
1943 &public_key)); | |
1944 | |
1945 // Export the public key as JWK and verify its contents | |
1946 std::vector<uint8_t> jwk; | |
1947 ASSERT_EQ(Status::Success(), | |
1948 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); | |
1949 EXPECT_TRUE(VerifyPublicJwk(jwk, | |
1950 test.jwk_alg, | |
1951 kPublicKeyModulusHex, | |
1952 kPublicKeyExponentHex, | |
1953 test.usage)); | |
1954 | |
1955 // Import the JWK back in to create a new key | |
1956 blink::WebCryptoKey public_key2 = blink::WebCryptoKey::createNull(); | |
1957 ASSERT_EQ( | |
1958 Status::Success(), | |
1959 ImportKeyJwk( | |
1960 CryptoData(jwk), import_algorithm, true, test.usage, &public_key2)); | |
1961 ASSERT_TRUE(public_key2.handle()); | |
1962 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); | |
1963 EXPECT_TRUE(public_key2.extractable()); | |
1964 EXPECT_EQ(import_algorithm.id(), public_key2.algorithm().id()); | |
1965 | |
1966 // TODO(eroman): Export the SPKI and verify matches. | |
1967 } | |
1968 } | |
1969 | |
1970 TEST(WebCryptoRsaSsaTest, ImportJwkRsaFailures) { | |
1971 base::DictionaryValue dict; | |
1972 RestoreJwkRsaDictionary(&dict); | |
1973 blink::WebCryptoAlgorithm algorithm = | |
1974 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
1975 blink::WebCryptoAlgorithmIdSha256); | |
1976 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageVerify; | |
1977 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
1978 | |
1979 // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) | |
1980 // entry, while an RSA private key must have those plus at least a "d" | |
1981 // (private exponent) entry. | |
1982 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
1983 // section 6.3. | |
1984 | |
1985 // Baseline pass. | |
1986 EXPECT_EQ(Status::Success(), | |
1987 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
1988 EXPECT_EQ(algorithm.id(), key.algorithm().id()); | |
1989 EXPECT_FALSE(key.extractable()); | |
1990 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
1991 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); | |
1992 | |
1993 // The following are specific failure cases for when kty = "RSA". | |
1994 | |
1995 // Fail if either "n" or "e" is not present or malformed. | |
1996 const std::string kKtyParmName[] = {"n", "e"}; | |
1997 for (size_t idx = 0; idx < ARRAYSIZE_UNSAFE(kKtyParmName); ++idx) { | |
1998 // Fail on missing parameter. | |
1999 dict.Remove(kKtyParmName[idx], NULL); | |
2000 EXPECT_NE(Status::Success(), | |
2001 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
2002 RestoreJwkRsaDictionary(&dict); | |
2003 | |
2004 // Fail on bad b64 parameter encoding. | |
2005 dict.SetString(kKtyParmName[idx], "Qk3f0DsytU8lfza2au #$% Htaw2xpop9yTuH0"); | |
2006 EXPECT_NE(Status::Success(), | |
2007 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
2008 RestoreJwkRsaDictionary(&dict); | |
2009 | |
2010 // Fail on empty parameter. | |
2011 dict.SetString(kKtyParmName[idx], ""); | |
2012 EXPECT_EQ(Status::ErrorJwkEmptyBigInteger(kKtyParmName[idx]), | |
2013 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
2014 RestoreJwkRsaDictionary(&dict); | |
2015 } | |
2016 } | |
2017 | |
2018 TEST(WebCryptoHmacTest, ImportJwkInputConsistency) { | |
2019 // The Web Crypto spec says that if a JWK value is present, but is | |
2020 // inconsistent with the input value, the operation must fail. | |
2021 | |
2022 // Consistency rules when JWK value is not present: Inputs should be used. | |
2023 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2024 bool extractable = false; | |
2025 blink::WebCryptoAlgorithm algorithm = | |
2026 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); | |
2027 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageVerify; | |
2028 base::DictionaryValue dict; | |
2029 dict.SetString("kty", "oct"); | |
2030 dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); | |
2031 std::vector<uint8_t> json_vec = MakeJsonVector(dict); | |
2032 EXPECT_EQ( | |
2033 Status::Success(), | |
2034 ImportKeyJwk( | |
2035 CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); | |
2036 EXPECT_TRUE(key.handle()); | |
2037 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
2038 EXPECT_EQ(extractable, key.extractable()); | |
2039 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
2040 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
2041 key.algorithm().hmacParams()->hash().id()); | |
2042 EXPECT_EQ(320u, key.algorithm().hmacParams()->lengthBits()); | |
2043 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
2044 key = blink::WebCryptoKey::createNull(); | |
2045 | |
2046 // Consistency rules when JWK value exists: Fail if inconsistency is found. | |
2047 | |
2048 // Pass: All input values are consistent with the JWK values. | |
2049 dict.Clear(); | |
2050 dict.SetString("kty", "oct"); | |
2051 dict.SetString("alg", "HS256"); | |
2052 dict.SetString("use", "sig"); | |
2053 dict.SetBoolean("ext", false); | |
2054 dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); | |
2055 json_vec = MakeJsonVector(dict); | |
2056 EXPECT_EQ( | |
2057 Status::Success(), | |
2058 ImportKeyJwk( | |
2059 CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); | |
2060 | |
2061 // Extractable cases: | |
2062 // 1. input=T, JWK=F ==> fail (inconsistent) | |
2063 // 4. input=F, JWK=F ==> pass, result extractable is F | |
2064 // 2. input=T, JWK=T ==> pass, result extractable is T | |
2065 // 3. input=F, JWK=T ==> pass, result extractable is F | |
2066 EXPECT_EQ( | |
2067 Status::ErrorJwkExtInconsistent(), | |
2068 ImportKeyJwk(CryptoData(json_vec), algorithm, true, usage_mask, &key)); | |
2069 EXPECT_EQ( | |
2070 Status::Success(), | |
2071 ImportKeyJwk(CryptoData(json_vec), algorithm, false, usage_mask, &key)); | |
2072 EXPECT_FALSE(key.extractable()); | |
2073 dict.SetBoolean("ext", true); | |
2074 EXPECT_EQ(Status::Success(), | |
2075 ImportKeyJwkFromDict(dict, algorithm, true, usage_mask, &key)); | |
2076 EXPECT_TRUE(key.extractable()); | |
2077 EXPECT_EQ(Status::Success(), | |
2078 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | |
2079 EXPECT_FALSE(key.extractable()); | |
2080 dict.SetBoolean("ext", true); // restore previous value | |
2081 | |
2082 // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value | |
2083 // (HMAC SHA256). | |
2084 dict.Clear(); | |
2085 dict.SetString("kty", "oct"); | |
2086 dict.SetString("alg", "HS256"); | |
2087 dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); | |
2088 EXPECT_EQ( | |
2089 Status::ErrorJwkAlgorithmInconsistent(), | |
2090 ImportKeyJwkFromDict(dict, | |
2091 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
2092 extractable, | |
2093 blink::WebCryptoKeyUsageEncrypt, | |
2094 &key)); | |
2095 // Fail: Input usage (encrypt) is inconsistent with JWK value (use=sig). | |
2096 EXPECT_EQ(Status::ErrorJwkUseInconsistent(), | |
2097 ImportKeyJwk(CryptoData(json_vec), | |
2098 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
2099 extractable, | |
2100 blink::WebCryptoKeyUsageEncrypt, | |
2101 &key)); | |
2102 | |
2103 // Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value | |
2104 // (HMAC SHA256). | |
2105 EXPECT_EQ( | |
2106 Status::ErrorJwkAlgorithmInconsistent(), | |
2107 ImportKeyJwk(CryptoData(json_vec), | |
2108 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), | |
2109 extractable, | |
2110 usage_mask, | |
2111 &key)); | |
2112 | |
2113 // Pass: JWK alg missing but input algorithm specified: use input value | |
2114 dict.Remove("alg", NULL); | |
2115 EXPECT_EQ(Status::Success(), | |
2116 ImportKeyJwkFromDict( | |
2117 dict, | |
2118 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
2119 extractable, | |
2120 usage_mask, | |
2121 &key)); | |
2122 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, algorithm.id()); | |
2123 dict.SetString("alg", "HS256"); | |
2124 | |
2125 // Fail: Input usage_mask (encrypt) is not a subset of the JWK value | |
2126 // (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. | |
2127 EXPECT_EQ(Status::ErrorCreateKeyBadUsages(), | |
2128 ImportKeyJwk(CryptoData(json_vec), | |
2129 algorithm, | |
2130 extractable, | |
2131 blink::WebCryptoKeyUsageEncrypt, | |
2132 &key)); | |
2133 | |
2134 // Fail: Input usage_mask (encrypt|sign|verify) is not a subset of the JWK | |
2135 // value (sign|verify). Moreover "encrypt" is not a valid usage for HMAC. | |
2136 usage_mask = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign | | |
2137 blink::WebCryptoKeyUsageVerify; | |
2138 EXPECT_EQ( | |
2139 Status::ErrorCreateKeyBadUsages(), | |
2140 ImportKeyJwk( | |
2141 CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); | |
2142 | |
2143 // TODO(padolph): kty vs alg consistency tests: Depending on the kty value, | |
2144 // only certain alg values are permitted. For example, when kty = "RSA" alg | |
2145 // must be of the RSA family, or when kty = "oct" alg must be symmetric | |
2146 // algorithm. | |
2147 | |
2148 // TODO(padolph): key_ops consistency tests | |
2149 } | |
2150 | |
2151 TEST(WebCryptoHmacTest, ImportJwkHappy) { | |
2152 // This test verifies the happy path of JWK import, including the application | |
2153 // of the imported key material. | |
2154 | |
2155 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2156 bool extractable = false; | |
2157 blink::WebCryptoAlgorithm algorithm = | |
2158 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256); | |
2159 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageSign; | |
2160 | |
2161 // Import a symmetric key JWK and HMAC-SHA256 sign() | |
2162 // Uses the first SHA256 test vector from the HMAC sample set above. | |
2163 | |
2164 base::DictionaryValue dict; | |
2165 dict.SetString("kty", "oct"); | |
2166 dict.SetString("alg", "HS256"); | |
2167 dict.SetString("use", "sig"); | |
2168 dict.SetBoolean("ext", false); | |
2169 dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg"); | |
2170 | |
2171 ASSERT_EQ( | |
2172 Status::Success(), | |
2173 ImportKeyJwkFromDict(dict, algorithm, extractable, usage_mask, &key)); | |
2174 | |
2175 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
2176 key.algorithm().hmacParams()->hash().id()); | |
2177 | |
2178 const std::vector<uint8_t> message_raw = HexStringToBytes( | |
2179 "b1689c2591eaf3c9e66070f8a77954ffb81749f1b00346f9dfe0b2ee905dcc288baf4a" | |
2180 "92de3f4001dd9f44c468c3d07d6c6ee82faceafc97c2fc0fc0601719d2dcd0aa2aec92" | |
2181 "d1b0ae933c65eb06a03c9c935c2bad0459810241347ab87e9f11adb30415424c6c7f5f" | |
2182 "22a003b8ab8de54f6ded0e3ab9245fa79568451dfa258e"); | |
2183 | |
2184 std::vector<uint8_t> output; | |
2185 | |
2186 ASSERT_EQ(Status::Success(), | |
2187 Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), | |
2188 key, | |
2189 CryptoData(message_raw), | |
2190 &output)); | |
2191 | |
2192 const std::string mac_raw = | |
2193 "769f00d3e6a6cc1fb426a14a4f76c6462e6149726e0dee0ec0cf97a16605ac8b"; | |
2194 | |
2195 EXPECT_BYTES_EQ_HEX(mac_raw, output); | |
2196 | |
2197 // TODO(padolph): Import an RSA public key JWK and use it | |
2198 } | |
2199 | |
2200 void ImportExportJwkSymmetricKey( | 503 void ImportExportJwkSymmetricKey( |
2201 int key_len_bits, | 504 int key_len_bits, |
2202 const blink::WebCryptoAlgorithm& import_algorithm, | 505 const blink::WebCryptoAlgorithm& import_algorithm, |
2203 blink::WebCryptoKeyUsageMask usages, | 506 blink::WebCryptoKeyUsageMask usages, |
2204 const std::string& jwk_alg) { | 507 const std::string& jwk_alg) { |
2205 std::vector<uint8_t> json; | 508 std::vector<uint8_t> json; |
2206 std::string key_hex; | 509 std::string key_hex; |
2207 | 510 |
2208 // Hardcoded pseudo-random bytes to use for keys of different lengths. | 511 // Hardcoded pseudo-random bytes to use for keys of different lengths. |
2209 switch (key_len_bits) { | 512 switch (key_len_bits) { |
(...skipping 21 matching lines...) Expand all Loading... |
2231 // Import a raw key. | 534 // Import a raw key. |
2232 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | 535 blink::WebCryptoKey key = ImportSecretKeyFromRaw( |
2233 HexStringToBytes(key_hex), import_algorithm, usages); | 536 HexStringToBytes(key_hex), import_algorithm, usages); |
2234 | 537 |
2235 // Export the key in JWK format and validate. | 538 // Export the key in JWK format and validate. |
2236 ASSERT_EQ(Status::Success(), | 539 ASSERT_EQ(Status::Success(), |
2237 ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); | 540 ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); |
2238 EXPECT_TRUE(VerifySecretJwk(json, jwk_alg, key_hex, usages)); | 541 EXPECT_TRUE(VerifySecretJwk(json, jwk_alg, key_hex, usages)); |
2239 | 542 |
2240 // Import the JWK-formatted key. | 543 // Import the JWK-formatted key. |
2241 ASSERT_EQ( | 544 ASSERT_EQ(Status::Success(), |
2242 Status::Success(), | 545 ImportKey(blink::WebCryptoKeyFormatJwk, |
2243 ImportKeyJwk(CryptoData(json), import_algorithm, true, usages, &key)); | 546 CryptoData(json), |
| 547 import_algorithm, |
| 548 true, |
| 549 usages, |
| 550 &key)); |
2244 EXPECT_TRUE(key.handle()); | 551 EXPECT_TRUE(key.handle()); |
2245 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | 552 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
2246 EXPECT_EQ(import_algorithm.id(), key.algorithm().id()); | 553 EXPECT_EQ(import_algorithm.id(), key.algorithm().id()); |
2247 EXPECT_EQ(true, key.extractable()); | 554 EXPECT_EQ(true, key.extractable()); |
2248 EXPECT_EQ(usages, key.usages()); | 555 EXPECT_EQ(usages, key.usages()); |
2249 | 556 |
2250 // Export the key in raw format and compare to the original. | 557 // Export the key in raw format and compare to the original. |
2251 std::vector<uint8_t> key_raw_out; | 558 std::vector<uint8_t> key_raw_out; |
2252 ASSERT_EQ(Status::Success(), | 559 ASSERT_EQ(Status::Success(), |
2253 ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); | 560 ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); |
2254 EXPECT_BYTES_EQ_HEX(key_hex, key_raw_out); | 561 EXPECT_BYTES_EQ_HEX(key_hex, key_raw_out); |
2255 } | 562 } |
2256 | 563 |
2257 TEST(WebCryptoAesCbcTest, ImportExportJwk) { | |
2258 const blink::WebCryptoAlgorithm algorithm = | |
2259 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
2260 | |
2261 // AES-CBC 128 | |
2262 ImportExportJwkSymmetricKey( | |
2263 128, | |
2264 algorithm, | |
2265 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
2266 "A128CBC"); | |
2267 | |
2268 // AES-CBC 256 | |
2269 ImportExportJwkSymmetricKey( | |
2270 256, algorithm, blink::WebCryptoKeyUsageDecrypt, "A256CBC"); | |
2271 | |
2272 // Large usage value | |
2273 ImportExportJwkSymmetricKey( | |
2274 256, | |
2275 algorithm, | |
2276 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | |
2277 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, | |
2278 "A256CBC"); | |
2279 } | |
2280 | |
2281 TEST(WebCryptoAesGcmTest, ImportExportJwk) { | |
2282 // Some Linux test runners may not have a new enough version of NSS. | |
2283 if (!SupportsAesGcm()) { | |
2284 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
2285 return; | |
2286 } | |
2287 | |
2288 const blink::WebCryptoAlgorithm algorithm = | |
2289 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm); | |
2290 | |
2291 // AES-GCM 128 | |
2292 ImportExportJwkSymmetricKey( | |
2293 128, | |
2294 algorithm, | |
2295 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
2296 "A128GCM"); | |
2297 | |
2298 // AES-GCM 256 | |
2299 ImportExportJwkSymmetricKey( | |
2300 256, algorithm, blink::WebCryptoKeyUsageDecrypt, "A256GCM"); | |
2301 } | |
2302 | |
2303 TEST(WebCryptoAesKwTest, ImportExportJwk) { | |
2304 const blink::WebCryptoAlgorithm algorithm = | |
2305 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
2306 | |
2307 // AES-KW 128 | |
2308 ImportExportJwkSymmetricKey( | |
2309 128, | |
2310 algorithm, | |
2311 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, | |
2312 "A128KW"); | |
2313 | |
2314 // AES-KW 256 | |
2315 ImportExportJwkSymmetricKey( | |
2316 256, | |
2317 algorithm, | |
2318 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, | |
2319 "A256KW"); | |
2320 } | |
2321 | |
2322 TEST(WebCryptoHmacTest, ImportExportJwk) { | |
2323 // HMAC SHA-1 | |
2324 ImportExportJwkSymmetricKey( | |
2325 256, | |
2326 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), | |
2327 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
2328 "HS1"); | |
2329 | |
2330 // HMAC SHA-384 | |
2331 ImportExportJwkSymmetricKey( | |
2332 384, | |
2333 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha384), | |
2334 blink::WebCryptoKeyUsageSign, | |
2335 "HS384"); | |
2336 | |
2337 // HMAC SHA-512 | |
2338 ImportExportJwkSymmetricKey( | |
2339 512, | |
2340 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha512), | |
2341 blink::WebCryptoKeyUsageVerify, | |
2342 "HS512"); | |
2343 | |
2344 // Zero usage value | |
2345 ImportExportJwkSymmetricKey( | |
2346 512, | |
2347 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha512), | |
2348 0, | |
2349 "HS512"); | |
2350 } | |
2351 | |
2352 TEST(WebCryptoHmacTest, ExportJwkEmptyKey) { | |
2353 const blink::WebCryptoAlgorithm import_algorithm = | |
2354 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1); | |
2355 | |
2356 blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; | |
2357 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2358 | |
2359 // Import a zero-byte HMAC key. | |
2360 const char key_data_hex[] = ""; | |
2361 key = ImportSecretKeyFromRaw( | |
2362 HexStringToBytes(key_data_hex), import_algorithm, usages); | |
2363 EXPECT_EQ(0u, key.algorithm().hmacParams()->lengthBits()); | |
2364 | |
2365 // Export the key in JWK format and validate. | |
2366 std::vector<uint8_t> json; | |
2367 ASSERT_EQ(Status::Success(), | |
2368 ExportKey(blink::WebCryptoKeyFormatJwk, key, &json)); | |
2369 EXPECT_TRUE(VerifySecretJwk(json, "HS1", key_data_hex, usages)); | |
2370 | |
2371 // Now try re-importing the JWK key. | |
2372 key = blink::WebCryptoKey::createNull(); | |
2373 EXPECT_EQ(Status::Success(), | |
2374 ImportKey(blink::WebCryptoKeyFormatJwk, | |
2375 CryptoData(json), | |
2376 import_algorithm, | |
2377 true, | |
2378 usages, | |
2379 &key)); | |
2380 | |
2381 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
2382 EXPECT_EQ(0u, key.algorithm().hmacParams()->lengthBits()); | |
2383 | |
2384 std::vector<uint8_t> exported_key_data; | |
2385 EXPECT_EQ(Status::Success(), | |
2386 ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key_data)); | |
2387 | |
2388 EXPECT_EQ(0u, exported_key_data.size()); | |
2389 } | |
2390 | |
2391 TEST(WebCryptoRsaSsaTest, ImportExportSpki) { | |
2392 // Passing case: Import a valid RSA key in SPKI format. | |
2393 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2394 ASSERT_EQ(Status::Success(), | |
2395 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2396 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
2397 CreateRsaHashedImportAlgorithm( | |
2398 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2399 blink::WebCryptoAlgorithmIdSha256), | |
2400 true, | |
2401 blink::WebCryptoKeyUsageVerify, | |
2402 &key)); | |
2403 EXPECT_TRUE(key.handle()); | |
2404 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); | |
2405 EXPECT_TRUE(key.extractable()); | |
2406 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
2407 EXPECT_EQ(kModulusLengthBits, | |
2408 key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2409 EXPECT_BYTES_EQ_HEX( | |
2410 "010001", | |
2411 CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); | |
2412 | |
2413 // Failing case: Empty SPKI data | |
2414 EXPECT_EQ( | |
2415 Status::ErrorImportEmptyKeyData(), | |
2416 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2417 CryptoData(std::vector<uint8_t>()), | |
2418 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
2419 true, | |
2420 blink::WebCryptoKeyUsageVerify, | |
2421 &key)); | |
2422 | |
2423 // Failing case: Bad DER encoding. | |
2424 EXPECT_EQ( | |
2425 Status::DataError(), | |
2426 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2427 CryptoData(HexStringToBytes("618333c4cb")), | |
2428 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
2429 true, | |
2430 blink::WebCryptoKeyUsageVerify, | |
2431 &key)); | |
2432 | |
2433 // Failing case: Import RSA key but provide an inconsistent input algorithm. | |
2434 EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
2435 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2436 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
2437 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
2438 true, | |
2439 blink::WebCryptoKeyUsageEncrypt, | |
2440 &key)); | |
2441 | |
2442 // Passing case: Export a previously imported RSA public key in SPKI format | |
2443 // and compare to original data. | |
2444 std::vector<uint8_t> output; | |
2445 ASSERT_EQ(Status::Success(), | |
2446 ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); | |
2447 EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, output); | |
2448 | |
2449 // Failing case: Try to export a previously imported RSA public key in raw | |
2450 // format (not allowed for a public key). | |
2451 EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), | |
2452 ExportKey(blink::WebCryptoKeyFormatRaw, key, &output)); | |
2453 | |
2454 // Failing case: Try to export a non-extractable key | |
2455 ASSERT_EQ(Status::Success(), | |
2456 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2457 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
2458 CreateRsaHashedImportAlgorithm( | |
2459 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2460 blink::WebCryptoAlgorithmIdSha256), | |
2461 false, | |
2462 blink::WebCryptoKeyUsageVerify, | |
2463 &key)); | |
2464 EXPECT_TRUE(key.handle()); | |
2465 EXPECT_FALSE(key.extractable()); | |
2466 EXPECT_EQ(Status::ErrorKeyNotExtractable(), | |
2467 ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); | |
2468 | |
2469 // TODO(eroman): Failing test: Import a SPKI with an unrecognized hash OID | |
2470 // TODO(eroman): Failing test: Import a SPKI with invalid algorithm params | |
2471 // TODO(eroman): Failing test: Import a SPKI with inconsistent parameters | |
2472 // (e.g. SHA-1 in OID, SHA-256 in params) | |
2473 // TODO(eroman): Failing test: Import a SPKI for RSA-SSA, but with params | |
2474 // as OAEP/PSS | |
2475 } | |
2476 | |
2477 TEST(WebCryptoRsaSsaTest, ImportExportPkcs8) { | |
2478 if (!SupportsRsaPrivateKeyImport()) | |
2479 return; | |
2480 | |
2481 // Passing case: Import a valid RSA key in PKCS#8 format. | |
2482 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2483 ASSERT_EQ(Status::Success(), | |
2484 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2485 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
2486 CreateRsaHashedImportAlgorithm( | |
2487 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2488 blink::WebCryptoAlgorithmIdSha1), | |
2489 true, | |
2490 blink::WebCryptoKeyUsageSign, | |
2491 &key)); | |
2492 EXPECT_TRUE(key.handle()); | |
2493 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, key.type()); | |
2494 EXPECT_TRUE(key.extractable()); | |
2495 EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); | |
2496 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
2497 key.algorithm().rsaHashedParams()->hash().id()); | |
2498 EXPECT_EQ(kModulusLengthBits, | |
2499 key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2500 EXPECT_BYTES_EQ_HEX( | |
2501 "010001", | |
2502 CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); | |
2503 | |
2504 std::vector<uint8_t> exported_key; | |
2505 ASSERT_EQ(Status::Success(), | |
2506 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key)); | |
2507 EXPECT_BYTES_EQ_HEX(kPrivateKeyPkcs8DerHex, exported_key); | |
2508 | |
2509 // Failing case: Empty PKCS#8 data | |
2510 EXPECT_EQ(Status::ErrorImportEmptyKeyData(), | |
2511 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2512 CryptoData(std::vector<uint8_t>()), | |
2513 CreateRsaHashedImportAlgorithm( | |
2514 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2515 blink::WebCryptoAlgorithmIdSha1), | |
2516 true, | |
2517 blink::WebCryptoKeyUsageSign, | |
2518 &key)); | |
2519 | |
2520 // Failing case: Bad DER encoding. | |
2521 EXPECT_EQ( | |
2522 Status::DataError(), | |
2523 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2524 CryptoData(HexStringToBytes("618333c4cb")), | |
2525 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
2526 true, | |
2527 blink::WebCryptoKeyUsageSign, | |
2528 &key)); | |
2529 | |
2530 // Failing case: Import RSA key but provide an inconsistent input algorithm | |
2531 // and usage. Several issues here: | |
2532 // * AES-CBC doesn't support PKCS8 key format | |
2533 // * AES-CBC doesn't support "sign" usage | |
2534 EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
2535 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2536 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
2537 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
2538 true, | |
2539 blink::WebCryptoKeyUsageSign, | |
2540 &key)); | |
2541 } | |
2542 | |
2543 // Tests importing of PKCS8 data that does not define a valid RSA key. | |
2544 TEST(WebCryptoRsaSsaTest, ImportInvalidPkcs8) { | |
2545 if (!SupportsRsaPrivateKeyImport()) | |
2546 return; | |
2547 | |
2548 // kPrivateKeyPkcs8DerHex defines an RSA private key in PKCS8 format, whose | |
2549 // parameters appear at the following offsets: | |
2550 // | |
2551 // n: (offset=36, len=129) | |
2552 // e: (offset=167, len=3) | |
2553 // d: (offset=173, len=128) | |
2554 // p: (offset=303, len=65) | |
2555 // q: (offset=370, len=65) | |
2556 // dp: (offset=437, len=64) | |
2557 // dq; (offset=503, len=64) | |
2558 // qi: (offset=569, len=64) | |
2559 | |
2560 // Do several tests, each of which invert a single byte within the input. | |
2561 const unsigned int kOffsetsToCorrupt[] = { | |
2562 50, // inside n | |
2563 168, // inside e | |
2564 175, // inside d | |
2565 333, // inside p | |
2566 373, // inside q | |
2567 450, // inside dp | |
2568 550, // inside dq | |
2569 600, // inside qi | |
2570 }; | |
2571 | |
2572 for (size_t test_index = 0; test_index < arraysize(kOffsetsToCorrupt); | |
2573 ++test_index) { | |
2574 SCOPED_TRACE(test_index); | |
2575 | |
2576 unsigned int i = kOffsetsToCorrupt[test_index]; | |
2577 std::vector<uint8_t> corrupted_data = | |
2578 HexStringToBytes(kPrivateKeyPkcs8DerHex); | |
2579 corrupted_data[i] = ~corrupted_data[i]; | |
2580 | |
2581 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2582 EXPECT_EQ(Status::DataError(), | |
2583 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2584 CryptoData(corrupted_data), | |
2585 CreateRsaHashedImportAlgorithm( | |
2586 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2587 blink::WebCryptoAlgorithmIdSha1), | |
2588 true, | |
2589 blink::WebCryptoKeyUsageSign, | |
2590 &key)); | |
2591 } | |
2592 } | |
2593 | |
2594 // Tests JWK import and export by doing a roundtrip key conversion and ensuring | |
2595 // it was lossless: | |
2596 // | |
2597 // PKCS8 --> JWK --> PKCS8 | |
2598 TEST(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) { | |
2599 if (!SupportsRsaPrivateKeyImport()) | |
2600 return; | |
2601 | |
2602 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2603 ASSERT_EQ(Status::Success(), | |
2604 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2605 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
2606 CreateRsaHashedImportAlgorithm( | |
2607 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2608 blink::WebCryptoAlgorithmIdSha1), | |
2609 true, | |
2610 blink::WebCryptoKeyUsageSign, | |
2611 &key)); | |
2612 | |
2613 std::vector<uint8_t> exported_key_jwk; | |
2614 ASSERT_EQ(Status::Success(), | |
2615 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_key_jwk)); | |
2616 | |
2617 // All of the optional parameters (p, q, dp, dq, qi) should be present in the | |
2618 // output. | |
2619 const char* expected_jwk = | |
2620 "{\"alg\":\"RS1\",\"d\":\"M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" | |
2621 "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" | |
2622 "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU\",\"dp\":" | |
2623 "\"KPoTk4ZVvh-" | |
2624 "KFZy6ylpy6hkMMAieGc0nSlVvNsT24Z9VSzTAd3kEJ7vdjdPt4kSDKPOF2Bsw6OQ7L_-" | |
2625 "gJ4YZeQ\",\"dq\":\"Gos485j6cSBJiY1_t57gp3ZoeRKZzfoJ78DlB6yyHtdDAe9b_Ui-" | |
2626 "RV6utuFnglWCdYCo5OjhQVHRUQqCo_LnKQ\",\"e\":\"AQAB\",\"ext\":true,\"key_" | |
2627 "ops\":[\"sign\"],\"kty\":\"RSA\",\"n\":" | |
2628 "\"pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" | |
2629 "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" | |
2630 "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc\",\"p\":\"5-" | |
2631 "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31WhU1vZs8w0Fg" | |
2632 "s7bc0-2o5kQw\",\"q\":\"tp3KHPfU1-yB51uQ_MqHSrzeEj_" | |
2633 "ScAGAqpBHm25I3o1n7ST58Z2FuidYdPVCzSDccj5pYzZKH5QlRSsmmmeZ_Q\",\"qi\":" | |
2634 "\"JxVqukEm0kqB86Uoy_sn9WiG-" | |
2635 "ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}"; | |
2636 | |
2637 ASSERT_EQ(CryptoData(std::string(expected_jwk)), | |
2638 CryptoData(exported_key_jwk)); | |
2639 | |
2640 ASSERT_EQ(Status::Success(), | |
2641 ImportKey(blink::WebCryptoKeyFormatJwk, | |
2642 CryptoData(exported_key_jwk), | |
2643 CreateRsaHashedImportAlgorithm( | |
2644 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2645 blink::WebCryptoAlgorithmIdSha1), | |
2646 true, | |
2647 blink::WebCryptoKeyUsageSign, | |
2648 &key)); | |
2649 | |
2650 std::vector<uint8_t> exported_key_pkcs8; | |
2651 ASSERT_EQ( | |
2652 Status::Success(), | |
2653 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key_pkcs8)); | |
2654 | |
2655 ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
2656 CryptoData(exported_key_pkcs8)); | |
2657 } | |
2658 | |
2659 // Tests importing multiple RSA private keys from JWK, and then exporting to | |
2660 // PKCS8. | |
2661 // | |
2662 // This is a regression test for http://crbug.com/378315, for which importing | |
2663 // a sequence of keys from JWK could yield the wrong key. The first key would | |
2664 // be imported correctly, however every key after that would actually import | |
2665 // the first key. | |
2666 TEST(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) { | |
2667 if (!SupportsRsaPrivateKeyImport()) | |
2668 return; | |
2669 | |
2670 scoped_ptr<base::ListValue> key_list; | |
2671 ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); | |
2672 | |
2673 // For this test to be meaningful the keys MUST be kept alive before importing | |
2674 // new keys. | |
2675 std::vector<blink::WebCryptoKey> live_keys; | |
2676 | |
2677 for (size_t key_index = 0; key_index < key_list->GetSize(); ++key_index) { | |
2678 SCOPED_TRACE(key_index); | |
2679 | |
2680 base::DictionaryValue* key_values; | |
2681 ASSERT_TRUE(key_list->GetDictionary(key_index, &key_values)); | |
2682 | |
2683 // Get the JWK representation of the key. | |
2684 base::DictionaryValue* key_jwk; | |
2685 ASSERT_TRUE(key_values->GetDictionary("jwk", &key_jwk)); | |
2686 | |
2687 // Get the PKCS8 representation of the key. | |
2688 std::string pkcs8_hex_string; | |
2689 ASSERT_TRUE(key_values->GetString("pkcs8", &pkcs8_hex_string)); | |
2690 std::vector<uint8_t> pkcs8_bytes = HexStringToBytes(pkcs8_hex_string); | |
2691 | |
2692 // Get the modulus length for the key. | |
2693 int modulus_length_bits = 0; | |
2694 ASSERT_TRUE(key_values->GetInteger("modulusLength", &modulus_length_bits)); | |
2695 | |
2696 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
2697 | |
2698 // Import the key from JWK. | |
2699 ASSERT_EQ( | |
2700 Status::Success(), | |
2701 ImportKeyJwkFromDict(*key_jwk, | |
2702 CreateRsaHashedImportAlgorithm( | |
2703 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2704 blink::WebCryptoAlgorithmIdSha256), | |
2705 true, | |
2706 blink::WebCryptoKeyUsageSign, | |
2707 &private_key)); | |
2708 | |
2709 live_keys.push_back(private_key); | |
2710 | |
2711 EXPECT_EQ( | |
2712 modulus_length_bits, | |
2713 static_cast<int>( | |
2714 private_key.algorithm().rsaHashedParams()->modulusLengthBits())); | |
2715 | |
2716 // Export to PKCS8 and verify that it matches expectation. | |
2717 std::vector<uint8_t> exported_key_pkcs8; | |
2718 ASSERT_EQ( | |
2719 Status::Success(), | |
2720 ExportKey( | |
2721 blink::WebCryptoKeyFormatPkcs8, private_key, &exported_key_pkcs8)); | |
2722 | |
2723 EXPECT_BYTES_EQ(pkcs8_bytes, exported_key_pkcs8); | |
2724 } | |
2725 } | |
2726 | |
2727 // Import an RSA private key using JWK. Next import a JWK containing the same | |
2728 // modulus, but mismatched parameters for the rest. It should NOT be possible | |
2729 // that the second import retrieves the first key. See http://crbug.com/378315 | |
2730 // for how that could happen. | |
2731 TEST(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) { | |
2732 #if defined(USE_NSS) | |
2733 if (!NSS_VersionCheck("3.16.2")) { | |
2734 LOG(WARNING) << "Skipping test because lacks NSS support"; | |
2735 return; | |
2736 } | |
2737 #endif | |
2738 | |
2739 scoped_ptr<base::ListValue> key_list; | |
2740 ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); | |
2741 | |
2742 // Import a 1024-bit private key. | |
2743 base::DictionaryValue* key1_props; | |
2744 ASSERT_TRUE(key_list->GetDictionary(1, &key1_props)); | |
2745 base::DictionaryValue* key1_jwk; | |
2746 ASSERT_TRUE(key1_props->GetDictionary("jwk", &key1_jwk)); | |
2747 | |
2748 blink::WebCryptoKey key1 = blink::WebCryptoKey::createNull(); | |
2749 ASSERT_EQ(Status::Success(), | |
2750 ImportKeyJwkFromDict(*key1_jwk, | |
2751 CreateRsaHashedImportAlgorithm( | |
2752 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2753 blink::WebCryptoAlgorithmIdSha256), | |
2754 true, | |
2755 blink::WebCryptoKeyUsageSign, | |
2756 &key1)); | |
2757 | |
2758 ASSERT_EQ(1024u, key1.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2759 | |
2760 // Construct a JWK using the modulus of key1, but all the other fields from | |
2761 // another key (also a 1024-bit private key). | |
2762 base::DictionaryValue* key2_props; | |
2763 ASSERT_TRUE(key_list->GetDictionary(5, &key2_props)); | |
2764 base::DictionaryValue* key2_jwk; | |
2765 ASSERT_TRUE(key2_props->GetDictionary("jwk", &key2_jwk)); | |
2766 std::string modulus; | |
2767 key1_jwk->GetString("n", &modulus); | |
2768 key2_jwk->SetString("n", modulus); | |
2769 | |
2770 // This should fail, as the n,e,d parameters are not consistent. It MUST NOT | |
2771 // somehow return the key created earlier. | |
2772 blink::WebCryptoKey key2 = blink::WebCryptoKey::createNull(); | |
2773 ASSERT_EQ(Status::OperationError(), | |
2774 ImportKeyJwkFromDict(*key2_jwk, | |
2775 CreateRsaHashedImportAlgorithm( | |
2776 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2777 blink::WebCryptoAlgorithmIdSha256), | |
2778 true, | |
2779 blink::WebCryptoKeyUsageSign, | |
2780 &key2)); | |
2781 } | |
2782 | |
2783 // Import a JWK RSA private key with some optional parameters missing (q, dp, | |
2784 // dq, qi). | |
2785 // | |
2786 // The only optional parameter included is "p". | |
2787 // | |
2788 // This fails because JWA says that producers must include either ALL optional | |
2789 // parameters or NONE. | |
2790 TEST(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkMissingOptionalParams) { | |
2791 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2792 | |
2793 base::DictionaryValue dict; | |
2794 dict.SetString("kty", "RSA"); | |
2795 dict.SetString("alg", "RS1"); | |
2796 | |
2797 dict.SetString( | |
2798 "n", | |
2799 "pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" | |
2800 "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" | |
2801 "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc"); | |
2802 dict.SetString("e", "AQAB"); | |
2803 dict.SetString( | |
2804 "d", | |
2805 "M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" | |
2806 "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" | |
2807 "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU"); | |
2808 | |
2809 dict.SetString("p", | |
2810 "5-" | |
2811 "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31W" | |
2812 "hU1vZs8w0Fgs7bc0-2o5kQw"); | |
2813 | |
2814 ASSERT_EQ(Status::ErrorJwkPropertyMissing("q"), | |
2815 ImportKeyJwkFromDict(dict, | |
2816 CreateRsaHashedImportAlgorithm( | |
2817 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2818 blink::WebCryptoAlgorithmIdSha1), | |
2819 true, | |
2820 blink::WebCryptoKeyUsageSign, | |
2821 &key)); | |
2822 } | |
2823 | |
2824 // Import a JWK RSA private key, without any of the optional parameters. | |
2825 // | |
2826 // According to JWA, such keys are valid, but applications SHOULD | |
2827 // include all the parameters when sending, and recipients MAY | |
2828 // accept them, but are not required to. Chromium's WebCrypto does | |
2829 // not allow such degenerate keys. | |
2830 TEST(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkIncorrectOptionalEmpty) { | |
2831 if (!SupportsRsaPrivateKeyImport()) | |
2832 return; | |
2833 | |
2834 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2835 | |
2836 base::DictionaryValue dict; | |
2837 dict.SetString("kty", "RSA"); | |
2838 dict.SetString("alg", "RS1"); | |
2839 | |
2840 dict.SetString( | |
2841 "n", | |
2842 "pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" | |
2843 "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" | |
2844 "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc"); | |
2845 dict.SetString("e", "AQAB"); | |
2846 dict.SetString( | |
2847 "d", | |
2848 "M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" | |
2849 "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" | |
2850 "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU"); | |
2851 | |
2852 ASSERT_EQ(Status::ErrorJwkPropertyMissing("p"), | |
2853 ImportKeyJwkFromDict(dict, | |
2854 CreateRsaHashedImportAlgorithm( | |
2855 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2856 blink::WebCryptoAlgorithmIdSha1), | |
2857 true, | |
2858 blink::WebCryptoKeyUsageSign, | |
2859 &key)); | |
2860 } | |
2861 | |
2862 // Tries importing a public RSA key whose exponent contains leading zeros. | |
2863 TEST(WebCryptoRsaSsaTest, ImportJwkRsaNonMinimalExponent) { | |
2864 base::DictionaryValue dict; | |
2865 | |
2866 dict.SetString("kty", "RSA"); | |
2867 dict.SetString("e", "AAEAAQ"); // 00 01 00 01 | |
2868 dict.SetString( | |
2869 "n", | |
2870 "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" | |
2871 "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" | |
2872 "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); | |
2873 | |
2874 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
2875 | |
2876 EXPECT_EQ(Status::ErrorJwkBigIntegerHasLeadingZero("e"), | |
2877 ImportKeyJwkFromDict(dict, | |
2878 CreateRsaHashedImportAlgorithm( | |
2879 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2880 blink::WebCryptoAlgorithmIdSha256), | |
2881 false, | |
2882 blink::WebCryptoKeyUsageVerify, | |
2883 &key)); | |
2884 } | |
2885 | |
2886 TEST(WebCryptoRsaSsaTest, GenerateKeyPairRsa) { | |
2887 // Note: using unrealistic short key lengths here to avoid bogging down tests. | |
2888 | |
2889 // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha256) | |
2890 const unsigned int modulus_length = 256; | |
2891 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
2892 blink::WebCryptoAlgorithm algorithm = | |
2893 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2894 blink::WebCryptoAlgorithmIdSha256, | |
2895 modulus_length, | |
2896 public_exponent); | |
2897 bool extractable = true; | |
2898 const blink::WebCryptoKeyUsageMask usage_mask = 0; | |
2899 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
2900 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
2901 | |
2902 EXPECT_EQ(Status::Success(), | |
2903 GenerateKeyPair( | |
2904 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
2905 EXPECT_FALSE(public_key.isNull()); | |
2906 EXPECT_FALSE(private_key.isNull()); | |
2907 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
2908 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
2909 EXPECT_EQ(modulus_length, | |
2910 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2911 EXPECT_EQ(modulus_length, | |
2912 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2913 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
2914 public_key.algorithm().rsaHashedParams()->hash().id()); | |
2915 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
2916 private_key.algorithm().rsaHashedParams()->hash().id()); | |
2917 EXPECT_TRUE(public_key.extractable()); | |
2918 EXPECT_EQ(extractable, private_key.extractable()); | |
2919 EXPECT_EQ(usage_mask, public_key.usages()); | |
2920 EXPECT_EQ(usage_mask, private_key.usages()); | |
2921 | |
2922 // Try exporting the generated key pair, and then re-importing to verify that | |
2923 // the exported data was valid. | |
2924 std::vector<uint8_t> public_key_spki; | |
2925 EXPECT_EQ( | |
2926 Status::Success(), | |
2927 ExportKey(blink::WebCryptoKeyFormatSpki, public_key, &public_key_spki)); | |
2928 | |
2929 if (SupportsRsaPrivateKeyImport()) { | |
2930 public_key = blink::WebCryptoKey::createNull(); | |
2931 EXPECT_EQ(Status::Success(), | |
2932 ImportKey(blink::WebCryptoKeyFormatSpki, | |
2933 CryptoData(public_key_spki), | |
2934 CreateRsaHashedImportAlgorithm( | |
2935 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2936 blink::WebCryptoAlgorithmIdSha256), | |
2937 true, | |
2938 usage_mask, | |
2939 &public_key)); | |
2940 EXPECT_EQ(modulus_length, | |
2941 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2942 | |
2943 std::vector<uint8_t> private_key_pkcs8; | |
2944 EXPECT_EQ( | |
2945 Status::Success(), | |
2946 ExportKey( | |
2947 blink::WebCryptoKeyFormatPkcs8, private_key, &private_key_pkcs8)); | |
2948 private_key = blink::WebCryptoKey::createNull(); | |
2949 EXPECT_EQ(Status::Success(), | |
2950 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
2951 CryptoData(private_key_pkcs8), | |
2952 CreateRsaHashedImportAlgorithm( | |
2953 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2954 blink::WebCryptoAlgorithmIdSha256), | |
2955 true, | |
2956 usage_mask, | |
2957 &private_key)); | |
2958 EXPECT_EQ(modulus_length, | |
2959 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
2960 } | |
2961 | |
2962 // Fail with bad modulus. | |
2963 algorithm = | |
2964 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2965 blink::WebCryptoAlgorithmIdSha256, | |
2966 0, | |
2967 public_exponent); | |
2968 EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), | |
2969 GenerateKeyPair( | |
2970 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
2971 | |
2972 // Fail with bad exponent: larger than unsigned long. | |
2973 unsigned int exponent_length = sizeof(unsigned long) + 1; // NOLINT | |
2974 const std::vector<uint8_t> long_exponent(exponent_length, 0x01); | |
2975 algorithm = | |
2976 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2977 blink::WebCryptoAlgorithmIdSha256, | |
2978 modulus_length, | |
2979 long_exponent); | |
2980 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
2981 GenerateKeyPair( | |
2982 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
2983 | |
2984 // Fail with bad exponent: empty. | |
2985 const std::vector<uint8_t> empty_exponent; | |
2986 algorithm = | |
2987 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2988 blink::WebCryptoAlgorithmIdSha256, | |
2989 modulus_length, | |
2990 empty_exponent); | |
2991 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
2992 GenerateKeyPair( | |
2993 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
2994 | |
2995 // Fail with bad exponent: all zeros. | |
2996 std::vector<uint8_t> exponent_with_leading_zeros(15, 0x00); | |
2997 algorithm = | |
2998 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
2999 blink::WebCryptoAlgorithmIdSha256, | |
3000 modulus_length, | |
3001 exponent_with_leading_zeros); | |
3002 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
3003 GenerateKeyPair( | |
3004 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
3005 | |
3006 // Key generation success using exponent with leading zeros. | |
3007 exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), | |
3008 public_exponent.begin(), | |
3009 public_exponent.end()); | |
3010 algorithm = | |
3011 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3012 blink::WebCryptoAlgorithmIdSha256, | |
3013 modulus_length, | |
3014 exponent_with_leading_zeros); | |
3015 EXPECT_EQ(Status::Success(), | |
3016 GenerateKeyPair( | |
3017 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
3018 EXPECT_FALSE(public_key.isNull()); | |
3019 EXPECT_FALSE(private_key.isNull()); | |
3020 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
3021 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
3022 EXPECT_TRUE(public_key.extractable()); | |
3023 EXPECT_EQ(extractable, private_key.extractable()); | |
3024 EXPECT_EQ(usage_mask, public_key.usages()); | |
3025 EXPECT_EQ(usage_mask, private_key.usages()); | |
3026 | |
3027 // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha1) | |
3028 algorithm = | |
3029 CreateRsaHashedKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3030 blink::WebCryptoAlgorithmIdSha1, | |
3031 modulus_length, | |
3032 public_exponent); | |
3033 EXPECT_EQ( | |
3034 Status::Success(), | |
3035 GenerateKeyPair(algorithm, false, usage_mask, &public_key, &private_key)); | |
3036 EXPECT_FALSE(public_key.isNull()); | |
3037 EXPECT_FALSE(private_key.isNull()); | |
3038 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
3039 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
3040 EXPECT_EQ(modulus_length, | |
3041 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
3042 EXPECT_EQ(modulus_length, | |
3043 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
3044 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
3045 public_key.algorithm().rsaHashedParams()->hash().id()); | |
3046 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
3047 private_key.algorithm().rsaHashedParams()->hash().id()); | |
3048 // Even though "extractable" was set to false, the public key remains | |
3049 // extractable. | |
3050 EXPECT_TRUE(public_key.extractable()); | |
3051 EXPECT_FALSE(private_key.extractable()); | |
3052 EXPECT_EQ(usage_mask, public_key.usages()); | |
3053 EXPECT_EQ(usage_mask, private_key.usages()); | |
3054 | |
3055 // Exporting a private key as SPKI format doesn't make sense. However this | |
3056 // will first fail because the key is not extractable. | |
3057 std::vector<uint8_t> output; | |
3058 EXPECT_EQ(Status::ErrorKeyNotExtractable(), | |
3059 ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); | |
3060 | |
3061 // Re-generate an extractable private_key and try to export it as SPKI format. | |
3062 // This should fail since spki is for public keys. | |
3063 EXPECT_EQ( | |
3064 Status::Success(), | |
3065 GenerateKeyPair(algorithm, true, usage_mask, &public_key, &private_key)); | |
3066 EXPECT_EQ(Status::ErrorUnexpectedKeyType(), | |
3067 ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); | |
3068 } | |
3069 | |
3070 TEST(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadModulusLength) { | |
3071 const unsigned int kBadModulusBits[] = { | |
3072 0, | |
3073 248, // Too small. | |
3074 257, // Not a multiple of 8. | |
3075 1023, // Not a multiple of 8. | |
3076 0xFFFFFFFF, // Too big. | |
3077 16384 + 8, // 16384 is the maxmimum length that NSS succeeds for. | |
3078 }; | |
3079 | |
3080 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
3081 | |
3082 for (size_t i = 0; i < arraysize(kBadModulusBits); ++i) { | |
3083 const unsigned int modulus_length_bits = kBadModulusBits[i]; | |
3084 blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( | |
3085 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3086 blink::WebCryptoAlgorithmIdSha256, | |
3087 modulus_length_bits, | |
3088 public_exponent); | |
3089 bool extractable = true; | |
3090 const blink::WebCryptoKeyUsageMask usage_mask = 0; | |
3091 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3092 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
3093 | |
3094 EXPECT_EQ( | |
3095 Status::ErrorGenerateRsaUnsupportedModulus(), | |
3096 GenerateKeyPair( | |
3097 algorithm, extractable, usage_mask, &public_key, &private_key)); | |
3098 } | |
3099 } | |
3100 | |
3101 // Try generating RSA key pairs using unsupported public exponents. Only | |
3102 // exponents of 3 and 65537 are supported. While both OpenSSL and NSS can | |
3103 // support other values, OpenSSL hangs when given invalid exponents, so use a | |
3104 // whitelist to validate the parameters. | |
3105 TEST(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadExponent) { | |
3106 const unsigned int modulus_length = 1024; | |
3107 | |
3108 const char* const kPublicExponents[] = { | |
3109 "11", // 17 - This is a valid public exponent, but currently disallowed. | |
3110 "00", | |
3111 "01", | |
3112 "02", | |
3113 "010000", // 65536 | |
3114 }; | |
3115 | |
3116 for (size_t i = 0; i < arraysize(kPublicExponents); ++i) { | |
3117 SCOPED_TRACE(i); | |
3118 blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( | |
3119 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3120 blink::WebCryptoAlgorithmIdSha256, | |
3121 modulus_length, | |
3122 HexStringToBytes(kPublicExponents[i])); | |
3123 | |
3124 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3125 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
3126 | |
3127 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
3128 GenerateKeyPair(algorithm, true, 0, &public_key, &private_key)); | |
3129 } | |
3130 } | |
3131 | |
3132 TEST(WebCryptoRsaSsaTest, SignVerifyFailures) { | |
3133 if (!SupportsRsaPrivateKeyImport()) | |
3134 return; | |
3135 | |
3136 // Import a key pair. | |
3137 blink::WebCryptoAlgorithm import_algorithm = | |
3138 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3139 blink::WebCryptoAlgorithmIdSha1); | |
3140 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3141 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
3142 ASSERT_NO_FATAL_FAILURE( | |
3143 ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), | |
3144 HexStringToBytes(kPrivateKeyPkcs8DerHex), | |
3145 import_algorithm, | |
3146 false, | |
3147 blink::WebCryptoKeyUsageVerify, | |
3148 blink::WebCryptoKeyUsageSign, | |
3149 &public_key, | |
3150 &private_key)); | |
3151 | |
3152 blink::WebCryptoAlgorithm algorithm = | |
3153 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); | |
3154 | |
3155 std::vector<uint8_t> signature; | |
3156 bool signature_match; | |
3157 | |
3158 // Compute a signature. | |
3159 const std::vector<uint8_t> data = HexStringToBytes("010203040506070809"); | |
3160 ASSERT_EQ(Status::Success(), | |
3161 Sign(algorithm, private_key, CryptoData(data), &signature)); | |
3162 | |
3163 // Ensure truncated signature does not verify by passing one less byte. | |
3164 EXPECT_EQ( | |
3165 Status::Success(), | |
3166 Verify(algorithm, | |
3167 public_key, | |
3168 CryptoData(vector_as_array(&signature), signature.size() - 1), | |
3169 CryptoData(data), | |
3170 &signature_match)); | |
3171 EXPECT_FALSE(signature_match); | |
3172 | |
3173 // Ensure truncated signature does not verify by passing no bytes. | |
3174 EXPECT_EQ(Status::Success(), | |
3175 Verify(algorithm, | |
3176 public_key, | |
3177 CryptoData(), | |
3178 CryptoData(data), | |
3179 &signature_match)); | |
3180 EXPECT_FALSE(signature_match); | |
3181 | |
3182 // Ensure corrupted signature does not verify. | |
3183 std::vector<uint8_t> corrupt_sig = signature; | |
3184 corrupt_sig[corrupt_sig.size() / 2] ^= 0x1; | |
3185 EXPECT_EQ(Status::Success(), | |
3186 Verify(algorithm, | |
3187 public_key, | |
3188 CryptoData(corrupt_sig), | |
3189 CryptoData(data), | |
3190 &signature_match)); | |
3191 EXPECT_FALSE(signature_match); | |
3192 | |
3193 // Ensure signatures that are greater than the modulus size fail. | |
3194 const unsigned int long_message_size_bytes = 1024; | |
3195 DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8); | |
3196 const unsigned char kLongSignature[long_message_size_bytes] = {0}; | |
3197 EXPECT_EQ(Status::Success(), | |
3198 Verify(algorithm, | |
3199 public_key, | |
3200 CryptoData(kLongSignature, sizeof(kLongSignature)), | |
3201 CryptoData(data), | |
3202 &signature_match)); | |
3203 EXPECT_FALSE(signature_match); | |
3204 | |
3205 // Ensure that signing and verifying with an incompatible algorithm fails. | |
3206 algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep); | |
3207 | |
3208 EXPECT_EQ(Status::ErrorUnexpected(), | |
3209 Sign(algorithm, private_key, CryptoData(data), &signature)); | |
3210 EXPECT_EQ(Status::ErrorUnexpected(), | |
3211 Verify(algorithm, | |
3212 public_key, | |
3213 CryptoData(signature), | |
3214 CryptoData(data), | |
3215 &signature_match)); | |
3216 | |
3217 // Some crypto libraries (NSS) can automatically select the RSA SSA inner hash | |
3218 // based solely on the contents of the input signature data. In the Web Crypto | |
3219 // implementation, the inner hash should be specified uniquely by the key | |
3220 // algorithm parameter. To validate this behavior, call Verify with a computed | |
3221 // signature that used one hash type (SHA-1), but pass in a key with a | |
3222 // different inner hash type (SHA-256). If the hash type is determined by the | |
3223 // signature itself (undesired), the verify will pass, while if the hash type | |
3224 // is specified by the key algorithm (desired), the verify will fail. | |
3225 | |
3226 // Compute a signature using SHA-1 as the inner hash. | |
3227 EXPECT_EQ(Status::Success(), | |
3228 Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
3229 private_key, | |
3230 CryptoData(data), | |
3231 &signature)); | |
3232 | |
3233 blink::WebCryptoKey public_key_256 = blink::WebCryptoKey::createNull(); | |
3234 EXPECT_EQ(Status::Success(), | |
3235 ImportKey(blink::WebCryptoKeyFormatSpki, | |
3236 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
3237 CreateRsaHashedImportAlgorithm( | |
3238 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3239 blink::WebCryptoAlgorithmIdSha256), | |
3240 true, | |
3241 blink::WebCryptoKeyUsageVerify, | |
3242 &public_key_256)); | |
3243 | |
3244 // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The | |
3245 // signature should not verify. | |
3246 // NOTE: public_key was produced by generateKey, and so its associated | |
3247 // algorithm has WebCryptoRsaKeyGenParams and not WebCryptoRsaSsaParams. Thus | |
3248 // it has no inner hash to conflict with the input algorithm. | |
3249 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
3250 private_key.algorithm().rsaHashedParams()->hash().id()); | |
3251 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
3252 public_key_256.algorithm().rsaHashedParams()->hash().id()); | |
3253 | |
3254 bool is_match; | |
3255 EXPECT_EQ(Status::Success(), | |
3256 Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
3257 public_key_256, | |
3258 CryptoData(signature), | |
3259 CryptoData(data), | |
3260 &is_match)); | |
3261 EXPECT_FALSE(is_match); | |
3262 } | |
3263 | |
3264 TEST(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) { | |
3265 if (!SupportsRsaPrivateKeyImport()) | |
3266 return; | |
3267 | |
3268 scoped_ptr<base::ListValue> tests; | |
3269 ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests)); | |
3270 | |
3271 // Import the key pair. | |
3272 blink::WebCryptoAlgorithm import_algorithm = | |
3273 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
3274 blink::WebCryptoAlgorithmIdSha1); | |
3275 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3276 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
3277 ASSERT_NO_FATAL_FAILURE( | |
3278 ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), | |
3279 HexStringToBytes(kPrivateKeyPkcs8DerHex), | |
3280 import_algorithm, | |
3281 false, | |
3282 blink::WebCryptoKeyUsageVerify, | |
3283 blink::WebCryptoKeyUsageSign, | |
3284 &public_key, | |
3285 &private_key)); | |
3286 | |
3287 blink::WebCryptoAlgorithm algorithm = | |
3288 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); | |
3289 | |
3290 // Validate the signatures are computed and verified as expected. | |
3291 std::vector<uint8_t> signature; | |
3292 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
3293 SCOPED_TRACE(test_index); | |
3294 | |
3295 base::DictionaryValue* test; | |
3296 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
3297 | |
3298 std::vector<uint8_t> test_message = | |
3299 GetBytesFromHexString(test, "message_hex"); | |
3300 std::vector<uint8_t> test_signature = | |
3301 GetBytesFromHexString(test, "signature_hex"); | |
3302 | |
3303 signature.clear(); | |
3304 ASSERT_EQ( | |
3305 Status::Success(), | |
3306 Sign(algorithm, private_key, CryptoData(test_message), &signature)); | |
3307 EXPECT_BYTES_EQ(test_signature, signature); | |
3308 | |
3309 bool is_match = false; | |
3310 ASSERT_EQ(Status::Success(), | |
3311 Verify(algorithm, | |
3312 public_key, | |
3313 CryptoData(test_signature), | |
3314 CryptoData(test_message), | |
3315 &is_match)); | |
3316 EXPECT_TRUE(is_match); | |
3317 } | |
3318 } | |
3319 | |
3320 TEST(WebCryptoAesKwTest, AesKwKeyImport) { | |
3321 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
3322 blink::WebCryptoAlgorithm algorithm = | |
3323 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3324 | |
3325 // Import a 128-bit Key Encryption Key (KEK) | |
3326 std::string key_raw_hex_in = "025a8cf3f08b4f6c5f33bbc76a471939"; | |
3327 ASSERT_EQ(Status::Success(), | |
3328 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3329 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3330 algorithm, | |
3331 true, | |
3332 blink::WebCryptoKeyUsageWrapKey, | |
3333 &key)); | |
3334 std::vector<uint8_t> key_raw_out; | |
3335 EXPECT_EQ(Status::Success(), | |
3336 ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); | |
3337 EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); | |
3338 | |
3339 // Import a 192-bit KEK | |
3340 key_raw_hex_in = "c0192c6466b2370decbb62b2cfef4384544ffeb4d2fbc103"; | |
3341 ASSERT_EQ(Status::ErrorAes192BitUnsupported(), | |
3342 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3343 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3344 algorithm, | |
3345 true, | |
3346 blink::WebCryptoKeyUsageWrapKey, | |
3347 &key)); | |
3348 | |
3349 // Import a 256-bit Key Encryption Key (KEK) | |
3350 key_raw_hex_in = | |
3351 "e11fe66380d90fa9ebefb74e0478e78f95664d0c67ca20ce4a0b5842863ac46f"; | |
3352 ASSERT_EQ(Status::Success(), | |
3353 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3354 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3355 algorithm, | |
3356 true, | |
3357 blink::WebCryptoKeyUsageWrapKey, | |
3358 &key)); | |
3359 EXPECT_EQ(Status::Success(), | |
3360 ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out)); | |
3361 EXPECT_BYTES_EQ_HEX(key_raw_hex_in, key_raw_out); | |
3362 | |
3363 // Fail import of 0 length key | |
3364 EXPECT_EQ(Status::ErrorImportAesKeyLength(), | |
3365 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3366 CryptoData(HexStringToBytes("")), | |
3367 algorithm, | |
3368 true, | |
3369 blink::WebCryptoKeyUsageWrapKey, | |
3370 &key)); | |
3371 | |
3372 // Fail import of 124-bit KEK | |
3373 key_raw_hex_in = "3e4566a2bdaa10cb68134fa66c15ddb"; | |
3374 EXPECT_EQ(Status::ErrorImportAesKeyLength(), | |
3375 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3376 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3377 algorithm, | |
3378 true, | |
3379 blink::WebCryptoKeyUsageWrapKey, | |
3380 &key)); | |
3381 | |
3382 // Fail import of 200-bit KEK | |
3383 key_raw_hex_in = "0a1d88608a5ad9fec64f1ada269ebab4baa2feeb8d95638c0e"; | |
3384 EXPECT_EQ(Status::ErrorImportAesKeyLength(), | |
3385 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3386 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3387 algorithm, | |
3388 true, | |
3389 blink::WebCryptoKeyUsageWrapKey, | |
3390 &key)); | |
3391 | |
3392 // Fail import of 260-bit KEK | |
3393 key_raw_hex_in = | |
3394 "72d4e475ff34215416c9ad9c8281247a4d730c5f275ac23f376e73e3bce8d7d5a"; | |
3395 EXPECT_EQ(Status::ErrorImportAesKeyLength(), | |
3396 ImportKey(blink::WebCryptoKeyFormatRaw, | |
3397 CryptoData(HexStringToBytes(key_raw_hex_in)), | |
3398 algorithm, | |
3399 true, | |
3400 blink::WebCryptoKeyUsageWrapKey, | |
3401 &key)); | |
3402 } | |
3403 | |
3404 TEST(WebCryptoAesKwTest, UnwrapFailures) { | |
3405 // This test exercises the code path common to all unwrap operations. | |
3406 scoped_ptr<base::ListValue> tests; | |
3407 ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); | |
3408 base::DictionaryValue* test; | |
3409 ASSERT_TRUE(tests->GetDictionary(0, &test)); | |
3410 const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); | |
3411 const std::vector<uint8_t> test_ciphertext = | |
3412 GetBytesFromHexString(test, "ciphertext"); | |
3413 | |
3414 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3415 | |
3416 // Using a wrapping algorithm that does not match the wrapping key algorithm | |
3417 // should fail. | |
3418 blink::WebCryptoKey wrapping_key = | |
3419 ImportSecretKeyFromRaw(test_kek, | |
3420 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), | |
3421 blink::WebCryptoKeyUsageUnwrapKey); | |
3422 EXPECT_EQ(Status::ErrorUnexpected(), | |
3423 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3424 CryptoData(test_ciphertext), | |
3425 wrapping_key, | |
3426 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3427 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3428 true, | |
3429 blink::WebCryptoKeyUsageEncrypt, | |
3430 &unwrapped_key)); | |
3431 } | |
3432 | |
3433 TEST(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) { | |
3434 scoped_ptr<base::ListValue> tests; | |
3435 ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); | |
3436 | |
3437 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
3438 SCOPED_TRACE(test_index); | |
3439 base::DictionaryValue* test; | |
3440 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
3441 const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); | |
3442 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
3443 const std::vector<uint8_t> test_ciphertext = | |
3444 GetBytesFromHexString(test, "ciphertext"); | |
3445 const blink::WebCryptoAlgorithm wrapping_algorithm = | |
3446 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3447 | |
3448 // Import the wrapping key. | |
3449 blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( | |
3450 test_kek, | |
3451 wrapping_algorithm, | |
3452 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); | |
3453 | |
3454 // Import the key to be wrapped. | |
3455 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
3456 test_key, | |
3457 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), | |
3458 blink::WebCryptoKeyUsageSign); | |
3459 | |
3460 // Wrap the key and verify the ciphertext result against the known answer. | |
3461 std::vector<uint8_t> wrapped_key; | |
3462 ASSERT_EQ(Status::Success(), | |
3463 WrapKey(blink::WebCryptoKeyFormatRaw, | |
3464 key, | |
3465 wrapping_key, | |
3466 wrapping_algorithm, | |
3467 &wrapped_key)); | |
3468 EXPECT_BYTES_EQ(test_ciphertext, wrapped_key); | |
3469 | |
3470 // Unwrap the known ciphertext to get a new test_key. | |
3471 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3472 ASSERT_EQ( | |
3473 Status::Success(), | |
3474 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3475 CryptoData(test_ciphertext), | |
3476 wrapping_key, | |
3477 wrapping_algorithm, | |
3478 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), | |
3479 true, | |
3480 blink::WebCryptoKeyUsageSign, | |
3481 &unwrapped_key)); | |
3482 EXPECT_FALSE(key.isNull()); | |
3483 EXPECT_TRUE(key.handle()); | |
3484 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
3485 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
3486 EXPECT_EQ(true, key.extractable()); | |
3487 EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); | |
3488 | |
3489 // Export the new key and compare its raw bytes with the original known key. | |
3490 std::vector<uint8_t> raw_key; | |
3491 EXPECT_EQ(Status::Success(), | |
3492 ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); | |
3493 EXPECT_BYTES_EQ(test_key, raw_key); | |
3494 } | |
3495 } | |
3496 | |
3497 // Unwrap a HMAC key using AES-KW, and then try doing a sign/verify with the | |
3498 // unwrapped key | |
3499 TEST(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) { | |
3500 scoped_ptr<base::ListValue> tests; | |
3501 ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); | |
3502 | |
3503 base::DictionaryValue* test; | |
3504 ASSERT_TRUE(tests->GetDictionary(0, &test)); | |
3505 const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); | |
3506 const std::vector<uint8_t> test_ciphertext = | |
3507 GetBytesFromHexString(test, "ciphertext"); | |
3508 const blink::WebCryptoAlgorithm wrapping_algorithm = | |
3509 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3510 | |
3511 // Import the wrapping key. | |
3512 blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( | |
3513 test_kek, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); | |
3514 | |
3515 // Unwrap the known ciphertext. | |
3516 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
3517 ASSERT_EQ( | |
3518 Status::Success(), | |
3519 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3520 CryptoData(test_ciphertext), | |
3521 wrapping_key, | |
3522 wrapping_algorithm, | |
3523 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1), | |
3524 false, | |
3525 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
3526 &key)); | |
3527 | |
3528 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | |
3529 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, key.algorithm().id()); | |
3530 EXPECT_FALSE(key.extractable()); | |
3531 EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
3532 key.usages()); | |
3533 | |
3534 // Sign an empty message and ensure it is verified. | |
3535 std::vector<uint8_t> test_message; | |
3536 std::vector<uint8_t> signature; | |
3537 | |
3538 ASSERT_EQ(Status::Success(), | |
3539 Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), | |
3540 key, | |
3541 CryptoData(test_message), | |
3542 &signature)); | |
3543 | |
3544 EXPECT_GT(signature.size(), 0u); | |
3545 | |
3546 bool verify_result; | |
3547 ASSERT_EQ(Status::Success(), | |
3548 Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdHmac), | |
3549 key, | |
3550 CryptoData(signature), | |
3551 CryptoData(test_message), | |
3552 &verify_result)); | |
3553 } | |
3554 | |
3555 TEST(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) { | |
3556 scoped_ptr<base::ListValue> tests; | |
3557 ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); | |
3558 base::DictionaryValue* test; | |
3559 // Use 256 bits of data with a 256-bit KEK | |
3560 ASSERT_TRUE(tests->GetDictionary(3, &test)); | |
3561 const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); | |
3562 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
3563 const std::vector<uint8_t> test_ciphertext = | |
3564 GetBytesFromHexString(test, "ciphertext"); | |
3565 const blink::WebCryptoAlgorithm wrapping_algorithm = | |
3566 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3567 const blink::WebCryptoAlgorithm key_algorithm = | |
3568 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
3569 // Import the wrapping key. | |
3570 blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( | |
3571 test_kek, | |
3572 wrapping_algorithm, | |
3573 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); | |
3574 // Import the key to be wrapped. | |
3575 blink::WebCryptoKey key = | |
3576 ImportSecretKeyFromRaw(test_key, | |
3577 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3578 blink::WebCryptoKeyUsageEncrypt); | |
3579 | |
3580 // Unwrap with wrapped data too small must fail. | |
3581 const std::vector<uint8_t> small_data(test_ciphertext.begin(), | |
3582 test_ciphertext.begin() + 23); | |
3583 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3584 EXPECT_EQ(Status::ErrorDataTooSmall(), | |
3585 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3586 CryptoData(small_data), | |
3587 wrapping_key, | |
3588 wrapping_algorithm, | |
3589 key_algorithm, | |
3590 true, | |
3591 blink::WebCryptoKeyUsageEncrypt, | |
3592 &unwrapped_key)); | |
3593 | |
3594 // Unwrap with wrapped data size not a multiple of 8 bytes must fail. | |
3595 const std::vector<uint8_t> unaligned_data(test_ciphertext.begin(), | |
3596 test_ciphertext.end() - 2); | |
3597 EXPECT_EQ(Status::ErrorInvalidAesKwDataLength(), | |
3598 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3599 CryptoData(unaligned_data), | |
3600 wrapping_key, | |
3601 wrapping_algorithm, | |
3602 key_algorithm, | |
3603 true, | |
3604 blink::WebCryptoKeyUsageEncrypt, | |
3605 &unwrapped_key)); | |
3606 } | |
3607 | |
3608 TEST(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapCorruptData) { | |
3609 scoped_ptr<base::ListValue> tests; | |
3610 ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests)); | |
3611 base::DictionaryValue* test; | |
3612 // Use 256 bits of data with a 256-bit KEK | |
3613 ASSERT_TRUE(tests->GetDictionary(3, &test)); | |
3614 const std::vector<uint8_t> test_kek = GetBytesFromHexString(test, "kek"); | |
3615 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
3616 const std::vector<uint8_t> test_ciphertext = | |
3617 GetBytesFromHexString(test, "ciphertext"); | |
3618 const blink::WebCryptoAlgorithm wrapping_algorithm = | |
3619 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3620 | |
3621 // Import the wrapping key. | |
3622 blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( | |
3623 test_kek, | |
3624 wrapping_algorithm, | |
3625 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey); | |
3626 | |
3627 // Unwrap of a corrupted version of the known ciphertext should fail, due to | |
3628 // AES-KW's built-in integrity check. | |
3629 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3630 EXPECT_EQ(Status::OperationError(), | |
3631 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3632 CryptoData(Corrupted(test_ciphertext)), | |
3633 wrapping_key, | |
3634 wrapping_algorithm, | |
3635 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3636 true, | |
3637 blink::WebCryptoKeyUsageEncrypt, | |
3638 &unwrapped_key)); | |
3639 } | |
3640 | |
3641 TEST(WebCryptoAesKwTest, AesKwJwkSymkeyUnwrapKnownData) { | |
3642 // The following data lists a known HMAC SHA-256 key, then a JWK | |
3643 // representation of this key which was encrypted ("wrapped") using AES-KW and | |
3644 // the following wrapping key. | |
3645 // For reference, the intermediate clear JWK is | |
3646 // {"alg":"HS256","ext":true,"k":<b64urlKey>,"key_ops":["verify"],"kty":"oct"} | |
3647 // (Not shown is space padding to ensure the cleartext meets the size | |
3648 // requirements of the AES-KW algorithm.) | |
3649 const std::vector<uint8_t> key_data = HexStringToBytes( | |
3650 "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"); | |
3651 const std::vector<uint8_t> wrapped_key_data = HexStringToBytes( | |
3652 "14E6380B35FDC5B72E1994764B6CB7BFDD64E7832894356AAEE6C3768FC3D0F115E6B0" | |
3653 "6729756225F999AA99FDF81FD6A359F1576D3D23DE6CB69C3937054EB497AC1E8C38D5" | |
3654 "5E01B9783A20C8D930020932CF25926103002213D0FC37279888154FEBCEDF31832158" | |
3655 "97938C5CFE5B10B4254D0C399F39D0"); | |
3656 const std::vector<uint8_t> wrapping_key_data = | |
3657 HexStringToBytes("000102030405060708090A0B0C0D0E0F"); | |
3658 const blink::WebCryptoAlgorithm wrapping_algorithm = | |
3659 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
3660 | |
3661 // Import the wrapping key. | |
3662 blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( | |
3663 wrapping_key_data, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); | |
3664 | |
3665 // Unwrap the known wrapped key data to produce a new key | |
3666 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3667 ASSERT_EQ( | |
3668 Status::Success(), | |
3669 UnwrapKey(blink::WebCryptoKeyFormatJwk, | |
3670 CryptoData(wrapped_key_data), | |
3671 wrapping_key, | |
3672 wrapping_algorithm, | |
3673 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
3674 true, | |
3675 blink::WebCryptoKeyUsageVerify, | |
3676 &unwrapped_key)); | |
3677 | |
3678 // Validate the new key's attributes. | |
3679 EXPECT_FALSE(unwrapped_key.isNull()); | |
3680 EXPECT_TRUE(unwrapped_key.handle()); | |
3681 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, unwrapped_key.type()); | |
3682 EXPECT_EQ(blink::WebCryptoAlgorithmIdHmac, unwrapped_key.algorithm().id()); | |
3683 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
3684 unwrapped_key.algorithm().hmacParams()->hash().id()); | |
3685 EXPECT_EQ(256u, unwrapped_key.algorithm().hmacParams()->lengthBits()); | |
3686 EXPECT_EQ(true, unwrapped_key.extractable()); | |
3687 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, unwrapped_key.usages()); | |
3688 | |
3689 // Export the new key's raw data and compare to the known original. | |
3690 std::vector<uint8_t> raw_key; | |
3691 EXPECT_EQ(Status::Success(), | |
3692 ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); | |
3693 EXPECT_BYTES_EQ(key_data, raw_key); | |
3694 } | |
3695 | |
3696 // TODO(eroman): | |
3697 // * Test decryption when the tag length exceeds input size | |
3698 // * Test decryption with empty input | |
3699 // * Test decryption with tag length of 0. | |
3700 TEST(WebCryptoAesGcmTest, SampleSets) { | |
3701 // Some Linux test runners may not have a new enough version of NSS. | |
3702 if (!SupportsAesGcm()) { | |
3703 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
3704 return; | |
3705 } | |
3706 | |
3707 scoped_ptr<base::ListValue> tests; | |
3708 ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests)); | |
3709 | |
3710 // Note that WebCrypto appends the authentication tag to the ciphertext. | |
3711 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
3712 SCOPED_TRACE(test_index); | |
3713 base::DictionaryValue* test; | |
3714 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
3715 | |
3716 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
3717 const std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); | |
3718 const std::vector<uint8_t> test_additional_data = | |
3719 GetBytesFromHexString(test, "additional_data"); | |
3720 const std::vector<uint8_t> test_plain_text = | |
3721 GetBytesFromHexString(test, "plain_text"); | |
3722 const std::vector<uint8_t> test_authentication_tag = | |
3723 GetBytesFromHexString(test, "authentication_tag"); | |
3724 const unsigned int test_tag_size_bits = test_authentication_tag.size() * 8; | |
3725 const std::vector<uint8_t> test_cipher_text = | |
3726 GetBytesFromHexString(test, "cipher_text"); | |
3727 | |
3728 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
3729 test_key, | |
3730 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | |
3731 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); | |
3732 | |
3733 // Verify exported raw key is identical to the imported data | |
3734 std::vector<uint8_t> raw_key; | |
3735 EXPECT_EQ(Status::Success(), | |
3736 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
3737 | |
3738 EXPECT_BYTES_EQ(test_key, raw_key); | |
3739 | |
3740 // Test encryption. | |
3741 std::vector<uint8_t> cipher_text; | |
3742 std::vector<uint8_t> authentication_tag; | |
3743 EXPECT_EQ(Status::Success(), | |
3744 AesGcmEncrypt(key, | |
3745 test_iv, | |
3746 test_additional_data, | |
3747 test_tag_size_bits, | |
3748 test_plain_text, | |
3749 &cipher_text, | |
3750 &authentication_tag)); | |
3751 | |
3752 EXPECT_BYTES_EQ(test_cipher_text, cipher_text); | |
3753 EXPECT_BYTES_EQ(test_authentication_tag, authentication_tag); | |
3754 | |
3755 // Test decryption. | |
3756 std::vector<uint8_t> plain_text; | |
3757 EXPECT_EQ(Status::Success(), | |
3758 AesGcmDecrypt(key, | |
3759 test_iv, | |
3760 test_additional_data, | |
3761 test_tag_size_bits, | |
3762 test_cipher_text, | |
3763 test_authentication_tag, | |
3764 &plain_text)); | |
3765 EXPECT_BYTES_EQ(test_plain_text, plain_text); | |
3766 | |
3767 // Decryption should fail if any of the inputs are tampered with. | |
3768 EXPECT_EQ(Status::OperationError(), | |
3769 AesGcmDecrypt(key, | |
3770 Corrupted(test_iv), | |
3771 test_additional_data, | |
3772 test_tag_size_bits, | |
3773 test_cipher_text, | |
3774 test_authentication_tag, | |
3775 &plain_text)); | |
3776 EXPECT_EQ(Status::OperationError(), | |
3777 AesGcmDecrypt(key, | |
3778 test_iv, | |
3779 Corrupted(test_additional_data), | |
3780 test_tag_size_bits, | |
3781 test_cipher_text, | |
3782 test_authentication_tag, | |
3783 &plain_text)); | |
3784 EXPECT_EQ(Status::OperationError(), | |
3785 AesGcmDecrypt(key, | |
3786 test_iv, | |
3787 test_additional_data, | |
3788 test_tag_size_bits, | |
3789 Corrupted(test_cipher_text), | |
3790 test_authentication_tag, | |
3791 &plain_text)); | |
3792 EXPECT_EQ(Status::OperationError(), | |
3793 AesGcmDecrypt(key, | |
3794 test_iv, | |
3795 test_additional_data, | |
3796 test_tag_size_bits, | |
3797 test_cipher_text, | |
3798 Corrupted(test_authentication_tag), | |
3799 &plain_text)); | |
3800 | |
3801 // Try different incorrect tag lengths | |
3802 uint8_t kAlternateTagLengths[] = {0, 8, 96, 120, 128, 160, 255}; | |
3803 for (size_t tag_i = 0; tag_i < arraysize(kAlternateTagLengths); ++tag_i) { | |
3804 unsigned int wrong_tag_size_bits = kAlternateTagLengths[tag_i]; | |
3805 if (test_tag_size_bits == wrong_tag_size_bits) | |
3806 continue; | |
3807 EXPECT_NE(Status::Success(), | |
3808 AesGcmDecrypt(key, | |
3809 test_iv, | |
3810 test_additional_data, | |
3811 wrong_tag_size_bits, | |
3812 test_cipher_text, | |
3813 test_authentication_tag, | |
3814 &plain_text)); | |
3815 } | |
3816 } | |
3817 } | |
3818 | |
3819 // AES 192-bit is not allowed: http://crbug.com/381829 | |
3820 TEST(WebCryptoAesCbcTest, ImportAesCbc192Raw) { | |
3821 std::vector<uint8_t> key_raw(24, 0); | |
3822 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
3823 Status status = ImportKey(blink::WebCryptoKeyFormatRaw, | |
3824 CryptoData(key_raw), | |
3825 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3826 true, | |
3827 blink::WebCryptoKeyUsageEncrypt, | |
3828 &key); | |
3829 ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status); | |
3830 } | |
3831 | |
3832 // AES 192-bit is not allowed: http://crbug.com/381829 | |
3833 TEST(WebCryptoAesCbcTest, ImportAesCbc192Jwk) { | |
3834 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
3835 | |
3836 base::DictionaryValue dict; | |
3837 dict.SetString("kty", "oct"); | |
3838 dict.SetString("alg", "A192CBC"); | |
3839 dict.SetString("k", "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"); | |
3840 | |
3841 EXPECT_EQ( | |
3842 Status::ErrorAes192BitUnsupported(), | |
3843 ImportKeyJwkFromDict(dict, | |
3844 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3845 false, | |
3846 blink::WebCryptoKeyUsageEncrypt, | |
3847 &key)); | |
3848 } | |
3849 | |
3850 // AES 192-bit is not allowed: http://crbug.com/381829 | |
3851 TEST(WebCryptoAesCbcTest, GenerateAesCbc192) { | |
3852 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
3853 Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192), | |
3854 true, | |
3855 blink::WebCryptoKeyUsageEncrypt, | |
3856 &key); | |
3857 ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status); | |
3858 } | |
3859 | |
3860 // AES 192-bit is not allowed: http://crbug.com/381829 | |
3861 TEST(WebCryptoAesCbcTest, UnwrapAesCbc192) { | |
3862 std::vector<uint8_t> wrapping_key_data(16, 0); | |
3863 std::vector<uint8_t> wrapped_key = HexStringToBytes( | |
3864 "1A07ACAB6C906E50883173C29441DB1DE91D34F45C435B5F99C822867FB3956F"); | |
3865 | |
3866 blink::WebCryptoKey wrapping_key = | |
3867 ImportSecretKeyFromRaw(wrapping_key_data, | |
3868 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), | |
3869 blink::WebCryptoKeyUsageUnwrapKey); | |
3870 | |
3871 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
3872 ASSERT_EQ(Status::ErrorAes192BitUnsupported(), | |
3873 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
3874 CryptoData(wrapped_key), | |
3875 wrapping_key, | |
3876 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw), | |
3877 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), | |
3878 true, | |
3879 blink::WebCryptoKeyUsageEncrypt, | |
3880 &unwrapped_key)); | |
3881 } | |
3882 | |
3883 // TODO(eroman): move into RSA-OAEP specific file or change name. | |
3884 scoped_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() { | |
3885 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); | |
3886 jwk->SetString("kty", "RSA"); | |
3887 jwk->SetString("n", | |
3888 Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex))); | |
3889 jwk->SetString("e", | |
3890 Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex))); | |
3891 return jwk.Pass(); | |
3892 } | |
3893 | |
3894 // Import a PKCS#8 private key that uses RSAPrivateKey with the | |
3895 // id-rsaEncryption OID. | |
3896 TEST(WebCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) { | |
3897 if (!SupportsRsaOaep()) { | |
3898 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3899 return; | |
3900 } | |
3901 | |
3902 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
3903 ASSERT_EQ(Status::Success(), | |
3904 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
3905 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
3906 CreateRsaHashedImportAlgorithm( | |
3907 blink::WebCryptoAlgorithmIdRsaOaep, | |
3908 blink::WebCryptoAlgorithmIdSha1), | |
3909 true, | |
3910 blink::WebCryptoKeyUsageDecrypt, | |
3911 &private_key)); | |
3912 } | |
3913 | |
3914 TEST(WebCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) { | |
3915 if (!SupportsRsaOaep()) { | |
3916 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3917 return; | |
3918 } | |
3919 | |
3920 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
3921 | |
3922 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3923 ASSERT_EQ(Status::Success(), | |
3924 ImportKeyJwkFromDict(*jwk.get(), | |
3925 CreateRsaHashedImportAlgorithm( | |
3926 blink::WebCryptoAlgorithmIdRsaOaep, | |
3927 blink::WebCryptoAlgorithmIdSha1), | |
3928 true, | |
3929 blink::WebCryptoKeyUsageEncrypt, | |
3930 &public_key)); | |
3931 } | |
3932 | |
3933 TEST(WebCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) { | |
3934 if (!SupportsRsaOaep()) { | |
3935 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3936 return; | |
3937 } | |
3938 | |
3939 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
3940 jwk->SetString("alg", "RSA-OAEP"); | |
3941 | |
3942 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3943 ASSERT_EQ(Status::Success(), | |
3944 ImportKeyJwkFromDict(*jwk.get(), | |
3945 CreateRsaHashedImportAlgorithm( | |
3946 blink::WebCryptoAlgorithmIdRsaOaep, | |
3947 blink::WebCryptoAlgorithmIdSha1), | |
3948 true, | |
3949 blink::WebCryptoKeyUsageEncrypt, | |
3950 &public_key)); | |
3951 } | |
3952 | |
3953 TEST(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) { | |
3954 if (!SupportsRsaOaep()) { | |
3955 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3956 return; | |
3957 } | |
3958 | |
3959 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
3960 jwk->SetString("alg", "RSA-OAEP-512"); | |
3961 | |
3962 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3963 ASSERT_EQ(Status::ErrorJwkAlgorithmInconsistent(), | |
3964 ImportKeyJwkFromDict(*jwk.get(), | |
3965 CreateRsaHashedImportAlgorithm( | |
3966 blink::WebCryptoAlgorithmIdRsaOaep, | |
3967 blink::WebCryptoAlgorithmIdSha1), | |
3968 true, | |
3969 blink::WebCryptoKeyUsageEncrypt, | |
3970 &public_key)); | |
3971 } | |
3972 | |
3973 TEST(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) { | |
3974 if (!SupportsRsaOaep()) { | |
3975 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3976 return; | |
3977 } | |
3978 | |
3979 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
3980 jwk->SetString("kty", "oct"); | |
3981 jwk->SetString("alg", "RSA-OAEP"); | |
3982 | |
3983 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
3984 ASSERT_EQ(Status::ErrorJwkUnexpectedKty("RSA"), | |
3985 ImportKeyJwkFromDict(*jwk.get(), | |
3986 CreateRsaHashedImportAlgorithm( | |
3987 blink::WebCryptoAlgorithmIdRsaOaep, | |
3988 blink::WebCryptoAlgorithmIdSha1), | |
3989 true, | |
3990 blink::WebCryptoKeyUsageEncrypt, | |
3991 &public_key)); | |
3992 } | |
3993 | |
3994 TEST(WebCryptoRsaOaepTest, ExportPublicJwk) { | |
3995 if (!SupportsRsaOaep()) { | |
3996 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
3997 return; | |
3998 } | |
3999 | |
4000 struct TestData { | |
4001 blink::WebCryptoAlgorithmId hash_alg; | |
4002 const char* expected_jwk_alg; | |
4003 } kTestData[] = {{blink::WebCryptoAlgorithmIdSha1, "RSA-OAEP"}, | |
4004 {blink::WebCryptoAlgorithmIdSha256, "RSA-OAEP-256"}, | |
4005 {blink::WebCryptoAlgorithmIdSha384, "RSA-OAEP-384"}, | |
4006 {blink::WebCryptoAlgorithmIdSha512, "RSA-OAEP-512"}}; | |
4007 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestData); ++i) { | |
4008 const TestData& test_data = kTestData[i]; | |
4009 SCOPED_TRACE(test_data.expected_jwk_alg); | |
4010 | |
4011 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
4012 jwk->SetString("alg", test_data.expected_jwk_alg); | |
4013 | |
4014 // Import the key in a known-good format | |
4015 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4016 ASSERT_EQ(Status::Success(), | |
4017 ImportKeyJwkFromDict( | |
4018 *jwk.get(), | |
4019 CreateRsaHashedImportAlgorithm( | |
4020 blink::WebCryptoAlgorithmIdRsaOaep, test_data.hash_alg), | |
4021 true, | |
4022 blink::WebCryptoKeyUsageEncrypt, | |
4023 &public_key)); | |
4024 | |
4025 // Now export the key as JWK and verify its contents | |
4026 std::vector<uint8_t> jwk_data; | |
4027 ASSERT_EQ(Status::Success(), | |
4028 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk_data)); | |
4029 EXPECT_TRUE(VerifyPublicJwk(jwk_data, | |
4030 test_data.expected_jwk_alg, | |
4031 kPublicKeyModulusHex, | |
4032 kPublicKeyExponentHex, | |
4033 blink::WebCryptoKeyUsageEncrypt)); | |
4034 } | |
4035 } | |
4036 | |
4037 TEST(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) { | |
4038 if (!SupportsRsaOaep()) { | |
4039 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4040 return; | |
4041 } | |
4042 | |
4043 scoped_ptr<base::ListValue> tests; | |
4044 ASSERT_TRUE(ReadJsonTestFileToList("rsa_oaep.json", &tests)); | |
4045 | |
4046 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
4047 SCOPED_TRACE(test_index); | |
4048 | |
4049 base::DictionaryValue* test = NULL; | |
4050 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
4051 | |
4052 blink::WebCryptoAlgorithm digest_algorithm = | |
4053 GetDigestAlgorithm(test, "hash"); | |
4054 ASSERT_FALSE(digest_algorithm.isNull()); | |
4055 std::vector<uint8_t> public_key_der = | |
4056 GetBytesFromHexString(test, "public_key"); | |
4057 std::vector<uint8_t> private_key_der = | |
4058 GetBytesFromHexString(test, "private_key"); | |
4059 std::vector<uint8_t> ciphertext = GetBytesFromHexString(test, "ciphertext"); | |
4060 std::vector<uint8_t> plaintext = GetBytesFromHexString(test, "plaintext"); | |
4061 std::vector<uint8_t> label = GetBytesFromHexString(test, "label"); | |
4062 | |
4063 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( | |
4064 blink::WebCryptoAlgorithmIdRsaOaep, digest_algorithm.id()); | |
4065 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4066 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4067 | |
4068 ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair(public_key_der, | |
4069 private_key_der, | |
4070 import_algorithm, | |
4071 false, | |
4072 blink::WebCryptoKeyUsageEncrypt, | |
4073 blink::WebCryptoKeyUsageDecrypt, | |
4074 &public_key, | |
4075 &private_key)); | |
4076 | |
4077 blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); | |
4078 std::vector<uint8_t> decrypted_data; | |
4079 ASSERT_EQ(Status::Success(), | |
4080 Decrypt(op_algorithm, | |
4081 private_key, | |
4082 CryptoData(ciphertext), | |
4083 &decrypted_data)); | |
4084 EXPECT_BYTES_EQ(plaintext, decrypted_data); | |
4085 std::vector<uint8_t> encrypted_data; | |
4086 ASSERT_EQ( | |
4087 Status::Success(), | |
4088 Encrypt( | |
4089 op_algorithm, public_key, CryptoData(plaintext), &encrypted_data)); | |
4090 std::vector<uint8_t> redecrypted_data; | |
4091 ASSERT_EQ(Status::Success(), | |
4092 Decrypt(op_algorithm, | |
4093 private_key, | |
4094 CryptoData(encrypted_data), | |
4095 &redecrypted_data)); | |
4096 EXPECT_BYTES_EQ(plaintext, redecrypted_data); | |
4097 } | |
4098 } | |
4099 | |
4100 TEST(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) { | |
4101 if (!SupportsRsaOaep()) { | |
4102 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4103 return; | |
4104 } | |
4105 | |
4106 const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha1; | |
4107 const size_t kHashSize = 20; | |
4108 | |
4109 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
4110 | |
4111 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4112 ASSERT_EQ(Status::Success(), | |
4113 ImportKeyJwkFromDict(*jwk.get(), | |
4114 CreateRsaHashedImportAlgorithm( | |
4115 blink::WebCryptoAlgorithmIdRsaOaep, kHash), | |
4116 true, | |
4117 blink::WebCryptoKeyUsageEncrypt, | |
4118 &public_key)); | |
4119 | |
4120 // The maximum size of an encrypted message is: | |
4121 // modulus length | |
4122 // - 1 (leading octet) | |
4123 // - hash size (maskedSeed) | |
4124 // - hash size (lHash portion of maskedDB) | |
4125 // - 1 (at least one octet for the padding string) | |
4126 size_t kMaxMessageSize = (kModulusLengthBits / 8) - 2 - (2 * kHashSize); | |
4127 | |
4128 // The label has no influence on the maximum message size. For simplicity, | |
4129 // use the empty string. | |
4130 std::vector<uint8_t> label; | |
4131 blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); | |
4132 | |
4133 // Test that a message just before the boundary succeeds. | |
4134 std::string large_message; | |
4135 large_message.resize(kMaxMessageSize - 1, 'A'); | |
4136 | |
4137 std::vector<uint8_t> ciphertext; | |
4138 ASSERT_EQ( | |
4139 Status::Success(), | |
4140 Encrypt( | |
4141 op_algorithm, public_key, CryptoData(large_message), &ciphertext)); | |
4142 | |
4143 // Test that a message at the boundary succeeds. | |
4144 large_message.resize(kMaxMessageSize, 'A'); | |
4145 ciphertext.clear(); | |
4146 | |
4147 ASSERT_EQ( | |
4148 Status::Success(), | |
4149 Encrypt( | |
4150 op_algorithm, public_key, CryptoData(large_message), &ciphertext)); | |
4151 | |
4152 // Test that a message greater than the largest size fails. | |
4153 large_message.resize(kMaxMessageSize + 1, 'A'); | |
4154 ciphertext.clear(); | |
4155 | |
4156 ASSERT_EQ( | |
4157 Status::OperationError(), | |
4158 Encrypt( | |
4159 op_algorithm, public_key, CryptoData(large_message), &ciphertext)); | |
4160 } | |
4161 | |
4162 // Ensures that if the selected hash algorithm for the RSA-OAEP message is too | |
4163 // large, then it is rejected, independent of the actual message to be | |
4164 // encrypted. | |
4165 // For example, a 1024-bit RSA key is too small to accomodate a message that | |
4166 // uses OAEP with SHA-512, since it requires 1040 bits to encode | |
4167 // (2 * hash size + 2 padding bytes). | |
4168 TEST(WebCryptoRsaOaepTest, EncryptWithLargeDigestFails) { | |
4169 if (!SupportsRsaOaep()) { | |
4170 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4171 return; | |
4172 } | |
4173 | |
4174 const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha512; | |
4175 | |
4176 scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); | |
4177 | |
4178 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4179 ASSERT_EQ(Status::Success(), | |
4180 ImportKeyJwkFromDict(*jwk.get(), | |
4181 CreateRsaHashedImportAlgorithm( | |
4182 blink::WebCryptoAlgorithmIdRsaOaep, kHash), | |
4183 true, | |
4184 blink::WebCryptoKeyUsageEncrypt, | |
4185 &public_key)); | |
4186 | |
4187 // The label has no influence on the maximum message size. For simplicity, | |
4188 // use the empty string. | |
4189 std::vector<uint8_t> label; | |
4190 blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); | |
4191 | |
4192 std::string small_message("A"); | |
4193 std::vector<uint8_t> ciphertext; | |
4194 // This is an operation error, as the internal consistency checking of the | |
4195 // algorithm parameters is up to the implementation. | |
4196 ASSERT_EQ( | |
4197 Status::OperationError(), | |
4198 Encrypt( | |
4199 op_algorithm, public_key, CryptoData(small_message), &ciphertext)); | |
4200 } | |
4201 | |
4202 TEST(WebCryptoRsaOaepTest, DecryptWithLargeMessageFails) { | |
4203 if (!SupportsRsaOaep()) { | |
4204 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4205 return; | |
4206 } | |
4207 | |
4208 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4209 ASSERT_EQ(Status::Success(), | |
4210 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
4211 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
4212 CreateRsaHashedImportAlgorithm( | |
4213 blink::WebCryptoAlgorithmIdRsaOaep, | |
4214 blink::WebCryptoAlgorithmIdSha1), | |
4215 true, | |
4216 blink::WebCryptoKeyUsageDecrypt, | |
4217 &private_key)); | |
4218 | |
4219 // The label has no influence on the maximum message size. For simplicity, | |
4220 // use the empty string. | |
4221 std::vector<uint8_t> label; | |
4222 blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); | |
4223 | |
4224 std::string large_dummy_message(kModulusLengthBits / 8, 'A'); | |
4225 std::vector<uint8_t> plaintext; | |
4226 | |
4227 ASSERT_EQ(Status::OperationError(), | |
4228 Decrypt(op_algorithm, | |
4229 private_key, | |
4230 CryptoData(large_dummy_message), | |
4231 &plaintext)); | |
4232 } | |
4233 | |
4234 TEST(WebCryptoRsaOaepTest, WrapUnwrapRawKey) { | |
4235 if (!SupportsRsaOaep()) { | |
4236 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4237 return; | |
4238 } | |
4239 | |
4240 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( | |
4241 blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); | |
4242 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4243 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4244 | |
4245 ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( | |
4246 HexStringToBytes(kPublicKeySpkiDerHex), | |
4247 HexStringToBytes(kPrivateKeyPkcs8DerHex), | |
4248 import_algorithm, | |
4249 false, | |
4250 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, | |
4251 blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, | |
4252 &public_key, | |
4253 &private_key)); | |
4254 | |
4255 std::vector<uint8_t> label; | |
4256 blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); | |
4257 | |
4258 const std::string key_hex = "000102030405060708090A0B0C0D0E0F"; | |
4259 const blink::WebCryptoAlgorithm key_algorithm = | |
4260 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
4261 | |
4262 blink::WebCryptoKey key = | |
4263 ImportSecretKeyFromRaw(HexStringToBytes(key_hex), | |
4264 key_algorithm, | |
4265 blink::WebCryptoKeyUsageEncrypt); | |
4266 ASSERT_FALSE(key.isNull()); | |
4267 | |
4268 std::vector<uint8_t> wrapped_key; | |
4269 ASSERT_EQ(Status::Success(), | |
4270 WrapKey(blink::WebCryptoKeyFormatRaw, | |
4271 key, | |
4272 public_key, | |
4273 wrapping_algorithm, | |
4274 &wrapped_key)); | |
4275 | |
4276 // Verify that |wrapped_key| can be decrypted and yields the key data. | |
4277 // Because |private_key| supports both decrypt and unwrap, this is valid. | |
4278 std::vector<uint8_t> decrypted_key; | |
4279 ASSERT_EQ(Status::Success(), | |
4280 Decrypt(wrapping_algorithm, | |
4281 private_key, | |
4282 CryptoData(wrapped_key), | |
4283 &decrypted_key)); | |
4284 EXPECT_BYTES_EQ_HEX(key_hex, decrypted_key); | |
4285 | |
4286 // Now attempt to unwrap the key, which should also decrypt the data. | |
4287 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
4288 ASSERT_EQ(Status::Success(), | |
4289 UnwrapKey(blink::WebCryptoKeyFormatRaw, | |
4290 CryptoData(wrapped_key), | |
4291 private_key, | |
4292 wrapping_algorithm, | |
4293 key_algorithm, | |
4294 true, | |
4295 blink::WebCryptoKeyUsageEncrypt, | |
4296 &unwrapped_key)); | |
4297 ASSERT_FALSE(unwrapped_key.isNull()); | |
4298 | |
4299 std::vector<uint8_t> raw_key; | |
4300 ASSERT_EQ(Status::Success(), | |
4301 ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); | |
4302 EXPECT_BYTES_EQ_HEX(key_hex, raw_key); | |
4303 } | |
4304 | |
4305 TEST(WebCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { | |
4306 if (!SupportsRsaOaep()) { | |
4307 LOG(WARNING) << "RSA-OAEP support not present; skipping."; | |
4308 return; | |
4309 } | |
4310 | |
4311 // The public and private portions of a 2048-bit RSA key with the | |
4312 // id-rsaEncryption OID | |
4313 const char kPublicKey2048SpkiDerHex[] = | |
4314 "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c5d8ce" | |
4315 "137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b300c6a6c9764" | |
4316 "f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448e7183a3a68" | |
4317 "e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872458d1b1e2f" | |
4318 "7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34ba17bc5d08" | |
4319 "a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea9893652d02fc606" | |
4320 "36f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d733711c89ca" | |
4321 "749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b557c16615d" | |
4322 "5d0203010001"; | |
4323 const char kPrivateKey2048Pkcs8DerHex[] = | |
4324 "308204bd020100300d06092a864886f70d0101010500048204a7308204a3020100028201" | |
4325 "0100c5d8ce137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b30" | |
4326 "0c6a6c9764f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448" | |
4327 "e7183a3a68e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872" | |
4328 "458d1b1e2f7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34" | |
4329 "ba17bc5d08a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea98936" | |
4330 "52d02fc60636f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d7" | |
4331 "33711c89ca749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b" | |
4332 "557c16615d5d02030100010282010074b70feb41a0b0fcbc207670400556c9450042ede3" | |
4333 "d4383fb1ce8f3558a6d4641d26dd4c333fa4db842d2b9cf9d2354d3e16ad027a9f682d8c" | |
4334 "f4145a1ad97b9edcd8a41c402bd9d8db10f62f43df854cdccbbb2100834f083f53ed6d42" | |
4335 "b1b729a59072b004a4e945fc027db15e9c121d1251464d320d4774d5732df6b3dbf751f4" | |
4336 "9b19c9db201e19989c883bbaad5333db47f64f6f7a95b8d4936b10d945aa3f794cfaab62" | |
4337 "e7d47686129358914f3b8085f03698a650ab5b8c7e45813f2b0515ec05b6e5195b6a7c2a" | |
4338 "0d36969745f431ded4fd059f6aa361a4649541016d356297362b778e90f077d48815b339" | |
4339 "ec6f43aba345df93e67fcb6c2cb5b4544e9be902818100e9c90abe5f9f32468c5b6d630c" | |
4340 "54a4d7d75e29a72cf792f21e242aac78fd7995c42dfd4ae871d2619ff7096cb05baa78e3" | |
4341 "23ecab338401a8059adf7a0d8be3b21edc9a9c82c5605634a2ec81ec053271721351868a" | |
4342 "4c2e50c689d7cef94e31ff23658af5843366e2b289c5bf81d72756a7b93487dd8770d69c" | |
4343 "1f4e089d6d89f302818100d8a58a727c4e209132afd9933b98c89aca862a01cc0be74133" | |
4344 "bee517909e5c379e526895ac4af11780c1fe91194c777c9670b6423f0f5a32fd7691a622" | |
4345 "113eef4bed2ef863363a335fd55b0e75088c582437237d7f3ed3f0a643950237bc6e6277" | |
4346 "ccd0d0a1b4170aa1047aa7ffa7c8c54be10e8c7327ae2e0885663963817f6f02818100e5" | |
4347 "aed9ba4d71b7502e6748a1ce247ecb7bd10c352d6d9256031cdf3c11a65e44b0b7ca2945" | |
4348 "134671195af84c6b3bb3d10ebf65ae916f38bd5dbc59a0ad1c69b8beaf57cb3a8335f19b" | |
4349 "c7117b576987b48331cd9fd3d1a293436b7bb5e1a35c6560de4b5688ea834367cb0997eb" | |
4350 "b578f59ed4cb724c47dba94d3b484c1876dcd70281807f15bc7d2406007cac2b138a96af" | |
4351 "2d1e00276b84da593132c253fcb73212732dfd25824c2a615bc3d9b7f2c8d2fa542d3562" | |
4352 "b0c7738e61eeff580a6056239fb367ea9e5efe73d4f846033602e90c36a78db6fa8ea792" | |
4353 "0769675ec58e237bd994d189c8045a96f5dd3a4f12547257ce224e3c9af830a4da3c0eab" | |
4354 "9227a0035ae9028180067caea877e0b23090fc689322b71fbcce63d6596e66ab5fcdbaa0" | |
4355 "0d49e93aba8effb4518c2da637f209028401a68f344865b4956b032c69acde51d29177ca" | |
4356 "3db99fdbf5e74848ed4fa7bdfc2ebb60e2aaa5354770a763e1399ab7a2099762d525fea0" | |
4357 "37f3e1972c45a477e66db95c9609bb27f862700ef93379930786cf751b"; | |
4358 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( | |
4359 blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); | |
4360 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4361 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4362 | |
4363 ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( | |
4364 HexStringToBytes(kPublicKey2048SpkiDerHex), | |
4365 HexStringToBytes(kPrivateKey2048Pkcs8DerHex), | |
4366 import_algorithm, | |
4367 false, | |
4368 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageWrapKey, | |
4369 blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, | |
4370 &public_key, | |
4371 &private_key)); | |
4372 | |
4373 std::vector<uint8_t> label; | |
4374 blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); | |
4375 | |
4376 const std::string key_hex = "000102030405060708090a0b0c0d0e0f"; | |
4377 const blink::WebCryptoAlgorithm key_algorithm = | |
4378 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
4379 | |
4380 blink::WebCryptoKey key = | |
4381 ImportSecretKeyFromRaw(HexStringToBytes(key_hex), | |
4382 key_algorithm, | |
4383 blink::WebCryptoKeyUsageEncrypt); | |
4384 ASSERT_FALSE(key.isNull()); | |
4385 | |
4386 std::vector<uint8_t> wrapped_key; | |
4387 ASSERT_EQ(Status::Success(), | |
4388 WrapKey(blink::WebCryptoKeyFormatJwk, | |
4389 key, | |
4390 public_key, | |
4391 wrapping_algorithm, | |
4392 &wrapped_key)); | |
4393 | |
4394 // Verify that |wrapped_key| can be decrypted and yields a valid JWK object. | |
4395 // Because |private_key| supports both decrypt and unwrap, this is valid. | |
4396 std::vector<uint8_t> decrypted_jwk; | |
4397 ASSERT_EQ(Status::Success(), | |
4398 Decrypt(wrapping_algorithm, | |
4399 private_key, | |
4400 CryptoData(wrapped_key), | |
4401 &decrypted_jwk)); | |
4402 EXPECT_TRUE(VerifySecretJwk( | |
4403 decrypted_jwk, "A128CBC", key_hex, blink::WebCryptoKeyUsageEncrypt)); | |
4404 | |
4405 // Now attempt to unwrap the key, which should also decrypt the data. | |
4406 blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); | |
4407 ASSERT_EQ(Status::Success(), | |
4408 UnwrapKey(blink::WebCryptoKeyFormatJwk, | |
4409 CryptoData(wrapped_key), | |
4410 private_key, | |
4411 wrapping_algorithm, | |
4412 key_algorithm, | |
4413 true, | |
4414 blink::WebCryptoKeyUsageEncrypt, | |
4415 &unwrapped_key)); | |
4416 ASSERT_FALSE(unwrapped_key.isNull()); | |
4417 | |
4418 std::vector<uint8_t> raw_key; | |
4419 ASSERT_EQ(Status::Success(), | |
4420 ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); | |
4421 EXPECT_BYTES_EQ_HEX(key_hex, raw_key); | |
4422 } | |
4423 | |
4424 // Try importing an RSA-SSA public key with unsupported key usages using SPKI | |
4425 // format. RSA-SSA public keys only support the 'verify' usage. | |
4426 TEST(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) { | |
4427 const blink::WebCryptoAlgorithm algorithm = | |
4428 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4429 blink::WebCryptoAlgorithmIdSha256); | |
4430 | |
4431 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4432 blink::WebCryptoKeyUsageSign, | |
4433 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
4434 blink::WebCryptoKeyUsageEncrypt, | |
4435 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
4436 }; | |
4437 | |
4438 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4439 SCOPED_TRACE(i); | |
4440 | |
4441 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4442 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4443 ImportKey(blink::WebCryptoKeyFormatSpki, | |
4444 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
4445 algorithm, | |
4446 false, | |
4447 bad_usages[i], | |
4448 &public_key)); | |
4449 } | |
4450 } | |
4451 | |
4452 // Try importing an RSA-SSA public key with unsupported key usages using JWK | |
4453 // format. RSA-SSA public keys only support the 'verify' usage. | |
4454 TEST(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_JWK) { | |
4455 const blink::WebCryptoAlgorithm algorithm = | |
4456 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4457 blink::WebCryptoAlgorithmIdSha256); | |
4458 | |
4459 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4460 blink::WebCryptoKeyUsageSign, | |
4461 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
4462 blink::WebCryptoKeyUsageEncrypt, | |
4463 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
4464 }; | |
4465 | |
4466 base::DictionaryValue dict; | |
4467 RestoreJwkRsaDictionary(&dict); | |
4468 dict.Remove("use", NULL); | |
4469 dict.SetString("alg", "RS256"); | |
4470 | |
4471 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4472 SCOPED_TRACE(i); | |
4473 | |
4474 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4475 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4476 ImportKeyJwkFromDict( | |
4477 dict, algorithm, false, bad_usages[i], &public_key)); | |
4478 } | |
4479 } | |
4480 | |
4481 // Try importing an AES-CBC key with unsupported key usages using raw | |
4482 // format. AES-CBC keys support the following usages: | |
4483 // 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' | |
4484 TEST(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) { | |
4485 const blink::WebCryptoAlgorithm algorithm = | |
4486 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); | |
4487 | |
4488 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4489 blink::WebCryptoKeyUsageSign, | |
4490 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageDecrypt, | |
4491 blink::WebCryptoKeyUsageDeriveBits, | |
4492 blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, | |
4493 }; | |
4494 | |
4495 std::vector<uint8_t> key_bytes(16); | |
4496 | |
4497 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4498 SCOPED_TRACE(i); | |
4499 | |
4500 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
4501 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4502 ImportKey(blink::WebCryptoKeyFormatRaw, | |
4503 CryptoData(key_bytes), | |
4504 algorithm, | |
4505 true, | |
4506 bad_usages[i], | |
4507 &key)); | |
4508 } | |
4509 } | |
4510 | |
4511 // Try importing an AES-KW key with unsupported key usages using raw | |
4512 // format. AES-KW keys support the following usages: | |
4513 // 'wrapKey', 'unwrapKey' | |
4514 TEST(WebCryptoAesKwTest, ImportKeyBadUsage_Raw) { | |
4515 const blink::WebCryptoAlgorithm algorithm = | |
4516 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
4517 | |
4518 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4519 blink::WebCryptoKeyUsageEncrypt, | |
4520 blink::WebCryptoKeyUsageDecrypt, | |
4521 blink::WebCryptoKeyUsageSign, | |
4522 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageUnwrapKey, | |
4523 blink::WebCryptoKeyUsageDeriveBits, | |
4524 blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify, | |
4525 }; | |
4526 | |
4527 std::vector<uint8_t> key_bytes(16); | |
4528 | |
4529 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4530 SCOPED_TRACE(i); | |
4531 | |
4532 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
4533 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4534 ImportKey(blink::WebCryptoKeyFormatRaw, | |
4535 CryptoData(key_bytes), | |
4536 algorithm, | |
4537 true, | |
4538 bad_usages[i], | |
4539 &key)); | |
4540 } | |
4541 } | |
4542 | |
4543 // Try unwrapping an HMAC key with unsupported usages using JWK format and | |
4544 // AES-KW. HMAC keys support the following usages: | |
4545 // 'sign', 'verify' | |
4546 TEST(WebCryptoAesKwTest, UnwrapHmacKeyBadUsage_JWK) { | |
4547 const blink::WebCryptoAlgorithm unwrap_algorithm = | |
4548 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
4549 | |
4550 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4551 blink::WebCryptoKeyUsageEncrypt, | |
4552 blink::WebCryptoKeyUsageDecrypt, | |
4553 blink::WebCryptoKeyUsageWrapKey, | |
4554 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, | |
4555 blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDeriveKey, | |
4556 }; | |
4557 | |
4558 // Import the wrapping key. | |
4559 blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); | |
4560 ASSERT_EQ(Status::Success(), | |
4561 ImportKey(blink::WebCryptoKeyFormatRaw, | |
4562 CryptoData(std::vector<uint8_t>(16)), | |
4563 unwrap_algorithm, | |
4564 true, | |
4565 blink::WebCryptoKeyUsageUnwrapKey, | |
4566 &wrapping_key)); | |
4567 | |
4568 // The JWK plain text is: | |
4569 // { "kty": "oct","alg": "HS256","k": "GADWrMRHwQfoNaXU5fZvTg=="} | |
4570 const char* kWrappedJwk = | |
4571 "0AA245F17064FFB2A7A094436A39BEBFC962C627303D1327EA750CE9F917688C2782A943" | |
4572 "7AE7586547AC490E8AE7D5B02D63868D5C3BB57D36C4C8C5BF3962ACEC6F42E767E5706" | |
4573 "4"; | |
4574 | |
4575 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4576 SCOPED_TRACE(i); | |
4577 | |
4578 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
4579 | |
4580 ASSERT_EQ( | |
4581 Status::ErrorCreateKeyBadUsages(), | |
4582 UnwrapKey(blink::WebCryptoKeyFormatJwk, | |
4583 CryptoData(HexStringToBytes(kWrappedJwk)), | |
4584 wrapping_key, | |
4585 unwrap_algorithm, | |
4586 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256), | |
4587 true, | |
4588 bad_usages[i], | |
4589 &key)); | |
4590 } | |
4591 } | |
4592 | |
4593 // Try unwrapping an RSA-SSA public key with unsupported usages using JWK format | |
4594 // and AES-KW. RSA-SSA public keys support the following usages: | |
4595 // 'verify' | |
4596 TEST(WebCryptoAesKwTest, UnwrapRsaSsaPublicKeyBadUsage_JWK) { | |
4597 const blink::WebCryptoAlgorithm unwrap_algorithm = | |
4598 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); | |
4599 | |
4600 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4601 blink::WebCryptoKeyUsageEncrypt, | |
4602 blink::WebCryptoKeyUsageSign, | |
4603 blink::WebCryptoKeyUsageDecrypt, | |
4604 blink::WebCryptoKeyUsageWrapKey, | |
4605 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey, | |
4606 }; | |
4607 | |
4608 // Import the wrapping key. | |
4609 blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); | |
4610 ASSERT_EQ(Status::Success(), | |
4611 ImportKey(blink::WebCryptoKeyFormatRaw, | |
4612 CryptoData(std::vector<uint8_t>(16)), | |
4613 unwrap_algorithm, | |
4614 true, | |
4615 blink::WebCryptoKeyUsageUnwrapKey, | |
4616 &wrapping_key)); | |
4617 | |
4618 // The JWK plaintext is: | |
4619 // { "kty": "RSA","alg": "RS256","n": "...","e": "AQAB"} | |
4620 | |
4621 const char* kWrappedJwk = | |
4622 "CE8DAEF99E977EE58958B8C4494755C846E883B2ECA575C5366622839AF71AB30875F152" | |
4623 "E8E33E15A7817A3A2874EB53EFE05C774D98BC936BA9BA29BEB8BB3F3C3CE2323CB3359D" | |
4624 "E3F426605CF95CCF0E01E870ABD7E35F62E030B5FB6E520A5885514D1D850FB64B57806D" | |
4625 "1ADA57C6E27DF345D8292D80F6B074F1BE51C4CF3D76ECC8886218551308681B44FAC60B" | |
4626 "8CF6EA439BC63239103D0AE81ADB96F908680586C6169284E32EB7DD09D31103EBDAC0C2" | |
4627 "40C72DCF0AEA454113CC47457B13305B25507CBEAB9BDC8D8E0F867F9167F9DCEF0D9F9B" | |
4628 "30F2EE83CEDFD51136852C8A5939B768"; | |
4629 | |
4630 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4631 SCOPED_TRACE(i); | |
4632 | |
4633 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
4634 | |
4635 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4636 UnwrapKey(blink::WebCryptoKeyFormatJwk, | |
4637 CryptoData(HexStringToBytes(kWrappedJwk)), | |
4638 wrapping_key, | |
4639 unwrap_algorithm, | |
4640 CreateRsaHashedImportAlgorithm( | |
4641 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4642 blink::WebCryptoAlgorithmIdSha256), | |
4643 true, | |
4644 bad_usages[i], | |
4645 &key)); | |
4646 } | |
4647 } | |
4648 | |
4649 // Generate an AES-CBC key with invalid usages. AES-CBC supports: | |
4650 // 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey' | |
4651 TEST(WebCryptoAesCbcTest, GenerateKeyBadUsages) { | |
4652 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4653 blink::WebCryptoKeyUsageSign, blink::WebCryptoKeyUsageVerify, | |
4654 blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageVerify, | |
4655 }; | |
4656 | |
4657 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4658 SCOPED_TRACE(i); | |
4659 | |
4660 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | |
4661 | |
4662 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4663 GenerateSecretKey( | |
4664 CreateAesCbcKeyGenAlgorithm(128), true, bad_usages[i], &key)); | |
4665 } | |
4666 } | |
4667 | |
4668 // Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports: | |
4669 // 'sign', 'verify' | |
4670 TEST(WebCryptoRsaSsaTest, GenerateKeyBadUsages) { | |
4671 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
4672 blink::WebCryptoKeyUsageDecrypt, | |
4673 blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDecrypt, | |
4674 blink::WebCryptoKeyUsageWrapKey, | |
4675 }; | |
4676 | |
4677 const unsigned int modulus_length = 256; | |
4678 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
4679 | |
4680 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
4681 SCOPED_TRACE(i); | |
4682 | |
4683 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4684 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4685 | |
4686 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
4687 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
4688 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4689 blink::WebCryptoAlgorithmIdSha256, | |
4690 modulus_length, | |
4691 public_exponent), | |
4692 true, | |
4693 bad_usages[i], | |
4694 &public_key, | |
4695 &private_key)); | |
4696 } | |
4697 } | |
4698 | |
4699 // Generate an RSA-SSA key pair. The public and private keys should select the | |
4700 // key usages which are applicable, and not have the exact same usages as was | |
4701 // specified to GenerateKey | |
4702 TEST(WebCryptoRsaSsaTest, GenerateKeyPairIntersectUsages) { | |
4703 const unsigned int modulus_length = 256; | |
4704 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
4705 | |
4706 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4707 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4708 | |
4709 ASSERT_EQ(Status::Success(), | |
4710 GenerateKeyPair( | |
4711 CreateRsaHashedKeyGenAlgorithm( | |
4712 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4713 blink::WebCryptoAlgorithmIdSha256, | |
4714 modulus_length, | |
4715 public_exponent), | |
4716 true, | |
4717 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
4718 &public_key, | |
4719 &private_key)); | |
4720 | |
4721 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, public_key.usages()); | |
4722 EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); | |
4723 | |
4724 // Try again but this time without the Verify usages. | |
4725 ASSERT_EQ(Status::Success(), | |
4726 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
4727 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4728 blink::WebCryptoAlgorithmIdSha256, | |
4729 modulus_length, | |
4730 public_exponent), | |
4731 true, | |
4732 blink::WebCryptoKeyUsageSign, | |
4733 &public_key, | |
4734 &private_key)); | |
4735 | |
4736 EXPECT_EQ(0, public_key.usages()); | |
4737 EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); | |
4738 } | |
4739 | |
4740 // Generate an AES-CBC key and an RSA key pair. Use the AES-CBC key to wrap the | |
4741 // key pair (using SPKI format for public key, PKCS8 format for private key). | |
4742 // Then unwrap the wrapped key pair and verify that the key data is the same. | |
4743 TEST(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) { | |
4744 if (!SupportsRsaPrivateKeyImport()) | |
4745 return; | |
4746 | |
4747 // Generate the wrapping key. | |
4748 blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull(); | |
4749 ASSERT_EQ(Status::Success(), | |
4750 GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), | |
4751 true, | |
4752 blink::WebCryptoKeyUsageWrapKey | | |
4753 blink::WebCryptoKeyUsageUnwrapKey, | |
4754 &wrapping_key)); | |
4755 | |
4756 // Generate an RSA key pair to be wrapped. | |
4757 const unsigned int modulus_length = 256; | |
4758 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
4759 | |
4760 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); | |
4761 blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); | |
4762 ASSERT_EQ(Status::Success(), | |
4763 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
4764 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4765 blink::WebCryptoAlgorithmIdSha256, | |
4766 modulus_length, | |
4767 public_exponent), | |
4768 true, | |
4769 0, | |
4770 &public_key, | |
4771 &private_key)); | |
4772 | |
4773 // Export key pair as SPKI + PKCS8 | |
4774 std::vector<uint8_t> public_key_spki; | |
4775 ASSERT_EQ( | |
4776 Status::Success(), | |
4777 ExportKey(blink::WebCryptoKeyFormatSpki, public_key, &public_key_spki)); | |
4778 | |
4779 std::vector<uint8_t> private_key_pkcs8; | |
4780 ASSERT_EQ( | |
4781 Status::Success(), | |
4782 ExportKey( | |
4783 blink::WebCryptoKeyFormatPkcs8, private_key, &private_key_pkcs8)); | |
4784 | |
4785 // Wrap the key pair. | |
4786 blink::WebCryptoAlgorithm wrap_algorithm = | |
4787 CreateAesCbcAlgorithm(std::vector<uint8_t>(16, 0)); | |
4788 | |
4789 std::vector<uint8_t> wrapped_public_key; | |
4790 ASSERT_EQ(Status::Success(), | |
4791 WrapKey(blink::WebCryptoKeyFormatSpki, | |
4792 public_key, | |
4793 wrapping_key, | |
4794 wrap_algorithm, | |
4795 &wrapped_public_key)); | |
4796 | |
4797 std::vector<uint8_t> wrapped_private_key; | |
4798 ASSERT_EQ(Status::Success(), | |
4799 WrapKey(blink::WebCryptoKeyFormatPkcs8, | |
4800 private_key, | |
4801 wrapping_key, | |
4802 wrap_algorithm, | |
4803 &wrapped_private_key)); | |
4804 | |
4805 // Unwrap the key pair. | |
4806 blink::WebCryptoAlgorithm rsa_import_algorithm = | |
4807 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
4808 blink::WebCryptoAlgorithmIdSha256); | |
4809 | |
4810 blink::WebCryptoKey unwrapped_public_key = blink::WebCryptoKey::createNull(); | |
4811 | |
4812 ASSERT_EQ(Status::Success(), | |
4813 UnwrapKey(blink::WebCryptoKeyFormatSpki, | |
4814 CryptoData(wrapped_public_key), | |
4815 wrapping_key, | |
4816 wrap_algorithm, | |
4817 rsa_import_algorithm, | |
4818 true, | |
4819 0, | |
4820 &unwrapped_public_key)); | |
4821 | |
4822 blink::WebCryptoKey unwrapped_private_key = blink::WebCryptoKey::createNull(); | |
4823 | |
4824 ASSERT_EQ(Status::Success(), | |
4825 UnwrapKey(blink::WebCryptoKeyFormatPkcs8, | |
4826 CryptoData(wrapped_private_key), | |
4827 wrapping_key, | |
4828 wrap_algorithm, | |
4829 rsa_import_algorithm, | |
4830 true, | |
4831 0, | |
4832 &unwrapped_private_key)); | |
4833 | |
4834 // Export unwrapped key pair as SPKI + PKCS8 | |
4835 std::vector<uint8_t> unwrapped_public_key_spki; | |
4836 ASSERT_EQ(Status::Success(), | |
4837 ExportKey(blink::WebCryptoKeyFormatSpki, | |
4838 unwrapped_public_key, | |
4839 &unwrapped_public_key_spki)); | |
4840 | |
4841 std::vector<uint8_t> unwrapped_private_key_pkcs8; | |
4842 ASSERT_EQ(Status::Success(), | |
4843 ExportKey(blink::WebCryptoKeyFormatPkcs8, | |
4844 unwrapped_private_key, | |
4845 &unwrapped_private_key_pkcs8)); | |
4846 | |
4847 EXPECT_EQ(public_key_spki, unwrapped_public_key_spki); | |
4848 EXPECT_EQ(private_key_pkcs8, unwrapped_private_key_pkcs8); | |
4849 | |
4850 EXPECT_NE(public_key_spki, wrapped_public_key); | |
4851 EXPECT_NE(private_key_pkcs8, wrapped_private_key); | |
4852 } | |
4853 | |
4854 } // namespace | |
4855 | |
4856 } // namespace webcrypto | 564 } // namespace webcrypto |
4857 | 565 |
4858 } // namespace content | 566 } // namesapce content |
OLD | NEW |