OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/stl_util.h" | |
6 #include "content/child/webcrypto/algorithm_dispatch.h" | |
7 #include "content/child/webcrypto/crypto_data.h" | |
8 #include "content/child/webcrypto/status.h" | |
9 #include "content/child/webcrypto/test/test_helpers.h" | |
10 #include "content/child/webcrypto/webcrypto_util.h" | |
11 #include "testing/gtest/include/gtest/gtest.h" | |
12 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
13 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
14 | |
15 namespace content { | |
16 | |
17 namespace webcrypto { | |
18 | |
19 namespace { | |
20 | |
21 // Creates an AES-GCM algorithm. | |
22 blink::WebCryptoAlgorithm CreateAesGcmAlgorithm( | |
23 const std::vector<uint8_t>& iv, | |
24 const std::vector<uint8_t>& additional_data, | |
25 unsigned int tag_length_bits) { | |
26 EXPECT_TRUE(SupportsAesGcm()); | |
27 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
28 blink::WebCryptoAlgorithmIdAesGcm, | |
29 new blink::WebCryptoAesGcmParams(vector_as_array(&iv), iv.size(), true, | |
30 vector_as_array(&additional_data), | |
31 additional_data.size(), true, | |
32 tag_length_bits)); | |
33 } | |
34 | |
35 blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm( | |
36 unsigned short key_length_bits) { | |
37 EXPECT_TRUE(SupportsAesGcm()); | |
38 return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesGcm, | |
39 key_length_bits); | |
40 } | |
41 | |
42 Status AesGcmEncrypt(const blink::WebCryptoKey& key, | |
43 const std::vector<uint8_t>& iv, | |
44 const std::vector<uint8_t>& additional_data, | |
45 unsigned int tag_length_bits, | |
46 const std::vector<uint8_t>& plain_text, | |
47 std::vector<uint8_t>* cipher_text, | |
48 std::vector<uint8_t>* authentication_tag) { | |
49 EXPECT_TRUE(SupportsAesGcm()); | |
50 blink::WebCryptoAlgorithm algorithm = | |
51 CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); | |
52 | |
53 std::vector<uint8_t> output; | |
54 Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output); | |
55 if (status.IsError()) | |
56 return status; | |
57 | |
58 if ((tag_length_bits % 8) != 0) { | |
59 ADD_FAILURE() << "Encrypt should have failed."; | |
60 return Status::OperationError(); | |
61 } | |
62 | |
63 size_t tag_length_bytes = tag_length_bits / 8; | |
64 | |
65 if (tag_length_bytes > output.size()) { | |
66 ADD_FAILURE() << "tag length is larger than output"; | |
67 return Status::OperationError(); | |
68 } | |
69 | |
70 // The encryption result is cipher text with authentication tag appended. | |
71 cipher_text->assign(output.begin(), | |
72 output.begin() + (output.size() - tag_length_bytes)); | |
73 authentication_tag->assign(output.begin() + cipher_text->size(), | |
74 output.end()); | |
75 | |
76 return Status::Success(); | |
77 } | |
78 | |
79 Status AesGcmDecrypt(const blink::WebCryptoKey& key, | |
80 const std::vector<uint8_t>& iv, | |
81 const std::vector<uint8_t>& additional_data, | |
82 unsigned int tag_length_bits, | |
83 const std::vector<uint8_t>& cipher_text, | |
84 const std::vector<uint8_t>& authentication_tag, | |
85 std::vector<uint8_t>* plain_text) { | |
86 EXPECT_TRUE(SupportsAesGcm()); | |
87 blink::WebCryptoAlgorithm algorithm = | |
88 CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits); | |
89 | |
90 // Join cipher text and authentication tag. | |
91 std::vector<uint8_t> cipher_text_with_tag; | |
92 cipher_text_with_tag.reserve(cipher_text.size() + authentication_tag.size()); | |
93 cipher_text_with_tag.insert(cipher_text_with_tag.end(), cipher_text.begin(), | |
94 cipher_text.end()); | |
95 cipher_text_with_tag.insert(cipher_text_with_tag.end(), | |
96 authentication_tag.begin(), | |
97 authentication_tag.end()); | |
98 | |
99 return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text); | |
100 } | |
101 | |
102 TEST(WebCryptoAesGcmTest, GenerateKeyBadLength) { | |
103 if (!SupportsAesGcm()) { | |
104 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
105 return; | |
106 } | |
107 | |
108 const unsigned short kKeyLen[] = {0, 127, 257}; | |
109 blink::WebCryptoKey key; | |
110 for (size_t i = 0; i < arraysize(kKeyLen); ++i) { | |
111 SCOPED_TRACE(i); | |
112 EXPECT_EQ(Status::ErrorGenerateAesKeyLength(), | |
113 GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, | |
114 blink::WebCryptoKeyUsageDecrypt, &key)); | |
115 } | |
116 } | |
117 | |
118 TEST(WebCryptoAesGcmTest, GenerateKeyEmptyUsage) { | |
119 if (!SupportsAesGcm()) { | |
120 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
121 return; | |
122 } | |
123 | |
124 blink::WebCryptoKey key; | |
125 EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(), | |
126 GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(256), true, 0, &key)); | |
127 } | |
128 | |
129 TEST(WebCryptoAesGcmTest, ImportExportJwk) { | |
130 // Some Linux test runners may not have a new enough version of NSS. | |
131 if (!SupportsAesGcm()) { | |
132 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
133 return; | |
134 } | |
135 | |
136 const blink::WebCryptoAlgorithm algorithm = | |
137 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm); | |
138 | |
139 // AES-GCM 128 | |
140 ImportExportJwkSymmetricKey( | |
141 128, algorithm, | |
142 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
143 "A128GCM"); | |
144 | |
145 // AES-GCM 256 | |
146 ImportExportJwkSymmetricKey(256, algorithm, blink::WebCryptoKeyUsageDecrypt, | |
147 "A256GCM"); | |
148 } | |
149 | |
150 // TODO(eroman): | |
151 // * Test decryption when the tag length exceeds input size | |
152 // * Test decryption with empty input | |
153 // * Test decryption with tag length of 0. | |
154 TEST(WebCryptoAesGcmTest, SampleSets) { | |
155 // Some Linux test runners may not have a new enough version of NSS. | |
156 if (!SupportsAesGcm()) { | |
157 LOG(WARNING) << "AES GCM not supported, skipping tests"; | |
158 return; | |
159 } | |
160 | |
161 scoped_ptr<base::ListValue> tests; | |
162 ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests)); | |
163 | |
164 // Note that WebCrypto appends the authentication tag to the ciphertext. | |
165 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
166 SCOPED_TRACE(test_index); | |
167 base::DictionaryValue* test; | |
168 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
169 | |
170 const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key"); | |
171 const std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv"); | |
172 const std::vector<uint8_t> test_additional_data = | |
173 GetBytesFromHexString(test, "additional_data"); | |
174 const std::vector<uint8_t> test_plain_text = | |
175 GetBytesFromHexString(test, "plain_text"); | |
176 const std::vector<uint8_t> test_authentication_tag = | |
177 GetBytesFromHexString(test, "authentication_tag"); | |
178 const unsigned int test_tag_size_bits = test_authentication_tag.size() * 8; | |
179 const std::vector<uint8_t> test_cipher_text = | |
180 GetBytesFromHexString(test, "cipher_text"); | |
181 | |
182 blink::WebCryptoKey key = ImportSecretKeyFromRaw( | |
183 test_key, CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | |
184 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); | |
185 | |
186 // Verify exported raw key is identical to the imported data | |
187 std::vector<uint8_t> raw_key; | |
188 EXPECT_EQ(Status::Success(), | |
189 ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key)); | |
190 | |
191 EXPECT_BYTES_EQ(test_key, raw_key); | |
192 | |
193 // Test encryption. | |
194 std::vector<uint8_t> cipher_text; | |
195 std::vector<uint8_t> authentication_tag; | |
196 EXPECT_EQ( | |
197 Status::Success(), | |
198 AesGcmEncrypt(key, test_iv, test_additional_data, test_tag_size_bits, | |
199 test_plain_text, &cipher_text, &authentication_tag)); | |
200 | |
201 EXPECT_BYTES_EQ(test_cipher_text, cipher_text); | |
202 EXPECT_BYTES_EQ(test_authentication_tag, authentication_tag); | |
203 | |
204 // Test decryption. | |
205 std::vector<uint8_t> plain_text; | |
206 EXPECT_EQ( | |
207 Status::Success(), | |
208 AesGcmDecrypt(key, test_iv, test_additional_data, test_tag_size_bits, | |
209 test_cipher_text, test_authentication_tag, &plain_text)); | |
210 EXPECT_BYTES_EQ(test_plain_text, plain_text); | |
211 | |
212 // Decryption should fail if any of the inputs are tampered with. | |
213 EXPECT_EQ(Status::OperationError(), | |
214 AesGcmDecrypt(key, Corrupted(test_iv), test_additional_data, | |
215 test_tag_size_bits, test_cipher_text, | |
216 test_authentication_tag, &plain_text)); | |
217 EXPECT_EQ(Status::OperationError(), | |
218 AesGcmDecrypt(key, test_iv, Corrupted(test_additional_data), | |
219 test_tag_size_bits, test_cipher_text, | |
220 test_authentication_tag, &plain_text)); | |
221 EXPECT_EQ(Status::OperationError(), | |
222 AesGcmDecrypt(key, test_iv, test_additional_data, | |
223 test_tag_size_bits, Corrupted(test_cipher_text), | |
224 test_authentication_tag, &plain_text)); | |
225 EXPECT_EQ(Status::OperationError(), | |
226 AesGcmDecrypt(key, test_iv, test_additional_data, | |
227 test_tag_size_bits, test_cipher_text, | |
228 Corrupted(test_authentication_tag), &plain_text)); | |
229 | |
230 // Try different incorrect tag lengths | |
231 uint8_t kAlternateTagLengths[] = {0, 8, 96, 120, 128, 160, 255}; | |
232 for (size_t tag_i = 0; tag_i < arraysize(kAlternateTagLengths); ++tag_i) { | |
233 unsigned int wrong_tag_size_bits = kAlternateTagLengths[tag_i]; | |
234 if (test_tag_size_bits == wrong_tag_size_bits) | |
235 continue; | |
236 EXPECT_NE(Status::Success(), | |
237 AesGcmDecrypt(key, test_iv, test_additional_data, | |
238 wrong_tag_size_bits, test_cipher_text, | |
239 test_authentication_tag, &plain_text)); | |
240 } | |
241 } | |
242 } | |
243 | |
244 } // namespace | |
245 | |
246 } // namespace webcrypto | |
247 | |
248 } // namespace content | |
OLD | NEW |